1 /*
2  * Copyright (c) 1997-2002 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 #ifdef HAVE_RES_SEARCH
36 #define USE_RESOLVER
37 #endif
38 #ifdef HAVE_ARPA_NAMESER_H
39 #include <arpa/nameser.h>
40 #endif
41 #include <fnmatch.h>
42 #include "resolve.h"
43 
44 RCSID("$Id: principal.c,v 1.99 2006/10/18 06:53:22 lha Exp $");
45 
46 #define princ_num_comp(P) ((P)->name.name_string.len)
47 #define princ_type(P) ((P)->name.name_type)
48 #define princ_comp(P) ((P)->name.name_string.val)
49 #define princ_ncomp(P, N) ((P)->name.name_string.val[(N)])
50 #define princ_realm(P) ((P)->realm)
51 
52 void KRB5_LIB_FUNCTION
krb5_free_principal(krb5_context context,krb5_principal p)53 krb5_free_principal(krb5_context context,
54 		    krb5_principal p)
55 {
56     if(p){
57 	free_Principal(p);
58 	free(p);
59     }
60 }
61 
62 void KRB5_LIB_FUNCTION
krb5_principal_set_type(krb5_context context,krb5_principal principal,int type)63 krb5_principal_set_type(krb5_context context,
64 			krb5_principal principal,
65 			int type)
66 {
67     princ_type(principal) = type;
68 }
69 
70 int KRB5_LIB_FUNCTION
krb5_principal_get_type(krb5_context context,krb5_const_principal principal)71 krb5_principal_get_type(krb5_context context,
72 			krb5_const_principal principal)
73 {
74     return princ_type(principal);
75 }
76 
77 const char* KRB5_LIB_FUNCTION
krb5_principal_get_realm(krb5_context context,krb5_const_principal principal)78 krb5_principal_get_realm(krb5_context context,
79 			 krb5_const_principal principal)
80 {
81     return princ_realm(principal);
82 }
83 
84 const char* KRB5_LIB_FUNCTION
krb5_principal_get_comp_string(krb5_context context,krb5_const_principal principal,unsigned int component)85 krb5_principal_get_comp_string(krb5_context context,
86 			       krb5_const_principal principal,
87 			       unsigned int component)
88 {
89     if(component >= princ_num_comp(principal))
90        return NULL;
91     return princ_ncomp(principal, component);
92 }
93 
94 krb5_error_code KRB5_LIB_FUNCTION
krb5_parse_name_flags(krb5_context context,const char * name,int flags,krb5_principal * principal)95 krb5_parse_name_flags(krb5_context context,
96 		      const char *name,
97 		      int flags,
98 		      krb5_principal *principal)
99 {
100     krb5_error_code ret;
101     heim_general_string *comp;
102     heim_general_string realm = NULL;
103     int ncomp;
104 
105     const char *p;
106     char *q;
107     char *s;
108     char *start;
109 
110     int n;
111     char c;
112     int got_realm = 0;
113 
114     *principal = NULL;
115 
116 #define RFLAGS (KRB5_PRINCIPAL_PARSE_NO_REALM|KRB5_PRINCIPAL_PARSE_MUST_REALM)
117 
118     if ((flags & RFLAGS) == RFLAGS) {
119 	krb5_set_error_string(context, "Can't require both realm and "
120 			      "no realm at the same time");
121 	return KRB5_ERR_NO_SERVICE;
122     }
123 #undef RFLAGS
124 
125     /* count number of component */
126     ncomp = 1;
127     for(p = name; *p; p++){
128 	if(*p=='\\'){
129 	    if(!p[1]) {
130 		krb5_set_error_string (context,
131 				       "trailing \\ in principal name");
132 		return KRB5_PARSE_MALFORMED;
133 	    }
134 	    p++;
135 	} else if(*p == '/')
136 	    ncomp++;
137     }
138     comp = calloc(ncomp, sizeof(*comp));
139     if (comp == NULL) {
140 	krb5_set_error_string (context, "malloc: out of memory");
141 	return ENOMEM;
142     }
143 
144     n = 0;
145     p = start = q = s = strdup(name);
146     if (start == NULL) {
147 	free (comp);
148 	krb5_set_error_string (context, "malloc: out of memory");
149 	return ENOMEM;
150     }
151     while(*p){
152 	c = *p++;
153 	if(c == '\\'){
154 	    c = *p++;
155 	    if(c == 'n')
156 		c = '\n';
157 	    else if(c == 't')
158 		c = '\t';
159 	    else if(c == 'b')
160 		c = '\b';
161 	    else if(c == '0')
162 		c = '\0';
163 	    else if(c == '\0') {
164 		krb5_set_error_string (context,
165 				       "trailing \\ in principal name");
166 		ret = KRB5_PARSE_MALFORMED;
167 		goto exit;
168 	    }
169 	}else if(c == '/' || c == '@'){
170 	    if(got_realm){
171 		krb5_set_error_string (context,
172 				       "part after realm in principal name");
173 		ret = KRB5_PARSE_MALFORMED;
174 		goto exit;
175 	    }else{
176 		comp[n] = malloc(q - start + 1);
177 		if (comp[n] == NULL) {
178 		    krb5_set_error_string (context, "malloc: out of memory");
179 		    ret = ENOMEM;
180 		    goto exit;
181 		}
182 		memcpy(comp[n], start, q - start);
183 		comp[n][q - start] = 0;
184 		n++;
185 	    }
186 	    if(c == '@')
187 		got_realm = 1;
188 	    start = q;
189 	    continue;
190 	}
191 	if(got_realm && (c == ':' || c == '/' || c == '\0')) {
192 	    krb5_set_error_string (context,
193 				   "part after realm in principal name");
194 	    ret = KRB5_PARSE_MALFORMED;
195 	    goto exit;
196 	}
197 	*q++ = c;
198     }
199     if(got_realm){
200 	if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) {
201 	    krb5_set_error_string (context, "realm found in 'short' principal "
202 				   "expected to be without one");
203 	    ret = KRB5_PARSE_MALFORMED;
204 	    goto exit;
205 	}
206 	realm = malloc(q - start + 1);
207 	if (realm == NULL) {
208 	    krb5_set_error_string (context, "malloc: out of memory");
209 	    ret = ENOMEM;
210 	    goto exit;
211 	}
212 	memcpy(realm, start, q - start);
213 	realm[q - start] = 0;
214     }else{
215 	if (flags & KRB5_PRINCIPAL_PARSE_MUST_REALM) {
216 	    krb5_set_error_string (context, "realm NOT found in principal "
217 				   "expected to be with one");
218 	    ret = KRB5_PARSE_MALFORMED;
219 	    goto exit;
220 	} else if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) {
221 	    realm = NULL;
222 	} else {
223 	    ret = krb5_get_default_realm (context, &realm);
224 	    if (ret)
225 		goto exit;
226 	}
227 
228 	comp[n] = malloc(q - start + 1);
229 	if (comp[n] == NULL) {
230 	    krb5_set_error_string (context, "malloc: out of memory");
231 	    ret = ENOMEM;
232 	    goto exit;
233 	}
234 	memcpy(comp[n], start, q - start);
235 	comp[n][q - start] = 0;
236 	n++;
237     }
238     *principal = malloc(sizeof(**principal));
239     if (*principal == NULL) {
240 	krb5_set_error_string (context, "malloc: out of memory");
241 	ret = ENOMEM;
242 	goto exit;
243     }
244     (*principal)->name.name_type = KRB5_NT_PRINCIPAL;
245     (*principal)->name.name_string.val = comp;
246     princ_num_comp(*principal) = n;
247     (*principal)->realm = realm;
248     free(s);
249     return 0;
250 exit:
251     while(n>0){
252 	free(comp[--n]);
253     }
254     free(comp);
255     free(realm);
256     free(s);
257     return ret;
258 }
259 
260 krb5_error_code KRB5_LIB_FUNCTION
krb5_parse_name(krb5_context context,const char * name,krb5_principal * principal)261 krb5_parse_name(krb5_context context,
262 		const char *name,
263 		krb5_principal *principal)
264 {
265     return krb5_parse_name_flags(context, name, 0, principal);
266 }
267 
268 static const char quotable_chars[] = " \n\t\b\\/@";
269 static const char replace_chars[] = " ntb\\/@";
270 
271 #define add_char(BASE, INDEX, LEN, C) do { if((INDEX) < (LEN)) (BASE)[(INDEX)++] = (C); }while(0);
272 
273 static size_t
quote_string(const char * s,char * out,size_t idx,size_t len)274 quote_string(const char *s, char *out, size_t idx, size_t len)
275 {
276     const char *p, *q;
277     for(p = s; *p && idx < len; p++){
278 	if((q = strchr(quotable_chars, *p))){
279 	    add_char(out, idx, len, '\\');
280 	    add_char(out, idx, len, replace_chars[q - quotable_chars]);
281 	}else
282 	    add_char(out, idx, len, *p);
283     }
284     if(idx < len)
285 	out[idx] = '\0';
286     return idx;
287 }
288 
289 
290 static krb5_error_code
unparse_name_fixed(krb5_context context,krb5_const_principal principal,char * name,size_t len,int flags)291 unparse_name_fixed(krb5_context context,
292 		   krb5_const_principal principal,
293 		   char *name,
294 		   size_t len,
295 		   int flags)
296 {
297     size_t idx = 0;
298     int i;
299     int short_form = (flags & KRB5_PRINCIPAL_UNPARSE_SHORT) != 0;
300     int no_realm = (flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) != 0;
301 
302     if (!no_realm && princ_realm(principal) == NULL) {
303 	krb5_set_error_string(context, "Realm missing from principal, "
304 			      "can't unparse");
305 	return ERANGE;
306     }
307 
308     for(i = 0; i < princ_num_comp(principal); i++){
309 	if(i)
310 	    add_char(name, idx, len, '/');
311 	idx = quote_string(princ_ncomp(principal, i), name, idx, len);
312 	if(idx == len) {
313 	    krb5_set_error_string(context, "Out of space printing principal");
314 	    return ERANGE;
315 	}
316     }
317     /* add realm if different from default realm */
318     if(short_form && !no_realm) {
319 	krb5_realm r;
320 	krb5_error_code ret;
321 	ret = krb5_get_default_realm(context, &r);
322 	if(ret)
323 	    return ret;
324 	if(strcmp(princ_realm(principal), r) != 0)
325 	    short_form = 0;
326 	free(r);
327     }
328     if(!short_form && !no_realm) {
329 	add_char(name, idx, len, '@');
330 	idx = quote_string(princ_realm(principal), name, idx, len);
331 	if(idx == len) {
332 	    krb5_set_error_string(context,
333 				  "Out of space printing realm of principal");
334 	    return ERANGE;
335 	}
336     }
337     return 0;
338 }
339 
340 krb5_error_code KRB5_LIB_FUNCTION
krb5_unparse_name_fixed(krb5_context context,krb5_const_principal principal,char * name,size_t len)341 krb5_unparse_name_fixed(krb5_context context,
342 			krb5_const_principal principal,
343 			char *name,
344 			size_t len)
345 {
346     return unparse_name_fixed(context, principal, name, len, 0);
347 }
348 
349 krb5_error_code KRB5_LIB_FUNCTION
krb5_unparse_name_fixed_short(krb5_context context,krb5_const_principal principal,char * name,size_t len)350 krb5_unparse_name_fixed_short(krb5_context context,
351 			      krb5_const_principal principal,
352 			      char *name,
353 			      size_t len)
354 {
355     return unparse_name_fixed(context, principal, name, len,
356 			      KRB5_PRINCIPAL_UNPARSE_SHORT);
357 }
358 
359 krb5_error_code KRB5_LIB_FUNCTION
krb5_unparse_name_fixed_flags(krb5_context context,krb5_const_principal principal,int flags,char * name,size_t len)360 krb5_unparse_name_fixed_flags(krb5_context context,
361 			      krb5_const_principal principal,
362 			      int flags,
363 			      char *name,
364 			      size_t len)
365 {
366     return unparse_name_fixed(context, principal, name, len, flags);
367 }
368 
369 static krb5_error_code
unparse_name(krb5_context context,krb5_const_principal principal,char ** name,int flags)370 unparse_name(krb5_context context,
371 	     krb5_const_principal principal,
372 	     char **name,
373 	     int flags)
374 {
375     size_t len = 0, plen;
376     int i;
377     krb5_error_code ret;
378     /* count length */
379     if (princ_realm(principal)) {
380 	plen = strlen(princ_realm(principal));
381 
382 	if(strcspn(princ_realm(principal), quotable_chars) == plen)
383 	    len += plen;
384 	else
385 	    len += 2*plen;
386 	len++; /* '@' */
387     }
388     for(i = 0; i < princ_num_comp(principal); i++){
389 	plen = strlen(princ_ncomp(principal, i));
390 	if(strcspn(princ_ncomp(principal, i), quotable_chars) == plen)
391 	    len += plen;
392 	else
393 	    len += 2*plen;
394 	len++;
395     }
396     len++; /* '\0' */
397     *name = malloc(len);
398     if(*name == NULL) {
399 	krb5_set_error_string (context, "malloc: out of memory");
400 	return ENOMEM;
401     }
402     ret = unparse_name_fixed(context, principal, *name, len, flags);
403     if(ret) {
404 	free(*name);
405 	*name = NULL;
406     }
407     return ret;
408 }
409 
410 krb5_error_code KRB5_LIB_FUNCTION
krb5_unparse_name(krb5_context context,krb5_const_principal principal,char ** name)411 krb5_unparse_name(krb5_context context,
412 		  krb5_const_principal principal,
413 		  char **name)
414 {
415     return unparse_name(context, principal, name, 0);
416 }
417 
418 krb5_error_code KRB5_LIB_FUNCTION
krb5_unparse_name_flags(krb5_context context,krb5_const_principal principal,int flags,char ** name)419 krb5_unparse_name_flags(krb5_context context,
420 			krb5_const_principal principal,
421 			int flags,
422 			char **name)
423 {
424     return unparse_name(context, principal, name, flags);
425 }
426 
427 krb5_error_code KRB5_LIB_FUNCTION
krb5_unparse_name_short(krb5_context context,krb5_const_principal principal,char ** name)428 krb5_unparse_name_short(krb5_context context,
429 			krb5_const_principal principal,
430 			char **name)
431 {
432     return unparse_name(context, principal, name, KRB5_PRINCIPAL_UNPARSE_SHORT);
433 }
434 
435 #if 0 /* not implemented */
436 
437 krb5_error_code KRB5_LIB_FUNCTION
438 krb5_unparse_name_ext(krb5_context context,
439 		      krb5_const_principal principal,
440 		      char **name,
441 		      size_t *size)
442 {
443     krb5_abortx(context, "unimplemented krb5_unparse_name_ext called");
444 }
445 
446 #endif
447 
448 krb5_realm * KRB5_LIB_FUNCTION
krb5_princ_realm(krb5_context context,krb5_principal principal)449 krb5_princ_realm(krb5_context context,
450 		 krb5_principal principal)
451 {
452     return &princ_realm(principal);
453 }
454 
455 
456 void KRB5_LIB_FUNCTION
krb5_princ_set_realm(krb5_context context,krb5_principal principal,krb5_realm * realm)457 krb5_princ_set_realm(krb5_context context,
458 		     krb5_principal principal,
459 		     krb5_realm *realm)
460 {
461     princ_realm(principal) = *realm;
462 }
463 
464 
465 krb5_error_code KRB5_LIB_FUNCTION
krb5_build_principal(krb5_context context,krb5_principal * principal,int rlen,krb5_const_realm realm,...)466 krb5_build_principal(krb5_context context,
467 		     krb5_principal *principal,
468 		     int rlen,
469 		     krb5_const_realm realm,
470 		     ...)
471 {
472     krb5_error_code ret;
473     va_list ap;
474     va_start(ap, realm);
475     ret = krb5_build_principal_va(context, principal, rlen, realm, ap);
476     va_end(ap);
477     return ret;
478 }
479 
480 static krb5_error_code
append_component(krb5_context context,krb5_principal p,const char * comp,size_t comp_len)481 append_component(krb5_context context, krb5_principal p,
482 		 const char *comp,
483 		 size_t comp_len)
484 {
485     heim_general_string *tmp;
486     size_t len = princ_num_comp(p);
487 
488     tmp = realloc(princ_comp(p), (len + 1) * sizeof(*tmp));
489     if(tmp == NULL) {
490 	krb5_set_error_string (context, "malloc: out of memory");
491 	return ENOMEM;
492     }
493     princ_comp(p) = tmp;
494     princ_ncomp(p, len) = malloc(comp_len + 1);
495     if (princ_ncomp(p, len) == NULL) {
496 	krb5_set_error_string (context, "malloc: out of memory");
497 	return ENOMEM;
498     }
499     memcpy (princ_ncomp(p, len), comp, comp_len);
500     princ_ncomp(p, len)[comp_len] = '\0';
501     princ_num_comp(p)++;
502     return 0;
503 }
504 
505 static void
va_ext_princ(krb5_context context,krb5_principal p,va_list ap)506 va_ext_princ(krb5_context context, krb5_principal p, va_list ap)
507 {
508     while(1){
509 	const char *s;
510 	int len;
511 	len = va_arg(ap, int);
512 	if(len == 0)
513 	    break;
514 	s = va_arg(ap, const char*);
515 	append_component(context, p, s, len);
516     }
517 }
518 
519 static void
va_princ(krb5_context context,krb5_principal p,va_list ap)520 va_princ(krb5_context context, krb5_principal p, va_list ap)
521 {
522     while(1){
523 	const char *s;
524 	s = va_arg(ap, const char*);
525 	if(s == NULL)
526 	    break;
527 	append_component(context, p, s, strlen(s));
528     }
529 }
530 
531 
532 static krb5_error_code
build_principal(krb5_context context,krb5_principal * principal,int rlen,krb5_const_realm realm,void (* func)(krb5_context,krb5_principal,va_list),va_list ap)533 build_principal(krb5_context context,
534 		krb5_principal *principal,
535 		int rlen,
536 		krb5_const_realm realm,
537 		void (*func)(krb5_context, krb5_principal, va_list),
538 		va_list ap)
539 {
540     krb5_principal p;
541 
542     p = calloc(1, sizeof(*p));
543     if (p == NULL) {
544 	krb5_set_error_string (context, "malloc: out of memory");
545 	return ENOMEM;
546     }
547     princ_type(p) = KRB5_NT_PRINCIPAL;
548 
549     princ_realm(p) = strdup(realm);
550     if(p->realm == NULL){
551 	free(p);
552 	krb5_set_error_string (context, "malloc: out of memory");
553 	return ENOMEM;
554     }
555 
556     (*func)(context, p, ap);
557     *principal = p;
558     return 0;
559 }
560 
561 krb5_error_code KRB5_LIB_FUNCTION
krb5_make_principal(krb5_context context,krb5_principal * principal,krb5_const_realm realm,...)562 krb5_make_principal(krb5_context context,
563 		    krb5_principal *principal,
564 		    krb5_const_realm realm,
565 		    ...)
566 {
567     krb5_error_code ret;
568     krb5_realm r = NULL;
569     va_list ap;
570     if(realm == NULL) {
571 	ret = krb5_get_default_realm(context, &r);
572 	if(ret)
573 	    return ret;
574 	realm = r;
575     }
576     va_start(ap, realm);
577     ret = krb5_build_principal_va(context, principal, strlen(realm), realm, ap);
578     va_end(ap);
579     if(r)
580 	free(r);
581     return ret;
582 }
583 
584 krb5_error_code KRB5_LIB_FUNCTION
krb5_build_principal_va(krb5_context context,krb5_principal * principal,int rlen,krb5_const_realm realm,va_list ap)585 krb5_build_principal_va(krb5_context context,
586 			krb5_principal *principal,
587 			int rlen,
588 			krb5_const_realm realm,
589 			va_list ap)
590 {
591     return build_principal(context, principal, rlen, realm, va_princ, ap);
592 }
593 
594 krb5_error_code KRB5_LIB_FUNCTION
krb5_build_principal_va_ext(krb5_context context,krb5_principal * principal,int rlen,krb5_const_realm realm,va_list ap)595 krb5_build_principal_va_ext(krb5_context context,
596 			    krb5_principal *principal,
597 			    int rlen,
598 			    krb5_const_realm realm,
599 			    va_list ap)
600 {
601     return build_principal(context, principal, rlen, realm, va_ext_princ, ap);
602 }
603 
604 
605 krb5_error_code KRB5_LIB_FUNCTION
krb5_build_principal_ext(krb5_context context,krb5_principal * principal,int rlen,krb5_const_realm realm,...)606 krb5_build_principal_ext(krb5_context context,
607 			 krb5_principal *principal,
608 			 int rlen,
609 			 krb5_const_realm realm,
610 			 ...)
611 {
612     krb5_error_code ret;
613     va_list ap;
614     va_start(ap, realm);
615     ret = krb5_build_principal_va_ext(context, principal, rlen, realm, ap);
616     va_end(ap);
617     return ret;
618 }
619 
620 
621 krb5_error_code KRB5_LIB_FUNCTION
krb5_copy_principal(krb5_context context,krb5_const_principal inprinc,krb5_principal * outprinc)622 krb5_copy_principal(krb5_context context,
623 		    krb5_const_principal inprinc,
624 		    krb5_principal *outprinc)
625 {
626     krb5_principal p = malloc(sizeof(*p));
627     if (p == NULL) {
628 	krb5_set_error_string (context, "malloc: out of memory");
629 	return ENOMEM;
630     }
631     if(copy_Principal(inprinc, p)) {
632 	free(p);
633 	krb5_set_error_string (context, "malloc: out of memory");
634 	return ENOMEM;
635     }
636     *outprinc = p;
637     return 0;
638 }
639 
640 /*
641  * return TRUE iff princ1 == princ2 (without considering the realm)
642  */
643 
644 krb5_boolean KRB5_LIB_FUNCTION
krb5_principal_compare_any_realm(krb5_context context,krb5_const_principal princ1,krb5_const_principal princ2)645 krb5_principal_compare_any_realm(krb5_context context,
646 				 krb5_const_principal princ1,
647 				 krb5_const_principal princ2)
648 {
649     int i;
650     if(princ_num_comp(princ1) != princ_num_comp(princ2))
651 	return FALSE;
652     for(i = 0; i < princ_num_comp(princ1); i++){
653 	if(strcmp(princ_ncomp(princ1, i), princ_ncomp(princ2, i)) != 0)
654 	    return FALSE;
655     }
656     return TRUE;
657 }
658 
659 /*
660  * return TRUE iff princ1 == princ2
661  */
662 
663 krb5_boolean KRB5_LIB_FUNCTION
krb5_principal_compare(krb5_context context,krb5_const_principal princ1,krb5_const_principal princ2)664 krb5_principal_compare(krb5_context context,
665 		       krb5_const_principal princ1,
666 		       krb5_const_principal princ2)
667 {
668     if(!krb5_realm_compare(context, princ1, princ2))
669 	return FALSE;
670     return krb5_principal_compare_any_realm(context, princ1, princ2);
671 }
672 
673 /*
674  * return TRUE iff realm(princ1) == realm(princ2)
675  */
676 
677 krb5_boolean KRB5_LIB_FUNCTION
krb5_realm_compare(krb5_context context,krb5_const_principal princ1,krb5_const_principal princ2)678 krb5_realm_compare(krb5_context context,
679 		   krb5_const_principal princ1,
680 		   krb5_const_principal princ2)
681 {
682     return strcmp(princ_realm(princ1), princ_realm(princ2)) == 0;
683 }
684 
685 /*
686  * return TRUE iff princ matches pattern
687  */
688 
689 krb5_boolean KRB5_LIB_FUNCTION
krb5_principal_match(krb5_context context,krb5_const_principal princ,krb5_const_principal pattern)690 krb5_principal_match(krb5_context context,
691 		     krb5_const_principal princ,
692 		     krb5_const_principal pattern)
693 {
694     int i;
695     if(princ_num_comp(princ) != princ_num_comp(pattern))
696 	return FALSE;
697     if(fnmatch(princ_realm(pattern), princ_realm(princ), 0) != 0)
698 	return FALSE;
699     for(i = 0; i < princ_num_comp(princ); i++){
700 	if(fnmatch(princ_ncomp(pattern, i), princ_ncomp(princ, i), 0) != 0)
701 	    return FALSE;
702     }
703     return TRUE;
704 }
705 
706 
707 static struct v4_name_convert {
708     const char *from;
709     const char *to;
710 } default_v4_name_convert[] = {
711     { "ftp",	"ftp" },
712     { "hprop",	"hprop" },
713     { "pop",	"pop" },
714     { "imap",	"imap" },
715     { "rcmd",	"host" },
716     { "smtp",	"smtp" },
717     { NULL, NULL }
718 };
719 
720 /*
721  * return the converted instance name of `name' in `realm'.
722  * look in the configuration file and then in the default set above.
723  * return NULL if no conversion is appropriate.
724  */
725 
726 static const char*
get_name_conversion(krb5_context context,const char * realm,const char * name)727 get_name_conversion(krb5_context context, const char *realm, const char *name)
728 {
729     struct v4_name_convert *q;
730     const char *p;
731 
732     p = krb5_config_get_string(context, NULL, "realms", realm,
733 			       "v4_name_convert", "host", name, NULL);
734     if(p == NULL)
735 	p = krb5_config_get_string(context, NULL, "libdefaults",
736 				   "v4_name_convert", "host", name, NULL);
737     if(p)
738 	return p;
739 
740     /* XXX should be possible to override default list */
741     p = krb5_config_get_string(context, NULL,
742 			       "realms",
743 			       realm,
744 			       "v4_name_convert",
745 			       "plain",
746 			       name,
747 			       NULL);
748     if(p)
749 	return NULL;
750     p = krb5_config_get_string(context, NULL,
751 			       "libdefaults",
752 			       "v4_name_convert",
753 			       "plain",
754 			       name,
755 			       NULL);
756     if(p)
757 	return NULL;
758     for(q = default_v4_name_convert; q->from; q++)
759 	if(strcmp(q->from, name) == 0)
760 	    return q->to;
761     return NULL;
762 }
763 
764 /*
765  * convert the v4 principal `name.instance@realm' to a v5 principal in `princ'.
766  * if `resolve', use DNS.
767  * if `func', use that function for validating the conversion
768  */
769 
770 krb5_error_code KRB5_LIB_FUNCTION
krb5_425_conv_principal_ext2(krb5_context context,const char * name,const char * instance,const char * realm,krb5_boolean (* func)(krb5_context,void *,krb5_principal),void * funcctx,krb5_boolean resolve,krb5_principal * princ)771 krb5_425_conv_principal_ext2(krb5_context context,
772 			     const char *name,
773 			     const char *instance,
774 			     const char *realm,
775 			     krb5_boolean (*func)(krb5_context,
776 						  void *, krb5_principal),
777 			     void *funcctx,
778 			     krb5_boolean resolve,
779 			     krb5_principal *princ)
780 {
781     const char *p;
782     krb5_error_code ret;
783     krb5_principal pr;
784     char host[MAXHOSTNAMELEN];
785     char local_hostname[MAXHOSTNAMELEN];
786 
787     /* do the following: if the name is found in the
788        `v4_name_convert:host' part, is is assumed to be a `host' type
789        principal, and the instance is looked up in the
790        `v4_instance_convert' part. if not found there the name is
791        (optionally) looked up as a hostname, and if that doesn't yield
792        anything, the `default_domain' is appended to the instance
793        */
794 
795     if(instance == NULL)
796 	goto no_host;
797     if(instance[0] == 0){
798 	instance = NULL;
799 	goto no_host;
800     }
801     p = get_name_conversion(context, realm, name);
802     if(p == NULL)
803 	goto no_host;
804     name = p;
805     p = krb5_config_get_string(context, NULL, "realms", realm,
806 			       "v4_instance_convert", instance, NULL);
807     if(p){
808 	instance = p;
809 	ret = krb5_make_principal(context, &pr, realm, name, instance, NULL);
810 	if(func == NULL || (*func)(context, funcctx, pr)){
811 	    *princ = pr;
812 	    return 0;
813 	}
814 	krb5_free_principal(context, pr);
815 	*princ = NULL;
816 	krb5_clear_error_string (context);
817 	return HEIM_ERR_V4_PRINC_NO_CONV;
818     }
819     if(resolve){
820 	krb5_boolean passed = FALSE;
821 	char *inst = NULL;
822 #ifdef USE_RESOLVER
823 	struct dns_reply *r;
824 
825 	r = dns_lookup(instance, "aaaa");
826 	if (r) {
827 	    if (r->head && r->head->type == T_AAAA) {
828 		inst = strdup(r->head->domain);
829 		passed = TRUE;
830 	    }
831 	    dns_free_data(r);
832 	} else {
833 	    r = dns_lookup(instance, "a");
834 	    if (r) {
835 		if(r->head && r->head->type == T_A) {
836 		    inst = strdup(r->head->domain);
837 		    passed = TRUE;
838 		}
839 		dns_free_data(r);
840 	    }
841 	}
842 #else
843 	struct addrinfo hints, *ai;
844 
845 	memset (&hints, 0, sizeof(hints));
846 	hints.ai_flags = AI_CANONNAME;
847 	ret = getaddrinfo(instance, NULL, &hints, &ai);
848 	if (ret == 0) {
849 	    const struct addrinfo *a;
850 	    for (a = ai; a != NULL; a = a->ai_next) {
851 		if (a->ai_canonname != NULL) {
852 		    inst = strdup (a->ai_canonname);
853 		    passed = TRUE;
854 		    break;
855 		}
856 	    }
857 	    freeaddrinfo (ai);
858 	}
859 #endif
860 	if (passed) {
861 	    if (inst == NULL) {
862 		krb5_set_error_string (context, "malloc: out of memory");
863 		return ENOMEM;
864 	    }
865 	    strlwr(inst);
866 	    ret = krb5_make_principal(context, &pr, realm, name, inst,
867 				      NULL);
868 	    free (inst);
869 	    if(ret == 0) {
870 		if(func == NULL || (*func)(context, funcctx, pr)){
871 		    *princ = pr;
872 		    return 0;
873 		}
874 		krb5_free_principal(context, pr);
875 	    }
876 	}
877     }
878     if(func != NULL) {
879 	snprintf(host, sizeof(host), "%s.%s", instance, realm);
880 	strlwr(host);
881 	ret = krb5_make_principal(context, &pr, realm, name, host, NULL);
882 	if((*func)(context, funcctx, pr)){
883 	    *princ = pr;
884 	    return 0;
885 	}
886 	krb5_free_principal(context, pr);
887     }
888 
889     /*
890      * if the instance is the first component of the local hostname,
891      * the converted host should be the long hostname.
892      */
893 
894     if (func == NULL &&
895         gethostname (local_hostname, sizeof(local_hostname)) == 0 &&
896         strncmp(instance, local_hostname, strlen(instance)) == 0 &&
897 	local_hostname[strlen(instance)] == '.') {
898 	strlcpy(host, local_hostname, sizeof(host));
899 	goto local_host;
900     }
901 
902     {
903 	char **domains, **d;
904 	domains = krb5_config_get_strings(context, NULL, "realms", realm,
905 					  "v4_domains", NULL);
906 	for(d = domains; d && *d; d++){
907 	    snprintf(host, sizeof(host), "%s.%s", instance, *d);
908 	    ret = krb5_make_principal(context, &pr, realm, name, host, NULL);
909 	    if(func == NULL || (*func)(context, funcctx, pr)){
910 		*princ = pr;
911 		krb5_config_free_strings(domains);
912 		return 0;
913 	    }
914 	    krb5_free_principal(context, pr);
915 	}
916 	krb5_config_free_strings(domains);
917     }
918 
919 
920     p = krb5_config_get_string(context, NULL, "realms", realm,
921 			       "default_domain", NULL);
922     if(p == NULL){
923 	/* this should be an error, just faking a name is not good */
924 	krb5_clear_error_string (context);
925 	return HEIM_ERR_V4_PRINC_NO_CONV;
926     }
927 
928     if (*p == '.')
929 	++p;
930     snprintf(host, sizeof(host), "%s.%s", instance, p);
931 local_host:
932     ret = krb5_make_principal(context, &pr, realm, name, host, NULL);
933     if(func == NULL || (*func)(context, funcctx, pr)){
934 	*princ = pr;
935 	return 0;
936     }
937     krb5_free_principal(context, pr);
938     krb5_clear_error_string (context);
939     return HEIM_ERR_V4_PRINC_NO_CONV;
940 no_host:
941     p = krb5_config_get_string(context, NULL,
942 			       "realms",
943 			       realm,
944 			       "v4_name_convert",
945 			       "plain",
946 			       name,
947 			       NULL);
948     if(p == NULL)
949 	p = krb5_config_get_string(context, NULL,
950 				   "libdefaults",
951 				   "v4_name_convert",
952 				   "plain",
953 				   name,
954 				   NULL);
955     if(p)
956 	name = p;
957 
958     ret = krb5_make_principal(context, &pr, realm, name, instance, NULL);
959     if(func == NULL || (*func)(context, funcctx, pr)){
960 	*princ = pr;
961 	return 0;
962     }
963     krb5_free_principal(context, pr);
964     krb5_clear_error_string (context);
965     return HEIM_ERR_V4_PRINC_NO_CONV;
966 }
967 
968 static krb5_boolean
convert_func(krb5_context conxtext,void * funcctx,krb5_principal principal)969 convert_func(krb5_context conxtext, void *funcctx, krb5_principal principal)
970 {
971     krb5_boolean (*func)(krb5_context, krb5_principal) = funcctx;
972     return (*func)(conxtext, principal);
973 }
974 
975 krb5_error_code KRB5_LIB_FUNCTION
krb5_425_conv_principal_ext(krb5_context context,const char * name,const char * instance,const char * realm,krb5_boolean (* func)(krb5_context,krb5_principal),krb5_boolean resolve,krb5_principal * principal)976 krb5_425_conv_principal_ext(krb5_context context,
977 			    const char *name,
978 			    const char *instance,
979 			    const char *realm,
980 			    krb5_boolean (*func)(krb5_context, krb5_principal),
981 			    krb5_boolean resolve,
982 			    krb5_principal *principal)
983 {
984     return krb5_425_conv_principal_ext2(context,
985 					name,
986 					instance,
987 					realm,
988 					func ? convert_func : NULL,
989 					func,
990 					resolve,
991 					principal);
992 }
993 
994 
995 
996 krb5_error_code KRB5_LIB_FUNCTION
krb5_425_conv_principal(krb5_context context,const char * name,const char * instance,const char * realm,krb5_principal * princ)997 krb5_425_conv_principal(krb5_context context,
998 			const char *name,
999 			const char *instance,
1000 			const char *realm,
1001 			krb5_principal *princ)
1002 {
1003     krb5_boolean resolve = krb5_config_get_bool(context,
1004 						NULL,
1005 						"libdefaults",
1006 						"v4_instance_resolve",
1007 						NULL);
1008 
1009     return krb5_425_conv_principal_ext(context, name, instance, realm,
1010 				       NULL, resolve, princ);
1011 }
1012 
1013 
1014 static int
check_list(const krb5_config_binding * l,const char * name,const char ** out)1015 check_list(const krb5_config_binding *l, const char *name, const char **out)
1016 {
1017     while(l){
1018 	if (l->type != krb5_config_string)
1019 	    continue;
1020 	if(strcmp(name, l->u.string) == 0) {
1021 	    *out = l->name;
1022 	    return 1;
1023 	}
1024 	l = l->next;
1025     }
1026     return 0;
1027 }
1028 
1029 static int
name_convert(krb5_context context,const char * name,const char * realm,const char ** out)1030 name_convert(krb5_context context, const char *name, const char *realm,
1031 	     const char **out)
1032 {
1033     const krb5_config_binding *l;
1034     l = krb5_config_get_list (context,
1035 			      NULL,
1036 			      "realms",
1037 			      realm,
1038 			      "v4_name_convert",
1039 			      "host",
1040 			      NULL);
1041     if(l && check_list(l, name, out))
1042 	return KRB5_NT_SRV_HST;
1043     l = krb5_config_get_list (context,
1044 			      NULL,
1045 			      "libdefaults",
1046 			      "v4_name_convert",
1047 			      "host",
1048 			      NULL);
1049     if(l && check_list(l, name, out))
1050 	return KRB5_NT_SRV_HST;
1051     l = krb5_config_get_list (context,
1052 			      NULL,
1053 			      "realms",
1054 			      realm,
1055 			      "v4_name_convert",
1056 			      "plain",
1057 			      NULL);
1058     if(l && check_list(l, name, out))
1059 	return KRB5_NT_UNKNOWN;
1060     l = krb5_config_get_list (context,
1061 			      NULL,
1062 			      "libdefaults",
1063 			      "v4_name_convert",
1064 			      "host",
1065 			      NULL);
1066     if(l && check_list(l, name, out))
1067 	return KRB5_NT_UNKNOWN;
1068 
1069     /* didn't find it in config file, try built-in list */
1070     {
1071 	struct v4_name_convert *q;
1072 	for(q = default_v4_name_convert; q->from; q++) {
1073 	    if(strcmp(name, q->to) == 0) {
1074 		*out = q->from;
1075 		return KRB5_NT_SRV_HST;
1076 	    }
1077 	}
1078     }
1079     return -1;
1080 }
1081 
1082 /*
1083  * convert the v5 principal in `principal' into a v4 corresponding one
1084  * in `name, instance, realm'
1085  * this is limited interface since there's no length given for these
1086  * three parameters.  They have to be 40 bytes each (ANAME_SZ).
1087  */
1088 
1089 krb5_error_code KRB5_LIB_FUNCTION
krb5_524_conv_principal(krb5_context context,const krb5_principal principal,char * name,char * instance,char * realm)1090 krb5_524_conv_principal(krb5_context context,
1091 			const krb5_principal principal,
1092 			char *name,
1093 			char *instance,
1094 			char *realm)
1095 {
1096     const char *n, *i, *r;
1097     char tmpinst[40];
1098     int type = princ_type(principal);
1099     const int aname_sz = 40;
1100 
1101     r = principal->realm;
1102 
1103     switch(principal->name.name_string.len){
1104     case 1:
1105 	n = principal->name.name_string.val[0];
1106 	i = "";
1107 	break;
1108     case 2:
1109 	n = principal->name.name_string.val[0];
1110 	i = principal->name.name_string.val[1];
1111 	break;
1112     default:
1113 	krb5_set_error_string (context,
1114 			       "cannot convert a %d component principal",
1115 			       principal->name.name_string.len);
1116 	return KRB5_PARSE_MALFORMED;
1117     }
1118 
1119     {
1120 	const char *tmp;
1121 	int t = name_convert(context, n, r, &tmp);
1122 	if(t >= 0) {
1123 	    type = t;
1124 	    n = tmp;
1125 	}
1126     }
1127 
1128     if(type == KRB5_NT_SRV_HST){
1129 	char *p;
1130 
1131 	strlcpy (tmpinst, i, sizeof(tmpinst));
1132 	p = strchr(tmpinst, '.');
1133 	if(p)
1134 	    *p = 0;
1135 	i = tmpinst;
1136     }
1137 
1138     if (strlcpy (name, n, aname_sz) >= aname_sz) {
1139 	krb5_set_error_string (context,
1140 			       "too long name component to convert");
1141 	return KRB5_PARSE_MALFORMED;
1142     }
1143     if (strlcpy (instance, i, aname_sz) >= aname_sz) {
1144 	krb5_set_error_string (context,
1145 			       "too long instance component to convert");
1146 	return KRB5_PARSE_MALFORMED;
1147     }
1148     if (strlcpy (realm, r, aname_sz) >= aname_sz) {
1149 	krb5_set_error_string (context,
1150 			       "too long realm component to convert");
1151 	return KRB5_PARSE_MALFORMED;
1152     }
1153     return 0;
1154 }
1155 
1156 /*
1157  * Create a principal in `ret_princ' for the service `sname' running
1158  * on host `hostname'.  */
1159 
1160 krb5_error_code KRB5_LIB_FUNCTION
krb5_sname_to_principal(krb5_context context,const char * hostname,const char * sname,int32_t type,krb5_principal * ret_princ)1161 krb5_sname_to_principal (krb5_context context,
1162 			 const char *hostname,
1163 			 const char *sname,
1164 			 int32_t type,
1165 			 krb5_principal *ret_princ)
1166 {
1167     krb5_error_code ret;
1168     char localhost[MAXHOSTNAMELEN];
1169     char **realms, *host = NULL;
1170 
1171     if(type != KRB5_NT_SRV_HST && type != KRB5_NT_UNKNOWN) {
1172 	krb5_set_error_string (context, "unsupported name type %d",
1173 			       type);
1174 	return KRB5_SNAME_UNSUPP_NAMETYPE;
1175     }
1176     if(hostname == NULL) {
1177 	gethostname(localhost, sizeof(localhost));
1178 	hostname = localhost;
1179     }
1180     if(sname == NULL)
1181 	sname = "host";
1182     if(type == KRB5_NT_SRV_HST) {
1183 	ret = krb5_expand_hostname_realms (context, hostname,
1184 					   &host, &realms);
1185 	if (ret)
1186 	    return ret;
1187 	strlwr(host);
1188 	hostname = host;
1189     } else {
1190 	ret = krb5_get_host_realm(context, hostname, &realms);
1191 	if(ret)
1192 	    return ret;
1193     }
1194 
1195     ret = krb5_make_principal(context, ret_princ, realms[0], sname,
1196 			      hostname, NULL);
1197     if(host)
1198 	free(host);
1199     krb5_free_host_realm(context, realms);
1200     return ret;
1201 }
1202