1 #pragma ident	"%Z%%M%	%I%	%E% SMI"
2 /*
3  * lib/krb5/krb/chk_trans.c
4  *
5  * Copyright 2001 by the Massachusetts Institute of Technology.
6  * All Rights Reserved.
7  *
8  * Export of this software from the United States of America may
9  *   require a specific license from the United States Government.
10  *   It is the responsibility of any person or organization contemplating
11  *   export to obtain such a license before exporting.
12  *
13  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14  * distribute this software and its documentation for any purpose and
15  * without fee is hereby granted, provided that the above copyright
16  * notice appear in all copies and that both that copyright notice and
17  * this permission notice appear in supporting documentation, and that
18  * the name of M.I.T. not be used in advertising or publicity pertaining
19  * to distribution of the software without specific, written prior
20  * permission.  Furthermore if you modify this software you must label
21  * your software as modified software and not distribute it in such a
22  * fashion that it might be confused with the original M.I.T. software.
23  * M.I.T. makes no representations about the suitability of
24  * this software for any purpose.  It is provided "as is" without express
25  * or implied warranty.
26  *
27  *
28  * krb5_check_transited_list()
29  */
30 #include <k5-int.h>
31 #include <stdarg.h>
32 
33 #define MAXLEN 512
34 
35 static krb5_error_code
36 process_intermediates (krb5_error_code (*fn)(krb5_data *, void *), void *data,
37 		       const krb5_data *n1, const krb5_data *n2) {
38     unsigned int len1, len2, i;
39     char *p1, *p2;
40 
41     len1 = n1->length;
42     len2 = n2->length;
43 
44     /* Simplify...  */
45     if (len1 > len2) {
46 	const krb5_data *p;
47 	int tmp = len1;
48 	len1 = len2;
49 	len2 = tmp;
50 	p = n1;
51 	n1 = n2;
52 	n2 = p;
53     }
54     /* Okay, now len1 is always shorter or equal.  */
55     if (len1 == len2) {
56 	if (memcmp (n1->data, n2->data, len1)) {
57 	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
58 	}
59 	return 0;
60     }
61     /* Now len1 is always shorter.  */
62     if (len1 == 0)
63 	/* Shouldn't be possible.  Internal error?  */
64 	return KRB5KRB_AP_ERR_ILL_CR_TKT;
65     p1 = n1->data;
66     p2 = n2->data;
67     if (p1[0] == '/') {
68 	/* X.500 style names, with common prefix.  */
69 	if (p2[0] != '/') {
70 	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
71 	}
72 	if (memcmp (p1, p2, len1)) {
73 	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
74 	}
75 	for (i = len1 + 1; i < len2; i++)
76 	    if (p2[i] == '/') {
77 		krb5_data d;
78 		krb5_error_code r;
79 
80 		d.data = p2;
81 		d.length = i;
82 		r = (*fn) (&d, data);
83 		if (r)
84 		    return r;
85 	    }
86     } else {
87 	/* Domain style names, with common suffix.  */
88 	if (p2[0] == '/') {
89 	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
90 	}
91 	if (memcmp (p1, p2 + (len2 - len1), len1)) {
92 	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
93 	}
94 	for (i = len2 - len1 - 1; i > 0; i--) {
95 	    if (p2[i-1] == '.') {
96 		krb5_data d;
97 		krb5_error_code r;
98 
99 		d.data = p2+i;
100 		d.length = len2 - i;
101 		r = (*fn) (&d, data);
102 		if (r)
103 		    return r;
104 	    }
105 	}
106     }
107     return 0;
108 }
109 
110 static krb5_error_code
111 maybe_join (krb5_data *last, krb5_data *buf, int bufsiz)
112 {
113     if (buf->length == 0)
114 	return 0;
115     if (buf->data[0] == '/') {
116 	if (last->length + buf->length > bufsiz) {
117 	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
118 	}
119 	memmove (buf->data+last->length, buf->data, buf->length);
120 	memcpy (buf->data, last->data, last->length);
121 	buf->length += last->length;
122     } else if (buf->data[buf->length-1] == '.') {
123 	/* We can ignore the case where the previous component was
124 	   empty; the strcat will be a no-op.  It should probably
125 	   be an error case, but let's be flexible.  */
126 	if (last->length+buf->length > bufsiz) {
127 	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
128 	}
129 	memcpy (buf->data + buf->length, last->data, last->length);
130 	buf->length += last->length;
131     }
132     /* Otherwise, do nothing.  */
133     return 0;
134 }
135 
136 /* The input strings cannot contain any \0 bytes, according to the
137    spec, but our API is such that they may not be \0 terminated
138    either.  Thus we keep on treating them as krb5_data objects instead
139    of C strings.  */
140 static krb5_error_code
141 foreach_realm (krb5_error_code (*fn)(krb5_data *comp,void *data), void *data,
142 	       const krb5_data *crealm, const krb5_data *srealm,
143 	       const krb5_data *transit)
144 {
145     char buf[MAXLEN], last[MAXLEN];
146     char *p, *bufp;
147     int next_lit, intermediates, l;
148     krb5_data this_component;
149     krb5_error_code r;
150     krb5_data last_component;
151 
152     /* Invariants:
153        - last_component points to last[]
154        - this_component points to buf[]
155        - last_component has length of last
156        - this_component has length of buf when calling out
157        Keep these consistent, and we should be okay.  */
158 
159     next_lit = 0;
160     intermediates = 0;
161     memset (buf, 0, sizeof (buf));
162 
163     this_component.data = buf;
164     last_component.data = last;
165     last_component.length = 0;
166 
167     if (transit->length == 0) {
168 	return 0;
169     }
170 
171     bufp = buf;
172     for (p = transit->data, l = transit->length; l; p++, l--) {
173 	if (next_lit) {
174 	    *bufp++ = *p;
175 	    if (bufp == buf+sizeof(buf))
176 		return KRB5KRB_AP_ERR_ILL_CR_TKT;
177 	    next_lit = 0;
178 	} else if (*p == '\\') {
179 	    next_lit = 1;
180 	} else if (*p == ',') {
181 	    if (bufp != buf) {
182 		this_component.length = bufp - buf;
183 		r = maybe_join (&last_component, &this_component, sizeof(buf));
184 		if (r)
185 		    return r;
186 		r = (*fn) (&this_component, data);
187 		if (r)
188 		    return r;
189 		if (intermediates) {
190 		    if (p == transit->data)
191 			r = process_intermediates (fn, data,
192 						   &this_component, crealm);
193 		    else {
194 			r = process_intermediates (fn, data, &this_component,
195 						   &last_component);
196 		    }
197 		    if (r)
198 			return r;
199 		}
200 		intermediates = 0;
201 		memcpy (last, buf, sizeof (buf));
202 		last_component.length = this_component.length;
203 		memset (buf, 0, sizeof (buf));
204 		bufp = buf;
205 	    } else {
206 		intermediates = 1;
207 		if (p == transit->data) {
208 		    if (crealm->length >= MAXLEN)
209 			return KRB5KRB_AP_ERR_ILL_CR_TKT;
210 		    memcpy (last, crealm->data, crealm->length);
211 		    last[crealm->length] = '\0';
212 		    last_component.length = crealm->length;
213 		}
214 	    }
215 	} else if (*p == ' ' && bufp == buf) {
216 	    /* This next component stands alone, even if it has a
217 	       trailing dot or leading slash.  */
218 	    memset (last, 0, sizeof (last));
219 	    last_component.length = 0;
220 	} else {
221 	    /* Not a special character; literal.  */
222 	    *bufp++ = *p;
223 	    if (bufp == buf+sizeof(buf))
224 		return KRB5KRB_AP_ERR_ILL_CR_TKT;
225 	}
226     }
227     /* At end.  Must be normal state.  */
228     /* Process trailing element or comma.  */
229     if (bufp == buf) {
230 	/* Trailing comma.  */
231 	r = process_intermediates (fn, data, &last_component, srealm);
232     } else {
233 	/* Trailing component.  */
234 	this_component.length = bufp - buf;
235 	r = maybe_join (&last_component, &this_component, sizeof(buf));
236 	if (r)
237 	    return r;
238 	r = (*fn) (&this_component, data);
239 	if (r)
240 	    return r;
241 	if (intermediates)
242 	    r = process_intermediates (fn, data, &this_component,
243 				       &last_component);
244     }
245     if (r != 0)
246 	return r;
247     return 0;
248 }
249 
250 
251 struct check_data {
252     krb5_context ctx;
253     krb5_principal *tgs;
254 };
255 
256 static int
257 same_data (krb5_data *d1, krb5_data *d2)
258 {
259     return (d1->length == d2->length
260 	    && !memcmp (d1->data, d2->data, d1->length));
261 }
262 
263 static krb5_error_code
264 check_realm_in_list (krb5_data *realm, void *data)
265 {
266     struct check_data *cdata = data;
267     int i;
268 
269     for (i = 0; cdata->tgs[i]; i++) {
270 	if (same_data (krb5_princ_realm (cdata->ctx, cdata->tgs[i]), realm))
271 	    return 0;
272     }
273     return KRB5KRB_AP_ERR_ILL_CR_TKT;
274 }
275 
276 krb5_error_code
277 krb5_check_transited_list (krb5_context ctx, krb5_data *trans_in,
278 			   const krb5_data *crealm, const krb5_data *srealm)
279 {
280     krb5_data trans;
281     struct check_data cdata;
282     krb5_error_code r;
283 
284     /*
285      * Work around buggy implementations that include NULL terminator in length.
286      */
287     trans.length = trans_in->length;
288     trans.data = (char *) trans_in->data;
289     if (trans.length && (trans.data[trans.length-1] == '\0'))
290 	trans.length--;
291 
292     if (trans.length == 0)
293 	return 0;
294 
295     r = krb5_walk_realm_tree (ctx, crealm, srealm, &cdata.tgs,
296 			      KRB5_REALM_BRANCH_CHAR);
297     if (r) {
298 	return r;
299     }
300 #ifdef DEBUG /* avoid compiler warning about 'd' unused */
301     {
302 	int i;
303 	for (i = 0; cdata.tgs[i]; i++) {
304 	    char *name;
305 	    r = krb5_unparse_name (ctx, cdata.tgs[i], &name);
306 	    free (name);
307 	}
308     }
309 #endif
310     cdata.ctx = ctx;
311     r = foreach_realm (check_realm_in_list, &cdata, crealm, srealm, &trans);
312     krb5_free_realm_tree (ctx, cdata.tgs);
313     return r;
314 }
315