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