Index: krb5/src/include/kerberosIV/krb.h =================================================================== --- krb5/src/include/kerberosIV/krb.h (revision 2284) +++ krb5/src/include/kerberosIV/krb.h (working copy) @@ -674,7 +674,7 @@ int KRB5_CALLCONV krb_save_credentials (char *service, char *instance, char *realm, C_Block session, int lifetime, int kvno, - KTEXT ticket, long issue_date); + KTEXT ticket, KRB4_32 issue_date); /* send_to_kdc.c */ /* XXX PRIVATE? KfM doesn't export. */ int send_to_kdc Index: krb5/src/lib/krb4/krb4int.h =================================================================== --- krb5/src/lib/krb4/krb4int.h (revision 2284) +++ krb5/src/lib/krb4/krb4int.h (working copy) @@ -90,7 +90,7 @@ char *krb_stime(long *); /* tf_util.c */ -int tf_save_cred(char *, char *, char *, C_Block, int , int, KTEXT, long); +int tf_save_cred(char *, char *, char *, C_Block, int , int, KTEXT, KRB4_32); /* unix_glue.c */ int krb_start_session(char *); @@ -112,7 +112,7 @@ void krb4int_et_fini(void); int krb4int_save_credentials_addr( - char *, char *, char *, C_Block, int, int, KTEXT, long, KRB_UINT32); + char *, char *, char *, C_Block, int, int, KTEXT, KRB4_32, KRB_UINT32); int krb4int_send_to_kdc_addr(KTEXT, KTEXT, char *, struct sockaddr *, socklen_t *); Index: krb5/src/lib/krb4/CCache-glue.c =================================================================== --- krb5/src/lib/krb4/CCache-glue.c (revision 2284) +++ krb5/src/lib/krb4/CCache-glue.c (working copy) @@ -120,7 +120,7 @@ int lifetime, int kvno, KTEXT ticket, - long issue_date) + KRB4_32 issue_date) { return krb4int_save_credentials_addr(service, instance, realm, session, lifetime, kvno, @@ -140,7 +140,7 @@ int lifetime, int kvno, KTEXT ticket, - long issue_date, + KRB4_32 issue_date, KRB_UINT32 local_address) { cc_int32 cc_err = ccNoError; Index: krb5/src/lib/krb4/memcache.c =================================================================== --- krb5/src/lib/krb4/memcache.c (revision 2284) +++ krb5/src/lib/krb4/memcache.c (working copy) @@ -469,8 +469,8 @@ C_Block session; /* Session key */ int lifetime; /* Lifetime */ int kvno; /* Key version number */ - KTEXT ticket; /* The ticket itself */ - long issue_date; /* The issue time */ + KTEXT ticket; /* The ticket itself */ + KRB4_32 issue_date; /* The issue time */ KRB_UINT32 laddr; { CREDENTIALS cr; @@ -500,7 +500,7 @@ int lifetime, int kvno, KTEXT ticket, - long issue_date) + KRB4_32 issue_date) { return krb4int_save_credentials_addr(name, inst, realm, session, lifetime, kvno, ticket, Index: krb5/src/lib/krb4/tf_util.c =================================================================== --- krb5/src/lib/krb4/tf_util.c (revision 2284) +++ krb5/src/lib/krb4/tf_util.c (working copy) @@ -28,6 +28,8 @@ #include "k5-int.h" #include "krb4int.h" +#include "../crypto/aes/aesopt.h" + #include #include #include @@ -43,6 +45,8 @@ #include #endif /* TKT_SHMEM */ + + #define TOO_BIG -1 #define TF_LCK_RETRY ((unsigned)2) /* seconds to sleep before * retry if ticket file is @@ -93,7 +97,166 @@ #endif #endif + +#if (PLATFORM_BYTE_ORDER == AES_LITTLE_ENDIAN) +/* This was taken from jhutz's patch for heimdal krb4. It only + * applies to little endian systems. Big endian systems have a + * less elegant solution documented below. + * + * This record is written after every real ticket, to ensure that + * both 32- and 64-bit readers will perceive the next real ticket + * as starting in the same place. This record looks like a ticket + * with the following properties: + * Field 32-bit 64-bit + * ============ ================= ================= + * sname "." "." + * sinst "" "" + * srealm ".." ".." + * session key 002E2E00 xxxxxxxx xxxxxxxx 00000000 + * lifetime 0 0 + * kvno 0 12 + * ticket 12 nulls 4 nulls + * issue 0 0 + * + * Our code always reads and writes the 32-bit format, but knows + * to skip 00000000 at the front of a record, and to completely + * ignore tickets for the special alignment principal. + */ +static unsigned char align_rec[] = { + 0x2e, 0x00, 0x00, 0x2e, 0x2e, 0x00, 0x00, 0x2e, + 0x2e, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 +}; + +#else /* Big Endian */ + +/* These alignment records are for big endian systems. We need more + * of them because the portion of the 64-bit issue_date that overlaps + * with the start of a ticket on 32-bit systems contains an unpredictable + * number of NULL bytes. Preceeding these records is a second copy of the + * 32-bit issue_date. The srealm for the alignment records is always one of + * ".." or "?.." + */ + +/* No NULL bytes + * This is actually two alignment records since both 32- and 64-bit + * readers will agree on everything in the first record up through the + * issue_date size, except where sname starts. + * Field (1) 32-bit 64-bit + * ============ ================= ================= + * sname "????." "." + * sinst "" "" + * srealm ".." ".." + * session key 00000000 xxxxxxxx 00000000 xxxxxxxx + * lifetime 0 0 + * kvno 0 0 + * ticket 4 nulls 4 nulls + * issue 0 0 + * + * Field (2) 32-bit 64-bit + * ============ ================= ================= + * sname "." "." + * sinst "" "" + * srealm ".." ".." + * session key 002E2E00 xxxxxxxx xxxxxxxx 00000000 + * lifetime 0 0 + * kvno 0 12 + * ticket 12 nulls 4 nulls + * issue 0 0 + * + */ +static unsigned char align_rec_0[] = { + 0x2e, 0x00, 0x00, 0x2e, 0x2e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2e, 0x2e, 0x00, + 0x00, 0x2e, 0x2e, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +/* One NULL byte + * Field 32-bit 64-bit + * ============ ================= ================= + * sname "x" |"xx"|"xxx" "." + * sinst "xx."|"x."|"." ".." + * srealm ".." "..." + * session key 2E2E2E00 xxxxxxxx xxxxxxxx 00000000 + * lifetime 0 0 + * kvno 0 12 + * ticket 12 nulls 4 nulls + * issue 0 0 + */ +static unsigned char align_rec_1[] = { + 0x2e, 0x00, 0x2e, 0x2e, 0x00, 0x2e, 0x2e, 0x2e, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 +}; + +/* Two NULL bytes + * Field 32-bit 64-bit + * ============ ================= ================= + * sname "x" |"x" |"xx" ".." + * sinst "" |"x" |"" "" + * srealm "x.."|".."|".." ".." + * session key 002E2E00 xxxxxxxx xxxxxxxx 00000000 + * lifetime 0 0 + * kvno 0 12 + * ticket 12 nulls 4 nulls + * issue 0 0 + */ + static unsigned char align_rec_2[] = { + 0x2e, 0x2e, 0x00, 0x00, 0x2e, 0x2e, 0x00, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* Three NULL bytes + * Things break here for 32-bit krb4 libraries that don't + * understand this alignment record. We can't really do + * anything about the fact that the three strings ended + * in the duplicate timestamp. The good news is that this + * only happens once every 0x1000000 seconds, once roughly + * every six and a half months. We'll live. + * + * Discussion on the krbdev list has suggested the + * issue_date be incremented by one in this case to avoid + * the problem. I'm leaving this here just in case. + * + * Field 32-bit 64-bit + * ============ ================= ================= + * sname "" "." + * sinst "" "" + * srealm "" ".." + * session key 2E00002E 2E00FFFF xxxx0000 0000xxxx + * lifetime 0 0 + * kvno 4294901760 917504 + * ticket 14 nulls 4 nulls + * issue 0 0 + */ /* +static unsigned char align_rec_3[] = { + 0x2e, 0x00, 0x00, 0x2e, 0x2e, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +*/ +#endif /* (PLATFORM_BYTE_ORDER == AES_LITTLE_ENDIAN) */ + +/* * fd must be initialized to something that won't ever occur as a real * file descriptor. Since open(2) returns only non-negative numbers as * valid file descriptors, and tf_init always stuffs the return value @@ -104,7 +267,7 @@ * are invalid (ie. when deciding whether tf_init has been * called.) * c. In tf_close, be sure it gets reinitialized to a negative - * number. + * number. */ static int fd = -1; static int curpos; /* Position in tfbfr */ @@ -136,7 +299,7 @@ * int lifetime * int kvno * KTEXT_ST ticket_st - * long issue_date + * KRB4_32 issue_date * * Strings are stored NUL-terminated, and read back until a NUL is * found or the indicated number of bytes have been read. (So if you @@ -169,13 +332,13 @@ /* * tf_init() should be called before the other ticket file routines. * It takes the name of the ticket file to use, "tf_name", and a - * read/write flag "rw" as arguments. + * read/write flag "rw" as arguments. * * It tries to open the ticket file, checks the mode, and if everything * is okay, locks the file. If it's opened for reading, the lock is - * shared. If it's opened for writing, the lock is exclusive. + * shared. If it's opened for writing, the lock is exclusive. * - * Returns KSUCCESS if all went well, otherwise one of the following: + * Returns KSUCCESS if all went well, otherwise one of the following: * * NO_TKT_FIL - file wasn't there * TKT_FIL_ACC - file was in wrong mode, etc. @@ -190,7 +353,7 @@ uid_t me, metoo; struct stat stat_buf, stat_buffd; #ifdef TKT_SHMEM - char shmidname[MAXPATHLEN]; + char shmidname[MAXPATHLEN]; FILE *sfp; int shmid; #endif @@ -229,7 +392,7 @@ * If "wflag" is set, open the ticket file in append-writeonly mode * and lock the ticket file in exclusive mode. If unable to lock * the file, sleep and try again. If we fail again, return with the - * proper error message. + * proper error message. */ curpos = sizeof(tfbfr); @@ -404,7 +567,7 @@ } /* * Otherwise "wflag" is not set and the ticket file should be opened - * for read-only operations and locked for shared access. + * for read-only operations and locked for shared access. */ if (me != metoo && do_seteuid(me) < 0) @@ -469,7 +632,7 @@ * principal's name is filled into the "p" parameter. If all goes well, * KSUCCESS is returned. If tf_init() wasn't called, TKT_FIL_INI is * returned. If the name was null, or EOF was encountered, or the name - * was longer than ANAME_SZ, TKT_FIL_FMT is returned. + * was longer than ANAME_SZ, TKT_FIL_FMT is returned. */ int KRB5_CALLCONV tf_get_pname(p) @@ -492,7 +655,7 @@ * goes well, KSUCCESS is returned. If tf_init() wasn't called, * TKT_FIL_INI is returned. If EOF was encountered, or the instance * was longer than ANAME_SZ, TKT_FIL_FMT is returned. Note that the - * instance may be null. + * instance may be null. */ int KRB5_CALLCONV tf_get_pinst(inst) @@ -512,34 +675,61 @@ * tf_get_cred() reads a CREDENTIALS record from a ticket file and fills * in the given structure "c". It should only be called after tf_init(), * tf_get_pname(), and tf_get_pinst() have been called. If all goes well, - * KSUCCESS is returned. Possible error codes are: + * KSUCCESS is returned. Possible error codes are: * * TKT_FIL_INI - tf_init wasn't called first * TKT_FIL_FMT - bad format * EOF - end of file encountered */ -int KRB5_CALLCONV tf_get_cred(c) +int KRB5_CALLCONV real_tf_get_cred(c) CREDENTIALS *c; { KTEXT ticket = &c->ticket_st; /* pointer to ticket */ int k_errno; - long issue_date; + unsigned char nullbuf[3]; /* used for 64-bit issue_date tf compatibility */ if (fd < 0) { if (krb_debug) fprintf(stderr, "tf_get_cred called before tf_init.\n"); return TKT_FIL_INI; } - if ((k_errno = tf_gets(c->service, SNAME_SZ)) < 2) - switch (k_errno) { - case TOO_BIG: - case 1: /* can't be just a null */ - tf_close(); - return TKT_FIL_FMT; - case 0: - return EOF; - } + if ((k_errno = tf_gets(c->service, SNAME_SZ)) < 2) { + +#if (PLATFORM_BYTE_ORDER == AES_BIG_ENDIAN) + /* If we're big endian then we can have a null service name as part of + * an alignment record. */ + if (k_errno < 2) + switch (k_errno) { + case TOO_BIG: + tf_close(); + return TKT_FIL_FMT; + case 0: + return EOF; + } +#else /* Little Endian */ + /* If we read an empty service name, it's possible that's because + * the file was written by someone who thinks issue_date should be + * 64 bits. If that is the case, there will be three more zeros, + * followed by the real record.*/ + + if (k_errno == 1 && + tf_read(nullbuf, 3) == 3 && + !nullbuf[0] && !nullbuf[1] && !nullbuf[2]) + k_errno = tf_gets(c->service, SNAME_SZ); + + if (k_errno < 2) + switch (k_errno) { + case TOO_BIG: + case 1: /* can't be just a null */ + tf_close(); + return TKT_FIL_FMT; + case 0: + return EOF; + } +#endif /*(PLATFORM_BYTE_ORDER == AES_BIG_ENDIAN)*/ + + } if ((k_errno = tf_gets(c->instance, INST_SZ)) < 1) switch (k_errno) { case TOO_BIG: @@ -547,7 +737,7 @@ case 0: return EOF; } - if ((k_errno = tf_gets(c->realm, REALM_SZ)) < 2) + if ((k_errno = tf_gets(c->realm, REALM_SZ)) < 2) { switch (k_errno) { case TOO_BIG: case 1: /* can't be just a null */ @@ -556,21 +746,85 @@ case 0: return EOF; } + } + if ( tf_read((char *) (c->session), KEY_SZ) < 1 || tf_read((char *) &(c->lifetime), sizeof(c->lifetime)) < 1 || tf_read((char *) &(c->kvno), sizeof(c->kvno)) < 1 || tf_read((char *) &(ticket->length), sizeof(ticket->length)) < 1 || - /* don't try to read a silly amount into ticket->dat */ + /* don't try to read a silly amount into ticket->dat */ ticket->length > MAX_KTXT_LEN || tf_read((char *) (ticket->dat), ticket->length) < 1 || - tf_read((char *) &(issue_date), sizeof(issue_date)) < 1 + tf_read((char *) &(c->issue_date), sizeof(c->issue_date)) < 1 ) { tf_close(); return TKT_FIL_FMT; } - c->issue_date = issue_date; + +#if (PLATFORM_BYTE_ORDER == AES_BIG_ENDIAN) + /* If the issue_date is 0 and we're not dealing with an alignment + record, then it's likely we've run into an issue_date written by + a 64-bit library that is using long instead of KRB4_32. Let's get + the next four bytes instead. + */ + if (0 == c->issue_date) { + int len = strlen(c->realm); + if (!(2 == len && 0 == strcmp(c->realm, "..")) && + !(3 == len && 0 == strcmp(c->realm + 1, ".."))) { + if (tf_read((char *) &(c->issue_date), sizeof(c->issue_date)) < 1) { + tf_close(); + return TKT_FIL_FMT; + } + } + } + +#endif + + return KSUCCESS; +} + +int KRB5_CALLCONV tf_get_cred(c) + CREDENTIALS *c; +{ + int k_errno; + int fake; + + do { + fake = 0; + k_errno = real_tf_get_cred(c); + if (k_errno) + return k_errno; + +#if (PLATFORM_BYTE_ORDER == AES_BIG_ENDIAN) + /* Here we're checking to see if the realm is one of the + * alignment record realms, ".." or "?..", so we can skip it. + * If it's not, then we need to verify that the service name + * was not null as this should be a valid ticket. + */ + { + int len = strlen(c->realm); + if (2 == len && 0 == strcmp(c->realm, "..")) + fake = 1; + if (3 == len && 0 == strcmp(c->realm + 1, "..")) + fake = 1; + if (!fake && 0 == strlen(c->service)) { + tf_close(); + return TKT_FIL_FMT; + } + } +#else /* Little Endian */ + /* Here we're checking to see if the service principal is the + * special alignment record principal ".@..", so we can skip it. + */ + if (strcmp(c->service, ".") == 0 && + strcmp(c->instance, "") == 0 && + strcmp(c->realm, "..") == 0) + fake = 1; +#endif /* (PLATFORM_BYTE_ORDER == AES_BIG_ENDIAN) */ + } while (fake); + #ifdef TKT_SHMEM memcpy(c->session, tmp_shm_addr, KEY_SZ); tmp_shm_addr += KEY_SZ; @@ -611,7 +865,7 @@ * tf_gets() is an internal routine. It takes a string "s" and a count * "n", and reads from the file until either it has read "n" characters, * or until it reads a null byte. When finished, what has been read exists - * in "s". If it encounters EOF or an error, it closes the ticket file. + * in "s". If it encounters EOF or an error, it closes the ticket file. * * Possible return values are: * @@ -711,7 +965,7 @@ int lifetime; /* Lifetime */ int kvno; /* Key version number */ KTEXT ticket; /* The ticket itself */ - long issue_date; /* The issue time */ + KRB4_32 issue_date; /* The issue time */ { off_t lseek(); @@ -777,10 +1031,66 @@ if (write(fd, (char *) (ticket->dat), count) != count) goto bad; /* Issue date */ - if (write(fd, (char *) &issue_date, sizeof(long)) - != sizeof(long)) + if (write(fd, (char *) &issue_date, sizeof(KRB4_32)) + != sizeof(KRB4_32)) goto bad; - + /* Alignment Record */ +#if (PLATFORM_BYTE_ORDER == AES_BIG_ENDIAN) + { + int null_bytes = 0; + if (0 == (issue_date & 0xff000000)) + ++null_bytes; + if (0 == (issue_date & 0x00ff0000)) + ++null_bytes; + if (0 == (issue_date & 0x0000ff00)) + ++null_bytes; + if (0 == (issue_date & 0x000000ff)) + ++null_bytes; + + switch(null_bytes) { + case 0: + /* Issue date */ + if (write(fd, (char *) &issue_date, sizeof(KRB4_32)) + != sizeof(KRB4_32)) + goto bad; + if (write(fd, align_rec_0, sizeof(align_rec_0)) + != sizeof(align_rec_0)) + goto bad; + break; + + case 1: + if (write(fd, (char *) &issue_date, sizeof(KRB4_32)) + != sizeof(KRB4_32)) + goto bad; + if (write(fd, align_rec_1, sizeof(align_rec_1)) + != sizeof(align_rec_1)) + goto bad; + break; + + case 3: + /* Three NULLS are troublesome but rare. We'll just pretend + * they don't exist by decrementing the issue_date. + */ + --issue_date; + case 2: + if (write(fd, (char *) &issue_date, sizeof(KRB4_32)) + != sizeof(KRB4_32)) + goto bad; + if (write(fd, align_rec_2, sizeof(align_rec_2)) + != sizeof(align_rec_2)) + goto bad; + break; + + default: + goto bad; + } + + } +#else + if (write(fd, align_rec, sizeof(align_rec)) != sizeof(align_rec)) + goto bad; +#endif + /* Actually, we should check each write for success */ return (KSUCCESS); bad: Index: krb5/src/lib/krb4/mac_stubs.c =================================================================== --- krb5/src/lib/krb4/mac_stubs.c (revision 2284) +++ krb5/src/lib/krb4/mac_stubs.c (working copy) @@ -277,7 +277,7 @@ int lifetime; /* Lifetime */ int kvno; /* Key version number */ KTEXT ticket; /* The ticket itself */ - long issue_date; /* The issue time */ + KRB4_32 issue_date; /* The issue time */ { short s; Index: krb5/src/lib/krb4/save_creds.c =================================================================== --- krb5/src/lib/krb4/save_creds.c (revision 2284) +++ krb5/src/lib/krb4/save_creds.c (working copy) @@ -54,7 +54,7 @@ int lifetime; /* Lifetime */ int kvno; /* Key version number */ KTEXT ticket; /* The ticket itself */ - long issue_date; /* The issue time */ + KRB4_32 issue_date; /* The issue time */ KRB_UINT32 local_addr; { int tf_status; /* return values of the tf_util calls */ @@ -79,7 +79,7 @@ int lifetime, int kvno, KTEXT ticket, - long issue_date) + KRB4_32 issue_date) { return krb4int_save_credentials_addr(service, instance, realm, session, lifetime, kvno,