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