s4:heimdal: import lorikeet-heimdal-200906080040 (commit 904d0124b46eed7a8ad6e5b73e89...
[amitay/samba.git] / source4 / heimdal / lib / krb5 / transited.c
1 /*
2  * Copyright (c) 1997 - 2001, 2003 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 /* this is an attempt at one of the most horrible `compression'
37    schemes that has ever been invented; it's so amazingly brain-dead
38    that words can not describe it, and all this just to save a few
39    silly bytes */
40
41 struct tr_realm {
42     char *realm;
43     unsigned leading_space:1;
44     unsigned leading_slash:1;
45     unsigned trailing_dot:1;
46     struct tr_realm *next;
47 };
48
49 static void
50 free_realms(struct tr_realm *r)
51 {
52     struct tr_realm *p;
53     while(r){
54         p = r;
55         r = r->next;
56         free(p->realm);
57         free(p);
58     }   
59 }
60
61 static int
62 make_path(krb5_context context, struct tr_realm *r,
63           const char *from, const char *to)
64 {
65     const char *p;
66     struct tr_realm *path = r->next;
67     struct tr_realm *tmp;
68
69     if(strlen(from) < strlen(to)){
70         const char *str;
71         str = from;
72         from = to;
73         to = str;
74     }
75         
76     if(strcmp(from + strlen(from) - strlen(to), to) == 0){
77         p = from;
78         while(1){
79             p = strchr(p, '.');
80             if(p == NULL) {
81                 krb5_clear_error_message (context);
82                 return KRB5KDC_ERR_POLICY;
83             }
84             p++;
85             if(strcmp(p, to) == 0)
86                 break;
87             tmp = calloc(1, sizeof(*tmp));
88             if(tmp == NULL){
89                 krb5_set_error_message(context, ENOMEM,
90                                        N_("malloc: out of memory", ""));
91                 return ENOMEM;
92             }
93             tmp->next = path;
94             path = tmp;
95             path->realm = strdup(p);
96             if(path->realm == NULL){
97                 r->next = path; /* XXX */
98                 krb5_set_error_message(context, ENOMEM,
99                                        N_("malloc: out of memory", ""));
100                 return ENOMEM;;
101             }
102         }
103     }else if(strncmp(from, to, strlen(to)) == 0){
104         p = from + strlen(from);
105         while(1){
106             while(p >= from && *p != '/') p--;
107             if(p == from) {
108                 r->next = path; /* XXX */
109                 return KRB5KDC_ERR_POLICY;
110             }
111             if(strncmp(to, from, p - from) == 0)
112                 break;
113             tmp = calloc(1, sizeof(*tmp));
114             if(tmp == NULL){
115                 krb5_set_error_message(context, ENOMEM,
116                                        N_("malloc: out of memory", ""));
117                 return ENOMEM;
118             }
119             tmp->next = path;
120             path = tmp;
121             path->realm = malloc(p - from + 1);
122             if(path->realm == NULL){
123                 r->next = path; /* XXX */
124                 krb5_set_error_message(context, ENOMEM,
125                                        N_("malloc: out of memory", ""));
126                 return ENOMEM;
127             }
128             memcpy(path->realm, from, p - from);
129             path->realm[p - from] = '\0';
130             p--;
131         }
132     } else {
133         krb5_clear_error_message (context);
134         return KRB5KDC_ERR_POLICY;
135     }
136     r->next = path;
137
138     return 0;
139 }
140
141 static int
142 make_paths(krb5_context context,
143            struct tr_realm *realms, const char *client_realm,
144            const char *server_realm)
145 {
146     struct tr_realm *r;
147     int ret;
148     const char *prev_realm = client_realm;
149     const char *next_realm = NULL;
150     for(r = realms; r; r = r->next){
151         /* it *might* be that you can have more than one empty
152            component in a row, at least that's how I interpret the
153            "," exception in 1510 */
154         if(r->realm[0] == '\0'){
155             while(r->next && r->next->realm[0] == '\0')
156                 r = r->next;
157             if(r->next)
158                 next_realm = r->next->realm;
159             else
160                 next_realm = server_realm;
161             ret = make_path(context, r, prev_realm, next_realm);
162             if(ret){
163                 free_realms(realms);
164                 return ret;
165             }
166         }
167         prev_realm = r->realm;
168     }
169     return 0;
170 }
171
172 static int
173 expand_realms(krb5_context context,
174               struct tr_realm *realms, const char *client_realm)
175 {
176     struct tr_realm *r;
177     const char *prev_realm = NULL;
178     for(r = realms; r; r = r->next){
179         if(r->trailing_dot){
180             char *tmp;
181             size_t len;
182
183             if(prev_realm == NULL)
184                 prev_realm = client_realm;
185
186             len = strlen(r->realm) + strlen(prev_realm) + 1;
187
188             tmp = realloc(r->realm, len);
189             if(tmp == NULL){
190                 free_realms(realms);
191                 krb5_set_error_message(context, ENOMEM,
192                                        N_("malloc: out of memory", ""));
193                 return ENOMEM;
194             }
195             r->realm = tmp;
196             strlcat(r->realm, prev_realm, len);
197         }else if(r->leading_slash && !r->leading_space && prev_realm){
198             /* yet another exception: if you use x500-names, the
199                leading realm doesn't have to be "quoted" with a space */
200             char *tmp;
201             size_t len = strlen(r->realm) + strlen(prev_realm) + 1;
202
203             tmp = malloc(len);
204             if(tmp == NULL){
205                 free_realms(realms);
206                 krb5_set_error_message(context, ENOMEM,
207                                        N_("malloc: out of memory", ""));
208                 return ENOMEM;
209             }
210             strlcpy(tmp, prev_realm, len);
211             strlcat(tmp, r->realm, len);
212             free(r->realm);
213             r->realm = tmp;
214         }
215         prev_realm = r->realm;
216     }
217     return 0;
218 }
219
220 static struct tr_realm *
221 make_realm(char *realm)
222 {
223     struct tr_realm *r;
224     char *p, *q;
225     int quote = 0;
226     r = calloc(1, sizeof(*r));
227     if(r == NULL){
228         free(realm);
229         return NULL;
230     }
231     r->realm = realm;
232     for(p = q = r->realm; *p; p++){
233         if(p == r->realm && *p == ' '){
234             r->leading_space = 1;
235             continue;
236         }
237         if(q == r->realm && *p == '/')
238             r->leading_slash = 1;
239         if(quote){
240             *q++ = *p;
241             quote = 0;
242             continue;
243         }
244         if(*p == '\\'){
245             quote = 1;
246             continue;
247         }
248         if(p[0] == '.' && p[1] == '\0')
249             r->trailing_dot = 1;
250         *q++ = *p;
251     }
252     *q = '\0';
253     return r;
254 }
255
256 static struct tr_realm*
257 append_realm(struct tr_realm *head, struct tr_realm *r)
258 {
259     struct tr_realm *p;
260     if(head == NULL){
261         r->next = NULL;
262         return r;
263     }
264     p = head;
265     while(p->next) p = p->next;
266     p->next = r;
267     return head;
268 }
269
270 static int
271 decode_realms(krb5_context context,
272               const char *tr, int length, struct tr_realm **realms)
273 {
274     struct tr_realm *r = NULL;
275
276     char *tmp;
277     int quote = 0;
278     const char *start = tr;
279     int i;
280
281     for(i = 0; i < length; i++){
282         if(quote){
283             quote = 0;
284             continue;
285         }
286         if(tr[i] == '\\'){
287             quote = 1;
288             continue;
289         }
290         if(tr[i] == ','){
291             tmp = malloc(tr + i - start + 1);
292             if(tmp == NULL){
293                 krb5_set_error_message(context, ENOMEM,
294                                        N_("malloc: out of memory", ""));
295                 return ENOMEM;
296             }
297             memcpy(tmp, start, tr + i - start);
298             tmp[tr + i - start] = '\0';
299             r = make_realm(tmp);
300             if(r == NULL){
301                 free_realms(*realms);
302                 krb5_set_error_message(context, ENOMEM,
303                                        N_("malloc: out of memory", ""));
304                 return ENOMEM;
305             }
306             *realms = append_realm(*realms, r);
307             start = tr + i + 1;
308         }
309     }
310     tmp = malloc(tr + i - start + 1);
311     if(tmp == NULL){
312         free(*realms);
313         krb5_set_error_message(context, ENOMEM,
314                                N_("malloc: out of memory", ""));
315         return ENOMEM;
316     }
317     memcpy(tmp, start, tr + i - start);
318     tmp[tr + i - start] = '\0';
319     r = make_realm(tmp);
320     if(r == NULL){
321         free_realms(*realms);
322         krb5_set_error_message(context, ENOMEM,
323                                N_("malloc: out of memory", ""));
324         return ENOMEM;
325     }
326     *realms = append_realm(*realms, r);
327
328     return 0;
329 }
330
331
332 krb5_error_code KRB5_LIB_FUNCTION
333 krb5_domain_x500_decode(krb5_context context,
334                         krb5_data tr, char ***realms, unsigned int *num_realms,
335                         const char *client_realm, const char *server_realm)
336 {
337     struct tr_realm *r = NULL;
338     struct tr_realm *p, **q;
339     int ret;
340
341     if(tr.length == 0) {
342         *realms = NULL;
343         *num_realms = 0;
344         return 0;
345     }
346
347     /* split string in components */
348     ret = decode_realms(context, tr.data, tr.length, &r);
349     if(ret)
350         return ret;
351
352     /* apply prefix rule */
353     ret = expand_realms(context, r, client_realm);
354     if(ret)
355         return ret;
356
357     ret = make_paths(context, r, client_realm, server_realm);
358     if(ret)
359         return ret;
360
361     /* remove empty components and count realms */
362     q = &r;
363     *num_realms = 0;
364     for(p = r; p; ){
365         if(p->realm[0] == '\0'){
366             free(p->realm);
367             *q = p->next;
368             free(p);
369             p = *q;
370         }else{
371             q = &p->next;
372             p = p->next;
373             (*num_realms)++;
374         }
375     }
376     if (*num_realms < 0 || *num_realms + 1 > UINT_MAX/sizeof(**realms))
377         return ERANGE;
378
379     {
380         char **R;
381         R = malloc((*num_realms + 1) * sizeof(*R));
382         if (R == NULL)
383             return ENOMEM;
384         *realms = R;
385         while(r){
386             *R++ = r->realm;
387             p = r->next;
388             free(r);
389             r = p;
390         }
391     }
392     return 0;
393 }
394
395 krb5_error_code KRB5_LIB_FUNCTION
396 krb5_domain_x500_encode(char **realms, unsigned int num_realms,
397                         krb5_data *encoding)
398 {
399     char *s = NULL;
400     int len = 0;
401     unsigned int i;
402     krb5_data_zero(encoding);
403     if (num_realms == 0)
404         return 0;
405     for(i = 0; i < num_realms; i++){
406         len += strlen(realms[i]);
407         if(realms[i][0] == '/')
408             len++;
409     }
410     len += num_realms - 1;
411     s = malloc(len + 1);
412     if (s == NULL)
413         return ENOMEM;
414     *s = '\0';
415     for(i = 0; i < num_realms; i++){
416         if(i && i < num_realms - 1)
417             strlcat(s, ",", len + 1);
418         if(realms[i][0] == '/')
419             strlcat(s, " ", len + 1);
420         strlcat(s, realms[i], len + 1);
421     }
422     encoding->data = s;
423     encoding->length = strlen(s);
424     return 0;
425 }
426
427 krb5_error_code KRB5_LIB_FUNCTION
428 krb5_check_transited(krb5_context context,
429                      krb5_const_realm client_realm,
430                      krb5_const_realm server_realm,
431                      krb5_realm *realms,
432                      unsigned int num_realms,
433                      int *bad_realm)
434 {
435     char **tr_realms;
436     char **p;
437     int i;
438
439     if(num_realms == 0)
440         return 0;
441
442     tr_realms = krb5_config_get_strings(context, NULL,
443                                         "capaths",
444                                         client_realm,
445                                         server_realm,
446                                         NULL);
447     for(i = 0; i < num_realms; i++) {
448         for(p = tr_realms; p && *p; p++) {
449             if(strcmp(*p, realms[i]) == 0)
450                 break;
451         }
452         if(p == NULL || *p == NULL) {
453             krb5_config_free_strings(tr_realms);
454             krb5_set_error_message (context, KRB5KRB_AP_ERR_ILL_CR_TKT,
455                                     N_("no transit allowed "
456                                        "through realm %s", ""),
457                                     realms[i]);
458             if(bad_realm)
459                 *bad_realm = i;
460             return KRB5KRB_AP_ERR_ILL_CR_TKT;
461         }
462     }
463     krb5_config_free_strings(tr_realms);
464     return 0;
465 }
466
467 krb5_error_code KRB5_LIB_FUNCTION
468 krb5_check_transited_realms(krb5_context context,
469                             const char *const *realms,
470                             unsigned int num_realms,
471                             int *bad_realm)
472 {
473     int i;
474     int ret = 0;
475     char **bad_realms = krb5_config_get_strings(context, NULL,
476                                                 "libdefaults",
477                                                 "transited_realms_reject",
478                                                 NULL);
479     if(bad_realms == NULL)
480         return 0;
481
482     for(i = 0; i < num_realms; i++) {
483         char **p;
484         for(p = bad_realms; *p; p++)
485             if(strcmp(*p, realms[i]) == 0) {
486                 ret = KRB5KRB_AP_ERR_ILL_CR_TKT;
487                 krb5_set_error_message (context, ret,
488                                         N_("no transit allowed "
489                                            "through realm %s", ""),
490                                         *p);
491                 if(bad_realm)
492                     *bad_realm = i;
493                 break;
494             }
495     }
496     krb5_config_free_strings(bad_realms);
497     return ret;
498 }
499
500 #if 0
501 int
502 main(int argc, char **argv)
503 {
504     krb5_data x;
505     char **r;
506     int num, i;
507     x.data = argv[1];
508     x.length = strlen(x.data);
509     if(domain_expand(x, &r, &num, argv[2], argv[3]))
510         exit(1);
511     for(i = 0; i < num; i++)
512         printf("%s\n", r[i]);
513     return 0;
514 }
515 #endif
516