From 308665f3840b56b47e347dad9ab9400aa123e089 Mon Sep 17 00:00:00 2001 From: Ken Raeburn Date: Wed, 20 Jun 2001 04:07:43 +0000 Subject: New implementation of transited-realm checking, with some test cases. The test cases currently check only t-r list expansion, not the validation step. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@13397 dc483132-0cff-0310-8789-dd5450dbe970 --- src/lib/krb5/krb/ChangeLog | 11 + src/lib/krb5/krb/Makefile.in | 9 +- src/lib/krb5/krb/chk_trans.c | 482 ++++++++++++++++++++++++++++++++++------- src/lib/krb5/krb/t_krb5.conf | 17 ++ src/lib/krb5/krb/transit-tests | 54 +++++ 5 files changed, 494 insertions(+), 79 deletions(-) create mode 100644 src/lib/krb5/krb/transit-tests (limited to 'src') diff --git a/src/lib/krb5/krb/ChangeLog b/src/lib/krb5/krb/ChangeLog index 8f38c32..4c86bb1 100644 --- a/src/lib/krb5/krb/ChangeLog +++ b/src/lib/krb5/krb/ChangeLog @@ -1,3 +1,14 @@ +2001-06-19 Ken Raeburn + + * chk_trans.c: Reimplemented from scratch. + * transit-tests: New file. + * Makefile.in (t_expand, t_expand.o): New targets. Build test + program from chk_trans.c. + (T_EXPAND_OBJS): New variable. + (TEST_PROGS): Add t_expand. + (check-unix): Run transit-tests. + * t_krb5.conf: Added capaths section. + 2001-06-16 Ken Raeburn * fwd_tgt.c (krb5_fwd_tgt_creds): Copy enctype for new creds from diff --git a/src/lib/krb5/krb/Makefile.in b/src/lib/krb5/krb/Makefile.in index 6418178..c07bd47 100644 --- a/src/lib/krb5/krb/Makefile.in +++ b/src/lib/krb5/krb/Makefile.in @@ -304,7 +304,13 @@ t_ser: $(T_SER_OBJS) $(KDB5_DEPLIBS) $(KRB5_BASE_DEPLIBS) t_deltat : $(T_DELTAT_OBJS) $(CC_LINK) -o t_deltat $(T_DELTAT_OBJS) -TEST_PROGS= t_walk_rtree t_kerb t_ser t_deltat +T_EXPAND_OBJS=t_expand.o +t_expand.o : chk_trans.c + $(CC) $(ALL_CFLAGS) -c -DTEST -o t_expand.o $(srcdir)/chk_trans.c +t_expand : $(T_EXPAND_OBJS) $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o t_expand $(T_EXPAND_OBJS) $(KRB5_BASE_LIBS) + +TEST_PROGS= t_walk_rtree t_kerb t_ser t_deltat t_expand check-unix:: $(TEST_PROGS) KRB5_CONFIG=$(srcdir)/t_krb5.conf ; export KRB5_CONFIG ;\ @@ -334,6 +340,7 @@ check-unix:: $(TEST_PROGS) KRB5_CONFIG=$(srcdir)/t_krb5.conf ; export KRB5_CONFIG ;\ $(RUN_SETUP) ./t_ser ./t_deltat + sh $(srcdir)/transit-tests clean:: $(RM) $(OUTPRE)t_walk_rtree$(EXEEXT) $(OUTPRE)t_walk_rtree.$(OBJEXT) \ diff --git a/src/lib/krb5/krb/chk_trans.c b/src/lib/krb5/krb/chk_trans.c index 357c438..87a32c2 100644 --- a/src/lib/krb5/krb/chk_trans.c +++ b/src/lib/krb5/krb/chk_trans.c @@ -1,12 +1,14 @@ /* - * Copyright (c) 1994 CyberSAFE Corporation. - * All rights reserved. + * lib/krb5/krb/chk_trans.c + * + * Copyright 2001 by the Massachusetts Institute of Technology. + * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. - * + * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright @@ -14,98 +16,422 @@ * 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. Neither M.I.T., the Open Computing Security Group, nor - * CyberSAFE Corporation make any representations about the suitability of + * 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. + * + * + * krb5_check_transited_list() */ - #include "k5-int.h" -#include +#include -#define MAX_REALM_LN 500 +#if defined (TEST) || defined (TEST2) +# undef DEBUG +# define DEBUG +#endif -krb5_error_code -krb5_check_transited_list(context, trans, realm1, realm2) - krb5_context context; - krb5_data *trans; - krb5_data *realm1; - krb5_data *realm2; -{ - char prev[MAX_REALM_LN+1]; - char next[MAX_REALM_LN+1]; - char *nextp; - int i, j; - int trans_length; - krb5_error_code retval = 0; - krb5_principal *tgs_list; - - if (trans == NULL || trans->data == NULL || trans->length == 0) - return(0); - trans_length = trans->data[trans->length-1] ? - trans->length : trans->length - 1; - - for (i = 0; i < trans_length; i++) - if (trans->data[i] == '\0') { - /* Realms may not contain ASCII NUL character. */ - return(KRB5KRB_AP_ERR_ILL_CR_TKT); - } +#ifdef DEBUG +#define verbose krb5int_chk_trans_verbose +static int verbose = 0; +# define Tprintf(ARGS) if (verbose) printf ARGS +#else +# define Tprintf(ARGS) (void)(0) +#endif + +#define MAXLEN 512 + +static krb5_error_code +process_intermediates (krb5_error_code (*fn)(krb5_data *, void *), void *data, + const krb5_data *n1, const krb5_data *n2) { + unsigned int len1, len2, i; + char *p1, *p2; + + Tprintf (("process_intermediates(%.*s,%.*s)\n", + n1->length, n1->data, n2->length, n2->data)); - if ((retval = krb5_walk_realm_tree(context, realm1, realm2, &tgs_list, - KRB5_REALM_BRANCH_CHAR))) { - return(retval); + len1 = n1->length; + len2 = n2->length; + + Tprintf (("(walking intermediates now)\n")); + /* Simplify... */ + if (len1 > len2) { + const krb5_data *p; + int tmp = len1; + len1 = len2; + len2 = tmp; + p = n1; + n1 = n2; + n2 = p; + } + /* Okay, now len1 is always shorter or equal. */ + if (len1 == len2) { + if (memcmp (n1->data, n2->data, len1)) { + Tprintf (("equal length but different strings in path: '%.*s' '%.*s'\n", + n1->length, n1->data, n2->length, n2->data)); + return KRB5KRB_AP_ERR_ILL_CR_TKT; + } + Tprintf (("(end intermediates)\n")); + return 0; } + /* Now len1 is always shorter. */ + if (len1 == 0) + /* Shouldn't be possible. Internal error? */ + return KRB5KRB_AP_ERR_ILL_CR_TKT; + p1 = n1->data; + p2 = n2->data; + if (p1[0] == '/') { + /* X.500 style names, with common prefix. */ + if (p2[0] != '/') { + Tprintf (("mixed name formats in path: x500='%.*s' domain='%.*s'\n", + len1, p1, len2, p2)); + return KRB5KRB_AP_ERR_ILL_CR_TKT; + } + if (memcmp (p1, p2, len1)) { + Tprintf (("x500 names with different prefixes '%.*s' '%.*s'\n", + len1, p1, len2, p2)); + return KRB5KRB_AP_ERR_ILL_CR_TKT; + } + for (i = len1 + 1; i < len2; i++) + if (p2[i] == '/') { + krb5_data d; + krb5_error_code r; - memset(prev, 0, sizeof(prev)); - memset(next, 0, sizeof(next)), nextp = next; - for (i = 0; i < trans_length; i++) { - if (i < trans_length-1 && trans->data[i] == '\\') { - i++; - *nextp++ = trans->data[i]; - if (nextp - next >= sizeof(next)) { - retval = KRB5KRB_AP_ERR_ILL_CR_TKT; - goto finish; + d.data = p2; + d.length = i; + r = (*fn) (&d, data); + if (r) + return r; } - continue; + } else { + /* Domain style names, with common suffix. */ + if (p2[0] == '/') { + Tprintf (("mixed name formats in path: domain='%.*s' x500='%.*s'\n", + len1, p1, len2, p2)); + return KRB5KRB_AP_ERR_ILL_CR_TKT; } - if (i < trans_length && trans->data[i] != ',') { - *nextp++ = trans->data[i]; - if (nextp - next >= sizeof(next)) { - retval = KRB5KRB_AP_ERR_ILL_CR_TKT; - goto finish; + if (memcmp (p1, p2 + (len2 - len1), len1)) { + Tprintf (("domain names with different suffixes '%.*s' '%.*s'\n", + len1, p1, len2, p2)); + return KRB5KRB_AP_ERR_ILL_CR_TKT; + } + for (i = len2 - len1 - 1; i > 0; i--) { + Tprintf (("looking at '%.*s'\n", len2 - i, p2+i)); + if (p2[i-1] == '.') { + krb5_data d; + krb5_error_code r; + + d.data = p2+i; + d.length = len2 - i; + r = (*fn) (&d, data); + if (r) + return r; } - continue; } - next[sizeof(next) - 1] = '\0'; - if (strlen(next) > 0) { - if (next[0] != '/') { - if (*(nextp-1) == '.' && strlen(next) + strlen(prev) <= MAX_REALM_LN) - strncat(next, prev, sizeof(next) - 1 - strlen(next)); - retval = KRB5KRB_AP_ERR_ILL_CR_TKT; - for (j = 0; tgs_list[j]; j++) { - if (strlen(next) == (size_t) krb5_princ_realm(context, tgs_list[j])->length && - !memcmp(next, krb5_princ_realm(context, tgs_list[j])->data, - strlen(next))) { - retval = 0; - break; + } + Tprintf (("(end intermediates)\n")); + return 0; +} + +static krb5_error_code +maybe_join (krb5_data *last, krb5_data *buf, int bufsiz) +{ + if (buf->length == 0) + return 0; + if (buf->data[0] == '/') { + if (last->length + buf->length > bufsiz) { + Tprintf (("too big: last=%d cur=%d max=%d\n", last->length, buf->length, bufsiz)); + return KRB5KRB_AP_ERR_ILL_CR_TKT; + } + memmove (buf->data+last->length, buf->data, buf->length); + memcpy (buf->data, last->data, last->length); + buf->length += last->length; + } else if (buf->data[buf->length-1] == '.') { + /* We can ignore the case where the previous component was + empty; the strcat will be a no-op. It should probably + be an error case, but let's be flexible. */ + if (last->length+buf->length > bufsiz) { + Tprintf (("too big\n")); + return KRB5KRB_AP_ERR_ILL_CR_TKT; + } + memcpy (buf->data + buf->length, last->data, last->length); + buf->length += last->length; + } + /* Otherwise, do nothing. */ + return 0; +} + +/* The input strings cannot contain any \0 bytes, according to the + spec, but our API is such that they may not be \0 terminated + either. Thus we keep on treating them as krb5_data objects instead + of C strings. */ +static krb5_error_code +foreach_realm (krb5_error_code (*fn)(krb5_data *comp,void *data), void *data, + const krb5_data *crealm, const krb5_data *srealm, + const krb5_data *transit) +{ + char buf[MAXLEN], last[MAXLEN]; + char *p, *bufp; + int next_lit, have_prev, intermediates, l; + krb5_data this_component; + krb5_error_code r; + krb5_data last_component; + + /* Invariants: + - last_component points to last[] + - this_component points to buf[] + - last_component has length of last + - this_component has length of buf when calling out + Keep these consistent, and we should be okay. */ + + next_lit = 0; + have_prev = 1; + intermediates = 0; + memset (buf, 0, sizeof (buf)); + + this_component.data = buf; + last_component.data = last; + last_component.length = 0; + +#define print_data(fmt,d) Tprintf((fmt,(int)(d)->length,(d)->data)) + print_data ("client realm: %.*s\n", crealm); + print_data ("server realm: %.*s\n", srealm); + print_data ("transit enc.: %.*s\n", transit); + + if (transit->length == 0) { + Tprintf (("no other realms transited\n")); + return 0; + } + + bufp = buf; + for (p = transit->data, l = transit->length; l; p++, l--) { + if (next_lit) { + *bufp++ = *p; + if (bufp == buf+sizeof(buf)) + return KRB5KRB_AP_ERR_ILL_CR_TKT; + next_lit = 0; + } else if (*p == '\\') { + next_lit = 1; + } else if (*p == ',') { + if (bufp != buf) { + this_component.length = bufp - buf; + r = maybe_join (&last_component, &this_component, sizeof(buf)); + if (r) + return r; + r = (*fn) (&this_component, data); + if (r) + return r; + if (intermediates) { + if (p == transit->data) + r = process_intermediates (fn, data, + &this_component, crealm); + else { + r = process_intermediates (fn, data, &this_component, + &last_component); } + if (r) + return r; + } + intermediates = 0; + have_prev = 1; + memcpy (last, buf, sizeof (buf)); + last_component.length = this_component.length; + memset (buf, 0, sizeof (buf)); + bufp = buf; + } else { + intermediates = 1; + if (p == transit->data) { + if (crealm->length >= MAXLEN) + return KRB5KRB_AP_ERR_ILL_CR_TKT; + memcpy (last, crealm->data, crealm->length); + last[crealm->length] = '\0'; + last_component.length = crealm->length; } - if (retval) goto finish; - } - if (i+1 < trans_length && trans->data[i+1] == ' ') { - i++; - memset(next, 0, sizeof(next)), nextp = next; - continue; - } - if (i+1 < trans_length && trans->data[i+1] != '/') { - strncpy(prev, next, sizeof(prev) - 1); - memset(next, 0, sizeof(next)), nextp = next; - continue; } + } else if (*p == ' ' && bufp == buf) { + /* This next component stands alone, even if it has a + trailing dot or leading slash. */ + memset (last, 0, sizeof (last)); + last_component.length = 0; + } else { + /* Not a special character; literal. */ + *bufp++ = *p; + if (bufp == buf+sizeof(buf)) + return KRB5KRB_AP_ERR_ILL_CR_TKT; + } + } + /* At end. Must be normal state. */ + if (next_lit) + Tprintf (("ending in next-char-literal state\n")); + /* Process trailing element or comma. */ + if (bufp == buf) { + /* Trailing comma. */ + r = process_intermediates (fn, data, &last_component, srealm); + } else { + /* Trailing component. */ + this_component.length = bufp - buf; + r = maybe_join (&last_component, &this_component, sizeof(buf)); + if (r) + return r; + r = (*fn) (&this_component, data); + if (r) + return r; + if (intermediates) + r = process_intermediates (fn, data, &this_component, + &last_component); + } + if (r != 0) + return r; + return 0; +} + + +struct check_data { + krb5_context ctx; + krb5_principal *tgs; +}; + +static int +same_data (krb5_data *d1, krb5_data *d2) +{ + return (d1->length == d2->length + && !memcmp (d1->data, d2->data, d1->length)); +} + +static krb5_error_code +check_realm_in_list (krb5_data *realm, void *data) +{ + struct check_data *cdata = data; + int i; + + Tprintf ((".. checking '%.*s'\n", (int) realm->length, realm->data)); + for (i = 0; cdata->tgs[i]; i++) { + if (same_data (krb5_princ_realm (cdata->ctx, cdata->tgs[i]), realm)) + return 0; + } + Tprintf (("BAD!\n")); + return KRB5KRB_AP_ERR_ILL_CR_TKT; +} + +krb5_error_code +krb5_check_transited_list (krb5_context ctx, krb5_data *trans, + krb5_data *crealm, krb5_data *srealm) +{ + struct check_data cdata; + krb5_error_code r; + + Tprintf (("krb5_check_transited_list(trans=\"%.*s\", crealm=\"%.*s\", srealm=\"%.*s\")\n", + (int) trans->length, trans->data, + (int) crealm->length, crealm->data, + (int) srealm->length, srealm->data)); + if (trans->length == 0) + return 0; + r = krb5_walk_realm_tree (ctx, crealm, srealm, &cdata.tgs, + KRB5_REALM_BRANCH_CHAR); + if (r) { + Tprintf (("error %ld\n", (long) r)); + return r; + } +#ifdef DEBUG /* avoid compiler warning about 'd' unused */ + { + int i; + Tprintf (("tgs list = {\n")); + for (i = 0; cdata.tgs[i]; i++) { + char *name; + r = krb5_unparse_name (ctx, cdata.tgs[i], &name); + Tprintf (("\t'%s'\n", name)); + free (name); } + Tprintf (("}\n")); + } +#endif + cdata.ctx = ctx; + r = foreach_realm (check_realm_in_list, &cdata, crealm, srealm, trans); + krb5_free_realm_tree (ctx, cdata.tgs); + return r; +} + +#ifdef TEST + +static krb5_error_code +print_a_realm (krb5_data *realm, void *data) +{ + printf ("%.*s\n", realm->length, realm->data); + return 0; +} + +int main (int argc, char *argv[]) { + const char *me; + krb5_data crealm, srealm, transit; + krb5_error_code r; + int expand_only = 0; + + me = strrchr (argv[0], '/'); + me = me ? me+1 : argv[0]; + + while (argc > 3 && argv[1][0] == '-') { + if (!strcmp ("-v", argv[1])) + verbose++, argc--, argv++; + else if (!strcmp ("-x", argv[1])) + expand_only++, argc--, argv++; + else + goto usage; + } + + if (argc != 4) { + usage: + printf ("usage: %s [-v] [-x] clientRealm serverRealm transitEncoding\n", + me); + return 1; } -finish: - krb5_free_realm_tree(context, tgs_list); - return(retval); + crealm.data = argv[1]; + crealm.length = strlen(argv[1]); + srealm.data = argv[2]; + srealm.length = strlen(argv[2]); + transit.data = argv[3]; + transit.length = strlen(argv[3]); + + if (expand_only) { + + printf ("client realm: %s\n", argv[1]); + printf ("server realm: %s\n", argv[2]); + printf ("transit enc.: %s\n", argv[3]); + + if (argv[3][0] == 0) { + printf ("no other realms transited\n"); + return 0; + } + + r = foreach_realm (print_a_realm, NULL, &crealm, &srealm, &transit); + if (r) + printf ("--> returned error %ld\n", (long) r); + return r != 0; + + } else { + + /* Actually check the values against the supplied krb5.conf file. */ + krb5_context ctx; + r = krb5_init_context (&ctx); + if (r) { + com_err (me, r, "initializing krb5 context"); + return 1; + } + r = krb5_check_transited_list (ctx, &transit, &crealm, &srealm); + if (r == KRB5KRB_AP_ERR_ILL_CR_TKT) { + printf ("NO\n"); + } else if (r == 0) { + printf ("YES\n"); + } else { + printf ("kablooey!\n"); + com_err (me, r, "checking transited-realm list"); + return 1; + } + return 0; + } } + +#endif /* TEST */ diff --git a/src/lib/krb5/krb/t_krb5.conf b/src/lib/krb5/krb/t_krb5.conf index 8d7a4d9..b25b1d3 100644 --- a/src/lib/krb5/krb/t_krb5.conf +++ b/src/lib/krb5/krb/t_krb5.conf @@ -26,6 +26,23 @@ v4_realm = SOME-REALLY-LONG-REALM-NAME-V4-CANNOT-HANDLE.COM } +[capaths] + /COM/HP/APOLLO/FOO = { + /COM/DEC/CRL = /COM/DEC + /COM/DEC/CRL = /COM + /COM/DEC/CRL = /COM/HP + /COM/DEC/CRL = /COM/HP/APOLLO + } + ATHENA.MIT.EDU = { + KERBEROS.COM = . + } + LCS.MIT.EDU = { + KERBEROS.COM = ATHENA.MIT.EDU + ATHENA.MIT.EDU = . + KABLOOEY.KERBEROS.COM = ATHENA.MIT.EDU + KABLOOEY.KERBEROS.COM = KERBEROS.COM + } + [domain_realm] .mit.edu = ATHENA.MIT.EDU mit.edu = ATHENA.MIT.EDU diff --git a/src/lib/krb5/krb/transit-tests b/src/lib/krb5/krb/transit-tests new file mode 100644 index 0000000..8ffb9c3 --- /dev/null +++ b/src/lib/krb5/krb/transit-tests @@ -0,0 +1,54 @@ +#!/bin/sh + +# Test the chk_trans.c code. +# BUG: Currently only tests expansion, not validation. + +trap "echo Failed. ; exit 1" 0 + +check='echo Running test "($1) ($2) ($3)" ... ; ./t_expand -x >tmpout1 "$@" || exit 1 ; grep -v : tmpout2 && sort tmpout1 && echo Got: `cat tmpout1` && (for i in $expected ; do echo $i ; done) | sort > tmpout2 && echo Exp: `cat tmpout2` && echo "" && cmp >/dev/null 2>&1 tmpout1 tmpout2 || exit 1' +checkerror='echo Running test "($1) ($2) ($3)", expecting error ... ; if ./t_expand -x >tmpout1 "$@" ; then echo Error was expected, but not reported. ; exit 1; else echo Expected error found. ; echo ""; fi' + +# +# Note: Expected realm expansion order is not important; program output +# and expected values will each be sorted before comparison. +# + +set ATHENA.MIT.EDU HACK.FOOBAR.COM ,EDU,BLORT.COM,COM, +expected="MIT.EDU EDU BLORT.COM COM FOOBAR.COM" +eval $check + +set ATHENA.MIT.EDU EDU , +expected="MIT.EDU" +eval $check + +set EDU ATHENA.MIT.EDU , +expected="MIT.EDU" +eval $check + +set x x "/COM,/HP,/APOLLO, /COM/DEC" +expected="/COM /COM/HP /COM/HP/APOLLO /COM/DEC" +eval $check + +set x x EDU,MIT.,ATHENA.,WASHINGTON.EDU,CS. +expected="EDU MIT.EDU ATHENA.MIT.EDU WASHINGTON.EDU CS.WASHINGTON.EDU" +eval $check + +set ATHENA.MIT.EDU /COM/HP/APOLLO ,EDU,/COM, +eval $checkerror + +set ATHENA.MIT.EDU /COM/HP/APOLLO ",EDU, /COM," +expected="EDU MIT.EDU /COM /COM/HP" +eval $check + +set ATHENA.MIT.EDU CS.CMU.EDU ,EDU, +expected="EDU MIT.EDU CMU.EDU" +eval $check + +set XYZZY.ATHENA.MIT.EDU XYZZY.CS.CMU.EDU ,EDU, +expected="EDU MIT.EDU ATHENA.MIT.EDU CMU.EDU CS.CMU.EDU" +eval $check + +rm tmpout1 tmpout2 +trap "" 0 +echo Success. +exit 0 -- cgit v1.1