Index: krb5/src/Makefile.in diff -u krb5/src/Makefile.in:1.1.1.4 krb5/src/Makefile.in:1.5 --- krb5/src/Makefile.in:1.1.1.4 Tue Nov 8 15:54:22 2005 +++ krb5/src/Makefile.in Tue Nov 8 17:32:43 2005 @@ -3,7 +3,7 @@ thisconfigdir=. myfulldir=. mydir=. -SUBDIRS=util include lib @krb524@ kdc kadmin slave clients appl tests \ +SUBDIRS=util include lib @krb524@ kdc kadmin slave replication clients appl tests \ config-files gen-manpages BUILDTOP=$(REL)$(C) LOCALINCLUDES = -I$(srcdir) Index: krb5/src/aclocal.m4 diff -u krb5/src/aclocal.m4:1.1.1.6 krb5/src/aclocal.m4:1.8 --- krb5/src/aclocal.m4:1.1.1.6 Tue Nov 8 15:54:31 2005 +++ krb5/src/aclocal.m4 Tue Nov 8 17:32:43 2005 @@ -55,6 +55,7 @@ AC_ARG_VAR(LD,[linker command [CC]]) AC_SUBST(LDFLAGS) dnl WITH_KRB4 dnl +WITH_UMICH_CHANGES dnl KRB5_AC_CHOOSE_ET dnl KRB5_AC_CHOOSE_SS dnl KRB5_AC_CHOOSE_DB dnl @@ -1754,3 +1757,40 @@ UTIL_LIB=-lutil])dnl AC_SUBST(UTIL_LIB) ]) +dnl +dnl begin UMICH additions +dnl Do these by default, but allow them to be turned +dnl off using --disable-* or --enable-*=no +dnl +define(WITH_UMICH_CHANGES, [ + AC_ARG_ENABLE([replication], + [ --enable-replication enable University of Michigan's replication of database entries -- vs. using propagation], + [case "$enable_replication" in + yes | no) ;; + *) enable_replication=yes ;; + esac], enable_replication=yes) + if test "$enable_replication" = yes ; then + AC_DEFINE(UMICH_REPLICATION,1,[Define if UMICH replication code is desired]) + fi + + AC_ARG_ENABLE([umich], + [ --enable-umich enable University of Michigan changes], + [case "$enable_umich" in + yes | no) ;; + *) enable_umich=yes ;; + esac], enable_umich=yes) + if test "$enable_umich" = yes ; then + AC_DEFINE(UMICH,1,[Define if all UMICH changes are desired]) + AC_DEFINE(KINIT_DEFAULT_BOTH,1,[Define if kinit should attempt to get both K4 and K5 by default]) + AC_PREFIX_DEFAULT(/usr/krb5) + fi + + AC_CHECK_LIB(crypt, crypt, + [ CRYPT_LIB="-lcrypt" ], + [ CRYPT_LIB="" ], + ) + +])dnl +dnl +dnl end UMICH additions +dnl Index: krb5/src/configure.in diff -u krb5/src/configure.in:1.1.1.4 krb5/src/configure.in:1.5 --- krb5/src/configure.in:1.1.1.4 Tue Nov 8 15:54:24 2005 +++ krb5/src/configure.in Tue Nov 8 17:32:44 2005 @@ -185,4 +185,4 @@ AC_CONFIG_SUBDIRS(kadmin clients appl tests) AC_CONFIG_FILES(krb5-config, [chmod +x krb5-config]) -V5_AC_OUTPUT_MAKEFILE(. util util/support util/send-pr lib kdc slave krb524 config-files gen-manpages) +V5_AC_OUTPUT_MAKEFILE(. util util/support util/send-pr lib kdc slave replication krb524 config-files gen-manpages) Index: krb5/src/include/krb5/stock/osconf.h diff -u krb5/src/include/krb5/stock/osconf.h:1.1.1.4 krb5/src/include/krb5/stock/osconf.h:1.5 --- krb5/src/include/krb5/stock/osconf.h:1.1.1.4 Tue Nov 8 16:10:55 2005 +++ krb5/src/include/krb5/stock/osconf.h Tue Nov 8 17:32:53 2005 @@ -124,4 +124,16 @@ #define KPROPD_DEFAULT_KRB_DB DEFAULT_KDB_FILE #define KPROPD_ACL_FILE "@LOCALSTATEDIR/krb5kdc/kpropd.acl" +#if defined(UMICH_REPLICATION) +/* + * krb5 incremental replication support follows + */ + +#define KREP_DEFAULT_FILE "@LOCALSTATEDIR/krb5kdc/master_delta" +#define KREPD_DEFAULT_FILE "@LOCALSTATEDIR/krb5kdc/slave_delta" +#define KREPD_ACL_FILE "@LOCALSTATEDIR/krb5kdc/kpropd.acl" +#define KREPD_DEFAULT_KRB_DB DEFAULT_KDB_FILE + +#endif /* UMICH_REPLICATION */ + #endif /* KRB5_OSCONF__ */ Index: krb5/src/lib/kadm5/kadm_err.et diff -u krb5/src/lib/kadm5/kadm_err.et:1.1.1.2 krb5/src/lib/kadm5/kadm_err.et:1.5 --- krb5/src/lib/kadm5/kadm_err.et:1.1.1.2 Mon Jul 21 16:36:01 2003 +++ krb5/src/lib/kadm5/kadm_err.et Mon Dec 15 16:02:35 2003 @@ -59,4 +59,7 @@ error_code KADM5_SETV4KEY_INVAL_ENCTYPE, "Invalid enctype for setv4key" error_code KADM5_SETKEY3_ETYPE_MISMATCH, "Mismatched enctypes for setkey3" error_code KADM5_MISSING_KRB5_CONF_PARAMS, "Missing parameters in krb5.conf required for kadmin client" +# #if defined(UMICH_REPLICATION) +error_code KADM5_UMICHREP_INIT_ERR, "Could not initialize to enable replication" +# #endif /* UMICH_REPLICATION */ end Index: krb5/src/lib/kadm5/srv/Makefile.in diff -u krb5/src/lib/kadm5/srv/Makefile.in:1.1.1.5 krb5/src/lib/kadm5/srv/Makefile.in:1.6 --- krb5/src/lib/kadm5/srv/Makefile.in:1.1.1.5 Tue Nov 8 16:08:39 2005 +++ krb5/src/lib/kadm5/srv/Makefile.in Tue Nov 8 17:33:06 2005 @@ -26,6 +26,10 @@ SHLIB_RDIRS=$(KRB5_LIBDIR) RELDIR=kadm5/srv +# +# UMICH MOD: +# Add krep_dump, obvious, and look to list of sources +# SRCS = $(srcdir)/svr_policy.c \ $(srcdir)/svr_principal.c \ $(srcdir)/server_acl.c \ @@ -38,8 +42,15 @@ $(srcdir)/adb_xdr.c \ $(srcdir)/adb_policy.c \ $(srcdir)/adb_free.c \ - $(srcdir)/adb_openclose.c + $(srcdir)/adb_openclose.c\ + $(srcdir)/krep_dump.c \ + $(srcdir)/obvious.c \ + $(srcdir)/look.c +# +# UMICH MOD: +# Add krep_dump, obvious, and look to list of objects +# OBJS = svr_policy.$(OBJEXT) \ svr_principal.$(OBJEXT) \ server_acl.$(OBJEXT) \ @@ -52,8 +63,15 @@ adb_xdr.$(OBJEXT) \ adb_policy.$(OBJEXT) \ adb_free.$(OBJEXT) \ - adb_openclose.$(OBJEXT) + adb_openclose.$(OBJEXT) \ + krep_dump.$(OBJEXT) \ + obvious.$(OBJEXT) \ + look.$(OBJEXT) +# +# UMICH MOD: +# Add krep_dump, obvious, and look to list of static objects +# STLIBOBJS = \ svr_policy.o \ svr_principal.o \ @@ -67,7 +85,10 @@ adb_xdr.o \ adb_policy.o \ adb_free.o \ - adb_openclose.o + adb_openclose.o \ + krep_dump.o \ + obvious.o \ + look.o all-unix:: includes all-unix:: all-liblinks Index: krb5/src/lib/kadm5/srv/libkadm5srv.exports diff -u krb5/src/lib/kadm5/srv/libkadm5srv.exports:1.1.1.5 krb5/src/lib/kadm5/srv/libkadm5srv.exports:1.6 --- krb5/src/lib/kadm5/srv/libkadm5srv.exports:1.1.1.5 2005-11-16 14:22:52.000000000 -0500 +++ krb5/src/lib/kadm5/srv/libkadm5srv.exports 2005-11-16 14:20:50.000000000 -0500 @@ -133,6 +133,7 @@ ovsec_kadm_randkey_principal ovsec_kadm_rename_principal passwd_check +process_umichrep_record xdr_chpass3_arg xdr_chpass_arg xdr_chrand3_arg Index: krb5/src/lib/kadm5/srv/krep_dump.c diff -u /dev/null krb5/src/lib/kadm5/srv/krep_dump.c:1.4 --- /dev/null Wed Nov 9 17:44:11 2005 +++ krb5/src/lib/kadm5/srv/krep_dump.c Tue Jul 22 15:53:45 2003 @@ -0,0 +1,1403 @@ +/* + * Copyright 1990,1991 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. 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. + * + * + * Dump/restore entries within a KDC database + * + * This module started as a copy of kadmin/dbutil/dump.c + * + * All fprintf(stderr, ...); messages are changed to com_err(....); + * to avoid problems when krepd is run under inetd. + * + */ + +#if defined(UMICH_REPLICATION) /* Entire module */ + +#include +#include +#include +#include +#include +/* #include "kdb5_util.h" */ + +#if HAVE_REGEX_H +#include +#endif /* HAVE_REGEX_H */ + +#if defined(UMICH_REPLICATION) +#include +#include "krep_dump.h" +#endif /* UMICH_REPLICATION */ +/* + * Use compile(3) if no regcomp present. + */ +#if !defined(HAVE_REGCOMP) && defined(HAVE_REGEXP_H) +#define INIT char *sp = instring; +#define GETC() (*sp++) +#define PEEKC() (*sp) +#define UNGETC(c) (--sp) +#define RETURN(c) return(c) +#define ERROR(c) +#define RE_BUF_SIZE 1024 +#include +#endif /* !HAVE_REGCOMP && HAVE_REGEXP_H */ + +struct dump_args { + char *programname; + FILE *ofile; + krb5_context kcontext; + char **names; + int nnames; + int verbose; +}; + +typedef krb5_error_code (*dump_func)(krb5_pointer, krb5_db_entry *); + +typedef krb5_error_code (*load_func)(char *, krb5_context, + FILE *, int, int *, void *); +int process_umichrep_record (char *, krb5_context, FILE *, + int, int *, void *); + +typedef struct _dump_version { + char *name; + char *header; + int updateonly; + int create_kadm5; + dump_func dump_princ; + osa_adb_iter_policy_func dump_policy; + load_func load_record; +} dump_version; + +dump_version umichrep_version_1 = { + "Kerberos version 5", + "Replication version 1.0\n", + 0, + 0, + krb5_umichrep_princ, + krb5_umichrep_policy, + process_umichrep_record, +}; + +struct dump_args rep_args; +krb5_context umichrep_context; + + +/* External data */ +extern char *current_dbname; +extern krb5_boolean dbactive; +extern int exit_status; +extern krb5_context util_context; +extern kadm5_config_params global_params; + +/* Strings */ + +/* These aren't used, otherwise the k5beta7 one is incorrect!!! KWC */ +static const char k5beta_dump_header[] = "kdb5_edit load_dump version 2.0\n"; +static const char k5beta6_dump_header[] = "kdb5_edit load_dump version 3.0\n"; +static const char k5beta7_dump_header[] = "kdb5_edit load_dump version 4\n"; + +static const char null_mprinc_name[] = "kdb5_dump@MISSING"; + +/* Message strings */ +static const char regex_err[] = "%s: regular expression error - %s\n"; +static const char regex_merr[] = "%s: regular expression match error - %s\n"; +static const char pname_unp_err[] = "%s: cannot unparse principal name (%s)\n"; +static const char mname_unp_err[] = "%s: cannot unparse modifier name (%s)\n"; +static const char nokeys_err[] = "%s: cannot find any standard key for %s\n"; +static const char sdump_tl_inc_err[] = "%s: tagged data list inconsistency for %s (counted %d, stored %d)\n"; +static const char stand_fmt_name[] = "Kerberos version 5"; +static const char old_fmt_name[] = "Kerberos version 5 old format"; +static const char b6_fmt_name[] = "Kerberos version 5 beta 6 format"; +static const char ofopen_error[] = "%s: cannot open %s for writing (%s)\n"; +static const char oflock_error[] = "%s: cannot lock %s (%s)\n"; +static const char dumprec_err[] = "%s: error performing %s dump (%s)\n"; +static const char dumphdr_err[] = "%s: error dumping %s header (%s)\n"; +static const char trash_end_fmt[] = "%s(%d): ignoring trash at end of line: "; +static const char read_name_string[] = "name string"; +static const char read_key_type[] = "key type"; +static const char read_key_data[] = "key data"; +static const char read_pr_data1[] = "first set of principal attributes"; +static const char read_mod_name[] = "modifier name"; +static const char read_pr_data2[] = "second set of principal attributes"; +static const char read_salt_data[] = "salt data"; +static const char read_akey_type[] = "alternate key type"; +static const char read_akey_data[] = "alternate key data"; +static const char read_asalt_type[] = "alternate salt type"; +static const char read_asalt_data[] = "alternate salt data"; +static const char read_exp_data[] = "expansion data"; +static const char store_err_fmt[] = "%s(%d): cannot store %s(%s)\n"; +static const char add_princ_fmt[] = "%s\n"; +static const char parse_err_fmt[] = "%s(%d): cannot parse %s (%s)\n"; +static const char read_err_fmt[] = "%s(%d): cannot read %s\n"; +static const char no_mem_fmt[] = "%s(%d): no memory for buffers\n"; +static const char rhead_err_fmt[] = "%s(%d): cannot match size tokens\n"; +static const char err_line_fmt[] = "%s: error processing line %d of %s\n"; +static const char head_bad_fmt[] = "%s: dump header bad in %s\n"; +static const char read_bytecnt[] = "record byte count"; +static const char read_encdata[] = "encoded data"; +static const char n_name_unp_fmt[] = "%s(%s): cannot unparse name\n"; +static const char n_dec_cont_fmt[] = "%s(%s): cannot decode contents\n"; +static const char read_nint_data[] = "principal static attributes"; +static const char read_tcontents[] = "tagged data contents"; +static const char read_ttypelen[] = "tagged data type and length"; +static const char read_kcontents[] = "key data contents"; +static const char read_ktypelen[] = "key data type and length"; +static const char read_econtents[] = "extra data contents"; +static const char k5beta_fmt_name[] = "Kerberos version 5 old format"; +static const char standard_fmt_name[] = "Kerberos version 5 format"; +static const char no_name_mem_fmt[] = "%s: cannot get memory for temporary name\n"; +static const char ctx_err_fmt[] = "%s: cannot initialize Kerberos context\n"; +static const char stdin_name[] = "standard input"; +static const char restfail_fmt[] = "%s: %s restore failed\n"; +static const char close_err_fmt[] = "%s: cannot close database (%s)\n"; +static const char dbinit_err_fmt[] = "%s: cannot initialize database (%s)\n"; +static const char dblock_err_fmt[] = "%s: cannot initialize database lock (%s)\n"; +static const char dbname_err_fmt[] = "%s: cannot set database name to %s (%s)\n"; +static const char dbdelerr_fmt[] = "%s: cannot delete bad database %s (%s)\n"; +static const char dbunlockerr_fmt[] = "%s: cannot unlock database %s (%s)\n"; +static const char dbrenerr_fmt[] = "%s: cannot rename database %s to %s (%s)\n"; +static const char dbcreaterr_fmt[] = "%s: cannot create database %s (%s)\n"; +static const char dfile_err_fmt[] = "%s: cannot open %s (%s)\n"; +static const char dfile_close_err_fmt[] = "%s: cannot close replication file (%s)\n"; + +static const char delete_princ_fmt[] = "deleting %s\n"; +static const char delete_err_fmt[] = "%s(%d): cannot delete %s(%s)\n"; +static const char delete_policy_fmt[] = "deleting policy %s\n"; + +static const char oldoption[] = "-old"; +static const char b6option[] = "-b6"; +static const char verboseoption[] = "-verbose"; +static const char updateoption[] = "-update"; +static const char ovoption[] = "-ov"; +static const char dump_tmptrail[] = "~"; + +/* + * name_matches() - See if a principal name matches a regular expression + * or string. + */ +static int +name_matches(name, arglist) + char *name; + struct dump_args *arglist; +{ +#if HAVE_REGCOMP + regex_t match_exp; + regmatch_t match_match; + int match_error; + char match_errmsg[BUFSIZ]; + size_t errmsg_size; +#elif HAVE_REGEXP_H + char regexp_buffer[RE_BUF_SIZE]; +#elif HAVE_RE_COMP + extern char *re_comp(); + char *re_result; +#endif /* HAVE_RE_COMP */ + int i, match; + + /* + * Plow, brute force, through the list of names/regular expressions. + */ + match = (arglist->nnames) ? 0 : 1; + for (i=0; innames; i++) { +#if HAVE_REGCOMP + /* + * Compile the regular expression. + */ + if (match_error = regcomp(&match_exp, + arglist->names[i], + REG_EXTENDED)) { + errmsg_size = regerror(match_error, + &match_exp, + match_errmsg, + sizeof(match_errmsg)); + com_err(arglist->programname, 0, + regex_err, arglist->programname, match_errmsg); + break; + } + /* + * See if we have a match. + */ + if (match_error = regexec(&match_exp, name, 1, &match_match, 0)) { + if (match_error != REG_NOMATCH) { + errmsg_size = regerror(match_error, + &match_exp, + match_errmsg, + sizeof(match_errmsg)); + com_err(arglist->programname, 0, regex_merr, + arglist->programname, match_errmsg); + break; + } + } + else { + /* + * We have a match. See if it matches the whole + * name. + */ + if ((match_match.rm_so == 0) && + (match_match.rm_eo == strlen(name))) + match = 1; + } + regfree(&match_exp); +#elif HAVE_REGEXP_H + /* + * Compile the regular expression. + */ + compile(arglist->names[i], + regexp_buffer, + ®exp_buffer[RE_BUF_SIZE], + '\0'); + if (step(name, regexp_buffer)) { + if ((loc1 == name) && + (loc2 == &name[strlen(name)])) + match = 1; + } +#elif HAVE_RE_COMP + /* + * Compile the regular expression. + */ + if (re_result = re_comp(arglist->names[i])) { + com_err(arglist->programname, 0, + regex_err, arglist->programname, re_result); + break; + } + if (re_exec(name)) + match = 1; +#else /* HAVE_RE_COMP */ + /* + * If no regular expression support, then just compare the strings. + */ + if (!strcmp(arglist->names[i], name)) + match = 1; +#endif /* HAVE_REGCOMP */ + if (match) + break; + } + return(match); +} + +/* + * dump_umichrep_k5beta6_iterator() - Output a dump record in krb5b6 format. + */ +static krb5_error_code +dump_umichrep_k5beta6_iterator(ptr, entry) + krb5_pointer ptr; + krb5_db_entry *entry; +{ + krb5_error_code retval; + struct dump_args *arg; + char *name; + krb5_tl_data *tlp; + krb5_key_data *kdata; + int counter, i, j; + + /* Initialize */ + arg = (struct dump_args *) ptr; + name = (char *) NULL; + + /* + * Flatten the principal name. + */ + if ((retval = krb5_unparse_name(arg->kcontext, + entry->princ, + &name))) { + com_err("krep", 0, pname_unp_err, + arg->programname, error_message(retval)); + return(retval); + } + /* + * If we don't have any match strings, or if our name matches, then + * proceed with the dump, otherwise, just forget about it. + */ + if (!arg->nnames || name_matches(name, arg)) { + /* + * We'd like to just blast out the contents as they would appear in + * the database so that we can just suck it back in, but it doesn't + * lend itself to easy editing. + */ + + /* + * The dump format is as follows: + * len strlen(name) n_tl_data n_key_data e_length + * name + * attributes max_life max_renewable_life expiration + * pw_expiration last_success last_failed fail_auth_count + * n_tl_data*[type length ] + * n_key_data*[ver kvno ver*(type length )] + * + * Fields which are not encapsulated by angle-brackets are to appear + * verbatim. Bracketed fields absence is indicated by a -1 in its + * place + */ + + /* + * Make sure that the tagged list is reasonably correct. + */ + counter = 0; + for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) { + counter++; + } + + if (counter == entry->n_tl_data) { + /* Pound out header */ + fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%s\t", + (int) entry->len, + strlen(name), + counter, + (int) entry->n_key_data, + (int) entry->e_length, + name); + fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", + entry->attributes, + entry->max_life, + entry->max_renewable_life, + entry->expiration, + entry->pw_expiration, + entry->last_success, + entry->last_failed, + entry->fail_auth_count); + /* Pound out tagged data. */ + for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) { +/*DBG com_err("krep debug dump", 0, "Pounding out %d bytes for data type %d", + tlp->tl_data_length, tlp->tl_data_type); */ + fprintf(arg->ofile, "%d\t%d\t", + (int) tlp->tl_data_type, + (int) tlp->tl_data_length); + if (tlp->tl_data_length) + for (i=0; itl_data_length; i++) + fprintf(arg->ofile, "%02x", tlp->tl_data_contents[i]); + else + fprintf(arg->ofile, "%d", -1); + fprintf(arg->ofile, "\t"); + } + + /* Pound out key data */ + for (counter=0; countern_key_data; counter++) { + kdata = &entry->key_data[counter]; + fprintf(arg->ofile, "%d\t%d\t", + (int) kdata->key_data_ver, + (int) kdata->key_data_kvno); + for (i=0; ikey_data_ver; i++) { + fprintf(arg->ofile, "%d\t%d\t", + kdata->key_data_type[i], + kdata->key_data_length[i]); + if (kdata->key_data_length[i]) + for (j=0; jkey_data_length[i]; j++) + fprintf(arg->ofile, "%02x", + kdata->key_data_contents[i][j]); + else + fprintf(arg->ofile, "%d", -1); + fprintf(arg->ofile, "\t"); + } + } + + /* Pound out extra data */ + if (entry->e_length) + for (i=0; ie_length; i++) + fprintf(arg->ofile, "%02x", entry->e_data[i]); + else + fprintf(arg->ofile, "%d", -1); + + /* Print trailer */ + fprintf(arg->ofile, ";\n"); + + if (arg->verbose) + com_err(arg->programname, 0, "%s\n", name); + } + else { + com_err(arg->programname, 0, sdump_tl_inc_err, + arg->programname, name, counter, (int) entry->n_tl_data); + retval = EINVAL; + } + } + krb5_xfree(name); + return(retval); +} + +/* + * dump_umichrep_k5beta7_iterator() - Output a dump record in krb5b7 format. + */ +static krb5_error_code +dump_umichrep_k5beta7_princ(ptr, entry) + krb5_pointer ptr; + krb5_db_entry *entry; +{ + krb5_error_code retval; + struct dump_args *arg; + char *name; + int tmp_nnames; + + /* Initialize */ + arg = (struct dump_args *) ptr; + name = (char *) NULL; + + /* + * Flatten the principal name. + */ + if ((retval = krb5_unparse_name(arg->kcontext, + entry->princ, + &name))) { + com_err(arg->programname, 0, pname_unp_err, + arg->programname, error_message(retval)); + return(retval); + } + /* + * If we don't have any match strings, or if our name matches, then + * proceed with the dump, otherwise, just forget about it. + */ + if (!arg->nnames || name_matches(name, arg)) { + fprintf(arg->ofile, "princ\t"); + + /* save the callee from matching the name again */ + tmp_nnames = arg->nnames; + arg->nnames = 0; + retval = dump_umichrep_k5beta6_iterator(ptr, entry); + arg->nnames = tmp_nnames; + } + + free(name); + return retval; +} + +void dump_umichrep_k5beta7_policy(void *data, osa_policy_ent_t entry) +{ + struct dump_args *arg; + + arg = (struct dump_args *) data; + fprintf(arg->ofile, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\n", entry->name, + entry->pw_min_life, entry->pw_max_life, entry->pw_min_length, + entry->pw_min_classes, entry->pw_history_num, + entry->policy_refcnt); +} + +#if defined(UMICH_REPLICATION) + +/* + * dump_umichrep_delprinc() - Output a dump record to delete a principal. + */ +static krb5_error_code +dump_umichrep_delprinc(ptr, princ) + krb5_pointer ptr; + krb5_principal princ; +{ + krb5_error_code retval; + struct dump_args *arg; + char *name; + + /* Initialize */ + arg = (struct dump_args *) ptr; + name = (char *) NULL; + + /* + * Flatten the principal name. + */ + if ((retval = krb5_unparse_name(arg->kcontext, + princ, + &name))) { + com_err(arg->programname, 0, pname_unp_err, + arg->programname, error_message(retval)); + return(retval); + } + + fprintf(arg->ofile, "%d\t%s", strlen(name), name); + + /* Print trailer */ + fprintf(arg->ofile, ";\n"); + + if (arg->verbose) + com_err(arg->programname, 0, "deleting %s\n", name); + + free(name); + return retval; +} + +/* + * dump_umichrep_delpolicy() - Output a dump record to delete a policy. + */ +static krb5_error_code +dump_umichrep_delpolicy(ptr, policyName) + krb5_pointer ptr; + kadm5_policy_t policyName; +{ + krb5_error_code retval = 0; + struct dump_args *arg; + + /* Initialize */ + arg = (struct dump_args *) ptr; + + fprintf(arg->ofile, "%d\t%s", strlen(policyName), policyName); + + /* Print trailer */ + fprintf(arg->ofile, ";\n"); + + if (arg->verbose) + com_err(arg->programname, 0, "deleting policy %s\n", policyName); + + return retval; +} + +/* + * krb5_umichrep_princ() - Output a dump record to add/modify a principal. + */ +krb5_error_code +krb5_umichrep_princ(entry) + krb5_db_entry *entry; +{ + krb5_error_code retval = 0; + krb5_error_code lockval; + + /* Get an exclusive lock on the file so it is not truncated on us */ + if ((lockval = krb5_lock_file(umichrep_context, + fileno(rep_args.ofile), + KRB5_LOCKMODE_EXCLUSIVE)) == 0) + { + + /* Go to the end of the file - it may have been truncated... */ + fseek(rep_args.ofile, 0, SEEK_END); + + fprintf(rep_args.ofile, "princ\t"); + retval = dump_umichrep_k5beta6_iterator(&rep_args, entry); + + /* flush our changes */ + fflush(rep_args.ofile); + + /* Release the lock on the file */ + if (lockval = krb5_lock_file(umichrep_context, + fileno(rep_args.ofile), + KRB5_LOCKMODE_UNLOCK)) { + com_err(rep_args.programname, errno, + "unlocking replication delta file in krb5_umichrep_princ()\n"); + } + } + else + { + retval = -1; + com_err(rep_args.programname, errno, + "locking replication delta file in krb5_umichrep_princ()\n"); + } + return retval; +} + +/* + * krb5_umichrep_policy() - Output a dump record to add/modify a policy. + */ +krb5_error_code +krb5_umichrep_policy(osa_policy_ent_t entry) +{ + krb5_error_code retval = 0; + krb5_error_code lockval; + + /* Get an exclusive lock on the file so it is not truncated on us */ + if ((lockval = krb5_lock_file(umichrep_context, + fileno(rep_args.ofile), + KRB5_LOCKMODE_EXCLUSIVE)) == 0) + { + /* Go to the end of the file - it may have been truncated... */ + fseek(rep_args.ofile, 0, SEEK_END); + + fprintf(rep_args.ofile, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\n", + entry->name, + entry->pw_min_life, entry->pw_max_life, entry->pw_min_length, + entry->pw_min_classes, entry->pw_history_num, + entry->policy_refcnt); + + /* flush our changes */ + fflush(rep_args.ofile); + + /* Release the lock on the file */ + if (lockval = krb5_lock_file(umichrep_context, + fileno(rep_args.ofile), + KRB5_LOCKMODE_UNLOCK)) { + com_err(rep_args.programname, errno, + "unlocking replication delta file in krb5_umichrep_policy()\n"); + } + } + else + { + retval = -1; + com_err(rep_args.programname, errno, + "locking replication delta file in krb5_umichrep_policy()\n"); + } + return retval; +} + +/* + * krb5_umichrep_delprinc() - Output a dump recode to delete principal. + */ +krb5_error_code +krb5_umichrep_delprinc(princ) + krb5_principal princ; +{ + krb5_error_code retval = 0; + krb5_error_code lockval; + + /* Get an exclusive lock on the file so it is not truncated on us */ + if ((lockval = krb5_lock_file(umichrep_context, + fileno(rep_args.ofile), + KRB5_LOCKMODE_EXCLUSIVE)) == 0) + { + /* Go to the end of the file - it may have been truncated... */ + fseek(rep_args.ofile, 0, SEEK_END); + + /* Write the record */ + fprintf(rep_args.ofile, "delprinc\t"); + retval = dump_umichrep_delprinc(&rep_args, princ); + + /* flush our changes */ + fflush(rep_args.ofile); + + /* Release the lock on the file */ + if (lockval = krb5_lock_file(umichrep_context, + fileno(rep_args.ofile), + KRB5_LOCKMODE_UNLOCK)) { + com_err(rep_args.programname, errno, + "unlocking replication delta file in krb5_umichrep_delprinc()\n"); + } + } + else + { + retval = -1; + com_err(rep_args.programname, errno, + "locking replication delta file in krb5_umichrep_delprinc()\n"); + } + return retval; +} + +/* + * krb5_umichrep_delpolicy() - Output a dump record to delete a policy. + */ +krb5_error_code +krb5_umichrep_delpolicy(policyName) + kadm5_policy_t policyName; +{ + krb5_error_code retval = 0; + krb5_error_code lockval; + + /* Get an exclusive lock on the file so it is not truncated on us */ + if ((lockval = krb5_lock_file(umichrep_context, + fileno(rep_args.ofile), + KRB5_LOCKMODE_EXCLUSIVE)) == 0) + { + /* Go to the end of the file - it may have been truncated... */ + fseek(rep_args.ofile, 0, SEEK_END); + + /* Write the record */ + fprintf(rep_args.ofile, "delpolicy\t"); + retval = dump_umichrep_delpolicy(&rep_args, policyName); + + /* flush our changes */ + fflush(rep_args.ofile); + + /* Release the lock on the file */ + if (lockval = krb5_lock_file(umichrep_context, + fileno(rep_args.ofile), + KRB5_LOCKMODE_UNLOCK)) { + com_err(rep_args.programname, errno, + "unlocking replication delta file in krb5_umichrep_delpolicy()\n"); + } + } + else + { + retval = -1; + com_err(rep_args.programname, errno, + "locking replication delta file in krb5_umichrep_delpolicy()\n"); + } + return retval; +} + +/* + * krb5_umichrep_init() - Initialize rep_args + */ +krb5_error_code +krb5_umichrep_init(char *programname, char *replfile) +{ + krb5_error_code retval, kret; + FILE * f; + int memUsed = 0; + + retval = krb5_init_context(&umichrep_context); + if (retval) { + com_err(programname, retval, + "from krb5_init_context() in krb5_umichrep_init\n"); + return retval; + } + + /* Init rep_args struct, open replication file, etc. */ + + rep_args.programname = (char *) malloc(strlen(programname) + 1); + if (!rep_args.programname) { + com_err(programname, 0, "Error allocating %d bytes for programname\n", + strlen(programname)+1); + retval++; + return retval; + } + strcpy(rep_args.programname, programname); + + /* + * Open the replication file + */ + if (!replfile) { + replfile = strdup(KREP_DEFAULT_FILE); + memUsed = 1; + } + + if ((f = fopen(replfile, "a+")) == NULL) { + com_err(programname, errno, dfile_err_fmt, programname, replfile, + error_message(errno)); + if (memUsed == 1) free(replfile); + retval++; + return retval; + } + if (memUsed == 1) free(replfile); + rep_args.ofile = f; + rep_args.kcontext = umichrep_context; + rep_args.names = NULL; + rep_args.nnames = 0; + rep_args.verbose = 0; + + return 0; +} + +/* + * krb5_umichrep_destroy() - Cleanup rep_args + */ +void +krb5_umichrep_destroy(void) +{ + int f; + + /* + * Close the replication file + */ + if ((f = fclose(rep_args.ofile)) != 0) { + char *programname = rep_args.programname; + com_err(programname, errno, dfile_close_err_fmt, programname, + error_message(errno)); + } + + /* clean rep_args struct */ + + free(rep_args.programname); + rep_args.programname = 0; +} + +#endif /* UMICH_REPLICATION */ + + +/* + * Read a string of bytes while counting the number of lines passed. + */ +static int +read_string(f, buf, len, lp) + FILE *f; + char *buf; + int len; + int *lp; +{ + int c; + int i, retval; + + retval = 0; + for (i=0; itl_data_next); + dbentry.n_tl_data++; + } + else { + error++; + break; + } + } + + /* Get memory for key list */ + if (t4 && !(kp = (krb5_key_data *) malloc((size_t) + (t4*sizeof(krb5_key_data))))) + error++; + + /* Get memory for extra data */ + if (t5 && !(op = (krb5_octet *) malloc((size_t) t5))) + error++; + + if (!error) { + dbentry.len = t1; + dbentry.n_key_data = t4; + dbentry.e_length = t5; + if (kp) { + memset(kp, 0, (size_t) (t4*sizeof(krb5_key_data))); + dbentry.key_data = kp; + kp = (krb5_key_data *) NULL; + } + if (op) { + memset(op, 0, (size_t) t5); + dbentry.e_data = op; + op = (krb5_octet *) NULL; + } + + /* Read in and parse the principal name */ + if (!read_string(filep, name, t2, linenop) && + !(kret = krb5_parse_name(kcontext, name, &dbentry.princ))) { + + /* Get the fixed principal attributes */ + nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", + &t2, &t3, &t4, &t5, &t6, &t7, &t8, &t9); + if (nread == 8) { + dbentry.attributes = (krb5_flags) t2; + dbentry.max_life = (krb5_deltat) t3; + dbentry.max_renewable_life = (krb5_deltat) t4; + dbentry.expiration = (krb5_timestamp) t5; + dbentry.pw_expiration = (krb5_timestamp) t6; + dbentry.last_success = (krb5_timestamp) t7; + dbentry.last_failed = (krb5_timestamp) t8; + dbentry.fail_auth_count = (krb5_kvno) t9; + } else { + try2read = read_nint_data; + error++; + } + + /* + * Get the tagged data. + * + * Really, this code ought to discard tl data types + * that it knows are special to the current version + * and were not supported in the previous version. + * But it's a pain to implement that here, and doing + * it at dump time has almost as good an effect, so + * that's what I did. [krb5-admin/89/ + */ + if (!error && dbentry.n_tl_data) { + for (tl = dbentry.tl_data; tl; tl = tl->tl_data_next) { + nread = fscanf(filep, "%d\t%d\t", &t1, &t2); + if (nread == 2) { + tl->tl_data_type = (krb5_int16) t1; + tl->tl_data_length = (krb5_int16) t2; +/*DBG com_err("krep debug restore", 0, "Reading %d bytes for data type %d", + tl->tl_data_length, tl->tl_data_type); */ + if (tl->tl_data_length) { + if (!(tl->tl_data_contents = + (krb5_octet *) malloc((size_t) t2+1)) || + read_octet_string(filep, + tl->tl_data_contents, + t2)) { + try2read = read_tcontents; + error++; + break; + } + } + else { + /* Should be a null field */ + nread = fscanf(filep, "%d", &t9); + if ((nread != 1) || (t9 != -1)) { + error++; + try2read = read_tcontents; + break; + } + } + } + else { + try2read = read_ttypelen; + error++; + break; + } + } + } + + /* Get the key data */ + if (!error && dbentry.n_key_data) { + for (i=0; !error && (ikey_data_ver = (krb5_int16) t1; + kdatap->key_data_kvno = (krb5_int16) t2; + + for (j=0; jkey_data_type[j] = t3; + kdatap->key_data_length[j] = t4; + if (t4) { + if (!(kdatap->key_data_contents[j] = + (krb5_octet *) + malloc((size_t) t4+1)) || + read_octet_string(filep, + kdatap->key_data_contents[j], + t4)) { + try2read = read_kcontents; + error++; + break; + } + } + else { + /* Should be a null field */ + nread = fscanf(filep, "%d", &t9); + if ((nread != 1) || (t9 != -1)) { + error++; + try2read = read_kcontents; + break; + } + } + } + else { + try2read = read_ktypelen; + error++; + break; + } + } + } + } + } + + /* Get the extra data */ + if (!error && dbentry.e_length) { + if (read_octet_string(filep, + dbentry.e_data, + (int) dbentry.e_length)) { + try2read = read_econtents; + error++; + } + } + else { + nread = fscanf(filep, "%d", &t9); + if ((nread != 1) || (t9 != -1)) { + error++; + try2read = read_econtents; + } + } + + /* Finally, find the end of the record. */ + if (!error) + find_record_end(filep, fname, *linenop); + + /* + * We have either read in all the data or choked. + */ + if (!error) { + one = 1; + if ((kret = krb5_db_put_principal(kcontext, + &dbentry, + &one))) { + com_err("krepd", 0, store_err_fmt, + fname, *linenop, + name, error_message(kret)); + } + else { + if (verbose) + com_err("krepd", 0, add_princ_fmt, name); + retval = 0; + } + } + else { + com_err("krepd", 0, read_err_fmt, fname, *linenop, try2read); + } + } + else { + if (kret) + com_err("krepd", 0, parse_err_fmt, + fname, *linenop, name, error_message(kret)); + else + com_err("krepd", 0, no_mem_fmt, fname, *linenop); + } + } + else { + com_err("krepd", 0, rhead_err_fmt, fname, *linenop); + } + + if (op) + free(op); + if (kp) + free(kp); + if (name) + free(name); + krb5_db_free_principal(kcontext, &dbentry, 1); + } + else { + if (nread == EOF) + retval = -1; + } + return(retval); +} + +int process_umichrep_k5beta7_policy(fname, kcontext, filep, verbose, linenop, pol_db) + char *fname; + krb5_context kcontext; + FILE *filep; + int verbose; + int *linenop; + void *pol_db; +{ + osa_policy_ent_rec rec; + char namebuf[1024]; + int nread, ret; + + (*linenop)++; + rec.name = namebuf; + + nread = fscanf(filep, "%1024s\t%d\t%d\t%d\t%d\t%d\t%d", rec.name, + &rec.pw_min_life, &rec.pw_max_life, + &rec.pw_min_length, &rec.pw_min_classes, + &rec.pw_history_num, &rec.policy_refcnt); + if (nread == EOF) + return -1; + else if (nread != 7) { + com_err("krepd", 0, "cannot parse policy on line %d (%d read)\n", + *linenop, nread); + return 1; + } + + if (ret = osa_adb_create_policy(pol_db, &rec)) { + if (ret == OSA_ADB_DUP) { + if (ret = osa_adb_put_policy(pol_db, &rec)) { + com_err("krepd", 0, + "cannot create/modify policy on line %d: %s\n", + *linenop, error_message(ret)); + return 1; + } + else { + if (verbose) + com_err("krepd", 0, "modified policy %s\n", rec.name); + } + } + else { + com_err("krepd", 0, "cannot create policy on line %d: %s\n", + *linenop, error_message(ret)); + } + } + else { + if (verbose) + com_err("krepd", 0, "created policy %s\n", rec.name); + } + + return 0; +} + +#if defined(UMICH_REPLICATION) + +/* + * process_umichrep_delprinc() - Handle a delete dump record. + * + * Returns -1 for end of file, 0 for success and 1 for failure. + */ +static int +process_umichrep_delprinc(fname, kcontext, filep, verbose, linenop, pol_db) + char *fname; + krb5_context kcontext; + FILE *filep; + int verbose; + int *linenop; + void *pol_db; +{ + int retval; + krb5_principal princ; + krb5_int32 t2; + int nread; + int error; + int one; + char *name; + krb5_error_code kret; + + (*linenop)++; + retval = 1; + name = (char *) NULL; + princ = (krb5_principal) NULL; + error = 0; + kret = 0; + nread = fscanf(filep, "%d\t", &t2); + if (nread == 1) { + /* Get memory for flattened principal name */ + if (!(name = (char *) malloc((size_t) t2 + 1))) + error++; + + if (!error) { + /* Read in and parse the principal name */ + if (!read_string(filep, name, t2, linenop) && + !(kret = krb5_parse_name(kcontext, name, &princ))) { + + /* Finally, find the end of the record. */ + if (!error) + find_record_end(filep, fname, *linenop); + + /* + * We have either read in all the data or choked. + */ + if (!error) { + one = 1; + if ((kret = krb5_db_delete_principal(kcontext, + princ, + &one))) { + com_err("krepd", 0, delete_err_fmt, + fname, *linenop, + name, error_message(kret)); + } + else { + if (verbose) + com_err("krepd", 0, delete_princ_fmt, name); + retval = 0; + } + } + } + else { + if (kret) + com_err("krepd", 0, parse_err_fmt, + fname, *linenop, name, error_message(kret)); + else + com_err("krepd", 0, no_mem_fmt, fname, *linenop); + } + } + else { + com_err("krepd", 0, rhead_err_fmt, fname, *linenop); + } + + if (name) + free(name); + if (princ) + krb5_free_principal(kcontext, princ); + } + else { + if (nread == EOF) + retval = -1; + } + return(retval); +} + +/* + * process_umichrep_delpolicy() - Handle a policy deletion dump record. + * + * Returns -1 for end of file, 0 for success and 1 for failure. + */ +static int +process_umichrep_delpolicy(fname, kcontext, filep, verbose, linenop, pol_db) + char *fname; + krb5_context kcontext; + FILE *filep; + int verbose; + int *linenop; + void *pol_db; +{ + int retval; + krb5_int32 t2; + int nread; + int error; + int one; + char *name; + int ret; + + (*linenop)++; + retval = 1; + name = (char *) NULL; + error = 0; + ret = 0; + nread = fscanf(filep, "%d\t", &t2); + if (nread == 1) { + /* Get memory for policy name */ + if (!(name = (char *) malloc((size_t) t2 + 1))) + error++; + + if (!error) { + /* Read in the policy name */ + if (!read_string(filep, name, t2, linenop) ) { + + /* Finally, find the end of the record. */ + if (!error) + find_record_end(filep, fname, *linenop); + + /* + * We have either read in all the data or choked. + */ + if (!error) { + if (ret = osa_adb_destroy_policy(pol_db, name)) { + com_err("krepd", 0, delete_err_fmt, + fname, *linenop, + name, error_message(ret)); + } + else { + if (verbose) + com_err("krepd", 0, delete_policy_fmt, name); + retval = 0; + } + } + } + else { + if (ret) + com_err("krepd", 0, parse_err_fmt, + fname, *linenop, name, error_message(ret)); + else + com_err("krepd", 0, no_mem_fmt, fname, *linenop); + } + } + else { + com_err("krepd", 0, rhead_err_fmt, fname, *linenop); + } + + if (name) + free(name); + } + else { + if (nread == EOF) + retval = -1; + } + return(retval); +} + +/* + * process_umichrep_record() - Handle a dump record in umichrep format. + * + * Returns -1 for end of file, 0 for success and 1 for failure. + */ +int +process_umichrep_record(fname, kcontext, filep, verbose, linenop, pol_db) + char *fname; + krb5_context kcontext; + FILE *filep; + int verbose; + int *linenop; + void *pol_db; +{ + int nread; + char rectype[100]; + + nread = fscanf(filep, "%100s\t", rectype); + if (nread == EOF) + return -1; + else if (nread != 1) + return 1; + if (strcmp(rectype, "princ") == 0) + process_umichrep_k5beta6_record(fname, kcontext, filep, verbose, + linenop, pol_db); + else if (strcmp(rectype, "delprinc") == 0) + process_umichrep_delprinc(fname, kcontext, filep, verbose, + linenop, pol_db); + else if (strcmp(rectype, "policy") == 0) + process_umichrep_k5beta7_policy(fname, kcontext, filep, verbose, + linenop, pol_db); + else if (strcmp(rectype, "delpolicy") == 0) + process_umichrep_delpolicy(fname, kcontext, filep, verbose, + linenop, pol_db); + else { + com_err("krepd", 0, "unknown record type \"%s\" on line %d\n", + rectype, *linenop); + return 1; + } + + return 0; +} +#endif /* UMICH_REPLICATION */ + +#endif /* UMICH_REPLICATION */ Index: krb5/src/lib/kadm5/srv/krep_dump.h diff -u /dev/null krb5/src/lib/kadm5/srv/krep_dump.h:1.3 --- /dev/null Wed Nov 9 17:44:11 2005 +++ krb5/src/lib/kadm5/srv/krep_dump.h Tue Jul 22 15:53:45 2003 @@ -0,0 +1,36 @@ +#ifndef _KREP_DUMP_H +#define _KREP_DUMP_H + +#if defined(UMICH_REPLICATION) + +/* + * begin replication stuff + */ + +#include "krb5/kdb.h" + +krb5_error_code +krb5_umichrep_init (char *, char *); + +krb5_error_code +krb5_umichrep_princ (krb5_db_entry *); + +krb5_error_code +krb5_umichrep_delprinc (krb5_principal); + +krb5_error_code +krb5_umichrep_policy (osa_policy_ent_t); + +krb5_error_code +krb5_umichrep_delpolicy (kadm5_policy_t); + +void +krb5_umichrep_destroy (void); + +/* + * end replication stuff + */ + +#endif /* UMICH_REPLICATION */ + +#endif /* _KREP_DUMP_H */ Index: krb5/src/lib/kadm5/srv/look.c diff -u /dev/null krb5/src/lib/kadm5/srv/look.c:1.1 --- /dev/null Wed Nov 9 17:44:11 2005 +++ krb5/src/lib/kadm5/srv/look.c Mon Oct 16 11:16:04 2000 @@ -0,0 +1,618 @@ +/* + * look - find words in dictionary + */ + +#include +#include +#include +#include +#include +#include +#ifdef __svr4__ +#include +#endif +char *strcpy(); +char *sbrk(); + +#define MAXSTRINGSIZE 512 /* usual max string length. */ + +#ifdef DEBUG +char *dictlist[20] = { +"/usr/dict/words", +"/usr/keys/dict/web2", +"/usr/keys/dict/web2a", "/usr/keys/dict/propernames", +"/usr/keys/dict/badpasswords", +0}; + +int Dflag; +int sflag; +#endif + +char onecase[256]; + +/* + * This internal routine processes one dictionary. Call "lookup" instead. + */ + +do_lookup(words, dictionary) + char **words; +{ + register i; + int fd; + register char *mp; + char *word; + long size; + int len; + char *array; + char *fp, *lp; + char *ep; + int last; + int r; + int wcount; + char *va; + int share; +#ifdef sun + int pgz; +#endif + + if (!onecase[1]) + { + for (i = 0; i < 256; ++i) +#if 0 +/* strcasecmp uses this order. Unfortunately, sort -f doesn't. So */ + if (isupper(i)) + onecase[i] = tolower(i); +#else +/* we use sort -f's order (so we can deal with /usr/dict/words) */ + if (islower(i)) + onecase[i] = toupper(i); +#endif + else + onecase[i] = i; + } + fd = open(dictionary, 0); + if (fd < 0) + { +#ifdef TESTING + perror(dictionary); +#endif + return 0; + } + size = lseek(fd, 0L, 2); + +#ifdef linux + share = MAP_SHARED; + va = 0; +#endif + +#ifdef _AIX + share = MAP_FILE|MAP_VARIABLE|MAP_SHARED, + va = 0; +#endif +#ifdef sun +#if 0 +/* This works fine here, but similar code failed elsewhere on Suns 4.0.3. */ + share = MAP_SHARED; + va = 0; +#else + share = MAP_SHARED | MAP_FIXED; + va = sbrk(0); + pgz = getpagesize(); + va += (pgz-1); va = (char*)(((long)va) & -pgz); + va += (pgz<<4); +#endif +#endif +#ifdef DEBUG +if (Dflag) +printf ("Checking in dictionary %s\n", dictionary); +#endif + array = mmap(va, size, PROT_READ, share, fd, 0L); + if (array == (char*)-1) + { + perror("mmap"); + goto Done; + } + ep = array + size; + r = 0; + for (wcount = 0; word = words[wcount]; ++wcount) + { + len = strlen(word); + fp = array; lp = ep; + last = 0; + for (;;) + { + mp = (char*)(((long)fp + (long)lp)/2); + while (mp < lp && *mp != '\n') + ++mp; + ++mp; + if (mp >= lp) + { + mp = fp; + last = 1; + } + Again: +#ifdef DEBUG +if (Dflag) +{ +printf ("Comparing <%s> and <", word); +for (i=0; mp+i < lp && mp[i] != '\n'; ++i) putchar(mp[i]); +printf ("> at offset %d", mp-array); +} +#endif + for (i = 0; i < len; ++i) + { + if (mp[i] == '\n' || mp+i >= lp) + { + r = 1; + break; + } + if (r = (onecase[word[i]] - onecase[mp[i]])) + break; + } + if (i == len) + r = -(mp+i < lp && mp[i] != '\n'); + if (!r) + { +#ifdef DEBUG +if (Dflag) printf (" found\n"); +#endif + r = 1; + break; + } + if (last) + { +#ifdef DEBUG +if (Dflag) printf (" not found\n"); +#endif + if (r < 0) + { + r = 0; + break; + } + while (mp < lp && *mp != '\n') + ++mp; + ++mp; + if (mp >= lp) + { + r = 0; + break; + } + goto Again; + } + if (r < 0) + { + lp = mp; /* first half */ +#ifdef DEBUG +if (Dflag) +printf (" at %c(%c),%c(%c) = first half\n", word[i], onecase[word[i]], +mp[i], onecase[mp[i]]); +#endif + } else { + fp = mp; /* last half */ +#ifdef DEBUG +if (Dflag) +printf (" at %c(%c),%c(%c) = last half\n", word[i], onecase[word[i]], +mp[i], onecase[mp[i]]); +#endif + } + } + if (r) + { + r = wcount+1; + break; + } +#ifdef DEBUG +if (Dflag) +{ +if (words[wcount+1]) printf ("Miss, so next word is <%s>\n", words[wcount+1]); +else printf ("Miss & no more words to check\n"); +} +#endif + } + if (munmap(array, size) < 0) + { + perror("munmap"); + } +Done: + close(fd); + return r; +} + +/* + * This is the real externally visible routine. + * + * It looks up a list of words in a list of dictionaries. + */ + +lookup(av, dicts, dictp) + char **av, **dicts, **dictp; +{ + int i, r; + for (i = 0; dicts[i]; ++i) + if (r = do_lookup(av, dicts[i])) + { + if (dictp) + *dictp = i[dicts]; + return r; + } + return 0; +} + +struct suffixes { + char *suffix; + char *replace; +} suffixes[] = { + {"iness", "y"}, + {"ness", ""}, + {"iless", "y"}, + {"less", ""}, + {"es", "e"}, + {"'s", ""}, + {"s", ""}, + {"nce", "nt"}, + {"ncy", "nt"}, + {"blity", "blity"}, + {"bility", "ble"}, + {"fiable", "fy"}, + {"able", "e"}, +/* {"able", ""}, /* old */ + {"ity", "e"}, + {"bly", "ble"}, + {"ily", "y"}, + {"ly", ""}, + {"ical", "ic"}, + {"mental", "ment"}, + {"ional", "ion"}, + {"ment", ""}, + {"ing", "e"}, + {"ater", 0}, + {"ier", "y"}, + {"ied", "y"}, + {"er", "e"}, + {"ed", "e"}, + {"istic", "ist"}, + {"graphic", "graphy"}, + {"iest", "y"}, /* new */ + {"est", "e"}, + {"metric", "metry"}, + {"metry", "meter"}, + {"logic", "logy"}, + {"ist", "e"}, + {"ism", "e"}, + {"fication", "fy"}, + {"ization", "ize"}, + {"ator", "ate"}, + {"tion", "te"}, + {"onian", "on"}, + {"an", "n"}, + {"tive", "e"}, + {"ize", "e"}, + {"ship", ""}, + {"hood", ""}, + {"like", ""}, + {"e", ""}, + {"eth", "e"}, /* new */ + {"123", ""}, /* new */ +{0,0}}; + +struct prefixes { + char *prefix; +} prefixes[] = { + "anti", + "bio", + "dis", + "electro", + "en", + "fore", + "hyper", + "intra", + "inter", + "iso", + "kilo", + "magneto", + "meta", + "micro", + "milli", + "mis", + "mono", + "multi", + "non", + "out", + "over", + "photo", + "poly", + "pre", + "pseudo", + "re", + "semi", + "stereo", + "sub", + "super", + "thermo", + "ultra", + "under", + "un", + "123", /* new */ + "ilove", /* new */ +0}; + +/* + * This word looks for possible suffixes & prefixes + * on a list of words, and returns an expanded out list. + */ + +int findfixes(word, av, nav, temp, tempend) + char *word; + char **av; + char *temp, *tempend; +{ + register char *cp; + register struct suffixes *sufp; + register struct prefixes *prefp; + register int i, j; + int k; + int len, slen, clen; + + i = 0; + if (nav < 2) + return 0; + av[i++] = word; + cp = temp; + if (i >= (nav-1)) goto NoMoreRoom; + /* if it ends in a single digit */ + len = strlen(av[0]); + if (len > 1 && isdigit(av[0][len-1]) && !isdigit(av[0][len-2])) + { + if (((tempend-1)-cp) <= len-1) + { +#ifdef DEBUG +if (Dflag) printf("no room left\n"); +#endif + } else { + strncpy(cp, av[0], len-1); + cp[len-1] = 0; +#ifdef DEBUG +if (Dflag) printf("Added word minus trailing digit <%s>\n", cp); +#endif + av[i++] = cp; + cp += len; + if (i >= (nav-1)) goto NoMoreRoom; + } + } +#ifdef DEBUG +else if (!Dflag) ; +else if (len <= 1) printf ("length wasn't large enough\n"); +else if (!isdigit(av[0][len-1])) printf ("didn't end with a digit\n"); +else if (isdigit(av[0][len-2])) printf ("penultimate character was a digit\n"); +else printf ("something fishy about <%s>\n", av[0]); +#endif + for (j = 0; j < i; ++j) + { + len = strlen(av[j]); +#ifdef DEBUG +if (Dflag) printf("Working on <%s> at %d\n", av[j], j); +#endif + for (prefp = prefixes; prefp->prefix; ++prefp) + { + slen = strlen(prefp->prefix); + if (slen >= len || strncmp(prefp->prefix, av[j], slen)) + { +#ifdef DEBUG +if (Dflag && slen < len) +printf ("<%s> - prefix %s doesn't match %.*s\n", av[j], +prefp->prefix, slen, av[j]); +#endif + continue; + } +#ifdef DEBUG +if (Dflag) printf ("Matched prefix <%s>\n", prefp->prefix); +#endif + if (((tempend-1)-cp) <= len-slen) + { +#ifdef DEBUG +if (Dflag) printf("no room left\n"); +#endif + continue; + } + strcpy(cp, av[j]+slen); +#ifdef DEBUG +if (Dflag) printf ("Added <%s> at %d (#1)\n", cp, i); +#endif + av[i++] = cp; + while (*cp++) + ; + if (i >= (nav-1)) goto NoMoreRoom; + } + for (sufp = suffixes; sufp->suffix; ++sufp) + { + slen = strlen(sufp->suffix); + if (slen >= len || strcasecmp( + sufp->suffix, av[j]+(len-slen))) + { +#ifdef DEBUG +if (Dflag && slen < len) +printf ("<%s> - suffix %s doesn't match %s\n", av[j], +sufp->suffix, av[j]+(len-slen)); +#endif + continue; + } +#ifdef DEBUG +if (Dflag && sufp->suffix) printf ("Matched suffix <%s> <%s>\n", +sufp->suffix, sufp->replace ? sufp->replace : "(null)"); +#endif + if (!sufp->replace) + { +#ifdef DEBUG +if (Dflag) printf ("No replacment for <%s>\n", av[j]); +#endif + continue; + } + if ((tempend-1)-cp <= len + strlen(sufp->replace)+slen) + { +#ifdef DEBUG +if (Dflag) printf("no room left\n"); +#endif + continue; + } + strcpy(cp, av[j]); + strcpy(cp+(len-slen), sufp->replace); + for (k = 0; k < i; ++k) + if (!strcasecmp(cp, av[k])) + break; + if (k < i) + { +#ifdef DEBUG +if (Dflag) printf("at %d, word matches <%s>\n", k, av[k]); +#endif + continue; + } +#ifdef DEBUG +if (Dflag) printf("Added <%s> at %d (#2)\n", cp, i); +#endif + av[i++] = cp; + while (*cp++) + ; + if (i >= (nav-1)) goto NoMoreRoom; + /* look for doubled consonants */ + clen = strlen(av[i-1]); + if (clen < 3 || av[i-1][clen-1] != av[i-1][clen-2]) + continue; + if ((tempend-1)-cp <= clen-1) + { +#ifdef DEBUG +if (Dflag) printf("no room left\n"); +#endif + continue; + } + cp -= 2; + *cp = 0; + ++cp; + strcpy(cp, av[i-1]); + cp[clen-1] = cp[clen-2]; + cp[clen] = 0; +#ifdef DEBUG +if (Dflag) printf("Added <%s> at %d (#3)\n", cp, i); +#endif + av[i++] = cp; + while (*cp++) + ; + if (i >= (nav-1)) goto NoMoreRoom; + } + } +NoMoreRoom: + av[i] = 0; + return i; +} + +#ifdef DEBUG + +procword(argp) + char *argp; +{ + int nc; + char temp[MAXSTRINGSIZE*30]; +#define EXSTRSIZE 160 + char *av[EXSTRSIZE/2]; + int i; + int l, s, sl; + char *dx; + + nc = findfixes(argp, av, EXSTRSIZE, temp, temp + sizeof temp); + if (sflag) + { +#if 0 + char temp2[MAXSTRINGSIZE*30]; + char *av2[EXSTRSIZE/2]; + int nc2, j; +#endif + s = -1; + sl = strlen(argp); + for (i = 0; i < nc; ++i) + { + l = strlen(av[i]); + if (l < sl) + { +#if 0 + nc2 = findfixes(av[i], av2, EXSTRSIZE, temp2, temp2 + sizeof temp2); + for (j = 0; j < nc2; ++j) + if (strcasecmp(av[i], argp)) + break; +#endif + s = i; + sl = l; + } + } + if (s > 0) + printf ("%s: %s\n", argp, av[s]); + else + printf ("%s; %s\n", argp, argp); + return; + } + switch(i = lookup(av, dictlist, &dx)) + { + default: + printf ("possible: %s => %s found in %s\n", argp, av[i-1], dx); + break; + case 1: + printf ("exact: %s found in %s\n", argp, dx); + break; + case 0: + printf ("not found: %s\n", argp); + break; + } +} + +main(argc,argv) + char **argv; +{ + char *argp; + char **dcp; + int dostdin; + + dostdin = 1; + dcp = dictlist; + while (--argc > 0) if (*(argp = *++argv)=='-') + while (*++argp) switch(*argp) + { + case 'd': + if (argc <= 1) goto Usage; + --argc; + *dcp = *++argv; + *++dcp = 0; + break; + case 's': + ++sflag; + break; +#ifdef DEBUG + case 'D': + ++Dflag; + break; +#endif + case '-': + break; + default: + fprintf (stderr,"Bad switch char <%c>\n", *argp); + Usage: + fprintf(stderr, "Usage: look [-d dictionary] words... \n"); + exit(1); + } + else + { + dostdin = 0; + procword(argp); + } + if (dostdin) + { + char iline[512]; + while (gets(iline)) + procword(iline); + } +} +#endif + +#ifdef __svr4__ +getpagesize() +{ + return sysconf(_SC_PAGESIZE); +} +#endif Index: krb5/src/lib/kadm5/srv/obvious.c diff -u /dev/null krb5/src/lib/kadm5/srv/obvious.c:1.1 --- /dev/null Wed Nov 9 17:44:11 2005 +++ krb5/src/lib/kadm5/srv/obvious.c Mon Oct 16 11:16:05 2000 @@ -0,0 +1,363 @@ +/* + * obvious.c + */ + +#include +#include +#include +#include +#include + +/* + * some system declarations + */ + +char *rindex(), *index(), *ctime(), *crypt(); +char *malloc(), *strcpy(), *strcat(), *realloc(); +char *sbrk(); +char *strdup(); +char *ttyname(); +char *getenv(); +char *getlogin(); +long time(); +extern int errno; + +#define MAXSTRINGSIZE 512 /* usual max string length. */ + +char *badpw2(); + +/* + * a list of dictionaries (sorted with "sort -u -f") + * The first is supplied with most Unixes. + * The next three come with bsdj & netbsd and are + * a larger set of words. + * + * the last is a list of passwords derived from "crack". + * that won't be caught by our other checks. + */ + +char *dictlist[] = { + "/usr/dict/words", + "/usr/local/dict/web2", + "/usr/local/dict/web2a", + "/usr/local/dict/propernames", + "/usr/local/dict/badpasswords", + "/var/log/badpasswords", +0}; + +obvious(oldpass, newpass, who, fullname) + char *oldpass, *newpass; + char *who, *fullname; +{ + char *ep; +#define MAXWORDS 512 + char temp[MAXSTRINGSIZE*64]; + char *av[MAXWORDS]; + int na; + + if (oldpass && !strcmp(oldpass, newpass)) + { + return 1; + } + strcpy(temp, newpass); + backwards(temp); + for (ep = temp; *ep++ ; ) + ; + na = findfixes(newpass, av, 2*MAXWORDS/3, ep, temp+sizeof temp); + if (na) + ep = av[na-1]; + else + ep = 0; + if (ep < temp || ep >= temp + sizeof temp) + ep = temp; + while (*ep++) + ; + (void) findfixes(temp, av+na, MAXWORDS-na, ep, temp+sizeof temp); + if (lookup(av, dictlist, (int*)0)) + { + return 1; + } + if (ep = badpw2(newpass, who, oldpass, fullname)) + { + return 1; + } + return 0; +} + +/* + * This is (almost exactly) the uniqname "ks.c" logic - the logic + * in the University of Michigan uniqname server that enforces + * password length limits. So there. -Marcus + * + * The logic in this file is concerned with looking + * for "obvious" bad choices, such as passwors that + * are too short, or resemble other information + * we know about the person. + */ + +/* + * get the next "word" out a line. + * Not very smart, I'm afraid... + */ + +char *getword(cp, word) + register char *cp; + char *word; +{ + char *wp; + + wp = word; + while (*cp && isspace(*cp)) + ++cp; + if (!*cp) + ; + else if (isalnum(*cp)) + while (*cp && isalnum(*cp)) + *wp++ = *cp++; + else + *wp++ = *cp++; + *wp = 0; + while (*cp && isspace(*cp)) + ++cp; + return cp; +} + +/* + * look for the min. number of changes to make a word + * look "like" another word. + */ + +ecount(s1, s2, lim) + char *s1, *s2; +{ + int this, best; + + if (!*s1) + return strlen(s2); + if (!*s2) + return strlen(s1); + if (*s1 == *s2 || tolower(*s1) == tolower(*s2)) + if (!strcmp(s1+1, s2+1)) + return 0; + else { + best = ecount(s1+1, s2+1, lim); + } + else + best = 99999999; + if (best < 2) return best; + if (lim <= 0) + return best; + + /* addition */ + this = 1 + ecount(s1+1, s2, lim-1); + if (best > this) best = this; + if (best < 2) return best; + + /* deletion */ + this = 1 + ecount(s1, s2+1, lim-1); + if (best > this) best = this; + if (best < 2) return best; + + /* substitution */ + this = 1 + ecount(s1+1, s2+1, lim-1); + if (best > this) best = this; + if (best < 2) return best; + + /* transposition */ + if (s1[0] == s2[1] && s1[1] == s2[0]) + { + this = 1 + ecount(s1+2, s2+2, lim-1); + if (best > this) best = this; + } + return best; +} + +/* + * reverse a word "in place" + */ + +backwards(src) + char *src; +{ + register char *s, *d; + int l; + register char t; + + s = src; + while (*s++) + ; + + l = s-src; + + s = (src+(l/2))-1; + d = s+1; + if (!(l & 1)) --s; + + while (s >= src) + { + t = *s; + *s = *d; + *d = t; + ++d; + --s; + } +} + +/* see if a word is too much like another word. */ + +char * pwmatches(cp, name) + char *cp, *name; +{ + int len, e; + char work[80]; + + len = strlen(name); + e = ecount(cp, name, 2); + if (!e) + return "maches name"; + else if (e < 3) + return "too close to name"; + if (len < 78) + { + strcpy(work, name); + backwards(work); + e = ecount(cp, work, 2); + if (!e) + return "matches name backwards"; + else if (e < 3) + return "nearly matches name backwards"; + } + if (len < 38 && e >= (len-3) && e <= (len+3)) + { + strcpy(work, name); + strcpy(work+len, name); + e = ecount(cp, work, 2); + if (!e) + return "matches name doubled"; + else if (e < 2) + return "too close to name doubled"; + backwards(work); + e = ecount(cp, work, 2); + if (!e) + return "matches name doubled and backwards"; + else if (e < 3) + return "nearly matches name doubled and backwards"; + } + return 0; +} + +char * weakpw(cp) + char *cp; +{ + int len; + unsigned long r, r2; + int f; + int min; +#define SAYWHAT 0 +#if SAYWHAT + static char temp[80]; +#endif + f = 0; + for (len = 0; cp[len]; ++len) + if (islower(cp[len])) + f |= 1; + else if (isupper(cp[len])) + f |= 2; + else if (isdigit(cp[len])) + f |= 4; + else if (isspace(cp[len])) + f |= 8; + else + f |= 16; + r = 0; + if (f & 1) r += 26; + if (f & 2) r += 26; + if (f & 4) r += 10; + if (f & 8) r += 2; + if (f & 16) r += 10; + switch(f) + { + case 0: + return "too short"; + case 1: r = 26; cp = "not enough lower case"; break; + case 2: r = 26; cp = "not enough upper case"; break; + case 3: r = 52; cp = "not enough alpha"; break; + case 4: r = 10; cp = "not enough digits"; break; + case 7: r = 62; cp = "not enough alphanumerics"; break; + case 8: return "only whitespace?"; + default: cp = "not enough stuff"; + } + r2 = r; + for (min = 1; r < 300000000; r *= r2, ++min) + { +#if 0 +printf ("min=%d r2=%d r=%d\n", min, r2, r); +#endif + ; + } + if (len < min) + { +#if SAYWHAT + sprintf(temp, "%s, min %d [f=%d m=%d p=%d]", + cp, min, f, r2, r); + return temp; +#else + return cp; +#endif + } + return 0; +} + +/* + * check a password against loginid and gecos + */ + +char * +badpw2(password, loginid, key, gecos) + char *password, *loginid, *key, *gecos; +{ + char *result; + char *word; + char *firstlast, *last; + int len; + if (result = weakpw(password)) + return result; + if (result = pwmatches(password, loginid)) + return result; + if (key && (result = pwmatches(password, key))) + return result; + if (result = pwmatches(password, gecos)) + return result; + len = strlen(gecos); + word = malloc(512+((len+512) * 3)); + if (word) + { + firstlast = word + (len*3)/2; + *firstlast = 0; + last = 0; + for (;;) + { + gecos = getword(gecos, word); + if (!*firstlast) + { + strcpy(firstlast, word); + } else { + if (*word) + { + if (!last) + last = firstlast + + strlen(firstlast); + *last = ' '; + strcpy(last+1, word); + } + } + if (*word && (result = pwmatches(password, word))) + break; + if (!*gecos) break; + } + if (!result && last) + result = pwmatches(password, firstlast); + } + free(word); + return result; +} Index: krb5/src/lib/kadm5/srv/server_init.c diff -u krb5/src/lib/kadm5/srv/server_init.c:1.1.1.2 krb5/src/lib/kadm5/srv/server_init.c:1.4 --- krb5/src/lib/kadm5/srv/server_init.c:1.1.1.2 Mon Jul 21 16:36:06 2003 +++ krb5/src/lib/kadm5/srv/server_init.c Tue Jul 22 10:32:21 2003 @@ -180,6 +180,14 @@ return KADM5_MISSING_CONF_PARAMS; } +#if defined(UMICH_REPLICATION) + if (ret = krb5_umichrep_init(client_name, NULL/* use default */)) { + krb5_free_context(handle->context); + free(handle); + return KADM5_UMICHREP_INIT_ERR; + } +#endif /* UMICH_REPLICATION */ + /* * Set the db_name based on configuration before calling * krb5_db_init, so it will get used. @@ -294,6 +302,9 @@ destroy_dict(); +#if defined(UMICH_REPLICATION) + krb5_umichrep_destroy(); +#endif /* UMICH_REPLICATION */ adb_policy_close(handle); krb5_db_fini(handle->context); krb5_free_principal(handle->context, handle->current_caller); Index: krb5/src/lib/kadm5/srv/server_kdb.c diff -u krb5/src/lib/kadm5/srv/server_kdb.c:1.1.1.3 krb5/src/lib/kadm5/srv/server_kdb.c:1.4 --- krb5/src/lib/kadm5/srv/server_kdb.c:1.1.1.3 Tue Nov 8 16:08:44 2005 +++ krb5/src/lib/kadm5/srv/server_kdb.c Tue Nov 8 17:33:07 2005 @@ -365,6 +365,17 @@ if (ret) return(ret); +#if defined(UMICH_REPLICATION) + if (ret = krb5_umichrep_princ(kdb)) { + /* + * Notify someone that synchronization has broken! + * The DB is actually correct, but the user will get + * an error back and the replicas won't see the change... + */ + return(ret); + } +#endif /* UMICH_REPLICATION */ + return(0); } @@ -376,6 +387,18 @@ ret = krb5_db_delete_principal(handle->context, name, &one); +#if defined(UMICH_REPLICATION) + if (ret) return ret; + + if (ret = krb5_umichrep_delprinc(name)) { + /* + * Notify someone that synchronization has broken! + * The DB is actually correct, but the user will get + * an error back and the replicas won't see the change... + */ + } +#endif /* UMICH_REPLICATION */ + return ret; } Index: krb5/src/lib/kadm5/srv/svr_policy.c diff -u krb5/src/lib/kadm5/srv/svr_policy.c:1.1.1.2 krb5/src/lib/kadm5/srv/svr_policy.c:1.3 --- krb5/src/lib/kadm5/srv/svr_policy.c:1.1.1.2 Mon Jul 21 16:36:07 2003 +++ krb5/src/lib/kadm5/srv/svr_policy.c Tue Jul 22 10:32:21 2003 @@ -140,7 +140,14 @@ else pent.policy_refcnt = entry->policy_refcnt; if ((ret = osa_adb_create_policy(handle->policy_db, &pent)) == OSA_ADB_OK) +#if defined(UMICH_REPLICATION) + { + krb5_umichrep_policy(&pent); return KADM5_OK; + } +#else /* UMICH_REPLICATION */ + return KADM5_OK; +#endif /* UMICH_REPLICATION */ else return ret; } @@ -166,7 +173,14 @@ } osa_free_policy_ent(entry); if ((ret = osa_adb_destroy_policy(handle->policy_db, name)) == OSA_ADB_OK) +#if defined(UMICH_REPLICATION) + { + krb5_umichrep_delpolicy(name); + return KADM5_OK; + } +#else /* UMICH_REPLICATION */ return KADM5_OK; +#endif /* UMICH_REPLICATION */ else return ret; } @@ -244,6 +258,9 @@ p->policy_refcnt = entry->policy_refcnt; switch ((ret = osa_adb_put_policy(handle->policy_db, p))) { case OSA_ADB_OK: +#if defined(UMICH_REPLICATION) + krb5_umichrep_policy(p); +#endif /* UMICH_REPLICATION */ ret = KADM5_OK; break; case OSA_ADB_NOENT: /* this should not happen here ... */ Index: krb5/src/replication/Makefile.in diff -u /dev/null krb5/src/replication/Makefile.in:1.5 --- /dev/null Wed Nov 9 17:44:12 2005 +++ krb5/src/replication/Makefile.in Tue Nov 8 21:17:33 2005 @@ -0,0 +1,42 @@ +thisconfigdir=.. +myfulldir=replication +mydir=replication +BUILDTOP=$(REL).. +PROG_LIBPATH=-L$(TOPLIBD) +PROG_RPATH=$(KRB5_LIBDIR) + +# The following may no longer be needed with the pthread support in 1.4 +#CC= cc_r # For AIX only! + +all:: krep krepd + +CLIENTSRCS= $(srcdir)/krep.c $(srcdir)/getslaves.c +CLIENTOBJS= krep.o getslaves.o + +SERVERSRCS= $(srcdir)/krepd.c +SERVEROBJS= krepd.o + +SRCS= $(CLIENTSRCS) $(SERVERSRCS) + + +krep: $(CLIENTOBJS) $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o krep $(CLIENTOBJS) $(KRB5_BASE_LIBS) -lpthread @LIBUTIL@ + +krepd: $(SERVEROBJS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(APPUTILS_DEPLIB) + $(CC_LINK) -o krepd $(SERVEROBJS) $(KADMSRV_LIBS) $(KRB5_BASE_LIBS) $(APPUTILS_LIB) -lpthread @LIBUTIL@ + + +install:: + for f in krep krepd; do \ + $(INSTALL_PROGRAM) $$f \ + $(DESTDIR)$(SERVER_BINDIR)/`echo $$f|sed '$(transform)'`; \ + $(INSTALL_DATA) $(srcdir)/$$f.M \ + ${DESTDIR}$(SERVER_MANDIR)/`echo $$f|sed '$(transform)'`.8; \ + done + +clean:: + $(RM) $(CLIENTOBJS) $(SERVEROBJS) + +clean:: + $(RM) krep krepd + Index: krb5/src/replication/getslaves.c diff -u /dev/null krb5/src/replication/getslaves.c:1.3 --- /dev/null Wed Nov 9 17:44:12 2005 +++ krb5/src/replication/getslaves.c Mon Feb 9 09:59:08 2004 @@ -0,0 +1,138 @@ +/* + * COPYRIGHT © 1998 + * THE REGENTS OF THE UNIVERSITY OF MICHIGAN + * ALL RIGHTS RESERVED + * + * PERMISSION IS GRANTED TO USE, COPY, CREATE DERIVATIVE WORKS + * AND REDISTRIBUTE THIS SOFTWARE AND SUCH DERIVATIVE WORKS + * FOR ANY PURPOSE, SO LONG AS NO FEE IS CHARGED, AND SO LONG + * AS THE COPYRIGHT NOTICE ABOVE, THIS GRANT OF PERMISSION, + * AND THE DISCLAIMER BELOW APPEAR IN ALL COPIES MADE; AND SO + * LONG AS THE NAME OF THE UNIVERSITY OF MICHIGAN IS NOT USED + * IN ANY ADVERTISING OR PUBLICITY PERTAINING TO THE USE OR + * DISTRIBUTION OF THIS SOFTWARE WITHOUT SPECIFIC, WRITTEN + * PRIOR AUTHORIZATION. + * + * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION + * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY + * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF + * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING + * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE + * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR + * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING + * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN + * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGES. +*/ + +/* Routine to obtain a list of all the slave KDCs we need to replicate to */ + +#include "k5-int.h" +#include /* for MAXHOSTNAMELEN definition on Solaris 2.6 */ +extern int debug; + +#include "krep.h" + +krb5_error_code +krb5_getslavelist(kcontext, realmp, slave_hosts, num_hosts) + krb5_context kcontext; + krb5_data *realmp; + char ***slave_hosts; + int *num_hosts; +{ + krb5_error_code kret; + char my_hostname[MAXHOSTNAMELEN]; + int rc; + int i, j, k; + char **hostlist = NULL; + +#if defined(__aix42) + struct hostent myhostent; + struct hostent_data myhostent_data; +#endif + + *num_hosts = 0; + kret = 0; + + if ((rc = krb5int_get_fq_local_hostname(my_hostname, sizeof(my_hostname)))) + return (krb5_error_code) rc; + + if (debug) + com_err(NULL, 0, "My name as returned from krb5int_get_fq_local_hostname is '%s'", + my_hostname); + + if ((kret = krb5_get_krbhst(kcontext, realmp, &hostlist))) + return kret; + + /* Now count the number of hosts in the realm */ + *num_hosts = 0; + for (i=0; hostlist[i]; i++) + (*num_hosts)++; + + if (debug) + com_err(NULL, 0, "Found %d KDCs for realm '%s'", *num_hosts, realmp->data); + + if (*num_hosts == 0) { + kret = KRB5_REALM_UNKNOWN; + return kret; + } + + /* First remove any duplicates that may have been returned */ + /* This was supplied by Darren Reed */ + for (i = 0; hostlist[i] != NULL; i++) { + for (j = i + 1; j < *num_hosts; ) { + if (debug) + com_err(NULL, 0, "Comparing '%s' and '%s' looking for duplicates", + hostlist[i], hostlist[j]); + if (!strcasecmp(hostlist[i], hostlist[j])) { + free(hostlist[j]); + hostlist[j] = NULL; + for (k = j + 1; k < *num_hosts; k++) + hostlist[k - 1] = hostlist[k]; + (*num_hosts)--; + if (k > j + 1) + hostlist[*num_hosts] = NULL; + } else { + j++; + } + } + } + + + /* Now find and remove ourself (the master) from the list */ + for (i=0; hostlist[i]; i++) { + if (debug) + com_err(NULL, 0, "Checking '%s' to see if it is me ('%s')", + hostlist[i], my_hostname); + if (!strcasecmp(hostlist[i], my_hostname)) + break; + } + if (i < *num_hosts) { + if (debug) + com_err(NULL, 0, "Found our name, removing it from the list"); + for (j=i, k=i+1; k < *num_hosts; j++, k++) + hostlist[j] = hostlist[k]; + (*num_hosts)--; + hostlist[*num_hosts] = NULL; /* blank out last entry */ + } + else { + com_err(NULL, 0, "WARNING: Unable to find our name, '%s', " + "in the list of hosts for realm '%s'", + my_hostname, realmp->data); + } + + if (debug) + com_err(NULL, 0, "Found %d unique slave KDCs for realm '%s'", *num_hosts, realmp->data); + + if (*num_hosts == 0) { + com_err(NULL, 0, "We are the only server for realm '%s'", realmp->data); + kret = 0; /* We're the only one, but that's OK */ + return kret; + } + + *slave_hosts = hostlist; + return kret; +} + Index: krb5/src/replication/krep.M diff -u /dev/null krb5/src/replication/krep.M:1.1 --- /dev/null Wed Nov 9 17:44:12 2005 +++ krb5/src/replication/krep.M Mon Oct 16 11:21:31 2000 @@ -0,0 +1,105 @@ +.\" replication/krep.M +.\" +.\" Copyright 1992 by the Massachusetts Institute of Technology. +.\" +.\" 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. 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. +.\" +.\" +.\" COPYRIGHT © 1998 +.\" THE REGENTS OF THE UNIVERSITY OF MICHIGAN +.\" ALL RIGHTS RESERVED +.\" +.\" PERMISSION IS GRANTED TO USE, COPY, CREATE DERIVATIVE WORKS +.\" AND REDISTRIBUTE THIS SOFTWARE AND SUCH DERIVATIVE WORKS +.\" FOR ANY PURPOSE, SO LONG AS NO FEE IS CHARGED, AND SO LONG +.\" AS THE COPYRIGHT NOTICE ABOVE, THIS GRANT OF PERMISSION, +.\" AND THE DISCLAIMER BELOW APPEAR IN ALL COPIES MADE; AND SO +.\" LONG AS THE NAME OF THE UNIVERSITY OF MICHIGAN IS NOT USED +.\" IN ANY ADVERTISING OR PUBLICITY PERTAINING TO THE USE OR +.\" DISTRIBUTION OF THIS SOFTWARE WITHOUT SPECIFIC, WRITTEN +.\" PRIOR AUTHORIZATION. +.\" +.\" THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION +.\" FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY +.\" PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF +.\" MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING +.\" WITHOUT LIMITATION THE IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE +.\" REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE +.\" FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR +.\" CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING +.\" OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN +.\" IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGES. +.\" +.\" +.TH KREP 8 +.SH NAME +krep \- replicate Kerberos V5 principal database changes to all slave servers +.SH SYNOPSIS +.B krep +[\fB\-r\fP \fIrealm\fP] [\fB\-f\fP \fIfile\fP] [\fB\-d\fP] [\fB\-P\fP +\fIport\fP] [\fB\-s\fP \fIkeytab\fP] [\fB\-S\fP \fIslave\-interval\fP] +[\fB\-T\fP \fItruncation\-interval\fP] +.br +.SH DESCRIPTION +.I krep +is used to replicate changes in a Kerberos V5 database from the master +Kerberos server to all slave servers defined in the kerberos configuration +file. +This is done by transmitting dump records to the slave servers over +an encrypted, secure channel. The dump records are created by +.IR kadmind (8) +as it writes changes to the database. The changes are normally collected +in file KREP_DEFAULT_FILE (/usr/local/var/krb5kdc/master_delta). +.P +There are threads to monitor the file for new updates, truncate the file +when all slaves have been updated, and a separate thread to transmit +changes to each slave server. +.SH OPTIONS +.TP +\fB\-r\fP \fIrealm\fP +specifies the realm of the master server; by default the realm returned +by +.IR krb5_default_local_realm (3) +is used. +.TP +\fB\-f\fP \fIfile\fP +specifies the filename where the file containing delta dump records is to +be found; by default this file is KREP_DEFAULT_FILE +(normally /usr/local/var/krb5kdc/master_delta). +.TP +\fB\-P\fP \fIport\fP +specifies the port to use to contact the +.I krepd +server on the remote host. +.TP +.B \-d +prints debugging information. +.TP +\fB\-s\fP \fIkeytab\fP +specifies the location of the keytab file. +.TP +\fB\-S\fP \fIslave\-interval\fP +specifies the time in seconds that each slave thread will wait before +checking for new changes to be propagated. +.TP +\fB\-T\fP \fItruncation\-interval\fP +specifies the time in seconds that the truncation thread will wait +between checks to see if the delta file can be safely truncated. +.SH SEE ALSO +krepd(8), kadmind(8) Index: krb5/src/replication/krep.c diff -u /dev/null krb5/src/replication/krep.c:1.9 --- /dev/null Wed Nov 9 17:44:12 2005 +++ krb5/src/replication/krep.c Tue Nov 8 21:17:35 2005 @@ -0,0 +1,1594 @@ +/* + * replication/krep.c + * + * Copyright 1990,1991 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. + * + * + */ +/* + * COPYRIGHT © 1998,2000 + * THE REGENTS OF THE UNIVERSITY OF MICHIGAN + * ALL RIGHTS RESERVED + * + * PERMISSION IS GRANTED TO USE, COPY, CREATE DERIVATIVE WORKS + * AND REDISTRIBUTE THIS SOFTWARE AND SUCH DERIVATIVE WORKS + * FOR ANY PURPOSE, SO LONG AS NO FEE IS CHARGED, AND SO LONG + * AS THE COPYRIGHT NOTICE ABOVE, THIS GRANT OF PERMISSION, + * AND THE DISCLAIMER BELOW APPEAR IN ALL COPIES MADE; AND SO + * LONG AS THE NAME OF THE UNIVERSITY OF MICHIGAN IS NOT USED + * IN ANY ADVERTISING OR PUBLICITY PERTAINING TO THE USE OR + * DISTRIBUTION OF THIS SOFTWARE WITHOUT SPECIFIC, WRITTEN + * PRIOR AUTHORIZATION. + * + * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION + * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY + * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF + * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING + * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE + * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR + * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING + * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN + * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGES. +*/ + +#if defined(UMICH_REPLICATION) +#if defined(sun) +#define _REENTRANT +#endif +#include +#endif /* UMICH_REPLICATION */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "k5-int.h" +#include "com_err.h" +#include "krep.h" + +#define SYSLOG_CLASS LOG_DAEMON +#define TEMP_CACHE_NAME_FORMAT "/tmp/kreptktXXXXXX" + +static char *krep_version = KREP_PROT_VERSION; + +char *progname = 0; +int debug = 0; +char *srvtab = 0; +char *realm = 0; +char *file = KREP_DEFAULT_FILE; +short port = 0; + +#if defined(UMICH_REPLICATION) + +int nofork = 0; +int verbose = 1; + +/* + * The following values are defaults and may + * be changed via command line + */ +int slaveThreadCheckTime = 15; /* # of secs between checks for more work */ +int truncThreadCheckTime = 30; /* # of secs between checks for truncation */ + +/* + * Made the following global so that the status signal + * handler can have access to them... + */ + +int num_hosts; +/* The following data items are protected via repl_stateMutex */ +pthread_mutex_t repl_stateMutex; +slave_data *slave_info; +trunc_info t_thread; +int truncation_count = 0; +/* End of items protected via repl_stateMutex */ + +/* Another mutex to serialize calls to kerberos libraries */ +pthread_mutex_t repl_kerblibMutex; + +#endif /* UMICH_REPLICATION */ + +krb5_principal my_principal; /* The Kerberos principal we'll be */ + /* running under, initialized in */ + /* get_tickets() */ + +/* Forward prototypes */ + +void PRS (int, char **); +void get_tickets (krb5_context, char *, slave_data *tt); +static void usage (void); +krb5_error_code open_connection(slave_data *tt, char *Errmsg, size_t ErrmsgSz); +int kerberos_authenticate (krb5_context, krb5_auth_context *, + int, krb5_principal, krb5_creds **, slave_data *tt); +void send_error (krb5_context, krb5_creds *, int, char *, krb5_error_code); + +#if defined(UMICH_REPLICATION) + +void signalBrokenPipe(int code); +void signalGetStatus(int code); +int repl_get_work( slave_data *tt, char *buffer, size_t bufsize, int *bytesread ); +void update_file_position( slave_data *tt, off_t offset ); +void connect_and_authenticate(slave_data *tt, + krb5_auth_context *auth_contextP, + krb5_creds **credsPP); +void * replication_thread_routine(void *parm ); +void * truncation_thread_routine(void *parm ); + +int xmit_dbinfo + (krb5_context, krb5_auth_context, krb5_creds *, + int, char *, int, char*); + +void signalBrokenPipe(int code) +{ + com_err(progname, 0, "Caught broken pipe signal"); +} + +/* + * This routine is called as a result of a SIGUSR1 signal being + * issued. It prints the status of all the threads. (Note that + * com_err is re-routed to syslog if not in debug mode.) + */ +void signalGetStatus(int code) +{ + int i; + + /* + * We do not get the mutex, just in case some thread is + * stuck while holding it. + */ + + com_err(NULL, 0, "The latest truncation count is %d", truncation_count); + for (i = 0; i < num_hosts; i++) + { + com_err(NULL, 0, + "---------- Thread number %d information ----------", i); + com_err(NULL, 0, " Hostname : %s", + slave_info[i].sl_hostname); + com_err(NULL, 0, " Delta file fd : %d", + slave_info[i].sl_fd); + com_err(NULL, 0, " Latest position : %d", + slave_info[i].sl_lastPosition); + com_err(NULL, 0, " Socket fd : %d", + slave_info[i].sl_socket); + com_err(NULL, 0, " Current state : %s", + slave_info[i].sl_currentState == REPL_STATE_IDLE ? "idle" : + slave_info[i].sl_currentState == REPL_STATE_PROCESSING ? + "processing" : "unknown"); + com_err(NULL, 0, " Truncation vers : %d", + slave_info[i].sl_lastTruncation); + } +} + + +/* + * Checks for new unprocessed entries in the delta file. + * Returns non-zero if there is work for the thread to do. + */ +int repl_get_work( slave_data *tt, char *buffer, size_t bufsize, int *bytesread ) +{ + int rc; + int workToDo = 0; /* Assume no work on entry */ + + /* Obtain state update lock */ + if ((rc = pthread_mutex_lock(tt->sl_stateUpdateMutex))) + { + com_err(progname, errno, "%s: failed to obtain mutex (%d)", + tt->sl_hostname, rc); + pthread_exit((void *) 1); + } + + /* Get a shared lock on the file */ + if ((rc = krb5_lock_file(0, tt->sl_fd, KRB5_LOCKMODE_SHARED)) ) + { + com_err(progname, errno, + "%s: error (%d) obtaining shared lock on delta file", + tt->sl_hostname, rc); + pthread_exit((void *) 1); + } + + /* Clear the buffer */ + memset(buffer, '\0', bufsize); + + /* Get current stat information for the file */ + if (fstat(tt->sl_fd, &tt->sl_statinfo)) + { + com_err(progname, errno, "%s: error obtaining delta file stat info", + tt->sl_hostname); + } + else + { + if (tt->sl_statinfo.st_size == 0 || + *tt->sl_truncationCount > tt->sl_lastTruncation) + { + /* File has been truncated */ + tt->sl_lastPosition = (off_t)0; + tt->sl_lastTruncation = *tt->sl_truncationCount; + fseek(tt->sl_file, (off_t)0, SEEK_SET); + } + } + + if ( fgets(buffer, (int)bufsize, tt->sl_file) != NULL) + { + if (buffer[strlen(buffer) - 1] == '\n') + buffer[strlen(buffer) - 1] = '\0'; + *bytesread = strlen(buffer); + workToDo = 1; + } + + if (workToDo) + tt->sl_currentState = REPL_STATE_PROCESSING; + else + tt->sl_currentState = REPL_STATE_IDLE; + + /* Release shared lock on the file */ + krb5_lock_file(0, tt->sl_fd, KRB5_LOCKMODE_UNLOCK); + + /* Release state update lock */ + if ((rc = pthread_mutex_unlock(tt->sl_stateUpdateMutex))) + { + com_err(progname, errno, "%s: failed to release mutex (%d)", + tt->sl_hostname, rc); + pthread_exit((void *) 1); + } + + if (debug) + com_err(progname, 0, "%s: There is%s work for this thread to do", + tt->sl_hostname, workToDo ? "" : " NOT"); + return workToDo; +} + +/* + * Update thread's file position (after receiving ack from slave) + */ +void update_file_position( slave_data *tt, off_t offset ) +{ + int rc; + + /* Obtain state update lock */ + if ((rc = pthread_mutex_lock(tt->sl_stateUpdateMutex))) + { + com_err(progname, errno, "%s: failed to obtain mutex (%d)", + tt->sl_hostname, rc); + pthread_exit((void *) 1); + } + + /* Get a shared lock on the file */ + if ((rc = krb5_lock_file(0, tt->sl_fd, KRB5_LOCKMODE_SHARED)) ) + { + com_err(progname, errno, + "%s: error (%d) obtaining shared lock on delta file", + tt->sl_hostname, rc); + pthread_exit((void *) 1); + } + + /* + * If offset is supplied, then we assume that we want to reset back + * to the last place where we were, so we fseek to lastPosition. + */ + if (offset) + { + if (debug) + com_err(progname, 0, "%s: Resetting file position...", + tt->sl_hostname); + if (-1 == fseek(tt->sl_file, (long) tt->sl_lastPosition, SEEK_SET)) { + com_err(progname, errno, "%s: error (%d) resetting file position", + tt->sl_hostname, errno); + pthread_exit((void *) 1); + } + } + + /* Get the current file position */ + tt->sl_lastPosition = (off_t) ftell(tt->sl_file); + + if (debug) + com_err(progname, 0, + "%s: lastTrunc %u, lastPosition %u, currentState 0x%08x", + tt->sl_hostname, tt->sl_lastTruncation, + tt->sl_lastPosition, tt->sl_currentState); + + /* Release shared lock on the file */ + krb5_lock_file(0, tt->sl_fd, KRB5_LOCKMODE_UNLOCK); + + /* Release state update lock */ + if ((rc = pthread_mutex_unlock(tt->sl_stateUpdateMutex))) + { + com_err(progname, errno, "%s: failed to release mutex (%d)", + tt->sl_hostname, rc); + pthread_exit((void *) 1); + } +} + +/* + * Called for initial connection to, and authentication with, a remote + * krepd on a slave KDC machine. Also called to reconnect after a + * broken pipe is encountered. Doesn't return until successfully + * connected and authenticated. + */ +void connect_and_authenticate(slave_data *tt, + krb5_auth_context *auth_contextP, + krb5_creds **credsPP) +{ + char Errmsg[256]; + krb5_error_code retval; + + /* + * If we don't have a connection, then keep trying every 15 seconds + * until we get a connection. Then authenticate until it works... + */ + if (tt->sl_socket < 0) + { + struct timeval sleeptime; + + com_err(NULL, 0, "Opening connection with %s", tt->sl_hostname); + + do { + if ((retval = open_connection(tt, Errmsg, sizeof(Errmsg)))) + { + com_err(progname, retval, "%s while opening connection to %s", + Errmsg, tt->sl_hostname); + + sleeptime.tv_sec = 15; + sleeptime.tv_usec = 0; + select(0, 0, 0, 0, &sleeptime); + } + } while (tt->sl_socket < 0); + + com_err(NULL, 0, "Authenticating with %s", tt->sl_hostname); + + while(kerberos_authenticate(tt->sl_context, auth_contextP, + tt->sl_socket, my_principal, credsPP, tt)) + { + /* Error messages are printed by routine, wait and try again... */ + sleeptime.tv_sec = 15; + sleeptime.tv_usec = 0; + select(0, 0, 0, 0, &sleeptime); + } + + /* + * Initialize the initial vector. (Only once per connection) + */ + if ((retval = krb5_auth_con_initivector(tt->sl_context, *auth_contextP))) { + com_err(progname, retval, "while allocating i_vector (%s)", + tt->sl_hostname); + return; + } + + com_err(NULL, 0, "Successfully connected with %s", tt->sl_hostname); + + } +} + +/* + * main routine for a thread replicating to a specific slave machine. There + * is one of these threads for each slave machine in the realm. Each thread + * has an individual file pointer reading the delta file so that each thread + * can be in a different place in the file. + * Loops forever checking for new work to do and sending the changes to + * the slave machine that it services.. + */ +void * replication_thread_routine(void *parm ) +{ + slave_data *tt = (slave_data *) parm; /* tt ==> This Thread */ + int rc; + int xmit_rc; + char *buffer; + size_t bufferSize = KREP_BUFSIZ; + struct timeval sleeptime; + + krb5_auth_context auth_context; + krb5_creds *credsP = &tt->sl_creds; + krb5_creds **credsPP = &credsP; +#ifdef POSIX_SIGNALS + struct sigaction sigpipe_action; +#endif + + /* Obtain state update lock */ + if ((rc = pthread_mutex_lock(tt->sl_stateUpdateMutex))) + { + com_err(progname, errno, "%s: failed to obtain state update mutex (%d)", + tt->sl_hostname, rc); + pthread_exit((void *) 1); + } + +#ifdef POSIX_SIGNALS + sigpipe_action.sa_handler = signalBrokenPipe; + sigemptyset(&sigpipe_action.sa_mask); + sigpipe_action.sa_flags = 0; + sigaction(SIGPIPE, &sigpipe_action, NULL); +#else + (void) signal(SIGPIPE, signalBrokenPipe); +#endif + + if (NULL == (buffer = (char *) malloc(bufferSize))) + { + com_err(progname, errno, "%s: failed to allocate %d bytes for buffer", + tt->sl_hostname, bufferSize); + pthread_exit((void *) 1); + } + + if (debug) + com_err(progname, 0, "%s: Opening file %s for input ...", + tt->sl_hostname, tt->sl_replfile); + if ((tt->sl_file = fopen(tt->sl_replfile, "r")) == NULL ) + { + com_err(progname, errno, "%s: failed to open file %s", + tt->sl_hostname, tt->sl_replfile); + pthread_exit((void *) 1); + } + + tt->sl_fd = fileno(tt->sl_file); + + /* Use non-blocking I/O */ + if (-1 == (rc = fcntl(tt->sl_fd, F_SETFL, FNDELAY))) + { + com_err(progname, errno, + "%s: failed to set file %s for non-blocking I/O", + tt->sl_hostname, tt->sl_replfile); + pthread_exit((void *) 1); + } + if (debug) + com_err(progname, 0, "%s: file is now open", tt->sl_hostname); + + /* Release state update lock */ + if ((rc = pthread_mutex_unlock(tt->sl_stateUpdateMutex))) + { + com_err(progname, errno, "%s: failed to release state update mutex (%d)", + tt->sl_hostname, rc); + pthread_exit((void *) 1); + } + + for (;;) { + int bytesread; + +reconnect: + connect_and_authenticate(tt, &auth_context, credsPP); + + /* + * repl_check_for_work returns non-zero if + * there is new work to do + */ + while (repl_get_work(tt, buffer, bufferSize, &bytesread )) + { + if (verbose || debug) + { + /* Print a readable log message */ + + char namebuff[256]; + char msgbuff[256]; + int numscanned = 0; + + if (0 == strncmp(buffer, "princ", 5)) + { + numscanned = sscanf(buffer, + "%*s\t%*d\t%*d\t%*d\t%*d\t%*d%s\t", + &namebuff[0]); + sprintf(msgbuff, "principal '%s'", namebuff); + } + else if (0 == strncmp(buffer, "delprinc", 8)) + { + numscanned = sscanf(buffer, "%*s\t%*d\t%s\t", &namebuff[0]); + if (1 == numscanned) + namebuff[strlen(namebuff)-1] = '\0'; + sprintf(msgbuff, "delete principal '%s'", namebuff); + } + else if (0 == strncmp(buffer, "policy", 6)) + { + numscanned = sscanf(buffer, "%*s\t%s\t", &namebuff[0]); + sprintf(msgbuff, "policy '%s'", namebuff); + } + else if (0 == strncmp(buffer, "delpolicy", 9)) + { + numscanned = sscanf(buffer, "%*s\t%*d\t%s\t", &namebuff[0]); + if (1 == numscanned) + namebuff[strlen(namebuff)-1] = '\0'; + sprintf(msgbuff, "delete policy '%s'", namebuff); + } + if (numscanned != 1) + sprintf(msgbuff, "Error scanning record: '%.60s'", buffer); + + com_err(NULL, 0, "%s: %s", tt->sl_hostname, msgbuff); + } + + /* Obtain kerberos library lock */ + if ((rc = pthread_mutex_lock(tt->sl_kerberosLibMutex))) + { + com_err(progname, errno, "%s: failed to obtain kerberos library mutex (%d)", + tt->sl_hostname, rc); + pthread_exit((void *) 1); + } + + xmit_rc = xmit_dbinfo(tt->sl_context, auth_context, &tt->sl_creds, + tt->sl_socket, buffer, bytesread, tt->sl_hostname); + + /* Release kerberos library lock */ + if ((rc = pthread_mutex_unlock(tt->sl_kerberosLibMutex))) + { + com_err(progname, errno, "%s: failed to release kerberos library mutex (%d)", + tt->sl_hostname, rc); + pthread_exit((void *) 1); + } + + if (xmit_rc) + { + /* + * It looks like transmission failed, back up our position in + * the file so we retry the last record. Then go back and + * reconnect and authenticate with the server... + */ + + com_err(NULL, 0, + "%s: Transmission failed, backing up our " + "file position and reconnecting...", tt->sl_hostname); + + update_file_position(tt, -(bytesread+1) ); + + close(tt->sl_socket); + tt->sl_socket = -1; + goto reconnect; + } + else + { + update_file_position(tt, 0); + } + } + + /* Sleep for a while before checking for more work */ + sleeptime.tv_sec = tt->sl_sleeptime; + sleeptime.tv_usec = 0; + select(0, 0, 0, 0, &sleeptime); + } +} + +/* + * One of these threads per krep. It monitors the status of the slave + * threads and the delta file. If all the slave threads are caught up + * to the end of the delta file, then the file is truncated so that it + * doesn't grow infinitely. A mutex is used to protect against a slave + * thread's status changing while this thread examines all the slave + * threads. File locking is used to protect against kadmind appending + * to the file while we are truncating it. + */ +void * truncation_thread_routine(void *parm ) +{ + trunc_info *ti = (trunc_info *) parm; + struct timeval sleeptime; + char threadname[] = "Truncation"; + struct stat secondStat; + slave_data *slave; + int doTruncate; + int i; + int rc; + + sleeptime.tv_sec = ti->tr_sleeptime; + sleeptime.tv_usec = 0; + if (debug) + com_err(progname, 0, + "%s: running - sleeping for %d seconds before processing", + threadname, sleeptime.tv_sec); + select(0, 0, 0, 0, &sleeptime); + + if (debug) { + com_err(progname, 0, "%s: beginning processing...", threadname); + com_err(progname, 0, "%s: Opening file %s ...", + threadname, ti->tr_replfile); + } + + if ((ti->tr_file = fopen(ti->tr_replfile, "r+")) == NULL) + { + com_err(progname, errno, "%s: failed to open delta file %s", + threadname, ti->tr_replfile); + pthread_exit((void *) 1); + } + + ti->tr_fd = fileno(ti->tr_file); + + if (debug) + com_err(progname, 0, "%s: file is now open", threadname); + + for (;;) + { + /* Assume that we're not going to truncate */ + doTruncate = 0; + + /* Obtain state update lock */ + if ((rc = pthread_mutex_lock(ti->tr_stateUpdateMutex))) + { + com_err(progname, errno, "%s: failed to obtain mutex (%d)", + threadname, rc); + pthread_exit((void *) 1); + } + + /* + * If all slave threads are idle and have processed to the current end + * of the file, then lock the file (so that krbkdc doesn't update it), + * truncate the file and set the flags so that the slave threads know + * that it's been truncated + */ + + /* Get current stat information for the file */ + if (fstat(ti->tr_fd, &ti->tr_statinfo)) + { + com_err(progname, errno, + "%s: error obtaining delta file stat info", + threadname); + } + else + { + if (ti->tr_statinfo.st_size != 0) + { + doTruncate = 1; + for (i = 0, slave = ti->tr_slaves; + i < ti->tr_numSlaves; + i++, slave++) + { + if (slave->sl_currentState != REPL_STATE_IDLE || + slave->sl_lastPosition != ti->tr_statinfo.st_size || + slave->sl_lastTruncation != *ti->tr_truncationCount) + { + doTruncate = 0; + break; + } + } + } + } + + if (doTruncate) + { + /* + * - lock the file + * - get another stat to make sure it hasn't + * changed while checking the slave threads + * - truncate the file + * - set the flags so that slave threads know file + * has been truncated + */ + + /* lock the file */ + + if (krb5_lock_file(0, ti->tr_fd, KRB5_LOCKMODE_EXCLUSIVE) ) + { + com_err(progname, errno, + "%s: unable to obtain delta file lock, " + "skipping truncation", threadname); + } + else + { + if (fstat(ti->tr_fd, &secondStat)) + { + com_err(progname, errno, "%s: error obtaining delta file " + "stat info", threadname); + } + else + { + if (ti->tr_statinfo.st_mtime != secondStat.st_mtime) + { + com_err(progname, 0, + "%s: file changed before I could truncate it", + threadname); + } + else + { + if (debug) + com_err(progname, 0, + "%s: TRUNCATING FILE !!!!!!!", + threadname); + + if ( ftruncate(ti->tr_fd, 0) ) + { + com_err(progname, errno, + "%s: could not truncate delta file '%s', " + "error %d", + threadname, ti->tr_replfile, errno); + } + else + { + *ti->tr_truncationCount += 1; + } + } + } + /* unlock the file */ + krb5_lock_file(0, ti->tr_fd, KRB5_LOCKMODE_UNLOCK); + } + } + + /* Release state update lock */ + if ((rc = pthread_mutex_unlock(ti->tr_stateUpdateMutex))) + { + com_err(progname, errno, "%s: failed to release mutex (%d)", + threadname, rc); + pthread_exit((void *) 1); + } + + /* Sleep for a while before checking again */ + sleeptime.tv_sec = ti->tr_sleeptime; + sleeptime.tv_usec = 0; + if (debug) + com_err(progname, 0, + "%s: sleeping for %d seconds before checking again", + threadname, sleeptime.tv_sec); + select(0, 0, 0, 0, &sleeptime); + } + return ((void *) 0); +} +#endif /* UMICH_REPLICATION */ + +static void usage() +{ + com_err(progname, 0, "\nUsage: %s [-r realm] [-f file] [-d] [-n] [-q] [-P port] \n" + "\t[-s srvtab] [-S slave_delay] [-T trunc_delay] \n\n", + progname); + exit(1); +} + +int +main(argc, argv) + int argc; + char **argv; +{ + krb5_error_code retval; + krb5_context main_context; +#if defined(UMICH_REPLICATION) + char **slave_hosts = NULL; + int i; + krb5_data my_realm; + int rc; +#ifdef POSIX_SIGNALS + struct sigaction sigusr1_action; +#endif + +#endif /* UMICH_REPLICATION */ + + retval = krb5_init_context(&main_context); + if (retval) { + com_err(argv[0], retval, "while initializing krb5"); + exit(1); + } + PRS(argc, argv); + +#ifdef POSIX_SIGNALS + sigusr1_action.sa_handler = signalGetStatus; + sigemptyset(&sigusr1_action.sa_mask); + sigusr1_action.sa_flags = 0; + sigaction(SIGUSR1, &sigusr1_action, NULL); +#else + (void) signal(SIGUSR1, signalGetStatus); +#endif + +#if defined(UMICH_REPLICATION) + + if (!nofork) + daemon(0, 0); + + /* + * If realm was not specified, get our default realm + */ + if (!realm) { + if ((retval = krb5_get_default_realm(main_context, &realm))) { + com_err(progname, retval, + "obtaining realm name"); + exit(1); + } + } + if ((my_realm.data = (char *) malloc(strlen(realm)+1)) == (char *) NULL) + { + com_err(progname, ENOMEM, + "allocating memory for realm information"); + exit(1); + } + my_realm.magic = KV5M_DATA; + my_realm.length = strlen(realm); + strcpy(my_realm.data, realm); + + /* + * Get a list of the slave servers for the specified + * realm (leaving ourself off the list) + */ + if ((retval = krb5_getslavelist(main_context, &my_realm, + &slave_hosts, &num_hosts))) { + com_err(progname, retval, + "while getting list of slave KDCs for realm %s", realm); + exit(1); + } + + if (num_hosts > 0) + { + /* + * Allocate memory for information about each slave server + * for use by the thread that will get started for each slave + */ + if ((slave_info = + (slave_data *)malloc(sizeof(slave_data) * num_hosts)) == NULL) { + com_err(progname, ENOMEM, + "allocating memory for thread information"); + exit(1); + } + memset(slave_info, '\0', sizeof(slave_data) * num_hosts); + } + + /* + * Initialize and lock a mutex to protect the slave information + */ + if ((rc = pthread_mutex_init(&repl_stateMutex, pthread_mutexattr_default))) + { + com_err(progname, errno, "Error %d initializing file mutex", rc); + exit(1); + } + + if ((rc = pthread_mutex_lock(&repl_stateMutex))) + { + com_err(progname, errno, + "Error %d locking mutex before threads are started!", + rc); + exit(1); + } + + /* + * Initialize and lock a mutex to serialize use of the kerberos library + */ + if ((rc = pthread_mutex_init(&repl_kerblibMutex, pthread_mutexattr_default))) + { + com_err(progname, errno, "Error %d initializing kerberos library mutex", rc); + exit(1); + } + if ((rc = pthread_mutex_lock(&repl_kerblibMutex))) + { + com_err(progname, errno, + "Error %d locking kerberos library mutex while starting threads!", + rc); + exit(1); + } + + /* + * Start a thread to handle updates for each slave + */ + for (i = 0; i < num_hosts ; i++) + { + /* Create a separate context for each thread */ + retval = krb5_init_context(&slave_info[i].sl_context); + if (retval) { + com_err(argv[0], retval, "while initializing krb5 for thread (%s)", + slave_hosts[i]); + exit(1); + } + get_tickets(slave_info[i].sl_context, slave_hosts[i], &slave_info[i]); + + slave_info[i].sl_socket = -1; /* disconnected */ + slave_info[i].sl_replfile = file; + slave_info[i].sl_stateUpdateMutex = &repl_stateMutex; + slave_info[i].sl_kerberosLibMutex = &repl_kerblibMutex; + slave_info[i].sl_truncationCount = &truncation_count; + slave_info[i].sl_sleeptime = slaveThreadCheckTime; + if (!(slave_info[i].sl_hostname = (char *) malloc(50))) { + com_err(progname, errno, "Allocating storage for slave hostname (%s)", + slave_hosts[i]); + exit(1); + } + sprintf(slave_info[i].sl_hostname, "%s", slave_hosts[i]); + if (debug) + com_err(progname, 0, + "Creating thread #%d (%s)", i, slave_hosts[i]); + if ((rc = pthread_create(&slave_info[i].sl_id, + pthread_attr_default, + replication_thread_routine, + (void *)(&slave_info[i]) ))) + { + com_err(progname, errno, + "error %d creating slave service thread (%s)...", + rc, slave_info[i].sl_hostname ); + exit(1); + } + } + + /* Release lock serializing kerberos library use */ + if ((rc = pthread_mutex_unlock(&repl_kerblibMutex))) + { + com_err(progname, errno, + "Error %d unlocking kerberos library mutex while starting threads!", + rc); + exit(1); + } + + /* + * Start an extra thread to handle truncation of the + * delta file. + */ + t_thread.tr_replfile = file; + t_thread.tr_stateUpdateMutex = &repl_stateMutex; + t_thread.tr_numSlaves = num_hosts; + t_thread.tr_slaves = slave_info; + t_thread.tr_truncationCount = &truncation_count; + t_thread.tr_sleeptime = truncThreadCheckTime; + if ((rc = pthread_create(&t_thread.tr_id, + pthread_attr_default, + truncation_thread_routine, + (void *)(&t_thread) ))) + { + com_err(progname, errno, + "error %d creating truncation service thread...", + rc); + exit(1); + } + + /* + * Set up to watch for any update thread terminating and attempt + * to restart it... + */ + + /* BUT FOR NOW ... just wait forever */ + if (debug) + com_err(progname, 0, + "Main is waiting 5 seconds before unlocking mutex"); + sleep(5); + + if ((rc = pthread_mutex_unlock(&repl_stateMutex))) + { + com_err(progname, errno, + "Error %d unlocking mutex before sleeping", rc); + exit(1); + } + if (debug) + com_err(progname, 0, + "Main is now going into infinite wait after unlocking mutex"); + + for (;;) + { + sleep(10000); + } + + com_err(progname, 0, "Main thread returned from infinite sleep??"); + exit(0); +} + +/* + * redirects com_err messages to syslog when not in debug mode + */ +static void +krep_com_err_proc(whoami, code, fmt, args) + const char *whoami; + long code; + const char *fmt; + va_list args; +{ + char error_buf[8096]; + + error_buf[0] = '\0'; + if (fmt) + vsprintf(error_buf, fmt, args); + syslog(LOG_ERR, "%s%s%s%s%s", whoami ? whoami : "", whoami ? ": " : "", + code ? error_message(code) : "", code ? " " : "", error_buf); +} + +#endif /* UMICH_REPLICATION */ + +void PRS(argc, argv) + int argc; + char **argv; +{ + register char *word, ch; + + progname = *argv++; + while (--argc && (word = *argv++)) { + if (*word == '-') { + word++; + while (word && (ch = *word++)) { + switch(ch){ + case 'r': + if (*word) + realm = word; + else + realm = *argv++; + if (!realm) + usage(); + word = 0; + break; + case 'f': + if (*word) + file = word; + else + file = *argv++; + if (!file) + usage(); + word = 0; + break; + case 'd': + debug++; + break; + case 'P': + if (*word) + port = htons(atoi(word)); + else + port = htons(atoi(*argv++)); + if (!port) + usage(); + word = 0; + break; + case 's': + if (*word) + srvtab = word; + else + srvtab = *argv++; + if (!srvtab) + usage(); + word = 0; + break; +#if defined(UMICH_REPLICATION) + case 'n': + nofork++; + break; + case 'q': + verbose = 0; + break; + case 'S': + if (*word) + slaveThreadCheckTime = atoi(word); + else + slaveThreadCheckTime = atoi(*argv++); + if (!slaveThreadCheckTime) + usage(); + word = 0; + break; + case 'T': + if (*word) + truncThreadCheckTime = atoi(word); + else + truncThreadCheckTime = atoi(*argv++); + if (!truncThreadCheckTime) + usage(); + word = 0; + break; +#endif /* UMICH_REPLICATION */ + default: + usage(); + } + + } + } else { + usage(); + } + } +#if defined(UMICH_REPLICATION) + /* + * If not is debug mode, switch com_err reporting to syslog + */ + if (! debug) { + openlog("krep", LOG_PID | LOG_ODELAY, SYSLOG_CLASS); + set_com_err_hook(krep_com_err_proc); + } +#endif /* UMICH_REPLICATION */ +} + +void get_tickets(context, slave_host, tt) + krb5_context context; + char *slave_host; + slave_data *tt; +{ + char buf[BUFSIZ]; + krb5_error_code retval; + char tkstring[30]; + krb5_keytab keytab = NULL; + + /* + * Figure out what tickets we'll be using to send stuff + */ + retval = krb5_sname_to_principal(context, NULL, NULL, + KRB5_NT_SRV_HST, &my_principal); + + if (debug) { + krb5_error_code local_retval; + char *my_principal_name = 0; + if ((local_retval = krb5_unparse_name(context, my_principal, &my_principal_name)) == 0) { + com_err(progname, 0, "%s: my principal name is '%s'", + slave_host, my_principal_name); + krb5_free_unparsed_name(context, my_principal_name); + } + } + + if (retval) { + com_err(progname, errno, "while setting client principal name (%s)", slave_host); + exit(1); + } + if (realm) { + (void) krb5_xfree(krb5_princ_realm(context, my_principal)->data); + krb5_princ_set_realm_length(context, my_principal, strlen(realm)); + krb5_princ_set_realm_data(context, my_principal, strdup(realm)); + } + + /* + * Initialize cache file which we're going to be using + */ + strcpy(tkstring, TEMP_CACHE_NAME_FORMAT); + (void) mkstemp(tkstring); + sprintf(buf, "FILE:%s", tkstring); + if (debug) + com_err(progname, 0, "%s: Using credentials cache file '%s'", + slave_host, buf); + if ((retval = krb5_cc_resolve(context, buf, &tt->sl_ccache))) { + com_err(progname, retval, "while opening credential cache %s for %s", + buf, slave_host); + exit(1); + } + if ((retval = krb5_cc_initialize(context, tt->sl_ccache, my_principal))) { + com_err (progname, retval, "when initializing cache %s for %s", + buf, slave_host); + exit(1); + } + + /* + * Get the tickets we'll need. + * + * Construct the principal name for the slave host. + */ + memset((char *)&tt->sl_creds, 0, sizeof(tt->sl_creds)); + retval = krb5_sname_to_principal(context, + slave_host, KREP_SERVICE_NAME, + KRB5_NT_SRV_HST, &tt->sl_creds.server); + if (debug) { + krb5_error_code local_retval; + char *slave_principal_name = 0; + if ((local_retval = krb5_unparse_name(context, tt->sl_creds.server, &slave_principal_name)) == 0) { + com_err(progname, 0, "%s: using principal name '%s' for this slave", + slave_host, slave_principal_name); + krb5_free_unparsed_name(context, slave_principal_name); + } + } + + if (retval) { + com_err(progname, errno, "while setting server principal name for %s", slave_host); + (void) krb5_cc_destroy(context, tt->sl_ccache); + exit(1); + } + if (realm) { + (void) krb5_xfree(krb5_princ_realm(context, tt->sl_creds.server)->data); + krb5_princ_set_realm_length(context, tt->sl_creds.server, strlen(realm)); + krb5_princ_set_realm_data(context, tt->sl_creds.server, strdup(realm)); + } + + /* + * Now fill in the client.... + */ + if ((retval = krb5_copy_principal(context, my_principal, &tt->sl_creds.client))) { + com_err(progname, retval, "While copying client principal for %s", slave_host); + (void) krb5_cc_destroy(context, tt->sl_ccache); + exit(1); + } + if (srvtab) { + if ((retval = krb5_kt_resolve(context, srvtab, &keytab))) { + com_err(progname, retval, "while resolving keytab for %s", slave_host); + (void) krb5_cc_destroy(context, tt->sl_ccache); + exit(1); + } + } + + retval = krb5_get_in_tkt_with_keytab(context, 0, 0, NULL, + NULL, keytab, tt->sl_ccache, &tt->sl_creds, 0); + if (retval) { + com_err(progname, retval, "while getting initial ticket for %s", slave_host); + (void) krb5_cc_destroy(context, tt->sl_ccache); + exit(1); + } + + if (keytab) + (void) krb5_kt_close(context, keytab); + + /* + * Now destroy the cache right away --- the credentials we + * need will be in tt->sl_creds. + */ + if ((retval = krb5_cc_destroy(context, tt->sl_ccache))) { + com_err(progname, retval, "while destroying ticket cache for %s", slave_host); + exit(1); + } +} + +krb5_error_code +open_connection(slave_data *tt, char *Errmsg, size_t ErrmsgSz) +{ + int s; + krb5_error_code retval; + + struct sockaddr_storage s_sa; + socklen_t s_sa_len; + struct sockaddr *sap; + + struct addrinfo *ailp, *aip, aihints; + char port_string[NI_MAXSERV]; + + memset(&aihints, 0, sizeof(aihints)); + aihints.ai_socktype = SOCK_STREAM; + aihints.ai_flags = AI_CANONNAME; + /* aihints.ai_protocol = IPPROTO_IP; */ + if (port) + snprintf(port_string, sizeof(port_string), "%u", port); + else + snprintf(port_string, sizeof(port_string), "%s", KREP_SERVICE); + + if (debug) + com_err(progname, 0, "%s: Calling getaddrinfo for host '%s' port '%s'", + tt->sl_hostname, tt->sl_hostname, port_string); + + retval = getaddrinfo(tt->sl_hostname, port_string, &aihints, &ailp); + if (retval) { + snprintf(Errmsg, ErrmsgSz, "getaddrinfo for host '%s' port '%s': %s", + tt->sl_hostname, port_string, gai_strerror(retval)); + return(EINVAL); + + } + + for (aip = ailp; aip; aip = aip->ai_next) { + if (debug) + com_err(progname, 0, "%s: Trying socket with AF: %d SOCKTYPE: %d PROTO: %d Last one?: %s", + tt->sl_hostname, aip->ai_family, + aip->ai_socktype, aip->ai_protocol, + aip->ai_next ? "no": "yes"); + s = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); + + if (s < 0) { + retval = errno; + (void) snprintf(Errmsg, ErrmsgSz, "in call to socket"); + continue; + } + if (connect(s, aip->ai_addr, aip->ai_addrlen) < 0) { + retval = errno; + close(s); + (void) snprintf(Errmsg, ErrmsgSz, "in call to connect"); + continue; + } + /* We got a connection, use it! */ + if (debug) + com_err(progname, 0, "%s: socket with AF: %d SOCKTYPE: %d PROTO: %d OK!", + tt->sl_hostname, aip->ai_family, + aip->ai_socktype, aip->ai_protocol); + break; + } + if (NULL == aip) + return retval; + + tt->sl_socket = s; + + /* + * Set receiver_addr and sender_addr. + */ + switch(aip->ai_family) { + case AF_INET: + tt->sl_receiver_addr.addrtype = ADDRTYPE_INET; + tt->sl_receiver_addr.length = 4; + tt->sl_receiver_addr.contents = (krb5_octet *) malloc(4); + memcpy((char *) tt->sl_receiver_addr.contents, + (&((struct sockaddr_in *)aip->ai_addr)->sin_addr), + 4); + break; + case AF_INET6: + tt->sl_receiver_addr.addrtype = ADDRTYPE_INET6; + tt->sl_receiver_addr.length = 16; + tt->sl_receiver_addr.contents = (krb5_octet *) malloc(16); + memcpy((char *) tt->sl_receiver_addr.contents, + (&((struct sockaddr_in6 *)aip->ai_addr)->sin6_addr), + 16); + break; + default: + tt->sl_receiver_addr.addrtype = -1; + tt->sl_receiver_addr.length = 0; + tt->sl_receiver_addr.contents = 0; + break; + } + + s_sa_len = sizeof(s_sa); + if (getsockname(s, (struct sockaddr *)&s_sa, &s_sa_len) < 0) { + retval = errno; + close(s); + (void) snprintf(Errmsg, ErrmsgSz, "in call to getsockname"); + return(retval); + } + sap = (struct sockaddr *)&s_sa; + switch(sap->sa_family) { + case AF_INET: + tt->sl_sender_addr.addrtype = ADDRTYPE_INET; + tt->sl_sender_addr.length = 4; + tt->sl_sender_addr.contents = (krb5_octet *) malloc(4); + memcpy((char *) tt->sl_sender_addr.contents, + (krb5_octet *) &sa2sin(sap)->sin_addr, + 4); + break; + case AF_INET6: + tt->sl_sender_addr.addrtype = ADDRTYPE_INET6; + tt->sl_sender_addr.length = 16; + tt->sl_sender_addr.contents = (krb5_octet *) malloc(16); + memcpy((char *) tt->sl_sender_addr.contents, + (krb5_octet *) &sa2sin6(sap)->sin6_addr, + 16); + break; + default: + tt->sl_sender_addr.addrtype = -1; + tt->sl_sender_addr.length = 0; + tt->sl_sender_addr.contents = 0; + break; + } + + + return(0); +} + +int +kerberos_authenticate(context, auth_context, fd, me, new_creds, tt) + krb5_context context; + krb5_auth_context *auth_context; + int fd; + krb5_principal me; + krb5_creds ** new_creds; + slave_data *tt; +{ + krb5_error_code retval; + krb5_error *error = NULL; + krb5_ap_rep_enc_part *rep_result; + int retcode = 0; + int rc; + + /* Obtain kerberos library lock */ + if ((rc = pthread_mutex_lock(tt->sl_kerberosLibMutex))) + { + com_err(progname, errno, "%s: failed to obtain kerberos library mutex (%d)", + tt->sl_hostname, rc); + pthread_exit((void *) 1); + } + + if ((retval = krb5_auth_con_init(context, auth_context))) + { + retcode = 1; + goto auth_return; + } + + krb5_auth_con_setflags(context, *auth_context, + KRB5_AUTH_CONTEXT_DO_SEQUENCE); + + if ((retval = krb5_auth_con_setaddrs(context, *auth_context, &tt->sl_sender_addr, + &tt->sl_receiver_addr))) { + com_err(progname, retval, "in krb5_auth_con_setaddrs (%s)", tt->sl_hostname); + retcode = 1; + goto auth_return; + } + + if ((retval = krb5_sendauth(context, auth_context, (void *)&fd, + krep_version, me, tt->sl_creds.server, + AP_OPTS_MUTUAL_REQUIRED, NULL, &tt->sl_creds, NULL, + &error, &rep_result, new_creds))) { + com_err(progname, retval, "while authenticating to server (%s)", tt->sl_hostname); + if (error) { + if (error->error == KRB_ERR_GENERIC) { + if (error->text.data) + com_err(progname, errno, + "Generic remote error: %s (%s)", + error->text.data, tt->sl_hostname); + } else if (error->error) { + com_err(progname, + error->error + ERROR_TABLE_BASE_krb5, + "signalled from server (%s)", tt->sl_hostname); + if (error->text.data) + com_err(progname, errno, + "Error text from server: %s (%s)", + error->text.data, tt->sl_hostname); + } + krb5_free_error(context, error); + } + retcode = 1; + } + else + { + krb5_free_ap_rep_enc_part(context, rep_result); + retcode = 0; + } + +auth_return: + /* Release kerberos library lock */ + if ((rc = pthread_mutex_unlock(tt->sl_kerberosLibMutex))) + { + com_err(progname, errno, "%s: failed to release kerberos library mutex (%d)", + tt->sl_hostname, rc); + pthread_exit((void *) 1); + } + return retcode; +} + +/* + * Send over the database information. We use a similar protocol + * to the one used by kprop: + * - Send a KRB_SAFE message containing the size of the data. + * - Then send the message, encrypted using KRB_PRIV. + * - Then we wait for a KRB_SAFE message with the size that the other + * side received and saved. + * + * At any point in the protocol, we may send a KRB_ERROR message; this + * will abort the entire operation. + */ +int +xmit_dbinfo(context, auth_context, my_creds, fd, msg_buff, msg_size, slaveName) + krb5_context context; + krb5_auth_context auth_context; + krb5_creds *my_creds; + int fd; + char *msg_buff; + int msg_size; + char *slaveName; +{ + krb5_int32 send_size; + krb5_data inbuf, outbuf; + char buf[KREP_BUFSIZ]; + krb5_error_code retval; + krb5_error *error; + + /* + * Send over the size + */ + + send_size = htonl(msg_size); + inbuf.data = (char *) &send_size; + inbuf.length = sizeof(send_size); /* must be 4, really */ + /* KREP_CKSUMTYPE */ + if ((retval = krb5_mk_safe(context, auth_context, &inbuf, + &outbuf, NULL))) { + com_err(progname, retval, "while encoding message size (%s)", slaveName); + send_error(context, my_creds, fd, + "while encoding message size", retval); + return 1; + } + if ((retval = krb5_write_message(context, (void *) &fd, &outbuf))) { + krb5_free_data_contents(context, &outbuf); + com_err(progname, retval, "while sending message size (%s)", slaveName); + return 1; + } + krb5_free_data_contents(context, &outbuf); + + /* + * Send over the message.... + */ + + inbuf.data = msg_buff; + inbuf.length = msg_size;; + if ((retval = krb5_mk_priv(context, auth_context, &inbuf, + &outbuf, NULL))) { + com_err(progname, retval, "while encoding message block (%s)", slaveName); + send_error(context, my_creds, fd, buf, retval); + return 1; + } + if ((retval = krb5_write_message(context, (void *)&fd,&outbuf))) { + krb5_free_data_contents(context, &outbuf); + com_err(progname, retval, "while sending message block (%s)", slaveName); + return 1; + } + krb5_free_data_contents(context, &outbuf); + + /* + * OK, we've sent the message; now let's wait for a success + * indication from the remote end. + */ + + if ((retval = krb5_read_message(context, (void *) &fd, &inbuf))) { + com_err(progname, retval, + "while reading response from server (%s)", slaveName); + return 1; + } + + /* + * If we got an error response back from the server, display + * the error message + */ + + if ((krb5_is_krb_error(&inbuf))) { + if ((retval = krb5_rd_error(context, &inbuf, &error))) { + com_err(progname, retval, + "while decoding error response from server (%s)", slaveName); + return 1; + } + if (error->error == KRB_ERR_GENERIC) { + if (error->text.data) + com_err(progname, errno, + "Generic remote error: %s (%s)", + error->text.data, slaveName); + } else if (error->error) { + com_err(progname, error->error + ERROR_TABLE_BASE_krb5, + "signalled from server (%s)", slaveName); + if (error->text.data) + com_err(progname, errno, + "Error text from server: %s (%s)", + error->text.data, slaveName); + } + krb5_free_error(context, error); + return 1; + } + + /* + * krepd returns the size of the message that we sent as an + * acknowlegment. If we get back the correct size, that means + * that we successfully got the data to the slave and it was + * saved to disk there. The slave is then responsible for + * applying the change to the database. + */ + if ((retval = krb5_rd_safe(context,auth_context,&inbuf,&outbuf,NULL))) { + com_err(progname, retval, + "while decoding final size packet from server (%s)", slaveName); + krb5_free_data_contents(context, &inbuf); + return 1; + } + memcpy((char *)&send_size, outbuf.data, sizeof(send_size)); + send_size = ntohl(send_size); + if (send_size != msg_size) { + com_err(progname, 0, + "Krepd sent message size %d, expecting %d (%s)", + send_size, msg_size, slaveName); + krb5_free_data_contents(context, &inbuf); + krb5_free_data_contents(context, &outbuf); + return 1; + } + + krb5_free_data_contents(context, &inbuf); + krb5_free_data_contents(context, &outbuf); + return 0; +} + +void +send_error(context, my_creds, fd, err_text, err_code) + krb5_context context; + krb5_creds *my_creds; + int fd; + char *err_text; + krb5_error_code err_code; +{ + krb5_error error; + const char *text; + krb5_data outbuf; + + memset((char *)&error, 0, sizeof(error)); + krb5_us_timeofday(context, &error.ctime, &error.cusec); + error.server = my_creds->server; + error.client = my_principal; + error.error = err_code - ERROR_TABLE_BASE_krb5; + if (error.error > 127) + error.error = KRB_ERR_GENERIC; + if (err_text) + text = err_text; + else + text = error_message(err_code); + error.text.length = strlen(text) + 1; + if ((error.text.data = malloc(error.text.length))) { + strcpy(error.text.data, text); + if (!krb5_mk_error(context, &error, &outbuf)) { + (void) krb5_write_message(context, (void *)&fd,&outbuf); + krb5_free_data_contents(context, &outbuf); + } + krb5_free_error(context, &error); + } +} + Index: krb5/src/replication/krep.h diff -u /dev/null krb5/src/replication/krep.h:1.3 --- /dev/null Wed Nov 9 17:44:12 2005 +++ krb5/src/replication/krep.h Mon Feb 9 09:59:09 2004 @@ -0,0 +1,134 @@ +#ifndef _KREP_H_ +#define _KREP_H_ +/* + * slave/kprop.h + * + * Copyright 1990,1991 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. 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. + * + * + */ + +/* + * COPYRIGHT © 1998 + * THE REGENTS OF THE UNIVERSITY OF MICHIGAN + * ALL RIGHTS RESERVED + * + * PERMISSION IS GRANTED TO USE, COPY, CREATE DERIVATIVE WORKS + * AND REDISTRIBUTE THIS SOFTWARE AND SUCH DERIVATIVE WORKS + * FOR ANY PURPOSE, SO LONG AS NO FEE IS CHARGED, AND SO LONG + * AS THE COPYRIGHT NOTICE ABOVE, THIS GRANT OF PERMISSION, + * AND THE DISCLAIMER BELOW APPEAR IN ALL COPIES MADE; AND SO + * LONG AS THE NAME OF THE UNIVERSITY OF MICHIGAN IS NOT USED + * IN ANY ADVERTISING OR PUBLICITY PERTAINING TO THE USE OR + * DISTRIBUTION OF THIS SOFTWARE WITHOUT SPECIFIC, WRITTEN + * PRIOR AUTHORIZATION. + * + * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION + * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY + * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF + * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING + * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE + * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR + * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING + * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN + * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGES. + */ + +#define KREP_SERVICE_NAME "host" +#define TGT_SERVICE_NAME "krbtgt" +#define KREP_SERVICE "krb5_rep" +#define KREP_CKSUMTYPE CKSUMTYPE_RSA_MD4_DES + +#define KREP_PROT_VERSION "krep5_00" + +#define KREP_BUFSIZ 32768 + +/* Definitions for state information */ +#define REPL_STATE_IDLE 0x00000001 +#define REPL_STATE_PROCESSING 0x00000002 + +/* Create prototype to silence compiler warning */ +krb5_error_code +krb5_getslavelist(krb5_context kcontext, + krb5_data *realmp, + char ***slave_hosts, + int *num_hosts); + +/* pathnames are in osconf.h, included via k5-int.h */ + +/* + * This is thread-specific stuff used by the worker threads + */ +typedef struct slave_data { + pthread_t sl_id; /* thread ID for this thread */ + char *sl_hostname; /* slave hostname this thread services */ + char *sl_replfile; /* name of the delta dump file */ + pthread_mutex_t *sl_kerberosLibMutex; /* pointer to mutex protecting + use of kerberos libraries */ + krb5_context sl_context; /* context */ + krb5_creds sl_creds; /* credentials with the remote machine */ + krb5_ccache sl_ccache; /* cred cache with the remote machine */ + pthread_mutex_t *sl_stateUpdateMutex; /* pointer to mutex protecting + these entries */ + int sl_fd; /* file descriptor for delta dump file */ + FILE *sl_file; /* file ptr for delta dump file */ + off_t sl_lastPosition; /* file pos when last stopped processing */ + int sl_socket; /* socket fd connected to slave */ + int sl_lastTruncation; /* last truncation "version" number we know about */ + int sl_currentState; /* processing state (idle or processing) */ + int sl_sleeptime; /* number of secs to sleep while idle */ + struct stat sl_statinfo; /* latest file stat info for this thread */ + int *sl_truncationCount; /* latest truncation "version" number */ + krb5_address sl_sender_addr; /* master's address info */ + krb5_address sl_receiver_addr; /* slave's address info */ +} slave_data; + +typedef struct trunc_info { + pthread_t tr_id; /* thread ID for this thread */ + char *tr_replfile; /* name of the delta dump file */ + int tr_fd; /* file descriptor for delta dump file */ + FILE *tr_file; /* file ptr for delta dump file */ + int tr_numSlaves; /* the number of slave threads to monitor */ + slave_data *tr_slaves; /* pointer to slave data info */ + int tr_sleeptime; /* number of secs between checks */ + pthread_mutex_t *tr_stateUpdateMutex; /* pointer to mutex protecting + these entries */ + struct stat tr_statinfo; /* latest file stat info */ + int *tr_truncationCount; /* latest trunction "version" number */ +} trunc_info; + + + +/* + * The following allow for use of either + * "real" POSIX threads or CMA/DCE threads + */ + +#if !defined(_DECTHREADS_) +#define pthread_mutexattr_default NULL +#define pthread_attr_default NULL +#define pthread_condattr_default NULL +#define pthread_addr_t void * +#endif /* _DECTHREADS_ */ + +#endif /* _KREP_H_ */ Index: krb5/src/replication/krepd.M diff -u /dev/null krb5/src/replication/krepd.M:1.1 --- /dev/null Wed Nov 9 17:44:12 2005 +++ krb5/src/replication/krepd.M Mon Oct 16 11:21:38 2000 @@ -0,0 +1,158 @@ +.\" replication/krepd.M +.\" +.\" Copyright 1992 by the Massachusetts Institute of Technology. +.\" +.\" 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. 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. +.\" +.\" COPYRIGHT © 1998 +.\" THE REGENTS OF THE UNIVERSITY OF MICHIGAN +.\" ALL RIGHTS RESERVED +.\" +.\" PERMISSION IS GRANTED TO USE, COPY, CREATE DERIVATIVE WORKS +.\" AND REDISTRIBUTE THIS SOFTWARE AND SUCH DERIVATIVE WORKS +.\" FOR ANY PURPOSE, SO LONG AS NO FEE IS CHARGED, AND SO LONG +.\" AS THE COPYRIGHT NOTICE ABOVE, THIS GRANT OF PERMISSION, +.\" AND THE DISCLAIMER BELOW APPEAR IN ALL COPIES MADE; AND SO +.\" LONG AS THE NAME OF THE UNIVERSITY OF MICHIGAN IS NOT USED +.\" IN ANY ADVERTISING OR PUBLICITY PERTAINING TO THE USE OR +.\" DISTRIBUTION OF THIS SOFTWARE WITHOUT SPECIFIC, WRITTEN +.\" PRIOR AUTHORIZATION. +.\" +.\" THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION +.\" FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY +.\" PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF +.\" MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING +.\" WITHOUT LIMITATION THE IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE +.\" REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE +.\" FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR +.\" CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING +.\" OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN +.\" IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGES. +.\" +.TH KREPD 8 +.SH NAME +krepd \- Kerberos V5 slave KDC replication server +.SH SYNOPSIS +.B krep +[ +.B \-r +.I realm +] [ +.B \-f +.I slave_delta_file +] [ +.B \-F +.I principal_database +] [ +.B \-s +.I keytab +] [ +.B \-a +.I acl_file_name +] [ +.B \-d +] [ +.B \-S +] [ +.B \-P +.I port +] +.br +.SH DESCRIPTION +.I krepd +is the server which accepts connections from the +.IR krep (8) +program. +.I krepd +accepts the dumped KDC database records and applies the changes to the +database used by +.IR krb5kdc (8). +Thus, the master Kerberos server can use +.IR krep (8) +to replicate its database to the slave slavers. Upon a successful +application of the changes, the slave Kerberos server will have an +up-to-date KDC database. +.PP +Normally, krepd is invoked out of +.I inetd(8). +This is done by adding a line to the inetd.conf file which looks like +this: + +krep stream tcp nowait root /usr/local/sbin/krepd krepd + +However, krepd can also run as a standalone deamon, if the +.B \-S +option is turned on. This is done for debugging purposes. +.SH OPTIONS +.TP +\fB\-r\fP \fIrealm\fP +specifies the realm of the master server; by default the realm returned +by +.IR krb5_default_local_realm (3) +is used. +.TP +\fB\-f\fP \fIfile\fP +specifies the filename where the dumped principal database records are +to be stored before being applied to the database; +by default this file is KREPD_DEFAULT_FILE +(normally /usr/local/var/krb5kdc/slave_delta). +.TP +\fB\-F\fP \fIprincipal_database\fP +specifies an alternate name for the principal database used by +.IR krb5kdc (8). +.TP +.B \-S +turn on standalone mode. Normally, krepd is invoked out of +.IR inetd (8) +so it expects a network connection to be passed to it from +.I inetd (8). +If the +.B \-S +option is specified, krepd will put itself into the background, and +wait for connections to the KREP_SERVICE port (normally krb5_rep). +.TP +.B \-d +turn on debug mode. In this mode, if the +.B \-S +option is selected, +.I krepd +will not detach itself from the current job and run in the background. +Instead, it will run in the foreground and print out debugging messages +during the database replication. +.TP +.B \-P +allow for an alternate port number for +.I krepd +to listen on. This is only useful if the program is run in standalone +mode. +.TP +\fB\-a\fP \fIacl_file_name\fP +specifies an alternate acl file name +.TP +\fB\-s\fP \fIkeytab\fP +specifies the location of the keytab file. +.SH FILES +.TP "\w'kpropd.acl\ \ 'u" +kpropd.acl +Access file for +.BR krepd . +Each entry is a line containing the principal of a host from which the +local machine will allow Kerberos database replication via krep. +.SH SEE ALSO +krep(8), krb5kdc(8), inetd(8) Index: krb5/src/replication/krepd.c diff -u /dev/null krb5/src/replication/krepd.c:1.4 --- /dev/null Wed Nov 9 17:44:12 2005 +++ krb5/src/replication/krepd.c Fri Jun 18 10:42:48 2004 @@ -0,0 +1,1414 @@ +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * 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 FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * slave/krepd.c + * + * Copyright 1990,1991 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. + * + */ +/* + * COPYRIGHT © 1998,2000 + * THE REGENTS OF THE UNIVERSITY OF MICHIGAN + * ALL RIGHTS RESERVED + * + * PERMISSION IS GRANTED TO USE, COPY, CREATE DERIVATIVE WORKS + * AND REDISTRIBUTE THIS SOFTWARE AND SUCH DERIVATIVE WORKS + * FOR ANY PURPOSE, SO LONG AS NO FEE IS CHARGED, AND SO LONG + * AS THE COPYRIGHT NOTICE ABOVE, THIS GRANT OF PERMISSION, + * AND THE DISCLAIMER BELOW APPEAR IN ALL COPIES MADE; AND SO + * LONG AS THE NAME OF THE UNIVERSITY OF MICHIGAN IS NOT USED + * IN ANY ADVERTISING OR PUBLICITY PERTAINING TO THE USE OR + * DISTRIBUTION OF THIS SOFTWARE WITHOUT SPECIFIC, WRITTEN + * PRIOR AUTHORIZATION. + * + * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION + * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY + * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF + * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING + * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE + * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR + * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING + * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN + * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGES. +*/ + +#if defined(UMICH_REPLICATION) +#if defined(sun) +#define _REENTRANT +#endif +#include +#endif /* UMICH_REPLICATION */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "k5-int.h" +#include "com_err.h" +#include + +#include +#include +#include "krep.h" + +#define SYSLOG_CLASS LOG_DAEMON + +static char *krep_version = KREP_PROT_VERSION; + +char *progname; +int debug = 0; +int verbose = 1; +char *srvtab = 0; +int standalone = 0; +#if defined(UMICH_REPLICATION) +int nofork = 1; /* By default, we do not fork */ +#endif + +krb5_principal server; /* This is our server principal name */ +krb5_principal client; /* This is who we're talking to */ +krb5_context krepd_context; +krb5_auth_context krepd_auth_context; +char *realm = NULL; /* Our realm */ + +osa_adb_policy_t policy_db; +kadm5_config_params global_params; + +pthread_mutex_t deltaFileMutex; +pthread_cond_t deltaFileCond; +int signaledToQuit = 0; + +/* char deltaFileName[] = "/usr/krb5kdc/slave_delta"; XXX */ +char *deltaFileName = KREPD_DEFAULT_FILE; + +char *kerb_database = KREPD_DEFAULT_KRB_DB; +char *acl_file_name = KREPD_ACL_FILE; + +krb5_address sender_addr; +krb5_address receiver_addr; +short port = 0; + +void PRS (char**); +void do_standalone (void); +void doit (int); +void kerberos_authenticate + (krb5_context context, + int fd, + krb5_principal *clientp, + krb5_enctype *etype, + struct sockaddr *s_sa); +krb5_boolean authorized_principal + (krb5_context, + krb5_principal, + krb5_enctype); +void recv_and_store_messages + (krb5_context, + int); +void * process_messages_and_truncate + (void *); +void load_database + (krb5_context, + char *, + char *); +void send_error + (krb5_context, + int, + krb5_error_code, + char *); +void recv_error + (krb5_context, + krb5_data *); + +int process_umichrep_record /* XXX s/b in a header */ + (char *, + krb5_context, + FILE *, + int, + int *, + void *); + +static void usage() +{ + com_err(progname, 0, +#if defined(UMICH_REPLICATION) + "\nUsage: %s [-r realm] [-s srvtab] [-dkSq] [-f slave_file]\n", +#else + "\nUsage: %s [-r realm] [-s srvtab] [-dSq] [-f slave_file]\n", +#endif + progname); + com_err(progname, 0, "\t[-F kerberos_db_file ]\n"); + com_err(progname, 0, "\t[-P port] [-a acl_file]\n"); + exit(1); +} + +int +main(argc, argv) + int argc; + char **argv; +{ + PRS(argv); + + if (standalone) + do_standalone(); + else + doit(0); + exit(0); +} + +void do_standalone() +{ + struct addrinfo aihints, *aip, *ailp; + int errorcode; + int finet, s; + char port_string[NI_MAXSERV]; + struct sockaddr_storage client_addr; + socklen_t addr_size; + + memset(&aihints, 0, sizeof(aihints)); + aihints.ai_socktype = SOCK_STREAM; + aihints.ai_protocol = IPPROTO_TCP; + aihints.ai_flags = AI_PASSIVE; /* Bind to all addresses */ + /* aihints.ai_family = AF_INET; Shouldn't have to specify this! */ + + if (port) + sprintf(port_string, "%u", port); + else + sprintf(port_string, "%s", KREP_SERVICE); + + if (debug) + com_err(progname, 0, "Service name is %s", port_string); + + errorcode = getaddrinfo(NULL, port_string, &aihints, &ailp); + if (errorcode) { + com_err(progname, 0, "Error from getaddrinfo: %s", + gai_strerror(errorcode)); + exit(1); + } + + for (aip = ailp; aip; aip = aip->ai_next) { + const int on = 1; + + if (debug) + com_err(progname, 0, + "Attempting socket call with AF: %d SOCKTYPE: %d PROTO: %d", + aip->ai_family, aip->ai_socktype, aip->ai_protocol); + finet = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); + if (finet < 0) { + if (debug) + com_err(progname, 0, + "Error creating socket with address family %d, protocol %d", + aip->ai_family, aip->ai_protocol); + continue; + } + errorcode = setsockopt(finet, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (errorcode) { + com_err(progname, errno, + "while trying to setsockopt(SO_REUSEADDR)"); + } + errorcode = bind(finet, aip->ai_addr, aip->ai_addrlen); + if (errorcode < 0) { + close(finet); + continue; + } + break; + } + if (NULL == aip) { + com_err(progname, 0, "Could not bind any interface"); + exit(1); + } + + if (!debug && !nofork) + daemon(1, 0); +#ifdef PID_FILE + if ((pidfile = fopen(PID_FILE, "w")) != NULL) { + fprintf(pidfile, "%d\n", getpid()); + fclose(pidfile); + } else + com_err(progname, errno, + "while opening pid file %s for writing", PID_FILE); +#endif + if (listen(finet, 5) < 0) { + com_err(progname, errno, "in listen call"); + exit(1); + } + while (1) { + int child_pid; + + if (debug) + com_err(progname, 0, "Waiting for connections ..."); + + addr_size = sizeof(client_addr); + s = accept(finet, (struct sockaddr *)&client_addr, &addr_size); + + if (s < 0) { + if (errno != EINTR) + com_err(progname, errno, + "from accept system call"); + continue; + } + if (debug || nofork) + child_pid = 0; + else + child_pid = fork(); + switch (child_pid) { + case -1: + com_err(progname, errno, "while forking"); + exit(1); + case 0: + (void) close(finet); + + doit(s); + close(s); + _exit(0); + default: + wait(0); + close(s); + + } + } +} + +void doit(fd) + int fd; +{ + struct sockaddr_storage client_addr; + socklen_t addr_len; + int on = 1; + krb5_error_code retval; + int rc; + pthread_t processThread; + pthread_addr_t processThreadStatus; + int exitStatus = 0; + krb5_enctype etype; + char hbuf[NI_MAXHOST]; + + /* + * Initialize mutex and condition variable + */ + + if ((rc = pthread_mutex_init(&deltaFileMutex, pthread_mutexattr_default))) + { + syslog(LOG_ERR, "Error %d initializing deltaFileMutex\n", rc); + exitStatus++; + goto closeAndExit; + } + + if ((rc = pthread_cond_init(&deltaFileCond, pthread_condattr_default))) + { + syslog(LOG_ERR, "Error %d initializing deltaFileCond\n", rc); + exitStatus++; + goto closeAndExit; + } + + /* + * Lock mutex to synchronize with other thread (make sure + * delta file is created before other thread tries to open it) + */ + + if ((rc = pthread_mutex_lock(&deltaFileMutex))) + { + syslog(LOG_ERR, "Error %d locking deltaFileMutex before " + "threads are started!\n", rc); + exitStatus++; + goto closeAndExit; + } + + /* + * Start another thread to process and truncate message file + */ + + if (debug > 1) + com_err(progname, 0, + "*** Starting thread to run process_messages_and_truncate ***\n"); + + if ((rc = pthread_create(&processThread, + pthread_attr_default, + process_messages_and_truncate, + NULL))) + { + syslog(LOG_ERR, "Error spawning thread to process delta file!\n"); + exitStatus++; + goto closeAndExit; + } + + /* + * Find out who we're talking to + */ + + addr_len = sizeof (client_addr); + if (getpeername(fd, (struct sockaddr *)&client_addr, &addr_len) < 0) { + com_err(progname, errno, "from getpeername\n"); + goto stopAndExit; + } + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (caddr_t) &on, + sizeof (on)) < 0) { + com_err(progname, errno, + "while attempting setsockopt (SO_KEEPALIVE)"); + } + + if ((getnameinfo((struct sockaddr *)&client_addr, addr_len, hbuf, sizeof(hbuf), + NULL, 0, NI_NAMEREQD))) { + if ((getnameinfo((struct sockaddr *)&client_addr, addr_len, + hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST))) { + sprintf(hbuf, ""); + } + } + + syslog(LOG_INFO, "Connection from %s", hbuf); + if (debug) + com_err(progname, 0, "Connection from %s\n", hbuf); + + /* + * Authenticate the peer and validate it is an authorized principal + */ + + kerberos_authenticate(krepd_context, fd, &client, &etype, + (struct sockaddr *)&client_addr); + + if (!authorized_principal(krepd_context, client, etype)) { + char *name; + + if ((retval = krb5_unparse_name(krepd_context, client, &name))) { + com_err(progname, retval, + "While unparsing client name"); + goto stopAndExit; + } + syslog(LOG_WARNING, + "Rejected connection from unauthorized principal %s", + name); + free(name); + goto stopAndExit; + } + + /* + * Now go process messages as long as we keep getting them. + */ + + recv_and_store_messages(krepd_context, fd); + +stopAndExit: + + /* + * Tell the other thread we're going down... + */ + + signaledToQuit = 1; + if ((rc = pthread_cond_broadcast(&deltaFileCond))) + { + syslog(LOG_ERR, "Error from pthread_cond_broadcast " + "in recv_and_store_messages\n"); + } + else + { + + if (debug) + com_err(progname, 0, + "Waiting for the process_messages_and_truncate " + "thread to end...\n"); + + if (0 != pthread_join(processThread, &processThreadStatus)) + { + com_err(progname, errno, + "while joining process_messages thread!\n"); + } + else + { + if (debug) + com_err(progname, 0, + "process_messages_and_truncate thread " + "ended with status %d\n", processThreadStatus); + } + } + +closeAndExit: + + /* + * Close the socket connected to master + */ + + if (close(fd) < 0) { + com_err(progname, errno, + "while trying to close database file"); + exitStatus++; + } + + com_err(progname, 0, "Terminating with status %d\n", exitStatus); + exit(exitStatus); +} + +static void +krepd_com_err_proc(whoami, code, fmt, args) + const char *whoami; + long code; + const char *fmt; + va_list args; +{ + char error_buf[8096]; + + error_buf[0] = '\0'; + if (fmt) + vsprintf(error_buf, fmt, args); + syslog(LOG_ERR, "%s%s%s%s%s", whoami ? whoami : "", whoami ? ": " : "", + code ? error_message(code) : "", code ? " " : "", error_buf); +} + +void PRS(argv) + char **argv; +{ + register char *word, ch; + krb5_error_code retval; + + initialize_adb_error_table(); + + retval = krb5_init_context(&krepd_context); + if (retval) { + com_err(argv[0], retval, "while initializing krb5"); + exit(1); + } + + progname = *argv++; + while ((word = *argv++)) { + if (*word == '-') { + word++; + while (word && (ch = *word++)) { + switch(ch){ + case 'f': + if (*word) + deltaFileName = word; + else + deltaFileName = *argv++; + if (!deltaFileName) + usage(); + word = 0; + break; + case 'F': + if (*word) + kerb_database = word; + else + kerb_database = *argv++; + if (!kerb_database) + usage(); + word = 0; +#if defined(UMICH_REPLICATION) + global_params.dbname = kerb_database; + global_params.mask |= KADM5_CONFIG_DBNAME; +#endif + break; + case 'P': + if (*word) + port = htons(atoi(word)); + else + port = htons(atoi(*argv++)); + if (!port) + usage(); + word = 0; + break; + case 'r': + if (*word) + realm = word; + else + realm = *argv++; + if (!realm) + usage(); + word = 0; +#if defined(UMICH_REPLICATION) + global_params.realm = realm; + global_params.mask |= KADM5_CONFIG_REALM; +#endif + break; + case 's': + if (*word) + srvtab = word; + else + srvtab = *argv++; + if (!srvtab) + usage(); + word = 0; +#if defined(UMICH_REPLICATION) + global_params.admin_keytab = srvtab; + global_params.mask |= KADM5_CONFIG_ADMIN_KEYTAB; +#endif + break; + case 'd': + debug++; + break; + case 'q': + verbose = 0; + break; + case 'S': + standalone++; + break; + case 'a': + if (*word) + acl_file_name = word; + else + acl_file_name = *argv++; + if (!acl_file_name) + usage(); + word = 0; +#if defined(UMICH_REPLICATION) + global_params.acl_file = acl_file_name; + global_params.mask |= KADM5_CONFIG_ACL_FILE; +#endif + break; +#if defined(UMICH_REPLICATION) + case 'k': + nofork = 0; + break; +#endif + default: + usage(); + } + + } + } else + /* We don't take any arguments, only options */ + usage(); + } + +#if defined(UMICH_REPLICATION) + if ((retval = kadm5_get_config_params(krepd_context, NULL, NULL, + &global_params, &global_params))) { + com_err(argv[0], retval, "while retrieving configuration parameters"); + exit(1); + } +#endif + + /* + * Switch com_err reporting to syslog + */ + openlog("krepd", LOG_PID | LOG_ODELAY, SYSLOG_CLASS); + set_com_err_hook(krepd_com_err_proc); + + /* + * Get my hostname, so we can construct my service name + */ + retval = krb5_sname_to_principal(krepd_context, + NULL, KREP_SERVICE_NAME, + KRB5_NT_SRV_HST, &server); + if (retval) { + com_err(progname, retval, + "While trying to construct my service name"); + exit(1); + } + if (realm) { + (void) krb5_xfree(krb5_princ_realm(context, server)->data); + krb5_princ_set_realm_length(context, server, strlen(realm)); + krb5_princ_set_realm_data(context, server, strdup(realm)); + } +} + +/* + * Figure out who's calling on the other end of the connection.... + */ +void +kerberos_authenticate(krb5_context context, + int fd, + krb5_principal *clientp, + krb5_enctype *etype, + struct sockaddr *s_sa) +{ + krb5_error_code retval; + krb5_ticket *ticket; + struct sockaddr_storage r_sa; + socklen_t r_sa_len; + krb5_keytab keytab = NULL; + struct sockaddr *sap = (struct sockaddr *)&r_sa; + + /* + * Set recv_addr and send_addr + * (borrowed stuff from kdc/network.c) + */ + switch(s_sa->sa_family) { + case AF_INET: + sender_addr.addrtype = ADDRTYPE_INET; + sender_addr.length = 4; + sender_addr.contents = (krb5_octet *) malloc(4); + memcpy((char *) sender_addr.contents, &sa2sin(s_sa)->sin_addr, + sender_addr.length); + break; + case AF_INET6: + if (IN6_IS_ADDR_V4MAPPED(&sa2sin6(s_sa)->sin6_addr)) { + sender_addr.addrtype = ADDRTYPE_INET; + sender_addr.length = 4; + sender_addr.contents = 12 + + (krb5_octet *) &sa2sin6(s_sa)->sin6_addr; + } else { + sender_addr.addrtype = ADDRTYPE_INET6; + sender_addr.length = 16; + sender_addr.contents = (krb5_octet *) &sa2sin6(s_sa)->sin6_addr; + } + break; + default: + sender_addr.addrtype = -1; + sender_addr.length = 0; + sender_addr.contents = 0; + break; + } + + r_sa_len = sizeof(r_sa); + if (getsockname(fd, (struct sockaddr *) &r_sa, &r_sa_len)) { + com_err(progname, errno, "while getting local socket address"); + exit(1); + } + + switch(sap->sa_family) { + case AF_INET: + receiver_addr.addrtype = ADDRTYPE_INET; + receiver_addr.length = 4; + receiver_addr.contents = (krb5_octet *) malloc(4); + memcpy((char *) receiver_addr.contents, &sa2sin(sap)->sin_addr, + receiver_addr.length); + break; + case AF_INET6: + if (IN6_IS_ADDR_V4MAPPED(&sa2sin6(sap)->sin6_addr)) { + receiver_addr.addrtype = ADDRTYPE_INET; + receiver_addr.length = 4; + receiver_addr.contents = 12 + + (krb5_octet *) &sa2sin6(sap)->sin6_addr; + } else { + receiver_addr.addrtype = ADDRTYPE_INET6; + receiver_addr.length = 16; + receiver_addr.contents = + (krb5_octet *) &sa2sin6(sap)->sin6_addr; + } + break; + default: + receiver_addr.addrtype = -1; + receiver_addr.length = 0; + receiver_addr.contents = 0; + break; + } + + if (debug) { + char *ourname; + if ((retval = krb5_unparse_name(context, server, &ourname))) { + com_err(progname, retval, "While unparsing server (our) name"); + exit(1); + } + com_err(progname, 0, "krb5_recvauth(%d, %s, %s, ...)\n", fd, krep_version, ourname); + free(ourname); + } + + if ((retval = krb5_auth_con_init(context, &krepd_auth_context))) { + syslog(LOG_ERR, "Error in krb5_auth_con_init: %s", + error_message(retval)); + exit(1); + } + + if ((retval = krb5_auth_con_setflags(context, krepd_auth_context, + KRB5_AUTH_CONTEXT_DO_SEQUENCE))) { + syslog(LOG_ERR, "Error in krb5_auth_con_setflags: %s", + error_message(retval)); + exit(1); + } + + if ((retval = krb5_auth_con_setaddrs(context, krepd_auth_context, + &receiver_addr, &sender_addr))) { + syslog(LOG_ERR, "Error in krb5_auth_con_setaddrs: %s", + error_message(retval)); + exit(1); + } + + if (srvtab) { + if ((retval = krb5_kt_resolve(context, srvtab, &keytab))) { + syslog(LOG_ERR, "Error in krb5_kt_resolve: %s", + error_message(retval)); + exit(1); + } + } + + if ((retval = krb5_recvauth(context, &krepd_auth_context, (void *) &fd, + krep_version, server, 0, keytab, &ticket))) { + syslog(LOG_ERR, "Error in krb5_recvauth: %s", error_message(retval)); + exit(1); + } + + if ((retval = krb5_copy_principal(context, + ticket->enc_part2->client, clientp))) { + syslog(LOG_ERR, "Error in krb5_copy_prinicpal: %s", + error_message(retval)); + exit(1); + } + + *etype = ticket->enc_part.enctype; + + if (debug) { + char * name; + char etypebuf[100]; + + if ((retval = krb5_unparse_name(context, *clientp, &name))) { + com_err(progname, retval, "While unparsing client name"); + exit(1); + } + + if ((retval = krb5_enctype_to_string(*etype, etypebuf, + sizeof(etypebuf)))) { + com_err(progname, retval, "While unparsing ticket etype"); + exit(1); + } + + com_err(progname, 0, "authenticated client: %s (etype == %s)\n", name, etypebuf); + free(name); + } + + krb5_free_ticket(context, ticket); +} + +krb5_boolean +authorized_principal(context, p, auth_etype) + krb5_context context; + krb5_principal p; + krb5_enctype auth_etype; +{ + char *name, *ptr; + char buf[1024]; + krb5_error_code retval; + FILE *acl_file; + int end; + krb5_enctype acl_etype; + + retval = krb5_unparse_name(context, p, &name); + if (retval) + return FALSE; + + acl_file = fopen(acl_file_name, "r"); + if (!acl_file) + return FALSE; + + while (!feof(acl_file)) { + if (!fgets(buf, sizeof(buf), acl_file)) + break; + end = strlen(buf) - 1; + if (buf[end] == '\n') + buf[end] = '\0'; + if (!strncmp(name, buf, strlen(name))) { + ptr = buf+strlen(name); + + /* if the next character is not whitespace or nul, then + the match is only partial. continue on to new lines. */ + if (*ptr && !isspace(*ptr)) + continue; + + /* otherwise, skip trailing whitespace */ + for (; *ptr && isspace(*ptr); ptr++) ; + + /* now, look for an etype string. if there isn't one, + return true. if there is an invalid string, continue. + If there is a valid string, return true only if it + matches the etype passed in, otherwise continue */ + + if ((*ptr) && + ((retval = krb5_string_to_enctype(ptr, &acl_etype)) || + (acl_etype != auth_etype))) + continue; + + free(name); + fclose(acl_file); + return TRUE; + } + } + free(name); + fclose(acl_file); + return FALSE; +} + +void +recv_and_store_messages(krb5_context context, int fd) +{ + FILE *deltaFile; + int msg_size; + krb5_data inbuf, outbuf, confmsg; + krb5_error_code retval; + int rc; + + if (debug > 1) + com_err(progname, 0, "recv_and_store_messages: entered"); + + /* + * Open the file for append or create it + */ + + if (NULL == (deltaFile = fopen(deltaFileName, "a+"))) + { + syslog(LOG_ERR, + "%s: Error opening deltaFile (%s) for append\n", + progname, deltaFileName); + return; + } + + /* + * With the file safely opened (or created) we can + * let the other thread continue + */ + + if ((rc = pthread_mutex_unlock(&deltaFileMutex))) + { + syslog(LOG_ERR, + "%s: error releasing mutex in recv_and_store_messages\n", + progname); + goto errorReturn; + } + + /* + * Initialize the initial vector. (only once per instantiation) + */ + + if ((retval = krb5_auth_con_initivector(context, krepd_auth_context))) { + send_error(context, fd, retval, "failed while initializing i_vector"); + com_err(progname, retval, "while initializing i_vector"); + goto errorReturn; + } + + for (;;) + { + /* + * Receive and decode size from client + */ + + if ((retval = krb5_read_message(context, (void *) &fd, &inbuf))) { + send_error(context, fd, retval, "while reading message size"); + com_err(progname, retval, + "while reading size of message from client"); + goto errorReturn; + } + if (krb5_is_krb_error(&inbuf)) + recv_error(context, &inbuf); + if ((retval = krb5_rd_safe(context,krepd_auth_context, + &inbuf,&outbuf,NULL))) { + krb5_free_data_contents(context, &inbuf); + send_error(context, fd, retval, "while decoding message size"); + com_err(progname, retval, + "while decoding message size from client"); + goto errorReturn; + } + memcpy((char *) &msg_size, outbuf.data, sizeof(msg_size)); + krb5_free_data_contents(context, &inbuf); + krb5_free_data_contents(context, &outbuf); + msg_size = ntohl(msg_size); + if (debug > 1) + com_err(progname, 0, + "recv_and_store_messages: msg_size is %d\n", msg_size); + + /* + * If we got a non-zero length message, process it + */ + + if ( msg_size > 0 ) + { + /* + * Read and decode the message with DB info + * Cannot put a null into the buffer since it seems + * to be the exact size req'd. So we use "%.*s" to + * print and write it... + */ + + if (debug > 2) + com_err(progname, 0, + "recv_and_store_messages: calling krb5_read_message()\n"); + + if ((retval = krb5_read_message(context, (void *) &fd, &inbuf))) { + com_err(progname, retval, "while reading database message"); + send_error(context, fd, retval, + "while reading database message"); + goto errorReturn; + } + if (krb5_is_krb_error(&inbuf)) { + recv_error(context, &inbuf); + } + if ((retval = krb5_rd_priv(context, krepd_auth_context, &inbuf, + &outbuf, NULL))) { + krb5_free_data_contents(context, &inbuf); + com_err(progname, retval, "while decoding database message"); + send_error(context, fd, retval, + "while decoding database message"); + goto errorReturn; + } + krb5_free_data_contents(context, &inbuf); + + if (debug > 1) + com_err(progname, 0, "recv_and_store_messages: got a %d-byte message", msg_size); +/* com_err() has a 1024-byte buffer. The longer entries created by + * sending the policy info blows out the buffer and causes an error... + * com_err(progname, 0, "recv_and_store_messages: " + * "the message at 0x%08x is\n'%.*s'\n", + * outbuf.data, msg_size, outbuf.data); + */ + + /* + * Process the record (write it to the file) + */ + + if (debug > 2) + com_err(progname, 0, + "recv_and_store_messages: obtaining mutex...\n"); + if ((rc = pthread_mutex_lock(&deltaFileMutex))) + { + syslog(LOG_ERR, + "Error obtaining mutex in recv_and_store_messages\n"); + goto errorReturn; + } + + /* go to the end of the file (in case it was truncated) */ + if (debug > 2) + com_err(progname, 0, "recv_and_store_messages:" + " seeking the end of the file...\n"); + fseek(deltaFile, 0, SEEK_END); + + if (debug > 2) + com_err(progname, 0, "recv_and_store_messages: " + "writing and flushing record...\n"); + fprintf(deltaFile, "%.*s\n", msg_size, outbuf.data); + fflush(deltaFile); + + if (debug > 2) + com_err(progname, 0, "recv_and_store_messages: " + "calling krb5_free_data_contents() with 0x%08x...\n", + outbuf.data); + krb5_free_data_contents(context, &outbuf); + + if (debug > 1) + com_err(progname, 0, "recv_and_store_messages: " + "signalling that there is work...\n"); + if ((rc = pthread_cond_broadcast(&deltaFileCond))) + { + syslog(LOG_ERR, "Error from pthread_cond_broadcast " + "in recv_and_store_messages\n"); + goto errorReturn; + } + + if (debug > 2) + com_err(progname, 0, + "recv_and_store_messages: releasing mutex...\n"); + if ((rc = pthread_mutex_unlock(&deltaFileMutex))) + { + syslog(LOG_ERR, + "Error obtaining mutex in recv_and_store_messages\n"); + goto errorReturn; + } + + /* + * Create acknowledgement message indicating the number + * of bytes received and stored + */ + + msg_size = htonl(msg_size); + inbuf.data = (char *) &msg_size; + inbuf.length = sizeof(msg_size); + if ((retval = krb5_mk_safe(context, krepd_auth_context, &inbuf, + &confmsg, NULL))) { + com_err(progname, retval, "while encoding acknowledgement"); + send_error(context, fd, retval, + "while encoding acknowledgement"); + goto errorReturn; + } + + /* + * Send the acknowledgement message + */ + + if (debug > 1) + { + com_err(progname, 0, "recv_and_store_messages: " + "calling krb5_write_message() with length %d\n", + confmsg.length); + } + if ((retval = krb5_write_message(krepd_context, (void *) &fd, + &confmsg))) { + krb5_free_data_contents(context, &confmsg); + com_err(progname, retval, "while sending acknowledgement"); + goto errorReturn; + } + krb5_free_data_contents(context, &confmsg); + } + else + { + /* + * When we get a zero-length message we go away... + */ + + if (debug) + com_err(progname, 0, "recv_and_store_messages: " + "returning because of zero-length msg\n"); + break; /* out of forever loop */ + } + } + +errorReturn: + + fclose(deltaFile); +} + +void * process_messages_and_truncate(void *parm) +{ + int rc; + FILE * deltaFile; + + if (debug > 1) + com_err(progname, 0, + "*** process_messages_and_truncate: thread entered ***\n"); + + /* + * Wait here until main thread has the file ready + */ + + if ((rc = pthread_mutex_lock(&deltaFileMutex))) + { + syslog(LOG_ERR, "Error initially obtaining mutex " + "in process_messages_and_truncate\n"); + pthread_exit((void *) 1); + } + + /* + * Open the delta file for reading + */ + + if (debug > 1) + com_err(progname, 0, "process_messages_and_truncate: " + "opening delta file %s...\n", deltaFileName); + + if (NULL == (deltaFile = fopen(deltaFileName, "r+"))) + { + syslog(LOG_ERR, + "Error opening deltaFile (%s) for reading\n", deltaFileName); + pthread_exit((void *) 1); + } + + /* + * Release the mutex for now ... + */ + + if ((rc = pthread_mutex_unlock(&deltaFileMutex))) + { + syslog(LOG_ERR, + "Error releasing mutex in process_messages_and_truncate\n"); + pthread_exit((void *) 1); + } + + /* + * Initialize use of the database + */ + + if (debug > 1) + com_err(progname, 0, "process_messages_and_truncate: " + "initializing use of the database (%s)...\n", + global_params.dbname); + + if ((rc = krb5_db_set_name(krepd_context, global_params.dbname))) + { + com_err(progname, 0, + "%s: %s while setting database name, aborting\n", + progname, error_message(rc)); + pthread_exit((void *)1); + } + if ((rc = krb5_db_init(krepd_context))) + { + com_err(progname, 0, + "%s: %s while initializing use of database, aborting\n", + progname, error_message(rc)); + pthread_exit((void *)1); + } + + /* + * Initialize use of the policy database ... + */ + + if (debug > 1) + com_err(progname, 0, "process_messages_and_truncate: " + "initializing use of the policy database (%s)...\n", + global_params.admin_dbname); + + if ((rc = osa_adb_open_policy(&policy_db, &global_params))) + { + com_err(progname, 0, + "%s: %s while initializing use of policy database, aborting\n", + progname, error_message(rc)); + if ((rc = krb5_db_fini(krepd_context))) + { + com_err(progname, 0, "%s: %s while terminating use of database\n", + progname, error_message(rc)); + } + pthread_exit((void *)1); + } + + /* + * Loop until signaled to quit. Processes the delta file + * applying the changes to the database until it reaches + * EOF or encounters an error + */ + + if (debug > 1) + com_err(progname, 0, "process_messages_and_truncate: " + "entering processing loop ..........\n"); + + while (!signaledToQuit) + { + int error = 0; + int lineno = 0; + + if (debug > 2) + com_err(progname, 0, "process_messages_and_truncate: " + "Calling process_umichrep_record\n"); + + while (! (error = process_umichrep_record(deltaFileName, + krepd_context, + deltaFile, + verbose, + &lineno, + policy_db))) + /* do nothing */ + ; + + if (debug > 2) + com_err(progname, 0, "process_messages_and_truncate: " + "Error (%d) from process_umichrep_record (Note: -1 means EOF)\n", error); + + if (-1 == error) + { + if (debug > 2) + com_err(progname, 0, "process_messages_and_truncate: " + "Doing EOF processing\n"); + + /* EOF */ + if ((rc = pthread_mutex_lock(&deltaFileMutex))) + { + com_err(progname, 0, "process_messages_and_truncate: " + "Error locking mutex at EOF\n"); + pthread_exit((void *) 1); + } + if ((rc = krb5_lock_file(0, fileno(deltaFile), + KRB5_LOCKMODE_EXCLUSIVE))) + { + com_err(progname, 0, + "Error (%d) locking deltaFile at EOF\n", rc); + pthread_mutex_unlock(&deltaFileMutex); + pthread_exit((void *) 1); + } + + if (debug > 2) + com_err(progname, 0, "process_messages_and_truncate: " + "Calling process_umichrep_record after EOF\n"); + + if (-1 == (error = process_umichrep_record(deltaFileName, + krepd_context, + deltaFile, + verbose, + &lineno, + policy_db))) + { + + if (debug > 1) + com_err(progname, 0, "process_messages_and_truncate: " + "Still EOF, TRUNCATING delta file !!!!!!!\n"); + + /* Still EOF */ + if (ftruncate(fileno(deltaFile), 0)) + { + com_err(progname, 0, "process_messages_and_truncate: " + "Error truncating deltaFile!\n"); + } + else + { + lineno = 0; + fseek(deltaFile, 0, SEEK_SET); + } + } + if (debug > 2) + com_err(progname, 0, "process_messages_and_truncate: " + "unlocking file...\n"); + + krb5_lock_file(0, fileno(deltaFile), KRB5_LOCKMODE_UNLOCK); + if (-1 == error) + { + /* + * Wait for something to be written to the file + */ + + if (debug > 1) + com_err(progname, 0, "process_messages_and_truncate: " + "About to wait for more work....\n"); + + if (pthread_cond_wait(&deltaFileCond, &deltaFileMutex)) + { + com_err(progname, 0, "process_messages_and_truncate: " + "Error from pthread_cond_wait!?\n"); + sleep(10); + } + + if (debug > 1) + com_err(progname, 0, "process_messages_and_truncate: " + "Back from pthread_cond_wait....\n"); + + } + if (debug > 2) + com_err(progname, 0, "process_messages_and_truncate: " + "releasing mutex...\n"); + + pthread_mutex_unlock(&deltaFileMutex); + } + else + { + /* Real error from process_umichrep_record */ + com_err(progname, 0, "process_messages_and_truncate: " + "Error returned from process_umichrep_record!\n"); + sleep(10); + } + } + + /* + * Close the database and stop logging + */ + + if (debug) + com_err(progname, 0, "process_messages_and_truncate: " + "Out of the loop, closing up....\n"); + + if ((rc = krb5_db_fini(krepd_context))) + { + com_err(progname, 0, "%s: %s while terminating use of database\n", + progname, error_message(rc)); + } + + /* + * Close the delta file and exit + */ + + fclose(deltaFile); + + if (debug) + com_err(progname, 0, "process_messages_and_truncate: Saying adios!\n"); + + pthread_exit((void *) 0); +} + + +void +send_error(context, fd, err_code, err_text) + krb5_context context; + int fd; + krb5_error_code err_code; + char *err_text; +{ + krb5_error error; + const char *text; + krb5_data outbuf; + char buf[1024]; + + memset((char *)&error, 0, sizeof(error)); + krb5_us_timeofday(context, &error.stime, &error.susec); + error.server = server; + error.client = client; + + if (err_text) + text = err_text; + else + text = error_message(err_code); + + error.error = err_code - ERROR_TABLE_BASE_krb5; + if (error.error > 127) { + error.error = KRB_ERR_GENERIC; + if (err_text) { + sprintf(buf, "%s %s", error_message(err_code), + err_text); + text = buf; + } + } + error.text.length = strlen(text) + 1; + if ((error.text.data = malloc(error.text.length))) { + strcpy(error.text.data, text); + if (!krb5_mk_error(context, &error, &outbuf)) { + (void) krb5_write_message(context, (void *)&fd,&outbuf); + krb5_free_data_contents(context, &outbuf); + } + free(error.text.data); + } +} + +void +recv_error(context, inbuf) + krb5_context context; + krb5_data *inbuf; +{ + krb5_error *error; + krb5_error_code retval; + + if ((retval = krb5_rd_error(context, inbuf, &error))) { + com_err(progname, retval, + "while decoding error packet from client"); + exit(1); + } + if (error->error == KRB_ERR_GENERIC) { + if (error->text.data) + com_err(progname, 0, + "Generic remote error: %s\n", + error->text.data); + } else if (error->error) { + com_err(progname, error->error + ERROR_TABLE_BASE_krb5, + "signalled from server"); + if (error->text.data) + com_err(progname, 0, + "Error text from client: %s\n", + error->text.data); + } + krb5_free_error(context, error); + exit(1); +} + Index: krb5/src/replication/threadtest.c diff -u /dev/null krb5/src/replication/threadtest.c:1.1 --- /dev/null Wed Nov 9 17:44:12 2005 +++ krb5/src/replication/threadtest.c Mon Oct 16 11:24:50 2000 @@ -0,0 +1,161 @@ +/* + * COPYRIGHT © 1998 + * THE REGENTS OF THE UNIVERSITY OF MICHIGAN + * ALL RIGHTS RESERVED + * + * PERMISSION IS GRANTED TO USE, COPY, CREATE DERIVATIVE WORKS + * AND REDISTRIBUTE THIS SOFTWARE AND SUCH DERIVATIVE WORKS + * FOR ANY PURPOSE, SO LONG AS NO FEE IS CHARGED, AND SO LONG + * AS THE COPYRIGHT NOTICE ABOVE, THIS GRANT OF PERMISSION, + * AND THE DISCLAIMER BELOW APPEAR IN ALL COPIES MADE; AND SO + * LONG AS THE NAME OF THE UNIVERSITY OF MICHIGAN IS NOT USED + * IN ANY ADVERTISING OR PUBLICITY PERTAINING TO THE USE OR + * DISTRIBUTION OF THIS SOFTWARE WITHOUT SPECIFIC, WRITTEN + * PRIOR AUTHORIZATION. + * + * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION + * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY + * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF + * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING + * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE + * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR + * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING + * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN + * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGES. +*/ + +#if defined(sun) +#define _REENTRANT +#endif +#include +#include +/* +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +*/ + +#include "k5-int.h" +#include "com_err.h" +#include + +#include +#include +#include "krep.h" + +int debug = 0; + +void * thread_routine + PROTOTYPE((void *)); + +#define MAX_THREADS 3 + +int +main(argc, argv) + int argc; + char **argv; +{ + pthread_t processThread[MAX_THREADS]; + pthread_addr_t processThreadStatus[MAX_THREADS]; + krb5_context main_context[MAX_THREADS]; + int i; + int rc; + + debug += 3; + + /* + * Before starting threads, see if there is a problem calling krb5_init_context + * more than once from a main thread. + */ + for (i = 0; i < MAX_THREADS; i++) + { + if (debug > 1) + fprintf(stderr, "main: calling krb5_init_context (%d)\n", i); + + rc = krb5_init_context(&main_context[i]); + + if (debug > 1) + fprintf(stderr, "main: returned from krb5_init_context (%d) with rc %d\n", i, rc); + + sleep(3); + } + + + /* + * Now start some threads... + */ + for (i = 0; i < MAX_THREADS; i++) + { + fprintf(stderr, "Spawning thread number %d\n", i); + if (rc = pthread_create(&processThread[i], + pthread_attr_default, + thread_routine, + (void *)(i*5))) + { + fprintf(stderr, "Error spawning thread to process delta file!\n"); + exit(1); + } + } + + /* + * And wait for them all to finish... + */ + for (i = 0; i < MAX_THREADS; i++) + { + fprintf(stderr, "Waiting for thread number %d to complete...\n", i); + pthread_join(processThread[i], &processThreadStatus[i]); + } + + exit(0); +} + +void * thread_routine(void *parm) +{ + int rc; + FILE * deltaFile; + krb5_context thread_context; + krb5_auth_context thread_auth_context; + int sleeptime = (int) parm; + + if (debug > 1) + fprintf(stderr, "*** thread_routine: thread entered ***\n"); + + if (debug > 1) + fprintf(stderr, "thread_routine: sleeping %d seconds before calling krb5_init_context\n", sleeptime); + + sleep(sleeptime); + + /* + * Initialize use of kerberos + */ + + if (debug > 1) + fprintf(stderr, "thread_routine: now calling krb5_init_context!\n"); + + rc = krb5_init_context(&thread_context); + + if (debug > 1) + fprintf(stderr, "thread_routine: returned from krb5_init_context with rc %d\n", rc); + + sleep(10); + + if (debug) + fprintf(stderr, "thread_routine: Saying adios!\n"); + + pthread_exit((void *) 0); +}