xref: /freebsd/crypto/heimdal/lib/krb5/transited.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
1 /*
2  * Copyright (c) 1997 - 2000 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include "krb5_locl.h"
35 
36 RCSID("$Id: transited.c,v 1.7 2000/02/07 13:30:41 joda Exp $");
37 
38 /* this is an attempt at one of the most horrible `compression'
39    schemes that has ever been invented; it's so amazingly brain-dead
40    that words can not describe it, and all this just to save a few
41    silly bytes */
42 
43 struct tr_realm {
44     char *realm;
45     unsigned leading_space:1;
46     unsigned leading_slash:1;
47     unsigned trailing_dot:1;
48     struct tr_realm *next;
49 };
50 
51 static void
52 free_realms(struct tr_realm *r)
53 {
54     struct tr_realm *p;
55     while(r){
56 	p = r;
57 	r = r->next;
58 	free(p->realm);
59 	free(p);
60     }
61 }
62 
63 static int
64 make_path(struct tr_realm *r, const char *from, const char *to)
65 {
66     const char *p;
67     struct tr_realm *path = r->next;
68     struct tr_realm *tmp;
69 
70     if(strlen(from) < strlen(to)){
71 	const char *tmp;
72 	tmp = from;
73 	from = to;
74 	to = tmp;
75     }
76 
77     if(strcmp(from + strlen(from) - strlen(to), to) == 0){
78 	p = from;
79 	while(1){
80 	    p = strchr(p, '.');
81 	    if(p == NULL)
82 		return KRB5KDC_ERR_POLICY;
83 	    p++;
84 	    if(strcmp(p, to) == 0)
85 		break;
86 	    tmp = calloc(1, sizeof(*tmp));
87 	    tmp->next = path;
88 	    path = tmp;
89 	    path->realm = strdup(p);
90 	    if(path->realm == NULL){
91 		r->next = path; /* XXX */
92 		return ENOMEM;;
93 	    }
94 	}
95     }else if(strncmp(from, to, strlen(to)) == 0){
96 	p = from + strlen(from);
97 	while(1){
98 	    while(p >= from && *p != '/') p--;
99 	    if(p == from)
100 		return KRB5KDC_ERR_POLICY;
101 	    if(strncmp(to, from, p - from) == 0)
102 		break;
103 	    tmp = calloc(1, sizeof(*tmp));
104 	    tmp->next = path;
105 	    path = tmp;
106 	    path->realm = malloc(p - from + 1);
107 	    if(path->realm == NULL){
108 		r->next = path; /* XXX */
109 		return ENOMEM;
110 	    }
111 	    memcpy(path->realm, from, p - from);
112 	    path->realm[p - from] = '\0';
113 	    p--;
114 	}
115     }else
116 	return KRB5KDC_ERR_POLICY;
117     r->next = path;
118 
119     return 0;
120 }
121 
122 static int
123 make_paths(struct tr_realm *realms, const char *client_realm,
124 	   const char *server_realm)
125 {
126     struct tr_realm *r;
127     int ret;
128     const char *prev_realm = client_realm;
129     const char *next_realm = NULL;
130     for(r = realms; r; r = r->next){
131 	/* it *might* be that you can have more than one empty
132 	   component in a row, at least that's how I interpret the
133 	   "," exception in 1510 */
134 	if(r->realm[0] == '\0'){
135 	    while(r->next && r->next->realm[0] == '\0')
136 		r = r->next;
137 	    if(r->next)
138 		next_realm = r->next->realm;
139 	    else
140 		next_realm = server_realm;
141 	    ret = make_path(r, prev_realm, next_realm);
142 	    if(ret){
143 		free_realms(realms);
144 		return ret;
145 	    }
146 	}
147 	prev_realm = r->realm;
148     }
149     return 0;
150 }
151 
152 static int
153 expand_realms(struct tr_realm *realms, const char *client_realm)
154 {
155     struct tr_realm *r;
156     const char *prev_realm = NULL;
157     for(r = realms; r; r = r->next){
158 	if(r->trailing_dot){
159 	    char *tmp;
160 	    if(prev_realm == NULL)
161 		prev_realm = client_realm;
162 	    tmp = realloc(r->realm, strlen(r->realm) + strlen(prev_realm) + 1);
163 	    if(tmp == NULL){
164 		free_realms(realms);
165 		return ENOMEM;
166 	    }
167 	    r->realm = tmp;
168 	    strcat(r->realm, prev_realm);
169 	}else if(r->leading_slash && !r->leading_space && prev_realm){
170 	    /* yet another exception: if you use x500-names, the
171                leading realm doesn't have to be "quoted" with a space */
172 	    char *tmp;
173 	    tmp = malloc(strlen(r->realm) + strlen(prev_realm) + 1);
174 	    if(tmp == NULL){
175 		free_realms(realms);
176 		return ENOMEM;
177 	    }
178 	    strcpy(tmp, prev_realm);
179 	    strcat(tmp, r->realm);
180 	    free(r->realm);
181 	    r->realm = tmp;
182 	}
183 	prev_realm = r->realm;
184     }
185     return 0;
186 }
187 
188 static struct tr_realm *
189 make_realm(char *realm)
190 {
191     struct tr_realm *r;
192     char *p, *q;
193     int quote = 0;
194     r = calloc(1, sizeof(*r));
195     if(r == NULL){
196 	free(realm);
197 	return NULL;
198     }
199     r->realm = realm;
200     for(p = q = r->realm; *p; p++){
201 	if(p == r->realm && *p == ' '){
202 	    r->leading_space = 1;
203 	    continue;
204 	}
205 	if(q == r->realm && *p == '/')
206 	    r->leading_slash = 1;
207 	if(quote){
208 	    *q++ = *p;
209 	    quote = 0;
210 	    continue;
211 	}
212 	if(*p == '\\'){
213 	    quote = 1;
214 	    continue;
215 	}
216 	if(p[0] == '.' && p[1] == '\0')
217 	    r->trailing_dot = 1;
218 	*q++ = *p;
219     }
220     *q = '\0';
221     return r;
222 }
223 
224 static struct tr_realm*
225 append_realm(struct tr_realm *head, struct tr_realm *r)
226 {
227     struct tr_realm *p;
228     if(head == NULL){
229 	r->next = NULL;
230 	return r;
231     }
232     p = head;
233     while(p->next) p = p->next;
234     p->next = r;
235     return head;
236 }
237 
238 static int
239 decode_realms(const char *tr, int length, struct tr_realm **realms)
240 {
241     struct tr_realm *r = NULL;
242 
243     char *tmp;
244     int quote = 0;
245     const char *start = tr;
246     int i;
247 
248     for(i = 0; i < length; i++){
249 	if(quote){
250 	    quote = 0;
251 	    continue;
252 	}
253 	if(tr[i] == '\\'){
254 	    quote = 1;
255 	    continue;
256 	}
257 	if(tr[i] == ','){
258 	    tmp = malloc(tr + i - start + 1);
259 	    memcpy(tmp, start, tr + i - start);
260 	    tmp[tr + i - start] = '\0';
261 	    r = make_realm(tmp);
262 	    if(r == NULL){
263 		free_realms(*realms);
264 		return ENOMEM;
265 	    }
266 	    *realms = append_realm(*realms, r);
267 	    start = tr + i + 1;
268 	}
269     }
270     tmp = malloc(tr + i - start + 1);
271     memcpy(tmp, start, tr + i - start);
272     tmp[tr + i - start] = '\0';
273     r = make_realm(tmp);
274     if(r == NULL){
275 	free_realms(*realms);
276 	return ENOMEM;
277     }
278     *realms = append_realm(*realms, r);
279 
280     return 0;
281 }
282 
283 
284 krb5_error_code
285 krb5_domain_x500_decode(krb5_data tr, char ***realms, int *num_realms,
286 			const char *client_realm, const char *server_realm)
287 {
288     struct tr_realm *r = NULL;
289     struct tr_realm *p, **q;
290     int ret;
291 
292     /* split string in components */
293     ret = decode_realms(tr.data, tr.length, &r);
294     if(ret)
295 	return ret;
296 
297     /* apply prefix rule */
298     ret = expand_realms(r, client_realm);
299     if(ret)
300 	return ret;
301 
302     ret = make_paths(r, client_realm, server_realm);
303     if(ret)
304 	return ret;
305 
306     /* remove empty components */
307     q = &r;
308     for(p = r; p; ){
309 	if(p->realm[0] == '\0'){
310 	    free(p->realm);
311 	    *q = p->next;
312 	    free(p);
313 	    p = *q;
314 	}else{
315 	    q = &p->next;
316 	    p = p->next;
317 	}
318     }
319     {
320 	char **R;
321 	*realms = NULL;
322 	*num_realms = 0;
323 	while(r){
324 	    R = realloc(*realms, (*num_realms + 1) * sizeof(**realms));
325 	    if(R == NULL) {
326 		free(*realms);
327 		return ENOMEM;
328 	    }
329 	    R[*num_realms] = r->realm;
330 	    (*num_realms)++;
331 	    *realms = R;
332 	    p = r->next;
333 	    free(r);
334 	    r = p;
335 	}
336     }
337     return 0;
338 }
339 
340 krb5_error_code
341 krb5_domain_x500_encode(char **realms, int num_realms, krb5_data *encoding)
342 {
343     char *s = NULL;
344     int len = 0;
345     int i;
346     for(i = 0; i < num_realms; i++){
347 	len += strlen(realms[i]);
348 	if(realms[i][0] == '/')
349 	    len++;
350     }
351     len += num_realms - 1;
352     s = malloc(len + 1);
353     *s = '\0';
354     for(i = 0; i < num_realms; i++){
355 	if(i && i < num_realms - 1)
356 	    strcat(s, ",");
357 	if(realms[i][0] == '/')
358 	    strcat(s, " ");
359 	strcat(s, realms[i]);
360     }
361     encoding->data = s;
362     encoding->length = strlen(s);
363     return 0;
364 }
365 
366 krb5_error_code
367 krb5_check_transited_realms(krb5_context context,
368 			    const char *const *realms,
369 			    int num_realms,
370 			    int *bad_realm)
371 {
372     int i;
373     int ret = 0;
374     char **bad_realms = krb5_config_get_strings(context, NULL,
375 						"libdefaults",
376 						"transited_realms_reject",
377 						NULL);
378     if(bad_realms == NULL)
379 	return 0;
380 
381     for(i = 0; i < num_realms; i++) {
382 	char **p;
383 	for(p = bad_realms; *p; p++)
384 	    if(strcmp(*p, realms[i]) == 0) {
385 		ret = KRB5KRB_AP_ERR_ILL_CR_TKT;
386 		if(bad_realm)
387 		    *bad_realm = i;
388 		break;
389 	    }
390     }
391     krb5_config_free_strings(bad_realms);
392     return ret;
393 }
394 
395 #if 0
396 int
397 main(int argc, char **argv)
398 {
399     krb5_data x;
400     char **r;
401     int num, i;
402     x.data = argv[1];
403     x.length = strlen(x.data);
404     if(domain_expand(x, &r, &num, argv[2], argv[3]))
405 	exit(1);
406     for(i = 0; i < num; i++)
407 	printf("%s\n", r[i]);
408     return 0;
409 }
410 #endif
411 
412