Line data Source code
1 : /*
2 : Authors:
3 : Sumit Bose <sbose@redhat.com>
4 :
5 : Copyright (C) 2009-2010 Red Hat
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 : #include <stdio.h>
21 : #include <errno.h>
22 : #include <talloc.h>
23 : #include <profile.h>
24 :
25 : #include "config.h"
26 :
27 : #include "util/util.h"
28 : #include "util/sss_krb5.h"
29 :
30 : static char *
31 0 : sss_krb5_get_primary(TALLOC_CTX *mem_ctx,
32 : const char *pattern,
33 : const char *hostname)
34 : {
35 : char *primary;
36 : char *dot;
37 : char *c;
38 : char *shortname;
39 :
40 0 : if (strcmp(pattern, "%S$") == 0) {
41 0 : shortname = talloc_strdup(mem_ctx, hostname);
42 0 : if (!shortname) return NULL;
43 :
44 0 : dot = strchr(shortname, '.');
45 0 : if (dot) {
46 0 : *dot = '\0';
47 : }
48 :
49 0 : for (c=shortname; *c != '\0'; ++c) {
50 0 : *c = toupper(*c);
51 : }
52 :
53 0 : primary = talloc_asprintf(mem_ctx, "%s$", shortname);
54 0 : talloc_free(shortname);
55 0 : return primary;
56 : }
57 :
58 0 : return talloc_asprintf(mem_ctx, pattern, hostname);
59 : }
60 :
61 0 : errno_t select_principal_from_keytab(TALLOC_CTX *mem_ctx,
62 : const char *hostname,
63 : const char *desired_realm,
64 : const char *keytab_name,
65 : char **_principal,
66 : char **_primary,
67 : char **_realm)
68 : {
69 0 : krb5_error_code kerr = 0;
70 0 : krb5_context krb_ctx = NULL;
71 0 : krb5_keytab keytab = NULL;
72 0 : krb5_principal client_princ = NULL;
73 : TALLOC_CTX *tmp_ctx;
74 0 : char *primary = NULL;
75 0 : char *realm = NULL;
76 0 : int i = 0;
77 : errno_t ret;
78 : char *principal_string;
79 : const char *realm_name;
80 : int realm_len;
81 :
82 : /**
83 : * The %s conversion is passed as-is, the %S conversion is translated to
84 : * "short host name"
85 : *
86 : * Priority of lookup:
87 : * - our.hostname@REALM or host/our.hostname@REALM depending on the input
88 : * - SHORT.HOSTNAME$@REALM (AD domain)
89 : * - host/our.hostname@REALM
90 : * - foobar$@REALM (AD domain)
91 : * - host/foobar@REALM
92 : * - host/foo@BAR
93 : * - pick the first principal in the keytab
94 : */
95 0 : const char *primary_patterns[] = {"%s", "%S$", "host/%s", "*$", "host/*",
96 : "host/*", NULL};
97 0 : const char *realm_patterns[] = {"%s", "%s", "%s", "%s", "%s",
98 : NULL, NULL};
99 :
100 0 : DEBUG(SSSDBG_FUNC_DATA,
101 : "trying to select the most appropriate principal from keytab\n");
102 0 : tmp_ctx = talloc_new(NULL);
103 0 : if (!tmp_ctx) {
104 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed\n");
105 0 : return ENOMEM;
106 : }
107 :
108 0 : kerr = krb5_init_context(&krb_ctx);
109 0 : if (kerr) {
110 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to init kerberos context\n");
111 0 : ret = EFAULT;
112 0 : goto done;
113 : }
114 :
115 0 : if (keytab_name != NULL) {
116 0 : kerr = krb5_kt_resolve(krb_ctx, keytab_name, &keytab);
117 : } else {
118 0 : kerr = krb5_kt_default(krb_ctx, &keytab);
119 : }
120 0 : if (kerr) {
121 0 : DEBUG(SSSDBG_FATAL_FAILURE,
122 : "Failed to read keytab [%s]: %s\n",
123 : KEYTAB_CLEAN_NAME,
124 : sss_krb5_get_error_message(krb_ctx, kerr));
125 0 : ret = EFAULT;
126 0 : goto done;
127 : }
128 :
129 0 : if (!desired_realm) {
130 0 : desired_realm = "*";
131 : }
132 0 : if (!hostname) {
133 0 : hostname = "*";
134 : }
135 :
136 : do {
137 0 : if (primary_patterns[i]) {
138 0 : primary = sss_krb5_get_primary(tmp_ctx,
139 : primary_patterns[i],
140 : hostname);
141 0 : if (primary == NULL) {
142 0 : ret = ENOMEM;
143 0 : goto done;
144 : }
145 : } else {
146 0 : primary = NULL;
147 : }
148 0 : if (realm_patterns[i]) {
149 0 : realm = talloc_asprintf(tmp_ctx, realm_patterns[i], desired_realm);
150 0 : if (realm == NULL) {
151 0 : ret = ENOMEM;
152 0 : goto done;
153 : }
154 : } else {
155 0 : realm = NULL;
156 : }
157 :
158 0 : kerr = find_principal_in_keytab(krb_ctx, keytab, primary, realm,
159 : &client_princ);
160 0 : talloc_zfree(primary);
161 0 : talloc_zfree(realm);
162 0 : if (kerr == 0) {
163 0 : break;
164 : }
165 0 : if (client_princ != NULL) {
166 0 : krb5_free_principal(krb_ctx, client_princ);
167 0 : client_princ = NULL;
168 : }
169 0 : i++;
170 0 : } while(primary_patterns[i-1] != NULL || realm_patterns[i-1] != NULL);
171 :
172 0 : if (kerr == 0) {
173 0 : if (_principal) {
174 0 : kerr = krb5_unparse_name(krb_ctx, client_princ, &principal_string);
175 0 : if (kerr) {
176 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_unparse_name failed\n");
177 0 : ret = EFAULT;
178 0 : goto done;
179 : }
180 :
181 0 : *_principal = talloc_strdup(mem_ctx, principal_string);
182 0 : free(principal_string);
183 0 : if (!*_principal) {
184 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed\n");
185 0 : ret = ENOMEM;
186 0 : goto done;
187 : }
188 0 : DEBUG(SSSDBG_FUNC_DATA, "Selected principal: %s\n", *_principal);
189 : }
190 :
191 0 : if (_primary) {
192 0 : kerr = sss_krb5_unparse_name_flags(krb_ctx, client_princ,
193 : KRB5_PRINCIPAL_UNPARSE_NO_REALM,
194 : &principal_string);
195 0 : if (kerr) {
196 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_unparse_name failed\n");
197 0 : ret = EFAULT;
198 0 : goto done;
199 : }
200 :
201 0 : *_primary = talloc_strdup(mem_ctx, principal_string);
202 0 : free(principal_string);
203 0 : if (!*_primary) {
204 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed\n");
205 0 : if (_principal) talloc_zfree(*_principal);
206 0 : ret = ENOMEM;
207 0 : goto done;
208 : }
209 0 : DEBUG(SSSDBG_FUNC_DATA, "Selected primary: %s\n", *_primary);
210 : }
211 :
212 0 : if (_realm) {
213 0 : sss_krb5_princ_realm(krb_ctx, client_princ,
214 : &realm_name,
215 : &realm_len);
216 0 : if (realm_len == 0) {
217 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sss_krb5_princ_realm failed.\n");
218 0 : if (_principal) talloc_zfree(*_principal);
219 0 : if (_primary) talloc_zfree(*_primary);
220 0 : ret = EINVAL;
221 0 : goto done;
222 : }
223 :
224 0 : *_realm = talloc_asprintf(mem_ctx, "%.*s",
225 : realm_len, realm_name);
226 0 : if (!*_realm) {
227 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed\n");
228 0 : if (_principal) talloc_zfree(*_principal);
229 0 : if (_primary) talloc_zfree(*_primary);
230 0 : ret = ENOMEM;
231 0 : goto done;
232 : }
233 0 : DEBUG(SSSDBG_FUNC_DATA, "Selected realm: %s\n", *_realm);
234 : }
235 :
236 0 : ret = EOK;
237 : } else {
238 0 : DEBUG(SSSDBG_MINOR_FAILURE, "No suitable principal found in keytab\n");
239 0 : ret = ENOENT;
240 : }
241 :
242 : done:
243 0 : if (ret != EOK) {
244 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Failed to read keytab [%s]: %s\n",
245 : KEYTAB_CLEAN_NAME, strerror(ret));
246 0 : sss_log(SSS_LOG_ERR, "Failed to read keytab [%s]: %s\n",
247 : KEYTAB_CLEAN_NAME, strerror(ret));
248 : }
249 0 : if (keytab) krb5_kt_close(krb_ctx, keytab);
250 0 : if (krb_ctx) krb5_free_context(krb_ctx);
251 0 : if (client_princ != NULL) {
252 0 : krb5_free_principal(krb_ctx, client_princ);
253 0 : client_princ = NULL;
254 : }
255 0 : talloc_free(tmp_ctx);
256 0 : return ret;
257 : }
258 :
259 : enum matching_mode {MODE_NORMAL, MODE_PREFIX, MODE_POSTFIX};
260 : /**
261 : * We only have primary and instances stored separately, we need to
262 : * join them to one string and compare that string.
263 : *
264 : * @param ctx kerberos context
265 : * @param principal principal we want to match
266 : * @param pattern_primary primary part of the principal we want to
267 : * perform matching against. It is possible to use * wildcard
268 : * at the beginning or at the end of the string. If NULL, it
269 : * will act as "*"
270 : * @param pattern_realm realm part of the principal we want to perform
271 : * the matching against. If NULL, it will act as "*"
272 : */
273 0 : static bool match_principal(krb5_context ctx,
274 : krb5_principal principal,
275 : const char *pattern_primary,
276 : const char *pattern_realm)
277 : {
278 0 : char *primary = NULL;
279 0 : char *primary_str = NULL;
280 0 : int primary_str_len = 0;
281 : int tmp_len;
282 : int len_diff;
283 : const char *realm_name;
284 : int realm_len;
285 :
286 0 : enum matching_mode mode = MODE_NORMAL;
287 : TALLOC_CTX *tmp_ctx;
288 0 : bool ret = false;
289 :
290 0 : sss_krb5_princ_realm(ctx, principal, &realm_name, &realm_len);
291 0 : if (realm_len == 0) {
292 0 : DEBUG(SSSDBG_MINOR_FAILURE, "sss_krb5_princ_realm failed.\n");
293 0 : return false;
294 : }
295 :
296 0 : tmp_ctx = talloc_new(NULL);
297 0 : if (!tmp_ctx) {
298 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed\n");
299 0 : return false;
300 : }
301 :
302 0 : if (pattern_primary) {
303 0 : tmp_len = strlen(pattern_primary);
304 0 : if (pattern_primary[tmp_len-1] == '*') {
305 0 : mode = MODE_PREFIX;
306 0 : primary_str = talloc_strdup(tmp_ctx, pattern_primary);
307 0 : primary_str[tmp_len-1] = '\0';
308 0 : primary_str_len = tmp_len-1;
309 0 : } else if (pattern_primary[0] == '*') {
310 0 : mode = MODE_POSTFIX;
311 0 : primary_str = talloc_strdup(tmp_ctx, pattern_primary+1);
312 0 : primary_str_len = tmp_len-1;
313 : }
314 :
315 0 : sss_krb5_unparse_name_flags(ctx, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM,
316 : &primary);
317 :
318 0 : len_diff = strlen(primary)-primary_str_len;
319 :
320 0 : if ((mode == MODE_NORMAL &&
321 0 : strcmp(primary, pattern_primary) != 0) ||
322 0 : (mode == MODE_PREFIX &&
323 0 : strncmp(primary, primary_str, primary_str_len) != 0) ||
324 0 : (mode == MODE_POSTFIX &&
325 0 : strcmp(primary+len_diff, primary_str) != 0)) {
326 : goto done;
327 : }
328 : }
329 :
330 0 : if (!pattern_realm || (realm_len == strlen(pattern_realm) &&
331 0 : strncmp(realm_name, pattern_realm, realm_len) == 0)) {
332 0 : DEBUG(SSSDBG_TRACE_LIBS,
333 : "Principal matched to the sample (%s@%s).\n", pattern_primary,
334 : pattern_realm);
335 0 : ret = true;
336 : }
337 :
338 : done:
339 0 : free(primary);
340 0 : talloc_free(tmp_ctx);
341 0 : return ret;
342 : }
343 :
344 0 : krb5_error_code find_principal_in_keytab(krb5_context ctx,
345 : krb5_keytab keytab,
346 : const char *pattern_primary,
347 : const char *pattern_realm,
348 : krb5_principal *princ)
349 : {
350 : krb5_error_code kerr;
351 : krb5_error_code kt_err;
352 : krb5_error_code kerr_d;
353 : krb5_kt_cursor cursor;
354 : krb5_keytab_entry entry;
355 0 : bool principal_found = false;
356 :
357 0 : memset(&cursor, 0, sizeof(cursor));
358 0 : kerr = krb5_kt_start_seq_get(ctx, keytab, &cursor);
359 0 : if (kerr != 0) {
360 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_kt_start_seq_get failed.\n");
361 0 : return kerr;
362 : }
363 :
364 0 : DEBUG(SSSDBG_TRACE_ALL,
365 : "Trying to find principal %s@%s in keytab.\n", pattern_primary, pattern_realm);
366 0 : memset(&entry, 0, sizeof(entry));
367 0 : while ((kt_err = krb5_kt_next_entry(ctx, keytab, &entry, &cursor)) == 0) {
368 0 : principal_found = match_principal(ctx, entry.principal, pattern_primary, pattern_realm);
369 0 : if (principal_found) {
370 0 : break;
371 : }
372 :
373 0 : kerr = sss_krb5_free_keytab_entry_contents(ctx, &entry);
374 0 : if (kerr != 0) {
375 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to free keytab entry.\n");
376 : }
377 0 : memset(&entry, 0, sizeof(entry));
378 : }
379 :
380 : /* Close the keytab here. Even though we're using cursors, the file
381 : * handle is stored in the krb5_keytab structure, and it gets
382 : * overwritten by other keytab calls, creating a leak. */
383 0 : kerr = krb5_kt_end_seq_get(ctx, keytab, &cursor);
384 0 : if (kerr != 0) {
385 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_kt_end_seq_get failed.\n");
386 0 : goto done;
387 : }
388 :
389 0 : if (!principal_found) {
390 0 : kerr = KRB5_KT_NOTFOUND;
391 0 : DEBUG(SSSDBG_TRACE_FUNC,
392 : "No principal matching %s@%s found in keytab.\n",
393 : pattern_primary, pattern_realm);
394 0 : goto done;
395 : }
396 :
397 : /* check if we got any errors from krb5_kt_next_entry */
398 0 : if (kt_err != 0 && kt_err != KRB5_KT_END) {
399 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Error while reading keytab.\n");
400 0 : goto done;
401 : }
402 :
403 0 : kerr = krb5_copy_principal(ctx, entry.principal, princ);
404 0 : if (kerr != 0) {
405 0 : DEBUG(SSSDBG_CRIT_FAILURE, "krb5_copy_principal failed.\n");
406 0 : goto done;
407 : }
408 :
409 0 : kerr = 0;
410 :
411 : done:
412 0 : kerr_d = sss_krb5_free_keytab_entry_contents(ctx, &entry);
413 0 : if (kerr_d != 0) {
414 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to free keytab entry.\n");
415 : }
416 :
417 0 : return kerr;
418 : }
419 :
420 0 : const char *KRB5_CALLCONV sss_krb5_get_error_message(krb5_context ctx,
421 : krb5_error_code ec)
422 : {
423 : #ifdef HAVE_KRB5_GET_ERROR_MESSAGE
424 0 : return krb5_get_error_message(ctx, ec);
425 : #else
426 : int ret;
427 : char *s = NULL;
428 : int size = sizeof("Kerberos error [XXXXXXXXXXXX]");
429 :
430 : s = malloc(sizeof(char) * (size));
431 : if (s == NULL) {
432 : return NULL;
433 : }
434 :
435 : ret = snprintf(s, size, "Kerberos error [%12d]", ec);
436 :
437 : if (ret < 0 || ret >= size) {
438 : free(s);
439 : return NULL;
440 : }
441 :
442 : return s;
443 : #endif
444 : }
445 :
446 0 : void KRB5_CALLCONV sss_krb5_free_error_message(krb5_context ctx, const char *s)
447 : {
448 : #ifdef HAVE_KRB5_GET_ERROR_MESSAGE
449 0 : krb5_free_error_message(ctx, s);
450 : #else
451 : free(s);
452 : #endif
453 :
454 0 : return;
455 : }
456 :
457 0 : krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_alloc(
458 : krb5_context context,
459 : krb5_get_init_creds_opt **opt)
460 : {
461 : #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
462 0 : return krb5_get_init_creds_opt_alloc(context, opt);
463 : #else
464 : *opt = calloc(1, sizeof(krb5_get_init_creds_opt));
465 : if (*opt == NULL) {
466 : return ENOMEM;
467 : }
468 : krb5_get_init_creds_opt_init(*opt);
469 :
470 : return 0;
471 : #endif
472 : }
473 :
474 0 : void KRB5_CALLCONV sss_krb5_get_init_creds_opt_free (krb5_context context,
475 : krb5_get_init_creds_opt *opt)
476 : {
477 : #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
478 0 : krb5_get_init_creds_opt_free(context, opt);
479 : #else
480 : free(opt);
481 : #endif
482 :
483 0 : return;
484 : }
485 :
486 0 : void KRB5_CALLCONV sss_krb5_free_unparsed_name(krb5_context context, char *name)
487 : {
488 : #ifdef HAVE_KRB5_FREE_UNPARSED_NAME
489 0 : krb5_free_unparsed_name(context, name);
490 : #else
491 : if (name != NULL) {
492 : memset(name, 0, strlen(name));
493 : free(name);
494 : }
495 : #endif
496 0 : }
497 :
498 :
499 0 : krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_set_expire_callback(
500 : krb5_context context,
501 : krb5_get_init_creds_opt *opt,
502 : krb5_expire_callback_func cb,
503 : void *data)
504 : {
505 : #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_EXPIRE_CALLBACK
506 0 : return krb5_get_init_creds_opt_set_expire_callback(context, opt, cb, data);
507 : #else
508 : DEBUG(SSSDBG_FUNC_DATA,
509 : "krb5_get_init_creds_opt_set_expire_callback not available.\n");
510 : return 0;
511 : #endif
512 : }
513 :
514 0 : errno_t check_fast(const char *str, bool *use_fast)
515 : {
516 : #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_FLAGS
517 0 : if (strcasecmp(str, "never") == 0 ) {
518 0 : *use_fast = false;
519 0 : } else if (strcasecmp(str, "try") == 0 || strcasecmp(str, "demand") == 0) {
520 0 : *use_fast = true;
521 : } else {
522 0 : sss_log(SSS_LOG_ALERT, "Unsupported value [%s] for option krb5_use_fast,"
523 : "please use never, try, or demand.\n", str);
524 0 : return EINVAL;
525 : }
526 :
527 0 : return EOK;
528 : #else
529 : sss_log(SSS_LOG_ALERT, "This build of sssd does not support FAST. "
530 : "Please remove option krb5_use_fast.\n");
531 : return EINVAL;
532 : #endif
533 : }
534 :
535 0 : krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_set_fast_ccache_name(
536 : krb5_context context,
537 : krb5_get_init_creds_opt *opt,
538 : const char *fast_ccache_name)
539 : {
540 : #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_CCACHE_NAME
541 0 : return krb5_get_init_creds_opt_set_fast_ccache_name(context, opt,
542 : fast_ccache_name);
543 : #else
544 : DEBUG(SSSDBG_FUNC_DATA,
545 : "krb5_get_init_creds_opt_set_fast_ccache_name not available.\n");
546 : return 0;
547 : #endif
548 : }
549 :
550 0 : krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_set_fast_flags(
551 : krb5_context context,
552 : krb5_get_init_creds_opt *opt,
553 : krb5_flags flags)
554 : {
555 : #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_FLAGS
556 0 : return krb5_get_init_creds_opt_set_fast_flags(context, opt, flags);
557 : #else
558 : DEBUG(SSSDBG_FUNC_DATA,
559 : "krb5_get_init_creds_opt_set_fast_flags not available.\n");
560 : return 0;
561 : #endif
562 : }
563 :
564 :
565 : #ifndef HAVE_KRB5_UNPARSE_NAME_FLAGS
566 : #ifndef REALM_SEP
567 : #define REALM_SEP '@'
568 : #endif
569 : #ifndef COMPONENT_SEP
570 : #define COMPONENT_SEP '/'
571 : #endif
572 :
573 : static int
574 : sss_krb5_copy_component_quoting(char *dest, const krb5_data *src, int flags)
575 : {
576 : int j;
577 : const char *cp = src->data;
578 : char *q = dest;
579 : int length = src->length;
580 :
581 : if (flags & KRB5_PRINCIPAL_UNPARSE_DISPLAY) {
582 : memcpy(dest, src->data, src->length);
583 : return src->length;
584 : }
585 :
586 : for (j=0; j < length; j++,cp++) {
587 : int no_realm = (flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) &&
588 : !(flags & KRB5_PRINCIPAL_UNPARSE_SHORT);
589 :
590 : switch (*cp) {
591 : case REALM_SEP:
592 : if (no_realm) {
593 : *q++ = *cp;
594 : break;
595 : }
596 : case COMPONENT_SEP:
597 : case '\\':
598 : *q++ = '\\';
599 : *q++ = *cp;
600 : break;
601 : case '\t':
602 : *q++ = '\\';
603 : *q++ = 't';
604 : break;
605 : case '\n':
606 : *q++ = '\\';
607 : *q++ = 'n';
608 : break;
609 : case '\b':
610 : *q++ = '\\';
611 : *q++ = 'b';
612 : break;
613 : case '\0':
614 : *q++ = '\\';
615 : *q++ = '0';
616 : break;
617 : default:
618 : *q++ = *cp;
619 : }
620 : }
621 : return q - dest;
622 : }
623 :
624 : static int
625 : sss_krb5_component_length_quoted(const krb5_data *src, int flags)
626 : {
627 : const char *cp = src->data;
628 : int length = src->length;
629 : int j;
630 : int size = length;
631 :
632 : if ((flags & KRB5_PRINCIPAL_UNPARSE_DISPLAY) == 0) {
633 : int no_realm = (flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) &&
634 : !(flags & KRB5_PRINCIPAL_UNPARSE_SHORT);
635 :
636 : for (j = 0; j < length; j++,cp++)
637 : if ((!no_realm && *cp == REALM_SEP) ||
638 : *cp == COMPONENT_SEP ||
639 : *cp == '\0' || *cp == '\\' || *cp == '\t' ||
640 : *cp == '\n' || *cp == '\b')
641 : size++;
642 : }
643 :
644 : return size;
645 : }
646 :
647 : #endif /* HAVE_KRB5_UNPARSE_NAME_FLAGS */
648 :
649 :
650 : krb5_error_code
651 0 : sss_krb5_parse_name_flags(krb5_context context, const char *name, int flags,
652 : krb5_principal *principal)
653 : {
654 : #ifdef HAVE_KRB5_PARSE_NAME_FLAGS
655 0 : return krb5_parse_name_flags(context, name, flags, principal);
656 : #else
657 : if (flags != 0) {
658 : DEBUG(SSSDBG_MINOR_FAILURE, "krb5_parse_name_flags not available on " \
659 : "this plattform, names are parsed " \
660 : "without flags. Some features like " \
661 : "enterprise principals might not work " \
662 : "as expected.\n");
663 : }
664 :
665 : return krb5_parse_name(context, name, principal);
666 : #endif
667 : }
668 :
669 : krb5_error_code
670 0 : sss_krb5_unparse_name_flags(krb5_context context, krb5_const_principal principal,
671 : int flags, char **name)
672 : {
673 : #ifdef HAVE_KRB5_UNPARSE_NAME_FLAGS
674 0 : return krb5_unparse_name_flags(context, principal, flags, name);
675 : #else
676 : char *cp, *q;
677 : int i;
678 : int length;
679 : krb5_int32 nelem;
680 : unsigned int totalsize = 0;
681 : char *default_realm = NULL;
682 : krb5_error_code ret = 0;
683 :
684 : if (name != NULL)
685 : *name = NULL;
686 :
687 : if (!principal || !name)
688 : return KRB5_PARSE_MALFORMED;
689 :
690 : if (flags & KRB5_PRINCIPAL_UNPARSE_SHORT) {
691 : /* omit realm if local realm */
692 : krb5_principal_data p;
693 :
694 : ret = krb5_get_default_realm(context, &default_realm);
695 : if (ret != 0)
696 : goto cleanup;
697 :
698 : krb5_princ_realm(context, &p)->length = strlen(default_realm);
699 : krb5_princ_realm(context, &p)->data = default_realm;
700 :
701 : if (krb5_realm_compare(context, &p, principal))
702 : flags |= KRB5_PRINCIPAL_UNPARSE_NO_REALM;
703 : }
704 :
705 : if ((flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) == 0) {
706 : totalsize += sss_krb5_component_length_quoted(krb5_princ_realm(context,
707 : principal),
708 : flags);
709 : totalsize++;
710 : }
711 :
712 : nelem = krb5_princ_size(context, principal);
713 : for (i = 0; i < (int) nelem; i++) {
714 : cp = krb5_princ_component(context, principal, i)->data;
715 : totalsize += sss_krb5_component_length_quoted(krb5_princ_component(context, principal, i), flags);
716 : totalsize++;
717 : }
718 : if (nelem == 0)
719 : totalsize++;
720 :
721 : *name = malloc(totalsize);
722 :
723 : if (!*name) {
724 : ret = ENOMEM;
725 : goto cleanup;
726 : }
727 :
728 : q = *name;
729 :
730 : for (i = 0; i < (int) nelem; i++) {
731 : cp = krb5_princ_component(context, principal, i)->data;
732 : length = krb5_princ_component(context, principal, i)->length;
733 : q += sss_krb5_copy_component_quoting(q,
734 : krb5_princ_component(context,
735 : principal,
736 : i),
737 : flags);
738 : *q++ = COMPONENT_SEP;
739 : }
740 :
741 : if (i > 0)
742 : q--;
743 : if ((flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) == 0) {
744 : *q++ = REALM_SEP;
745 : q += sss_krb5_copy_component_quoting(q, krb5_princ_realm(context, principal), flags);
746 : }
747 : *q++ = '\0';
748 :
749 : cleanup:
750 : free(default_realm);
751 :
752 : return ret;
753 : #endif /* HAVE_KRB5_UNPARSE_NAME_FLAGS */
754 : }
755 :
756 0 : void sss_krb5_get_init_creds_opt_set_canonicalize(krb5_get_init_creds_opt *opts,
757 : int canonicalize)
758 : {
759 : /* FIXME: The extra check for HAVE_KRB5_TICKET_TIMES is a workaround due to Heimdal
760 : * defining krb5_get_init_creds_opt_set_canonicalize() with a different set of
761 : * arguments. We should use a better configure check in the future.
762 : */
763 : #if defined(HAVE_KRB5_GET_INIT_CREDS_OPT_SET_CANONICALIZE) && defined(HAVE_KRB5_TICKET_TIMES)
764 0 : krb5_get_init_creds_opt_set_canonicalize(opts, canonicalize);
765 : #else
766 : DEBUG(SSSDBG_OP_FAILURE, "Kerberos principal canonicalization is not available!\n");
767 : #endif
768 0 : }
769 :
770 : #ifdef HAVE_KRB5_PRINCIPAL_GET_REALM
771 : void sss_krb5_princ_realm(krb5_context context, krb5_const_principal princ,
772 : const char **realm, int *len)
773 : {
774 : const char *realm_str = krb5_principal_get_realm(context, princ);
775 :
776 : if (realm_str != NULL) {
777 : *realm = realm_str;
778 : *len = strlen(realm_str);
779 : } else {
780 : *realm = NULL;
781 : *len = 0;
782 : }
783 : }
784 : #else
785 0 : void sss_krb5_princ_realm(krb5_context context, krb5_const_principal princ,
786 : const char **realm, int *len)
787 : {
788 : const krb5_data *data;
789 :
790 0 : data = krb5_princ_realm(context, princ);
791 0 : if (data) {
792 0 : *realm = data->data;
793 0 : *len = data->length;
794 : } else {
795 0 : *realm = NULL;
796 0 : *len = 0;
797 : }
798 0 : }
799 : #endif
800 :
801 : #ifdef HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS
802 : krb5_error_code
803 0 : sss_krb5_free_keytab_entry_contents(krb5_context context,
804 : krb5_keytab_entry *entry)
805 : {
806 0 : return krb5_free_keytab_entry_contents(context, entry);
807 : }
808 : #else
809 : krb5_error_code
810 : sss_krb5_free_keytab_entry_contents(krb5_context context,
811 : krb5_keytab_entry *entry)
812 : {
813 : return krb5_kt_free_entry(context, entry);
814 : }
815 : #endif
816 :
817 :
818 : #ifdef HAVE_KRB5_SET_TRACE_CALLBACK
819 :
820 : #ifndef HAVE_KRB5_TRACE_INFO
821 : /* krb5-1.10 had struct krb5_trace_info, 1.11 has type named krb5_trace_info */
822 : typedef struct krb5_trace_info krb5_trace_info;
823 : #endif /* HAVE_KRB5_TRACE_INFO */
824 :
825 : static void
826 0 : sss_child_krb5_trace_cb(krb5_context context,
827 : const krb5_trace_info *info, void *data)
828 : {
829 0 : if (info == NULL) {
830 : /* Null info means destroy the callback data. */
831 0 : return;
832 : }
833 :
834 0 : DEBUG(SSSDBG_TRACE_ALL, "%s\n", info->message);
835 : }
836 :
837 : errno_t
838 0 : sss_child_set_krb5_tracing(krb5_context ctx)
839 : {
840 0 : return krb5_set_trace_callback(ctx, sss_child_krb5_trace_cb, NULL);
841 : }
842 : #else /* HAVE_KRB5_SET_TRACE_CALLBACK */
843 : errno_t
844 : sss_child_set_krb5_tracing(krb5_context ctx)
845 : {
846 : DEBUG(SSSDBG_CONF_SETTINGS, "krb5 tracing is not available\n");
847 : return 0;
848 : }
849 : #endif /* HAVE_KRB5_SET_TRACE_CALLBACK */
850 :
851 0 : krb5_error_code sss_krb5_find_authdata(krb5_context context,
852 : krb5_authdata *const *ticket_authdata,
853 : krb5_authdata *const *ap_req_authdata,
854 : krb5_authdatatype ad_type,
855 : krb5_authdata ***results)
856 : {
857 : #ifdef HAVE_KRB5_FIND_AUTHDATA
858 0 : return krb5_find_authdata(context, ticket_authdata, ap_req_authdata,
859 : ad_type, results);
860 : #else
861 : return ENOTSUP;
862 : #endif
863 : }
864 :
865 0 : krb5_error_code sss_extract_pac(krb5_context ctx,
866 : krb5_ccache ccache,
867 : krb5_principal server_principal,
868 : krb5_principal client_principal,
869 : krb5_keytab keytab,
870 : krb5_authdata ***_pac_authdata)
871 : {
872 : #ifdef HAVE_PAC_RESPONDER
873 : krb5_error_code kerr;
874 : krb5_creds mcred;
875 : krb5_creds cred;
876 0 : krb5_authdata **pac_authdata = NULL;
877 0 : krb5_pac pac = NULL;
878 : int ret;
879 0 : krb5_ticket *ticket = NULL;
880 : krb5_keytab_entry entry;
881 :
882 0 : memset(&entry, 0, sizeof(entry));
883 0 : memset(&mcred, 0, sizeof(mcred));
884 0 : memset(&cred, 0, sizeof(mcred));
885 :
886 0 : mcred.server = server_principal;
887 0 : mcred.client = client_principal;
888 :
889 0 : kerr = krb5_cc_retrieve_cred(ctx, ccache, 0, &mcred, &cred);
890 0 : if (kerr != 0) {
891 0 : DEBUG(SSSDBG_OP_FAILURE, "krb5_cc_retrieve_cred failed.\n");
892 0 : goto done;
893 : }
894 :
895 0 : kerr = krb5_decode_ticket(&cred.ticket, &ticket);
896 0 : if (kerr != 0) {
897 0 : DEBUG(SSSDBG_OP_FAILURE, "krb5_decode_ticket failed.\n");
898 0 : goto done;
899 : }
900 :
901 0 : kerr = krb5_server_decrypt_ticket_keytab(ctx, keytab, ticket);
902 0 : if (kerr != 0) {
903 0 : DEBUG(SSSDBG_OP_FAILURE, "krb5_server_decrypt_ticket_keytab failed.\n");
904 0 : goto done;
905 : }
906 :
907 0 : kerr = sss_krb5_find_authdata(ctx,
908 0 : ticket->enc_part2->authorization_data, NULL,
909 : KRB5_AUTHDATA_WIN2K_PAC, &pac_authdata);
910 0 : if (kerr != 0) {
911 0 : DEBUG(SSSDBG_OP_FAILURE, "krb5_find_authdata failed.\n");
912 0 : goto done;
913 : }
914 :
915 0 : if (pac_authdata == NULL || pac_authdata[0] == NULL) {
916 0 : DEBUG(SSSDBG_OP_FAILURE, "No PAC authdata available.\n");
917 0 : kerr = ENOENT;
918 0 : goto done;
919 : }
920 :
921 0 : if (pac_authdata[1] != NULL) {
922 0 : DEBUG(SSSDBG_OP_FAILURE, "More than one PAC autdata found.\n");
923 0 : kerr = EINVAL;
924 0 : goto done;
925 : }
926 :
927 0 : kerr = krb5_pac_parse(ctx, pac_authdata[0]->contents,
928 0 : pac_authdata[0]->length, &pac);
929 0 : if (kerr != 0) {
930 0 : DEBUG(SSSDBG_OP_FAILURE, "krb5_pac_parse failed.\n");
931 0 : goto done;
932 : }
933 :
934 0 : kerr = krb5_kt_get_entry(ctx, keytab, ticket->server,
935 0 : ticket->enc_part.kvno, ticket->enc_part.enctype,
936 : &entry);
937 0 : if (kerr != 0) {
938 0 : DEBUG(SSSDBG_OP_FAILURE, "krb5_kt_get_entry failed.\n");
939 0 : goto done;
940 : }
941 :
942 0 : kerr = krb5_pac_verify(ctx, pac, 0, NULL, &entry.key, NULL);
943 0 : if (kerr != 0) {
944 0 : DEBUG(SSSDBG_OP_FAILURE, "krb5_pac_verify failed.\n");
945 0 : goto done;
946 : }
947 :
948 0 : ret = unsetenv("_SSS_LOOPS");
949 0 : if (ret != EOK) {
950 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to unset _SSS_LOOPS, "
951 : "sss_pac_make_request will most certainly fail.\n");
952 : }
953 :
954 0 : *_pac_authdata = pac_authdata;
955 0 : kerr = 0;
956 :
957 : done:
958 0 : if (kerr != 0) {
959 0 : krb5_free_authdata(ctx, pac_authdata);
960 : }
961 0 : if (entry.magic != 0) {
962 0 : krb5_free_keytab_entry_contents(ctx, &entry);
963 : }
964 0 : krb5_pac_free(ctx, pac);
965 0 : if (ticket != NULL) {
966 0 : krb5_free_ticket(ctx, ticket);
967 : }
968 :
969 0 : krb5_free_cred_contents(ctx, &cred);
970 0 : return kerr;
971 : #else
972 : return ENOTSUP;
973 : #endif
974 : }
975 :
976 0 : char * sss_get_ccache_name_for_principal(TALLOC_CTX *mem_ctx,
977 : krb5_context ctx,
978 : krb5_principal principal,
979 : const char *location)
980 : {
981 : #ifdef HAVE_KRB5_CC_COLLECTION
982 : krb5_error_code kerr;
983 0 : krb5_ccache tmp_cc = NULL;
984 0 : char *tmp_ccname = NULL;
985 0 : char *ret_ccname = NULL;
986 :
987 0 : DEBUG(SSSDBG_TRACE_ALL,
988 : "Location: [%s]\n", location);
989 :
990 0 : kerr = krb5_cc_set_default_name(ctx, location);
991 0 : if (kerr != 0) {
992 0 : KRB5_DEBUG(SSSDBG_MINOR_FAILURE, ctx, kerr);
993 0 : return NULL;
994 : }
995 :
996 0 : kerr = krb5_cc_cache_match(ctx, principal, &tmp_cc);
997 0 : if (kerr != 0) {
998 0 : const char *err_msg = sss_krb5_get_error_message(ctx, kerr);
999 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
1000 : "krb5_cc_cache_match failed: [%d][%s]\n", kerr, err_msg);
1001 0 : sss_krb5_free_error_message(ctx, err_msg);
1002 0 : return NULL;
1003 : }
1004 :
1005 0 : kerr = krb5_cc_get_full_name(ctx, tmp_cc, &tmp_ccname);
1006 0 : if (kerr != 0) {
1007 0 : KRB5_DEBUG(SSSDBG_MINOR_FAILURE, ctx, kerr);
1008 0 : goto done;
1009 : }
1010 :
1011 0 : DEBUG(SSSDBG_TRACE_ALL,
1012 : "tmp_ccname: [%s]\n", tmp_ccname);
1013 :
1014 0 : ret_ccname = talloc_strdup(mem_ctx, tmp_ccname);
1015 0 : if (ret_ccname == NULL) {
1016 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed (ENOMEM).\n");
1017 : }
1018 :
1019 : done:
1020 0 : if (tmp_cc != NULL) {
1021 0 : kerr = krb5_cc_close(ctx, tmp_cc);
1022 0 : if (kerr != 0) {
1023 0 : KRB5_DEBUG(SSSDBG_MINOR_FAILURE, ctx, kerr);
1024 : }
1025 : }
1026 0 : krb5_free_string(ctx, tmp_ccname);
1027 :
1028 0 : return ret_ccname;
1029 : #else
1030 : return NULL;
1031 : #endif /* HAVE_KRB5_CC_COLLECTION */
1032 : }
1033 :
1034 0 : krb5_error_code sss_krb5_kt_have_content(krb5_context context,
1035 : krb5_keytab keytab)
1036 : {
1037 : #ifdef HAVE_KRB5_KT_HAVE_CONTENT
1038 0 : return krb5_kt_have_content(context, keytab);
1039 : #else
1040 : krb5_keytab_entry entry;
1041 : krb5_kt_cursor cursor;
1042 : krb5_error_code kerr;
1043 : krb5_error_code kerr_end;
1044 :
1045 : kerr = krb5_kt_start_seq_get(context, keytab, &cursor);
1046 : if (kerr != 0) {
1047 : DEBUG(SSSDBG_OP_FAILURE,
1048 : "krb5_kt_start_seq_get failed, assuming no entries.\n");
1049 : return KRB5_KT_NOTFOUND;
1050 : }
1051 :
1052 : kerr = krb5_kt_next_entry(context, keytab, &entry, &cursor);
1053 : kerr_end = krb5_kt_end_seq_get(context, keytab, &cursor);
1054 : if (kerr != 0) {
1055 : DEBUG(SSSDBG_OP_FAILURE,
1056 : "krb5_kt_next_entry failed, assuming no entries.\n");
1057 : return KRB5_KT_NOTFOUND;
1058 : }
1059 : kerr = krb5_free_keytab_entry_contents(context, &entry);
1060 :
1061 : if (kerr_end != 0) {
1062 : DEBUG(SSSDBG_TRACE_FUNC,
1063 : "krb5_kt_end_seq_get failed, ignored.\n");
1064 : }
1065 : if (kerr != 0) {
1066 : DEBUG(SSSDBG_TRACE_FUNC,
1067 : "krb5_free_keytab_entry_contents failed, ignored.\n");
1068 : }
1069 :
1070 : return 0;
1071 : #endif
1072 : }
1073 :
1074 : #define KDC_PROXY_INDICATOR "https://"
1075 : #define KDC_PROXY_INDICATOR_LEN (sizeof(KDC_PROXY_INDICATOR) - 1)
1076 :
1077 4 : bool sss_krb5_realm_has_proxy(const char *realm)
1078 : {
1079 4 : krb5_context context = NULL;
1080 : krb5_error_code kerr;
1081 4 : struct _profile_t *profile = NULL;
1082 4 : const char *profile_path[4] = {"realms", NULL, "kdc", NULL};
1083 4 : char **list = NULL;
1084 4 : bool res = false;
1085 : size_t c;
1086 :
1087 4 : if (realm == NULL) {
1088 1 : return false;
1089 : }
1090 :
1091 3 : kerr = krb5_init_context(&context);
1092 3 : if (kerr != 0) {
1093 0 : DEBUG(SSSDBG_OP_FAILURE, "krb5_init_context failed.\n");
1094 0 : return false;
1095 : }
1096 :
1097 3 : kerr = krb5_get_profile(context, &profile);
1098 3 : if (kerr != 0) {
1099 0 : DEBUG(SSSDBG_OP_FAILURE, "krb5_get_profile failed.\n");
1100 0 : goto done;
1101 : }
1102 :
1103 3 : profile_path[1] = realm;
1104 :
1105 3 : kerr = profile_get_values(profile, profile_path, &list);
1106 3 : if (kerr == PROF_NO_RELATION || kerr == PROF_NO_SECTION) {
1107 1 : kerr = 0;
1108 1 : goto done;
1109 2 : } else if (kerr != 0) {
1110 0 : DEBUG(SSSDBG_OP_FAILURE, "profile_get_values failed.\n");
1111 0 : goto done;
1112 : }
1113 :
1114 3 : for (c = 0; list[c] != NULL; c++) {
1115 2 : if (strncasecmp(KDC_PROXY_INDICATOR, list[c],
1116 : KDC_PROXY_INDICATOR_LEN) == 0) {
1117 1 : DEBUG(SSSDBG_TRACE_ALL,
1118 : "Found KDC Proxy indicator [%s] in [%s].\n",
1119 : KDC_PROXY_INDICATOR, list[c]);
1120 1 : res = true;
1121 1 : break;
1122 : }
1123 : }
1124 :
1125 : done:
1126 3 : profile_free_list(list);
1127 3 : profile_release(profile);
1128 3 : krb5_free_context(context);
1129 :
1130 3 : return res;
1131 : }
|