155682Smarkm/*
2233294Sstas * Copyright (c) 1997 - 2001, 2003 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
555682Smarkm *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
955682Smarkm *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
2055682Smarkm *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "krb5_locl.h"
3555682Smarkm
3655682Smarkm/* this is an attempt at one of the most horrible `compression'
3755682Smarkm   schemes that has ever been invented; it's so amazingly brain-dead
3855682Smarkm   that words can not describe it, and all this just to save a few
3955682Smarkm   silly bytes */
4055682Smarkm
4155682Smarkmstruct tr_realm {
4255682Smarkm    char *realm;
4355682Smarkm    unsigned leading_space:1;
4455682Smarkm    unsigned leading_slash:1;
4555682Smarkm    unsigned trailing_dot:1;
4655682Smarkm    struct tr_realm *next;
4755682Smarkm};
4855682Smarkm
4955682Smarkmstatic void
5055682Smarkmfree_realms(struct tr_realm *r)
5155682Smarkm{
5255682Smarkm    struct tr_realm *p;
5355682Smarkm    while(r){
5455682Smarkm	p = r;
5555682Smarkm	r = r->next;
5655682Smarkm	free(p->realm);
5755682Smarkm	free(p);
58233294Sstas    }
5955682Smarkm}
6055682Smarkm
6155682Smarkmstatic int
6278527Sassarmake_path(krb5_context context, struct tr_realm *r,
6378527Sassar	  const char *from, const char *to)
6455682Smarkm{
65233294Sstas    struct tr_realm *tmp;
6655682Smarkm    const char *p;
6755682Smarkm
6855682Smarkm    if(strlen(from) < strlen(to)){
69178825Sdfr	const char *str;
70178825Sdfr	str = from;
7155682Smarkm	from = to;
72178825Sdfr	to = str;
7355682Smarkm    }
74233294Sstas
7555682Smarkm    if(strcmp(from + strlen(from) - strlen(to), to) == 0){
7655682Smarkm	p = from;
7755682Smarkm	while(1){
7855682Smarkm	    p = strchr(p, '.');
7978527Sassar	    if(p == NULL) {
80233294Sstas		krb5_clear_error_message (context);
8155682Smarkm		return KRB5KDC_ERR_POLICY;
8278527Sassar	    }
8355682Smarkm	    p++;
8455682Smarkm	    if(strcmp(p, to) == 0)
8555682Smarkm		break;
8655682Smarkm	    tmp = calloc(1, sizeof(*tmp));
87233294Sstas	    if(tmp == NULL)
88233294Sstas		return krb5_enomem(context);
89233294Sstas	    tmp->next = r->next;
90233294Sstas	    r->next = tmp;
91233294Sstas	    tmp->realm = strdup(p);
92233294Sstas	    if(tmp->realm == NULL){
93233294Sstas		r->next = tmp->next;
94233294Sstas		free(tmp);
95233294Sstas		return krb5_enomem(context);
96178825Sdfr	    }
9755682Smarkm	}
9855682Smarkm    }else if(strncmp(from, to, strlen(to)) == 0){
9955682Smarkm	p = from + strlen(from);
10055682Smarkm	while(1){
10155682Smarkm	    while(p >= from && *p != '/') p--;
102233294Sstas	    if(p == from)
10355682Smarkm		return KRB5KDC_ERR_POLICY;
104233294Sstas
10555682Smarkm	    if(strncmp(to, from, p - from) == 0)
10655682Smarkm		break;
10755682Smarkm	    tmp = calloc(1, sizeof(*tmp));
108233294Sstas	    if(tmp == NULL)
109233294Sstas		return krb5_enomem(context);
110233294Sstas	    tmp->next = r->next;
111233294Sstas	    r->next = tmp;
112233294Sstas	    tmp->realm = malloc(p - from + 1);
113233294Sstas	    if(tmp->realm == NULL){
114233294Sstas		r->next = tmp->next;
115233294Sstas		free(tmp);
116233294Sstas		return krb5_enomem(context);
117178825Sdfr	    }
118233294Sstas	    memcpy(tmp->realm, from, p - from);
119233294Sstas	    tmp->realm[p - from] = '\0';
12055682Smarkm	    p--;
12155682Smarkm	}
12278527Sassar    } else {
123233294Sstas	krb5_clear_error_message (context);
12455682Smarkm	return KRB5KDC_ERR_POLICY;
12578527Sassar    }
126233294Sstas
12755682Smarkm    return 0;
12855682Smarkm}
12955682Smarkm
13055682Smarkmstatic int
13178527Sassarmake_paths(krb5_context context,
132233294Sstas	   struct tr_realm *realms, const char *client_realm,
13355682Smarkm	   const char *server_realm)
13455682Smarkm{
13555682Smarkm    struct tr_realm *r;
13655682Smarkm    int ret;
13755682Smarkm    const char *prev_realm = client_realm;
13855682Smarkm    const char *next_realm = NULL;
13955682Smarkm    for(r = realms; r; r = r->next){
14055682Smarkm	/* it *might* be that you can have more than one empty
14155682Smarkm	   component in a row, at least that's how I interpret the
14255682Smarkm	   "," exception in 1510 */
14355682Smarkm	if(r->realm[0] == '\0'){
14455682Smarkm	    while(r->next && r->next->realm[0] == '\0')
14555682Smarkm		r = r->next;
14655682Smarkm	    if(r->next)
14755682Smarkm		next_realm = r->next->realm;
14855682Smarkm	    else
14955682Smarkm		next_realm = server_realm;
15078527Sassar	    ret = make_path(context, r, prev_realm, next_realm);
15155682Smarkm	    if(ret){
15255682Smarkm		free_realms(realms);
15355682Smarkm		return ret;
15455682Smarkm	    }
15555682Smarkm	}
15655682Smarkm	prev_realm = r->realm;
15755682Smarkm    }
15855682Smarkm    return 0;
15955682Smarkm}
16055682Smarkm
16155682Smarkmstatic int
16278527Sassarexpand_realms(krb5_context context,
16378527Sassar	      struct tr_realm *realms, const char *client_realm)
16455682Smarkm{
16555682Smarkm    struct tr_realm *r;
16655682Smarkm    const char *prev_realm = NULL;
16755682Smarkm    for(r = realms; r; r = r->next){
16855682Smarkm	if(r->trailing_dot){
16955682Smarkm	    char *tmp;
170178825Sdfr	    size_t len;
171120945Snectar
17255682Smarkm	    if(prev_realm == NULL)
17355682Smarkm		prev_realm = client_realm;
174178825Sdfr
175178825Sdfr	    len = strlen(r->realm) + strlen(prev_realm) + 1;
176178825Sdfr
177120945Snectar	    tmp = realloc(r->realm, len);
17855682Smarkm	    if(tmp == NULL){
17955682Smarkm		free_realms(realms);
180233294Sstas		return krb5_enomem(context);
18155682Smarkm	    }
18255682Smarkm	    r->realm = tmp;
183120945Snectar	    strlcat(r->realm, prev_realm, len);
18455682Smarkm	}else if(r->leading_slash && !r->leading_space && prev_realm){
18555682Smarkm	    /* yet another exception: if you use x500-names, the
18655682Smarkm               leading realm doesn't have to be "quoted" with a space */
18755682Smarkm	    char *tmp;
188120945Snectar	    size_t len = strlen(r->realm) + strlen(prev_realm) + 1;
189120945Snectar
190120945Snectar	    tmp = malloc(len);
19155682Smarkm	    if(tmp == NULL){
19255682Smarkm		free_realms(realms);
193233294Sstas		return krb5_enomem(context);
19455682Smarkm	    }
195120945Snectar	    strlcpy(tmp, prev_realm, len);
196120945Snectar	    strlcat(tmp, r->realm, len);
19755682Smarkm	    free(r->realm);
19855682Smarkm	    r->realm = tmp;
19955682Smarkm	}
20055682Smarkm	prev_realm = r->realm;
20155682Smarkm    }
20255682Smarkm    return 0;
20355682Smarkm}
20455682Smarkm
20555682Smarkmstatic struct tr_realm *
20655682Smarkmmake_realm(char *realm)
20755682Smarkm{
20855682Smarkm    struct tr_realm *r;
20955682Smarkm    char *p, *q;
21055682Smarkm    int quote = 0;
21155682Smarkm    r = calloc(1, sizeof(*r));
21255682Smarkm    if(r == NULL){
21355682Smarkm	free(realm);
21455682Smarkm	return NULL;
21555682Smarkm    }
21655682Smarkm    r->realm = realm;
21755682Smarkm    for(p = q = r->realm; *p; p++){
21855682Smarkm	if(p == r->realm && *p == ' '){
21955682Smarkm	    r->leading_space = 1;
22055682Smarkm	    continue;
22155682Smarkm	}
22255682Smarkm	if(q == r->realm && *p == '/')
22355682Smarkm	    r->leading_slash = 1;
22455682Smarkm	if(quote){
22555682Smarkm	    *q++ = *p;
22655682Smarkm	    quote = 0;
22755682Smarkm	    continue;
22855682Smarkm	}
22955682Smarkm	if(*p == '\\'){
23055682Smarkm	    quote = 1;
23155682Smarkm	    continue;
23255682Smarkm	}
23355682Smarkm	if(p[0] == '.' && p[1] == '\0')
23455682Smarkm	    r->trailing_dot = 1;
23555682Smarkm	*q++ = *p;
23655682Smarkm    }
23755682Smarkm    *q = '\0';
23855682Smarkm    return r;
23955682Smarkm}
24055682Smarkm
24155682Smarkmstatic struct tr_realm*
24255682Smarkmappend_realm(struct tr_realm *head, struct tr_realm *r)
24355682Smarkm{
24455682Smarkm    struct tr_realm *p;
24555682Smarkm    if(head == NULL){
24655682Smarkm	r->next = NULL;
24755682Smarkm	return r;
24855682Smarkm    }
24955682Smarkm    p = head;
25055682Smarkm    while(p->next) p = p->next;
25155682Smarkm    p->next = r;
25255682Smarkm    return head;
25355682Smarkm}
25455682Smarkm
25555682Smarkmstatic int
25678527Sassardecode_realms(krb5_context context,
25778527Sassar	      const char *tr, int length, struct tr_realm **realms)
25855682Smarkm{
25955682Smarkm    struct tr_realm *r = NULL;
26055682Smarkm
26155682Smarkm    char *tmp;
26255682Smarkm    int quote = 0;
26355682Smarkm    const char *start = tr;
26455682Smarkm    int i;
26555682Smarkm
26655682Smarkm    for(i = 0; i < length; i++){
26755682Smarkm	if(quote){
26855682Smarkm	    quote = 0;
26955682Smarkm	    continue;
27055682Smarkm	}
27155682Smarkm	if(tr[i] == '\\'){
27255682Smarkm	    quote = 1;
27355682Smarkm	    continue;
27455682Smarkm	}
27555682Smarkm	if(tr[i] == ','){
27655682Smarkm	    tmp = malloc(tr + i - start + 1);
277233294Sstas	    if(tmp == NULL)
278233294Sstas		return krb5_enomem(context);
27957416Smarkm	    memcpy(tmp, start, tr + i - start);
28055682Smarkm	    tmp[tr + i - start] = '\0';
28155682Smarkm	    r = make_realm(tmp);
28255682Smarkm	    if(r == NULL){
28355682Smarkm		free_realms(*realms);
284233294Sstas		return krb5_enomem(context);
28555682Smarkm	    }
28655682Smarkm	    *realms = append_realm(*realms, r);
28755682Smarkm	    start = tr + i + 1;
28855682Smarkm	}
28955682Smarkm    }
29055682Smarkm    tmp = malloc(tr + i - start + 1);
291178825Sdfr    if(tmp == NULL){
292178825Sdfr	free(*realms);
293233294Sstas	return krb5_enomem(context);
294178825Sdfr    }
29557416Smarkm    memcpy(tmp, start, tr + i - start);
29655682Smarkm    tmp[tr + i - start] = '\0';
29755682Smarkm    r = make_realm(tmp);
29855682Smarkm    if(r == NULL){
29955682Smarkm	free_realms(*realms);
300233294Sstas	return krb5_enomem(context);
30155682Smarkm    }
30255682Smarkm    *realms = append_realm(*realms, r);
303233294Sstas
30455682Smarkm    return 0;
30555682Smarkm}
30655682Smarkm
30755682Smarkm
308233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
30978527Sassarkrb5_domain_x500_decode(krb5_context context,
310233294Sstas			krb5_data tr, char ***realms, unsigned int *num_realms,
31155682Smarkm			const char *client_realm, const char *server_realm)
31255682Smarkm{
31355682Smarkm    struct tr_realm *r = NULL;
31455682Smarkm    struct tr_realm *p, **q;
31555682Smarkm    int ret;
316233294Sstas
317127808Snectar    if(tr.length == 0) {
318127808Snectar	*realms = NULL;
319127808Snectar	*num_realms = 0;
320127808Snectar	return 0;
321127808Snectar    }
322127808Snectar
32355682Smarkm    /* split string in components */
32478527Sassar    ret = decode_realms(context, tr.data, tr.length, &r);
32555682Smarkm    if(ret)
32655682Smarkm	return ret;
327233294Sstas
32855682Smarkm    /* apply prefix rule */
32978527Sassar    ret = expand_realms(context, r, client_realm);
33055682Smarkm    if(ret)
33155682Smarkm	return ret;
332233294Sstas
33378527Sassar    ret = make_paths(context, r, client_realm, server_realm);
33455682Smarkm    if(ret)
33555682Smarkm	return ret;
336233294Sstas
337103423Snectar    /* remove empty components and count realms */
338103423Snectar    *num_realms = 0;
339233294Sstas    for(q = &r; *q; ){
340233294Sstas	if((*q)->realm[0] == '\0'){
341233294Sstas	    p = *q;
342233294Sstas	    *q = (*q)->next;
34355682Smarkm	    free(p->realm);
34455682Smarkm	    free(p);
34555682Smarkm	}else{
346233294Sstas	    q = &(*q)->next;
347103423Snectar	    (*num_realms)++;
34855682Smarkm	}
34955682Smarkm    }
350233294Sstas    if (*num_realms + 1 > UINT_MAX/sizeof(**realms))
351103423Snectar	return ERANGE;
352103423Snectar
35355682Smarkm    {
35455682Smarkm	char **R;
355103423Snectar	R = malloc((*num_realms + 1) * sizeof(*R));
356103423Snectar	if (R == NULL)
357233294Sstas	    return krb5_enomem(context);
358103423Snectar	*realms = R;
35955682Smarkm	while(r){
360103423Snectar	    *R++ = r->realm;
36155682Smarkm	    p = r->next;
36255682Smarkm	    free(r);
36355682Smarkm	    r = p;
36455682Smarkm	}
36555682Smarkm    }
36655682Smarkm    return 0;
36755682Smarkm}
36855682Smarkm
369233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
370233294Sstaskrb5_domain_x500_encode(char **realms, unsigned int num_realms,
371233294Sstas			krb5_data *encoding)
37255682Smarkm{
37355682Smarkm    char *s = NULL;
37455682Smarkm    int len = 0;
375233294Sstas    unsigned int i;
376127808Snectar    krb5_data_zero(encoding);
377127808Snectar    if (num_realms == 0)
378127808Snectar	return 0;
37955682Smarkm    for(i = 0; i < num_realms; i++){
38055682Smarkm	len += strlen(realms[i]);
38155682Smarkm	if(realms[i][0] == '/')
38255682Smarkm	    len++;
38355682Smarkm    }
38455682Smarkm    len += num_realms - 1;
38555682Smarkm    s = malloc(len + 1);
386127808Snectar    if (s == NULL)
387127808Snectar	return ENOMEM;
38855682Smarkm    *s = '\0';
38955682Smarkm    for(i = 0; i < num_realms; i++){
390233294Sstas	if(i)
391120945Snectar	    strlcat(s, ",", len + 1);
39255682Smarkm	if(realms[i][0] == '/')
393120945Snectar	    strlcat(s, " ", len + 1);
394120945Snectar	strlcat(s, realms[i], len + 1);
39555682Smarkm    }
39655682Smarkm    encoding->data = s;
39755682Smarkm    encoding->length = strlen(s);
39855682Smarkm    return 0;
39955682Smarkm}
40055682Smarkm
401233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
402127808Snectarkrb5_check_transited(krb5_context context,
403127808Snectar		     krb5_const_realm client_realm,
404127808Snectar		     krb5_const_realm server_realm,
405127808Snectar		     krb5_realm *realms,
406233294Sstas		     unsigned int num_realms,
407127808Snectar		     int *bad_realm)
408127808Snectar{
409127808Snectar    char **tr_realms;
410127808Snectar    char **p;
411233294Sstas    size_t i;
412127808Snectar
413127808Snectar    if(num_realms == 0)
414127808Snectar	return 0;
415233294Sstas
416233294Sstas    tr_realms = krb5_config_get_strings(context, NULL,
417233294Sstas					"capaths",
418233294Sstas					client_realm,
419233294Sstas					server_realm,
420127808Snectar					NULL);
421127808Snectar    for(i = 0; i < num_realms; i++) {
422127808Snectar	for(p = tr_realms; p && *p; p++) {
423127808Snectar	    if(strcmp(*p, realms[i]) == 0)
424127808Snectar		break;
425127808Snectar	}
426127808Snectar	if(p == NULL || *p == NULL) {
427127808Snectar	    krb5_config_free_strings(tr_realms);
428233294Sstas	    krb5_set_error_message (context, KRB5KRB_AP_ERR_ILL_CR_TKT,
429233294Sstas				    N_("no transit allowed "
430233294Sstas				       "through realm %s", ""),
431233294Sstas				    realms[i]);
432127808Snectar	    if(bad_realm)
433127808Snectar		*bad_realm = i;
434127808Snectar	    return KRB5KRB_AP_ERR_ILL_CR_TKT;
435127808Snectar	}
436127808Snectar    }
437127808Snectar    krb5_config_free_strings(tr_realms);
438127808Snectar    return 0;
439127808Snectar}
440127808Snectar
441233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
44257419Smarkmkrb5_check_transited_realms(krb5_context context,
443233294Sstas			    const char *const *realms,
444233294Sstas			    unsigned int num_realms,
44557419Smarkm			    int *bad_realm)
44657419Smarkm{
447233294Sstas    size_t i;
44857419Smarkm    int ret = 0;
449233294Sstas    char **bad_realms = krb5_config_get_strings(context, NULL,
450233294Sstas						"libdefaults",
451233294Sstas						"transited_realms_reject",
45257419Smarkm						NULL);
45357419Smarkm    if(bad_realms == NULL)
45457419Smarkm	return 0;
45557419Smarkm
45657419Smarkm    for(i = 0; i < num_realms; i++) {
45757419Smarkm	char **p;
45857419Smarkm	for(p = bad_realms; *p; p++)
45957419Smarkm	    if(strcmp(*p, realms[i]) == 0) {
46057419Smarkm		ret = KRB5KRB_AP_ERR_ILL_CR_TKT;
461233294Sstas		krb5_set_error_message (context, ret,
462233294Sstas					N_("no transit allowed "
463233294Sstas					   "through realm %s", ""),
464233294Sstas					*p);
46557419Smarkm		if(bad_realm)
46657419Smarkm		    *bad_realm = i;
46757419Smarkm		break;
46857419Smarkm	    }
46957419Smarkm    }
47057419Smarkm    krb5_config_free_strings(bad_realms);
47157419Smarkm    return ret;
47257419Smarkm}
47357419Smarkm
47455682Smarkm#if 0
47555682Smarkmint
47655682Smarkmmain(int argc, char **argv)
47755682Smarkm{
47855682Smarkm    krb5_data x;
47955682Smarkm    char **r;
48055682Smarkm    int num, i;
48155682Smarkm    x.data = argv[1];
48255682Smarkm    x.length = strlen(x.data);
48355682Smarkm    if(domain_expand(x, &r, &num, argv[2], argv[3]))
48455682Smarkm	exit(1);
48555682Smarkm    for(i = 0; i < num; i++)
48655682Smarkm	printf("%s\n", r[i]);
48755682Smarkm    return 0;
48855682Smarkm}
48955682Smarkm#endif
49055682Smarkm
491