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"
39 #define DBGC_CLASS DBGC_AUTH
41 static void cli_credentials_invalidate_client_gss_creds(
42 struct cli_credentials *cred,
43 enum credentials_obtained obtained);
45 /* Free a memory ccache */
46 static int free_mccache(struct ccache_container *ccc)
48 if (ccc->ccache != NULL) {
49 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
57 /* Free a disk-based ccache */
58 static int free_dccache(struct ccache_container *ccc)
60 if (ccc->ccache != NULL) {
61 krb5_cc_close(ccc->smb_krb5_context->krb5_context,
69 static uint32_t smb_gss_krb5_copy_ccache(uint32_t *min_stat,
71 struct ccache_container *ccc)
73 #ifndef SAMBA4_USES_HEIMDAL /* MIT 1.10 */
74 krb5_context context = ccc->smb_krb5_context->krb5_context;
75 krb5_ccache dummy_ccache = NULL;
76 krb5_creds creds = {0};
77 krb5_cc_cursor cursor = NULL;
78 krb5_principal princ = NULL;
81 uint32_t maj_stat = GSS_S_FAILURE;
83 dummy_name = talloc_asprintf(ccc,
84 "MEMORY:gss_krb5_copy_ccache-%p",
86 if (dummy_name == NULL) {
92 * Create a dummy ccache, so we can iterate over the credentials
93 * and find the default principal for the ccache we want to
94 * copy. The new ccache needs to be initialized with this
97 code = krb5_cc_resolve(context, dummy_name, &dummy_ccache);
98 TALLOC_FREE(dummy_name);
101 return GSS_S_FAILURE;
105 * We do not need set a default principal on the temporary dummy
106 * ccache, as we do consume it at all in this function.
108 maj_stat = gss_krb5_copy_ccache(min_stat, cred, dummy_ccache);
110 krb5_cc_close(context, dummy_ccache);
114 code = krb5_cc_start_seq_get(context, dummy_ccache, &cursor);
116 krb5_cc_close(context, dummy_ccache);
118 return GSS_S_FAILURE;
121 code = krb5_cc_next_cred(context,
126 krb5_cc_close(context, dummy_ccache);
128 return GSS_S_FAILURE;
132 if (creds.ticket_flags & TKT_FLG_PRE_AUTH) {
135 tgs = krb5_princ_component(context,
138 if (tgs != NULL && tgs->length >= 1) {
141 cmp = memcmp(tgs->data,
144 if (cmp == 0 && creds.client != NULL) {
145 princ = creds.client;
152 krb5_free_cred_contents(context, &creds);
154 code = krb5_cc_next_cred(context,
160 if (code == KRB5_CC_END) {
161 krb5_cc_end_seq_get(context, dummy_ccache, &cursor);
164 krb5_cc_close(context, dummy_ccache);
166 if (code != 0 || princ == NULL) {
167 krb5_free_cred_contents(context, &creds);
169 return GSS_S_FAILURE;
173 * Set the default principal for the cache we copy
174 * into. This is needed to be able that other calls
175 * can read it with e.g. gss_acquire_cred() or
176 * krb5_cc_get_principal().
178 code = krb5_cc_initialize(context, ccc->ccache, princ);
180 krb5_free_cred_contents(context, &creds);
182 return GSS_S_FAILURE;
184 krb5_free_cred_contents(context, &creds);
186 #endif /* SAMBA4_USES_HEIMDAL */
188 return gss_krb5_copy_ccache(min_stat,
193 _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
194 struct loadparm_context *lp_ctx,
195 struct smb_krb5_context **smb_krb5_context)
198 if (cred->smb_krb5_context) {
199 *smb_krb5_context = cred->smb_krb5_context;
203 ret = smb_krb5_init_context(cred, lp_ctx,
204 &cred->smb_krb5_context);
206 cred->smb_krb5_context = NULL;
209 *smb_krb5_context = cred->smb_krb5_context;
213 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
214 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
216 _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
217 struct smb_krb5_context *smb_krb5_context)
219 if (smb_krb5_context == NULL) {
220 talloc_unlink(cred, cred->smb_krb5_context);
221 cred->smb_krb5_context = NULL;
225 if (!talloc_reference(cred, smb_krb5_context)) {
226 return NT_STATUS_NO_MEMORY;
228 cred->smb_krb5_context = smb_krb5_context;
232 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
233 struct ccache_container *ccache,
234 enum credentials_obtained obtained,
235 const char **error_string)
239 krb5_principal princ;
243 if (cred->ccache_obtained > obtained) {
247 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
248 ccache->ccache, &princ);
251 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
252 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
257 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
259 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
260 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
265 ok = cli_credentials_set_principal(cred, name, obtained);
266 krb5_free_unparsed_name(ccache->smb_krb5_context->krb5_context, name);
268 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
272 realm = smb_krb5_principal_get_realm(ccache->smb_krb5_context->krb5_context,
274 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
278 ok = cli_credentials_set_realm(cred, realm, obtained);
284 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
285 cred->ccache_obtained = obtained;
290 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
291 struct loadparm_context *lp_ctx,
293 enum credentials_obtained obtained,
294 const char **error_string)
297 krb5_principal princ;
298 struct ccache_container *ccc;
299 if (cred->ccache_obtained > obtained) {
303 ccc = talloc(cred, struct ccache_container);
305 (*error_string) = error_message(ENOMEM);
309 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
310 &ccc->smb_krb5_context);
312 (*error_string) = error_message(ret);
316 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
318 (*error_string) = error_message(ENOMEM);
323 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
325 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
327 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
333 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
335 (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
336 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
343 talloc_set_destructor(ccc, free_dccache);
345 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
348 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
349 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
352 (*error_string) = error_message(ret);
357 cred->ccache_obtained = obtained;
358 talloc_steal(cred, ccc);
360 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
367 * Indicate the we failed to log in to this service/host with these
368 * credentials. The caller passes an unsigned int which they
369 * initialise to the number of times they would like to retry.
371 * This method is used to support re-trying with freshly fetched
372 * credentials in case a server is rebuilt while clients have
373 * non-expired tickets. When the client code gets a logon failure they
374 * throw away the existing credentials for the server and retry.
376 _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
377 const char *principal,
380 struct ccache_container *ccc;
381 krb5_creds creds, creds2;
384 if (principal == NULL) {
385 /* no way to delete if we don't know the principal */
391 /* not a kerberos connection */
396 /* We have already tried discarding the credentials */
402 ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
407 ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
409 /* don't retry - we didn't find these credentials to remove */
410 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
414 ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
415 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
416 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
418 /* don't retry - we didn't find these credentials to
419 * remove. Note that with the current backend this
420 * never happens, as it always returns 0 even if the
421 * creds don't exist, which is why we do a separate
422 * krb5_cc_retrieve_cred() above.
430 static int cli_credentials_new_ccache(struct cli_credentials *cred,
431 struct loadparm_context *lp_ctx,
433 struct ccache_container **_ccc,
434 const char **error_string)
436 bool must_free_cc_name = false;
438 struct ccache_container *ccc = talloc(cred, struct ccache_container);
443 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
444 &ccc->smb_krb5_context);
447 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
451 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
453 (*error_string) = strerror(ENOMEM);
458 must_free_cc_name = true;
460 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
461 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
462 (unsigned int)getpid(), ccc);
464 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
470 (*error_string) = strerror(ENOMEM);
475 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
478 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
480 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
482 talloc_free(ccache_name);
487 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
488 talloc_set_destructor(ccc, free_mccache);
490 talloc_set_destructor(ccc, free_dccache);
493 if (must_free_cc_name) {
494 talloc_free(ccache_name);
502 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
503 struct tevent_context *event_ctx,
504 struct loadparm_context *lp_ctx,
506 struct ccache_container **ccc,
507 const char **error_string)
510 enum credentials_obtained obtained;
512 if (cred->machine_account_pending) {
513 cli_credentials_set_machine_account(cred, lp_ctx);
516 if (cred->ccache_obtained >= cred->ccache_threshold &&
517 cred->ccache_obtained > CRED_UNINITIALISED) {
519 bool expired = false;
520 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
521 cred->ccache->ccache, &lifetime);
522 if (ret == KRB5_CC_END) {
523 /* If we have a particular ccache set, without
524 * an initial ticket, then assume there is a
526 } else if (ret == 0) {
528 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
529 cli_credentials_get_principal(cred, cred)));
531 } else if (lifetime < 300) {
532 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
533 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
537 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
538 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
543 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
544 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
551 if (cli_credentials_is_anonymous(cred)) {
552 (*error_string) = "Cannot get anonymous kerberos credentials";
556 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
561 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
566 ret = cli_credentials_set_from_ccache(cred, *ccc,
567 obtained, error_string);
570 cred->ccache_obtained = cred->principal_obtained;
574 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
578 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
579 struct tevent_context *event_ctx,
580 struct loadparm_context *lp_ctx,
581 struct ccache_container **ccc,
582 const char **error_string)
584 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
587 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
588 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
590 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
591 talloc_unlink(cred, cred->client_gss_creds);
592 cred->client_gss_creds = NULL;
594 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
597 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
598 enum credentials_obtained obtained)
600 /* If the caller just changed the username/password etc, then
601 * any cached credentials are now invalid */
602 if (obtained >= cred->client_gss_creds_obtained) {
603 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
604 talloc_unlink(cred, cred->client_gss_creds);
605 cred->client_gss_creds = NULL;
607 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
609 /* Now that we know that the data is 'this specified', then
610 * don't allow something less 'known' to be returned as a
611 * ccache. Ie, if the username is on the command line, we
612 * don't want to later guess to use a file-based ccache */
613 if (obtained > cred->client_gss_creds_threshold) {
614 cred->client_gss_creds_threshold = obtained;
618 /* We have good reason to think this CCACHE is invalid. Blow it away */
619 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
621 if (cred->ccache_obtained > CRED_UNINITIALISED) {
622 talloc_unlink(cred, cred->ccache);
625 cred->ccache_obtained = CRED_UNINITIALISED;
627 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
630 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
631 enum credentials_obtained obtained)
633 /* If the caller just changed the username/password etc, then
634 * any cached credentials are now invalid */
635 if (obtained >= cred->ccache_obtained) {
636 if (cred->ccache_obtained > CRED_UNINITIALISED) {
637 talloc_unlink(cred, cred->ccache);
640 cred->ccache_obtained = CRED_UNINITIALISED;
642 /* Now that we know that the data is 'this specified', then
643 * don't allow something less 'known' to be returned as a
644 * ccache. i.e, if the username is on the command line, we
645 * don't want to later guess to use a file-based ccache */
646 if (obtained > cred->ccache_threshold) {
647 cred->ccache_threshold = obtained;
650 cli_credentials_invalidate_client_gss_creds(cred,
654 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
657 (void)gss_release_cred(&min_stat, &gcc->creds);
661 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
662 struct tevent_context *event_ctx,
663 struct loadparm_context *lp_ctx,
664 struct gssapi_creds_container **_gcc,
665 const char **error_string)
668 OM_uint32 maj_stat, min_stat;
669 struct gssapi_creds_container *gcc;
670 struct ccache_container *ccache;
671 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
672 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
673 gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
675 krb5_enctype *etypes = NULL;
677 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
678 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
679 bool expired = false;
680 OM_uint32 lifetime = 0;
681 gss_cred_usage_t usage = 0;
682 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
683 NULL, &lifetime, &usage, NULL);
684 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
685 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
687 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
688 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
690 } else if (maj_stat != GSS_S_COMPLETE) {
691 *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
692 gssapi_error_string(cred, maj_stat, min_stat, NULL));
696 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
698 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
699 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
701 *_gcc = cred->client_gss_creds;
706 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
707 &ccache, error_string);
709 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
710 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
712 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
717 gcc = talloc(cred, struct gssapi_creds_container);
719 (*error_string) = error_message(ENOMEM);
723 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
724 ccache->ccache, NULL, NULL,
726 if ((maj_stat == GSS_S_FAILURE) &&
727 (min_stat == (OM_uint32)KRB5_CC_END ||
728 min_stat == (OM_uint32)KRB5_CC_NOTFOUND ||
729 min_stat == (OM_uint32)KRB5_FCC_NOFILE))
731 /* This CCACHE is no good. Ensure we don't use it again */
732 cli_credentials_unconditionally_invalidate_ccache(cred);
734 /* Now try again to get a ccache */
735 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
736 &ccache, error_string);
738 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
742 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
743 ccache->ccache, NULL, NULL,
755 (*error_string) = talloc_asprintf(cred, "smb_gss_krb5_import_cred failed: %s", error_message(ret));
761 * transfer the enctypes from the smb_krb5_context to the gssapi layer
763 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
764 * to configure the enctypes via the krb5.conf.
766 * And the gss_init_sec_context() creates it's own krb5_context and
767 * the TGS-REQ had all enctypes in it and only the ones configured
768 * and used for the AS-REQ, so it wasn't possible to disable the usage
771 min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
774 OM_uint32 num_ktypes;
776 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
778 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
789 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
794 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
796 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
798 * This allows us to disable SIGN and SEAL on a TLS connection with
799 * GSS-SPNENO. For example ldaps:// connections.
801 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
802 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
804 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
814 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
818 cred->client_gss_creds_obtained = cred->ccache_obtained;
819 talloc_set_destructor(gcc, free_gssapi_creds);
820 cred->client_gss_creds = gcc;
826 Set a gssapi cred_id_t into the credentials system. (Client case)
828 This grabs the credentials both 'intact' and getting the krb5
829 ccache out of it. This routine can be generalised in future for
830 the case where we deal with GSSAPI mechs other than krb5.
832 On sucess, the caller must not free gssapi_cred, as it now belongs
833 to the credentials system.
836 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
837 struct loadparm_context *lp_ctx,
838 gss_cred_id_t gssapi_cred,
839 enum credentials_obtained obtained,
840 const char **error_string)
843 OM_uint32 maj_stat, min_stat;
844 struct ccache_container *ccc = NULL;
845 struct gssapi_creds_container *gcc = NULL;
846 if (cred->client_gss_creds_obtained > obtained) {
850 gcc = talloc(cred, struct gssapi_creds_container);
852 (*error_string) = error_message(ENOMEM);
856 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
861 maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
871 (*error_string) = error_message(ENOMEM);
876 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
879 cred->ccache_obtained = obtained;
881 gcc->creds = gssapi_cred;
882 talloc_set_destructor(gcc, free_gssapi_creds);
884 /* set the clinet_gss_creds_obtained here, as it just
885 got set to UNINITIALISED by the calls above */
886 cred->client_gss_creds_obtained = obtained;
887 cred->client_gss_creds = gcc;
892 static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
895 const struct ccache_container *old_ccc = NULL;
896 struct ccache_container *ccc = NULL;
897 char *ccache_name = NULL;
899 old_ccc = cred->ccache;
900 if (old_ccc == NULL) {
904 ccc = talloc(cred, struct ccache_container);
911 ccache_name = talloc_asprintf(ccc, "MEMORY:%p", ccc);
913 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
914 ccache_name, &ccc->ccache);
920 talloc_set_destructor(ccc, free_mccache);
922 TALLOC_FREE(ccache_name);
924 ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
925 old_ccc->ccache, ccc->ccache);
932 cred->client_gss_creds = NULL;
933 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
937 _PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
938 struct cli_credentials *src)
940 struct cli_credentials *dst;
943 dst = talloc(mem_ctx, struct cli_credentials);
950 ret = cli_credentials_shallow_ccache(dst);
959 /* Get the keytab (actually, a container containing the krb5_keytab)
960 * attached to this context. If this hasn't been done or set before,
961 * it will be generated from the password.
963 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
964 struct loadparm_context *lp_ctx,
965 struct keytab_container **_ktc)
968 struct keytab_container *ktc;
969 struct smb_krb5_context *smb_krb5_context;
970 const char *keytab_name;
973 const char *username = cli_credentials_get_username(cred);
974 const char *upn = NULL;
975 const char *realm = cli_credentials_get_realm(cred);
976 char *salt_principal = NULL;
977 bool is_computer = false;
979 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
980 cred->username_obtained))) {
981 *_ktc = cred->keytab;
985 if (cli_credentials_is_anonymous(cred)) {
989 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
995 mem_ctx = talloc_new(cred);
1000 switch (cred->secure_channel_type) {
1001 case SEC_CHAN_WKSTA:
1007 upn = cli_credentials_get_principal(cred, mem_ctx);
1009 TALLOC_FREE(mem_ctx);
1015 ret = smb_krb5_salt_principal(realm,
1016 username, /* sAMAccountName */
1017 upn, /* userPrincipalName */
1022 talloc_free(mem_ctx);
1026 ret = smb_krb5_create_memory_keytab(mem_ctx,
1027 smb_krb5_context->krb5_context,
1028 cli_credentials_get_password(cred),
1032 cli_credentials_get_kvno(cred),
1036 talloc_free(mem_ctx);
1040 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1041 keytab, keytab_name, &ktc);
1043 talloc_free(mem_ctx);
1047 cred->keytab_obtained = (MAX(cred->principal_obtained,
1048 cred->username_obtained));
1050 /* We make this keytab up based on a password. Therefore
1051 * match-by-key is acceptable, we can't match on the wrong
1053 ktc->password_based = true;
1055 talloc_steal(cred, ktc);
1057 *_ktc = cred->keytab;
1058 talloc_free(mem_ctx);
1062 /* Given the name of a keytab (presumably in the format
1063 * FILE:/etc/krb5.keytab), open it and attach it */
1065 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
1066 struct loadparm_context *lp_ctx,
1067 const char *keytab_name,
1068 enum credentials_obtained obtained)
1070 krb5_error_code ret;
1071 struct keytab_container *ktc;
1072 struct smb_krb5_context *smb_krb5_context;
1073 TALLOC_CTX *mem_ctx;
1075 if (cred->keytab_obtained >= obtained) {
1079 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1084 mem_ctx = talloc_new(cred);
1089 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1090 NULL, keytab_name, &ktc);
1095 cred->keytab_obtained = obtained;
1097 talloc_steal(cred, ktc);
1099 talloc_free(mem_ctx);
1104 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1106 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
1107 struct loadparm_context *lp_ctx,
1108 struct gssapi_creds_container **_gcc)
1111 OM_uint32 maj_stat, min_stat;
1112 struct gssapi_creds_container *gcc;
1113 struct keytab_container *ktc;
1114 struct smb_krb5_context *smb_krb5_context;
1115 TALLOC_CTX *mem_ctx;
1116 krb5_principal princ;
1117 const char *error_string;
1118 enum credentials_obtained obtained;
1120 mem_ctx = talloc_new(cred);
1125 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1130 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1132 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1134 talloc_free(mem_ctx);
1138 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1139 talloc_free(mem_ctx);
1140 *_gcc = cred->server_gss_creds;
1144 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1146 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1150 gcc = talloc(cred, struct gssapi_creds_container);
1152 talloc_free(mem_ctx);
1156 if (ktc->password_based || obtained < CRED_SPECIFIED) {
1158 * This creates a GSSAPI cred_id_t for match-by-key with only
1163 maj_stat = smb_gss_krb5_import_cred(&min_stat,
1164 smb_krb5_context->krb5_context,
1176 cred->server_gss_creds_obtained = cred->keytab_obtained;
1177 talloc_set_destructor(gcc, free_gssapi_creds);
1178 cred->server_gss_creds = gcc;
1181 talloc_free(mem_ctx);
1189 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1196 * Return Kerberos KVNO
1199 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1205 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
1207 return cred->salt_principal;
1210 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
1212 talloc_free(cred->salt_principal);
1213 cred->salt_principal = talloc_strdup(cred, principal);
1216 /* The 'impersonate_principal' is used to allow one Kerberos principal
1217 * (and it's associated keytab etc) to impersonate another. The
1218 * ability to do this is controlled by the KDC, but it is generally
1219 * permitted to impersonate anyone to yourself. This allows any
1220 * member of the domain to get the groups of a user. This is also
1221 * known as S4U2Self */
1223 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1225 return cred->impersonate_principal;
1229 * The 'self_service' is the service principal that
1230 * represents the same object (by its objectSid)
1231 * as the client principal (typically our machine account).
1232 * When trying to impersonate 'impersonate_principal' with
1235 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1237 return cred->self_service;
1240 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1241 const char *principal,
1242 const char *self_service)
1244 talloc_free(cred->impersonate_principal);
1245 cred->impersonate_principal = talloc_strdup(cred, principal);
1246 talloc_free(cred->self_service);
1247 cred->self_service = talloc_strdup(cred, self_service);
1248 cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
1252 * when impersonating for S4U2proxy we need to set the target principal.
1253 * Similarly, we may only be authorized to do general impersonation to
1254 * some particular services.
1256 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1258 * NULL means that tickets will be obtained for the krbtgt service.
1261 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1263 return cred->target_service;
1266 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1268 talloc_free(cred->target_service);
1269 cred->target_service = talloc_strdup(cred, target_service);