2 Unix SMB/CIFS implementation.
4 Handle user credentials (as regards krb5)
6 Copyright (C) Jelmer Vernooij 2005
7 Copyright (C) Tim Potter 2001
8 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "system/kerberos.h"
26 #include "system/gssapi.h"
27 #include "auth/kerberos/kerberos.h"
28 #include "auth/credentials/credentials.h"
29 #include "auth/credentials/credentials_internal.h"
30 #include "auth/credentials/credentials_proto.h"
31 #include "auth/credentials/credentials_krb5.h"
32 #include "auth/kerberos/kerberos_credentials.h"
33 #include "auth/kerberos/kerberos_srv_keytab.h"
34 #include "auth/kerberos/kerberos_util.h"
35 #include "auth/kerberos/pac_utils.h"
36 #include "param/param.h"
38 static void cli_credentials_invalidate_client_gss_creds(
39 struct cli_credentials *cred,
40 enum credentials_obtained obtained);
42 /* Free a memory ccache */
43 static int free_mccache(struct ccache_container *ccc)
45 if (ccc->ccache != NULL) {
46 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
54 /* Free a disk-based ccache */
55 static int free_dccache(struct ccache_container *ccc)
57 if (ccc->ccache != NULL) {
58 krb5_cc_close(ccc->smb_krb5_context->krb5_context,
66 _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
67 struct loadparm_context *lp_ctx,
68 struct smb_krb5_context **smb_krb5_context)
71 if (cred->smb_krb5_context) {
72 *smb_krb5_context = cred->smb_krb5_context;
76 ret = smb_krb5_init_context(cred, lp_ctx,
77 &cred->smb_krb5_context);
79 cred->smb_krb5_context = NULL;
82 *smb_krb5_context = cred->smb_krb5_context;
86 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
87 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
89 _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
90 struct smb_krb5_context *smb_krb5_context)
92 if (smb_krb5_context == NULL) {
93 talloc_unlink(cred, cred->smb_krb5_context);
94 cred->smb_krb5_context = NULL;
98 if (!talloc_reference(cred, smb_krb5_context)) {
99 return NT_STATUS_NO_MEMORY;
101 cred->smb_krb5_context = smb_krb5_context;
105 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
106 struct ccache_container *ccache,
107 enum credentials_obtained obtained,
108 const char **error_string)
111 krb5_principal princ;
115 if (cred->ccache_obtained > obtained) {
119 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
120 ccache->ccache, &princ);
123 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
124 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
129 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
131 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
132 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
137 cli_credentials_set_principal(cred, name, obtained);
141 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
143 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
144 cred->ccache_obtained = obtained;
149 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
150 struct loadparm_context *lp_ctx,
152 enum credentials_obtained obtained,
153 const char **error_string)
156 krb5_principal princ;
157 struct ccache_container *ccc;
158 if (cred->ccache_obtained > obtained) {
162 ccc = talloc(cred, struct ccache_container);
164 (*error_string) = error_message(ENOMEM);
168 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
169 &ccc->smb_krb5_context);
171 (*error_string) = error_message(ret);
175 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
177 (*error_string) = error_message(ENOMEM);
182 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
184 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
186 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
192 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
194 (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
195 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
202 talloc_set_destructor(ccc, free_dccache);
204 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
207 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
208 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
211 (*error_string) = error_message(ret);
216 cred->ccache_obtained = obtained;
217 talloc_steal(cred, ccc);
219 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
226 * Indicate the we failed to log in to this service/host with these
227 * credentials. The caller passes an unsigned int which they
228 * initialise to the number of times they would like to retry.
230 * This method is used to support re-trying with freshly fetched
231 * credentials in case a server is rebuilt while clients have
232 * non-expired tickets. When the client code gets a logon failure they
233 * throw away the existing credentials for the server and retry.
235 _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
236 const char *principal,
239 struct ccache_container *ccc;
240 krb5_creds creds, creds2;
243 if (principal == NULL) {
244 /* no way to delete if we don't know the principal */
250 /* not a kerberos connection */
255 /* We have already tried discarding the credentials */
261 ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
266 ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
268 /* don't retry - we didn't find these credentials to remove */
269 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
273 ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
274 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
275 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
277 /* don't retry - we didn't find these credentials to
278 * remove. Note that with the current backend this
279 * never happens, as it always returns 0 even if the
280 * creds don't exist, which is why we do a separate
281 * krb5_cc_retrieve_cred() above.
289 static int cli_credentials_new_ccache(struct cli_credentials *cred,
290 struct loadparm_context *lp_ctx,
292 struct ccache_container **_ccc,
293 const char **error_string)
295 bool must_free_cc_name = false;
297 struct ccache_container *ccc = talloc(cred, struct ccache_container);
302 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
303 &ccc->smb_krb5_context);
306 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
310 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
312 (*error_string) = strerror(ENOMEM);
317 must_free_cc_name = true;
319 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
320 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
321 (unsigned int)getpid(), ccc);
323 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
329 (*error_string) = strerror(ENOMEM);
334 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
337 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
339 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
341 talloc_free(ccache_name);
346 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
347 talloc_set_destructor(ccc, free_mccache);
349 talloc_set_destructor(ccc, free_dccache);
352 if (must_free_cc_name) {
353 talloc_free(ccache_name);
361 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
362 struct tevent_context *event_ctx,
363 struct loadparm_context *lp_ctx,
365 struct ccache_container **ccc,
366 const char **error_string)
369 enum credentials_obtained obtained;
371 if (cred->machine_account_pending) {
372 cli_credentials_set_machine_account(cred, lp_ctx);
375 if (cred->ccache_obtained >= cred->ccache_threshold &&
376 cred->ccache_obtained > CRED_UNINITIALISED) {
378 bool expired = false;
379 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
380 cred->ccache->ccache, &lifetime);
381 if (ret == KRB5_CC_END) {
382 /* If we have a particular ccache set, without
383 * an initial ticket, then assume there is a
385 } else if (ret == 0) {
387 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
388 cli_credentials_get_principal(cred, cred)));
390 } else if (lifetime < 300) {
391 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
392 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
396 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
397 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
402 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
403 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
410 if (cli_credentials_is_anonymous(cred)) {
411 (*error_string) = "Cannot get anonymous kerberos credentials";
415 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
420 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
425 ret = cli_credentials_set_from_ccache(cred, *ccc,
426 obtained, error_string);
429 cred->ccache_obtained = cred->principal_obtained;
433 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
437 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
438 struct tevent_context *event_ctx,
439 struct loadparm_context *lp_ctx,
440 struct ccache_container **ccc,
441 const char **error_string)
443 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
446 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
447 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
449 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
450 talloc_unlink(cred, cred->client_gss_creds);
451 cred->client_gss_creds = NULL;
453 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
456 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
457 enum credentials_obtained obtained)
459 /* If the caller just changed the username/password etc, then
460 * any cached credentials are now invalid */
461 if (obtained >= cred->client_gss_creds_obtained) {
462 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
463 talloc_unlink(cred, cred->client_gss_creds);
464 cred->client_gss_creds = NULL;
466 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
468 /* Now that we know that the data is 'this specified', then
469 * don't allow something less 'known' to be returned as a
470 * ccache. Ie, if the username is on the command line, we
471 * don't want to later guess to use a file-based ccache */
472 if (obtained > cred->client_gss_creds_threshold) {
473 cred->client_gss_creds_threshold = obtained;
477 /* We have good reason to think this CCACHE is invalid. Blow it away */
478 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
480 if (cred->ccache_obtained > CRED_UNINITIALISED) {
481 talloc_unlink(cred, cred->ccache);
484 cred->ccache_obtained = CRED_UNINITIALISED;
486 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
489 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
490 enum credentials_obtained obtained)
492 /* If the caller just changed the username/password etc, then
493 * any cached credentials are now invalid */
494 if (obtained >= cred->ccache_obtained) {
495 if (cred->ccache_obtained > CRED_UNINITIALISED) {
496 talloc_unlink(cred, cred->ccache);
499 cred->ccache_obtained = CRED_UNINITIALISED;
501 /* Now that we know that the data is 'this specified', then
502 * don't allow something less 'known' to be returned as a
503 * ccache. i.e, if the username is on the command line, we
504 * don't want to later guess to use a file-based ccache */
505 if (obtained > cred->ccache_threshold) {
506 cred->ccache_threshold = obtained;
509 cli_credentials_invalidate_client_gss_creds(cred,
513 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
516 (void)gss_release_cred(&min_stat, &gcc->creds);
520 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
521 struct tevent_context *event_ctx,
522 struct loadparm_context *lp_ctx,
523 struct gssapi_creds_container **_gcc,
524 const char **error_string)
527 OM_uint32 maj_stat, min_stat;
528 struct gssapi_creds_container *gcc;
529 struct ccache_container *ccache;
530 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
531 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
532 gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
534 krb5_enctype *etypes = NULL;
536 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
537 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
538 bool expired = false;
539 OM_uint32 lifetime = 0;
540 gss_cred_usage_t usage = 0;
541 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
542 NULL, &lifetime, &usage, NULL);
543 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
544 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
546 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
547 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
549 } else if (maj_stat != GSS_S_COMPLETE) {
550 *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
551 gssapi_error_string(cred, maj_stat, min_stat, NULL));
555 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
557 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
558 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
560 *_gcc = cred->client_gss_creds;
565 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
566 &ccache, error_string);
568 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
569 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
571 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
576 gcc = talloc(cred, struct gssapi_creds_container);
578 (*error_string) = error_message(ENOMEM);
582 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
584 if ((maj_stat == GSS_S_FAILURE) &&
585 (min_stat == (OM_uint32)KRB5_CC_END ||
586 min_stat == (OM_uint32)KRB5_CC_NOTFOUND ||
587 min_stat == (OM_uint32)KRB5_FCC_NOFILE))
589 /* This CCACHE is no good. Ensure we don't use it again */
590 cli_credentials_unconditionally_invalidate_ccache(cred);
592 /* Now try again to get a ccache */
593 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
594 &ccache, error_string);
596 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
600 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
612 (*error_string) = talloc_asprintf(cred, "gss_krb5_import_cred failed: %s", error_message(ret));
618 * transfer the enctypes from the smb_krb5_context to the gssapi layer
620 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
621 * to configure the enctypes via the krb5.conf.
623 * And the gss_init_sec_context() creates it's own krb5_context and
624 * the TGS-REQ had all enctypes in it and only the ones configured
625 * and used for the AS-REQ, so it wasn't possible to disable the usage
628 min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
631 OM_uint32 num_ktypes;
633 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
635 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
646 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
651 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
653 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
655 * This allows us to disable SIGN and SEAL on a TLS connection with
656 * GSS-SPNENO. For example ldaps:// connections.
658 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
659 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
661 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
671 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
675 cred->client_gss_creds_obtained = cred->ccache_obtained;
676 talloc_set_destructor(gcc, free_gssapi_creds);
677 cred->client_gss_creds = gcc;
683 Set a gssapi cred_id_t into the credentials system. (Client case)
685 This grabs the credentials both 'intact' and getting the krb5
686 ccache out of it. This routine can be generalised in future for
687 the case where we deal with GSSAPI mechs other than krb5.
689 On sucess, the caller must not free gssapi_cred, as it now belongs
690 to the credentials system.
693 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
694 struct loadparm_context *lp_ctx,
695 gss_cred_id_t gssapi_cred,
696 enum credentials_obtained obtained,
697 const char **error_string)
700 OM_uint32 maj_stat, min_stat;
701 struct ccache_container *ccc;
702 struct gssapi_creds_container *gcc;
703 if (cred->client_gss_creds_obtained > obtained) {
707 gcc = talloc(cred, struct gssapi_creds_container);
709 (*error_string) = error_message(ENOMEM);
713 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
718 maj_stat = gss_krb5_copy_ccache(&min_stat,
719 gssapi_cred, ccc->ccache);
727 (*error_string) = error_message(ENOMEM);
732 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
735 cred->ccache_obtained = obtained;
737 gcc->creds = gssapi_cred;
738 talloc_set_destructor(gcc, free_gssapi_creds);
740 /* set the clinet_gss_creds_obtained here, as it just
741 got set to UNINITIALISED by the calls above */
742 cred->client_gss_creds_obtained = obtained;
743 cred->client_gss_creds = gcc;
748 static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
751 const struct ccache_container *old_ccc = NULL;
752 struct ccache_container *ccc = NULL;
753 char *ccache_name = NULL;
755 old_ccc = cred->ccache;
756 if (old_ccc == NULL) {
760 ccc = talloc(cred, struct ccache_container);
767 ccache_name = talloc_asprintf(ccc, "MEMORY:%p", ccc);
769 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
770 ccache_name, &ccc->ccache);
776 talloc_set_destructor(ccc, free_mccache);
778 TALLOC_FREE(ccache_name);
780 ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
781 old_ccc->ccache, ccc->ccache);
788 cred->client_gss_creds = NULL;
789 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
793 _PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
794 struct cli_credentials *src)
796 struct cli_credentials *dst;
799 dst = talloc(mem_ctx, struct cli_credentials);
806 ret = cli_credentials_shallow_ccache(dst);
815 static int smb_krb5_create_salt_principal(TALLOC_CTX *mem_ctx,
816 const char *samAccountName,
818 const char **salt_principal,
819 const char **error_string)
821 char *machine_username;
822 bool is_machine_account = false;
827 if (samAccountName == NULL) {
828 *error_string = "Cannot determine salt principal, no "
829 "saltPrincipal or samAccountName specified";
834 *error_string = "Cannot make principal without a realm";
838 tmp_ctx = talloc_new(mem_ctx);
839 if (tmp_ctx == NULL) {
840 *error_string = "Cannot allocate talloc context";
844 upper_realm = strupper_talloc(tmp_ctx, realm);
845 if (upper_realm == NULL) {
846 *error_string = "Cannot allocate to upper case realm";
850 machine_username = strlower_talloc(tmp_ctx, samAccountName);
851 if (!machine_username) {
852 *error_string = "Cannot duplicate samAccountName";
856 if (machine_username[strlen(machine_username) - 1] == '$') {
857 machine_username[strlen(machine_username) - 1] = '\0';
858 is_machine_account = true;
861 if (is_machine_account) {
864 lower_realm = strlower_talloc(tmp_ctx, realm);
865 if (lower_realm == NULL) {
866 *error_string = "Cannot allocate to lower case realm";
870 *salt_principal = talloc_asprintf(mem_ctx,
876 *salt_principal = talloc_asprintf(mem_ctx,
881 if (*salt_principal == NULL) {
882 *error_string = "Cannot create salt principal";
888 talloc_free(tmp_ctx);
892 /* Get the keytab (actually, a container containing the krb5_keytab)
893 * attached to this context. If this hasn't been done or set before,
894 * it will be generated from the password.
896 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
897 struct loadparm_context *lp_ctx,
898 struct keytab_container **_ktc)
901 struct keytab_container *ktc;
902 struct smb_krb5_context *smb_krb5_context;
903 const char *keytab_name;
906 const char *username = cli_credentials_get_username(cred);
907 const char *realm = cli_credentials_get_realm(cred);
908 const char *error_string;
909 const char *salt_principal;
911 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
912 cred->username_obtained))) {
913 *_ktc = cred->keytab;
917 if (cli_credentials_is_anonymous(cred)) {
921 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
927 mem_ctx = talloc_new(cred);
933 * FIXME: Currently there is no better way than to create the correct
934 * salt principal by checking if the username ends with a '$'. It would
935 * be better if it is part of the credentials.
937 ret = smb_krb5_create_salt_principal(mem_ctx,
943 talloc_free(mem_ctx);
947 ret = smb_krb5_create_memory_keytab(mem_ctx,
948 smb_krb5_context->krb5_context,
949 cli_credentials_get_password(cred),
953 cli_credentials_get_kvno(cred),
957 talloc_free(mem_ctx);
961 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
962 keytab, keytab_name, &ktc);
964 talloc_free(mem_ctx);
968 cred->keytab_obtained = (MAX(cred->principal_obtained,
969 cred->username_obtained));
971 /* We make this keytab up based on a password. Therefore
972 * match-by-key is acceptable, we can't match on the wrong
974 ktc->password_based = true;
976 talloc_steal(cred, ktc);
978 *_ktc = cred->keytab;
979 talloc_free(mem_ctx);
983 /* Given the name of a keytab (presumably in the format
984 * FILE:/etc/krb5.keytab), open it and attach it */
986 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
987 struct loadparm_context *lp_ctx,
988 const char *keytab_name,
989 enum credentials_obtained obtained)
992 struct keytab_container *ktc;
993 struct smb_krb5_context *smb_krb5_context;
996 if (cred->keytab_obtained >= obtained) {
1000 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1005 mem_ctx = talloc_new(cred);
1010 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1011 NULL, keytab_name, &ktc);
1016 cred->keytab_obtained = obtained;
1018 talloc_steal(cred, ktc);
1020 talloc_free(mem_ctx);
1025 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1027 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
1028 struct loadparm_context *lp_ctx,
1029 struct gssapi_creds_container **_gcc)
1032 OM_uint32 maj_stat, min_stat;
1033 struct gssapi_creds_container *gcc;
1034 struct keytab_container *ktc;
1035 struct smb_krb5_context *smb_krb5_context;
1036 TALLOC_CTX *mem_ctx;
1037 krb5_principal princ;
1038 const char *error_string;
1039 enum credentials_obtained obtained;
1041 mem_ctx = talloc_new(cred);
1046 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1051 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1053 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1055 talloc_free(mem_ctx);
1059 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1060 talloc_free(mem_ctx);
1061 *_gcc = cred->server_gss_creds;
1065 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1067 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1071 gcc = talloc(cred, struct gssapi_creds_container);
1073 talloc_free(mem_ctx);
1077 if (ktc->password_based || obtained < CRED_SPECIFIED) {
1078 /* This creates a GSSAPI cred_id_t for match-by-key with only the keytab set */
1079 maj_stat = gss_krb5_import_cred(&min_stat, NULL, NULL, ktc->keytab,
1082 /* This creates a GSSAPI cred_id_t with the principal and keytab set, matching by name */
1083 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
1094 cred->server_gss_creds_obtained = cred->keytab_obtained;
1095 talloc_set_destructor(gcc, free_gssapi_creds);
1096 cred->server_gss_creds = gcc;
1099 talloc_free(mem_ctx);
1107 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1114 * Return Kerberos KVNO
1117 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1123 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
1125 return cred->salt_principal;
1128 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
1130 talloc_free(cred->salt_principal);
1131 cred->salt_principal = talloc_strdup(cred, principal);
1134 /* The 'impersonate_principal' is used to allow one Kerberos principal
1135 * (and it's associated keytab etc) to impersonate another. The
1136 * ability to do this is controlled by the KDC, but it is generally
1137 * permitted to impersonate anyone to yourself. This allows any
1138 * member of the domain to get the groups of a user. This is also
1139 * known as S4U2Self */
1141 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1143 return cred->impersonate_principal;
1147 * The 'self_service' is the service principal that
1148 * represents the same object (by its objectSid)
1149 * as the client principal (typically our machine account).
1150 * When trying to impersonate 'impersonate_principal' with
1153 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1155 return cred->self_service;
1158 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1159 const char *principal,
1160 const char *self_service)
1162 talloc_free(cred->impersonate_principal);
1163 cred->impersonate_principal = talloc_strdup(cred, principal);
1164 talloc_free(cred->self_service);
1165 cred->self_service = talloc_strdup(cred, self_service);
1166 cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
1170 * when impersonating for S4U2proxy we need to set the target principal.
1171 * Similarly, we may only be authorized to do general impersonation to
1172 * some particular services.
1174 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1176 * NULL means that tickets will be obtained for the krbtgt service.
1179 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1181 return cred->target_service;
1184 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1186 talloc_free(cred->target_service);
1187 cred->target_service = talloc_strdup(cred, target_service);