Line data Source code
1 : /*
2 : * Copyright (c) 1997-2007 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 : /**
35 : * @page krb5_principal_intro The principal handing functions.
36 : *
37 : * A Kerberos principal is a email address looking string that
38 : * contains two parts separated by @. The second part is the kerberos
39 : * realm the principal belongs to and the first is a list of 0 or
40 : * more components. For example
41 : * @verbatim
42 : lha@SU.SE
43 : host/hummel.it.su.se@SU.SE
44 : host/admin@H5L.ORG
45 : @endverbatim
46 : *
47 : * See the library functions here: @ref krb5_principal
48 : */
49 :
50 : #include "krb5_locl.h"
51 : #ifdef HAVE_RES_SEARCH
52 : #define USE_RESOLVER
53 : #endif
54 : #ifdef HAVE_ARPA_NAMESER_H
55 : #include <arpa/nameser.h>
56 : #endif
57 : #include <fnmatch.h>
58 : #include "resolve.h"
59 :
60 : #define princ_num_comp(P) ((P)->name.name_string.len)
61 : #define princ_type(P) ((P)->name.name_type)
62 : #define princ_comp(P) ((P)->name.name_string.val)
63 : #define princ_ncomp(P, N) ((P)->name.name_string.val[(N)])
64 : #define princ_realm(P) ((P)->realm)
65 :
66 : static krb5_error_code
67 1032682 : set_default_princ_type(krb5_principal p, NAME_TYPE defnt)
68 : {
69 1032682 : if (princ_num_comp(p) > 1 && strcmp(princ_ncomp(p, 0), KRB5_TGS_NAME) == 0)
70 145256 : princ_type(p) = KRB5_NT_SRV_INST;
71 887426 : else if (princ_num_comp(p) > 1 && strcmp(princ_ncomp(p, 0), "host") == 0)
72 38645 : princ_type(p) = KRB5_NT_SRV_HST;
73 848781 : else if (princ_num_comp(p) > 1 && strcmp(princ_ncomp(p, 0), "kca_service") == 0)
74 0 : princ_type(p) = KRB5_NT_SRV_HST;
75 1260681 : else if (princ_num_comp(p) == 2 &&
76 411900 : strcmp(princ_ncomp(p, 0), KRB5_WELLKNOWN_NAME) == 0)
77 2662 : princ_type(p) = KRB5_NT_WELLKNOWN;
78 846119 : else if (princ_num_comp(p) == 1 && strchr(princ_ncomp(p, 0), '@') != NULL)
79 301 : princ_type(p) = KRB5_NT_SMTP_NAME;
80 : else
81 845818 : princ_type(p) = defnt;
82 1032682 : return 0;
83 : }
84 :
85 : static krb5_error_code append_component(krb5_context, krb5_principal,
86 : const char *, size_t);
87 :
88 : /**
89 : * Frees a Kerberos principal allocated by the library with
90 : * krb5_parse_name(), krb5_make_principal() or any other related
91 : * principal functions.
92 : *
93 : * @param context A Kerberos context.
94 : * @param p a principal to free.
95 : *
96 : * @return An krb5 error code, see krb5_get_error_message().
97 : *
98 : * @ingroup krb5_principal
99 : */
100 :
101 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
102 8501899 : krb5_free_principal(krb5_context context,
103 : krb5_principal p)
104 : {
105 8501899 : if(p){
106 7158101 : if (p->nameattrs && p->nameattrs->pac)
107 1224966 : heim_release(p->nameattrs->pac);
108 7158101 : free_Principal(p);
109 7158101 : free(p);
110 : }
111 8501899 : }
112 :
113 : /**
114 : * Set the type of the principal
115 : *
116 : * @param context A Kerberos context.
117 : * @param principal principal to set the type for
118 : * @param type the new type
119 : *
120 : * @return An krb5 error code, see krb5_get_error_message().
121 : *
122 : * @ingroup krb5_principal
123 : */
124 :
125 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
126 53730 : krb5_principal_set_type(krb5_context context,
127 : krb5_principal principal,
128 : int type)
129 : {
130 53730 : princ_type(principal) = type;
131 53730 : }
132 :
133 : /**
134 : * Get the type of the principal
135 : *
136 : * @param context A Kerberos context.
137 : * @param principal principal to get the type for
138 : *
139 : * @return the type of principal
140 : *
141 : * @ingroup krb5_principal
142 : */
143 :
144 : KRB5_LIB_FUNCTION int KRB5_LIB_CALL
145 411892 : krb5_principal_get_type(krb5_context context,
146 : krb5_const_principal principal)
147 : {
148 411892 : return princ_type(principal);
149 : }
150 :
151 : /**
152 : * Get the realm of the principal
153 : *
154 : * @param context A Kerberos context.
155 : * @param principal principal to get the realm for
156 : *
157 : * @return realm of the principal, don't free or use after krb5_principal is freed
158 : *
159 : * @ingroup krb5_principal
160 : */
161 :
162 : KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
163 859159 : krb5_principal_get_realm(krb5_context context,
164 : krb5_const_principal principal)
165 : {
166 859159 : return princ_realm(principal);
167 : }
168 :
169 : KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
170 1067848 : krb5_principal_get_comp_string(krb5_context context,
171 : krb5_const_principal principal,
172 : unsigned int component)
173 : {
174 1067848 : if(component >= princ_num_comp(principal))
175 72494 : return NULL;
176 995354 : return princ_ncomp(principal, component);
177 : }
178 :
179 : /**
180 : * Get number of component is principal.
181 : *
182 : * @param context Kerberos 5 context
183 : * @param principal principal to query
184 : *
185 : * @return number of components in string
186 : *
187 : * @ingroup krb5_principal
188 : */
189 :
190 : KRB5_LIB_FUNCTION unsigned int KRB5_LIB_CALL
191 958571 : krb5_principal_get_num_comp(krb5_context context,
192 : krb5_const_principal principal)
193 : {
194 958571 : return princ_num_comp(principal);
195 : }
196 :
197 : /**
198 : * Parse a name into a krb5_principal structure, flags controls the behavior.
199 : *
200 : * @param context Kerberos 5 context
201 : * @param name name to parse into a Kerberos principal
202 : * @param flags flags to control the behavior
203 : * @param principal returned principal, free with krb5_free_principal().
204 : *
205 : * @return An krb5 error code, see krb5_get_error_message().
206 : *
207 : * @ingroup krb5_principal
208 : */
209 :
210 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
211 527242 : krb5_parse_name_flags(krb5_context context,
212 : const char *name,
213 : int flags,
214 : krb5_principal *principal)
215 : {
216 : krb5_error_code ret;
217 : heim_general_string *comp;
218 527242 : heim_general_string realm = NULL;
219 : int ncomp;
220 :
221 : const char *p;
222 : char *q;
223 : char *s;
224 : char *start;
225 :
226 : int n;
227 : char c;
228 527242 : int got_realm = 0;
229 527242 : int first_at = 1;
230 527242 : int no_realm = flags & KRB5_PRINCIPAL_PARSE_NO_REALM;
231 527242 : int require_realm = flags & KRB5_PRINCIPAL_PARSE_REQUIRE_REALM;
232 527242 : int enterprise = flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE;
233 527242 : int ignore_realm = flags & KRB5_PRINCIPAL_PARSE_IGNORE_REALM;
234 527242 : int no_def_realm = flags & KRB5_PRINCIPAL_PARSE_NO_DEF_REALM;
235 :
236 527242 : *principal = NULL;
237 :
238 527242 : if (no_realm && require_realm) {
239 0 : krb5_set_error_message(context, EINVAL,
240 0 : N_("Can't require both realm and "
241 : "no realm at the same time", ""));
242 0 : return EINVAL;
243 : }
244 :
245 : /* count number of component,
246 : * enterprise names only have one component
247 : */
248 527242 : ncomp = 1;
249 527242 : if (!enterprise) {
250 5634492 : for (p = name; *p; p++) {
251 5508015 : if (*p=='\\') {
252 2641 : if (!p[1]) {
253 0 : krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
254 0 : N_("trailing \\ in principal name", ""));
255 0 : return KRB5_PARSE_MALFORMED;
256 : }
257 2641 : p++;
258 5505374 : } else if (*p == '/')
259 65774 : ncomp++;
260 5439600 : else if (*p == '@')
261 318630 : break;
262 : }
263 : }
264 527242 : comp = calloc(ncomp, sizeof(*comp));
265 527242 : if (comp == NULL)
266 0 : return krb5_enomem(context);
267 :
268 527242 : n = 0;
269 527242 : p = start = q = s = strdup(name);
270 527242 : if (start == NULL) {
271 0 : free(comp);
272 0 : return krb5_enomem(context);
273 : }
274 16163897 : while (*p) {
275 15109414 : c = *p++;
276 15109414 : if (c == '\\') {
277 2641 : c = *p++;
278 2641 : if (c == 'n')
279 0 : c = '\n';
280 2641 : else if (c == 't')
281 0 : c = '\t';
282 2641 : else if (c == 'b')
283 0 : c = '\b';
284 2641 : else if (c == '0') {
285 : /*
286 : * We'll ignore trailing embedded NULs in components and
287 : * realms, but can't support any other embedded NULs.
288 : */
289 0 : while (*p) {
290 0 : if ((*p == '/' || *p == '@') && !got_realm)
291 0 : break;
292 0 : if (*(p++) != '\\' || *(p++) != '0') {
293 0 : ret = KRB5_PARSE_MALFORMED;
294 0 : krb5_set_error_message(context, ret,
295 0 : N_("embedded NULs in principal "
296 : "name not supported", ""));
297 0 : goto exit;
298 : }
299 : }
300 0 : continue;
301 2641 : } else if (c == '\0') {
302 0 : ret = KRB5_PARSE_MALFORMED;
303 0 : krb5_set_error_message(context, ret,
304 0 : N_("trailing \\ in principal name", ""));
305 0 : goto exit;
306 : }
307 15106773 : } else if (enterprise && first_at) {
308 2268950 : if (c == '@')
309 82135 : first_at = 0;
310 13972298 : } else if ((c == '/' && !enterprise) || c == '@') {
311 384405 : if (got_realm) {
312 1 : ret = KRB5_PARSE_MALFORMED;
313 1 : krb5_set_error_message(context, ret,
314 1 : N_("part after realm in principal name", ""));
315 1 : goto exit;
316 : } else {
317 384404 : comp[n] = malloc(q - start + 1);
318 384404 : if (comp[n] == NULL) {
319 0 : ret = krb5_enomem(context);
320 0 : goto exit;
321 : }
322 384404 : memcpy(comp[n], start, q - start);
323 384404 : comp[n][q - start] = 0;
324 384404 : n++;
325 : }
326 384404 : if (c == '@')
327 318630 : got_realm = 1;
328 384404 : start = q;
329 384404 : continue;
330 : }
331 14725009 : if (got_realm && (c == '/' || c == '\0')) {
332 0 : ret = KRB5_PARSE_MALFORMED;
333 0 : krb5_set_error_message(context, ret,
334 0 : N_("part after realm in principal name", ""));
335 0 : goto exit;
336 : }
337 14725009 : *q++ = c;
338 : }
339 527241 : if (got_realm) {
340 318629 : if (no_realm) {
341 6 : ret = KRB5_PARSE_MALFORMED;
342 6 : krb5_set_error_message(context, ret,
343 6 : N_("realm found in 'short' principal "
344 : "expected to be without one", ""));
345 6 : goto exit;
346 : }
347 318623 : if (!ignore_realm) {
348 318623 : realm = malloc(q - start + 1);
349 318623 : if (realm == NULL) {
350 0 : ret = krb5_enomem(context);
351 0 : goto exit;
352 : }
353 318623 : memcpy(realm, start, q - start);
354 318623 : realm[q - start] = 0;
355 : }
356 : } else {
357 208612 : if (require_realm) {
358 2341 : ret = KRB5_PARSE_MALFORMED;
359 2341 : krb5_set_error_message(context, ret,
360 2341 : N_("realm NOT found in principal "
361 : "expected to be with one", ""));
362 2341 : goto exit;
363 206271 : } else if (no_realm || no_def_realm) {
364 185843 : realm = NULL;
365 : } else {
366 20428 : ret = krb5_get_default_realm(context, &realm);
367 20428 : if (ret)
368 0 : goto exit;
369 : }
370 :
371 206271 : comp[n] = malloc(q - start + 1);
372 206271 : if (comp[n] == NULL) {
373 0 : ret = krb5_enomem(context);
374 0 : goto exit;
375 : }
376 206271 : memcpy(comp[n], start, q - start);
377 206271 : comp[n][q - start] = 0;
378 206271 : n++;
379 : }
380 524894 : *principal = calloc(1, sizeof(**principal));
381 524894 : if (*principal == NULL) {
382 0 : ret = krb5_enomem(context);
383 0 : goto exit;
384 : }
385 524894 : (*principal)->name.name_string.val = comp;
386 524894 : princ_num_comp(*principal) = n;
387 524894 : (*principal)->realm = realm;
388 524894 : if (enterprise)
389 82135 : princ_type(*principal) = KRB5_NT_ENTERPRISE_PRINCIPAL;
390 : else
391 442759 : set_default_princ_type(*principal, KRB5_NT_PRINCIPAL);
392 524894 : free(s);
393 524894 : return 0;
394 2348 : exit:
395 5112 : while (n>0) {
396 416 : free(comp[--n]);
397 : }
398 2348 : free(comp);
399 2348 : krb5_free_default_realm(context, realm);
400 2348 : free(s);
401 2348 : return ret;
402 : }
403 :
404 : /**
405 : * Parse a name into a krb5_principal structure
406 : *
407 : * @param context Kerberos 5 context
408 : * @param name name to parse into a Kerberos principal
409 : * @param principal returned principal, free with krb5_free_principal().
410 : *
411 : * @return An krb5 error code, see krb5_get_error_message().
412 : *
413 : * @ingroup krb5_principal
414 : */
415 :
416 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
417 250105 : krb5_parse_name(krb5_context context,
418 : const char *name,
419 : krb5_principal *principal)
420 : {
421 250105 : return krb5_parse_name_flags(context, name, 0, principal);
422 : }
423 :
424 : static const char quotable_chars[] = " \n\t\b\\/@";
425 : static const char replace_chars[] = " ntb\\/@";
426 :
427 : #define add_char(BASE, INDEX, LEN, C) do { if((INDEX) < (LEN)) (BASE)[(INDEX)++] = (C); }while(0);
428 :
429 : static size_t
430 1680759 : quote_string(const char *s, char *out, size_t idx, size_t len, int display)
431 : {
432 : const char *p, *q;
433 26825603 : for(p = s; *p && idx < len; p++){
434 25144844 : q = strchr(quotable_chars, *p);
435 25144844 : if (q && display) {
436 6226 : add_char(out, idx, len, replace_chars[q - quotable_chars]);
437 25138618 : } else if (q) {
438 12407 : add_char(out, idx, len, '\\');
439 12407 : add_char(out, idx, len, replace_chars[q - quotable_chars]);
440 : }else
441 25126211 : add_char(out, idx, len, *p);
442 : }
443 1680759 : if(idx < len)
444 1680759 : out[idx] = '\0';
445 1680759 : return idx;
446 : }
447 :
448 :
449 : static krb5_error_code
450 883510 : unparse_name_fixed(krb5_context context,
451 : krb5_const_principal principal,
452 : char *name,
453 : size_t len,
454 : int flags)
455 : {
456 883510 : size_t idx = 0;
457 : size_t i;
458 883510 : int short_form = (flags & KRB5_PRINCIPAL_UNPARSE_SHORT) != 0;
459 883510 : int no_realm = (flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) != 0;
460 883510 : int display = (flags & KRB5_PRINCIPAL_UNPARSE_DISPLAY) != 0;
461 :
462 883510 : if (name == NULL) {
463 0 : krb5_set_error_message(context, EINVAL,
464 0 : N_("Invalid name buffer, "
465 : "can't unparse", ""));
466 0 : return EINVAL;
467 : }
468 :
469 883510 : if (len == 0) {
470 0 : krb5_set_error_message(context, ERANGE,
471 0 : N_("Invalid name buffer length, "
472 : "can't unparse", ""));
473 0 : return ERANGE;
474 : }
475 :
476 883510 : name[0] = '\0';
477 :
478 883510 : if (!no_realm && princ_realm(principal) == NULL) {
479 0 : krb5_set_error_message(context, ERANGE,
480 0 : N_("Realm missing from principal, "
481 : "can't unparse", ""));
482 0 : return ERANGE;
483 : }
484 :
485 1940180 : for(i = 0; i < princ_num_comp(principal); i++){
486 1056670 : if(i)
487 173160 : add_char(name, idx, len, '/');
488 1056670 : idx = quote_string(princ_ncomp(principal, i), name, idx, len, display);
489 1056670 : if(idx == len) {
490 0 : krb5_set_error_message(context, ERANGE,
491 0 : N_("Out of space printing principal", ""));
492 0 : return ERANGE;
493 : }
494 : }
495 : /* add realm if different from default realm */
496 883510 : if(short_form && !no_realm) {
497 : krb5_realm r;
498 : krb5_error_code ret;
499 3 : ret = krb5_get_default_realm(context, &r);
500 3 : if(ret)
501 0 : return ret;
502 3 : if(strcmp(princ_realm(principal), r) != 0)
503 0 : short_form = 0;
504 3 : krb5_free_default_realm(context, r);
505 : }
506 883510 : if(!short_form && !no_realm) {
507 624089 : add_char(name, idx, len, '@');
508 624089 : idx = quote_string(princ_realm(principal), name, idx, len, display);
509 624089 : if(idx == len) {
510 0 : krb5_set_error_message(context, ERANGE,
511 0 : N_("Out of space printing "
512 : "realm of principal", ""));
513 0 : return ERANGE;
514 : }
515 : }
516 883510 : return 0;
517 : }
518 :
519 : /**
520 : * Unparse the principal name to a fixed buffer
521 : *
522 : * @param context A Kerberos context.
523 : * @param principal principal to unparse
524 : * @param name buffer to write name to
525 : * @param len length of buffer
526 : *
527 : * @return An krb5 error code, see krb5_get_error_message().
528 : *
529 : * @ingroup krb5_principal
530 : */
531 :
532 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
533 4206 : krb5_unparse_name_fixed(krb5_context context,
534 : krb5_const_principal principal,
535 : char *name,
536 : size_t len)
537 : {
538 4206 : return unparse_name_fixed(context, principal, name, len, 0);
539 : }
540 :
541 : /**
542 : * Unparse the principal name to a fixed buffer. The realm is skipped
543 : * if its a default realm.
544 : *
545 : * @param context A Kerberos context.
546 : * @param principal principal to unparse
547 : * @param name buffer to write name to
548 : * @param len length of buffer
549 : *
550 : * @return An krb5 error code, see krb5_get_error_message().
551 : *
552 : * @ingroup krb5_principal
553 : */
554 :
555 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
556 0 : krb5_unparse_name_fixed_short(krb5_context context,
557 : krb5_const_principal principal,
558 : char *name,
559 : size_t len)
560 : {
561 0 : return unparse_name_fixed(context, principal, name, len,
562 : KRB5_PRINCIPAL_UNPARSE_SHORT);
563 : }
564 :
565 : /**
566 : * Unparse the principal name with unparse flags to a fixed buffer.
567 : *
568 : * @param context A Kerberos context.
569 : * @param principal principal to unparse
570 : * @param flags unparse flags
571 : * @param name buffer to write name to
572 : * @param len length of buffer
573 : *
574 : * @return An krb5 error code, see krb5_get_error_message().
575 : *
576 : * @ingroup krb5_principal
577 : */
578 :
579 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
580 0 : krb5_unparse_name_fixed_flags(krb5_context context,
581 : krb5_const_principal principal,
582 : int flags,
583 : char *name,
584 : size_t len)
585 : {
586 0 : return unparse_name_fixed(context, principal, name, len, flags);
587 : }
588 :
589 : static krb5_error_code
590 879304 : unparse_name(krb5_context context,
591 : krb5_const_principal principal,
592 : char **name,
593 : int flags)
594 : {
595 879304 : size_t len = 0, plen;
596 : size_t i;
597 : krb5_error_code ret;
598 : /* count length */
599 879304 : if (princ_realm(principal)) {
600 859913 : plen = strlen(princ_realm(principal));
601 :
602 859913 : if(strcspn(princ_realm(principal), quotable_chars) == plen)
603 859913 : len += plen;
604 : else
605 0 : len += 2*plen;
606 859913 : len++; /* '@' */
607 : }
608 1929844 : for(i = 0; i < princ_num_comp(principal); i++){
609 1050540 : plen = strlen(princ_ncomp(principal, i));
610 1050540 : if(strcspn(princ_ncomp(principal, i), quotable_chars) == plen)
611 1033435 : len += plen;
612 : else
613 17105 : len += 2*plen;
614 1050540 : len++;
615 : }
616 879304 : len++; /* '\0' */
617 879304 : *name = malloc(len);
618 879304 : if(*name == NULL)
619 0 : return krb5_enomem(context);
620 879304 : ret = unparse_name_fixed(context, principal, *name, len, flags);
621 879304 : if(ret) {
622 0 : free(*name);
623 0 : *name = NULL;
624 : }
625 879304 : return ret;
626 : }
627 :
628 : /**
629 : * Unparse the Kerberos name into a string
630 : *
631 : * @param context Kerberos 5 context
632 : * @param principal principal to query
633 : * @param name resulting string, free with krb5_xfree()
634 : *
635 : * @return An krb5 error code, see krb5_get_error_message().
636 : *
637 : * @ingroup krb5_principal
638 : */
639 :
640 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
641 488383 : krb5_unparse_name(krb5_context context,
642 : krb5_const_principal principal,
643 : char **name)
644 : {
645 488383 : return unparse_name(context, principal, name, 0);
646 : }
647 :
648 : /**
649 : * Unparse the Kerberos name into a string
650 : *
651 : * @param context Kerberos 5 context
652 : * @param principal principal to query
653 : * @param flags flag to determine the behavior
654 : * @param name resulting string, free with krb5_xfree()
655 : *
656 : * @return An krb5 error code, see krb5_get_error_message().
657 : *
658 : * @ingroup krb5_principal
659 : */
660 :
661 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
662 390918 : krb5_unparse_name_flags(krb5_context context,
663 : krb5_const_principal principal,
664 : int flags,
665 : char **name)
666 : {
667 390918 : return unparse_name(context, principal, name, flags);
668 : }
669 :
670 : /**
671 : * Unparse the principal name to a allocated buffer. The realm is
672 : * skipped if its a default realm.
673 : *
674 : * @param context A Kerberos context.
675 : * @param principal principal to unparse
676 : * @param name returned buffer, free with krb5_xfree()
677 : *
678 : * @return An krb5 error code, see krb5_get_error_message().
679 : *
680 : * @ingroup krb5_principal
681 : */
682 :
683 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
684 3 : krb5_unparse_name_short(krb5_context context,
685 : krb5_const_principal principal,
686 : char **name)
687 : {
688 3 : return unparse_name(context, principal, name, KRB5_PRINCIPAL_UNPARSE_SHORT);
689 : }
690 :
691 : /**
692 : * Set a new realm for a principal, and as a side-effect free the
693 : * previous realm.
694 : *
695 : * @param context A Kerberos context.
696 : * @param principal principal set the realm for
697 : * @param realm the new realm to set
698 : *
699 : * @return An krb5 error code, see krb5_get_error_message().
700 : *
701 : * @ingroup krb5_principal
702 : */
703 :
704 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
705 322344 : krb5_principal_set_realm(krb5_context context,
706 : krb5_principal principal,
707 : krb5_const_realm realm)
708 : {
709 322344 : if (princ_realm(principal))
710 159060 : free(princ_realm(principal));
711 :
712 322344 : if (realm == NULL)
713 0 : princ_realm(principal) = NULL;
714 322344 : else if ((princ_realm(principal) = strdup(realm)) == NULL)
715 0 : return krb5_enomem(context);
716 322344 : return 0;
717 : }
718 :
719 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
720 0 : krb5_principal_set_comp_string(krb5_context context,
721 : krb5_principal principal,
722 : unsigned int k,
723 : const char *component)
724 : {
725 : char *s;
726 : size_t i;
727 :
728 0 : for (i = princ_num_comp(principal); i <= k; i++)
729 0 : append_component(context, principal, "", 0);
730 0 : s = strdup(component);
731 0 : if (s == NULL)
732 0 : return krb5_enomem(context);
733 0 : free(princ_ncomp(principal, k));
734 0 : princ_ncomp(principal, k) = s;
735 0 : return 0;
736 : }
737 :
738 : #ifndef HEIMDAL_SMALLER
739 : /**
740 : * Build a principal using vararg style building
741 : *
742 : * @param context A Kerberos context.
743 : * @param principal returned principal
744 : * @param rlen length of realm
745 : * @param realm realm name
746 : * @param ... a list of components ended with NULL.
747 : *
748 : * @return An krb5 error code, see krb5_get_error_message().
749 : *
750 : * @ingroup krb5_principal
751 : */
752 :
753 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
754 2910 : krb5_build_principal(krb5_context context,
755 : krb5_principal *principal,
756 : int rlen,
757 : krb5_const_realm realm,
758 : ...)
759 : {
760 : krb5_error_code ret;
761 : va_list ap;
762 2910 : va_start(ap, realm);
763 2910 : ret = krb5_build_principal_va(context, principal, rlen, realm, ap);
764 2910 : va_end(ap);
765 2910 : return ret;
766 : }
767 : #endif
768 :
769 : /**
770 : * Build a principal using vararg style building
771 : *
772 : * @param context A Kerberos context.
773 : * @param principal returned principal
774 : * @param realm realm name
775 : * @param ... a list of components ended with NULL.
776 : *
777 : * @return An krb5 error code, see krb5_get_error_message().
778 : *
779 : * @ingroup krb5_principal
780 : */
781 :
782 : /* coverity[+alloc : arg-*1] */
783 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
784 573830 : krb5_make_principal(krb5_context context,
785 : krb5_principal *principal,
786 : krb5_const_realm realm,
787 : ...)
788 : {
789 : krb5_error_code ret;
790 573830 : krb5_realm r = NULL;
791 : va_list ap;
792 :
793 573830 : *principal = NULL;
794 :
795 573830 : if(realm == NULL) {
796 0 : ret = krb5_get_default_realm(context, &r);
797 0 : if(ret)
798 0 : return ret;
799 0 : realm = r;
800 : }
801 573830 : va_start(ap, realm);
802 573830 : ret = krb5_build_principal_va(context, principal, strlen(realm), realm, ap);
803 573830 : va_end(ap);
804 573830 : if(r)
805 0 : krb5_free_default_realm(context, r);
806 573830 : return ret;
807 : }
808 :
809 : static krb5_error_code
810 1159455 : append_component(krb5_context context, krb5_principal p,
811 : const char *comp,
812 : size_t comp_len)
813 : {
814 : heim_general_string *tmp;
815 1159455 : size_t len = princ_num_comp(p);
816 :
817 1159455 : tmp = realloc(princ_comp(p), (len + 1) * sizeof(*tmp));
818 1159455 : if(tmp == NULL)
819 0 : return krb5_enomem(context);
820 1159455 : princ_comp(p) = tmp;
821 1159455 : princ_ncomp(p, len) = malloc(comp_len + 1);
822 1159455 : if (princ_ncomp(p, len) == NULL)
823 0 : return krb5_enomem(context);
824 1159455 : memcpy (princ_ncomp(p, len), comp, comp_len);
825 1159455 : princ_ncomp(p, len)[comp_len] = '\0';
826 1159455 : princ_num_comp(p)++;
827 1159455 : return 0;
828 : }
829 :
830 : static krb5_error_code
831 13183 : va_ext_princ(krb5_context context, krb5_principal p, va_list ap)
832 : {
833 13183 : krb5_error_code ret = 0;
834 :
835 24582 : while (1){
836 : const char *s;
837 : int len;
838 :
839 37765 : if ((len = va_arg(ap, int)) == 0)
840 13183 : break;
841 24582 : s = va_arg(ap, const char*);
842 24582 : if ((ret = append_component(context, p, s, len)) != 0)
843 0 : break;
844 : }
845 13183 : return ret;
846 : }
847 :
848 : static krb5_error_code
849 576740 : va_princ(krb5_context context, krb5_principal p, va_list ap)
850 : {
851 576740 : krb5_error_code ret = 0;
852 :
853 1134873 : while (1){
854 : const char *s;
855 :
856 1711613 : if ((s = va_arg(ap, const char*)) == NULL)
857 576740 : break;
858 1134873 : if ((ret = append_component(context, p, s, strlen(s))) != 0)
859 0 : break;
860 : }
861 576740 : return ret;
862 : }
863 :
864 : static krb5_error_code
865 589923 : build_principal(krb5_context context,
866 : krb5_principal *principal,
867 : int rlen,
868 : krb5_const_realm realm,
869 : krb5_error_code (*func)(krb5_context, krb5_principal, va_list),
870 : va_list ap)
871 : {
872 : krb5_error_code ret;
873 : krb5_principal p;
874 :
875 589923 : *principal = NULL;
876 589923 : p = calloc(1, sizeof(*p));
877 589923 : if (p == NULL)
878 0 : return krb5_enomem(context);
879 :
880 589923 : princ_realm(p) = strdup(realm);
881 589923 : if (p->realm == NULL) {
882 0 : free(p);
883 0 : return krb5_enomem(context);
884 : }
885 :
886 589923 : ret = func(context, p, ap);
887 589923 : if (ret == 0) {
888 589923 : *principal = p;
889 589923 : set_default_princ_type(p, KRB5_NT_PRINCIPAL);
890 : } else
891 0 : krb5_free_principal(context, p);
892 589923 : return ret;
893 : }
894 :
895 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
896 576740 : krb5_build_principal_va(krb5_context context,
897 : krb5_principal *principal,
898 : int rlen,
899 : krb5_const_realm realm,
900 : va_list ap)
901 : {
902 576740 : return build_principal(context, principal, rlen, realm, va_princ, ap);
903 : }
904 :
905 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
906 13183 : krb5_build_principal_va_ext(krb5_context context,
907 : krb5_principal *principal,
908 : int rlen,
909 : krb5_const_realm realm,
910 : va_list ap)
911 : {
912 13183 : return build_principal(context, principal, rlen, realm, va_ext_princ, ap);
913 : }
914 :
915 :
916 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
917 13183 : krb5_build_principal_ext(krb5_context context,
918 : krb5_principal *principal,
919 : int rlen,
920 : krb5_const_realm realm,
921 : ...)
922 : {
923 : krb5_error_code ret;
924 : va_list ap;
925 13183 : va_start(ap, realm);
926 13183 : ret = krb5_build_principal_va_ext(context, principal, rlen, realm, ap);
927 13183 : va_end(ap);
928 13183 : return ret;
929 : }
930 :
931 : /**
932 : * Copy a principal
933 : *
934 : * @param context A Kerberos context.
935 : * @param inprinc principal to copy
936 : * @param outprinc copied principal, free with krb5_free_principal()
937 : *
938 : * @return An krb5 error code, see krb5_get_error_message().
939 : *
940 : * @ingroup krb5_principal
941 : */
942 :
943 :
944 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
945 5490482 : krb5_copy_principal(krb5_context context,
946 : krb5_const_principal inprinc,
947 : krb5_principal *outprinc)
948 : {
949 : krb5_principal p;
950 :
951 5490482 : *outprinc = NULL;
952 :
953 5490482 : p = malloc(sizeof(*p));
954 5490482 : if (p == NULL)
955 0 : return krb5_enomem(context);
956 5490482 : if(copy_Principal(inprinc, p)) {
957 0 : free(p);
958 0 : return krb5_enomem(context);
959 : }
960 5490482 : if (inprinc->nameattrs && inprinc->nameattrs->pac)
961 1209079 : p->nameattrs->pac = heim_retain(inprinc->nameattrs->pac);
962 :
963 5490482 : *outprinc = p;
964 5490482 : return 0;
965 : }
966 :
967 : /**
968 : * Return TRUE iff princ1 == princ2 (without considering the realm)
969 : *
970 : * @param context Kerberos 5 context
971 : * @param princ1 first principal to compare
972 : * @param princ2 second principal to compare
973 : *
974 : * @return non zero if equal, 0 if not
975 : *
976 : * @ingroup krb5_principal
977 : * @see krb5_principal_compare()
978 : * @see krb5_realm_compare()
979 : */
980 :
981 : KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
982 1137997 : krb5_principal_compare_any_realm(krb5_context context,
983 : krb5_const_principal princ1,
984 : krb5_const_principal princ2)
985 : {
986 : size_t i;
987 1137997 : if(princ_num_comp(princ1) != princ_num_comp(princ2))
988 139248 : return FALSE;
989 2103620 : for(i = 0; i < princ_num_comp(princ1); i++){
990 1456486 : if(strcmp(princ_ncomp(princ1, i), princ_ncomp(princ2, i)) != 0)
991 351615 : return FALSE;
992 : }
993 647134 : return TRUE;
994 : }
995 :
996 : KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
997 576 : _krb5_principal_compare_PrincipalName(krb5_context context,
998 : krb5_const_principal princ1,
999 : PrincipalName *princ2)
1000 : {
1001 : size_t i;
1002 576 : if (princ_num_comp(princ1) != princ2->name_string.len)
1003 0 : return FALSE;
1004 2016 : for(i = 0; i < princ_num_comp(princ1); i++){
1005 1440 : if(strcmp(princ_ncomp(princ1, i), princ2->name_string.val[i]) != 0)
1006 0 : return FALSE;
1007 : }
1008 576 : return TRUE;
1009 : }
1010 :
1011 :
1012 : /**
1013 : * Compares the two principals, including realm of the principals and returns
1014 : * TRUE if they are the same and FALSE if not.
1015 : *
1016 : * @param context Kerberos 5 context
1017 : * @param princ1 first principal to compare
1018 : * @param princ2 second principal to compare
1019 : *
1020 : * @ingroup krb5_principal
1021 : * @see krb5_principal_compare_any_realm()
1022 : * @see krb5_realm_compare()
1023 : */
1024 :
1025 : /*
1026 : * return TRUE iff princ1 == princ2
1027 : */
1028 :
1029 : KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1030 1530125 : krb5_principal_compare(krb5_context context,
1031 : krb5_const_principal princ1,
1032 : krb5_const_principal princ2)
1033 : {
1034 1530125 : if (!krb5_realm_compare(context, princ1, princ2))
1035 439302 : return FALSE;
1036 1090823 : return krb5_principal_compare_any_realm(context, princ1, princ2);
1037 : }
1038 :
1039 : /**
1040 : * return TRUE iff realm(princ1) == realm(princ2)
1041 : *
1042 : * @param context Kerberos 5 context
1043 : * @param princ1 first principal to compare
1044 : * @param princ2 second principal to compare
1045 : *
1046 : * @ingroup krb5_principal
1047 : * @see krb5_principal_compare_any_realm()
1048 : * @see krb5_principal_compare()
1049 : */
1050 :
1051 : KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1052 1553876 : krb5_realm_compare(krb5_context context,
1053 : krb5_const_principal princ1,
1054 : krb5_const_principal princ2)
1055 : {
1056 1553876 : return strcmp(princ_realm(princ1), princ_realm(princ2)) == 0;
1057 : }
1058 :
1059 : /**
1060 : * return TRUE iff princ matches pattern
1061 : *
1062 : * @ingroup krb5_principal
1063 : */
1064 :
1065 : KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1066 0 : krb5_principal_match(krb5_context context,
1067 : krb5_const_principal princ,
1068 : krb5_const_principal pattern)
1069 : {
1070 : size_t i;
1071 0 : if(princ_num_comp(princ) != princ_num_comp(pattern))
1072 0 : return FALSE;
1073 0 : if(fnmatch(princ_realm(pattern), princ_realm(princ), 0) != 0)
1074 0 : return FALSE;
1075 0 : for(i = 0; i < princ_num_comp(princ); i++){
1076 0 : if(fnmatch(princ_ncomp(pattern, i), princ_ncomp(princ, i), 0) != 0)
1077 0 : return FALSE;
1078 : }
1079 0 : return TRUE;
1080 : }
1081 :
1082 : /*
1083 : * This is the original krb5_sname_to_principal(), renamed to be a
1084 : * helper of the new one.
1085 : */
1086 : static krb5_error_code
1087 0 : krb5_sname_to_principal_old(krb5_context context,
1088 : const char *realm,
1089 : const char *hostname,
1090 : const char *sname,
1091 : int32_t type,
1092 : krb5_principal *ret_princ)
1093 : {
1094 : krb5_error_code ret;
1095 : char localhost[MAXHOSTNAMELEN];
1096 0 : char **realms = NULL, *host = NULL;
1097 :
1098 0 : if(type != KRB5_NT_SRV_HST && type != KRB5_NT_UNKNOWN) {
1099 0 : krb5_set_error_message(context, KRB5_SNAME_UNSUPP_NAMETYPE,
1100 0 : N_("unsupported name type %d", ""),
1101 : (int)type);
1102 0 : return KRB5_SNAME_UNSUPP_NAMETYPE;
1103 : }
1104 0 : if(hostname == NULL) {
1105 0 : ret = gethostname(localhost, sizeof(localhost) - 1);
1106 0 : if (ret != 0) {
1107 0 : ret = errno;
1108 0 : krb5_set_error_message(context, ret,
1109 0 : N_("Failed to get local hostname", ""));
1110 0 : return ret;
1111 : }
1112 0 : localhost[sizeof(localhost) - 1] = '\0';
1113 0 : hostname = localhost;
1114 : }
1115 0 : if(sname == NULL)
1116 0 : sname = "host";
1117 0 : if(type == KRB5_NT_SRV_HST) {
1118 0 : if (realm)
1119 0 : ret = krb5_expand_hostname(context, hostname, &host);
1120 : else
1121 0 : ret = krb5_expand_hostname_realms(context, hostname,
1122 : &host, &realms);
1123 0 : if (ret)
1124 0 : return ret;
1125 0 : strlwr(host);
1126 0 : hostname = host;
1127 0 : if (!realm)
1128 0 : realm = realms[0];
1129 0 : } else if (!realm) {
1130 0 : ret = krb5_get_host_realm(context, hostname, &realms);
1131 0 : if(ret)
1132 0 : return ret;
1133 0 : realm = realms[0];
1134 : }
1135 :
1136 0 : ret = krb5_make_principal(context, ret_princ, realm, sname,
1137 : hostname, NULL);
1138 0 : if(host)
1139 0 : free(host);
1140 0 : if (realms)
1141 0 : krb5_free_host_realm(context, realms);
1142 0 : return ret;
1143 : }
1144 :
1145 : static const struct {
1146 : const char *type;
1147 : int32_t value;
1148 : } nametypes[] = {
1149 : { "UNKNOWN", KRB5_NT_UNKNOWN },
1150 : { "PRINCIPAL", KRB5_NT_PRINCIPAL },
1151 : { "SRV_INST", KRB5_NT_SRV_INST },
1152 : { "SRV_HST", KRB5_NT_SRV_HST },
1153 : { "SRV_XHST", KRB5_NT_SRV_XHST },
1154 : { "UID", KRB5_NT_UID },
1155 : { "X500_PRINCIPAL", KRB5_NT_X500_PRINCIPAL },
1156 : { "SMTP_NAME", KRB5_NT_SMTP_NAME },
1157 : { "ENTERPRISE_PRINCIPAL", KRB5_NT_ENTERPRISE_PRINCIPAL },
1158 : { "WELLKNOWN", KRB5_NT_WELLKNOWN },
1159 : { "SRV_HST_DOMAIN", KRB5_NT_SRV_HST_DOMAIN },
1160 : { "ENT_PRINCIPAL_AND_ID", KRB5_NT_ENT_PRINCIPAL_AND_ID },
1161 : { "MS_PRINCIPAL", KRB5_NT_MS_PRINCIPAL },
1162 : { "MS_PRINCIPAL_AND_ID", KRB5_NT_MS_PRINCIPAL_AND_ID },
1163 : { "SRV_HST_NEEDS_CANON", KRB5_NT_SRV_HST_NEEDS_CANON },
1164 : { NULL, 0 }
1165 : };
1166 :
1167 : /**
1168 : * Parse nametype string and return a nametype integer
1169 : *
1170 : * @ingroup krb5_principal
1171 : */
1172 :
1173 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1174 0 : krb5_parse_nametype(krb5_context context, const char *str, int32_t *nametype)
1175 : {
1176 : size_t i;
1177 :
1178 0 : for(i = 0; nametypes[i].type; i++) {
1179 0 : if (strcasecmp(nametypes[i].type, str) == 0) {
1180 0 : *nametype = nametypes[i].value;
1181 0 : return 0;
1182 : }
1183 : }
1184 0 : krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
1185 0 : N_("Failed to find name type %s", ""), str);
1186 0 : return KRB5_PARSE_MALFORMED;
1187 : }
1188 :
1189 : /**
1190 : * Returns true if name is Kerberos NULL name
1191 : *
1192 : * @ingroup krb5_principal
1193 : */
1194 :
1195 : krb5_boolean KRB5_LIB_FUNCTION
1196 0 : krb5_principal_is_null(krb5_context context, krb5_const_principal principal)
1197 : {
1198 0 : if (principal->name.name_type == KRB5_NT_WELLKNOWN &&
1199 0 : principal->name.name_string.len == 2 &&
1200 0 : strcmp(principal->name.name_string.val[0], "WELLKNOWN") == 0 &&
1201 0 : strcmp(principal->name.name_string.val[1], "NULL") == 0)
1202 0 : return TRUE;
1203 0 : return FALSE;
1204 : }
1205 :
1206 : const char _krb5_wellknown_lkdc[] = "WELLKNOWN:COM.APPLE.LKDC";
1207 : static const char lkdc_prefix[] = "LKDC:";
1208 :
1209 : /**
1210 : * Returns true if name is Kerberos an LKDC realm
1211 : *
1212 : * @ingroup krb5_principal
1213 : */
1214 :
1215 : krb5_boolean KRB5_LIB_FUNCTION
1216 169 : krb5_realm_is_lkdc(const char *realm)
1217 : {
1218 :
1219 338 : return strncmp(realm, lkdc_prefix, sizeof(lkdc_prefix)-1) == 0 ||
1220 169 : strncmp(realm, _krb5_wellknown_lkdc, sizeof(_krb5_wellknown_lkdc) - 1) == 0;
1221 : }
1222 :
1223 : /**
1224 : * Returns true if name is Kerberos an LKDC realm
1225 : *
1226 : * @ingroup krb5_principal
1227 : */
1228 :
1229 : krb5_boolean KRB5_LIB_FUNCTION
1230 77 : krb5_principal_is_lkdc(krb5_context context, krb5_const_principal principal)
1231 : {
1232 77 : return krb5_realm_is_lkdc(principal->realm);
1233 : }
1234 :
1235 : /**
1236 : * Returns true if name is Kerberos an LKDC realm
1237 : *
1238 : * @ingroup krb5_principal
1239 : */
1240 :
1241 : krb5_boolean KRB5_LIB_FUNCTION
1242 0 : krb5_principal_is_pku2u(krb5_context context, krb5_const_principal principal)
1243 : {
1244 0 : return strcmp(principal->realm, KRB5_PKU2U_REALM_NAME) == 0;
1245 : }
1246 :
1247 : /**
1248 : * Check if the cname part of the principal is a krbtgt principal
1249 : *
1250 : * @ingroup krb5_principal
1251 : */
1252 :
1253 : KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1254 400164 : krb5_principal_is_krbtgt(krb5_context context, krb5_const_principal p)
1255 : {
1256 724713 : return p->name.name_string.len == 2 &&
1257 324549 : strcmp(p->name.name_string.val[0], KRB5_TGS_NAME) == 0;
1258 : }
1259 :
1260 : /**
1261 : * Returns true iff name is an WELLKNOWN:ORG.H5L.HOSTBASED-SERVICE
1262 : *
1263 : * @ingroup krb5_principal
1264 : */
1265 :
1266 : krb5_boolean KRB5_LIB_FUNCTION
1267 0 : krb5_principal_is_gss_hostbased_service(krb5_context context,
1268 : krb5_const_principal principal)
1269 : {
1270 0 : if (principal == NULL)
1271 0 : return FALSE;
1272 0 : if (principal->name.name_string.len != 2)
1273 0 : return FALSE;
1274 0 : if (strcmp(principal->name.name_string.val[1], KRB5_GSS_HOSTBASED_SERVICE_NAME) != 0)
1275 0 : return FALSE;
1276 0 : return TRUE;
1277 : }
1278 :
1279 : /**
1280 : * Check if the cname part of the principal is a initial or renewed krbtgt principal
1281 : *
1282 : * @ingroup krb5_principal
1283 : */
1284 :
1285 : krb5_boolean KRB5_LIB_FUNCTION
1286 412603 : krb5_principal_is_root_krbtgt(krb5_context context, krb5_const_principal p)
1287 : {
1288 825165 : return p->name.name_string.len == 2 &&
1289 607023 : strcmp(p->name.name_string.val[0], KRB5_TGS_NAME) == 0 &&
1290 194420 : strcmp(p->name.name_string.val[1], p->realm) == 0;
1291 : }
1292 :
1293 : /**
1294 : * Returns true iff name is WELLKNOWN/ANONYMOUS
1295 : *
1296 : * @ingroup krb5_principal
1297 : */
1298 :
1299 : KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1300 82986 : krb5_principal_is_anonymous(krb5_context context,
1301 : krb5_const_principal p,
1302 : unsigned int flags)
1303 : {
1304 : /*
1305 : * Heimdal versions 7.5 and below left the name-type at KRB5_NT_PRINCIPAL
1306 : * even with anonymous pkinit responses. To retain interoperability with
1307 : * legacy KDCs, the name-type is not checked by the client after requesting
1308 : * a fully anonymous ticket.
1309 : */
1310 165972 : if (!(flags & KRB5_ANON_IGNORE_NAME_TYPE) &&
1311 165895 : p->name.name_type != KRB5_NT_WELLKNOWN &&
1312 82909 : p->name.name_type != KRB5_NT_UNKNOWN)
1313 82909 : return FALSE;
1314 :
1315 154 : if (p->name.name_string.len != 2 ||
1316 154 : strcmp(p->name.name_string.val[0], KRB5_WELLKNOWN_NAME) != 0 ||
1317 77 : strcmp(p->name.name_string.val[1], KRB5_ANON_NAME) != 0)
1318 0 : return FALSE;
1319 :
1320 : /*
1321 : * While unauthenticated clients SHOULD get "WELLKNOWN:ANONYMOUS" as their
1322 : * realm, Heimdal KDCs prior to 7.0 returned the requested realm. While
1323 : * such tickets might lead *servers* to unwittingly grant access to fully
1324 : * anonymous clients, trusting that the client was authenticated to the
1325 : * realm in question, doing it right is the KDC's job, the client should
1326 : * not refuse such a ticket.
1327 : *
1328 : * If we ever do decide to enforce WELLKNOWN:ANONYMOUS for unauthenticated
1329 : * clients, it is essential that calls that pass KRB5_ANON_MATCH_ANY still
1330 : * ignore the realm, as in that case either case matches one of the two
1331 : * possible conditions.
1332 : */
1333 77 : if (flags & KRB5_ANON_MATCH_UNAUTHENTICATED)
1334 77 : return TRUE;
1335 :
1336 : /*
1337 : * Finally, authenticated clients that asked to be only anonymized do
1338 : * legitimately expect a non-anon realm.
1339 : */
1340 0 : return strcmp(p->realm, KRB5_ANON_REALM) != 0;
1341 : }
1342 :
1343 : /**
1344 : * Returns true iff name is WELLKNOWN/FEDERATED
1345 : *
1346 : * @ingroup krb5_principal
1347 : */
1348 :
1349 : KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1350 0 : krb5_principal_is_federated(krb5_context context,
1351 : krb5_const_principal p)
1352 : {
1353 0 : if (p->name.name_type != KRB5_NT_WELLKNOWN &&
1354 0 : p->name.name_type != KRB5_NT_UNKNOWN)
1355 0 : return FALSE;
1356 :
1357 0 : if (p->name.name_string.len != 2 ||
1358 0 : strcmp(p->name.name_string.val[0], KRB5_WELLKNOWN_NAME) != 0 ||
1359 0 : strcmp(p->name.name_string.val[1], KRB5_FEDERATED_NAME) != 0)
1360 0 : return FALSE;
1361 :
1362 0 : return TRUE;
1363 : }
1364 :
1365 : static int
1366 0 : tolower_ascii(int c)
1367 : {
1368 0 : if (c >= 'A' && c <= 'Z')
1369 0 : return 'a' + (c - 'A');
1370 0 : return c;
1371 : }
1372 :
1373 : typedef enum krb5_name_canon_rule_type {
1374 : KRB5_NCRT_BOGUS = 0,
1375 : KRB5_NCRT_AS_IS,
1376 : KRB5_NCRT_QUALIFY,
1377 : KRB5_NCRT_NSS
1378 : } krb5_name_canon_rule_type;
1379 :
1380 : #ifdef UINT8_MAX
1381 : #define MAXDOTS UINT8_MAX
1382 : #else
1383 : #define MAXDOTS (255U)
1384 : #endif
1385 : #ifdef UINT16_MAX
1386 : #define MAXORDER UINT16_MAX
1387 : #else
1388 : #define MAXORDER (65535U)
1389 : #endif
1390 :
1391 : struct krb5_name_canon_rule_data {
1392 : krb5_name_canon_rule_type type;
1393 : krb5_name_canon_rule_options options;
1394 : uint8_t mindots; /* match this many dots or more */
1395 : uint8_t maxdots; /* match no more than this many dots */
1396 : uint16_t explicit_order; /* given order */
1397 : uint16_t order; /* actual order */
1398 : char *match_domain; /* match this stem */
1399 : char *match_realm; /* match this realm */
1400 : char *domain; /* qualify with this domain */
1401 : char *realm; /* qualify with this realm */
1402 : };
1403 :
1404 : /**
1405 : * Create a principal for the given service running on the given
1406 : * hostname. If KRB5_NT_SRV_HST is used, the hostname is canonicalized
1407 : * according the configured name canonicalization rules, with
1408 : * canonicalization delayed in some cases. One rule involves DNS, which
1409 : * is insecure unless DNSSEC is used, but we don't use DNSSEC-capable
1410 : * resolver APIs here, so that if DNSSEC is used we wouldn't know it.
1411 : *
1412 : * Canonicalization is immediate (not delayed) only when there is only
1413 : * one canonicalization rule and that rule indicates that we should do a
1414 : * host lookup by name (i.e., DNS).
1415 : *
1416 : * @param context A Kerberos context.
1417 : * @param hostname hostname to use
1418 : * @param sname Service name to use
1419 : * @param type name type of principal, use KRB5_NT_SRV_HST or KRB5_NT_UNKNOWN.
1420 : * @param ret_princ return principal, free with krb5_free_principal().
1421 : *
1422 : * @return An krb5 error code, see krb5_get_error_message().
1423 : *
1424 : * @ingroup krb5_principal
1425 : */
1426 :
1427 : /* coverity[+alloc : arg-*4] */
1428 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1429 1595 : krb5_sname_to_principal(krb5_context context,
1430 : const char *hostname,
1431 : const char *sname,
1432 : int32_t type,
1433 : krb5_principal *ret_princ)
1434 : {
1435 : char *realm, *remote_host;
1436 : krb5_error_code ret;
1437 : register char *cp;
1438 : char localname[MAXHOSTNAMELEN];
1439 :
1440 1595 : *ret_princ = NULL;
1441 :
1442 1595 : if ((type != KRB5_NT_UNKNOWN) &&
1443 : (type != KRB5_NT_SRV_HST))
1444 0 : return KRB5_SNAME_UNSUPP_NAMETYPE;
1445 :
1446 : /* if hostname is NULL, use local hostname */
1447 1595 : if (hostname == NULL) {
1448 0 : if (gethostname(localname, MAXHOSTNAMELEN))
1449 0 : return errno;
1450 0 : hostname = localname;
1451 : }
1452 :
1453 : /* if sname is NULL, use "host" */
1454 1595 : if (sname == NULL)
1455 0 : sname = "host";
1456 :
1457 1595 : remote_host = strdup(hostname);
1458 1595 : if (remote_host == NULL)
1459 0 : return krb5_enomem(context);
1460 :
1461 1595 : if (type == KRB5_NT_SRV_HST) {
1462 : krb5_name_canon_rule rules;
1463 :
1464 : /* Lower-case the hostname, because that's the convention */
1465 15132 : for (cp = remote_host; *cp; cp++)
1466 13537 : if (isupper((int) (*cp)))
1467 12129 : *cp = tolower((int) (*cp));
1468 :
1469 : /*
1470 : * If there is only one name canon rule and it says to
1471 : * canonicalize the old way, do that now, as we used to.
1472 : */
1473 1595 : ret = _krb5_get_name_canon_rules(context, &rules);
1474 1595 : if (ret) {
1475 0 : _krb5_debug(context, 5, "Failed to get name canon rules: ret = %d",
1476 : ret);
1477 0 : free(remote_host);
1478 0 : return ret;
1479 : }
1480 1595 : if (rules[0].type == KRB5_NCRT_NSS &&
1481 0 : rules[1].type == KRB5_NCRT_BOGUS) {
1482 0 : _krb5_debug(context, 5, "Using nss for name canon immediately");
1483 0 : ret = krb5_sname_to_principal_old(context, rules[0].realm,
1484 : remote_host, sname,
1485 : KRB5_NT_SRV_HST, ret_princ);
1486 0 : free(remote_host);
1487 0 : return ret;
1488 : }
1489 : }
1490 :
1491 : /* Remove trailing dots */
1492 1595 : if (remote_host[0]) {
1493 3190 : for (cp = remote_host + strlen(remote_host)-1;
1494 1595 : *cp == '.' && cp > remote_host;
1495 0 : cp--) {
1496 0 : *cp = '\0';
1497 : }
1498 : }
1499 :
1500 1595 : realm = ""; /* "Referral realm" */
1501 :
1502 1595 : ret = krb5_build_principal(context, ret_princ, strlen(realm),
1503 : realm, sname, remote_host,
1504 : (char *)0);
1505 :
1506 1595 : if (ret == 0 && type == KRB5_NT_SRV_HST) {
1507 : /*
1508 : * Hostname canonicalization is done elsewhere (in
1509 : * krb5_get_credentials() and krb5_kt_get_entry()).
1510 : *
1511 : * We overload the name type to indicate to those functions that
1512 : * this principal name requires canonicalization.
1513 : *
1514 : * We can't use the empty realm to denote the need to
1515 : * canonicalize the hostname too: it would mean that users who
1516 : * want to assert knowledge of a service's realm must also know
1517 : * the canonical hostname, but in practice they don't.
1518 : */
1519 1595 : (*ret_princ)->name.name_type = KRB5_NT_SRV_HST_NEEDS_CANON;
1520 :
1521 1595 : _krb5_debug(context, 5, "Building a delayed canon principal for %s/%s@",
1522 : sname, remote_host);
1523 : }
1524 :
1525 1595 : free(remote_host);
1526 1595 : return ret;
1527 : }
1528 :
1529 : static void
1530 0 : tolower_str(char *s)
1531 : {
1532 0 : for (; *s != '\0'; s++) {
1533 0 : if (isupper(*s))
1534 0 : *s = tolower_ascii(*s);
1535 : }
1536 0 : }
1537 :
1538 : static krb5_error_code
1539 0 : rule_parse_token(krb5_context context, krb5_name_canon_rule rule,
1540 : const char *tok)
1541 : {
1542 : long int n;
1543 0 : int needs_type = rule->type == KRB5_NCRT_BOGUS;
1544 :
1545 : /*
1546 : * Rules consist of a sequence of tokens, some of which indicate
1547 : * what type of rule the rule is, and some of which set rule options
1548 : * or ancilliary data. Last rule type token wins.
1549 : */
1550 :
1551 : /* Rule type tokens: */
1552 0 : if (needs_type && strcmp(tok, "as-is") == 0) {
1553 0 : rule->type = KRB5_NCRT_AS_IS;
1554 0 : } else if (needs_type && strcmp(tok, "qualify") == 0) {
1555 0 : rule->type = KRB5_NCRT_QUALIFY;
1556 0 : } else if (needs_type && strcmp(tok, "nss") == 0) {
1557 0 : rule->type = KRB5_NCRT_NSS;
1558 : /* Rule options: */
1559 0 : } else if (strcmp(tok, "use_fast") == 0) {
1560 0 : rule->options |= KRB5_NCRO_USE_FAST;
1561 0 : } else if (strcmp(tok, "use_dnssec") == 0) {
1562 0 : rule->options |= KRB5_NCRO_USE_DNSSEC;
1563 0 : } else if (strcmp(tok, "ccache_only") == 0) {
1564 0 : rule->options |= KRB5_NCRO_GC_ONLY;
1565 0 : } else if (strcmp(tok, "no_referrals") == 0) {
1566 0 : rule->options |= KRB5_NCRO_NO_REFERRALS;
1567 0 : } else if (strcmp(tok, "use_referrals") == 0) {
1568 0 : rule->options &= ~KRB5_NCRO_NO_REFERRALS;
1569 0 : if (rule->realm == NULL) {
1570 0 : rule->realm = strdup("");
1571 0 : if (rule->realm == NULL)
1572 0 : return krb5_enomem(context);
1573 : }
1574 0 : } else if (strcmp(tok, "lookup_realm") == 0) {
1575 0 : rule->options |= KRB5_NCRO_LOOKUP_REALM;
1576 0 : free(rule->realm);
1577 0 : rule->realm = NULL;
1578 : /* Rule ancilliary data: */
1579 0 : } else if (strncmp(tok, "domain=", strlen("domain=")) == 0) {
1580 0 : free(rule->domain);
1581 0 : rule->domain = strdup(tok + strlen("domain="));
1582 0 : if (rule->domain == NULL)
1583 0 : return krb5_enomem(context);
1584 0 : tolower_str(rule->domain);
1585 0 : } else if (strncmp(tok, "realm=", strlen("realm=")) == 0) {
1586 0 : free(rule->realm);
1587 0 : rule->realm = strdup(tok + strlen("realm="));
1588 0 : if (rule->realm == NULL)
1589 0 : return krb5_enomem(context);
1590 0 : } else if (strncmp(tok, "match_domain=", strlen("match_domain=")) == 0) {
1591 0 : free(rule->match_domain);
1592 0 : rule->match_domain = strdup(tok + strlen("match_domain="));
1593 0 : if (rule->match_domain == NULL)
1594 0 : return krb5_enomem(context);
1595 0 : tolower_str(rule->match_domain);
1596 0 : } else if (strncmp(tok, "match_realm=", strlen("match_realm=")) == 0) {
1597 0 : free(rule->match_realm);
1598 0 : rule->match_realm = strdup(tok + strlen("match_realm="));
1599 0 : if (rule->match_realm == NULL)
1600 0 : return krb5_enomem(context);
1601 0 : } else if (strncmp(tok, "mindots=", strlen("mindots=")) == 0) {
1602 0 : errno = 0;
1603 0 : n = strtol(tok + strlen("mindots="), NULL, 10);
1604 0 : if (errno == 0 && n > 0 && n <= MAXDOTS)
1605 0 : rule->mindots = n;
1606 0 : } else if (strncmp(tok, "maxdots=", strlen("maxdots=")) == 0) {
1607 0 : errno = 0;
1608 0 : n = strtol(tok + strlen("maxdots="), NULL, 10);
1609 0 : if (errno == 0 && n > 0 && n <= MAXDOTS)
1610 0 : rule->maxdots = n;
1611 0 : } else if (strncmp(tok, "order=", strlen("order=")) == 0) {
1612 0 : errno = 0;
1613 0 : n = strtol(tok + strlen("order="), NULL, 10);
1614 0 : if (errno == 0 && n > 0 && n <= MAXORDER)
1615 0 : rule->explicit_order = n;
1616 : } else {
1617 0 : _krb5_debug(context, 5,
1618 : "Unrecognized name canonicalization rule token %s", tok);
1619 0 : return EINVAL;
1620 : }
1621 0 : return 0;
1622 : }
1623 :
1624 : static int
1625 0 : rule_cmp(const void *a, const void *b)
1626 : {
1627 0 : krb5_const_name_canon_rule left = a;
1628 0 : krb5_const_name_canon_rule right = b;
1629 :
1630 0 : if (left->type == KRB5_NCRT_BOGUS &&
1631 0 : right->type == KRB5_NCRT_BOGUS)
1632 0 : return 0;
1633 0 : if (left->type == KRB5_NCRT_BOGUS)
1634 0 : return 1;
1635 0 : if (right->type == KRB5_NCRT_BOGUS)
1636 0 : return -1;
1637 0 : if (left->explicit_order < right->explicit_order)
1638 0 : return -1;
1639 0 : if (left->explicit_order > right->explicit_order)
1640 0 : return 1;
1641 0 : return left->order - right->order;
1642 : }
1643 :
1644 : static krb5_error_code
1645 250 : parse_name_canon_rules(krb5_context context, char **rulestrs,
1646 : krb5_name_canon_rule *rules)
1647 : {
1648 : krb5_error_code ret;
1649 : char *tok;
1650 : char *cp;
1651 : char **cpp;
1652 : size_t n;
1653 : size_t i, k;
1654 250 : int do_sort = 0;
1655 : krb5_name_canon_rule r;
1656 :
1657 250 : *rules = NULL;
1658 :
1659 250 : for (n =0, cpp = rulestrs; cpp != NULL && *cpp != NULL; cpp++)
1660 0 : n++;
1661 :
1662 250 : n += 2; /* Always at least one rule; two for the default case */
1663 :
1664 250 : if ((r = calloc(n, sizeof (*r))) == NULL)
1665 0 : return krb5_enomem(context);
1666 :
1667 750 : for (k = 0; k < n; k++) {
1668 500 : r[k].type = KRB5_NCRT_BOGUS;
1669 500 : r[k].match_domain = NULL;
1670 500 : r[k].match_realm = NULL;
1671 500 : r[k].domain = NULL;
1672 500 : r[k].realm = NULL;
1673 : }
1674 :
1675 250 : for (i = 0, k = 0; i < n && rulestrs != NULL && rulestrs[i] != NULL; i++) {
1676 0 : cp = rulestrs[i];
1677 0 : r[k].explicit_order = MAXORDER; /* mark order, see below */
1678 0 : r[k].maxdots = MAXDOTS;
1679 0 : r[k].order = k; /* default order */
1680 :
1681 : /* Tokenize and parse value */
1682 : do {
1683 0 : tok = cp;
1684 0 : cp = strchr(cp, ':'); /* XXX use strtok_r() */
1685 0 : if (cp)
1686 0 : *cp++ = '\0'; /* delimit token */
1687 0 : ret = rule_parse_token(context, &r[k], tok);
1688 0 : if (ret == EINVAL) {
1689 0 : r[k].type = KRB5_NCRT_BOGUS;
1690 0 : break;
1691 : }
1692 0 : if (ret) {
1693 0 : _krb5_free_name_canon_rules(context, r);
1694 0 : return ret;
1695 : }
1696 0 : } while (cp && *cp);
1697 0 : if (r[k].explicit_order != MAXORDER)
1698 0 : do_sort = 1;
1699 :
1700 : /* Validate parsed rule */
1701 0 : if (r[k].type == KRB5_NCRT_BOGUS ||
1702 0 : (r[k].type == KRB5_NCRT_QUALIFY && !r[k].domain) ||
1703 0 : (r[k].type == KRB5_NCRT_NSS && r[k].domain)) {
1704 : /* Invalid rule; mark it so and clean up */
1705 0 : r[k].type = KRB5_NCRT_BOGUS;
1706 0 : free(r[k].match_domain);
1707 0 : free(r[k].match_realm);
1708 0 : free(r[k].domain);
1709 0 : free(r[k].realm);
1710 0 : r[k].realm = NULL;
1711 0 : r[k].domain = NULL;
1712 0 : r[k].match_domain = NULL;
1713 0 : r[k].match_realm = NULL;
1714 0 : _krb5_debug(context, 5,
1715 : "Ignoring invalid name canonicalization rule %lu",
1716 : (unsigned long)i);
1717 0 : continue;
1718 : }
1719 0 : k++; /* good rule */
1720 : }
1721 :
1722 250 : if (do_sort) {
1723 : /*
1724 : * Note that we make make this a stable sort by using appareance
1725 : * and explicit order.
1726 : */
1727 0 : qsort(r, n, sizeof(r[0]), rule_cmp);
1728 : }
1729 :
1730 250 : if (r[0].type == KRB5_NCRT_BOGUS) {
1731 : /* No rules, or no valid rules */
1732 250 : if (context->flags & KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME) {
1733 0 : r[0].type = KRB5_NCRT_NSS;
1734 : } else {
1735 250 : r[0].type = KRB5_NCRT_AS_IS;
1736 : }
1737 : }
1738 :
1739 250 : *rules = r;
1740 250 : return 0; /* We don't communicate bad rule errors here */
1741 : }
1742 :
1743 : /*
1744 : * This exists only because the hostname canonicalization behavior in Heimdal
1745 : * (and other implementations of Kerberos) has been to use getaddrinfo(),
1746 : * unsafe though it is, for ages. We can't fix it in one day.
1747 : */
1748 : static void
1749 0 : make_rules_safe(krb5_context context, krb5_name_canon_rule rules)
1750 : {
1751 : /*
1752 : * If the only rule were to use the name service (getaddrinfo()) then we're
1753 : * bound to fail. We could try to convert that rule to an as-is rule, but
1754 : * when we do get a validating resolver we'd be unhappy that we did such a
1755 : * conversion. Better let the user get failures and make them think about
1756 : * their naming rules.
1757 : */
1758 0 : if (rules == NULL)
1759 0 : return;
1760 0 : for (; rules[0].type != KRB5_NCRT_BOGUS; rules++) {
1761 0 : if (rules->type == KRB5_NCRT_NSS)
1762 0 : rules->options |= KRB5_NCRO_USE_DNSSEC;
1763 : else
1764 0 : rules->options |= KRB5_NCRO_USE_FAST;
1765 : }
1766 : }
1767 :
1768 : /**
1769 : * This function returns an array of host-based service name
1770 : * canonicalization rules. The array of rules is organized as a list.
1771 : * See the definition of krb5_name_canon_rule.
1772 : *
1773 : * @param context A Kerberos context.
1774 : * @param rules Output location for array of rules.
1775 : */
1776 : KRB5_LIB_FUNCTION krb5_error_code
1777 3190 : _krb5_get_name_canon_rules(krb5_context context, krb5_name_canon_rule *rules)
1778 : {
1779 : krb5_error_code ret;
1780 3190 : char **values = NULL;
1781 :
1782 3190 : *rules = context->name_canon_rules;
1783 3190 : if (*rules != NULL)
1784 2940 : return 0;
1785 :
1786 250 : values = krb5_config_get_strings(context, NULL,
1787 : "libdefaults", "name_canon_rules", NULL);
1788 250 : ret = parse_name_canon_rules(context, values, rules);
1789 250 : krb5_config_free_strings(values);
1790 250 : if (ret)
1791 0 : return ret;
1792 :
1793 250 : if (krb5_config_get_bool_default(context, NULL, FALSE,
1794 : "libdefaults", "safe_name_canon", NULL))
1795 0 : make_rules_safe(context, *rules);
1796 :
1797 250 : heim_assert((*rules)[0].type != KRB5_NCRT_BOGUS,
1798 : "internal error in parsing principal name "
1799 : "canonicalization rules");
1800 :
1801 : /* Memoize */
1802 250 : context->name_canon_rules = *rules;
1803 :
1804 250 : return 0;
1805 : }
1806 :
1807 : static krb5_error_code
1808 0 : get_host_realm(krb5_context context, const char *hostname, char **realm)
1809 : {
1810 : krb5_error_code ret;
1811 0 : char **hrealms = NULL;
1812 :
1813 0 : *realm = NULL;
1814 0 : ret = krb5_get_host_realm(context, hostname, &hrealms);
1815 0 : if (ret)
1816 0 : return ret;
1817 0 : if (hrealms == NULL)
1818 0 : return KRB5_ERR_HOST_REALM_UNKNOWN; /* krb5_set_error() already done */
1819 0 : if (hrealms[0] == NULL) {
1820 0 : krb5_free_host_realm(context, hrealms);
1821 0 : return KRB5_ERR_HOST_REALM_UNKNOWN; /* krb5_set_error() already done */
1822 : }
1823 0 : *realm = strdup(hrealms[0]);
1824 0 : krb5_free_host_realm(context, hrealms);
1825 0 : if (*realm == NULL)
1826 0 : return krb5_enomem(context);
1827 0 : return 0;
1828 : }
1829 :
1830 : static int
1831 0 : is_domain_suffix(const char *domain, const char *suffix)
1832 : {
1833 0 : size_t dlen = strlen(domain);
1834 0 : size_t slen = strlen(suffix);
1835 :
1836 0 : if (dlen < slen + 2)
1837 0 : return 0;
1838 :
1839 0 : if (strcasecmp(domain + (dlen - slen), suffix) != 0)
1840 0 : return 0;
1841 :
1842 0 : if (domain[(dlen - slen) - 1] != '.')
1843 0 : return 0;
1844 0 : return 1;
1845 : }
1846 :
1847 : /*
1848 : * Applies a name canonicalization rule to a principal.
1849 : *
1850 : * Returns zero and no out_princ if the rule does not match.
1851 : * Returns zero and an out_princ if the rule does match.
1852 : */
1853 : static krb5_error_code
1854 1595 : apply_name_canon_rule(krb5_context context, krb5_name_canon_rule rules,
1855 : size_t rule_idx, krb5_const_principal in_princ,
1856 : krb5_principal *out_princ,
1857 : krb5_name_canon_rule_options *rule_opts)
1858 : {
1859 1595 : krb5_name_canon_rule rule = &rules[rule_idx];
1860 : krb5_error_code ret;
1861 1595 : unsigned int ndots = 0;
1862 1595 : krb5_principal nss = NULL;
1863 1595 : const char *sname = NULL;
1864 1595 : const char *orig_hostname = NULL;
1865 1595 : const char *new_hostname = NULL;
1866 1595 : const char *new_realm = NULL;
1867 1595 : const char *port = "";
1868 : const char *cp;
1869 1595 : char *hostname_sans_port = NULL;
1870 1595 : char *hostname_with_port = NULL;
1871 1595 : char *tmp_hostname = NULL;
1872 1595 : char *tmp_realm = NULL;
1873 :
1874 1595 : *out_princ = NULL; /* Signal no match */
1875 :
1876 1595 : if (rule_opts != NULL)
1877 1595 : *rule_opts = rule->options;
1878 :
1879 1595 : if (rule->type == KRB5_NCRT_BOGUS)
1880 0 : return 0; /* rule doesn't apply */
1881 :
1882 1595 : sname = krb5_principal_get_comp_string(context, in_princ, 0);
1883 1595 : orig_hostname = krb5_principal_get_comp_string(context, in_princ, 1);
1884 :
1885 : /*
1886 : * Some apps want to use the very non-standard svc/hostname:port@REALM
1887 : * form. We do our best to support that here :(
1888 : */
1889 1595 : port = strchr(orig_hostname, ':');
1890 1595 : if (port != NULL) {
1891 0 : hostname_sans_port = strndup(orig_hostname, port - orig_hostname);
1892 0 : if (hostname_sans_port == NULL)
1893 0 : return krb5_enomem(context);
1894 0 : orig_hostname = hostname_sans_port;
1895 : }
1896 :
1897 1595 : _krb5_debug(context, 5, N_("Applying a name rule (type %d) to %s", ""),
1898 1595 : rule->type, orig_hostname);
1899 :
1900 1595 : if (rule->mindots > 0 || rule->maxdots > 0) {
1901 0 : for (cp = strchr(orig_hostname, '.'); cp && *cp; cp = strchr(cp + 1, '.'))
1902 0 : ndots++;
1903 : }
1904 1595 : if (rule->mindots > 0 && ndots < rule->mindots)
1905 0 : return 0;
1906 1595 : if (ndots > rule->maxdots)
1907 0 : return 0;
1908 :
1909 1595 : if (rule->match_domain != NULL &&
1910 0 : !is_domain_suffix(orig_hostname, rule->match_domain))
1911 0 : return 0;
1912 :
1913 1595 : if (rule->match_realm != NULL &&
1914 0 : strcmp(rule->match_realm, in_princ->realm) != 0)
1915 0 : return 0;
1916 :
1917 1595 : new_realm = rule->realm;
1918 1595 : switch (rule->type) {
1919 1595 : case KRB5_NCRT_AS_IS:
1920 1595 : break;
1921 :
1922 0 : case KRB5_NCRT_QUALIFY:
1923 0 : heim_assert(rule->domain != NULL,
1924 : "missing domain for qualify name canon rule");
1925 0 : if (asprintf(&tmp_hostname, "%s.%s", orig_hostname,
1926 0 : rule->domain) == -1 || tmp_hostname == NULL) {
1927 0 : ret = krb5_enomem(context);
1928 0 : goto out;
1929 : }
1930 0 : new_hostname = tmp_hostname;
1931 0 : break;
1932 :
1933 0 : case KRB5_NCRT_NSS:
1934 0 : if ((rule->options & KRB5_NCRO_USE_DNSSEC)) {
1935 0 : ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1936 0 : krb5_set_error_message(context, ret,
1937 : "Secure hostname resolution not supported");
1938 0 : goto out;
1939 : }
1940 0 : _krb5_debug(context, 5, "Using name service lookups");
1941 0 : ret = krb5_sname_to_principal_old(context, rule->realm,
1942 : orig_hostname, sname,
1943 : KRB5_NT_SRV_HST,
1944 : &nss);
1945 0 : if (rules[rule_idx + 1].type != KRB5_NCRT_BOGUS &&
1946 0 : (ret == KRB5_ERR_BAD_HOSTNAME ||
1947 : ret == KRB5_ERR_HOST_REALM_UNKNOWN)) {
1948 : /*
1949 : * Bad hostname / realm unknown -> rule inapplicable if
1950 : * there's more rules. If it's the last rule then we want
1951 : * to return all errors from krb5_sname_to_principal_old()
1952 : * here.
1953 : */
1954 0 : ret = 0;
1955 0 : goto out;
1956 : }
1957 0 : if (ret)
1958 0 : goto out;
1959 :
1960 0 : new_hostname = krb5_principal_get_comp_string(context, nss, 1);
1961 0 : new_realm = krb5_principal_get_realm(context, nss);
1962 0 : break;
1963 :
1964 0 : default:
1965 : /* Can't happen */
1966 0 : ret = 0;
1967 0 : goto out;
1968 : }
1969 :
1970 : /*
1971 : * This rule applies.
1972 : *
1973 : * Copy in_princ and mutate the copy per the matched rule.
1974 : *
1975 : * This way we apply to principals with two or more components, such as
1976 : * domain-based names.
1977 : */
1978 1595 : ret = krb5_copy_principal(context, in_princ, out_princ);
1979 1595 : if (ret)
1980 0 : goto out;
1981 :
1982 1595 : if (new_realm == NULL && (rule->options & KRB5_NCRO_LOOKUP_REALM) != 0) {
1983 0 : ret = get_host_realm(context, new_hostname, &tmp_realm);
1984 0 : if (ret)
1985 0 : goto out;
1986 0 : new_realm = tmp_realm;
1987 : }
1988 :
1989 : /* If we stripped off a :port, add it back in */
1990 1595 : if (port != NULL && new_hostname != NULL) {
1991 0 : if (asprintf(&hostname_with_port, "%s%s", new_hostname, port) == -1 ||
1992 0 : hostname_with_port == NULL) {
1993 0 : ret = krb5_enomem(context);
1994 0 : goto out;
1995 : }
1996 0 : new_hostname = hostname_with_port;
1997 : }
1998 :
1999 1595 : if (new_realm != NULL &&
2000 0 : (ret = krb5_principal_set_realm(context, *out_princ, new_realm)))
2001 0 : goto out;
2002 1595 : if (new_hostname != NULL &&
2003 0 : (ret = krb5_principal_set_comp_string(context, *out_princ, 1, new_hostname)))
2004 0 : goto out;
2005 1595 : if (princ_type(*out_princ) == KRB5_NT_SRV_HST_NEEDS_CANON)
2006 1595 : princ_type(*out_princ) = KRB5_NT_SRV_HST;
2007 :
2008 : /* Trace rule application */
2009 : {
2010 : krb5_error_code ret2;
2011 : char *unparsed;
2012 :
2013 1595 : ret2 = krb5_unparse_name(context, *out_princ, &unparsed);
2014 1595 : if (ret2) {
2015 0 : _krb5_debug(context, 5,
2016 0 : N_("Couldn't unparse canonicalized princicpal (%d)",
2017 : ""),
2018 : ret);
2019 : } else {
2020 3190 : _krb5_debug(context, 5,
2021 1595 : N_("Name canon rule application yields %s", ""),
2022 : unparsed);
2023 1595 : free(unparsed);
2024 : }
2025 : }
2026 :
2027 1595 : out:
2028 1595 : free(hostname_sans_port);
2029 1595 : free(hostname_with_port);
2030 1595 : free(tmp_hostname);
2031 1595 : free(tmp_realm);
2032 1595 : krb5_free_principal(context, nss);
2033 1595 : if (ret)
2034 0 : krb5_set_error_message(context, ret,
2035 0 : N_("Name canon rule application failed", ""));
2036 1595 : return ret;
2037 : }
2038 :
2039 : /**
2040 : * Free name canonicalization rules
2041 : */
2042 : KRB5_LIB_FUNCTION void
2043 533651 : _krb5_free_name_canon_rules(krb5_context context, krb5_name_canon_rule rules)
2044 : {
2045 : size_t k;
2046 :
2047 533651 : if (rules == NULL)
2048 533651 : return;
2049 :
2050 0 : for (k = 0; rules[k].type != KRB5_NCRT_BOGUS; k++) {
2051 0 : free(rules[k].match_domain);
2052 0 : free(rules[k].match_realm);
2053 0 : free(rules[k].domain);
2054 0 : free(rules[k].realm);
2055 : }
2056 0 : free(rules);
2057 : }
2058 :
2059 : struct krb5_name_canon_iterator_data {
2060 : krb5_name_canon_rule rules;
2061 : krb5_const_principal in_princ; /* given princ */
2062 : krb5_const_principal out_princ; /* princ to be output */
2063 : krb5_principal tmp_princ; /* to be freed */
2064 : int is_trivial; /* no canon to be done */
2065 : int done; /* no more rules to be applied */
2066 : size_t cursor; /* current/next rule */
2067 : };
2068 :
2069 : /**
2070 : * Initialize name canonicalization iterator.
2071 : *
2072 : * @param context Kerberos context
2073 : * @param in_princ principal name to be canonicalized OR
2074 : * @param iter output iterator object
2075 : */
2076 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2077 126905 : krb5_name_canon_iterator_start(krb5_context context,
2078 : krb5_const_principal in_princ,
2079 : krb5_name_canon_iterator *iter)
2080 : {
2081 : krb5_error_code ret;
2082 : krb5_name_canon_iterator state;
2083 :
2084 126905 : *iter = NULL;
2085 :
2086 126905 : state = calloc(1, sizeof (*state));
2087 126905 : if (state == NULL)
2088 0 : return krb5_enomem(context);
2089 126905 : state->in_princ = in_princ;
2090 :
2091 126905 : if (princ_type(state->in_princ) == KRB5_NT_SRV_HST_NEEDS_CANON) {
2092 1595 : ret = _krb5_get_name_canon_rules(context, &state->rules);
2093 1595 : if (ret)
2094 0 : goto out;
2095 : } else {
2096 : /* Name needs no canon -> trivial iterator: in_princ is canonical */
2097 125310 : state->is_trivial = 1;
2098 : }
2099 :
2100 126905 : *iter = state;
2101 126905 : return 0;
2102 :
2103 0 : out:
2104 0 : krb5_free_name_canon_iterator(context, state);
2105 0 : return krb5_enomem(context);
2106 : }
2107 :
2108 : /*
2109 : * Helper for name canon iteration.
2110 : */
2111 : static krb5_error_code
2112 128340 : name_canon_iterate(krb5_context context,
2113 : krb5_name_canon_iterator *iter,
2114 : krb5_name_canon_rule_options *rule_opts)
2115 : {
2116 : krb5_error_code ret;
2117 128340 : krb5_name_canon_iterator state = *iter;
2118 :
2119 128340 : if (rule_opts)
2120 81871 : *rule_opts = 0;
2121 :
2122 128340 : if (state == NULL)
2123 0 : return 0;
2124 :
2125 128340 : if (state->done) {
2126 1435 : krb5_free_name_canon_iterator(context, state);
2127 1435 : *iter = NULL;
2128 1435 : return 0;
2129 : }
2130 :
2131 126905 : if (state->is_trivial && !state->done) {
2132 125310 : state->out_princ = state->in_princ;
2133 125310 : state->done = 1;
2134 125310 : return 0;
2135 : }
2136 :
2137 1595 : heim_assert(state->rules != NULL &&
2138 : state->rules[state->cursor].type != KRB5_NCRT_BOGUS,
2139 : "Internal error during name canonicalization");
2140 :
2141 : do {
2142 1595 : krb5_free_principal(context, state->tmp_princ);
2143 1595 : ret = apply_name_canon_rule(context, state->rules, state->cursor,
2144 : state->in_princ, &state->tmp_princ, rule_opts);
2145 1595 : if (ret) {
2146 0 : krb5_free_name_canon_iterator(context, state);
2147 0 : *iter = NULL;
2148 0 : return ret;
2149 : }
2150 1595 : state->cursor++;
2151 1595 : } while (state->tmp_princ == NULL &&
2152 1595 : state->rules[state->cursor].type != KRB5_NCRT_BOGUS);
2153 :
2154 1595 : if (state->rules[state->cursor].type == KRB5_NCRT_BOGUS)
2155 1595 : state->done = 1;
2156 :
2157 1595 : state->out_princ = state->tmp_princ;
2158 1595 : if (state->tmp_princ == NULL) {
2159 0 : krb5_free_name_canon_iterator(context, state);
2160 0 : *iter = NULL;
2161 0 : return 0;
2162 : }
2163 1595 : return 0;
2164 : }
2165 :
2166 : /**
2167 : * Iteratively apply name canon rules, outputing a principal and rule
2168 : * options each time. Iteration completes when the @iter is NULL on
2169 : * return or when an error is returned. Callers must free the iterator
2170 : * if they abandon it mid-way.
2171 : *
2172 : * @param context Kerberos context
2173 : * @param iter name canon rule iterator (input/output)
2174 : * @param try_princ output principal name
2175 : * @param rule_opts output rule options
2176 : */
2177 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2178 128340 : krb5_name_canon_iterate(krb5_context context,
2179 : krb5_name_canon_iterator *iter,
2180 : krb5_const_principal *try_princ,
2181 : krb5_name_canon_rule_options *rule_opts)
2182 : {
2183 : krb5_error_code ret;
2184 :
2185 128340 : *try_princ = NULL;
2186 :
2187 128340 : ret = name_canon_iterate(context, iter, rule_opts);
2188 128340 : if (*iter)
2189 126905 : *try_princ = (*iter)->out_princ;
2190 128340 : return ret;
2191 : }
2192 :
2193 : /**
2194 : * Free a name canonicalization rule iterator.
2195 : */
2196 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2197 128340 : krb5_free_name_canon_iterator(krb5_context context,
2198 : krb5_name_canon_iterator iter)
2199 : {
2200 128340 : if (iter == NULL)
2201 1435 : return;
2202 126905 : if (iter->tmp_princ)
2203 1595 : krb5_free_principal(context, iter->tmp_princ);
2204 126905 : free(iter);
2205 : }
|