changed `struct fd_set' to `fd_set'
[heimdal.git] / lib / krb5 / principal.c
blobf37cc9224cc0e2b72a83a7cc7a8bfe158ab21a5b
1 /*
2 * Copyright (c) 1997 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by Kungliga Tekniska
20 * Högskolan and its contributors.
22 * 4. Neither the name of the Institute nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
39 #include "krb5_locl.h"
41 RCSID("$Id$");
43 /* Public principal handling functions */
45 #define princ_num_comp(P) ((P)->name.name_string.len)
46 #define princ_type(P) ((P)->name.name_type)
47 #define princ_comp(P) ((P)->name.name_string.val)
48 #define princ_ncomp(P, N) ((P)->name.name_string.val[(N)])
49 #define princ_realm(P) ((P)->realm)
51 void
52 krb5_free_principal(krb5_context context,
53 krb5_principal p)
55 if(p){
56 free_Principal(p);
57 free(p);
61 krb5_error_code
62 krb5_parse_name(krb5_context context,
63 const char *name,
64 krb5_principal *principal)
67 general_string *comp;
68 general_string realm;
69 int ncomp;
71 char *p;
72 char *q;
73 char *s;
74 char *start;
76 int n;
77 char c;
78 int got_realm = 0;
80 /* count number of component */
81 ncomp = 1;
82 for(p = (char*)name; *p; p++){
83 if(*p=='\\'){
84 if(!p[1])
85 return KRB5_PARSE_MALFORMED;
86 p++;
87 } else if(*p == '/')
88 ncomp++;
90 comp = calloc(ncomp, sizeof(*comp));
92 n = 0;
93 start = q = p = s = strdup(name);
94 while(*p){
95 c = *p++;
96 if(c == '\\'){
97 c = *p++;
98 if(c == 'n')
99 c = '\n';
100 else if(c == 't')
101 c = '\t';
102 else if(c == 'b')
103 c = '\b';
104 else if(c == '0')
105 c = '\0';
106 }else if(c == '/' || c == '@'){
107 if(got_realm){
108 exit:
109 while(n>0){
110 free(comp[--n]);
112 free(comp);
113 free(s);
114 return KRB5_PARSE_MALFORMED;
115 }else{
116 comp[n] = malloc(q - start + 1);
117 strncpy(comp[n], start, q - start);
118 comp[n][q - start] = 0;
119 n++;
121 if(c == '@')
122 got_realm = 1;
123 start = q;
124 continue;
126 if(got_realm && (c == ':' || c == '/' || c == '\0'))
127 goto exit;
128 *q++ = c;
130 if(got_realm){
131 realm = malloc(q - start + 1);
132 strncpy(realm, start, q - start);
133 realm[q - start] = 0;
134 }else{
135 krb5_get_default_realm (context, &realm);
137 comp[n] = malloc(q - start + 1);
138 strncpy(comp[n], start, q - start);
139 comp[n][q - start] = 0;
140 n++;
142 *principal = malloc(sizeof(**principal));
143 (*principal)->name.name_type = KRB5_NT_PRINCIPAL;
144 (*principal)->name.name_string.val = comp;
145 princ_num_comp(*principal) = n;
146 (*principal)->realm = realm;
147 free(s);
148 return 0;
151 static const char quotable_chars[] = "\n\t\b\\/@";
152 static const char replace_chars[] = "ntb\\/@";
154 #define add_char(BASE, INDEX, LEN, C) do { if((INDEX) < (LEN)) (BASE)[(INDEX)++] = (C); }while(0);
156 static size_t
157 quote_string(const char *s, char *out, size_t index, size_t len)
159 const char *p, *q;
160 for(p = s; *p && index < len; p++){
161 if((q = strchr(quotable_chars, *p))){
162 add_char(out, index, len, '\\');
163 add_char(out, index, len, replace_chars[q - quotable_chars]);
164 }else
165 add_char(out, index, len, *p);
167 if(index < len)
168 out[index] = '\0';
169 return index;
173 krb5_error_code
174 krb5_unparse_name_fixed(krb5_context context,
175 krb5_const_principal principal,
176 char *name,
177 size_t len)
179 size_t index = 0;
180 int i;
181 for(i = 0; i < princ_num_comp(principal); i++){
182 if(i)
183 add_char(name, index, len, '/');
184 index = quote_string(princ_ncomp(principal, i), name, index, len);
186 add_char(name, index, len, '@');
187 index = quote_string(princ_realm(principal), name, index, len);
188 if(index == len)
189 return ENOMEM; /* XXX */
190 return 0;
193 krb5_error_code
194 krb5_unparse_name(krb5_context context,
195 krb5_const_principal principal,
196 char **name)
198 size_t len = 0, plen;
199 int i;
200 krb5_error_code ret;
201 /* count length */
202 plen = strlen(princ_realm(principal));
203 if(strcspn(princ_realm(principal), quotable_chars) == plen)
204 len += plen;
205 else
206 len += 2*plen;
207 len++;
208 for(i = 0; i < princ_num_comp(principal); i++){
209 plen = strlen(princ_ncomp(principal, i));
210 if(strcspn(princ_ncomp(principal, i), quotable_chars) == plen)
211 len += plen;
212 else
213 len += 2*plen;
214 len++;
216 *name = malloc(len);
217 if(*name == NULL)
218 return ENOMEM;
219 ret = krb5_unparse_name_fixed(context, principal, *name, len);
220 if(ret)
221 free(*name);
222 return ret;
225 #if 0 /* not implemented */
227 krb5_error_code
228 krb5_unparse_name_ext(krb5_context context,
229 krb5_const_principal principal,
230 char **name,
231 size_t *size)
233 fprintf(stderr, "krb5_unparse_name_ext: not implemented\n");
234 abort();
237 #endif
239 krb5_realm*
240 krb5_princ_realm(krb5_context context,
241 krb5_principal principal)
243 return &princ_realm(principal);
247 void
248 krb5_princ_set_realm(krb5_context context,
249 krb5_principal principal,
250 krb5_realm *realm)
252 princ_realm(principal) = *realm;
256 krb5_error_code
257 krb5_build_principal(krb5_context context,
258 krb5_principal *principal,
259 int rlen,
260 krb5_const_realm realm,
261 ...)
263 krb5_error_code ret;
264 va_list ap;
265 va_start(ap, realm);
266 ret = krb5_build_principal_va(context, principal, rlen, realm, ap);
267 va_end(ap);
268 return ret;
271 static krb5_error_code
272 append_component(krb5_context context, krb5_principal p,
273 general_string comp,
274 size_t comp_len)
276 general_string *tmp;
277 size_t len = princ_num_comp(p);
278 tmp = realloc(princ_comp(p), (len + 1) * sizeof(*tmp));
279 if(tmp == NULL)
280 return ENOMEM;
281 princ_comp(p) = tmp;
282 princ_ncomp(p, len) = malloc(comp_len + 1);
283 memcpy (princ_ncomp(p, len), comp, comp_len);
284 princ_ncomp(p, len)[comp_len] = '\0';
285 princ_num_comp(p)++;
286 return 0;
289 static void
290 va_ext_princ(krb5_context context, krb5_principal p, va_list ap)
292 while(1){
293 char *s;
294 int len;
295 len = va_arg(ap, int);
296 if(len == 0)
297 break;
298 s = va_arg(ap, char*);
299 append_component(context, p, s, len);
303 static void
304 va_princ(krb5_context context, krb5_principal p, va_list ap)
306 while(1){
307 char *s;
308 s = va_arg(ap, char*);
309 if(s == NULL)
310 break;
311 append_component(context, p, s, strlen(s));
316 static krb5_error_code
317 build_principal(krb5_context context,
318 krb5_principal *principal,
319 int rlen,
320 krb5_const_realm realm,
321 void (*func)(krb5_context, krb5_principal, va_list),
322 va_list ap)
324 krb5_principal p;
326 p = calloc(1, sizeof(*p));
327 if (p == NULL)
328 return ENOMEM;
329 princ_type(p) = KRB5_NT_PRINCIPAL;
331 princ_realm(p) = strdup(realm);
332 if(p->realm == NULL){
333 free(p);
334 return ENOMEM;
337 (*func)(context, p, ap);
338 *principal = p;
339 return 0;
342 krb5_error_code
343 krb5_make_principal(krb5_context context,
344 krb5_principal *principal,
345 krb5_const_realm realm,
346 ...)
348 krb5_error_code ret;
349 krb5_realm r = NULL;
350 va_list ap;
351 if(realm == NULL){
352 ret = krb5_get_default_realm(context, &r);
353 if(ret)
354 return ret;
355 realm = r;
357 va_start(ap, realm);
358 ret = krb5_build_principal_va(context, principal, strlen(realm), realm, ap);
359 va_end(ap);
360 if(r)
361 free(r);
362 return ret;
365 krb5_error_code
366 krb5_build_principal_va(krb5_context context,
367 krb5_principal *principal,
368 int rlen,
369 krb5_const_realm realm,
370 va_list ap)
372 return build_principal(context, principal, rlen, realm, va_princ, ap);
375 krb5_error_code
376 krb5_build_principal_va_ext(krb5_context context,
377 krb5_principal *principal,
378 int rlen,
379 krb5_const_realm realm,
380 va_list ap)
382 return build_principal(context, principal, rlen, realm, va_ext_princ, ap);
386 krb5_error_code
387 krb5_build_principal_ext(krb5_context context,
388 krb5_principal *principal,
389 int rlen,
390 krb5_const_realm realm,
391 ...)
393 krb5_error_code ret;
394 va_list ap;
395 va_start(ap, realm);
396 ret = krb5_build_principal_va_ext(context, principal, rlen, realm, ap);
397 va_end(ap);
398 return ret;
402 krb5_error_code
403 krb5_copy_principal(krb5_context context,
404 krb5_const_principal inprinc,
405 krb5_principal *outprinc)
407 krb5_principal p = malloc(sizeof(*p));
408 if (p == NULL)
409 return ENOMEM;
410 if(copy_Principal(inprinc, p))
411 return ENOMEM;
412 *outprinc = p;
413 return 0;
417 krb5_boolean
418 krb5_principal_compare_any_realm(krb5_context context,
419 krb5_const_principal princ1,
420 krb5_const_principal princ2)
422 int i;
423 if(princ_num_comp(princ1) != princ_num_comp(princ2))
424 return FALSE;
425 for(i = 0; i < princ_num_comp(princ1); i++){
426 if(strcmp(princ_ncomp(princ1, i), princ_ncomp(princ2, i)) != 0)
427 return FALSE;
429 return TRUE;
432 krb5_boolean
433 krb5_principal_compare(krb5_context context,
434 krb5_const_principal princ1,
435 krb5_const_principal princ2)
437 if(!krb5_realm_compare(context, princ1, princ2))
438 return FALSE;
439 return krb5_principal_compare_any_realm(context, princ1, princ2);
443 krb5_boolean
444 krb5_realm_compare(krb5_context context,
445 krb5_const_principal princ1,
446 krb5_const_principal princ2)
448 return strcmp(princ_realm(princ1), princ_realm(princ2)) == 0;
451 krb5_error_code
452 krb5_425_conv_principal_ext(krb5_context context,
453 const char *name,
454 const char *instance,
455 const char *realm,
456 krb5_boolean (*func)(krb5_context, krb5_principal),
457 krb5_boolean resolve,
458 krb5_principal *princ)
460 const char *p;
461 krb5_error_code ret;
462 krb5_principal pr;
463 char host[128];
465 /* do the following: if the name is found in the
466 `v4_name_convert:host' part, is is assumed to be a `host' type
467 principal, and the instance is looked up in the
468 `v4_instance_convert' part. if not found there the name is
469 (optionally) looked up as a hostname, and if that doesn't yield
470 anything, the `default_domain' is appended to the instance
473 if(instance == NULL)
474 goto no_host;
475 if(instance[0] == 0){
476 instance = NULL;
477 goto no_host;
479 p = krb5_config_get_string(context->cf, "realms", realm,
480 "v4_name_convert", "host", name, NULL);
481 if(p == NULL)
482 p = krb5_config_get_string(context->cf, "libdefaults",
483 "v4_name_convert", "host", name, NULL);
484 if(p == NULL)
485 goto no_host;
486 name = p;
487 p = krb5_config_get_string(context->cf, "realms", realm,
488 "v4_instance_convert", instance, NULL);
489 if(p){
490 instance = p;
491 ret = krb5_make_principal(context, &pr, realm, name, instance, NULL);
492 if(func == NULL || (*func)(context, pr)){
493 *princ = pr;
494 return 0;
496 krb5_free_principal(context, pr);
497 *princ = NULL;
498 return HEIM_ERR_V4_PRINC_NO_CONV;
500 if(resolve){
501 struct hostent *hp = roken_gethostbyname(instance);
502 if(hp){
503 instance = hp->h_name;
504 ret = krb5_make_principal(context, &pr,
505 realm, name, instance, NULL);
506 if(func == NULL || (*func)(context, pr)){
507 *princ = pr;
508 return 0;
510 krb5_free_principal(context, pr);
514 char **domains, **d;
515 domains = krb5_config_get_strings(context->cf, "realms", realm,
516 "v4_domains", NULL);
517 for(d = domains; d && *d; d++){
518 snprintf(host, sizeof(host), "%s.%s", instance, *d);
519 ret = krb5_make_principal(context, &pr, realm, name, host, NULL);
520 if(func == NULL || (*func)(context, pr)){
521 *princ = pr;
522 krb5_config_free_strings(domains);
523 return 0;
525 krb5_free_principal(context, pr);
527 krb5_config_free_strings(domains);
531 p = krb5_config_get_string(context->cf, "realms", realm,
532 "default_domain", NULL);
533 if(p == NULL){
534 /* should this be an error or should it silently
535 succeed? */
536 return HEIM_ERR_V4_PRINC_NO_CONV;
539 snprintf(host, sizeof(host), "%s.%s", instance, p);
540 ret = krb5_make_principal(context, &pr, realm, name, host, NULL);
541 if(func == NULL || (*func)(context, pr)){
542 *princ = pr;
543 return 0;
545 krb5_free_principal(context, pr);
546 return HEIM_ERR_V4_PRINC_NO_CONV;
547 no_host:
548 p = krb5_config_get_string(context->cf,
549 "realms",
550 realm,
551 "v4_name_convert",
552 "plain",
553 name,
554 NULL);
555 if(p == NULL)
556 p = krb5_config_get_string(context->cf,
557 "libdefaults",
558 "v4_name_convert",
559 "plain",
560 name,
561 NULL);
562 if(p)
563 name = p;
565 ret = krb5_make_principal(context, &pr, realm, name, instance, NULL);
566 if(func == NULL || (*func)(context, pr)){
567 *princ = pr;
568 return 0;
570 krb5_free_principal(context, pr);
571 return HEIM_ERR_V4_PRINC_NO_CONV;
574 krb5_error_code
575 krb5_425_conv_principal(krb5_context context,
576 const char *name,
577 const char *instance,
578 const char *realm,
579 krb5_principal *princ)
581 krb5_boolean resolve = krb5_config_get_bool(context->cf,
582 "libdefaults",
583 "v4_instance_resolve",
584 NULL);
586 return krb5_425_conv_principal_ext(context, name, instance, realm,
587 NULL, resolve, princ);
591 static char*
592 name_convert(krb5_context context, const char *name, const char *realm,
593 const char *section)
595 const krb5_config_binding *l;
596 l = krb5_config_get_list (context->cf,
597 "realms",
598 realm,
599 "v4_name_convert",
600 section,
601 NULL);
602 if(l == NULL)
603 l = krb5_config_get_list (context->cf,
604 "libdefaults",
605 "v4_name_convert",
606 section,
607 NULL);
609 while(l){
610 if (l->type != krb5_config_string)
611 continue;
612 if(strcmp(name, l->u.string) == 0)
613 return l->name;
614 l = l->next;
616 return NULL;
619 krb5_error_code
620 krb5_524_conv_principal(krb5_context context,
621 const krb5_principal principal,
622 char *name,
623 char *instance,
624 char *realm)
626 char *n, *i, *r;
627 char tmpinst[40];
628 int type = princ_type(principal);
630 r = principal->realm;
632 switch(principal->name.name_string.len){
633 case 1:
634 n = principal->name.name_string.val[0];
635 i = "";
636 break;
637 case 2:
638 n = principal->name.name_string.val[0];
639 i = principal->name.name_string.val[1];
640 break;
641 default:
642 return KRB5_PARSE_MALFORMED;
646 char *tmp = name_convert(context, n, r, "host");
647 if(tmp){
648 type = KRB5_NT_SRV_HST;
649 n = tmp;
650 }else{
651 tmp = name_convert(context, n, r, "plain");
652 if(tmp){
653 type = KRB5_NT_UNKNOWN;
654 n = tmp;
659 if(type == KRB5_NT_SRV_HST){
660 char *p;
661 strncpy(tmpinst, i, sizeof(tmpinst));
662 tmpinst[sizeof(tmpinst) - 1] = 0;
663 p = strchr(tmpinst, '.');
664 if(p) *p = 0;
665 i = tmpinst;
668 if(strlen(r) >= 40)
669 return KRB5_PARSE_MALFORMED;
670 if(strlen(n) >= 40)
671 return KRB5_PARSE_MALFORMED;
672 if(strlen(i) >= 40)
673 return KRB5_PARSE_MALFORMED;
674 strcpy(realm, r);
675 strcpy(name, n);
676 strcpy(instance, i);
677 return 0;
682 krb5_error_code
683 krb5_sname_to_principal (krb5_context context,
684 const char *hostname,
685 const char *sname,
686 int32_t type,
687 krb5_principal *ret_princ)
689 krb5_error_code ret;
690 char localhost[128];
691 char **realms, *host = NULL;
693 if(type != KRB5_NT_SRV_HST && type != KRB5_NT_UNKNOWN)
694 return KRB5_SNAME_UNSUPP_NAMETYPE;
695 if(hostname == NULL){
696 gethostname(localhost, sizeof(localhost));
697 hostname = localhost;
699 if(sname == NULL)
700 sname = "host";
701 if(type == KRB5_NT_SRV_HST){
702 struct hostent *hp;
703 hp = roken_gethostbyname(hostname);
704 if(hp != NULL)
705 hostname = hp->h_name;
707 if(type == KRB5_NT_SRV_HST){
708 host = strdup(hostname);
709 if(host == NULL){
710 return ENOMEM;
712 strlwr(host);
713 hostname = host;
715 ret = krb5_get_host_realm(context, hostname, &realms);
716 if(ret)
717 return ret;
718 ret = krb5_make_principal(context, ret_princ, realms[0], sname,
719 hostname, NULL);
720 if(host)
721 free(host);
722 krb5_free_host_realm(context, realms);
723 return ret;