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 1369904 : set_default_princ_type(krb5_principal p, NAME_TYPE defnt)
68 : {
69 1369904 : if (princ_num_comp(p) > 1 && strcmp(princ_ncomp(p, 0), KRB5_TGS_NAME) == 0)
70 223512 : princ_type(p) = KRB5_NT_SRV_INST;
71 1146392 : else if (princ_num_comp(p) > 1 && strcmp(princ_ncomp(p, 0), "host") == 0)
72 54525 : princ_type(p) = KRB5_NT_SRV_HST;
73 1091867 : 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 1091867 : else if (princ_num_comp(p) == 2 &&
76 488574 : strcmp(princ_ncomp(p, 0), KRB5_WELLKNOWN_NAME) == 0)
77 416 : princ_type(p) = KRB5_NT_WELLKNOWN;
78 1091451 : else if (princ_num_comp(p) == 1 && strchr(princ_ncomp(p, 0), '@') != NULL)
79 577 : princ_type(p) = KRB5_NT_SMTP_NAME;
80 : else
81 1090874 : princ_type(p) = defnt;
82 1369904 : 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 10588963 : krb5_free_principal(krb5_context context,
103 : krb5_principal p)
104 : {
105 10588963 : if(p){
106 8755861 : if (p->nameattrs && p->nameattrs->pac)
107 1440517 : heim_release(p->nameattrs->pac);
108 8755861 : free_Principal(p);
109 8755861 : free(p);
110 : }
111 10588963 : }
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 96006 : krb5_principal_set_type(krb5_context context,
127 : krb5_principal principal,
128 : int type)
129 : {
130 96006 : princ_type(principal) = type;
131 96006 : }
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 611028 : krb5_principal_get_type(krb5_context context,
146 : krb5_const_principal principal)
147 : {
148 611028 : 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 1209516 : krb5_principal_get_realm(krb5_context context,
164 : krb5_const_principal principal)
165 : {
166 1209516 : return princ_realm(principal);
167 : }
168 :
169 : KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
170 1479508 : krb5_principal_get_comp_string(krb5_context context,
171 : krb5_const_principal principal,
172 : unsigned int component)
173 : {
174 1479508 : if(component >= princ_num_comp(principal))
175 101615 : return NULL;
176 1374480 : 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 1364692 : krb5_principal_get_num_comp(krb5_context context,
192 : krb5_const_principal principal)
193 : {
194 1364692 : 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 719483 : krb5_parse_name_flags(krb5_context context,
212 : const char *name,
213 : int flags,
214 : krb5_principal *principal)
215 : {
216 22730 : krb5_error_code ret;
217 22730 : heim_general_string *comp;
218 719483 : heim_general_string realm = NULL;
219 22730 : int ncomp;
220 :
221 22730 : const char *p;
222 22730 : char *q;
223 22730 : char *s;
224 22730 : char *start;
225 :
226 22730 : int n;
227 22730 : char c;
228 719483 : int got_realm = 0;
229 719483 : int first_at = 1;
230 719483 : int no_realm = flags & KRB5_PRINCIPAL_PARSE_NO_REALM;
231 719483 : int require_realm = flags & KRB5_PRINCIPAL_PARSE_REQUIRE_REALM;
232 719483 : int enterprise = flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE;
233 719483 : int ignore_realm = flags & KRB5_PRINCIPAL_PARSE_IGNORE_REALM;
234 719483 : int no_def_realm = flags & KRB5_PRINCIPAL_PARSE_NO_DEF_REALM;
235 :
236 719483 : *principal = NULL;
237 :
238 719483 : 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 719483 : ncomp = 1;
249 719483 : if (!enterprise) {
250 7728807 : for (p = name; *p; p++) {
251 7552730 : if (*p=='\\') {
252 2663 : 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 2663 : p++;
258 7550067 : } else if (*p == '/')
259 100441 : ncomp++;
260 7449626 : else if (*p == '@')
261 424163 : break;
262 : }
263 : }
264 719483 : comp = calloc(ncomp, sizeof(*comp));
265 719483 : if (comp == NULL)
266 0 : return krb5_enomem(context);
267 :
268 719483 : n = 0;
269 719483 : p = start = q = s = strdup(name);
270 719483 : if (start == NULL) {
271 0 : free(comp);
272 0 : return krb5_enomem(context);
273 : }
274 21437686 : while (*p) {
275 20718205 : c = *p++;
276 20718205 : if (c == '\\') {
277 2663 : c = *p++;
278 2663 : if (c == 'n')
279 0 : c = '\n';
280 2663 : else if (c == 't')
281 0 : c = '\t';
282 2663 : else if (c == 'b')
283 0 : c = '\b';
284 2663 : 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 2663 : } 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 20715542 : } else if (enterprise && first_at) {
308 1432082 : if (c == '@')
309 104447 : first_at = 0;
310 19283460 : } else if ((c == '/' && !enterprise) || c == '@') {
311 539395 : if (got_realm) {
312 2 : ret = KRB5_PARSE_MALFORMED;
313 2 : krb5_set_error_message(context, ret,
314 2 : N_("part after realm in principal name", ""));
315 2 : goto exit;
316 : } else {
317 539393 : comp[n] = malloc(q - start + 1);
318 539393 : if (comp[n] == NULL) {
319 0 : ret = krb5_enomem(context);
320 0 : goto exit;
321 : }
322 539393 : memcpy(comp[n], start, q - start);
323 539393 : comp[n][q - start] = 0;
324 539393 : n++;
325 : }
326 539393 : if (c == '@')
327 438952 : got_realm = 1;
328 539393 : start = q;
329 539393 : continue;
330 : }
331 20178810 : 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 20178810 : *q++ = c;
338 : }
339 719481 : if (got_realm) {
340 438950 : 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 438944 : if (!ignore_realm) {
348 438944 : realm = malloc(q - start + 1);
349 438944 : if (realm == NULL) {
350 0 : ret = krb5_enomem(context);
351 0 : goto exit;
352 : }
353 438944 : memcpy(realm, start, q - start);
354 438944 : realm[q - start] = 0;
355 : }
356 : } else {
357 280531 : if (require_realm) {
358 2684 : ret = KRB5_PARSE_MALFORMED;
359 2684 : krb5_set_error_message(context, ret,
360 2684 : N_("realm NOT found in principal "
361 : "expected to be with one", ""));
362 2684 : goto exit;
363 277847 : } else if (no_realm || no_def_realm) {
364 245218 : realm = NULL;
365 : } else {
366 32629 : ret = krb5_get_default_realm(context, &realm);
367 32629 : if (ret)
368 0 : goto exit;
369 : }
370 :
371 277847 : comp[n] = malloc(q - start + 1);
372 277847 : if (comp[n] == NULL) {
373 0 : ret = krb5_enomem(context);
374 0 : goto exit;
375 : }
376 277847 : memcpy(comp[n], start, q - start);
377 277847 : comp[n][q - start] = 0;
378 277847 : n++;
379 : }
380 716791 : *principal = calloc(1, sizeof(**principal));
381 716791 : if (*principal == NULL) {
382 0 : ret = krb5_enomem(context);
383 0 : goto exit;
384 : }
385 716791 : (*principal)->name.name_string.val = comp;
386 716791 : princ_num_comp(*principal) = n;
387 716791 : (*principal)->realm = realm;
388 716791 : if (enterprise)
389 104455 : princ_type(*principal) = KRB5_NT_ENTERPRISE_PRINCIPAL;
390 : else
391 612336 : set_default_princ_type(*principal, KRB5_NT_PRINCIPAL);
392 716791 : free(s);
393 716791 : return 0;
394 2692 : exit:
395 3137 : while (n>0) {
396 445 : free(comp[--n]);
397 : }
398 2692 : free(comp);
399 2692 : krb5_free_default_realm(context, realm);
400 2692 : free(s);
401 2692 : 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 346664 : krb5_parse_name(krb5_context context,
418 : const char *name,
419 : krb5_principal *principal)
420 : {
421 346664 : 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 2352318 : quote_string(const char *s, char *out, size_t idx, size_t len, int display)
431 : {
432 72147 : const char *p, *q;
433 38286006 : for(p = s; *p && idx < len; p++){
434 35933688 : q = strchr(quotable_chars, *p);
435 35933688 : if (q && display) {
436 6365 : add_char(out, idx, len, replace_chars[q - quotable_chars]);
437 35927323 : } else if (q) {
438 13852 : add_char(out, idx, len, '\\');
439 13852 : add_char(out, idx, len, replace_chars[q - quotable_chars]);
440 : }else
441 35913474 : add_char(out, idx, len, *p);
442 : }
443 2352318 : if(idx < len)
444 2352318 : out[idx] = '\0';
445 2352318 : return idx;
446 : }
447 :
448 :
449 : static krb5_error_code
450 1228355 : unparse_name_fixed(krb5_context context,
451 : krb5_const_principal principal,
452 : char *name,
453 : size_t len,
454 : int flags)
455 : {
456 1228355 : size_t idx = 0;
457 37879 : size_t i;
458 1228355 : int short_form = (flags & KRB5_PRINCIPAL_UNPARSE_SHORT) != 0;
459 1228355 : int no_realm = (flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) != 0;
460 1228355 : int display = (flags & KRB5_PRINCIPAL_UNPARSE_DISPLAY) != 0;
461 :
462 1228355 : 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 1228355 : 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 1228355 : name[0] = '\0';
477 :
478 1228355 : 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 2717915 : for(i = 0; i < princ_num_comp(principal); i++){
486 1489560 : if(i)
487 261205 : add_char(name, idx, len, '/');
488 1489560 : idx = quote_string(princ_ncomp(principal, i), name, idx, len, display);
489 1489560 : 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 1228355 : if(short_form && !no_realm) {
497 0 : krb5_realm r;
498 0 : krb5_error_code ret;
499 0 : ret = krb5_get_default_realm(context, &r);
500 0 : if(ret)
501 0 : return ret;
502 0 : if(strcmp(princ_realm(principal), r) != 0)
503 0 : short_form = 0;
504 0 : krb5_free_default_realm(context, r);
505 : }
506 1228355 : if(!short_form && !no_realm) {
507 862758 : add_char(name, idx, len, '@');
508 862758 : idx = quote_string(princ_realm(principal), name, idx, len, display);
509 862758 : 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 1190476 : 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 5108 : krb5_unparse_name_fixed(krb5_context context,
534 : krb5_const_principal principal,
535 : char *name,
536 : size_t len)
537 : {
538 5108 : 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 1223247 : unparse_name(krb5_context context,
591 : krb5_const_principal principal,
592 : char **name,
593 : int flags)
594 : {
595 1223247 : size_t len = 0, plen;
596 37879 : size_t i;
597 37879 : krb5_error_code ret;
598 : /* count length */
599 1223247 : if (princ_realm(principal)) {
600 1191701 : plen = strlen(princ_realm(principal));
601 :
602 1191701 : if(strcspn(princ_realm(principal), quotable_chars) == plen)
603 1154985 : len += plen;
604 : else
605 0 : len += 2*plen;
606 1191701 : len++; /* '@' */
607 : }
608 2705009 : for(i = 0; i < princ_num_comp(principal); i++){
609 1481762 : plen = strlen(princ_ncomp(principal, i));
610 1481762 : if(strcspn(princ_ncomp(principal, i), quotable_chars) == plen)
611 1463340 : len += plen;
612 : else
613 18422 : len += 2*plen;
614 1481762 : len++;
615 : }
616 1223247 : len++; /* '\0' */
617 1223247 : *name = malloc(len);
618 1223247 : if(*name == NULL)
619 0 : return krb5_enomem(context);
620 1223247 : ret = unparse_name_fixed(context, principal, *name, len, flags);
621 1223247 : if(ret) {
622 0 : free(*name);
623 0 : *name = NULL;
624 : }
625 1185368 : 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 681878 : krb5_unparse_name(krb5_context context,
642 : krb5_const_principal principal,
643 : char **name)
644 : {
645 681878 : 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 541369 : krb5_unparse_name_flags(krb5_context context,
663 : krb5_const_principal principal,
664 : int flags,
665 : char **name)
666 : {
667 541369 : 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 0 : krb5_unparse_name_short(krb5_context context,
685 : krb5_const_principal principal,
686 : char **name)
687 : {
688 0 : 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 433558 : krb5_principal_set_realm(krb5_context context,
706 : krb5_principal principal,
707 : krb5_const_realm realm)
708 : {
709 433558 : if (princ_realm(principal))
710 225656 : free(princ_realm(principal));
711 :
712 433558 : if (realm == NULL)
713 0 : princ_realm(principal) = NULL;
714 433558 : else if ((princ_realm(principal) = strdup(realm)) == NULL)
715 0 : return krb5_enomem(context);
716 421788 : 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 0 : char *s;
726 0 : 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 3064 : krb5_build_principal(krb5_context context,
755 : krb5_principal *principal,
756 : int rlen,
757 : krb5_const_realm realm,
758 : ...)
759 : {
760 28 : krb5_error_code ret;
761 28 : va_list ap;
762 3064 : va_start(ap, realm);
763 3064 : ret = krb5_build_principal_va(context, principal, rlen, realm, ap);
764 3064 : va_end(ap);
765 3064 : 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 739042 : krb5_make_principal(krb5_context context,
785 : krb5_principal *principal,
786 : krb5_const_realm realm,
787 : ...)
788 : {
789 17539 : krb5_error_code ret;
790 739042 : krb5_realm r = NULL;
791 17539 : va_list ap;
792 :
793 739042 : *principal = NULL;
794 :
795 739042 : if(realm == NULL) {
796 1 : ret = krb5_get_default_realm(context, &r);
797 1 : if(ret)
798 0 : return ret;
799 1 : realm = r;
800 : }
801 739042 : va_start(ap, realm);
802 739042 : ret = krb5_build_principal_va(context, principal, strlen(realm), realm, ap);
803 739042 : va_end(ap);
804 739042 : if(r)
805 1 : krb5_free_default_realm(context, r);
806 721503 : return ret;
807 : }
808 :
809 : static krb5_error_code
810 1477131 : append_component(krb5_context context, krb5_principal p,
811 : const char *comp,
812 : size_t comp_len)
813 : {
814 34670 : heim_general_string *tmp;
815 1477131 : size_t len = princ_num_comp(p);
816 :
817 1477131 : tmp = realloc(princ_comp(p), (len + 1) * sizeof(*tmp));
818 1477131 : if(tmp == NULL)
819 0 : return krb5_enomem(context);
820 1477131 : princ_comp(p) = tmp;
821 1477131 : princ_ncomp(p, len) = malloc(comp_len + 1);
822 1477131 : if (princ_ncomp(p, len) == NULL)
823 0 : return krb5_enomem(context);
824 1477131 : memcpy (princ_ncomp(p, len), comp, comp_len);
825 1477131 : princ_ncomp(p, len)[comp_len] = '\0';
826 1477131 : princ_num_comp(p)++;
827 1477131 : return 0;
828 : }
829 :
830 : static krb5_error_code
831 15462 : va_ext_princ(krb5_context context, krb5_principal p, va_list ap)
832 : {
833 15462 : krb5_error_code ret = 0;
834 :
835 28186 : while (1){
836 455 : const char *s;
837 455 : int len;
838 :
839 43481 : if ((len = va_arg(ap, int)) == 0)
840 15295 : break;
841 28019 : s = va_arg(ap, const char*);
842 28019 : if ((ret = append_component(context, p, s, len)) != 0)
843 0 : break;
844 : }
845 15462 : return ret;
846 : }
847 :
848 : static krb5_error_code
849 742106 : va_princ(krb5_context context, krb5_principal p, va_list ap)
850 : {
851 742106 : krb5_error_code ret = 0;
852 :
853 1466679 : while (1){
854 51949 : const char *s;
855 :
856 2191218 : if ((s = va_arg(ap, const char*)) == NULL)
857 724539 : break;
858 1449112 : if ((ret = append_component(context, p, s, strlen(s))) != 0)
859 0 : break;
860 : }
861 742106 : return ret;
862 : }
863 :
864 : static krb5_error_code
865 757568 : 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 17734 : krb5_error_code ret;
873 17734 : krb5_principal p;
874 :
875 757568 : *principal = NULL;
876 757568 : p = calloc(1, sizeof(*p));
877 757568 : if (p == NULL)
878 0 : return krb5_enomem(context);
879 :
880 757568 : princ_realm(p) = strdup(realm);
881 757568 : if (p->realm == NULL) {
882 0 : free(p);
883 0 : return krb5_enomem(context);
884 : }
885 :
886 757568 : ret = func(context, p, ap);
887 757568 : if (ret == 0) {
888 757568 : *principal = p;
889 757568 : set_default_princ_type(p, KRB5_NT_PRINCIPAL);
890 : } else
891 0 : krb5_free_principal(context, p);
892 739834 : return ret;
893 : }
894 :
895 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
896 742106 : 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 742106 : return build_principal(context, principal, rlen, realm, va_princ, ap);
903 : }
904 :
905 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
906 15462 : 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 15462 : 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 15462 : krb5_build_principal_ext(krb5_context context,
918 : krb5_principal *principal,
919 : int rlen,
920 : krb5_const_realm realm,
921 : ...)
922 : {
923 167 : krb5_error_code ret;
924 167 : va_list ap;
925 15462 : va_start(ap, realm);
926 15462 : ret = krb5_build_principal_va_ext(context, principal, rlen, realm, ap);
927 15462 : va_end(ap);
928 15462 : 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 6584817 : krb5_copy_principal(krb5_context context,
946 : krb5_const_principal inprinc,
947 : krb5_principal *outprinc)
948 : {
949 142481 : krb5_principal p;
950 :
951 6584817 : *outprinc = NULL;
952 :
953 6584817 : p = malloc(sizeof(*p));
954 6584817 : if (p == NULL)
955 0 : return krb5_enomem(context);
956 6584817 : if(copy_Principal(inprinc, p)) {
957 0 : free(p);
958 0 : return krb5_enomem(context);
959 : }
960 6584817 : if (inprinc->nameattrs && inprinc->nameattrs->pac)
961 1419048 : p->nameattrs->pac = heim_retain(inprinc->nameattrs->pac);
962 :
963 6584817 : *outprinc = p;
964 6584817 : 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 1407921 : krb5_principal_compare_any_realm(krb5_context context,
983 : krb5_const_principal princ1,
984 : krb5_const_principal princ2)
985 : {
986 36074 : size_t i;
987 1407921 : if(princ_num_comp(princ1) != princ_num_comp(princ2))
988 166993 : return FALSE;
989 2608179 : for(i = 0; i < princ_num_comp(princ1); i++){
990 1801001 : if(strcmp(princ_ncomp(princ1, i), princ_ncomp(princ2, i)) != 0)
991 419794 : return FALSE;
992 : }
993 785060 : return TRUE;
994 : }
995 :
996 : KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
997 1152 : _krb5_principal_compare_PrincipalName(krb5_context context,
998 : krb5_const_principal princ1,
999 : PrincipalName *princ2)
1000 : {
1001 0 : size_t i;
1002 1152 : if (princ_num_comp(princ1) != princ2->name_string.len)
1003 0 : return FALSE;
1004 4032 : for(i = 0; i < princ_num_comp(princ1); i++){
1005 2880 : if(strcmp(princ_ncomp(princ1, i), princ2->name_string.val[i]) != 0)
1006 0 : return FALSE;
1007 : }
1008 1152 : 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 1871740 : krb5_principal_compare(krb5_context context,
1031 : krb5_const_principal princ1,
1032 : krb5_const_principal princ2)
1033 : {
1034 1871740 : if (!krb5_realm_compare(context, princ1, princ2))
1035 511139 : return FALSE;
1036 1348515 : 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 1905186 : krb5_realm_compare(krb5_context context,
1053 : krb5_const_principal princ1,
1054 : krb5_const_principal princ2)
1055 : {
1056 1905186 : 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 0 : 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 0 : krb5_error_code ret;
1095 0 : 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 0 : 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 313 : krb5_realm_is_lkdc(const char *realm)
1217 : {
1218 :
1219 626 : return strncmp(realm, lkdc_prefix, sizeof(lkdc_prefix)-1) == 0 ||
1220 313 : 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 124 : krb5_principal_is_lkdc(krb5_context context, krb5_const_principal principal)
1231 : {
1232 124 : 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 name is a krbtgt principal
1249 : *
1250 : * @ingroup krb5_principal
1251 : */
1252 :
1253 : KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1254 652692 : krb5_principalname_is_krbtgt(krb5_context context, const PrincipalName *p)
1255 : {
1256 1284515 : return 1 <= p->name_string.len &&
1257 1283143 : p->name_string.len <= 2 &&
1258 651320 : strcmp(p->name_string.val[0], KRB5_TGS_NAME) == 0;
1259 : }
1260 :
1261 : /**
1262 : * Check if the cname part of the principal is a krbtgt principal
1263 : *
1264 : * @ingroup krb5_principal
1265 : */
1266 :
1267 : KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1268 600124 : krb5_principal_is_krbtgt(krb5_context context, krb5_const_principal p)
1269 : {
1270 600124 : return krb5_principalname_is_krbtgt(context, &p->name);
1271 : }
1272 :
1273 : /**
1274 : * Returns true iff name is an WELLKNOWN:ORG.H5L.HOSTBASED-SERVICE
1275 : *
1276 : * @ingroup krb5_principal
1277 : */
1278 :
1279 : krb5_boolean KRB5_LIB_FUNCTION
1280 0 : krb5_principal_is_gss_hostbased_service(krb5_context context,
1281 : krb5_const_principal principal)
1282 : {
1283 0 : if (principal == NULL)
1284 0 : return FALSE;
1285 0 : if (principal->name.name_string.len != 2)
1286 0 : return FALSE;
1287 0 : if (strcmp(principal->name.name_string.val[1], KRB5_GSS_HOSTBASED_SERVICE_NAME) != 0)
1288 0 : return FALSE;
1289 0 : return TRUE;
1290 : }
1291 :
1292 : /**
1293 : * Check if the cname part of the principal is a initial or renewed krbtgt principal
1294 : *
1295 : * @ingroup krb5_principal
1296 : */
1297 :
1298 : krb5_boolean KRB5_LIB_FUNCTION
1299 469379 : krb5_principal_is_root_krbtgt(krb5_context context, krb5_const_principal p)
1300 : {
1301 938692 : return p->name.name_string.len == 2 &&
1302 690675 : strcmp(p->name.name_string.val[0], KRB5_TGS_NAME) == 0 &&
1303 225109 : strcmp(p->name.name_string.val[1], p->realm) == 0;
1304 : }
1305 :
1306 : /**
1307 : * Returns true iff name is WELLKNOWN/ANONYMOUS
1308 : *
1309 : * @ingroup krb5_principal
1310 : */
1311 :
1312 : KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1313 127517 : krb5_principal_is_anonymous(krb5_context context,
1314 : krb5_const_principal p,
1315 : unsigned int flags)
1316 : {
1317 : /*
1318 : * Heimdal versions 7.5 and below left the name-type at KRB5_NT_PRINCIPAL
1319 : * even with anonymous pkinit responses. To retain interoperability with
1320 : * legacy KDCs, the name-type is not checked by the client after requesting
1321 : * a fully anonymous ticket.
1322 : */
1323 127517 : if (!(flags & KRB5_ANON_IGNORE_NAME_TYPE) &&
1324 127517 : p->name.name_type != KRB5_NT_WELLKNOWN &&
1325 122830 : p->name.name_type != KRB5_NT_UNKNOWN)
1326 122830 : return FALSE;
1327 :
1328 104 : if (p->name.name_string.len != 2 ||
1329 104 : strcmp(p->name.name_string.val[0], KRB5_WELLKNOWN_NAME) != 0 ||
1330 104 : strcmp(p->name.name_string.val[1], KRB5_ANON_NAME) != 0)
1331 0 : return FALSE;
1332 :
1333 : /*
1334 : * While unauthenticated clients SHOULD get "WELLKNOWN:ANONYMOUS" as their
1335 : * realm, Heimdal KDCs prior to 7.0 returned the requested realm. While
1336 : * such tickets might lead *servers* to unwittingly grant access to fully
1337 : * anonymous clients, trusting that the client was authenticated to the
1338 : * realm in question, doing it right is the KDC's job, the client should
1339 : * not refuse such a ticket.
1340 : *
1341 : * If we ever do decide to enforce WELLKNOWN:ANONYMOUS for unauthenticated
1342 : * clients, it is essential that calls that pass KRB5_ANON_MATCH_ANY still
1343 : * ignore the realm, as in that case either case matches one of the two
1344 : * possible conditions.
1345 : */
1346 104 : if (flags & KRB5_ANON_MATCH_UNAUTHENTICATED)
1347 104 : return TRUE;
1348 :
1349 : /*
1350 : * Finally, authenticated clients that asked to be only anonymized do
1351 : * legitimately expect a non-anon realm.
1352 : */
1353 0 : return strcmp(p->realm, KRB5_ANON_REALM) != 0;
1354 : }
1355 :
1356 : /**
1357 : * Returns true iff name is WELLKNOWN/FEDERATED
1358 : *
1359 : * @ingroup krb5_principal
1360 : */
1361 :
1362 : KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1363 0 : krb5_principal_is_federated(krb5_context context,
1364 : krb5_const_principal p)
1365 : {
1366 0 : if (p->name.name_type != KRB5_NT_WELLKNOWN &&
1367 0 : p->name.name_type != KRB5_NT_UNKNOWN)
1368 0 : return FALSE;
1369 :
1370 0 : if (p->name.name_string.len != 2 ||
1371 0 : strcmp(p->name.name_string.val[0], KRB5_WELLKNOWN_NAME) != 0 ||
1372 0 : strcmp(p->name.name_string.val[1], KRB5_FEDERATED_NAME) != 0)
1373 0 : return FALSE;
1374 :
1375 0 : return TRUE;
1376 : }
1377 :
1378 : static int
1379 0 : tolower_ascii(int c)
1380 : {
1381 0 : if (c >= 'A' && c <= 'Z')
1382 0 : return 'a' + (c - 'A');
1383 0 : return c;
1384 : }
1385 :
1386 : typedef enum krb5_name_canon_rule_type {
1387 : KRB5_NCRT_BOGUS = 0,
1388 : KRB5_NCRT_AS_IS,
1389 : KRB5_NCRT_QUALIFY,
1390 : KRB5_NCRT_NSS
1391 : } krb5_name_canon_rule_type;
1392 :
1393 : #ifdef UINT8_MAX
1394 : #define MAXDOTS UINT8_MAX
1395 : #else
1396 : #define MAXDOTS (255U)
1397 : #endif
1398 : #ifdef UINT16_MAX
1399 : #define MAXORDER UINT16_MAX
1400 : #else
1401 : #define MAXORDER (65535U)
1402 : #endif
1403 :
1404 : struct krb5_name_canon_rule_data {
1405 : krb5_name_canon_rule_type type;
1406 : krb5_name_canon_rule_options options;
1407 : uint8_t mindots; /* match this many dots or more */
1408 : uint8_t maxdots; /* match no more than this many dots */
1409 : uint16_t explicit_order; /* given order */
1410 : uint16_t order; /* actual order */
1411 : char *match_domain; /* match this stem */
1412 : char *match_realm; /* match this realm */
1413 : char *domain; /* qualify with this domain */
1414 : char *realm; /* qualify with this realm */
1415 : };
1416 :
1417 : /**
1418 : * Create a principal for the given service running on the given
1419 : * hostname. If KRB5_NT_SRV_HST is used, the hostname is canonicalized
1420 : * according the configured name canonicalization rules, with
1421 : * canonicalization delayed in some cases. One rule involves DNS, which
1422 : * is insecure unless DNSSEC is used, but we don't use DNSSEC-capable
1423 : * resolver APIs here, so that if DNSSEC is used we wouldn't know it.
1424 : *
1425 : * Canonicalization is immediate (not delayed) only when there is only
1426 : * one canonicalization rule and that rule indicates that we should do a
1427 : * host lookup by name (i.e., DNS).
1428 : *
1429 : * @param context A Kerberos context.
1430 : * @param hostname hostname to use
1431 : * @param sname Service name to use
1432 : * @param type name type of principal, use KRB5_NT_SRV_HST or KRB5_NT_UNKNOWN.
1433 : * @param ret_princ return principal, free with krb5_free_principal().
1434 : *
1435 : * @return An krb5 error code, see krb5_get_error_message().
1436 : *
1437 : * @ingroup krb5_principal
1438 : */
1439 :
1440 : /* coverity[+alloc : arg-*4] */
1441 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1442 1748 : krb5_sname_to_principal(krb5_context context,
1443 : const char *hostname,
1444 : const char *sname,
1445 : int32_t type,
1446 : krb5_principal *ret_princ)
1447 : {
1448 28 : char *realm, *remote_host;
1449 28 : krb5_error_code ret;
1450 28 : register char *cp;
1451 28 : char localname[MAXHOSTNAMELEN];
1452 :
1453 1748 : *ret_princ = NULL;
1454 :
1455 1748 : if ((type != KRB5_NT_UNKNOWN) &&
1456 28 : (type != KRB5_NT_SRV_HST))
1457 0 : return KRB5_SNAME_UNSUPP_NAMETYPE;
1458 :
1459 : /* if hostname is NULL, use local hostname */
1460 1748 : if (hostname == NULL) {
1461 0 : if (gethostname(localname, MAXHOSTNAMELEN))
1462 0 : return errno;
1463 0 : hostname = localname;
1464 : }
1465 :
1466 : /* if sname is NULL, use "host" */
1467 1748 : if (sname == NULL)
1468 0 : sname = "host";
1469 :
1470 1748 : remote_host = strdup(hostname);
1471 1748 : if (remote_host == NULL)
1472 0 : return krb5_enomem(context);
1473 :
1474 1748 : if (type == KRB5_NT_SRV_HST) {
1475 : krb5_name_canon_rule rules;
1476 :
1477 : /* Lower-case the hostname, because that's the convention */
1478 16353 : for (cp = remote_host; *cp; cp++)
1479 14605 : if (isupper((unsigned char) (*cp)))
1480 12831 : *cp = tolower((unsigned char) (*cp));
1481 :
1482 : /*
1483 : * If there is only one name canon rule and it says to
1484 : * canonicalize the old way, do that now, as we used to.
1485 : */
1486 1748 : ret = _krb5_get_name_canon_rules(context, &rules);
1487 1748 : if (ret) {
1488 0 : _krb5_debug(context, 5, "Failed to get name canon rules: ret = %d",
1489 : ret);
1490 0 : free(remote_host);
1491 0 : return ret;
1492 : }
1493 1748 : if (rules[0].type == KRB5_NCRT_NSS &&
1494 0 : rules[1].type == KRB5_NCRT_BOGUS) {
1495 0 : _krb5_debug(context, 5, "Using nss for name canon immediately");
1496 0 : ret = krb5_sname_to_principal_old(context, rules[0].realm,
1497 : remote_host, sname,
1498 : KRB5_NT_SRV_HST, ret_princ);
1499 0 : free(remote_host);
1500 0 : return ret;
1501 : }
1502 : }
1503 :
1504 : /* Remove trailing dots */
1505 1748 : if (remote_host[0]) {
1506 1748 : for (cp = remote_host + strlen(remote_host)-1;
1507 1748 : *cp == '.' && cp > remote_host;
1508 0 : cp--) {
1509 0 : *cp = '\0';
1510 : }
1511 : }
1512 :
1513 1748 : realm = ""; /* "Referral realm" */
1514 :
1515 1748 : ret = krb5_build_principal(context, ret_princ, strlen(realm),
1516 : realm, sname, remote_host,
1517 : (char *)0);
1518 :
1519 1748 : if (ret == 0 && type == KRB5_NT_SRV_HST) {
1520 : /*
1521 : * Hostname canonicalization is done elsewhere (in
1522 : * krb5_get_credentials() and krb5_kt_get_entry()).
1523 : *
1524 : * We overload the name type to indicate to those functions that
1525 : * this principal name requires canonicalization.
1526 : *
1527 : * We can't use the empty realm to denote the need to
1528 : * canonicalize the hostname too: it would mean that users who
1529 : * want to assert knowledge of a service's realm must also know
1530 : * the canonical hostname, but in practice they don't.
1531 : */
1532 1748 : (*ret_princ)->name.name_type = KRB5_NT_SRV_HST_NEEDS_CANON;
1533 :
1534 1748 : _krb5_debug(context, 5, "Building a delayed canon principal for %s/%s@",
1535 : sname, remote_host);
1536 : }
1537 :
1538 1748 : free(remote_host);
1539 1748 : return ret;
1540 : }
1541 :
1542 : static void
1543 0 : tolower_str(char *s)
1544 : {
1545 0 : for (; *s != '\0'; s++) {
1546 0 : if (isupper((unsigned char)*s))
1547 0 : *s = tolower_ascii(*s);
1548 : }
1549 0 : }
1550 :
1551 : static krb5_error_code
1552 0 : rule_parse_token(krb5_context context, krb5_name_canon_rule rule,
1553 : const char *tok)
1554 : {
1555 0 : long int n;
1556 0 : int needs_type = rule->type == KRB5_NCRT_BOGUS;
1557 :
1558 : /*
1559 : * Rules consist of a sequence of tokens, some of which indicate
1560 : * what type of rule the rule is, and some of which set rule options
1561 : * or ancilliary data. Last rule type token wins.
1562 : */
1563 :
1564 : /* Rule type tokens: */
1565 0 : if (needs_type && strcmp(tok, "as-is") == 0) {
1566 0 : rule->type = KRB5_NCRT_AS_IS;
1567 0 : } else if (needs_type && strcmp(tok, "qualify") == 0) {
1568 0 : rule->type = KRB5_NCRT_QUALIFY;
1569 0 : } else if (needs_type && strcmp(tok, "nss") == 0) {
1570 0 : rule->type = KRB5_NCRT_NSS;
1571 : /* Rule options: */
1572 0 : } else if (strcmp(tok, "use_fast") == 0) {
1573 0 : rule->options |= KRB5_NCRO_USE_FAST;
1574 0 : } else if (strcmp(tok, "use_dnssec") == 0) {
1575 0 : rule->options |= KRB5_NCRO_USE_DNSSEC;
1576 0 : } else if (strcmp(tok, "ccache_only") == 0) {
1577 0 : rule->options |= KRB5_NCRO_GC_ONLY;
1578 0 : } else if (strcmp(tok, "no_referrals") == 0) {
1579 0 : rule->options |= KRB5_NCRO_NO_REFERRALS;
1580 0 : } else if (strcmp(tok, "use_referrals") == 0) {
1581 0 : rule->options &= ~KRB5_NCRO_NO_REFERRALS;
1582 0 : if (rule->realm == NULL) {
1583 0 : rule->realm = strdup("");
1584 0 : if (rule->realm == NULL)
1585 0 : return krb5_enomem(context);
1586 : }
1587 0 : } else if (strcmp(tok, "lookup_realm") == 0) {
1588 0 : rule->options |= KRB5_NCRO_LOOKUP_REALM;
1589 0 : free(rule->realm);
1590 0 : rule->realm = NULL;
1591 : /* Rule ancilliary data: */
1592 0 : } else if (strncmp(tok, "domain=", strlen("domain=")) == 0) {
1593 0 : free(rule->domain);
1594 0 : rule->domain = strdup(tok + strlen("domain="));
1595 0 : if (rule->domain == NULL)
1596 0 : return krb5_enomem(context);
1597 0 : tolower_str(rule->domain);
1598 0 : } else if (strncmp(tok, "realm=", strlen("realm=")) == 0) {
1599 0 : free(rule->realm);
1600 0 : rule->realm = strdup(tok + strlen("realm="));
1601 0 : if (rule->realm == NULL)
1602 0 : return krb5_enomem(context);
1603 0 : } else if (strncmp(tok, "match_domain=", strlen("match_domain=")) == 0) {
1604 0 : free(rule->match_domain);
1605 0 : rule->match_domain = strdup(tok + strlen("match_domain="));
1606 0 : if (rule->match_domain == NULL)
1607 0 : return krb5_enomem(context);
1608 0 : tolower_str(rule->match_domain);
1609 0 : } else if (strncmp(tok, "match_realm=", strlen("match_realm=")) == 0) {
1610 0 : free(rule->match_realm);
1611 0 : rule->match_realm = strdup(tok + strlen("match_realm="));
1612 0 : if (rule->match_realm == NULL)
1613 0 : return krb5_enomem(context);
1614 0 : } else if (strncmp(tok, "mindots=", strlen("mindots=")) == 0) {
1615 0 : errno = 0;
1616 0 : n = strtol(tok + strlen("mindots="), NULL, 10);
1617 0 : if (errno == 0 && n > 0 && n <= MAXDOTS)
1618 0 : rule->mindots = n;
1619 0 : } else if (strncmp(tok, "maxdots=", strlen("maxdots=")) == 0) {
1620 0 : errno = 0;
1621 0 : n = strtol(tok + strlen("maxdots="), NULL, 10);
1622 0 : if (errno == 0 && n > 0 && n <= MAXDOTS)
1623 0 : rule->maxdots = n;
1624 0 : } else if (strncmp(tok, "order=", strlen("order=")) == 0) {
1625 0 : errno = 0;
1626 0 : n = strtol(tok + strlen("order="), NULL, 10);
1627 0 : if (errno == 0 && n > 0 && n <= MAXORDER)
1628 0 : rule->explicit_order = n;
1629 : } else {
1630 0 : _krb5_debug(context, 5,
1631 : "Unrecognized name canonicalization rule token %s", tok);
1632 0 : return EINVAL;
1633 : }
1634 0 : return 0;
1635 : }
1636 :
1637 : static int
1638 0 : rule_cmp(const void *a, const void *b)
1639 : {
1640 0 : krb5_const_name_canon_rule left = a;
1641 0 : krb5_const_name_canon_rule right = b;
1642 :
1643 0 : if (left->type == KRB5_NCRT_BOGUS &&
1644 0 : right->type == KRB5_NCRT_BOGUS)
1645 0 : return 0;
1646 0 : if (left->type == KRB5_NCRT_BOGUS)
1647 0 : return 1;
1648 0 : if (right->type == KRB5_NCRT_BOGUS)
1649 0 : return -1;
1650 0 : if (left->explicit_order < right->explicit_order)
1651 0 : return -1;
1652 0 : if (left->explicit_order > right->explicit_order)
1653 0 : return 1;
1654 0 : return left->order - right->order;
1655 : }
1656 :
1657 : static krb5_error_code
1658 281 : parse_name_canon_rules(krb5_context context, char **rulestrs,
1659 : krb5_name_canon_rule *rules)
1660 : {
1661 5 : krb5_error_code ret;
1662 5 : char *tok;
1663 5 : char *cp;
1664 5 : char **cpp;
1665 5 : size_t n;
1666 5 : size_t i, k;
1667 281 : int do_sort = 0;
1668 5 : krb5_name_canon_rule r;
1669 :
1670 281 : *rules = NULL;
1671 :
1672 281 : for (n =0, cpp = rulestrs; cpp != NULL && *cpp != NULL; cpp++)
1673 0 : n++;
1674 :
1675 281 : n += 2; /* Always at least one rule; two for the default case */
1676 :
1677 281 : if ((r = calloc(n, sizeof (*r))) == NULL)
1678 0 : return krb5_enomem(context);
1679 :
1680 843 : for (k = 0; k < n; k++) {
1681 562 : r[k].type = KRB5_NCRT_BOGUS;
1682 562 : r[k].match_domain = NULL;
1683 562 : r[k].match_realm = NULL;
1684 562 : r[k].domain = NULL;
1685 562 : r[k].realm = NULL;
1686 : }
1687 :
1688 281 : for (i = 0, k = 0; i < n && rulestrs != NULL && rulestrs[i] != NULL; i++) {
1689 0 : cp = rulestrs[i];
1690 0 : r[k].explicit_order = MAXORDER; /* mark order, see below */
1691 0 : r[k].maxdots = MAXDOTS;
1692 0 : r[k].order = k; /* default order */
1693 :
1694 : /* Tokenize and parse value */
1695 0 : do {
1696 0 : tok = cp;
1697 0 : cp = strchr(cp, ':'); /* XXX use strtok_r() */
1698 0 : if (cp)
1699 0 : *cp++ = '\0'; /* delimit token */
1700 0 : ret = rule_parse_token(context, &r[k], tok);
1701 0 : if (ret == EINVAL) {
1702 0 : r[k].type = KRB5_NCRT_BOGUS;
1703 0 : break;
1704 : }
1705 0 : if (ret) {
1706 0 : _krb5_free_name_canon_rules(context, r);
1707 0 : return ret;
1708 : }
1709 0 : } while (cp && *cp);
1710 0 : if (r[k].explicit_order != MAXORDER)
1711 0 : do_sort = 1;
1712 :
1713 : /* Validate parsed rule */
1714 0 : if (r[k].type == KRB5_NCRT_BOGUS ||
1715 0 : (r[k].type == KRB5_NCRT_QUALIFY && !r[k].domain) ||
1716 0 : (r[k].type == KRB5_NCRT_NSS && r[k].domain)) {
1717 : /* Invalid rule; mark it so and clean up */
1718 0 : r[k].type = KRB5_NCRT_BOGUS;
1719 0 : free(r[k].match_domain);
1720 0 : free(r[k].match_realm);
1721 0 : free(r[k].domain);
1722 0 : free(r[k].realm);
1723 0 : r[k].realm = NULL;
1724 0 : r[k].domain = NULL;
1725 0 : r[k].match_domain = NULL;
1726 0 : r[k].match_realm = NULL;
1727 0 : _krb5_debug(context, 5,
1728 : "Ignoring invalid name canonicalization rule %lu",
1729 : (unsigned long)i);
1730 0 : continue;
1731 : }
1732 0 : k++; /* good rule */
1733 : }
1734 :
1735 281 : if (do_sort) {
1736 : /*
1737 : * Note that we make make this a stable sort by using appareance
1738 : * and explicit order.
1739 : */
1740 0 : qsort(r, n, sizeof(r[0]), rule_cmp);
1741 : }
1742 :
1743 281 : if (r[0].type == KRB5_NCRT_BOGUS) {
1744 : /* No rules, or no valid rules */
1745 281 : if (context->flags & KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME) {
1746 0 : r[0].type = KRB5_NCRT_NSS;
1747 : } else {
1748 281 : r[0].type = KRB5_NCRT_AS_IS;
1749 : }
1750 : }
1751 :
1752 281 : *rules = r;
1753 281 : return 0; /* We don't communicate bad rule errors here */
1754 : }
1755 :
1756 : /*
1757 : * This exists only because the hostname canonicalization behavior in Heimdal
1758 : * (and other implementations of Kerberos) has been to use getaddrinfo(),
1759 : * unsafe though it is, for ages. We can't fix it in one day.
1760 : */
1761 : static void
1762 0 : make_rules_safe(krb5_context context, krb5_name_canon_rule rules)
1763 : {
1764 : /*
1765 : * If the only rule were to use the name service (getaddrinfo()) then we're
1766 : * bound to fail. We could try to convert that rule to an as-is rule, but
1767 : * when we do get a validating resolver we'd be unhappy that we did such a
1768 : * conversion. Better let the user get failures and make them think about
1769 : * their naming rules.
1770 : */
1771 0 : if (rules == NULL)
1772 0 : return;
1773 0 : for (; rules[0].type != KRB5_NCRT_BOGUS; rules++) {
1774 0 : if (rules->type == KRB5_NCRT_NSS)
1775 0 : rules->options |= KRB5_NCRO_USE_DNSSEC;
1776 : else
1777 0 : rules->options |= KRB5_NCRO_USE_FAST;
1778 : }
1779 : }
1780 :
1781 : /**
1782 : * This function returns an array of host-based service name
1783 : * canonicalization rules. The array of rules is organized as a list.
1784 : * See the definition of krb5_name_canon_rule.
1785 : *
1786 : * @param context A Kerberos context.
1787 : * @param rules Output location for array of rules.
1788 : */
1789 : KRB5_LIB_FUNCTION krb5_error_code
1790 3496 : _krb5_get_name_canon_rules(krb5_context context, krb5_name_canon_rule *rules)
1791 : {
1792 56 : krb5_error_code ret;
1793 3496 : char **values = NULL;
1794 :
1795 3496 : *rules = context->name_canon_rules;
1796 3496 : if (*rules != NULL)
1797 3164 : return 0;
1798 :
1799 281 : values = krb5_config_get_strings(context, NULL,
1800 : "libdefaults", "name_canon_rules", NULL);
1801 281 : ret = parse_name_canon_rules(context, values, rules);
1802 281 : krb5_config_free_strings(values);
1803 281 : if (ret)
1804 0 : return ret;
1805 281 : if (*rules == NULL)
1806 0 : return krb5_enomem(context);
1807 :
1808 281 : if (krb5_config_get_bool_default(context, NULL, FALSE,
1809 : "libdefaults", "safe_name_canon", NULL))
1810 0 : make_rules_safe(context, *rules);
1811 :
1812 281 : heim_assert((*rules)[0].type != KRB5_NCRT_BOGUS,
1813 : "internal error in parsing principal name "
1814 : "canonicalization rules");
1815 :
1816 : /* Memoize */
1817 281 : context->name_canon_rules = *rules;
1818 :
1819 281 : return 0;
1820 : }
1821 :
1822 : static krb5_error_code
1823 0 : get_host_realm(krb5_context context, const char *hostname, char **realm)
1824 : {
1825 0 : krb5_error_code ret;
1826 0 : char **hrealms = NULL;
1827 :
1828 0 : *realm = NULL;
1829 0 : ret = krb5_get_host_realm(context, hostname, &hrealms);
1830 0 : if (ret)
1831 0 : return ret;
1832 0 : if (hrealms == NULL)
1833 0 : return KRB5_ERR_HOST_REALM_UNKNOWN; /* krb5_set_error() already done */
1834 0 : if (hrealms[0] == NULL) {
1835 0 : krb5_free_host_realm(context, hrealms);
1836 0 : return KRB5_ERR_HOST_REALM_UNKNOWN; /* krb5_set_error() already done */
1837 : }
1838 0 : *realm = strdup(hrealms[0]);
1839 0 : krb5_free_host_realm(context, hrealms);
1840 0 : if (*realm == NULL)
1841 0 : return krb5_enomem(context);
1842 0 : return 0;
1843 : }
1844 :
1845 : static int
1846 0 : is_domain_suffix(const char *domain, const char *suffix)
1847 : {
1848 0 : size_t dlen = strlen(domain);
1849 0 : size_t slen = strlen(suffix);
1850 :
1851 0 : if (dlen < slen + 2)
1852 0 : return 0;
1853 :
1854 0 : if (strcasecmp(domain + (dlen - slen), suffix) != 0)
1855 0 : return 0;
1856 :
1857 0 : if (domain[(dlen - slen) - 1] != '.')
1858 0 : return 0;
1859 0 : return 1;
1860 : }
1861 :
1862 : /*
1863 : * Applies a name canonicalization rule to a principal.
1864 : *
1865 : * Returns zero and no out_princ if the rule does not match.
1866 : * Returns zero and an out_princ if the rule does match.
1867 : */
1868 : static krb5_error_code
1869 1748 : apply_name_canon_rule(krb5_context context, krb5_name_canon_rule rules,
1870 : size_t rule_idx, krb5_const_principal in_princ,
1871 : krb5_principal *out_princ,
1872 : krb5_name_canon_rule_options *rule_opts)
1873 : {
1874 1748 : krb5_name_canon_rule rule = &rules[rule_idx];
1875 1748 : krb5_error_code ret = 0;
1876 1748 : unsigned int ndots = 0;
1877 1748 : krb5_principal nss = NULL;
1878 1748 : const char *sname = NULL;
1879 1748 : const char *orig_hostname = NULL;
1880 1748 : const char *new_hostname = NULL;
1881 1748 : const char *new_realm = NULL;
1882 1748 : const char *port = "";
1883 28 : const char *cp;
1884 1748 : char *hostname_sans_port = NULL;
1885 1748 : char *hostname_with_port = NULL;
1886 1748 : char *tmp_hostname = NULL;
1887 1748 : char *tmp_realm = NULL;
1888 :
1889 1748 : *out_princ = NULL; /* Signal no match */
1890 :
1891 1748 : if (rule_opts != NULL)
1892 1748 : *rule_opts = rule->options;
1893 :
1894 1748 : if (rule->type == KRB5_NCRT_BOGUS)
1895 0 : return 0; /* rule doesn't apply */
1896 :
1897 1748 : sname = krb5_principal_get_comp_string(context, in_princ, 0);
1898 1748 : orig_hostname = krb5_principal_get_comp_string(context, in_princ, 1);
1899 :
1900 : /*
1901 : * Some apps want to use the very non-standard svc/hostname:port@REALM
1902 : * form. We do our best to support that here :(
1903 : */
1904 1748 : port = strchr(orig_hostname, ':');
1905 1748 : if (port != NULL) {
1906 0 : hostname_sans_port = strndup(orig_hostname, port - orig_hostname);
1907 0 : if (hostname_sans_port == NULL)
1908 0 : return krb5_enomem(context);
1909 0 : orig_hostname = hostname_sans_port;
1910 : }
1911 :
1912 1748 : _krb5_debug(context, 5, N_("Applying a name rule (type %d) to %s", ""),
1913 1748 : rule->type, orig_hostname);
1914 :
1915 1748 : if (rule->mindots > 0 || rule->maxdots > 0) {
1916 0 : for (cp = strchr(orig_hostname, '.'); cp && *cp; cp = strchr(cp + 1, '.'))
1917 0 : ndots++;
1918 : }
1919 1748 : if (rule->mindots > 0 && ndots < rule->mindots)
1920 0 : goto out;
1921 1748 : if (ndots > rule->maxdots)
1922 0 : goto out;
1923 :
1924 1748 : if (rule->match_domain != NULL &&
1925 0 : !is_domain_suffix(orig_hostname, rule->match_domain))
1926 0 : goto out;
1927 :
1928 1748 : if (rule->match_realm != NULL &&
1929 0 : strcmp(rule->match_realm, in_princ->realm) != 0)
1930 0 : goto out;
1931 :
1932 1748 : new_realm = rule->realm;
1933 1748 : switch (rule->type) {
1934 1720 : case KRB5_NCRT_AS_IS:
1935 1720 : break;
1936 :
1937 0 : case KRB5_NCRT_QUALIFY:
1938 0 : heim_assert(rule->domain != NULL,
1939 : "missing domain for qualify name canon rule");
1940 0 : if (asprintf(&tmp_hostname, "%s.%s", orig_hostname,
1941 0 : rule->domain) == -1 || tmp_hostname == NULL) {
1942 0 : ret = krb5_enomem(context);
1943 0 : goto out;
1944 : }
1945 0 : new_hostname = tmp_hostname;
1946 0 : break;
1947 :
1948 0 : case KRB5_NCRT_NSS:
1949 0 : if ((rule->options & KRB5_NCRO_USE_DNSSEC)) {
1950 0 : ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1951 0 : krb5_set_error_message(context, ret,
1952 : "Secure hostname resolution not supported");
1953 0 : goto out;
1954 : }
1955 0 : _krb5_debug(context, 5, "Using name service lookups");
1956 0 : ret = krb5_sname_to_principal_old(context, rule->realm,
1957 : orig_hostname, sname,
1958 : KRB5_NT_SRV_HST,
1959 : &nss);
1960 0 : if (rules[rule_idx + 1].type != KRB5_NCRT_BOGUS &&
1961 0 : (ret == KRB5_ERR_BAD_HOSTNAME ||
1962 : ret == KRB5_ERR_HOST_REALM_UNKNOWN)) {
1963 : /*
1964 : * Bad hostname / realm unknown -> rule inapplicable if
1965 : * there's more rules. If it's the last rule then we want
1966 : * to return all errors from krb5_sname_to_principal_old()
1967 : * here.
1968 : */
1969 0 : ret = 0;
1970 0 : goto out;
1971 : }
1972 0 : if (ret)
1973 0 : goto out;
1974 :
1975 0 : new_hostname = krb5_principal_get_comp_string(context, nss, 1);
1976 0 : new_realm = krb5_principal_get_realm(context, nss);
1977 0 : break;
1978 :
1979 0 : default:
1980 : /* Can't happen */
1981 0 : ret = 0;
1982 0 : goto out;
1983 : }
1984 :
1985 : /*
1986 : * This rule applies.
1987 : *
1988 : * Copy in_princ and mutate the copy per the matched rule.
1989 : *
1990 : * This way we apply to principals with two or more components, such as
1991 : * domain-based names.
1992 : */
1993 1748 : ret = krb5_copy_principal(context, in_princ, out_princ);
1994 1748 : if (ret)
1995 0 : goto out;
1996 :
1997 1748 : if (new_realm == NULL && (rule->options & KRB5_NCRO_LOOKUP_REALM) != 0) {
1998 0 : ret = get_host_realm(context, new_hostname, &tmp_realm);
1999 0 : if (ret)
2000 0 : goto out;
2001 0 : new_realm = tmp_realm;
2002 : }
2003 :
2004 : /* If we stripped off a :port, add it back in */
2005 1748 : if (port != NULL && new_hostname != NULL) {
2006 0 : if (asprintf(&hostname_with_port, "%s%s", new_hostname, port) == -1 ||
2007 0 : hostname_with_port == NULL) {
2008 0 : ret = krb5_enomem(context);
2009 0 : goto out;
2010 : }
2011 0 : new_hostname = hostname_with_port;
2012 : }
2013 :
2014 1748 : if (new_realm != NULL &&
2015 0 : (ret = krb5_principal_set_realm(context, *out_princ, new_realm)))
2016 0 : goto out;
2017 1748 : if (new_hostname != NULL &&
2018 0 : (ret = krb5_principal_set_comp_string(context, *out_princ, 1, new_hostname)))
2019 0 : goto out;
2020 1748 : if (princ_type(*out_princ) == KRB5_NT_SRV_HST_NEEDS_CANON)
2021 1748 : princ_type(*out_princ) = KRB5_NT_SRV_HST;
2022 :
2023 : /* Trace rule application */
2024 : {
2025 28 : krb5_error_code ret2;
2026 28 : char *unparsed;
2027 :
2028 1748 : ret2 = krb5_unparse_name(context, *out_princ, &unparsed);
2029 1748 : if (ret2) {
2030 0 : _krb5_debug(context, 5,
2031 0 : N_("Couldn't unparse canonicalized princicpal (%d)",
2032 : ""),
2033 : ret);
2034 : } else {
2035 1748 : _krb5_debug(context, 5,
2036 1748 : N_("Name canon rule application yields %s", ""),
2037 : unparsed);
2038 1748 : free(unparsed);
2039 : }
2040 : }
2041 :
2042 1748 : out:
2043 1748 : free(hostname_sans_port);
2044 1748 : free(hostname_with_port);
2045 1748 : free(tmp_hostname);
2046 1748 : free(tmp_realm);
2047 1748 : krb5_free_principal(context, nss);
2048 1748 : if (ret)
2049 0 : krb5_set_error_message(context, ret,
2050 0 : N_("Name canon rule application failed", ""));
2051 1720 : return ret;
2052 : }
2053 :
2054 : /**
2055 : * Free name canonicalization rules
2056 : */
2057 : KRB5_LIB_FUNCTION void
2058 753173 : _krb5_free_name_canon_rules(krb5_context context, krb5_name_canon_rule rules)
2059 : {
2060 17909 : size_t k;
2061 :
2062 753173 : if (rules == NULL)
2063 735264 : return;
2064 :
2065 0 : for (k = 0; rules[k].type != KRB5_NCRT_BOGUS; k++) {
2066 0 : free(rules[k].match_domain);
2067 0 : free(rules[k].match_realm);
2068 0 : free(rules[k].domain);
2069 0 : free(rules[k].realm);
2070 : }
2071 0 : free(rules);
2072 : }
2073 :
2074 : struct krb5_name_canon_iterator_data {
2075 : krb5_name_canon_rule rules;
2076 : krb5_const_principal in_princ; /* given princ */
2077 : krb5_const_principal out_princ; /* princ to be output */
2078 : krb5_principal tmp_princ; /* to be freed */
2079 : int is_trivial; /* no canon to be done */
2080 : int done; /* no more rules to be applied */
2081 : size_t cursor; /* current/next rule */
2082 : };
2083 :
2084 : /**
2085 : * Initialize name canonicalization iterator.
2086 : *
2087 : * @param context Kerberos context
2088 : * @param in_princ principal name to be canonicalized OR
2089 : * @param iter output iterator object
2090 : */
2091 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2092 158730 : krb5_name_canon_iterator_start(krb5_context context,
2093 : krb5_const_principal in_princ,
2094 : krb5_name_canon_iterator *iter)
2095 : {
2096 5021 : krb5_error_code ret;
2097 5021 : krb5_name_canon_iterator state;
2098 :
2099 158730 : *iter = NULL;
2100 :
2101 158730 : state = calloc(1, sizeof (*state));
2102 158730 : if (state == NULL)
2103 0 : return krb5_enomem(context);
2104 158730 : state->in_princ = in_princ;
2105 :
2106 158730 : if (princ_type(state->in_princ) == KRB5_NT_SRV_HST_NEEDS_CANON) {
2107 1748 : ret = _krb5_get_name_canon_rules(context, &state->rules);
2108 1748 : if (ret)
2109 0 : goto out;
2110 : } else {
2111 : /* Name needs no canon -> trivial iterator: in_princ is canonical */
2112 156982 : state->is_trivial = 1;
2113 : }
2114 :
2115 158730 : *iter = state;
2116 158730 : return 0;
2117 :
2118 0 : out:
2119 0 : krb5_free_name_canon_iterator(context, state);
2120 0 : return krb5_enomem(context);
2121 : }
2122 :
2123 : /*
2124 : * Helper for name canon iteration.
2125 : */
2126 : static krb5_error_code
2127 160375 : name_canon_iterate(krb5_context context,
2128 : krb5_name_canon_iterator *iter,
2129 : krb5_name_canon_rule_options *rule_opts)
2130 : {
2131 5021 : krb5_error_code ret;
2132 160375 : krb5_name_canon_iterator state = *iter;
2133 :
2134 160375 : if (rule_opts)
2135 105985 : *rule_opts = 0;
2136 :
2137 160375 : if (state == NULL)
2138 0 : return 0;
2139 :
2140 160375 : if (state->done) {
2141 1645 : krb5_free_name_canon_iterator(context, state);
2142 1645 : *iter = NULL;
2143 1645 : return 0;
2144 : }
2145 :
2146 158730 : if (state->is_trivial && !state->done) {
2147 156982 : state->out_princ = state->in_princ;
2148 156982 : state->done = 1;
2149 156982 : return 0;
2150 : }
2151 :
2152 1748 : heim_assert(state->rules != NULL &&
2153 : state->rules[state->cursor].type != KRB5_NCRT_BOGUS,
2154 : "Internal error during name canonicalization");
2155 :
2156 28 : do {
2157 1748 : krb5_free_principal(context, state->tmp_princ);
2158 1748 : ret = apply_name_canon_rule(context, state->rules, state->cursor,
2159 : state->in_princ, &state->tmp_princ, rule_opts);
2160 1748 : if (ret) {
2161 0 : krb5_free_name_canon_iterator(context, state);
2162 0 : *iter = NULL;
2163 0 : return ret;
2164 : }
2165 1748 : state->cursor++;
2166 1748 : } while (state->tmp_princ == NULL &&
2167 0 : state->rules[state->cursor].type != KRB5_NCRT_BOGUS);
2168 :
2169 1748 : if (state->rules[state->cursor].type == KRB5_NCRT_BOGUS)
2170 1748 : state->done = 1;
2171 :
2172 1748 : state->out_princ = state->tmp_princ;
2173 1748 : if (state->tmp_princ == NULL) {
2174 0 : krb5_free_name_canon_iterator(context, state);
2175 0 : *iter = NULL;
2176 0 : return 0;
2177 : }
2178 1720 : return 0;
2179 : }
2180 :
2181 : /**
2182 : * Iteratively apply name canon rules, outputing a principal and rule
2183 : * options each time. Iteration completes when the @iter is NULL on
2184 : * return or when an error is returned. Callers must free the iterator
2185 : * if they abandon it mid-way.
2186 : *
2187 : * @param context Kerberos context
2188 : * @param iter name canon rule iterator (input/output)
2189 : * @param try_princ output principal name
2190 : * @param rule_opts output rule options
2191 : */
2192 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2193 160375 : krb5_name_canon_iterate(krb5_context context,
2194 : krb5_name_canon_iterator *iter,
2195 : krb5_const_principal *try_princ,
2196 : krb5_name_canon_rule_options *rule_opts)
2197 : {
2198 5021 : krb5_error_code ret;
2199 :
2200 160375 : *try_princ = NULL;
2201 :
2202 160375 : ret = name_canon_iterate(context, iter, rule_opts);
2203 160375 : if (*iter)
2204 158730 : *try_princ = (*iter)->out_princ;
2205 160375 : return ret;
2206 : }
2207 :
2208 : /**
2209 : * Free a name canonicalization rule iterator.
2210 : */
2211 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2212 160375 : krb5_free_name_canon_iterator(krb5_context context,
2213 : krb5_name_canon_iterator iter)
2214 : {
2215 160375 : if (iter == NULL)
2216 1645 : return;
2217 158730 : if (iter->tmp_princ)
2218 1748 : krb5_free_principal(context, iter->tmp_princ);
2219 158730 : free(iter);
2220 : }
|