2 Unix SMB/CIFS implementation.
4 Kerberos utility functions for GENSEC
6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "system/kerberos.h"
25 #include "auth/kerberos/kerberos.h"
26 #include "auth/credentials/credentials.h"
27 #include "auth/credentials/credentials_krb5.h"
28 #include "auth/kerberos/kerberos_credentials.h"
29 #include "auth/kerberos/kerberos_util.h"
31 struct principal_container {
32 struct smb_krb5_context *smb_krb5_context;
33 krb5_principal principal;
34 const char *string_form; /* Optional */
37 static krb5_error_code free_principal(struct principal_container *pc)
39 /* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */
40 krb5_free_principal(pc->smb_krb5_context->krb5_context, pc->principal);
46 static krb5_error_code parse_principal(TALLOC_CTX *parent_ctx,
47 const char *princ_string,
48 struct smb_krb5_context *smb_krb5_context,
49 krb5_principal *princ,
50 const char **error_string)
53 struct principal_container *mem_ctx;
54 if (princ_string == NULL) {
60 * Start with talloc(), talloc_reference() and only then call
61 * krb5_parse_name(). If any of them fails, the cleanup code is simpler.
63 mem_ctx = talloc(parent_ctx, struct principal_container);
65 (*error_string) = error_message(ENOMEM);
69 mem_ctx->smb_krb5_context = talloc_reference(mem_ctx,
71 if (mem_ctx->smb_krb5_context == NULL) {
72 (*error_string) = error_message(ENOMEM);
77 ret = krb5_parse_name(smb_krb5_context->krb5_context,
81 (*error_string) = smb_get_krb5_error_message(
82 smb_krb5_context->krb5_context,
88 /* This song-and-dance effectively puts the principal
89 * into talloc, so we can't lose it. */
90 mem_ctx->principal = *princ;
91 talloc_set_destructor(mem_ctx, free_principal);
95 /* Obtain the principal set on this context. Requires a
96 * smb_krb5_context because we are doing krb5 principal parsing with
97 * the library routines. The returned princ is placed in the talloc
98 * system by means of a destructor (do *not* free). */
100 krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
101 struct cli_credentials *credentials,
102 struct smb_krb5_context *smb_krb5_context,
103 krb5_principal *princ,
104 enum credentials_obtained *obtained,
105 const char **error_string)
108 const char *princ_string;
109 TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
110 *obtained = CRED_UNINITIALISED;
113 (*error_string) = error_message(ENOMEM);
116 princ_string = cli_credentials_get_principal_and_obtained(credentials,
124 ret = parse_principal(parent_ctx, princ_string,
125 smb_krb5_context, princ, error_string);
126 talloc_free(mem_ctx);
130 /* Obtain the principal set on this context. Requires a
131 * smb_krb5_context because we are doing krb5 principal parsing with
132 * the library routines. The returned princ is placed in the talloc
133 * system by means of a destructor (do *not* free). */
135 static krb5_error_code impersonate_principal_from_credentials(
136 TALLOC_CTX *parent_ctx,
137 struct cli_credentials *credentials,
138 struct smb_krb5_context *smb_krb5_context,
139 krb5_principal *princ,
140 const char **error_string)
142 return parse_principal(parent_ctx,
143 cli_credentials_get_impersonate_principal(credentials),
144 smb_krb5_context, princ, error_string);
147 krb5_error_code smb_krb5_create_principals_array(TALLOC_CTX *mem_ctx,
148 krb5_context context,
149 const char *account_name,
153 uint32_t *pnum_principals,
154 krb5_principal **pprincipals,
155 const char **error_string)
157 krb5_error_code code;
159 uint32_t num_principals = 0;
160 krb5_principal *principals;
163 tmp_ctx = talloc_new(mem_ctx);
164 if (tmp_ctx == NULL) {
165 *error_string = "Cannot allocate tmp_ctx";
170 *error_string = "Cannot create principal without a realm";
175 if (account_name == NULL && (num_spns == 0 || spns == NULL)) {
176 *error_string = "Cannot create principal without an account or SPN";
181 if (account_name != NULL && account_name[0] != '\0') {
184 num_principals += num_spns;
186 principals = talloc_zero_array(tmp_ctx,
189 if (principals == NULL) {
190 *error_string = "Cannot allocate principals";
195 for (i = 0; i < num_spns; i++) {
196 code = krb5_parse_name(context, spns[i], &(principals[i]));
198 *error_string = smb_get_krb5_error_message(context,
205 if (account_name != NULL && account_name[0] != '\0') {
206 code = smb_krb5_make_principal(context,
212 *error_string = smb_get_krb5_error_message(context,
219 if (pnum_principals != NULL) {
220 *pnum_principals = num_principals;
222 if (pprincipals != NULL) {
223 *pprincipals = talloc_steal(mem_ctx, principals);
229 talloc_free(tmp_ctx);
234 * Return a freshly allocated ccache (destroyed by destructor on child
235 * of parent_ctx), for a given set of client credentials
238 krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
239 struct cli_credentials *credentials,
240 struct smb_krb5_context *smb_krb5_context,
241 struct loadparm_context *lp_ctx,
242 struct tevent_context *event_ctx,
244 enum credentials_obtained *obtained,
245 const char **error_string)
248 const char *password;
249 const char *self_service;
250 const char *target_service;
252 krb5_principal princ;
253 krb5_principal impersonate_principal;
255 TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
256 krb5_get_init_creds_opt *krb_options;
257 struct cli_credentials *fast_creds;
260 (*error_string) = strerror(ENOMEM);
264 ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ, obtained, error_string);
266 talloc_free(mem_ctx);
271 (*error_string) = talloc_asprintf(credentials, "principal, username or realm was not specified in the credentials");
272 talloc_free(mem_ctx);
273 return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
276 ret = impersonate_principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &impersonate_principal, error_string);
278 talloc_free(mem_ctx);
282 self_service = cli_credentials_get_self_service(credentials);
283 target_service = cli_credentials_get_target_service(credentials);
285 password = cli_credentials_get_password(credentials);
287 /* setup the krb5 options we want */
288 if ((ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options))) {
289 (*error_string) = talloc_asprintf(credentials, "krb5_get_init_creds_opt_alloc failed (%s)\n",
290 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
292 talloc_free(mem_ctx);
296 #ifdef SAMBA4_USES_HEIMDAL /* Disable for now MIT reads defaults when needed */
297 /* get the defaults */
298 krb5_get_init_creds_opt_set_default_flags(smb_krb5_context->krb5_context, NULL, NULL, krb_options);
300 /* set if we want a forwardable ticket */
301 switch (cli_credentials_get_krb_forwardable(credentials)) {
302 case CRED_AUTO_KRB_FORWARDABLE:
304 case CRED_NO_KRB_FORWARDABLE:
305 krb5_get_init_creds_opt_set_forwardable(krb_options, FALSE);
307 case CRED_FORCE_KRB_FORWARDABLE:
308 krb5_get_init_creds_opt_set_forwardable(krb_options, TRUE);
312 #ifdef SAMBA4_USES_HEIMDAL /* FIXME: MIT does not have this yet */
314 * In order to work against windows KDCs even if we use
315 * the netbios domain name as realm, we need to add the following
317 * KRB5_INIT_CREDS_NO_C_CANON_CHECK;
318 * KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK;
320 * On MIT: Set pkinit_eku_checking to none
322 krb5_get_init_creds_opt_set_win2k(smb_krb5_context->krb5_context,
324 krb5_get_init_creds_opt_set_canonicalize(smb_krb5_context->krb5_context,
327 krb5_get_init_creds_opt_set_canonicalize(krb_options, true);
330 fast_creds = cli_credentials_get_krb5_fast_armor_credentials(credentials);
332 if (fast_creds != NULL) {
333 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_CCACHE
334 struct ccache_container *fast_ccc = NULL;
335 const char *fast_error_string = NULL;
336 ret = cli_credentials_get_ccache(fast_creds, event_ctx, lp_ctx, &fast_ccc, &fast_error_string);
338 (*error_string) = talloc_asprintf(credentials,
339 "Obtaining the Kerberos FAST armor credentials failed: %s\n",
343 krb5_get_init_creds_opt_set_fast_ccache(smb_krb5_context->krb5_context,
347 *error_string = talloc_strdup(credentials,
348 "Using Kerberos FAST "
349 "armor credentials not possible "
350 "with this Kerberos library. "
351 "Modern MIT or Samba's embedded "
357 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_FLAGS
361 * This ensures that if FAST was required, but no armor
362 * credentials cache was specified, we proceed with (eg)
365 require_fast = cli_credentials_get_krb5_require_fast_armor(credentials);
367 krb5_get_init_creds_opt_set_fast_flags(smb_krb5_context->krb5_context,
376 #ifdef SAMBA4_USES_HEIMDAL
377 struct tevent_context *previous_ev;
378 /* Do this every time, in case we have weird recursive issues here */
379 ret = smb_krb5_context_set_event_ctx(smb_krb5_context, event_ctx, &previous_ev);
381 talloc_free(mem_ctx);
382 krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
387 if (impersonate_principal) {
388 ret = smb_krb5_kinit_s4u2_ccache(smb_krb5_context->krb5_context,
392 impersonate_principal,
399 ret = smb_krb5_kinit_password_ccache(smb_krb5_context->krb5_context,
408 } else if (impersonate_principal) {
409 talloc_free(mem_ctx);
410 (*error_string) = "INTERNAL error: Cannot impersonate principal with just a keyblock. A password must be specified in the credentials";
411 krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
412 #ifdef SAMBA4_USES_HEIMDAL
413 smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
417 /* No password available, try to use a keyblock instead */
419 krb5_keyblock keyblock;
420 const struct samr_Password *mach_pwd;
421 mach_pwd = cli_credentials_get_nt_hash(credentials, mem_ctx);
423 talloc_free(mem_ctx);
424 (*error_string) = "kinit_to_ccache: No password available for kinit\n";
425 krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
426 #ifdef SAMBA4_USES_HEIMDAL
427 smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
431 ret = smb_krb5_keyblock_init_contents(smb_krb5_context->krb5_context,
432 ENCTYPE_ARCFOUR_HMAC,
433 mach_pwd->hash, sizeof(mach_pwd->hash),
437 ret = smb_krb5_kinit_keyblock_ccache(smb_krb5_context->krb5_context,
445 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &keyblock);
449 #ifdef SAMBA4_USES_HEIMDAL
450 smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
453 if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
454 /* Perhaps we have been given an invalid skew, so try again without it */
455 time_t t = time(NULL);
456 krb5_set_real_time(smb_krb5_context->krb5_context, t, 0);
458 /* not a skew problem */
463 krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
465 if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
466 (*error_string) = talloc_asprintf(credentials, "kinit for %s failed (%s)\n",
467 cli_credentials_get_principal(credentials, mem_ctx),
468 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
470 talloc_free(mem_ctx);
474 /* cope with ticket being in the future due to clock skew */
475 if ((unsigned)kdc_time > time(NULL)) {
476 time_t t = time(NULL);
477 int time_offset =(unsigned)kdc_time-t;
478 DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
479 krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
482 if (ret == KRB5KDC_ERR_PREAUTH_FAILED && cli_credentials_wrong_password(credentials)) {
483 ret = kinit_to_ccache(parent_ctx,
493 (*error_string) = talloc_asprintf(credentials, "kinit for %s failed (%s)\n",
494 cli_credentials_get_principal(credentials, mem_ctx),
495 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
497 talloc_free(mem_ctx);
501 DEBUG(10,("kinit for %s succeeded\n",
502 cli_credentials_get_principal(credentials, mem_ctx)));
505 talloc_free(mem_ctx);
509 static krb5_error_code free_keytab_container(struct keytab_container *ktc)
511 return krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab);
514 krb5_error_code smb_krb5_get_keytab_container(TALLOC_CTX *mem_ctx,
515 struct smb_krb5_context *smb_krb5_context,
516 krb5_keytab opt_keytab,
517 const char *keytab_name,
518 struct keytab_container **ktc)
524 * Start with talloc(), talloc_reference() and only then call
525 * krb5_kt_resolve(). If any of them fails, the cleanup code is simpler.
527 *ktc = talloc(mem_ctx, struct keytab_container);
532 (*ktc)->smb_krb5_context = talloc_reference(*ktc, smb_krb5_context);
533 if ((*ktc)->smb_krb5_context == NULL) {
541 ret = krb5_kt_resolve(smb_krb5_context->krb5_context,
542 keytab_name, &keytab);
544 DEBUG(1,("failed to open krb5 keytab: %s\n",
545 smb_get_krb5_error_message(
546 smb_krb5_context->krb5_context,
553 (*ktc)->keytab = keytab;
554 (*ktc)->password_based = false;
555 talloc_set_destructor(*ktc, free_keytab_container);
561 * Walk the keytab, looking for entries of this principal name,
562 * with KVNO other than current kvno -1.
564 * These entries are now stale,
565 * we only keep the current and previous entries around.
567 * Inspired by the code in Samba3 for 'use kerberos keytab'.
569 krb5_error_code smb_krb5_remove_obsolete_keytab_entries(TALLOC_CTX *mem_ctx,
570 krb5_context context,
572 uint32_t num_principals,
573 krb5_principal *principals,
575 bool *found_previous,
576 const char **error_string)
579 krb5_error_code code;
580 krb5_kt_cursor cursor;
582 tmp_ctx = talloc_new(mem_ctx);
583 if (tmp_ctx == NULL) {
584 *error_string = "Cannot allocate tmp_ctx";
588 *found_previous = true;
590 code = krb5_kt_start_seq_get(context, keytab, &cursor);
594 #ifdef HEIM_ERR_OPNOTSUPP
595 case HEIM_ERR_OPNOTSUPP:
599 /* no point enumerating if there isn't anything here */
603 *error_string = talloc_asprintf(mem_ctx,
604 "failed to open keytab for read of old entries: %s\n",
605 smb_get_krb5_error_message(context, code, tmp_ctx));
610 krb5_kvno old_kvno = kvno - 1;
611 krb5_keytab_entry entry;
612 bool matched = false;
615 code = krb5_kt_next_entry(context, keytab, &entry, &cursor);
620 for (i = 0; i < num_principals; i++) {
623 ok = smb_krb5_kt_compare(context,
636 * Free the entry, it wasn't the one we were looking
639 krb5_kt_free_entry(context, &entry);
640 /* Make sure we do not double free */
646 * Delete it, if it is not kvno - 1.
648 * Some keytab files store the kvno only in 8bits. Limit the
649 * compare to 8bits, so that we don't miss old keys and delete
652 if ((entry.vno & 0xff) != (old_kvno & 0xff)) {
655 /* Release the enumeration. We are going to
656 * have to start this from the top again,
657 * because deletes during enumeration may not
658 * always be consistent.
660 * Also, the enumeration locks a FILE: keytab
662 krb5_kt_end_seq_get(context, keytab, &cursor);
664 code = krb5_kt_remove_entry(context, keytab, &entry);
665 krb5_kt_free_entry(context, &entry);
667 /* Make sure we do not double free */
670 /* Deleted: Restart from the top */
671 rc = krb5_kt_start_seq_get(context, keytab, &cursor);
673 krb5_kt_free_entry(context, &entry);
675 /* Make sure we do not double free */
678 DEBUG(1, ("failed to restart enumeration of keytab: %s\n",
679 smb_get_krb5_error_message(context,
683 talloc_free(tmp_ctx);
692 *found_previous = true;
695 /* Free the entry, we don't need it any more */
696 krb5_kt_free_entry(context, &entry);
697 /* Make sure we do not double free */
701 krb5_kt_end_seq_get(context, keytab, &cursor);
710 *error_string = talloc_asprintf(mem_ctx,
711 "failed in deleting old entries for principal: %s\n",
712 smb_get_krb5_error_message(context,
720 talloc_free(tmp_ctx);
725 * Walk the keytab, looking for entries of this principal name,
726 * with KVNO and key equal
728 * These entries do not need to be replaced, so we want to tell the caller not to add them again
730 * Inspired by the code in Samba3 for 'use kerberos keytab'.
732 krb5_error_code smb_krb5_is_exact_entry_in_keytab(TALLOC_CTX *mem_ctx,
733 krb5_context context,
735 krb5_keytab_entry *to_match,
737 const char **error_string)
740 krb5_error_code code;
741 krb5_kt_cursor cursor;
743 tmp_ctx = talloc_new(mem_ctx);
744 if (tmp_ctx == NULL) {
745 *error_string = "Cannot allocate tmp_ctx";
751 code = krb5_kt_start_seq_get(context, keytab, &cursor);
755 #ifdef HEIM_ERR_OPNOTSUPP
756 case HEIM_ERR_OPNOTSUPP:
760 /* no point enumerating if there isn't anything here */
764 *error_string = talloc_asprintf(mem_ctx,
765 "failed to open keytab for read of existing entries: %s\n",
766 smb_get_krb5_error_message(context, code, tmp_ctx));
771 krb5_keytab_entry entry;
772 bool matched = false;
775 code = krb5_kt_next_entry(context, keytab, &entry, &cursor);
780 ok = smb_krb5_kt_compare(context,
784 KRB5_KEY_TYPE(KRB5_KT_KEY(to_match)));
786 /* This is not a security check, constant time is not required */
787 if ((KRB5_KEY_LENGTH(KRB5_KT_KEY(&entry)) == KRB5_KEY_LENGTH(KRB5_KT_KEY(to_match)))
788 && memcmp(KRB5_KEY_DATA(KRB5_KT_KEY(&entry)), KRB5_KEY_DATA(KRB5_KT_KEY(to_match)),
789 KRB5_KEY_LENGTH(KRB5_KT_KEY(&entry))) == 0) {
794 /* Free the entry, we don't need it any more */
795 krb5_kt_free_entry(context, &entry);
796 /* Make sure we do not double free */
804 krb5_kt_end_seq_get(context, keytab, &cursor);
813 *error_string = talloc_asprintf(mem_ctx,
814 "failed in checking old entries for principal: %s\n",
815 smb_get_krb5_error_message(context,
823 talloc_free(tmp_ctx);