#include "k5-int.h"
#include <stdarg.h>
#if defined (TEST) || defined (TEST2)
# undef DEBUG
# define DEBUG
#endif
#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",
(int) n1->length, n1->data, (int) n2->length, n2->data));
len1 = n1->length;
len2 = n2->length;
Tprintf (("(walking intermediates now)\n"));
if (len1 > len2) {
const krb5_data *p;
int tmp = len1;
len1 = len2;
len2 = tmp;
p = n1;
n1 = n2;
n2 = p;
}
if (len1 == len2) {
if (memcmp (n1->data, n2->data, len1)) {
Tprintf (("equal length but different strings in path: '%.*s' '%.*s'\n",
(int) n1->length, n1->data, (int) n2->length, n2->data));
return KRB5KRB_AP_ERR_ILL_CR_TKT;
}
Tprintf (("(end intermediates)\n"));
return 0;
}
if (len1 == 0)
return KRB5KRB_AP_ERR_ILL_CR_TKT;
p1 = n1->data;
p2 = n2->data;
if (p1[0] == '/') {
if (p2[0] != '/') {
Tprintf (("mixed name formats in path: x500='%.*s' domain='%.*s'\n",
(int) len1, p1, (int) len2, p2));
return KRB5KRB_AP_ERR_ILL_CR_TKT;
}
if (memcmp (p1, p2, len1)) {
Tprintf (("x500 names with different prefixes '%.*s' '%.*s'\n",
(int) len1, p1, (int) 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;
d.data = p2;
d.length = i;
r = (*fn) (&d, data);
if (r)
return r;
}
} else {
if (p2[0] == '/') {
Tprintf (("mixed name formats in path: domain='%.*s' x500='%.*s'\n",
(int) len1, p1, (int) len2, p2));
return KRB5KRB_AP_ERR_ILL_CR_TKT;
}
if (memcmp (p1, p2 + (len2 - len1), len1)) {
Tprintf (("domain names with different suffixes '%.*s' '%.*s'\n",
(int) len1, p1, (int) len2, p2));
return KRB5KRB_AP_ERR_ILL_CR_TKT;
}
for (i = len2 - len1 - 1; i > 0; i--) {
Tprintf (("looking at '%.*s'\n", (int) (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;
}
}
}
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] == '.') {
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;
}
return 0;
}
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, intermediates, l;
krb5_data this_component;
krb5_error_code r;
krb5_data last_component;
next_lit = 0;
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;
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;
}
}
} else if (*p == ' ' && bufp == buf) {
memset (last, 0, sizeof (last));
last_component.length = 0;
} else {
*bufp++ = *p;
if (bufp == buf+sizeof(buf))
return KRB5KRB_AP_ERR_ILL_CR_TKT;
}
}
if (next_lit)
Tprintf (("ending in next-char-literal state\n"));
if (bufp == buf) {
r = process_intermediates (fn, data, &last_component, srealm);
} else {
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, const krb5_data *trans_in,
const krb5_data *crealm, const krb5_data *srealm)
{
krb5_data trans;
struct check_data cdata;
krb5_error_code r;
trans.length = trans_in->length;
trans.data = (char *) trans_in->data;
if (trans.length && (trans.data[trans.length-1] == '\0'))
trans.length--;
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
{
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", (int) 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;
}
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 {
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