diff options
| author | Cy Schubert <cy@FreeBSD.org> | 2017-07-07 17:03:42 +0000 |
|---|---|---|
| committer | Cy Schubert <cy@FreeBSD.org> | 2017-07-07 17:03:42 +0000 |
| commit | 33a9b234e7087f573ef08cd7318c6497ba08b439 (patch) | |
| tree | d0ea40ad3bf5463a3c55795977c71bcb7d781b4b /src/lib/krb5/rcache | |
Diffstat (limited to 'src/lib/krb5/rcache')
| -rw-r--r-- | src/lib/krb5/rcache/Makefile.in | 50 | ||||
| -rw-r--r-- | src/lib/krb5/rcache/README | 82 | ||||
| -rw-r--r-- | src/lib/krb5/rcache/RELEASE | 17 | ||||
| -rw-r--r-- | src/lib/krb5/rcache/deps | 98 | ||||
| -rw-r--r-- | src/lib/krb5/rcache/rc-int.h | 91 | ||||
| -rw-r--r-- | src/lib/krb5/rcache/rc_base.c | 181 | ||||
| -rw-r--r-- | src/lib/krb5/rcache/rc_base.h | 15 | ||||
| -rw-r--r-- | src/lib/krb5/rcache/rc_conv.c | 76 | ||||
| -rw-r--r-- | src/lib/krb5/rcache/rc_dfl.c | 851 | ||||
| -rw-r--r-- | src/lib/krb5/rcache/rc_dfl.h | 48 | ||||
| -rw-r--r-- | src/lib/krb5/rcache/rc_io.c | 522 | ||||
| -rw-r--r-- | src/lib/krb5/rcache/rc_io.h | 60 | ||||
| -rw-r--r-- | src/lib/krb5/rcache/rc_none.c | 96 | ||||
| -rw-r--r-- | src/lib/krb5/rcache/rcdef.c | 45 | ||||
| -rw-r--r-- | src/lib/krb5/rcache/rcfns.c | 95 | ||||
| -rw-r--r-- | src/lib/krb5/rcache/ser_rc.c | 212 | ||||
| -rw-r--r-- | src/lib/krb5/rcache/t_replay.c | 265 |
17 files changed, 2804 insertions, 0 deletions
diff --git a/src/lib/krb5/rcache/Makefile.in b/src/lib/krb5/rcache/Makefile.in new file mode 100644 index 000000000000..5b800c572fc3 --- /dev/null +++ b/src/lib/krb5/rcache/Makefile.in @@ -0,0 +1,50 @@ +mydir=lib$(S)krb5$(S)rcache +BUILDTOP=$(REL)..$(S)..$(S).. + +##DOS##BUILDTOP = ..\..\.. +##DOS##PREFIXDIR=rcache +##DOS##OBJFILE=..\$(OUTPRE)$(PREFIXDIR).lst + +STLIBOBJS = \ + rc_base.o \ + rc_dfl.o \ + rc_io.o \ + rcdef.o \ + rc_none.o \ + rc_conv.o \ + ser_rc.o \ + rcfns.o + +OBJS= \ + $(OUTPRE)rc_base.$(OBJEXT) \ + $(OUTPRE)rc_dfl.$(OBJEXT) \ + $(OUTPRE)rc_io.$(OBJEXT) \ + $(OUTPRE)rcdef.$(OBJEXT) \ + $(OUTPRE)rc_none.$(OBJEXT) \ + $(OUTPRE)rc_conv.$(OBJEXT) \ + $(OUTPRE)ser_rc.$(OBJEXT) \ + $(OUTPRE)rcfns.$(OBJEXT) + +SRCS= \ + $(srcdir)/rc_base.c \ + $(srcdir)/rc_dfl.c \ + $(srcdir)/rc_io.c \ + $(srcdir)/rcdef.c \ + $(srcdir)/rc_none.c \ + $(srcdir)/rc_conv.c \ + $(srcdir)/ser_rc.c \ + $(srcdir)/rcfns.c \ + $(srcdir)/t_replay.c + +##DOS##LIBOBJS = $(OBJS) + +all-unix: all-libobjs +clean-unix:: clean-libobjs + +T_REPLAY_OBJS= t_replay.o + +t_replay: $(T_REPLAY_OBJS) $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o t_replay $(T_REPLAY_OBJS) $(KRB5_BASE_LIBS) + +@libobj_frag@ + diff --git a/src/lib/krb5/rcache/README b/src/lib/krb5/rcache/README new file mode 100644 index 000000000000..13a45a1d869f --- /dev/null +++ b/src/lib/krb5/rcache/README @@ -0,0 +1,82 @@ +/* +Copyright 1990, Daniel J. Bernstein. All rights reserved. + +Please address any questions or comments to the author at brnstnd@acf10.nyu.edu. +*/ + +The #include's should be rewritten. + +All functions return 0 on success. + +Environment variables: KRB5RCACHETYPE, KRB5RCACHENAME, KRB5RCACHEDIR, +and TMPDIR. Obsolete: KRB5RCACHE. + +All header files are both ANSI-compatible and K&R-compatible. The .c files +are only ANSI compatible. Everything passes gcc -Wall -ansi -pedantic. + +Strings are freed using FREE(), which is defined in terms of free(). + +The error header files should be redone. + +The header files don't use __ because that's reserved. + +Each .c file assumes <malloc.h>. rc_io.c assumes fsync() and a gaggle of +error codes. These assumptions are not as portable as the code itself. + + +rcache.c: + +The rcache.c compatibility interface's type registration is a no-op; it +simply passes the type name on to rc_base.h. rcache.h is obsolete; use +rc_base.h if possible. + +There are some slight differences between rcache.c and the prototypes I +saw in krb/func-proto.h. Don't look at me, it's your interface. + +rcache.c's get_name doesn't fill with zeros unless strncpy does. + + +rc_base.c: + +It doesn't take linker magic to preregister types. Just change the +typehead initialization in rc_base.c, with an appropriate include file +setting the ops. + + +rc_dfl.c: + +If NOIOSTUFF is defined when rc_dfl.c is compiled, all dfl rcaches will +be per-process. This is untested. + +Provided that separate threads use separate rcaches, rc_dfl.c is safe +for multithreading. + +Getting the name of a cache is only valid after it is created and before +it is closed. Recovering a cache is only valid after it has been created. + +krb5_unparse_name had better produce a zero-terminated string. + +rc_dfl.c isn't smart enough to try expunge/retry upon a malloc error. +Then again, such an error indicates that the whole system's about to die; +without real memory management there's no good solution. + +HASHSIZE can be defined at compile time. It defaults to 997 in rc_dfl.c. +EXCESSREPS can be defined at compile time. It defaults to 30 in rc_dfl.c. + +Hopefully adding a deltat to a time to compare to another time cannot +overflow. + +In rc_dfl's struct dfl_data, the name field is never freed, even though +it may be malloced by io_creat on a generate-name call. This should not +be a problem: a single process should not be opening and closing many +rcaches. One fix would be another field to indicate whether the string +was malloced or not; normally this is an unstated characteristic of a +char pointer, but here it would have to be explicit. + + +rc_io.c: + +rc_io.c assumes that siginterrupt() is not set. If siginterrupt() is set +and a signal occurs during, say, close(), then the close will fail. + +On a machine without fsync() you might as well not use the disk at all. diff --git a/src/lib/krb5/rcache/RELEASE b/src/lib/krb5/rcache/RELEASE new file mode 100644 index 000000000000..21a4624726a6 --- /dev/null +++ b/src/lib/krb5/rcache/RELEASE @@ -0,0 +1,17 @@ +Text of Mr. Bernstein's release: + +"I" henceforth refers to Daniel J. Bernstein. + +"rcshar" henceforth refers to the attached document, as sent from Daniel +J. Bernstein to Project Athena on 11 March 1990 + +I am the author of and sole copyright holder upon rcshar. + +I hereby waive copyright upon rcshar. rcshar is hereby public domain. + +I hereby also waive copyright upon any works that are (1) derived from +rcshar and (2) prepared between 11 March 1990 and 1 January 1991. + +Daniel J. Bernstein + +<signature>, dated 7 July 1990 diff --git a/src/lib/krb5/rcache/deps b/src/lib/krb5/rcache/deps new file mode 100644 index 000000000000..0fe8808722c0 --- /dev/null +++ b/src/lib/krb5/rcache/deps @@ -0,0 +1,98 @@ +# +# Generated makefile dependencies follow. +# +rc_base.so rc_base.po $(OUTPRE)rc_base.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h rc-int.h rc_base.c \ + rc_base.h +rc_dfl.so rc_dfl.po $(OUTPRE)rc_dfl.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h rc-int.h rc_base.h \ + rc_dfl.c rc_dfl.h rc_io.h +rc_io.so rc_io.po $(OUTPRE)rc_io.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h rc_base.h rc_dfl.h \ + rc_io.c rc_io.h +rcdef.so rcdef.po $(OUTPRE)rcdef.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h rc-int.h rc_dfl.h \ + rcdef.c +rc_none.so rc_none.po $(OUTPRE)rc_none.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h rc-int.h rc_none.c +rc_conv.so rc_conv.po $(OUTPRE)rc_conv.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h rc_base.h rc_conv.c +ser_rc.so ser_rc.po $(OUTPRE)ser_rc.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h rc-int.h ser_rc.c +rcfns.so rcfns.po $(OUTPRE)rcfns.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h rc-int.h rcfns.c +t_replay.so t_replay.po $(OUTPRE)t_replay.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + t_replay.c diff --git a/src/lib/krb5/rcache/rc-int.h b/src/lib/krb5/rcache/rc-int.h new file mode 100644 index 000000000000..72a9483c02fe --- /dev/null +++ b/src/lib/krb5/rcache/rc-int.h @@ -0,0 +1,91 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/krb5/rcache/rc-int.h */ +/* + * Copyright 2004 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* This file contains constant and function declarations used in the + * file-based replay cache routines. */ + +#ifndef __KRB5_RCACHE_INT_H__ +#define __KRB5_RCACHE_INT_H__ + +int krb5int_rc_finish_init(void); + +void krb5int_rc_terminate(void); + +struct krb5_rc_st { + krb5_magic magic; + const struct _krb5_rc_ops *ops; + krb5_pointer data; + k5_mutex_t lock; +}; + +struct _krb5_rc_ops { + krb5_magic magic; + char *type; + krb5_error_code (KRB5_CALLCONV *init)( + krb5_context, + krb5_rcache, + krb5_deltat); /* create */ + krb5_error_code (KRB5_CALLCONV *recover)( + krb5_context, + krb5_rcache); /* open */ + krb5_error_code (KRB5_CALLCONV *recover_or_init)( + krb5_context, + krb5_rcache, + krb5_deltat); + krb5_error_code (KRB5_CALLCONV *destroy)( + krb5_context, + krb5_rcache); + krb5_error_code (KRB5_CALLCONV *close)( + krb5_context, + krb5_rcache); + krb5_error_code (KRB5_CALLCONV *store)( + krb5_context, + krb5_rcache, + krb5_donot_replay *); + krb5_error_code (KRB5_CALLCONV *expunge)( + krb5_context, + krb5_rcache); + krb5_error_code (KRB5_CALLCONV *get_span)( + krb5_context, + krb5_rcache, + krb5_deltat *); + char *(KRB5_CALLCONV *get_name)( + krb5_context, + krb5_rcache); + krb5_error_code (KRB5_CALLCONV *resolve)( + krb5_context, + krb5_rcache, + char *); +}; + +typedef struct _krb5_rc_ops krb5_rc_ops; + +krb5_error_code krb5_rc_register_type(krb5_context, const krb5_rc_ops *); + +extern const krb5_rc_ops krb5_rc_dfl_ops; +extern const krb5_rc_ops krb5_rc_none_ops; + +#endif /* __KRB5_RCACHE_INT_H__ */ diff --git a/src/lib/krb5/rcache/rc_base.c b/src/lib/krb5/rcache/rc_base.c new file mode 100644 index 000000000000..373ac3046305 --- /dev/null +++ b/src/lib/krb5/rcache/rc_base.c @@ -0,0 +1,181 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/krb5/rcache/rc_base.c */ +/* + * This file of the Kerberos V5 software is derived from public-domain code + * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>. + * + */ + +/* + * Base "glue" functions for the replay cache. + */ + +#include "rc_base.h" +#include "rc-int.h" +#include "k5-thread.h" + +struct krb5_rc_typelist { + const krb5_rc_ops *ops; + struct krb5_rc_typelist *next; +}; +static struct krb5_rc_typelist none = { &krb5_rc_none_ops, 0 }; +static struct krb5_rc_typelist krb5_rc_typelist_dfl = { &krb5_rc_dfl_ops, &none }; +static struct krb5_rc_typelist *typehead = &krb5_rc_typelist_dfl; +static k5_mutex_t rc_typelist_lock = K5_MUTEX_PARTIAL_INITIALIZER; + +int +krb5int_rc_finish_init(void) +{ + return k5_mutex_finish_init(&rc_typelist_lock); +} + +void +krb5int_rc_terminate(void) +{ + struct krb5_rc_typelist *t, *t_next; + k5_mutex_destroy(&rc_typelist_lock); + for (t = typehead; t != &krb5_rc_typelist_dfl; t = t_next) { + t_next = t->next; + free(t); + } +} + +krb5_error_code +krb5_rc_register_type(krb5_context context, const krb5_rc_ops *ops) +{ + struct krb5_rc_typelist *t; + + k5_mutex_lock(&rc_typelist_lock); + for (t = typehead;t && strcmp(t->ops->type,ops->type);t = t->next) + ; + if (t) { + k5_mutex_unlock(&rc_typelist_lock); + return KRB5_RC_TYPE_EXISTS; + } + t = (struct krb5_rc_typelist *) malloc(sizeof(struct krb5_rc_typelist)); + if (t == NULL) { + k5_mutex_unlock(&rc_typelist_lock); + return KRB5_RC_MALLOC; + } + t->next = typehead; + t->ops = ops; + typehead = t; + k5_mutex_unlock(&rc_typelist_lock); + return 0; +} + +krb5_error_code +krb5_rc_resolve_type(krb5_context context, krb5_rcache *idptr, + const char *type) +{ + struct krb5_rc_typelist *t; + krb5_error_code err; + krb5_rcache id; + + *idptr = NULL; + + /* Find the named type in the list. */ + k5_mutex_lock(&rc_typelist_lock); + for (t = typehead; t && strcmp(t->ops->type, type); t = t->next) + ; + k5_mutex_unlock(&rc_typelist_lock); + if (!t) + return KRB5_RC_TYPE_NOTFOUND; + + /* Create and return the rcache structure. */ + id = malloc(sizeof(*id)); + if (!id) + return KRB5_RC_MALLOC; + err = k5_mutex_init(&id->lock); + if (err) { + free(id); + return err; + } + id->data = NULL; /* Gets real data when resolved */ + id->magic = 0; /* Gets real magic after resolved */ + id->ops = t->ops; + *idptr = id; + return 0; +} + +char * krb5_rc_get_type(krb5_context context, krb5_rcache id) +{ + return id->ops->type; +} + +char * +krb5_rc_default_type(krb5_context context) +{ + char *s; + if ((s = getenv("KRB5RCACHETYPE"))) + return s; + else + return "dfl"; +} + +char * +krb5_rc_default_name(krb5_context context) +{ + char *s; + if ((s = getenv("KRB5RCACHENAME"))) + return s; + else + return (char *) 0; +} + +krb5_error_code +krb5_rc_default(krb5_context context, krb5_rcache *idptr) +{ + krb5_error_code retval; + krb5_rcache id; + + *idptr = NULL; + retval = krb5_rc_resolve_type(context, &id, krb5_rc_default_type(context)); + if (retval) + return retval; + retval = krb5_rc_resolve(context, id, krb5_rc_default_name(context)); + if (retval) { + k5_mutex_destroy(&id->lock); + free(id); + return retval; + } + id->magic = KV5M_RCACHE; + *idptr = id; + return retval; +} + + +krb5_error_code +krb5_rc_resolve_full(krb5_context context, krb5_rcache *idptr, + const char *string_name) +{ + char *type; + char *residual; + krb5_error_code retval; + unsigned int diff; + krb5_rcache id; + + *idptr = NULL; + + if (!(residual = strchr(string_name,':'))) + return KRB5_RC_PARSE; + + diff = residual - string_name; + if (!(type = malloc(diff + 1))) + return KRB5_RC_MALLOC; + (void) strncpy(type, string_name, diff); + type[residual - string_name] = '\0'; + + retval = krb5_rc_resolve_type(context, &id,type); + free(type); + if (retval) + return retval; + if ((retval = krb5_rc_resolve(context, id,residual + 1))) { + k5_mutex_destroy(&id->lock); + free(id); + return retval; + } + id->magic = KV5M_RCACHE; + *idptr = id; + return retval; +} diff --git a/src/lib/krb5/rcache/rc_base.h b/src/lib/krb5/rcache/rc_base.h new file mode 100644 index 000000000000..0530b90f8423 --- /dev/null +++ b/src/lib/krb5/rcache/rc_base.h @@ -0,0 +1,15 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/krb5/rcache/rc_base.h */ +/* + * This file of the Kerberos V5 software is derived from public-domain code + * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>. + * + */ + +#ifndef KRB5_RC_H +#define KRB5_RC_H +#include "k5-int.h" + +/* all the stuff that was here is now in rcache.h, included by krb5/krb5.h */ + +#endif diff --git a/src/lib/krb5/rcache/rc_conv.c b/src/lib/krb5/rcache/rc_conv.c new file mode 100644 index 000000000000..0e021f5d8e42 --- /dev/null +++ b/src/lib/krb5/rcache/rc_conv.c @@ -0,0 +1,76 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/krb5/rcache/rc_conv.c */ +/* + * This file of the Kerberos V5 software is derived from public-domain code + * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>. + * + */ + +/* + * An implementation for the default replay cache type. + */ + +#include "rc_base.h" + +/* + Local stuff: + krb5_auth_to_replay(context, krb5_tkt_authent *auth,krb5_donot_replay *rep) + given auth, take important information and make rep; return -1 if failed +*/ + +krb5_error_code +krb5_auth_to_rep(krb5_context context, krb5_tkt_authent *auth, krb5_donot_replay *rep) +{ + krb5_error_code retval; + rep->cusec = auth->authenticator->cusec; + rep->ctime = auth->authenticator->ctime; + if ((retval = krb5_unparse_name(context, auth->ticket->server, &rep->server))) + return retval; /* shouldn't happen */ + if ((retval = krb5_unparse_name(context, auth->authenticator->client, + &rep->client))) { + free(rep->server); + return retval; /* shouldn't happen. */ + } + return 0; +} + +/* + * Generate a printable hash value for a message for use in a replay + * record. It is not necessary for this hash function to be + * collision-proof (the only thing you can do with a second preimage + * is produce a false replay error) but for fine granularity replay detection + * it is necessary for the function to be consistent across implementations. + * When two implementations sharing a single replay cache don't agree on hash + * function, the code falls back to legacy replay detection based on + * (client, server, timestamp, usec) tuples. We do an unkeyed + * SHA256 hash of the message and convert it into uppercase hex + * representation. + */ +krb5_error_code +krb5_rc_hash_message(krb5_context context, const krb5_data *message, + char **out) +{ + krb5_error_code retval; + uint8_t cksum[K5_SHA256_HASHLEN]; + char *hash, *ptr; + unsigned int i; + + *out = NULL; + + /* Calculate the binary checksum. */ + retval = k5_sha256(message, cksum); + if (retval) + return retval; + + /* Convert the checksum into printable form. */ + hash = malloc(K5_SHA256_HASHLEN * 2 + 1); + if (!hash) { + return KRB5_RC_MALLOC; + } + + for (i = 0, ptr = hash; i < K5_SHA256_HASHLEN; i++, ptr += 2) + snprintf(ptr, 3, "%02X", cksum[i]); + *ptr = '\0'; + *out = hash; + return 0; +} diff --git a/src/lib/krb5/rcache/rc_dfl.c b/src/lib/krb5/rcache/rc_dfl.c new file mode 100644 index 000000000000..c4d2c744da40 --- /dev/null +++ b/src/lib/krb5/rcache/rc_dfl.c @@ -0,0 +1,851 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/krb5/rcache/rc_dfl.c */ +/* + * This file of the Kerberos V5 software is derived from public-domain code + * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>. + * + */ + +/* + * An implementation for the default replay cache type. + */ +#include "k5-int.h" +#include "rc_base.h" +#include "rc_dfl.h" +#include "rc_io.h" +#include "rc-int.h" + +/* + * If NOIOSTUFF is defined at compile time, dfl rcaches will be per-process. + */ + +/* + Local stuff: + + static int hash(krb5_donot_replay *rep, int hsize) + returns hash value of *rep, between 0 and hsize - 1 + HASHSIZE + size of hash table (constant), can be preset + static int cmp(krb5_donot_replay *old, krb5_donot_replay *new, krb5_deltat t) + compare old and new; return CMP_REPLAY or CMP_HOHUM + static int alive(krb5_context, krb5_donot_replay *new, krb5_deltat t) + see if new is still alive; return CMP_EXPIRED or CMP_HOHUM + CMP_MALLOC, CMP_EXPIRED, CMP_REPLAY, CMP_HOHUM + return codes from cmp(), alive(), and store() + struct dfl_data + data stored in this cache type, namely "dfl" + struct authlist + multilinked list of reps + static int rc_store(context, krb5_rcache id, krb5_donot_replay *rep) + store rep in cache id; return CMP_REPLAY if replay, else CMP_MALLOC/CMP_HOHUM + +*/ + +#ifndef HASHSIZE +#define HASHSIZE 997 /* a convenient prime */ +#endif + +#ifndef EXCESSREPS +#define EXCESSREPS 30 +#endif + +/* + * The rcache will be automatically expunged when the number of + * expired krb5_donot_replays encountered incidentally in searching + * exceeds the number of live krb5_donot_replays by EXCESSREPS. With + * the defaults here, a typical cache might build up some 10K of + * expired krb5_donot_replays before an automatic expunge, with the + * waste basically independent of the number of stores per minute. + * + * The rcache will also automatically be expunged when it encounters + * more than EXCESSREPS expired entries when recovering a cache in + * dfl_recover. + */ + +static unsigned int +hash(krb5_donot_replay *rep, unsigned int hsize) +{ + unsigned int h = rep->cusec + rep->ctime; + h += *rep->server; + h += *rep->client; + return h % hsize; +} + +#define CMP_MALLOC -3 +#define CMP_EXPIRED -2 +#define CMP_REPLAY -1 +#define CMP_HOHUM 0 + +/*ARGSUSED*/ +static int +cmp(krb5_donot_replay *old, krb5_donot_replay *new1, krb5_deltat t) +{ + if ((old->cusec == new1->cusec) && /* most likely to distinguish */ + (old->ctime == new1->ctime) && + (strcmp(old->client, new1->client) == 0) && + (strcmp(old->server, new1->server) == 0)) { /* always true */ + /* If both records include message hashes, compare them as well. */ + if (old->msghash == NULL || new1->msghash == NULL || + strcmp(old->msghash, new1->msghash) == 0) + return CMP_REPLAY; + } + return CMP_HOHUM; +} + +static int +alive(krb5_int32 mytime, krb5_donot_replay *new1, krb5_deltat t) +{ + if (mytime == 0) + return CMP_HOHUM; /* who cares? */ + /* I hope we don't have to worry about overflow */ + if (new1->ctime + t < mytime) + return CMP_EXPIRED; + return CMP_HOHUM; +} + +struct dfl_data +{ + char *name; + krb5_deltat lifespan; + unsigned int hsize; + int numhits; + int nummisses; + struct authlist **h; + struct authlist *a; +#ifndef NOIOSTUFF + krb5_rc_iostuff d; +#endif + char recovering; +}; + +struct authlist +{ + krb5_donot_replay rep; + struct authlist *na; + struct authlist *nh; +}; + +/* of course, list is backwards from file */ +/* hash could be forwards since we have to search on match, but naaaah */ + +static int +rc_store(krb5_context context, krb5_rcache id, krb5_donot_replay *rep, + krb5_int32 now, krb5_boolean fromfile) +{ + struct dfl_data *t = (struct dfl_data *)id->data; + unsigned int rephash; + struct authlist *ta; + + rephash = hash(rep, t->hsize); + + for (ta = t->h[rephash]; ta; ta = ta->nh) { + switch(cmp(&ta->rep, rep, t->lifespan)) + { + case CMP_REPLAY: + if (fromfile) { + /* + * This is an expected collision between a hash + * extension record and a normal-format record. Make + * sure the message hash is included in the stored + * record and carry on. + */ + if (!ta->rep.msghash && rep->msghash) { + if (!(ta->rep.msghash = strdup(rep->msghash))) + return CMP_MALLOC; + } + return CMP_HOHUM; + } else + return CMP_REPLAY; + case CMP_HOHUM: + if (alive(now, &ta->rep, t->lifespan) == CMP_EXPIRED) + t->nummisses++; + else + t->numhits++; + break; + default: + ; /* wtf? */ + } + } + + if (!(ta = (struct authlist *) malloc(sizeof(struct authlist)))) + return CMP_MALLOC; + ta->rep = *rep; + ta->rep.client = ta->rep.server = ta->rep.msghash = NULL; + if (!(ta->rep.client = strdup(rep->client))) + goto error; + if (!(ta->rep.server = strdup(rep->server))) + goto error; + if (rep->msghash && !(ta->rep.msghash = strdup(rep->msghash))) + goto error; + ta->na = t->a; t->a = ta; + ta->nh = t->h[rephash]; t->h[rephash] = ta; + return CMP_HOHUM; +error: + if (ta->rep.client) + free(ta->rep.client); + if (ta->rep.server) + free(ta->rep.server); + if (ta->rep.msghash) + free(ta->rep.msghash); + free(ta); + return CMP_MALLOC; +} + +char * KRB5_CALLCONV +krb5_rc_dfl_get_name(krb5_context context, krb5_rcache id) +{ + return ((struct dfl_data *) (id->data))->name; +} + +krb5_error_code KRB5_CALLCONV +krb5_rc_dfl_get_span(krb5_context context, krb5_rcache id, + krb5_deltat *lifespan) +{ + struct dfl_data *t; + + k5_mutex_lock(&id->lock); + t = (struct dfl_data *) id->data; + *lifespan = t->lifespan; + k5_mutex_unlock(&id->lock); + return 0; +} + +static krb5_error_code KRB5_CALLCONV +krb5_rc_dfl_init_locked(krb5_context context, krb5_rcache id, krb5_deltat lifespan) +{ + struct dfl_data *t = (struct dfl_data *)id->data; + krb5_error_code retval; + + t->lifespan = lifespan ? lifespan : context->clockskew; + /* default to clockskew from the context */ +#ifndef NOIOSTUFF + if ((retval = krb5_rc_io_creat(context, &t->d, &t->name))) { + return retval; + } + if ((krb5_rc_io_write(context, &t->d, + (krb5_pointer) &t->lifespan, sizeof(t->lifespan)) + || krb5_rc_io_sync(context, &t->d))) { + return KRB5_RC_IO; + } +#endif + return 0; +} + +krb5_error_code KRB5_CALLCONV +krb5_rc_dfl_init(krb5_context context, krb5_rcache id, krb5_deltat lifespan) +{ + krb5_error_code retval; + + k5_mutex_lock(&id->lock); + retval = krb5_rc_dfl_init_locked(context, id, lifespan); + k5_mutex_unlock(&id->lock); + return retval; +} + +/* Called with the mutex already locked. */ +krb5_error_code +krb5_rc_dfl_close_no_free(krb5_context context, krb5_rcache id) +{ + struct dfl_data *t = (struct dfl_data *)id->data; + struct authlist *q; + + free(t->h); + if (t->name) + free(t->name); + while ((q = t->a)) + { + t->a = q->na; + free(q->rep.client); + free(q->rep.server); + if (q->rep.msghash) + free(q->rep.msghash); + free(q); + } +#ifndef NOIOSTUFF + (void) krb5_rc_io_close(context, &t->d); +#endif + free(t); + return 0; +} + +krb5_error_code KRB5_CALLCONV +krb5_rc_dfl_close(krb5_context context, krb5_rcache id) +{ + k5_mutex_lock(&id->lock); + krb5_rc_dfl_close_no_free(context, id); + k5_mutex_unlock(&id->lock); + k5_mutex_destroy(&id->lock); + free(id); + return 0; +} + +krb5_error_code KRB5_CALLCONV +krb5_rc_dfl_destroy(krb5_context context, krb5_rcache id) +{ +#ifndef NOIOSTUFF + if (krb5_rc_io_destroy(context, &((struct dfl_data *) (id->data))->d)) + return KRB5_RC_IO; +#endif + return krb5_rc_dfl_close(context, id); +} + +krb5_error_code KRB5_CALLCONV +krb5_rc_dfl_resolve(krb5_context context, krb5_rcache id, char *name) +{ + struct dfl_data *t = 0; + krb5_error_code retval; + + /* allocate id? no */ + if (!(t = (struct dfl_data *) calloc(1, sizeof(struct dfl_data)))) + return KRB5_RC_MALLOC; + id->data = (krb5_pointer) t; + if (name) { + t->name = strdup(name); + if (!t->name) { + retval = KRB5_RC_MALLOC; + goto cleanup; + } + } else + t->name = 0; + t->numhits = t->nummisses = 0; + t->hsize = HASHSIZE; /* no need to store---it's memory-only */ + t->h = (struct authlist **) malloc(t->hsize*sizeof(struct authlist *)); + if (!t->h) { + retval = KRB5_RC_MALLOC; + goto cleanup; + } + memset(t->h, 0, t->hsize*sizeof(struct authlist *)); + t->a = (struct authlist *) 0; +#ifndef NOIOSTUFF + t->d.fd = -1; +#endif + t->recovering = 0; + return 0; + +cleanup: + if (t) { + if (t->name) + free(t->name); + if (t->h) + free(t->h); + free(t); + } + return retval; +} + +void +krb5_rc_free_entry(krb5_context context, krb5_donot_replay **rep) +{ + krb5_donot_replay *rp = *rep; + + *rep = NULL; + if (rp) + { + if (rp->client) + free(rp->client); + if (rp->server) + free(rp->server); + if (rp->msghash) + free(rp->msghash); + rp->client = NULL; + rp->server = NULL; + rp->msghash = NULL; + free(rp); + } +} + +/* + * Parse a string in the format <len>:<data>, with the length + * represented in ASCII decimal. On parse failure, return 0 but set + * *result to NULL. + */ +static krb5_error_code +parse_counted_string(char **strptr, char **result) +{ + char *str = *strptr, *end; + unsigned long len; + + *result = NULL; + + /* Parse the length, expecting a ':' afterwards. */ + errno = 0; + len = strtoul(str, &end, 10); + if (errno != 0 || *end != ':' || len > strlen(end + 1)) + return 0; + + /* Allocate space for *result and copy the data. */ + *result = malloc(len + 1); + if (!*result) + return KRB5_RC_MALLOC; + memcpy(*result, end + 1, len); + (*result)[len] = '\0'; + *strptr = end + 1 + len; + return 0; +} + +/* + * Hash extension records have the format: + * client = <empty string> + * server = SHA256:<msghash> <clientlen>:<client> <serverlen>:<server> + * Spaces in the client and server string are represented with + * with backslashes. Client and server lengths are represented in + * ASCII decimal (which is different from the 32-bit binary we use + * elsewhere in the replay cache). + * + * On parse failure, we leave the record unmodified. + */ +static krb5_error_code +check_hash_extension(krb5_donot_replay *rep) +{ + char *msghash = NULL, *client = NULL, *server = NULL, *str, *end; + krb5_error_code retval = 0; + + /* Check if this appears to match the hash extension format. */ + if (*rep->client) + return 0; + if (strncmp(rep->server, "SHA256:", 7) != 0) + return 0; + + /* Parse out the message hash. */ + str = rep->server + 7; + end = strchr(str, ' '); + if (!end) + return 0; + msghash = k5memdup0(str, end - str, &retval); + if (!msghash) + return KRB5_RC_MALLOC; + str = end + 1; + + /* Parse out the client and server. */ + retval = parse_counted_string(&str, &client); + if (retval != 0 || client == NULL) + goto error; + if (*str != ' ') + goto error; + str++; + retval = parse_counted_string(&str, &server); + if (retval != 0 || server == NULL) + goto error; + if (*str) + goto error; + + free(rep->client); + free(rep->server); + rep->client = client; + rep->server = server; + rep->msghash = msghash; + return 0; + +error: + if (msghash) + free(msghash); + if (client) + free(client); + if (server) + free(server); + return retval; +} + +static krb5_error_code +krb5_rc_io_fetch(krb5_context context, struct dfl_data *t, + krb5_donot_replay *rep, int maxlen) +{ + int len2; + unsigned int len; + krb5_error_code retval; + + rep->client = rep->server = rep->msghash = NULL; + + retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) &len2, + sizeof(len2)); + if (retval) + return retval; + + if ((len2 <= 0) || (len2 >= maxlen)) + return KRB5_RC_IO_EOF; + + len = len2; + rep->client = malloc (len); + if (!rep->client) + return KRB5_RC_MALLOC; + + retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) rep->client, len); + if (retval) + goto errout; + + retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) &len2, + sizeof(len2)); + if (retval) + goto errout; + + if ((len2 <= 0) || (len2 >= maxlen)) { + retval = KRB5_RC_IO_EOF; + goto errout; + } + len = len2; + + rep->server = malloc (len); + if (!rep->server) { + retval = KRB5_RC_MALLOC; + goto errout; + } + + retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) rep->server, len); + if (retval) + goto errout; + + retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) &rep->cusec, + sizeof(rep->cusec)); + if (retval) + goto errout; + + retval = krb5_rc_io_read(context, &t->d, (krb5_pointer) &rep->ctime, + sizeof(rep->ctime)); + if (retval) + goto errout; + + retval = check_hash_extension(rep); + if (retval) + goto errout; + + return 0; + +errout: + if (rep->client) + free(rep->client); + if (rep->server) + free(rep->server); + if (rep->msghash) + free(rep->msghash); + rep->client = rep->server = 0; + return retval; +} + + +static krb5_error_code +krb5_rc_dfl_expunge_locked(krb5_context context, krb5_rcache id); + +static krb5_error_code +krb5_rc_dfl_recover_locked(krb5_context context, krb5_rcache id) +{ +#ifdef NOIOSTUFF + return KRB5_RC_NOIO; +#else + + struct dfl_data *t = (struct dfl_data *)id->data; + krb5_donot_replay *rep = 0; + krb5_error_code retval; + long max_size; + int expired_entries = 0; + krb5_int32 now; + + if ((retval = krb5_rc_io_open(context, &t->d, t->name))) { + return retval; + } + + t->recovering = 1; + + max_size = krb5_rc_io_size(context, &t->d); + + rep = NULL; + if (krb5_rc_io_read(context, &t->d, (krb5_pointer) &t->lifespan, + sizeof(t->lifespan))) { + retval = KRB5_RC_IO; + goto io_fail; + } + + if (!(rep = (krb5_donot_replay *) malloc(sizeof(krb5_donot_replay)))) { + retval = KRB5_RC_MALLOC; + goto io_fail; + } + rep->client = rep->server = rep->msghash = NULL; + + if (krb5_timeofday(context, &now)) + now = 0; + + /* now read in each auth_replay and insert into table */ + for (;;) { + if (krb5_rc_io_mark(context, &t->d)) { + retval = KRB5_RC_IO; + goto io_fail; + } + + retval = krb5_rc_io_fetch(context, t, rep, (int) max_size); + + if (retval == KRB5_RC_IO_EOF) + break; + else if (retval != 0) + goto io_fail; + + if (alive(now, rep, t->lifespan) != CMP_EXPIRED) { + if (rc_store(context, id, rep, now, TRUE) == CMP_MALLOC) { + retval = KRB5_RC_MALLOC; goto io_fail; + } + } else { + expired_entries++; + } + + /* + * free fields allocated by rc_io_fetch + */ + free(rep->server); + free(rep->client); + if (rep->msghash) + free(rep->msghash); + rep->client = rep->server = rep->msghash = NULL; + } + retval = 0; + krb5_rc_io_unmark(context, &t->d); + /* + * An automatic expunge here could remove the need for + * mark/unmark but that would be inefficient. + */ +io_fail: + krb5_rc_free_entry(context, &rep); + if (retval) + krb5_rc_io_close(context, &t->d); + else if (expired_entries > EXCESSREPS) + retval = krb5_rc_dfl_expunge_locked(context, id); + t->recovering = 0; + return retval; + +#endif +} + +krb5_error_code KRB5_CALLCONV +krb5_rc_dfl_recover(krb5_context context, krb5_rcache id) +{ + krb5_error_code ret; + + k5_mutex_lock(&id->lock); + ret = krb5_rc_dfl_recover_locked(context, id); + k5_mutex_unlock(&id->lock); + return ret; +} + +krb5_error_code KRB5_CALLCONV +krb5_rc_dfl_recover_or_init(krb5_context context, krb5_rcache id, + krb5_deltat lifespan) +{ + krb5_error_code retval; + + k5_mutex_lock(&id->lock); + retval = krb5_rc_dfl_recover_locked(context, id); + if (retval) + retval = krb5_rc_dfl_init_locked(context, id, lifespan); + k5_mutex_unlock(&id->lock); + return retval; +} + +static krb5_error_code +krb5_rc_io_store(krb5_context context, struct dfl_data *t, + krb5_donot_replay *rep) +{ + size_t clientlen, serverlen; + unsigned int len; + krb5_error_code ret; + struct k5buf buf, extbuf; + char *extstr; + + clientlen = strlen(rep->client); + serverlen = strlen(rep->server); + + if (rep->msghash) { + /* + * Write a hash extension record, to be followed by a record + * in regular format (without the message hash) for the + * benefit of old implementations. + */ + + /* Format the extension value so we know its length. */ + k5_buf_init_dynamic(&extbuf); + k5_buf_add_fmt(&extbuf, "SHA256:%s %lu:%s %lu:%s", rep->msghash, + (unsigned long)clientlen, rep->client, + (unsigned long)serverlen, rep->server); + if (k5_buf_status(&extbuf) != 0) + return KRB5_RC_MALLOC; + extstr = extbuf.data; + + /* + * Put the extension value into the server field of a + * regular-format record, with an empty client field. + */ + k5_buf_init_dynamic(&buf); + len = 1; + k5_buf_add_len(&buf, (char *)&len, sizeof(len)); + k5_buf_add_len(&buf, "", 1); + len = strlen(extstr) + 1; + k5_buf_add_len(&buf, (char *)&len, sizeof(len)); + k5_buf_add_len(&buf, extstr, len); + k5_buf_add_len(&buf, (char *)&rep->cusec, sizeof(rep->cusec)); + k5_buf_add_len(&buf, (char *)&rep->ctime, sizeof(rep->ctime)); + free(extstr); + } else /* No extension record needed. */ + k5_buf_init_dynamic(&buf); + + len = clientlen + 1; + k5_buf_add_len(&buf, (char *)&len, sizeof(len)); + k5_buf_add_len(&buf, rep->client, len); + len = serverlen + 1; + k5_buf_add_len(&buf, (char *)&len, sizeof(len)); + k5_buf_add_len(&buf, rep->server, len); + k5_buf_add_len(&buf, (char *)&rep->cusec, sizeof(rep->cusec)); + k5_buf_add_len(&buf, (char *)&rep->ctime, sizeof(rep->ctime)); + + if (k5_buf_status(&buf) != 0) + return KRB5_RC_MALLOC; + + ret = krb5_rc_io_write(context, &t->d, buf.data, buf.len); + k5_buf_free(&buf); + return ret; +} + +static krb5_error_code krb5_rc_dfl_expunge_locked(krb5_context, krb5_rcache); + +krb5_error_code KRB5_CALLCONV +krb5_rc_dfl_store(krb5_context context, krb5_rcache id, krb5_donot_replay *rep) +{ + krb5_error_code ret; + struct dfl_data *t; + krb5_int32 now; + + ret = krb5_timeofday(context, &now); + if (ret) + return ret; + + k5_mutex_lock(&id->lock); + + switch(rc_store(context, id, rep, now, FALSE)) { + case CMP_MALLOC: + k5_mutex_unlock(&id->lock); + return KRB5_RC_MALLOC; + case CMP_REPLAY: + k5_mutex_unlock(&id->lock); + return KRB5KRB_AP_ERR_REPEAT; + case 0: break; + default: /* wtf? */ ; + } + t = (struct dfl_data *)id->data; +#ifndef NOIOSTUFF + ret = krb5_rc_io_store(context, t, rep); + if (ret) { + k5_mutex_unlock(&id->lock); + return ret; + } +#endif + /* Shall we automatically expunge? */ + if (t->nummisses > t->numhits + EXCESSREPS) + { + ret = krb5_rc_dfl_expunge_locked(context, id); + k5_mutex_unlock(&id->lock); + return ret; + } +#ifndef NOIOSTUFF + else + { + if (krb5_rc_io_sync(context, &t->d)) { + k5_mutex_unlock(&id->lock); + return KRB5_RC_IO; + } + } +#endif + k5_mutex_unlock(&id->lock); + return 0; +} + +static krb5_error_code +krb5_rc_dfl_expunge_locked(krb5_context context, krb5_rcache id) +{ + struct dfl_data *t = (struct dfl_data *)id->data; +#ifdef NOIOSTUFF + unsigned int i; + struct authlist **q; + struct authlist **qt; + struct authlist *r; + struct authlist *rt; + krb5_int32 now; + + if (krb5_timestamp(context, &now)) + now = 0; + + for (q = &t->a; *q; q = qt) { + qt = &(*q)->na; + if (alive(now, &(*q)->rep, t->lifespan) == CMP_EXPIRED) { + free((*q)->rep.client); + free((*q)->rep.server); + if ((*q)->rep.msghash) + free((*q)->rep.msghash); + free(*q); + *q = *qt; /* why doesn't this feel right? */ + } + } + for (i = 0; i < t->hsize; i++) + t->h[i] = (struct authlist *) 0; + for (r = t->a; r; r = r->na) { + i = hash(&r->rep, t->hsize); + rt = t->h[i]; + t->h[i] = r; + r->nh = rt; + } + return 0; +#else + struct authlist *q; + char *name; + krb5_error_code retval = 0; + krb5_rcache tmp; + krb5_deltat lifespan = t->lifespan; /* save original lifespan */ + + if (! t->recovering) { + name = t->name; + t->name = 0; /* Clear name so it isn't freed */ + (void) krb5_rc_dfl_close_no_free(context, id); + retval = krb5_rc_dfl_resolve(context, id, name); + free(name); + if (retval) + return retval; + retval = krb5_rc_dfl_recover_locked(context, id); + if (retval) + return retval; + t = (struct dfl_data *)id->data; /* point to recovered cache */ + } + + retval = krb5_rc_resolve_type(context, &tmp, "dfl"); + if (retval) + return retval; + retval = krb5_rc_resolve(context, tmp, 0); + if (retval) + goto cleanup; + retval = krb5_rc_initialize(context, tmp, lifespan); + if (retval) + goto cleanup; + for (q = t->a; q; q = q->na) { + if (krb5_rc_io_store(context, (struct dfl_data *)tmp->data, &q->rep)) { + retval = KRB5_RC_IO; + goto cleanup; + } + } + /* NOTE: We set retval in case we have an error */ + retval = KRB5_RC_IO; + if (krb5_rc_io_sync(context, &((struct dfl_data *)tmp->data)->d)) + goto cleanup; + if (krb5_rc_io_sync(context, &t->d)) + goto cleanup; + if (krb5_rc_io_move(context, &t->d, &((struct dfl_data *)tmp->data)->d)) + goto cleanup; + retval = 0; +cleanup: + (void) krb5_rc_dfl_close(context, tmp); + return retval; +#endif +} + +krb5_error_code KRB5_CALLCONV +krb5_rc_dfl_expunge(krb5_context context, krb5_rcache id) +{ + krb5_error_code ret; + + k5_mutex_lock(&id->lock); + ret = krb5_rc_dfl_expunge_locked(context, id); + k5_mutex_unlock(&id->lock); + return ret; +} diff --git a/src/lib/krb5/rcache/rc_dfl.h b/src/lib/krb5/rcache/rc_dfl.h new file mode 100644 index 000000000000..ad036a207e3d --- /dev/null +++ b/src/lib/krb5/rcache/rc_dfl.h @@ -0,0 +1,48 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/krb5/rcache/rc_dfl.h */ +/* + * This file of the Kerberos V5 software is derived from public-domain code + * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>. + * + */ + +/* + * Declarations for the default replay cache implementation. + */ + +#ifndef KRB5_RC_DFL_H +#define KRB5_RC_DFL_H + +krb5_error_code KRB5_CALLCONV +krb5_rc_dfl_init(krb5_context, krb5_rcache, krb5_deltat); + +krb5_error_code KRB5_CALLCONV +krb5_rc_dfl_recover(krb5_context, krb5_rcache); + +krb5_error_code KRB5_CALLCONV +krb5_rc_dfl_recover_or_init(krb5_context, krb5_rcache, krb5_deltat); + +krb5_error_code KRB5_CALLCONV +krb5_rc_dfl_destroy(krb5_context, krb5_rcache); + +krb5_error_code KRB5_CALLCONV +krb5_rc_dfl_close(krb5_context, krb5_rcache); + +krb5_error_code KRB5_CALLCONV +krb5_rc_dfl_store(krb5_context, krb5_rcache, krb5_donot_replay *); + +krb5_error_code KRB5_CALLCONV +krb5_rc_dfl_expunge(krb5_context, krb5_rcache); + +krb5_error_code KRB5_CALLCONV +krb5_rc_dfl_get_span(krb5_context, krb5_rcache, krb5_deltat *); + +char * KRB5_CALLCONV +krb5_rc_dfl_get_name(krb5_context, krb5_rcache); + +krb5_error_code KRB5_CALLCONV +krb5_rc_dfl_resolve(krb5_context, krb5_rcache, char *); + +krb5_error_code krb5_rc_dfl_close_no_free(krb5_context, krb5_rcache); +void krb5_rc_free_entry(krb5_context, krb5_donot_replay **); +#endif diff --git a/src/lib/krb5/rcache/rc_io.c b/src/lib/krb5/rcache/rc_io.c new file mode 100644 index 000000000000..b9859fe9f657 --- /dev/null +++ b/src/lib/krb5/rcache/rc_io.c @@ -0,0 +1,522 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/krb5/rcache/rc_io.c */ +/* + * This file of the Kerberos V5 software is derived from public-domain code + * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>. + * + */ + +/* + * I/O functions for the replay cache default implementation. + */ + +#if defined(_WIN32) +# define PATH_SEPARATOR "\\" +#else +# define PATH_SEPARATOR "/" +#endif + +#define KRB5_RC_VNO 0x0501 /* krb5, rcache v 1 */ + +#if HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#include "k5-int.h" +#include <stdio.h> /* for P_tmpdir */ +#include "rc_base.h" +#include "rc_dfl.h" +#include "rc_io.h" + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#ifdef HAVE_NETINET_IN_H +#if !defined(_WINSOCKAPI_) +#include <netinet/in.h> +#endif +#else +#error find some way to use net-byte-order file version numbers. +#endif + +#define UNIQUE getpid() /* hopefully unique number */ + +#define GETDIR (dir = getdir(), dirlen = strlen(dir) + sizeof(PATH_SEPARATOR) - 1) + +static char * +getdir(void) +{ + char *dir; + + if (!(dir = getenv("KRB5RCACHEDIR"))) { +#if defined(_WIN32) + if (!(dir = getenv("TEMP"))) + if (!(dir = getenv("TMP"))) + dir = "C:"; +#else + if (!(dir = getenv("TMPDIR"))) { +#ifdef RCTMPDIR + dir = RCTMPDIR; +#else + dir = "/tmp"; +#endif + } +#endif + } + return dir; +} + +/* + * Called from krb5_rc_io_creat(); calls mkstemp() and does some + * sanity checking on the file modes in case some broken mkstemp() + * implementation creates the file with overly permissive modes. To + * avoid race conditions, do not fchmod() a file for which mkstemp set + * incorrect modes. + */ +static krb5_error_code +krb5_rc_io_mkstemp(krb5_context context, krb5_rc_iostuff *d, char *dir) +{ + krb5_error_code retval = 0; +#if HAVE_SYS_STAT_H + struct stat stbuf; + + memset(&stbuf, 0, sizeof(stbuf)); +#endif + if (asprintf(&d->fn, "%s%skrb5_RCXXXXXX", + dir, PATH_SEPARATOR) < 0) { + d->fn = NULL; + return KRB5_RC_IO_MALLOC; + } + d->fd = mkstemp(d->fn); + if (d->fd == -1) { + /* + * This return value is deliberate because d->fd == -1 causes + * caller to go into errno interpretation code. + */ + return 0; + } +#if HAVE_SYS_STAT_H + /* + * Be paranoid and check that mkstemp made the file accessible + * only to the user. + */ + retval = fstat(d->fd, &stbuf); + if (retval) { + k5_setmsg(context, retval, + _("Cannot fstat replay cache file %s: %s"), + d->fn, strerror(errno)); + return KRB5_RC_IO_UNKNOWN; + } + if (stbuf.st_mode & 077) { + k5_setmsg(context, retval, + _("Insecure mkstemp() file mode for replay cache file %s; " + "try running this program with umask 077"), d->fn); + return KRB5_RC_IO_UNKNOWN; + } +#endif + return 0; +} + +#if 0 +static krb5_error_code rc_map_errno (int) __attribute__((cold)); +#endif + +static krb5_error_code +rc_map_errno (krb5_context context, int e, const char *fn, + const char *operation) +{ + switch (e) { + case EFBIG: +#ifdef EDQUOT + case EDQUOT: +#endif + case ENOSPC: + return KRB5_RC_IO_SPACE; + + case EIO: + return KRB5_RC_IO_IO; + + case EPERM: + case EACCES: + case EROFS: + case EEXIST: + k5_setmsg(context, KRB5_RC_IO_PERM, + _("Cannot %s replay cache file %s: %s"), + operation, fn, strerror(e)); + return KRB5_RC_IO_PERM; + + default: + k5_setmsg(context, KRB5_RC_IO_UNKNOWN, _("Cannot %s replay cache: %s"), + operation, strerror(e)); + return KRB5_RC_IO_UNKNOWN; + } +} + + +krb5_error_code +krb5_rc_io_creat(krb5_context context, krb5_rc_iostuff *d, char **fn) +{ + krb5_int16 rc_vno = htons(KRB5_RC_VNO); + krb5_error_code retval = 0; + int flags, do_not_unlink = 0; + char *dir; + size_t dirlen; + + GETDIR; + if (fn && *fn) { + if (asprintf(&d->fn, "%s%s%s", dir, PATH_SEPARATOR, *fn) < 0) + return KRB5_RC_IO_MALLOC; + d->fd = -1; + do { + if (unlink(d->fn) == -1 && errno != ENOENT) + break; + flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_BINARY; + d->fd = THREEPARAMOPEN(d->fn, flags, 0600); + } while (d->fd == -1 && errno == EEXIST); + } else { + retval = krb5_rc_io_mkstemp(context, d, dir); + if (retval) + goto cleanup; + if (d->fd != -1 && fn) { + *fn = strdup(d->fn + dirlen); + if (*fn == NULL) { + free(d->fn); + return KRB5_RC_IO_MALLOC; + } + } + } + if (d->fd == -1) { + retval = rc_map_errno(context, errno, d->fn, "create"); + if (retval == KRB5_RC_IO_PERM) + do_not_unlink = 1; + goto cleanup; + } + set_cloexec_fd(d->fd); + retval = krb5_rc_io_write(context, d, (krb5_pointer)&rc_vno, + sizeof(rc_vno)); + if (retval) + goto cleanup; + + retval = krb5_rc_io_sync(context, d); + +cleanup: + if (retval) { + if (d->fn) { + if (!do_not_unlink) + (void) unlink(d->fn); + free(d->fn); + d->fn = NULL; + } + if (d->fd != -1) { + (void) close(d->fd); + } + } + return retval; +} + +static krb5_error_code +krb5_rc_io_open_internal(krb5_context context, krb5_rc_iostuff *d, char *fn, + char* full_pathname) +{ + krb5_int16 rc_vno; + krb5_error_code retval = 0; + int do_not_unlink = 1; +#ifndef NO_USERID + struct stat sb1, sb2; +#endif + char *dir; + + dir = getdir(); + if (full_pathname) { + if (!(d->fn = strdup(full_pathname))) + return KRB5_RC_IO_MALLOC; + } else { + if (asprintf(&d->fn, "%s%s%s", dir, PATH_SEPARATOR, fn) < 0) + return KRB5_RC_IO_MALLOC; + } + +#ifdef NO_USERID + d->fd = THREEPARAMOPEN(d->fn, O_RDWR | O_BINARY, 0600); + if (d->fd == -1) { + retval = rc_map_errno(context, errno, d->fn, "open"); + goto cleanup; + } +#else + d->fd = -1; + retval = lstat(d->fn, &sb1); + if (retval != 0) { + retval = rc_map_errno(context, errno, d->fn, "lstat"); + goto cleanup; + } + d->fd = THREEPARAMOPEN(d->fn, O_RDWR | O_BINARY, 0600); + if (d->fd < 0) { + retval = rc_map_errno(context, errno, d->fn, "open"); + goto cleanup; + } + retval = fstat(d->fd, &sb2); + if (retval < 0) { + retval = rc_map_errno(context, errno, d->fn, "fstat"); + goto cleanup; + } + /* check if someone was playing with symlinks */ + if ((sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) + || (sb1.st_mode & S_IFMT) != S_IFREG) + { + retval = KRB5_RC_IO_PERM; + k5_setmsg(context, retval, "rcache not a file %s", d->fn); + goto cleanup; + } + /* check that non other can read/write/execute the file */ + if (sb1.st_mode & 077) { + k5_setmsg(context, retval, + _("Insecure file mode for replay cache file %s"), d->fn); + return KRB5_RC_IO_UNKNOWN; + } + /* owned by me */ + if (sb1.st_uid != geteuid()) { + retval = KRB5_RC_IO_PERM; + k5_setmsg(context, retval, _("rcache not owned by %d"), + (int)geteuid()); + goto cleanup; + } +#endif + set_cloexec_fd(d->fd); + + do_not_unlink = 0; + retval = krb5_rc_io_read(context, d, (krb5_pointer) &rc_vno, + sizeof(rc_vno)); + if (retval) + goto cleanup; + + if (ntohs(rc_vno) != KRB5_RC_VNO) + retval = KRB5_RCACHE_BADVNO; + +cleanup: + if (retval) { + if (!do_not_unlink) + (void) unlink(d->fn); + free(d->fn); + d->fn = NULL; + if (d->fd >= 0) + (void) close(d->fd); + } + return retval; +} + +krb5_error_code +krb5_rc_io_open(krb5_context context, krb5_rc_iostuff *d, char *fn) +{ + return krb5_rc_io_open_internal(context, d, fn, NULL); +} + +krb5_error_code +krb5_rc_io_move(krb5_context context, krb5_rc_iostuff *new1, + krb5_rc_iostuff *old) +{ +#if defined(_WIN32) || defined(__CYGWIN__) + char *new_fn = NULL; + char *old_fn = NULL; + off_t offset = 0; + krb5_error_code retval = 0; + /* + * Initial work around provided by Tom Sanfilippo to work around + * poor Windows emulation of POSIX functions. Rename and dup has + * different semantics! + * + * Additional fixes and explanation provided by dalmeida@mit.edu: + * + * First, we save the offset of "old". Then, we close and remove + * the "new" file so we can do the rename. We also close "old" to + * make sure the rename succeeds (though that might not be + * necessary on some systems). + * + * Next, we do the rename. If all goes well, we seek the "new" + * file to the position "old" was at. + * + * --- WARNING!!! --- + * + * Since "old" is now gone, we mourn its disappearance, but we + * cannot emulate that Unix behavior... THIS BEHAVIOR IS + * DIFFERENT FROM UNIX. However, it is ok because this function + * gets called such that "old" gets closed right afterwards. + */ + offset = lseek(old->fd, 0, SEEK_CUR); + + new_fn = new1->fn; + new1->fn = NULL; + close(new1->fd); + new1->fd = -1; + + unlink(new_fn); + + old_fn = old->fn; + old->fn = NULL; + close(old->fd); + old->fd = -1; + + if (rename(old_fn, new_fn) == -1) { /* MUST be atomic! */ + retval = KRB5_RC_IO_UNKNOWN; + goto cleanup; + } + + retval = krb5_rc_io_open_internal(context, new1, 0, new_fn); + if (retval) + goto cleanup; + + if (lseek(new1->fd, offset, SEEK_SET) == -1) { + retval = KRB5_RC_IO_UNKNOWN; + goto cleanup; + } + +cleanup: + free(new_fn); + free(old_fn); + return retval; +#else + char *fn = NULL; + if (rename(old->fn, new1->fn) == -1) /* MUST be atomic! */ + return KRB5_RC_IO_UNKNOWN; + fn = new1->fn; + new1->fn = NULL; /* avoid clobbering */ + (void) krb5_rc_io_close(context, new1); + new1->fn = fn; + new1->fd = dup(old->fd); + set_cloexec_fd(new1->fd); + return 0; +#endif +} + +krb5_error_code +krb5_rc_io_write(krb5_context context, krb5_rc_iostuff *d, krb5_pointer buf, + unsigned int num) +{ + if (write(d->fd, (char *) buf, num) == -1) + switch(errno) + { +#ifdef EDQUOT + case EDQUOT: +#endif + case EFBIG: + case ENOSPC: + k5_setmsg(context, KRB5_RC_IO_SPACE, + _("Can't write to replay cache: %s"), strerror(errno)); + return KRB5_RC_IO_SPACE; + case EIO: + k5_setmsg(context, KRB5_RC_IO_IO, + _("Can't write to replay cache: %s"), strerror(errno)); + return KRB5_RC_IO_IO; + case EBADF: + default: + k5_setmsg(context, KRB5_RC_IO_UNKNOWN, + _("Can't write to replay cache: %s"), strerror(errno)); + return KRB5_RC_IO_UNKNOWN; + } + return 0; +} + +krb5_error_code +krb5_rc_io_sync(krb5_context context, krb5_rc_iostuff *d) +{ +#if defined(_WIN32) +#ifndef fsync +#define fsync _commit +#endif +#endif + if (fsync(d->fd) == -1) { + switch(errno) + { + case EBADF: return KRB5_RC_IO_UNKNOWN; + case EIO: return KRB5_RC_IO_IO; + default: + k5_setmsg(context, KRB5_RC_IO_UNKNOWN, + _("Cannot sync replay cache file: %s"), strerror(errno)); + return KRB5_RC_IO_UNKNOWN; + } + } + return 0; +} + +krb5_error_code +krb5_rc_io_read(krb5_context context, krb5_rc_iostuff *d, krb5_pointer buf, + unsigned int num) +{ + int count; + if ((count = read(d->fd, (char *) buf, num)) == -1) + switch(errno) + { + case EIO: return KRB5_RC_IO_IO; + case EBADF: + default: + k5_setmsg(context, KRB5_RC_IO_UNKNOWN, + _("Can't read from replay cache: %s"), strerror(errno)); + return KRB5_RC_IO_UNKNOWN; + } + if (count < 0 || (unsigned int)count != num) + return KRB5_RC_IO_EOF; + return 0; +} + +krb5_error_code +krb5_rc_io_close(krb5_context context, krb5_rc_iostuff *d) +{ + if (d->fn != NULL) { + free(d->fn); + d->fn = NULL; + } + if (d->fd != -1) { + if (close(d->fd) == -1) /* can't happen */ + return KRB5_RC_IO_UNKNOWN; + d->fd = -1; + } + return 0; +} + +krb5_error_code +krb5_rc_io_destroy(krb5_context context, krb5_rc_iostuff *d) +{ + if (unlink(d->fn) == -1) + switch(errno) + { + case EIO: + k5_setmsg(context, KRB5_RC_IO_IO, + _("Can't destroy replay cache: %s"), strerror(errno)); + return KRB5_RC_IO_IO; + case EPERM: + case EBUSY: + case EROFS: + k5_setmsg(context, KRB5_RC_IO_PERM, + _("Can't destroy replay cache: %s"), strerror(errno)); + return KRB5_RC_IO_PERM; + case EBADF: + default: + k5_setmsg(context, KRB5_RC_IO_UNKNOWN, + _("Can't destroy replay cache: %s"), strerror(errno)); + return KRB5_RC_IO_UNKNOWN; + } + return 0; +} + +krb5_error_code +krb5_rc_io_mark(krb5_context context, krb5_rc_iostuff *d) +{ + d->mark = lseek(d->fd, (off_t) 0, SEEK_CUR); /* can't fail */ + return 0; +} + +krb5_error_code +krb5_rc_io_unmark(krb5_context context, krb5_rc_iostuff *d) +{ + (void) lseek(d->fd, d->mark, SEEK_SET); /* if it fails, tough luck */ + return 0; +} + +long +krb5_rc_io_size(krb5_context context, krb5_rc_iostuff *d) +{ + struct stat statb; + + if (fstat(d->fd, &statb) == 0) + return statb.st_size; + else + return 0; +} diff --git a/src/lib/krb5/rcache/rc_io.h b/src/lib/krb5/rcache/rc_io.h new file mode 100644 index 000000000000..f5ab23903822 --- /dev/null +++ b/src/lib/krb5/rcache/rc_io.h @@ -0,0 +1,60 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/krb5/rcache/rc_io.h */ +/* + * This file of the Kerberos V5 software is derived from public-domain code + * contributed by Daniel J. Bernstein, <brnstnd@acf10.nyu.edu>. + * + */ + +/* + * Declarations for the I/O sub-package of the replay cache + */ + +#ifndef KRB5_RC_IO_H +#define KRB5_RC_IO_H + +typedef struct krb5_rc_iostuff { + int fd; +#ifdef MSDOS_FILESYSTEM + long mark; +#else + off_t mark; /* on newer systems, should be pos_t */ +#endif + char *fn; +} krb5_rc_iostuff; + +/* first argument is always iostuff for result file */ + +krb5_error_code +krb5_rc_io_creat(krb5_context, krb5_rc_iostuff *, char **); + +krb5_error_code +krb5_rc_io_open(krb5_context, krb5_rc_iostuff *, char *); + +krb5_error_code +krb5_rc_io_move(krb5_context, krb5_rc_iostuff *, krb5_rc_iostuff *); + +krb5_error_code +krb5_rc_io_write(krb5_context, krb5_rc_iostuff *, krb5_pointer, unsigned int); + +krb5_error_code +krb5_rc_io_read(krb5_context, krb5_rc_iostuff *, krb5_pointer, unsigned int); + +krb5_error_code +krb5_rc_io_close(krb5_context, krb5_rc_iostuff *); + +krb5_error_code +krb5_rc_io_destroy(krb5_context, krb5_rc_iostuff *); + +krb5_error_code +krb5_rc_io_mark(krb5_context, krb5_rc_iostuff *); + +krb5_error_code +krb5_rc_io_unmark(krb5_context, krb5_rc_iostuff *); + +krb5_error_code +krb5_rc_io_sync(krb5_context, krb5_rc_iostuff *); + +long +krb5_rc_io_size(krb5_context, krb5_rc_iostuff *); +#endif diff --git a/src/lib/krb5/rcache/rc_none.c b/src/lib/krb5/rcache/rc_none.c new file mode 100644 index 000000000000..e30aed09f135 --- /dev/null +++ b/src/lib/krb5/rcache/rc_none.c @@ -0,0 +1,96 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/krb5/rcache/rc_none.c */ +/* + * Copyright 2004 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* + * + * replay cache no-op implementation + */ + +#include "k5-int.h" +#include "rc-int.h" + +static krb5_error_code KRB5_CALLCONV +krb5_rc_none_init(krb5_context ctx, krb5_rcache rc, krb5_deltat d) +{ + return 0; +} +#define krb5_rc_none_recover_or_init krb5_rc_none_init + +static krb5_error_code KRB5_CALLCONV +krb5_rc_none_noargs(krb5_context ctx, krb5_rcache rc) +{ + return 0; +} +#define krb5_rc_none_recover krb5_rc_none_noargs +#define krb5_rc_none_expunge krb5_rc_none_noargs + +static krb5_error_code KRB5_CALLCONV +krb5_rc_none_close(krb5_context ctx, krb5_rcache rc) +{ + free (rc); + return 0; +} +#define krb5_rc_none_destroy krb5_rc_none_close + +static krb5_error_code KRB5_CALLCONV +krb5_rc_none_store(krb5_context ctx, krb5_rcache rc, krb5_donot_replay *r) +{ + return 0; +} + +static krb5_error_code KRB5_CALLCONV +krb5_rc_none_get_span(krb5_context ctx, krb5_rcache rc, krb5_deltat *d) +{ + return 0; +} + +static char * KRB5_CALLCONV +krb5_rc_none_get_name(krb5_context ctx, krb5_rcache rc) +{ + return ""; +} + +static krb5_error_code KRB5_CALLCONV +krb5_rc_none_resolve(krb5_context ctx, krb5_rcache rc, char *name) +{ + rc->data = "none"; + return 0; +} + +const krb5_rc_ops krb5_rc_none_ops = { + 0, + "none", + krb5_rc_none_init, + krb5_rc_none_recover, + krb5_rc_none_recover_or_init, + krb5_rc_none_destroy, + krb5_rc_none_close, + krb5_rc_none_store, + krb5_rc_none_expunge, + krb5_rc_none_get_span, + krb5_rc_none_get_name, + krb5_rc_none_resolve +}; diff --git a/src/lib/krb5/rcache/rcdef.c b/src/lib/krb5/rcache/rcdef.c new file mode 100644 index 000000000000..01d166bec280 --- /dev/null +++ b/src/lib/krb5/rcache/rcdef.c @@ -0,0 +1,45 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/krb5/rcache/rcdef.c - Default replay cache operations vector */ +/* + * Copyright 1990 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "rc-int.h" +#include "rc_dfl.h" + +const krb5_rc_ops krb5_rc_dfl_ops = +{ + 0, + "dfl", + krb5_rc_dfl_init, + krb5_rc_dfl_recover, + krb5_rc_dfl_recover_or_init, + krb5_rc_dfl_destroy, + krb5_rc_dfl_close, + krb5_rc_dfl_store, + krb5_rc_dfl_expunge, + krb5_rc_dfl_get_span, + krb5_rc_dfl_get_name, + krb5_rc_dfl_resolve +}; diff --git a/src/lib/krb5/rcache/rcfns.c b/src/lib/krb5/rcache/rcfns.c new file mode 100644 index 000000000000..b12864ab51d6 --- /dev/null +++ b/src/lib/krb5/rcache/rcfns.c @@ -0,0 +1,95 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/krb5/rcache/rcfns.c */ +/* + * Copyright 2001 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* + * Dispatch methods for replay cache code. + */ + +#include "k5-int.h" +#include "rc-int.h" + +krb5_error_code KRB5_CALLCONV +krb5_rc_initialize (krb5_context context, krb5_rcache id, krb5_deltat span) +{ + return krb5_x(id->ops->init,(context, id, span)); +} + +krb5_error_code KRB5_CALLCONV +krb5_rc_recover_or_initialize (krb5_context context, krb5_rcache id, + krb5_deltat span) +{ + return krb5_x(id->ops->recover_or_init,(context, id, span)); +} + +krb5_error_code KRB5_CALLCONV +krb5_rc_recover (krb5_context context, krb5_rcache id) +{ + return krb5_x((id)->ops->recover,(context, id)); +} + +krb5_error_code KRB5_CALLCONV +krb5_rc_destroy (krb5_context context, krb5_rcache id) +{ + return krb5_x((id)->ops->destroy,(context, id)); +} + +krb5_error_code KRB5_CALLCONV +krb5_rc_close (krb5_context context, krb5_rcache id) +{ + return krb5_x((id)->ops->close,(context, id)); +} + +krb5_error_code KRB5_CALLCONV +krb5_rc_store (krb5_context context, krb5_rcache id, + krb5_donot_replay *dontreplay) +{ + return krb5_x((id)->ops->store,(context, id, dontreplay)); +} + +krb5_error_code KRB5_CALLCONV +krb5_rc_expunge (krb5_context context, krb5_rcache id) +{ + return krb5_x((id)->ops->expunge,(context, id)); +} + +krb5_error_code KRB5_CALLCONV +krb5_rc_get_lifespan (krb5_context context, krb5_rcache id, + krb5_deltat *spanp) +{ + return krb5_x((id)->ops->get_span,(context, id, spanp)); +} + +char *KRB5_CALLCONV +krb5_rc_get_name (krb5_context context, krb5_rcache id) +{ + return krb5_xc((id)->ops->get_name,(context, id)); +} + +krb5_error_code KRB5_CALLCONV +krb5_rc_resolve (krb5_context context, krb5_rcache id, char *name) +{ + return krb5_x((id)->ops->resolve,(context, id, name)); +} diff --git a/src/lib/krb5/rcache/ser_rc.c b/src/lib/krb5/rcache/ser_rc.c new file mode 100644 index 000000000000..556af21e5e48 --- /dev/null +++ b/src/lib/krb5/rcache/ser_rc.c @@ -0,0 +1,212 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/krb5/rcache/ser_rc.c - Serialize replay cache context */ +/* + * Copyright 1995 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "rc-int.h" + +/* + * Routines to deal with externalizing krb5_rcache. + * krb5_rcache_size(); + * krb5_rcache_externalize(); + * krb5_rcache_internalize(); + */ +static krb5_error_code +krb5_rcache_size(krb5_context, krb5_pointer, size_t *); + +static krb5_error_code +krb5_rcache_externalize(krb5_context, krb5_pointer, krb5_octet **, size_t *); + +static krb5_error_code +krb5_rcache_internalize(krb5_context,krb5_pointer *, krb5_octet **, size_t *); + +/* + * Serialization entry for this type. + */ +static const krb5_ser_entry krb5_rcache_ser_entry = { + KV5M_RCACHE, /* Type */ + krb5_rcache_size, /* Sizer routine */ + krb5_rcache_externalize, /* Externalize routine */ + krb5_rcache_internalize /* Internalize routine */ +}; + +/* + * krb5_rcache_size() - Determine the size required to externalize + * this krb5_rcache variant. + */ +static krb5_error_code +krb5_rcache_size(krb5_context kcontext, krb5_pointer arg, size_t *sizep) +{ + krb5_error_code kret; + krb5_rcache rcache; + size_t required; + + kret = EINVAL; + if ((rcache = (krb5_rcache) arg)) { + /* + * Saving FILE: variants of krb5_rcache requires at minimum: + * krb5_int32 for KV5M_RCACHE + * krb5_int32 for length of rcache name. + * krb5_int32 for KV5M_RCACHE + */ + required = sizeof(krb5_int32) * 3; + if (rcache->ops && rcache->ops->type) + required += (strlen(rcache->ops->type)+1); + + /* + * The rcache name is formed as follows: + * <type>:<name> + */ + required += strlen(krb5_rc_get_name(kcontext, rcache)); + + kret = 0; + *sizep += required; + } + return(kret); +} + +/* + * krb5_rcache_externalize() - Externalize the krb5_rcache. + */ +static krb5_error_code +krb5_rcache_externalize(krb5_context kcontext, krb5_pointer arg, krb5_octet **buffer, size_t *lenremain) +{ + krb5_error_code kret; + krb5_rcache rcache; + size_t required; + krb5_octet *bp; + size_t remain; + char *rcname; + char *fnamep; + + required = 0; + bp = *buffer; + remain = *lenremain; + kret = EINVAL; + if ((rcache = (krb5_rcache) arg)) { + kret = ENOMEM; + if (!krb5_rcache_size(kcontext, arg, &required) && + (required <= remain)) { + /* Our identifier */ + (void) krb5_ser_pack_int32(KV5M_RCACHE, &bp, &remain); + + fnamep = krb5_rc_get_name(kcontext, rcache); + + if (rcache->ops->type) { + if (asprintf(&rcname, "%s:%s", rcache->ops->type, fnamep) < 0) + rcname = NULL; + } else + rcname = strdup(fnamep); + + if (rcname) { + /* Put the length of the file name */ + (void) krb5_ser_pack_int32((krb5_int32) strlen(rcname), + &bp, &remain); + + /* Put the name */ + (void) krb5_ser_pack_bytes((krb5_octet *) rcname, + strlen(rcname), + &bp, &remain); + + /* Put the trailer */ + (void) krb5_ser_pack_int32(KV5M_RCACHE, &bp, &remain); + kret = 0; + *buffer = bp; + *lenremain = remain; + free(rcname); + } + } + } + return(kret); +} + +/* + * krb5_rcache_internalize() - Internalize the krb5_rcache. + */ +static krb5_error_code +krb5_rcache_internalize(krb5_context kcontext, krb5_pointer *argp, krb5_octet **buffer, size_t *lenremain) +{ + krb5_error_code kret; + krb5_rcache rcache = NULL; + krb5_int32 ibuf; + krb5_octet *bp; + size_t remain; + char *rcname = NULL; + + bp = *buffer; + remain = *lenremain; + + /* Read our magic number */ + if (krb5_ser_unpack_int32(&ibuf, &bp, &remain) || ibuf != KV5M_RCACHE) + return EINVAL; + + /* Get the length of the rcache name */ + kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain); + if (kret) + return kret; + + /* Get the rcache name. */ + rcname = malloc(ibuf + 1); + if (!rcname) + return ENOMEM; + kret = krb5_ser_unpack_bytes((krb5_octet*)rcname, (size_t) ibuf, + &bp, &remain); + if (kret) + goto cleanup; + rcname[ibuf] = '\0'; + + /* Resolve and recover the rcache. */ + kret = krb5_rc_resolve_full(kcontext, &rcache, rcname); + if (kret) + goto cleanup; + krb5_rc_recover(kcontext, rcache); + + /* Read our magic number again. */ + kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain); + if (kret) + goto cleanup; + if (ibuf != KV5M_RCACHE) { + kret = EINVAL; + goto cleanup; + } + + *buffer = bp; + *lenremain = remain; + *argp = (krb5_pointer) rcache; +cleanup: + free(rcname); + if (kret != 0 && rcache) + krb5_rc_close(kcontext, rcache); + return kret; +} + +/* + * Register the rcache serializer. + */ +krb5_error_code KRB5_CALLCONV +krb5_ser_rcache_init(krb5_context kcontext) +{ + return(krb5_register_serializer(kcontext, &krb5_rcache_ser_entry)); +} diff --git a/src/lib/krb5/rcache/t_replay.c b/src/lib/krb5/rcache/t_replay.c new file mode 100644 index 000000000000..db273ec2f221 --- /dev/null +++ b/src/lib/krb5/rcache/t_replay.c @@ -0,0 +1,265 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/krb5/rcache/t_replay.c - Test harness for replay cache */ +/* + * Copyright (C) 2009 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" + +static void +usage(const char *progname) +{ + fprintf(stderr, "%s: Usage:\n", progname); + fprintf(stderr, " %s dump <filename>\n", progname); + fprintf(stderr, " %s store <rc> <cli> <srv> <msg> <tstamp> <usec>" + " <now> <now-usec>\n", progname); + fprintf(stderr, " %s expunge <rc> <now> <now-usec>\n", progname); + exit(1); +} + +static char * +read_counted_string(FILE *fp) +{ + unsigned int len; + char *str; + + if (fread(&len, sizeof(len), 1, fp) != 1) + return NULL; + if (len == 0 || len > 10000) + return NULL; + if ((str = malloc(len)) == NULL) + return NULL; + if (fread(str, 1, len, fp) != len) + return NULL; + if (str[len - 1] != 0) + return NULL; + return str; +} + +static void +dump_rcache(const char *filename) +{ + FILE *fp; + krb5_deltat lifespan; + krb5_int16 vno; + char *str; + krb5_int32 usec; + krb5_timestamp timestamp; + + fp = fopen(filename, "r"); + if (!fp) { + fprintf(stderr, "Can't open filename: %s\n", strerror(errno)); + return; + } + if (fread(&vno, sizeof(vno), 1, fp) != 1) + return; + if (fread(&lifespan, sizeof(lifespan), 1, fp) != 1) + return; + printf("Lifespan: %ld\n", (long) lifespan); + while (1) { + printf("---\n"); + + if (!(str = read_counted_string(fp))) + return; + printf("Client: %s\n", str); + free(str); + + if (!(str = read_counted_string(fp))) + return; + printf("Server: %s\n", str); + free(str); + + if (fread(&usec, sizeof(usec), 1, fp) != 1) + return; + printf("Microseconds: %ld\n", (long) usec); + + if (fread(×tamp, sizeof(timestamp), 1, fp) != 1) + return; + printf("Timestamp: %ld\n", (long) timestamp); + } +} + +static void +store(krb5_context ctx, char *rcspec, char *client, char *server, char *msg, + krb5_timestamp timestamp, krb5_int32 usec, krb5_timestamp now_timestamp, + krb5_int32 now_usec) +{ + krb5_rcache rc = NULL; + krb5_error_code retval = 0; + char *hash = NULL; + krb5_donot_replay rep; + krb5_data d; + + if (now_timestamp > 0) + krb5_set_debugging_time(ctx, now_timestamp, now_usec); + if ((retval = krb5_rc_resolve_full(ctx, &rc, rcspec))) + goto cleanup; + if ((retval = krb5_rc_recover_or_initialize(ctx, rc, ctx->clockskew))) + goto cleanup; + if (msg) { + d.data = msg; + d.length = strlen(msg); + if ((retval = krb5_rc_hash_message(ctx, &d, &hash))) + goto cleanup; + } + rep.client = client; + rep.server = server; + rep.msghash = hash; + rep.cusec = usec; + rep.ctime = timestamp; + retval = krb5_rc_store(ctx, rc, &rep); +cleanup: + if (retval == KRB5KRB_AP_ERR_REPEAT) + printf("Replay\n"); + else if (!retval) + printf("Entry successfully stored\n"); + else + fprintf(stderr, "Failure: %s\n", krb5_get_error_message(ctx, retval)); + if (rc) + krb5_rc_close(ctx, rc); + if (hash) + free(hash); +} + +static void +expunge(krb5_context ctx, char *rcspec, krb5_timestamp now_timestamp, + krb5_int32 now_usec) +{ + krb5_rcache rc = NULL; + krb5_error_code retval = 0; + + if (now_timestamp > 0) + krb5_set_debugging_time(ctx, now_timestamp, now_usec); + if ((retval = krb5_rc_resolve_full(ctx, &rc, rcspec))) + goto cleanup; + if ((retval = krb5_rc_recover_or_initialize(ctx, rc, ctx->clockskew))) + goto cleanup; + retval = krb5_rc_expunge(ctx, rc); +cleanup: + if (!retval) + printf("Cache successfully expunged\n"); + else + fprintf(stderr, "Failure: %s\n", krb5_get_error_message(ctx, retval)); + if (rc) + krb5_rc_close(ctx, rc); +} + +int +main(int argc, char **argv) +{ + krb5_context ctx; + krb5_error_code retval; + const char *progname; + + retval = krb5_init_context(&ctx); + if (retval) { + fprintf(stderr, "krb5_init_context returned error %ld\n", + (long) retval); + exit(1); + } + progname = argv[0]; + + /* Parse arguments. */ + argc--; argv++; + while (argc) { + if (strcmp(*argv, "dump") == 0) { + /* + * Without going through the rcache interface, dump a + * named dfl-format rcache file to stdout. Takes a full + * pathname argument. + */ + const char *filename; + + argc--; argv++; + if (!argc) usage(progname); + filename = *argv; + dump_rcache(filename); + } else if (strcmp(*argv, "store") == 0) { + /* + * Using the rcache interface, store a replay record. + * Takes an rcache spec like dfl:host as the first + * argument. If non-empty, the "msg" argument will be + * hashed and provided in the replay record. The + * now-timestamp argument can be 0 to use the current + * time. + */ + char *rcspec, *client, *server, *msg; + krb5_timestamp timestamp, now_timestamp; + krb5_int32 usec, now_usec; + + argc--; argv++; + if (!argc) usage(progname); + rcspec = *argv; + argc--; argv++; + if (!argc) usage(progname); + client = *argv; + argc--; argv++; + if (!argc) usage(progname); + server = *argv; + argc--; argv++; + if (!argc) usage(progname); + msg = (**argv) ? *argv : NULL; + argc--; argv++; + if (!argc) usage(progname); + timestamp = (krb5_timestamp) atol(*argv); + argc--; argv++; + if (!argc) usage(progname); + usec = (krb5_int32) atol(*argv); + argc--; argv++; + if (!argc) usage(progname); + now_timestamp = (krb5_timestamp) atol(*argv); + argc--; argv++; + if (!argc) usage(progname); + now_usec = (krb5_int32) atol(*argv); + + store(ctx, rcspec, client, server, msg, timestamp, usec, + now_timestamp, now_usec); + } else if (strcmp(*argv, "expunge") == 0) { + /* + * Using the rcache interface, expunge a replay cache. + * The now-timestamp argument can be 0 to use the current + * time. + */ + char *rcspec; + krb5_timestamp now_timestamp; + krb5_int32 now_usec; + + argc--; argv++; + if (!argc) usage(progname); + rcspec = *argv; + argc--; argv++; + if (!argc) usage(progname); + now_timestamp = (krb5_timestamp) atol(*argv); + argc--; argv++; + if (!argc) usage(progname); + now_usec = (krb5_int32) atol(*argv); + expunge(ctx, rcspec, now_timestamp, now_usec); + } else + usage(progname); + argc--; argv++; + } + + krb5_free_context(ctx); + + return 0; +} |
