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"
37 #include "../libds/common/flags.h"
40 #define DBGC_CLASS DBGC_AUTH
42 static void cli_credentials_invalidate_client_gss_creds(
43 struct cli_credentials *cred,
44 enum credentials_obtained obtained);
46 /* Free a memory ccache */
47 static int free_mccache(struct ccache_container *ccc)
49 if (ccc->ccache != NULL) {
50 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
58 /* Free a disk-based ccache */
59 static int free_dccache(struct ccache_container *ccc)
61 if (ccc->ccache != NULL) {
62 krb5_cc_close(ccc->smb_krb5_context->krb5_context,
70 static uint32_t smb_gss_krb5_copy_ccache(uint32_t *min_stat,
72 struct ccache_container *ccc)
74 #ifndef SAMBA4_USES_HEIMDAL /* MIT 1.10 */
75 krb5_context context = ccc->smb_krb5_context->krb5_context;
76 krb5_ccache dummy_ccache = NULL;
77 krb5_creds creds = {0};
78 krb5_cc_cursor cursor = NULL;
79 krb5_principal princ = NULL;
82 uint32_t maj_stat = GSS_S_FAILURE;
84 dummy_name = talloc_asprintf(ccc,
85 "MEMORY:gss_krb5_copy_ccache-%p",
87 if (dummy_name == NULL) {
93 * Create a dummy ccache, so we can iterate over the credentials
94 * and find the default principal for the ccache we want to
95 * copy. The new ccache needs to be initialized with this
98 code = krb5_cc_resolve(context, dummy_name, &dummy_ccache);
99 TALLOC_FREE(dummy_name);
102 return GSS_S_FAILURE;
106 * We do not need set a default principal on the temporary dummy
107 * ccache, as we do consume it at all in this function.
109 maj_stat = gss_krb5_copy_ccache(min_stat, cred, dummy_ccache);
111 krb5_cc_close(context, dummy_ccache);
115 code = krb5_cc_start_seq_get(context, dummy_ccache, &cursor);
117 krb5_cc_close(context, dummy_ccache);
119 return GSS_S_FAILURE;
122 code = krb5_cc_next_cred(context,
127 krb5_cc_close(context, dummy_ccache);
129 return GSS_S_FAILURE;
133 if (creds.ticket_flags & TKT_FLG_PRE_AUTH) {
136 tgs = krb5_princ_component(context,
139 if (tgs != NULL && tgs->length >= 1) {
142 cmp = memcmp(tgs->data,
145 if (cmp == 0 && creds.client != NULL) {
146 princ = creds.client;
153 krb5_free_cred_contents(context, &creds);
155 code = krb5_cc_next_cred(context,
161 if (code == KRB5_CC_END) {
162 krb5_cc_end_seq_get(context, dummy_ccache, &cursor);
165 krb5_cc_close(context, dummy_ccache);
167 if (code != 0 || princ == NULL) {
168 krb5_free_cred_contents(context, &creds);
170 return GSS_S_FAILURE;
174 * Set the default principal for the cache we copy
175 * into. This is needed to be able that other calls
176 * can read it with e.g. gss_acquire_cred() or
177 * krb5_cc_get_principal().
179 code = krb5_cc_initialize(context, ccc->ccache, princ);
181 krb5_free_cred_contents(context, &creds);
183 return GSS_S_FAILURE;
185 krb5_free_cred_contents(context, &creds);
187 #endif /* SAMBA4_USES_HEIMDAL */
189 return gss_krb5_copy_ccache(min_stat,
194 _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
195 struct loadparm_context *lp_ctx,
196 struct smb_krb5_context **smb_krb5_context)
199 if (cred->smb_krb5_context) {
200 *smb_krb5_context = cred->smb_krb5_context;
204 ret = smb_krb5_init_context(cred, lp_ctx,
205 &cred->smb_krb5_context);
207 cred->smb_krb5_context = NULL;
210 *smb_krb5_context = cred->smb_krb5_context;
214 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
215 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
217 _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
218 struct smb_krb5_context *smb_krb5_context)
220 if (smb_krb5_context == NULL) {
221 talloc_unlink(cred, cred->smb_krb5_context);
222 cred->smb_krb5_context = NULL;
226 if (!talloc_reference(cred, smb_krb5_context)) {
227 return NT_STATUS_NO_MEMORY;
229 cred->smb_krb5_context = smb_krb5_context;
233 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
234 struct ccache_container *ccache,
235 enum credentials_obtained obtained,
236 const char **error_string)
240 krb5_principal princ;
244 if (cred->ccache_obtained > obtained) {
248 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
249 ccache->ccache, &princ);
252 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
253 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
258 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
260 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
261 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
266 ok = cli_credentials_set_principal(cred, name, obtained);
267 krb5_free_unparsed_name(ccache->smb_krb5_context->krb5_context, name);
269 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
273 realm = smb_krb5_principal_get_realm(
274 cred, ccache->smb_krb5_context->krb5_context, princ);
275 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
279 ok = cli_credentials_set_realm(cred, realm, obtained);
285 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
286 cred->ccache_obtained = obtained;
291 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
292 struct loadparm_context *lp_ctx,
294 enum credentials_obtained obtained,
295 const char **error_string)
298 krb5_principal princ;
299 struct ccache_container *ccc;
300 if (cred->ccache_obtained > obtained) {
304 ccc = talloc(cred, struct ccache_container);
306 (*error_string) = error_message(ENOMEM);
310 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
311 &ccc->smb_krb5_context);
313 (*error_string) = error_message(ret);
317 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
319 (*error_string) = error_message(ENOMEM);
324 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
326 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
328 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
334 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
336 (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
337 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
344 talloc_set_destructor(ccc, free_dccache);
346 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
349 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
350 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
353 (*error_string) = error_message(ret);
360 cred->ccache_obtained = obtained;
362 cli_credentials_invalidate_client_gss_creds(
363 cred, cred->ccache_obtained);
369 * Indicate the we failed to log in to this service/host with these
370 * credentials. The caller passes an unsigned int which they
371 * initialise to the number of times they would like to retry.
373 * This method is used to support re-trying with freshly fetched
374 * credentials in case a server is rebuilt while clients have
375 * non-expired tickets. When the client code gets a logon failure they
376 * throw away the existing credentials for the server and retry.
378 _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
379 const char *principal,
382 struct ccache_container *ccc;
383 krb5_creds creds, creds2;
386 if (principal == NULL) {
387 /* no way to delete if we don't know the principal */
393 /* not a kerberos connection */
398 /* We have already tried discarding the credentials */
404 ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
409 /* MIT kerberos requires creds.client to match against cached
411 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context,
415 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context,
417 DBG_ERR("krb5_cc_get_principal failed: %s\n",
418 smb_get_krb5_error_message(
419 ccc->smb_krb5_context->krb5_context,
424 ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
426 /* don't retry - we didn't find these credentials to remove */
427 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
431 ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
432 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
433 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
435 /* don't retry - we didn't find these credentials to
436 * remove. Note that with the current backend this
437 * never happens, as it always returns 0 even if the
438 * creds don't exist, which is why we do a separate
439 * krb5_cc_retrieve_cred() above.
447 static int cli_credentials_new_ccache(struct cli_credentials *cred,
448 struct loadparm_context *lp_ctx,
450 struct ccache_container **_ccc,
451 const char **error_string)
453 bool must_free_cc_name = false;
455 struct ccache_container *ccc = talloc(cred, struct ccache_container);
460 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
461 &ccc->smb_krb5_context);
464 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
468 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
470 (*error_string) = strerror(ENOMEM);
475 must_free_cc_name = true;
477 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
478 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
479 (unsigned int)getpid(), ccc);
481 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
487 (*error_string) = strerror(ENOMEM);
492 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
495 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
497 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
499 talloc_free(ccache_name);
504 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
505 talloc_set_destructor(ccc, free_mccache);
507 talloc_set_destructor(ccc, free_dccache);
510 if (must_free_cc_name) {
511 talloc_free(ccache_name);
519 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
520 struct tevent_context *event_ctx,
521 struct loadparm_context *lp_ctx,
523 struct ccache_container **ccc,
524 const char **error_string)
527 enum credentials_obtained obtained;
529 if (cred->machine_account_pending) {
530 cli_credentials_set_machine_account(cred, lp_ctx);
533 if (cred->ccache_obtained >= cred->ccache_threshold &&
534 cred->ccache_obtained > CRED_UNINITIALISED) {
536 bool expired = false;
537 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
538 cred->ccache->ccache, &lifetime);
539 if (ret == KRB5_CC_END) {
540 /* If we have a particular ccache set, without
541 * an initial ticket, then assume there is a
543 } else if (ret == 0) {
545 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
546 cli_credentials_get_principal(cred, cred)));
548 } else if (lifetime < 300) {
549 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
550 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
554 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
555 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
560 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
561 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
568 if (cli_credentials_is_anonymous(cred)) {
569 (*error_string) = "Cannot get anonymous kerberos credentials";
573 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
578 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
583 ret = cli_credentials_set_from_ccache(cred, *ccc,
584 obtained, error_string);
587 cred->ccache_obtained = cred->principal_obtained;
591 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
595 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
596 struct tevent_context *event_ctx,
597 struct loadparm_context *lp_ctx,
598 struct ccache_container **ccc,
599 const char **error_string)
601 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
604 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
605 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
607 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
608 talloc_unlink(cred, cred->client_gss_creds);
609 cred->client_gss_creds = NULL;
611 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
614 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
615 enum credentials_obtained obtained)
617 /* If the caller just changed the username/password etc, then
618 * any cached credentials are now invalid */
619 if (obtained >= cred->client_gss_creds_obtained) {
620 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
621 talloc_unlink(cred, cred->client_gss_creds);
622 cred->client_gss_creds = NULL;
624 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
626 /* Now that we know that the data is 'this specified', then
627 * don't allow something less 'known' to be returned as a
628 * ccache. Ie, if the username is on the command line, we
629 * don't want to later guess to use a file-based ccache */
630 if (obtained > cred->client_gss_creds_threshold) {
631 cred->client_gss_creds_threshold = obtained;
635 /* We have good reason to think this CCACHE is invalid. Blow it away */
636 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
638 if (cred->ccache_obtained > CRED_UNINITIALISED) {
639 talloc_unlink(cred, cred->ccache);
642 cred->ccache_obtained = CRED_UNINITIALISED;
644 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
647 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
648 enum credentials_obtained obtained)
650 /* If the caller just changed the username/password etc, then
651 * any cached credentials are now invalid */
652 if (obtained >= cred->ccache_obtained) {
653 if (cred->ccache_obtained > CRED_UNINITIALISED) {
654 talloc_unlink(cred, cred->ccache);
657 cred->ccache_obtained = CRED_UNINITIALISED;
659 /* Now that we know that the data is 'this specified', then
660 * don't allow something less 'known' to be returned as a
661 * ccache. i.e, if the username is on the command line, we
662 * don't want to later guess to use a file-based ccache */
663 if (obtained > cred->ccache_threshold) {
664 cred->ccache_threshold = obtained;
667 cli_credentials_invalidate_client_gss_creds(cred,
671 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
674 (void)gss_release_cred(&min_stat, &gcc->creds);
678 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
679 struct tevent_context *event_ctx,
680 struct loadparm_context *lp_ctx,
681 struct gssapi_creds_container **_gcc,
682 const char **error_string)
685 OM_uint32 maj_stat, min_stat;
686 struct gssapi_creds_container *gcc;
687 struct ccache_container *ccache;
688 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
689 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
690 gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
692 krb5_enctype *etypes = NULL;
694 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
695 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
696 bool expired = false;
697 OM_uint32 lifetime = 0;
698 gss_cred_usage_t usage = 0;
699 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
700 NULL, &lifetime, &usage, NULL);
701 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
702 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
704 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
705 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
707 } else if (maj_stat != GSS_S_COMPLETE) {
708 *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
709 gssapi_error_string(cred, maj_stat, min_stat, NULL));
713 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
715 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
716 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
718 *_gcc = cred->client_gss_creds;
723 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
724 &ccache, error_string);
726 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
727 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
729 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
734 gcc = talloc(cred, struct gssapi_creds_container);
736 (*error_string) = error_message(ENOMEM);
740 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
741 ccache->ccache, NULL, NULL,
743 if ((maj_stat == GSS_S_FAILURE) &&
744 (min_stat == (OM_uint32)KRB5_CC_END ||
745 min_stat == (OM_uint32)KRB5_CC_NOTFOUND ||
746 min_stat == (OM_uint32)KRB5_FCC_NOFILE))
748 /* This CCACHE is no good. Ensure we don't use it again */
749 cli_credentials_unconditionally_invalidate_ccache(cred);
751 /* Now try again to get a ccache */
752 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
753 &ccache, error_string);
755 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
759 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
760 ccache->ccache, NULL, NULL,
772 (*error_string) = talloc_asprintf(cred, "smb_gss_krb5_import_cred failed: %s", error_message(ret));
778 * transfer the enctypes from the smb_krb5_context to the gssapi layer
780 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
781 * to configure the enctypes via the krb5.conf.
783 * And the gss_init_sec_context() creates it's own krb5_context and
784 * the TGS-REQ had all enctypes in it and only the ones configured
785 * and used for the AS-REQ, so it wasn't possible to disable the usage
788 min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
791 OM_uint32 num_ktypes;
793 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
795 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
806 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
811 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
813 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
815 * This allows us to disable SIGN and SEAL on a TLS connection with
816 * GSS-SPNENO. For example ldaps:// connections.
818 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
819 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
821 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
831 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
835 cred->client_gss_creds_obtained = cred->ccache_obtained;
836 talloc_set_destructor(gcc, free_gssapi_creds);
837 cred->client_gss_creds = gcc;
843 Set a gssapi cred_id_t into the credentials system. (Client case)
845 This grabs the credentials both 'intact' and getting the krb5
846 ccache out of it. This routine can be generalised in future for
847 the case where we deal with GSSAPI mechs other than krb5.
849 On sucess, the caller must not free gssapi_cred, as it now belongs
850 to the credentials system.
853 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
854 struct loadparm_context *lp_ctx,
855 gss_cred_id_t gssapi_cred,
856 enum credentials_obtained obtained,
857 const char **error_string)
860 OM_uint32 maj_stat, min_stat;
861 struct ccache_container *ccc = NULL;
862 struct gssapi_creds_container *gcc = NULL;
863 if (cred->client_gss_creds_obtained > obtained) {
867 gcc = talloc(cred, struct gssapi_creds_container);
869 (*error_string) = error_message(ENOMEM);
873 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
878 maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
888 (*error_string) = error_message(ENOMEM);
893 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
896 cred->ccache_obtained = obtained;
898 gcc->creds = gssapi_cred;
899 talloc_set_destructor(gcc, free_gssapi_creds);
901 /* set the clinet_gss_creds_obtained here, as it just
902 got set to UNINITIALISED by the calls above */
903 cred->client_gss_creds_obtained = obtained;
904 cred->client_gss_creds = gcc;
909 static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
912 const struct ccache_container *old_ccc = NULL;
913 struct ccache_container *ccc = NULL;
914 char *ccache_name = NULL;
915 krb5_principal princ;
917 old_ccc = cred->ccache;
918 if (old_ccc == NULL) {
922 ret = krb5_cc_get_principal(
923 old_ccc->smb_krb5_context->krb5_context,
928 * This is an empty ccache. No point in copying anything.
933 krb5_free_principal(old_ccc->smb_krb5_context->krb5_context, princ);
935 ccc = talloc(cred, struct ccache_container);
942 ccache_name = talloc_asprintf(ccc, "MEMORY:%p", ccc);
944 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
945 ccache_name, &ccc->ccache);
951 talloc_set_destructor(ccc, free_mccache);
953 TALLOC_FREE(ccache_name);
955 ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
956 old_ccc->ccache, ccc->ccache);
963 cred->client_gss_creds = NULL;
964 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
968 _PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
969 struct cli_credentials *src)
971 struct cli_credentials *dst;
974 dst = talloc(mem_ctx, struct cli_credentials);
981 ret = cli_credentials_shallow_ccache(dst);
990 /* Get the keytab (actually, a container containing the krb5_keytab)
991 * attached to this context. If this hasn't been done or set before,
992 * it will be generated from the password.
994 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
995 struct loadparm_context *lp_ctx,
996 struct keytab_container **_ktc)
999 struct keytab_container *ktc;
1000 struct smb_krb5_context *smb_krb5_context;
1001 const char *keytab_name;
1003 TALLOC_CTX *mem_ctx;
1004 const char *username = cli_credentials_get_username(cred);
1005 const char *upn = NULL;
1006 const char *realm = cli_credentials_get_realm(cred);
1007 char *salt_principal = NULL;
1008 uint32_t uac_flags = 0;
1010 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
1011 cred->username_obtained))) {
1012 *_ktc = cred->keytab;
1016 if (cli_credentials_is_anonymous(cred)) {
1020 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1026 mem_ctx = talloc_new(cred);
1031 switch (cred->secure_channel_type) {
1032 case SEC_CHAN_WKSTA:
1034 uac_flags = UF_WORKSTATION_TRUST_ACCOUNT;
1037 uac_flags = UF_SERVER_TRUST_ACCOUNT;
1039 case SEC_CHAN_DOMAIN:
1040 case SEC_CHAN_DNS_DOMAIN:
1041 uac_flags = UF_INTERDOMAIN_TRUST_ACCOUNT;
1044 upn = cli_credentials_get_principal(cred, mem_ctx);
1046 TALLOC_FREE(mem_ctx);
1049 uac_flags = UF_NORMAL_ACCOUNT;
1053 ret = smb_krb5_salt_principal(realm,
1054 username, /* sAMAccountName */
1055 upn, /* userPrincipalName */
1060 talloc_free(mem_ctx);
1064 ret = smb_krb5_create_memory_keytab(mem_ctx,
1065 smb_krb5_context->krb5_context,
1066 cli_credentials_get_password(cred),
1070 cli_credentials_get_kvno(cred),
1074 talloc_free(mem_ctx);
1078 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1079 keytab, keytab_name, &ktc);
1081 talloc_free(mem_ctx);
1085 cred->keytab_obtained = (MAX(cred->principal_obtained,
1086 cred->username_obtained));
1088 /* We make this keytab up based on a password. Therefore
1089 * match-by-key is acceptable, we can't match on the wrong
1091 ktc->password_based = true;
1093 talloc_steal(cred, ktc);
1095 *_ktc = cred->keytab;
1096 talloc_free(mem_ctx);
1100 /* Given the name of a keytab (presumably in the format
1101 * FILE:/etc/krb5.keytab), open it and attach it */
1103 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
1104 struct loadparm_context *lp_ctx,
1105 const char *keytab_name,
1106 enum credentials_obtained obtained)
1108 krb5_error_code ret;
1109 struct keytab_container *ktc;
1110 struct smb_krb5_context *smb_krb5_context;
1111 TALLOC_CTX *mem_ctx;
1113 if (cred->keytab_obtained >= obtained) {
1117 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1122 mem_ctx = talloc_new(cred);
1127 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1128 NULL, keytab_name, &ktc);
1133 cred->keytab_obtained = obtained;
1135 talloc_steal(cred, ktc);
1137 talloc_free(mem_ctx);
1142 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1144 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
1145 struct loadparm_context *lp_ctx,
1146 struct gssapi_creds_container **_gcc)
1149 OM_uint32 maj_stat, min_stat;
1150 struct gssapi_creds_container *gcc;
1151 struct keytab_container *ktc;
1152 struct smb_krb5_context *smb_krb5_context;
1153 TALLOC_CTX *mem_ctx;
1154 krb5_principal princ;
1155 const char *error_string;
1156 enum credentials_obtained obtained;
1158 mem_ctx = talloc_new(cred);
1163 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1168 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1170 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1172 talloc_free(mem_ctx);
1176 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1177 talloc_free(mem_ctx);
1178 *_gcc = cred->server_gss_creds;
1182 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1184 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1188 gcc = talloc(cred, struct gssapi_creds_container);
1190 talloc_free(mem_ctx);
1194 if (ktc->password_based || obtained < CRED_SPECIFIED) {
1196 * This creates a GSSAPI cred_id_t for match-by-key with only
1201 maj_stat = smb_gss_krb5_import_cred(&min_stat,
1202 smb_krb5_context->krb5_context,
1214 cred->server_gss_creds_obtained = cred->keytab_obtained;
1215 talloc_set_destructor(gcc, free_gssapi_creds);
1216 cred->server_gss_creds = gcc;
1219 talloc_free(mem_ctx);
1227 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1234 * Return Kerberos KVNO
1237 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1243 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
1245 return cred->salt_principal;
1248 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
1250 talloc_free(cred->salt_principal);
1251 cred->salt_principal = talloc_strdup(cred, principal);
1254 /* The 'impersonate_principal' is used to allow one Kerberos principal
1255 * (and it's associated keytab etc) to impersonate another. The
1256 * ability to do this is controlled by the KDC, but it is generally
1257 * permitted to impersonate anyone to yourself. This allows any
1258 * member of the domain to get the groups of a user. This is also
1259 * known as S4U2Self */
1261 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1263 return cred->impersonate_principal;
1267 * The 'self_service' is the service principal that
1268 * represents the same object (by its objectSid)
1269 * as the client principal (typically our machine account).
1270 * When trying to impersonate 'impersonate_principal' with
1273 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1275 return cred->self_service;
1278 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1279 const char *principal,
1280 const char *self_service)
1282 talloc_free(cred->impersonate_principal);
1283 cred->impersonate_principal = talloc_strdup(cred, principal);
1284 talloc_free(cred->self_service);
1285 cred->self_service = talloc_strdup(cred, self_service);
1286 cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
1290 * when impersonating for S4U2proxy we need to set the target principal.
1291 * Similarly, we may only be authorized to do general impersonation to
1292 * some particular services.
1294 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1296 * NULL means that tickets will be obtained for the krbtgt service.
1299 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1301 return cred->target_service;
1304 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1306 talloc_free(cred->target_service);
1307 cred->target_service = talloc_strdup(cred, target_service);