xref: /freebsd/crypto/krb5/src/lib/krb5/krb/chk_trans.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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