diff -Naur afs-krb5.orig/patches/fakeka.patch afs-krb5/patches/fakeka.patch --- afs-krb5.orig/patches/fakeka.patch 1969-12-31 18:00:00.000000000 -0600 +++ afs-krb5/patches/fakeka.patch 2005-08-06 16:28:19.289999000 -0500 @@ -0,0 +1,94 @@ +--- fakeka.c.orig 2005-07-27 23:02:58.323377906 -0400 ++++ fakeka.c 2005-07-27 23:06:16.271593547 -0400 +@@ -117,6 +117,8 @@ + char *localcell = NULL; + krb5_timestamp req_time; + kadm5_config_params realm_params; ++krb5_data seed; /* same as krb5kdc main.c */ ++krb5_keyblock temp_key; + int debug = 0; + + +@@ -730,7 +732,8 @@ + */ + + rv = make_reply_packet(context, handle, reply, challenge + 1, start_time, +- end_time, name, inst, "krbtgt", localcell, ++ end_time, name, inst, "krbtgt", ++ localrealm, + ckey, csched, "tgsT"); + error: + memset(ckey, 0, sizeof(ckey)); +@@ -1258,6 +1261,8 @@ + exit(1); + } + ++ /* This is actually the modern method */ ++ + if ((code = kadm5_get_config_params(context, NULL, NULL, NULL, + &realm_params))) { + com_err(argv[0], code, "while getting realm parameters"); +@@ -1269,11 +1274,10 @@ + exit(1); + } + +- /* +- * We need to initialize the random number generator for DES. Use +- * the master key to do this. ++ /* ++ * Get the master key + */ +- ++ + if ((code = krb5_parse_name(context, realm_params.mask & + KADM5_CONFIG_MKEY_NAME ? + realm_params.mkey_name : "K/M", +@@ -1288,16 +1292,45 @@ + exit(1); + } + ++ ++ /* ++ * Decrypt the master key using any available enctype ++ */ ++ + if ((code = kadm5_decrypt_key(handle, &master_princ_rec, +- ENCTYPE_DES_CBC_CRC, -1, 0, &mkey, NULL, +- NULL))) { ++ -1, -1, 0, &mkey, NULL, NULL))) { ++ + com_err(argv[0], code, "while decrypting the master key"); + exit(1); ++ } ++ ++ ++ /* ++ * We need to initialize the random number generator for DES. Use ++ * the modern krb5kdc method. ++ */ ++ ++ seed.length = mkey.length; ++ seed.data = mkey.contents; ++ ++ if((code = krb5_c_random_add_entropy(context, ++ KRB5_C_RANDSOURCE_TRUSTEDPARTY, ++ &seed))) { ++ com_err(argv[0], code, "while attempting to add entropy"); ++ exit(1); + } + +- des_init_random_number_generator(mkey.contents); ++ if ((code = krb5_c_make_random_key(context, ++ ENCTYPE_DES_CBC_CRC, &temp_key))) { ++ com_err(argv[0], code, ++ "while initializing V4 random key generator"); ++ exit(1); ++ } ++ ++ des_init_random_number_generator(temp_key.contents); + + krb5_free_keyblock_contents(context, &mkey); ++ krb5_free_keyblock_contents(context, &temp_key); + + kadm5_free_principal_ent(handle, &master_princ_rec); + diff -Naur afs-krb5.orig/README afs-krb5/README --- afs-krb5.orig/README 2003-03-18 12:43:04.000000000 -0600 +++ afs-krb5/README 2005-08-06 17:03:35.699999000 -0500 @@ -1,6 +1,6 @@ $Id: README,v 1.13 2003/03/18 18:43:04 kenh Exp $ -This is the README for version 2.0 of the NRL AFS-Kerberos 5 migration kit. +This is the README for version 2.X of the NRL AFS-Kerberos 5 migration kit. Please note this is an UNOFFICIAL kit, and none of the procedures or code contained within are endorsed by MIT, the OpenAFS Elders, or the @@ -37,6 +37,8 @@ (actually, it _might_ work with DCE, but I have no way or interest in testing it with DCE). +Version 2.1 was update to reflect changes in MIT Kerberos V Release 1.4.1 + TABLE OF CONTENTS ----------------- @@ -62,8 +64,9 @@ AFS cell name than your Kerberos realm. patches - A directory containing patches to apply to a vanilla - Kerberos 5 1.0.6 release tree. Currently this directory - contains one file: "monster-patch" + Kerberos 5 release trees. Currently this directory + contains one old patch: "monster-patch" (for Release 1.2.7) + and a newer patch fakeka.patch (for Release 1.4.1) src - A directory containing various migration programs and utilities (more detail below). @@ -86,6 +89,11 @@ into a MIT Kerberos 5 dump file, which you can load into your database using "kdb5_util". + kas-kdb-merge.pl - A script to merge in information recording + which user last modified a given ka database + entry. Otherwise this information will be lost when + you convert to krb5. + asetkey - This is a version of the "asetkey" program from MIT, but updated to use the Kerberos 5 keytab routines. This is used to extract the AFS service key from a V5 keytab @@ -104,7 +112,9 @@ Kerberos 4, and modified for Kerberos 5 by me. Note that MIT Kerberos 5 versions 1.3 and later will ship with a version of fakeka; you should use that version - in preference to the one provided here. + in preference to the one provided here. The patch + directory includes a patch that should be applied to + the fakeka in Release 1.4.1. ka-forwarder - This is a program to forward KA requests from your AFS database servers to your V5 KDC (if you aren't running @@ -170,7 +180,7 @@ of Kerberos, please contact the me (kenh@cmf.nrl.navy.mil) and I'll try to help you out. -AN ADDIOTIONAL NOTE ABOUT THE MONSTER-PATCH +AN ADDITIONAL NOTE ABOUT THE MONSTER-PATCH My eventual goal is to incorporate all of the AFS-related changes into the MIT Kerberos 5 distribution. Many of these changes will already be in @@ -182,6 +192,8 @@ % cd $KRB5SRCDIR % patch < monster-patch +(NB: Solaris users will need to get GNU patch.) + You should have only three rejected patches for the following files: kadmin/server/kadm_rpc_svc.c @@ -212,6 +224,11 @@ compatibility. You should also be sure to build with --with-krb4 (it's the default, but some of the migration kit depends on it). +The --with-afs flag needs to specify where to find AFS, as in + --with-afs=/usr/afsws +Otherwise the build will fail when it tries to build AFS dependent +programs. + STEP THREE: TEST KERBEROS 5 --------------------------- @@ -221,6 +238,14 @@ you create it, so it might be a good idea to create one that's the same name as your AFS cell (but all uppercase, of course). +If you are running your KDC on an AFS database machine, which has its +negatives and positives, you will need to temporarily stop 'kaserver' +when you start the kerberos daemons. The Kerberos daemons refuse to +start unless they can bind all their addresses. After starting them, +you can start kaserver again. It will complain that it's unable to +bind all its desired ports, but it will start up anyway and will be +able to service 'klog' requests. + Don't try to do anything with AFS at this point; just see if you can get the Kerberos basics working. Make sure Kerberos "telnet"/"rlogin" work, forwarding your tickets, you understand how to administrate your @@ -262,9 +287,14 @@ If you built Kerberos in the same directory as the source for it, only use the --with-krb5-obj flag. +On Solaris, ka-forwarder sometimes gets a build error. +You may need to need to edit the generated Makefile in src/ to define + NETLIBS = -lsocket -lnsl + Once you've built the software, you will want to test out the included -V5 "aklog". To do this, do the following things: (Note! These are order -dependent). +V5 "aklog". (Recent versions of OpenAFS apparently come with aklog. +Use the OpenAFS one in preference if you have it.) To do this, do the +following things: (Note! These are order dependent.) 1) Create an AFS principal in the Kerberos database. Call it: @@ -284,6 +314,8 @@ KDC server machines. This translates V5 tickets to V4 tickets, and is used by "aklog". Unless you want to have to have the AFS key in a keytab on your KDC, add the "-m" option to krb524d when starting it up. + In fact krb524d won't run unless you give it some flag, so "-m" + can be seen as mandatory. 3) On your AFS update server (or wherever your master KeyFile is), use "kadmin" to extract the afs key into the keytab on that machine with @@ -291,7 +323,7 @@ % kadmin ... - kadmin: ktadd -k /etc/krb5.keytab -e des-cbc-crc afs@YOUR.CELL.NAME + kadmin: ktadd -k /etc/krb5.keytab -e des-cbc-crc:normal afs@YOUR.CELL.NAME Entry for principal afs@YOUR.CELL.NAME with kvno 6, ... added to ... Note that "ktadd" actually _changes_ the key to a new random key @@ -440,6 +472,10 @@ If you have problems, look at the syslog information put out by "fakeka" and "ka-forwarder", which goes to the "daemon" facility. +If you get the error + fakeka: No matching key in entry while decrypting the master key +this may mean you are using an unpatched fakeka. See patches/fakeka.patch + STEP SEVEN: THE DREADED MIGRATION --------------------------------- @@ -488,6 +524,18 @@ afs@YOUR.CELL.NAME admin@YOUR.CELL.NAME + Optionally, if you want to preserve as much information in the + database as possible, you should perform the following steps: + + kas list -long >/tmp/kas_output.txt + ./afs2k5db /usr/afs/db/kaserver.DB0 >/tmp/krb5-dumpfile + ./kas-kdb-merge.pl /tmp/krb5-dumpfile /tmp/kas-output.txt \ + YOUR.REALM.NAME >/tmp/final-krb5-database + + and then edit the resulting file (to remove the four principals + mentioned above). NB: This script will choke on cross-realm + principals. + 8) We then brought up "fakeka" on the KDC and created a special "ka-forwarder" instance in "bos" on the database servers: @@ -605,6 +653,9 @@ bunch of typographical fixes. - Matt Crawford for a bunch of fixes to the MIT codebase (especially the cracklib cleanup!) +- Chris Wing for improving the retention of information from kaserver.DB0 + to Kerberos 5 +- Matt Benjamin for fixing fakeka to work with any master key type. Questions, comments, suggestions, bug fixes, fan and hate mail can be sent to: diff -Naur afs-krb5.orig/src/afs2k5db.c afs-krb5/src/afs2k5db.c --- afs-krb5.orig/src/afs2k5db.c 2003-03-16 19:13:34.000000000 -0600 +++ afs-krb5/src/afs2k5db.c 2005-08-04 15:10:11.020001000 -0500 @@ -79,6 +79,7 @@ long flags; /* random flags */ #define KAFFREE 0x002 /* entry is on free list */ #define KAFOLDKEYS 0x010 /* entry is used to store old keys */ +#define KAFNOTGS 0x008 /* entry cannot get a TGT */ long next; /* next block same entry (or freelist) */ Date user_expiration; /* user registration good till then */ Date modification_time; /* time of last update */ @@ -125,7 +126,8 @@ void db_header_output(FILE *); void db_entry_output(FILE *, krb5_context, char *, char *, char *, int, - krb5_deltat, krb5_key_data *, Date); + krb5_deltat, krb5_key_data *, Date, Date, Date, + krb5_flags); int main(int argc, char *argv[]) @@ -328,6 +330,8 @@ db_header_output(stdout); for (;;) { + krb5_flags princ_flags = 0; + n = read(kasdb, (char *) &dbentry, sizeof(struct kaentry)); if (! n) break; @@ -342,6 +346,15 @@ continue; /* + * If the kaserver entry had NOTGS set, map this to + * KRB5_KDB_DISALLOW_ALL_TIX to get a similar meaning. + * (allow an administrator to disable a principal without + * changing the password or expiration data) + */ + if (ntohl(dbentry.flags) & KAFNOTGS) + princ_flags |= KRB5_KDB_DISALLOW_ALL_TIX; + + /* * Okay, now we are getting records from the KA database. * Now we have to decide what to do with them. If we * weren't given any users on the command line, then @@ -381,6 +394,14 @@ exit(1); } + /* + * kaserver sets the NOTGS flag for krbtgt principal; we don't + * want to translate it to DISALLOW_ALL_TIX + */ + if (!strcmp(dbentry.userID.name, "krbtgt")) { + princ_flags = 0; + } + if ((!strcmp(dbentry.userID.name, "krbtgt")) && (dbentry.userID.instance && (strcmp(dbentry.userID.instance, realm)))) @@ -392,7 +413,10 @@ lifetime == 0 ? dbentry.max_ticket_lifetime : lifetime, - &key_data,dbentry.user_expiration); + &key_data,dbentry.user_expiration, + dbentry.modification_time, + dbentry.change_password_time, + princ_flags); } db_entry_output(stdout, convert_context, dbentry.userID.name, @@ -400,7 +424,9 @@ NULL : dbentry.userID.instance, realm, kvno == -1 ? dbentry.key_version : kvno, lifetime == 0 ? dbentry.max_ticket_lifetime : - lifetime, &key_data,dbentry.user_expiration); + lifetime, &key_data,dbentry.user_expiration, + dbentry.modification_time, + dbentry.change_password_time, princ_flags); } diff -Naur afs-krb5.orig/src/fakeka.c afs-krb5/src/fakeka.c --- afs-krb5.orig/src/fakeka.c 1999-03-11 15:46:09.000000000 -0600 +++ afs-krb5/src/fakeka.c 2005-08-06 16:34:03.120006000 -0500 @@ -157,6 +157,8 @@ char *localcell = NULL; krb5_timestamp req_time; kadm5_config_params realm_params; +krb5_data seed; /* same as krb5kdc main.c */ +krb5_keyblock temp_key; int debug = 0; @@ -767,7 +769,8 @@ */ rv = make_reply_packet(context, handle, reply, challenge + 1, start_time, - end_time, name, inst, "krbtgt", localcell, + end_time, name, inst, "krbtgt", + localrealm, ckey, csched, "tgsT"); error: bzero(ckey, sizeof(ckey)); @@ -1293,6 +1296,8 @@ exit(1); } + /* This is actually the modern method */ + if ((code = kadm5_get_config_params(context, NULL, NULL, NULL, &realm_params))) { com_err(argv[0], code, "while getting realm parameters"); @@ -1304,11 +1309,10 @@ exit(1); } - /* - * We need to initialize the random number generator for DES. Use - * the master key to do this. + /* + * Get the master key */ - + if ((code = krb5_parse_name(context, realm_params.mask & KADM5_CONFIG_MKEY_NAME ? realm_params.mkey_name : "K/M", @@ -1323,16 +1327,45 @@ exit(1); } + + /* + * Decrypt the master key using any available enctype + */ + if ((code = kadm5_decrypt_key(handle, &master_princ_rec, - ENCTYPE_DES_CBC_CRC, -1, 0, &mkey, NULL, - NULL))) { + -1, -1, 0, &mkey, NULL, NULL))) { + com_err(argv[0], code, "while decrypting the master key"); exit(1); + } + + + /* + * We need to initialize the random number generator for DES. Use + * the modern krb5kdc method. + */ + + seed.length = mkey.length; + seed.data = mkey.contents; + + if((code = krb5_c_random_add_entropy(context, + KRB5_C_RANDSOURCE_TRUSTEDPARTY, + &seed))) { + com_err(argv[0], code, "while attempting to add entropy"); + exit(1); } - des_init_random_number_generator(mkey.contents); + if ((code = krb5_c_make_random_key(context, + ENCTYPE_DES_CBC_CRC, &temp_key))) { + com_err(argv[0], code, + "while initializing V4 random key generator"); + exit(1); + } + + des_init_random_number_generator(temp_key.contents); krb5_free_keyblock_contents(context, &mkey); + krb5_free_keyblock_contents(context, &temp_key); kadm5_free_principal_ent(handle, &master_princ_rec); diff -Naur afs-krb5.orig/src/k5dbsubs.c afs-krb5/src/k5dbsubs.c --- afs-krb5.orig/src/k5dbsubs.c 1999-09-10 13:47:05.000000000 -0500 +++ afs-krb5/src/k5dbsubs.c 2005-08-04 15:10:11.060000000 -0500 @@ -51,11 +51,13 @@ void db_entry_output(FILE *f, krb5_context convert_context, char *user, char *instance, char *realm, int kvno, - krb5_deltat lifetime, krb5_key_data *key_data, Date expiration) + krb5_deltat lifetime, krb5_key_data *key_data, + Date expiration, Date last_modified, Date last_changepw, + krb5_flags attributes) { krb5_principal princ; krb5_error_code retval; - krb5_timestamp time; + krb5_timestamp time = last_modified; unsigned char moddata[4]; char *name = NULL, *mname; int i, j; @@ -88,12 +90,18 @@ fprintf(f, "%d\t", (int) strlen(name)); /* - * These are the number of "tl_data" (one, the master principal + expire time), + * These are the number of "tl_data" (two; the last modifier of the + * principal, and the last time the password was changed), * the number of key data (only one, the AFS key), and the length of the * extra data (0) */ - fprintf(f, "1\t1\t0\t"); + if (last_changepw) { + fprintf(f, "2\t"); + } else { + fprintf(f, "1\t"); + } + fprintf(f, "1\t0\t"); /* * The principal itself @@ -102,21 +110,23 @@ fprintf(f, "%s\t", name); /* - * Attributes (none), maximum life (variable), maximum rewnewable life (7 days) - * expiration (epoch), password expiration (never), last success (never), + * Attributes, maximum life (variable), maximum rewnewable life (7 days) + * expiration (0 means never), password expiration (never), last success (never), * last failure (never), failed authorization count (none) */ - fprintf(f, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", 0, (int) lifetime, - 60*60*24*7, (expiration != -1) ? expiration : 2145830400, 0, 0, 0, 0); + fprintf(f, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", attributes, (int) lifetime, + 60*60*24*7, (expiration != -1) ? expiration : 0, 0, 0, 0, 0); /* * Put a MOD PRINC entry (although I'm not quite sure what this is for) */ - if ((retval= krb5_timeofday(convert_context, &time))) { - com_err("db_entry_output", retval, "while getting time of day"); - exit(1); + if (!time) { + if ((retval= krb5_timeofday(convert_context, &time))) { + com_err("db_entry_output", retval, "while getting time of day"); + exit(1); + } } if ((retval = krb5_unparse_name(convert_context, princ, &mname))) { com_err("db_entry_output", retval, "while unparsing a name"); @@ -135,6 +145,20 @@ fprintf(f, "\t"); /* + * Put a LAST_PWD_CHANGE entry + */ + if (last_changepw) { + krb5_kdb_encode_int32(last_changepw, moddata); + + fprintf(f, "%d\t%d\t", KRB5_TL_LAST_PWD_CHANGE, 4); + + for (i = 0; i < 4; i++) + fprintf(f, "%02x", moddata[i]); + + fprintf(f, "\t"); + } + +/* * Now, output the key data. Output two entries - one for the key, one * for the salt (the salt is needed for AFS). Note that we don't output * the salt if one isn't included (for example, if you're doing an AFS diff -Naur afs-krb5.orig/src/kas-kdb-merge.pl afs-krb5/src/kas-kdb-merge.pl --- afs-krb5.orig/src/kas-kdb-merge.pl 1969-12-31 18:00:00.000000000 -0600 +++ afs-krb5/src/kas-kdb-merge.pl 2005-08-05 12:02:52.659999000 -0500 @@ -0,0 +1,143 @@ +#!/usr/local/bin/perl + + +# Add in the correct 'last modified by' information from the kaserver into +# a krb5 KDC dump file generated by 'afs2k5db'. Sigh. +# There is no other good way to do this, because the 'modification_id' +# field depends on having the Ubik library up and running to read hash +# tables, etc. to locate entries. + + +# XXX: need to do more parsing of the individual tl_data to make sure we +# get what we want. For now, assume the first tl_data is TL_MOD_PRINC + + +if (@ARGV != 3) { + print STDERR "Usage: \n"; + exit(1); +} + +$REALMNAME = $ARGV[2]; + +if (!open(KASDUMP, "<$ARGV[1]")) { + print STDERR "failed to open kas dump\n"; + exit(1); +} + +while ($line = ) { + chomp($line); + + if ($line =~ /^User data for (\S+)/) { + $current_princ = $1; + + next; + } + + if ($line =~ /last mod on \S+\s+\S+\s+\S+\s+\S+\s+\S+ by\s*$/) { + $modifier = "unknown"; + + # the modifier entry went away? + $modmap{$current_princ} = $modifier; + next; + } + + if ($line =~ /last mod on \S+\s+\S+\s+\S+\s+\S+\s+\S+ by (\S+)/) { + $modifier = $1; + + # this only happened for AuthServer.Admin? + if ($modifier eq "") { + $modifier = "unknown"; + } + + $modmap{$current_princ} = $modifier; + } +} + +close(KASDUMP); + + +# open dump file +if (!open(DUMPFILE, "<$ARGV[0]")) { + print STDERR "failed to open dump file\n"; + exit(1); +} + +# skip first line +$line = ; +print $line; + +while ($line = ) { + chomp($line); + + if (substr($line, -1) ne ";") { + print STDERR "Error: Line is invalid - missing ';'\n"; + exit(1); + } + + # remove ; + chop($line); + + my (@records) = split(/\t/, $line); + + if ($records[0] ne "princ") { + print STDERR "Error: Line $linecnt is invalid - first field is +not 'princ'\n"; + exit(1); + } + + # get some more information from this record + my $n_tl_data = $records[3]; + my $n_key_data = $records[4]; + my $princ_name = $records[6]; + my $key = $records[15 + 3 * $n_tl_data + 4]; + + my $modprinc_data = $records[17]; + + $REALMAT = '@' . $REALMNAME; + + if (substr($princ_name, -length($REALMAT)) ne $REALMAT) { + print STDERR "bad conversion: $princ_name\n"; + exit(1); + } + + $k4princ = substr($princ_name, 0, -length($REALMAT)); + + # Transform / to ' + $k4princ =~ tr/\//./; + + # transform spaces to "\040" + $k4princ =~ s/ /\\040/g; + + if (!defined($modmap{$k4princ})) { + print STDERR "No mapping for $princ_name conv $k4princ\n"; + exit(1); + } + + # XXX: we could just store the modifier as-is (without @REALM), + # but when I load the resulting kdb database, kadmin prints the + # information with @REALM added back to it anyway. + $mapped = $modmap{$k4princ} . $ATREALM; + + # Replace existing data with hex-encoded string. + $hex = ""; + for($x=0;$x