1 /*
2  * lib/krb5/krb/chk_trans.c
3  *
4  * Copyright 2001 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  *
26  *
27  * krb5_check_transited_list()
28  */
29 #include "k5-int.h"
30 #include <stdarg.h>
31 
32 #if defined (TEST) || defined (TEST2)
33 # undef DEBUG
34 # define DEBUG
35 #endif
36 
37 #ifdef DEBUG
38 #define verbose krb5int_chk_trans_verbose
39 static int verbose = 0;
40 # define Tprintf(ARGS) if (verbose) printf ARGS
41 #else
42 # define Tprintf(ARGS) (void)(0)
43 #endif
44 
45 #define MAXLEN 512
46 
47 static krb5_error_code
process_intermediates(krb5_error_code (* fn)(krb5_data *,void *),void * data,const krb5_data * n1,const krb5_data * n2)48 process_intermediates (krb5_error_code (*fn)(krb5_data *, void *), void *data,
49 		       const krb5_data *n1, const krb5_data *n2) {
50     unsigned int len1, len2, i;
51     char *p1, *p2;
52 
53     Tprintf (("process_intermediates(%.*s,%.*s)\n",
54 	      (int) n1->length, n1->data, (int) n2->length, n2->data));
55 
56     len1 = n1->length;
57     len2 = n2->length;
58 
59     Tprintf (("(walking intermediates now)\n"));
60     /* Simplify...  */
61     if (len1 > len2) {
62 	const krb5_data *p;
63 	int tmp = len1;
64 	len1 = len2;
65 	len2 = tmp;
66 	p = n1;
67 	n1 = n2;
68 	n2 = p;
69     }
70     /* Okay, now len1 is always shorter or equal.  */
71     if (len1 == len2) {
72 	if (memcmp (n1->data, n2->data, len1)) {
73 	    Tprintf (("equal length but different strings in path: '%.*s' '%.*s'\n",
74 		      (int) n1->length, n1->data, (int) n2->length, n2->data));
75 	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
76 	}
77 	Tprintf (("(end intermediates)\n"));
78 	return 0;
79     }
80     /* Now len1 is always shorter.  */
81     if (len1 == 0)
82 	/* Shouldn't be possible.  Internal error?  */
83 	return KRB5KRB_AP_ERR_ILL_CR_TKT;
84     p1 = n1->data;
85     p2 = n2->data;
86     if (p1[0] == '/') {
87 	/* X.500 style names, with common prefix.  */
88 	if (p2[0] != '/') {
89 	    Tprintf (("mixed name formats in path: x500='%.*s' domain='%.*s'\n",
90 		      (int) len1, p1, (int) len2, p2));
91 	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
92 	}
93 	if (memcmp (p1, p2, len1)) {
94 	    Tprintf (("x500 names with different prefixes '%.*s' '%.*s'\n",
95 		      (int) len1, p1, (int) len2, p2));
96 	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
97 	}
98 	for (i = len1 + 1; i < len2; i++)
99 	    if (p2[i] == '/') {
100 		krb5_data d;
101 		krb5_error_code r;
102 
103 		d.data = p2;
104 		d.length = i;
105 		r = (*fn) (&d, data);
106 		if (r)
107 		    return r;
108 	    }
109     } else {
110 	/* Domain style names, with common suffix.  */
111 	if (p2[0] == '/') {
112 	    Tprintf (("mixed name formats in path: domain='%.*s' x500='%.*s'\n",
113 		      (int) len1, p1, (int) len2, p2));
114 	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
115 	}
116 	if (memcmp (p1, p2 + (len2 - len1), len1)) {
117 	    Tprintf (("domain names with different suffixes '%.*s' '%.*s'\n",
118 		      (int) len1, p1, (int) len2, p2));
119 	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
120 	}
121 	for (i = len2 - len1 - 1; i > 0; i--) {
122 	    Tprintf (("looking at '%.*s'\n", (int) (len2 - i), p2+i));
123 	    if (p2[i-1] == '.') {
124 		krb5_data d;
125 		krb5_error_code r;
126 
127 		d.data = p2+i;
128 		d.length = len2 - i;
129 		r = (*fn) (&d, data);
130 		if (r)
131 		    return r;
132 	    }
133 	}
134     }
135     Tprintf (("(end intermediates)\n"));
136     return 0;
137 }
138 
139 static krb5_error_code
maybe_join(krb5_data * last,krb5_data * buf,int bufsiz)140 maybe_join (krb5_data *last, krb5_data *buf, int bufsiz)
141 {
142     if (buf->length == 0)
143 	return 0;
144     if (buf->data[0] == '/') {
145 	if (last->length + buf->length > bufsiz) {
146 	    Tprintf (("too big: last=%d cur=%d max=%d\n", last->length, buf->length, bufsiz));
147 	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
148 	}
149 	memmove (buf->data+last->length, buf->data, buf->length);
150 	memcpy (buf->data, last->data, last->length);
151 	buf->length += last->length;
152     } else if (buf->data[buf->length-1] == '.') {
153 	/* We can ignore the case where the previous component was
154 	   empty; the strcat will be a no-op.  It should probably
155 	   be an error case, but let's be flexible.  */
156 	if (last->length+buf->length > bufsiz) {
157 	    Tprintf (("too big\n"));
158 	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
159 	}
160 	memcpy (buf->data + buf->length, last->data, last->length);
161 	buf->length += last->length;
162     }
163     /* Otherwise, do nothing.  */
164     return 0;
165 }
166 
167 /* The input strings cannot contain any \0 bytes, according to the
168    spec, but our API is such that they may not be \0 terminated
169    either.  Thus we keep on treating them as krb5_data objects instead
170    of C strings.  */
171 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)172 foreach_realm (krb5_error_code (*fn)(krb5_data *comp,void *data), void *data,
173 	       const krb5_data *crealm, const krb5_data *srealm,
174 	       const krb5_data *transit)
175 {
176     char buf[MAXLEN], last[MAXLEN];
177     char *p, *bufp;
178     int next_lit, intermediates, l;
179     krb5_data this_component;
180     krb5_error_code r;
181     krb5_data last_component;
182 
183     /* Invariants:
184        - last_component points to last[]
185        - this_component points to buf[]
186        - last_component has length of last
187        - this_component has length of buf when calling out
188        Keep these consistent, and we should be okay.  */
189 
190     next_lit = 0;
191     intermediates = 0;
192     memset (buf, 0, sizeof (buf));
193 
194     this_component.data = buf;
195     last_component.data = last;
196     last_component.length = 0;
197 
198 #define print_data(fmt,d) Tprintf((fmt,(int)(d)->length,(d)->data))
199     print_data ("client realm: %.*s\n", crealm);
200     print_data ("server realm: %.*s\n", srealm);
201     print_data ("transit enc.: %.*s\n", transit);
202 
203     if (transit->length == 0) {
204 	Tprintf (("no other realms transited\n"));
205 	return 0;
206     }
207 
208     bufp = buf;
209     for (p = transit->data, l = transit->length; l; p++, l--) {
210 	if (next_lit) {
211 	    *bufp++ = *p;
212 	    if (bufp == buf+sizeof(buf))
213 		return KRB5KRB_AP_ERR_ILL_CR_TKT;
214 	    next_lit = 0;
215 	} else if (*p == '\\') {
216 	    next_lit = 1;
217 	} else if (*p == ',') {
218 	    if (bufp != buf) {
219 		this_component.length = bufp - buf;
220 		r = maybe_join (&last_component, &this_component, sizeof(buf));
221 		if (r)
222 		    return r;
223 		r = (*fn) (&this_component, data);
224 		if (r)
225 		    return r;
226 		if (intermediates) {
227 		    if (p == transit->data)
228 			r = process_intermediates (fn, data,
229 						   &this_component, crealm);
230 		    else {
231 			r = process_intermediates (fn, data, &this_component,
232 						   &last_component);
233 		    }
234 		    if (r)
235 			return r;
236 		}
237 		intermediates = 0;
238 		memcpy (last, buf, sizeof (buf));
239 		last_component.length = this_component.length;
240 		memset (buf, 0, sizeof (buf));
241 		bufp = buf;
242 	    } else {
243 		intermediates = 1;
244 		if (p == transit->data) {
245 		    if (crealm->length >= MAXLEN)
246 			return KRB5KRB_AP_ERR_ILL_CR_TKT;
247 		    memcpy (last, crealm->data, crealm->length);
248 		    last[crealm->length] = '\0';
249 		    last_component.length = crealm->length;
250 		}
251 	    }
252 	} else if (*p == ' ' && bufp == buf) {
253 	    /* This next component stands alone, even if it has a
254 	       trailing dot or leading slash.  */
255 	    memset (last, 0, sizeof (last));
256 	    last_component.length = 0;
257 	} else {
258 	    /* Not a special character; literal.  */
259 	    *bufp++ = *p;
260 	    if (bufp == buf+sizeof(buf))
261 		return KRB5KRB_AP_ERR_ILL_CR_TKT;
262 	}
263     }
264     /* At end.  Must be normal state.  */
265     if (next_lit)
266 	Tprintf (("ending in next-char-literal state\n"));
267     /* Process trailing element or comma.  */
268     if (bufp == buf) {
269 	/* Trailing comma.  */
270 	r = process_intermediates (fn, data, &last_component, srealm);
271     } else {
272 	/* Trailing component.  */
273 	this_component.length = bufp - buf;
274 	r = maybe_join (&last_component, &this_component, sizeof(buf));
275 	if (r)
276 	    return r;
277 	r = (*fn) (&this_component, data);
278 	if (r)
279 	    return r;
280 	if (intermediates)
281 	    r = process_intermediates (fn, data, &this_component,
282 				       &last_component);
283     }
284     if (r != 0)
285 	return r;
286     return 0;
287 }
288 
289 
290 struct check_data {
291     krb5_context ctx;
292     krb5_principal *tgs;
293 };
294 
295 static int
same_data(krb5_data * d1,krb5_data * d2)296 same_data (krb5_data *d1, krb5_data *d2)
297 {
298     return (d1->length == d2->length
299 	    && !memcmp (d1->data, d2->data, d1->length));
300 }
301 
302 static krb5_error_code
check_realm_in_list(krb5_data * realm,void * data)303 check_realm_in_list (krb5_data *realm, void *data)
304 {
305     struct check_data *cdata = data;
306     int i;
307 
308     Tprintf ((".. checking '%.*s'\n", (int) realm->length, realm->data));
309     for (i = 0; cdata->tgs[i]; i++) {
310 	if (same_data (krb5_princ_realm (cdata->ctx, cdata->tgs[i]), realm))
311 	    return 0;
312     }
313     Tprintf (("BAD!\n"));
314     return KRB5KRB_AP_ERR_ILL_CR_TKT;
315 }
316 
317 krb5_error_code
krb5_check_transited_list(krb5_context ctx,const krb5_data * trans_in,const krb5_data * crealm,const krb5_data * srealm)318 krb5_check_transited_list (krb5_context ctx, const krb5_data *trans_in,
319 			   const krb5_data *crealm, const krb5_data *srealm)
320 {
321     krb5_data trans;
322     struct check_data cdata;
323     krb5_error_code r;
324 
325     trans.length = trans_in->length;
326     trans.data = (char *) trans_in->data;
327     if (trans.length && (trans.data[trans.length-1] == '\0'))
328 	trans.length--;
329 
330     Tprintf (("krb5_check_transited_list(trans=\"%.*s\", crealm=\"%.*s\", srealm=\"%.*s\")\n",
331 	      (int) trans.length, trans.data,
332 	      (int) crealm->length, crealm->data,
333 	      (int) srealm->length, srealm->data));
334     if (trans.length == 0)
335 	return 0;
336     r = krb5_walk_realm_tree (ctx, crealm, srealm, &cdata.tgs,
337 			      KRB5_REALM_BRANCH_CHAR);
338     if (r) {
339 	Tprintf (("error %ld\n", (long) r));
340 	return r;
341     }
342 #ifdef DEBUG /* avoid compiler warning about 'd' unused */
343     {
344 	int i;
345 	Tprintf (("tgs list = {\n"));
346 	for (i = 0; cdata.tgs[i]; i++) {
347 	    char *name;
348 	    r = krb5_unparse_name (ctx, cdata.tgs[i], &name);
349 	    Tprintf (("\t'%s'\n", name));
350 	    free (name);
351 	}
352 	Tprintf (("}\n"));
353     }
354 #endif
355     cdata.ctx = ctx;
356     r = foreach_realm (check_realm_in_list, &cdata, crealm, srealm, &trans);
357     krb5_free_realm_tree (ctx, cdata.tgs);
358     return r;
359 }
360 
361 #ifdef TEST
362 
363 static krb5_error_code
print_a_realm(krb5_data * realm,void * data)364 print_a_realm (krb5_data *realm, void *data)
365 {
366     printf ("%.*s\n", (int) realm->length, realm->data);
367     return 0;
368 }
369 
main(int argc,char * argv[])370 int main (int argc, char *argv[]) {
371     const char *me;
372     krb5_data crealm, srealm, transit;
373     krb5_error_code r;
374     int expand_only = 0;
375 
376     me = strrchr (argv[0], '/');
377     me = me ? me+1 : argv[0];
378 
379     while (argc > 3 && argv[1][0] == '-') {
380 	if (!strcmp ("-v", argv[1]))
381 	    verbose++, argc--, argv++;
382 	else if (!strcmp ("-x", argv[1]))
383 	    expand_only++, argc--, argv++;
384 	else
385 	    goto usage;
386     }
387 
388     if (argc != 4) {
389     usage:
390 	printf ("usage: %s [-v] [-x] clientRealm serverRealm transitEncoding\n",
391 		me);
392 	return 1;
393     }
394 
395     crealm.data = argv[1];
396     crealm.length = strlen(argv[1]);
397     srealm.data = argv[2];
398     srealm.length = strlen(argv[2]);
399     transit.data = argv[3];
400     transit.length = strlen(argv[3]);
401 
402     if (expand_only) {
403 
404 	printf ("client realm: %s\n", argv[1]);
405 	printf ("server realm: %s\n", argv[2]);
406 	printf ("transit enc.: %s\n", argv[3]);
407 
408 	if (argv[3][0] == 0) {
409 	    printf ("no other realms transited\n");
410 	    return 0;
411 	}
412 
413 	r = foreach_realm (print_a_realm, NULL, &crealm, &srealm, &transit);
414 	if (r)
415 	    printf ("--> returned error %ld\n", (long) r);
416 	return r != 0;
417 
418     } else {
419 
420 	/* Actually check the values against the supplied krb5.conf file.  */
421 	krb5_context ctx;
422 	r = krb5_init_context (&ctx);
423 	if (r) {
424 	    com_err (me, r, "initializing krb5 context");
425 	    return 1;
426 	}
427 	r = krb5_check_transited_list (ctx, &transit, &crealm, &srealm);
428 	if (r == KRB5KRB_AP_ERR_ILL_CR_TKT) {
429 	    printf ("NO\n");
430 	} else if (r == 0) {
431 	    printf ("YES\n");
432 	} else {
433 	    printf ("kablooey!\n");
434 	    com_err (me, r, "checking transited-realm list");
435 	    return 1;
436 	}
437 	return 0;
438     }
439 }
440 
441 #endif /* TEST */
442