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 static uint32_t smb_gss_krb5_copy_ccache(uint32_t *min_stat,
68 struct ccache_container *ccc)
70 #ifndef SAMBA4_USES_HEIMDAL /* MIT 1.10 */
71 krb5_context context = ccc->smb_krb5_context->krb5_context;
72 krb5_ccache dummy_ccache = NULL;
73 krb5_creds creds = {0};
74 krb5_cc_cursor cursor = NULL;
75 krb5_principal princ = NULL;
78 uint32_t maj_stat = GSS_S_FAILURE;
80 dummy_name = talloc_asprintf(ccc,
81 "MEMORY:gss_krb5_copy_ccache-%p",
83 if (dummy_name == NULL) {
89 * Create a dummy ccache, so we can iterate over the credentials
90 * and find the default principal for the ccache we want to
91 * copy. The new ccache needs to be initialized with this
94 code = krb5_cc_resolve(context, dummy_name, &dummy_ccache);
95 TALLOC_FREE(dummy_name);
102 * We do not need set a default principal on the temporary dummy
103 * ccache, as we do consume it at all in this function.
105 maj_stat = gss_krb5_copy_ccache(min_stat, cred, dummy_ccache);
107 krb5_cc_close(context, dummy_ccache);
111 code = krb5_cc_start_seq_get(context, dummy_ccache, &cursor);
113 krb5_cc_close(context, dummy_ccache);
115 return GSS_S_FAILURE;
118 code = krb5_cc_next_cred(context,
123 krb5_cc_close(context, dummy_ccache);
125 return GSS_S_FAILURE;
129 if (creds.ticket_flags & TKT_FLG_PRE_AUTH) {
132 tgs = krb5_princ_component(context,
135 if (tgs != NULL && tgs->length >= 1) {
138 cmp = memcmp(tgs->data,
141 if (cmp == 0 && creds.client != NULL) {
142 princ = creds.client;
149 krb5_free_cred_contents(context, &creds);
151 code = krb5_cc_next_cred(context,
157 if (code == KRB5_CC_END) {
158 krb5_cc_end_seq_get(context, dummy_ccache, &cursor);
161 krb5_cc_close(context, dummy_ccache);
163 if (code != 0 || princ == NULL) {
164 krb5_free_cred_contents(context, &creds);
166 return GSS_S_FAILURE;
170 * Set the default principal for the cache we copy
171 * into. This is needed to be able that other calls
172 * can read it with e.g. gss_acquire_cred() or
173 * krb5_cc_get_principal().
175 code = krb5_cc_initialize(context, ccc->ccache, princ);
177 krb5_free_cred_contents(context, &creds);
179 return GSS_S_FAILURE;
181 krb5_free_cred_contents(context, &creds);
183 #endif /* SAMBA4_USES_HEIMDAL */
185 return gss_krb5_copy_ccache(min_stat,
190 _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
191 struct loadparm_context *lp_ctx,
192 struct smb_krb5_context **smb_krb5_context)
195 if (cred->smb_krb5_context) {
196 *smb_krb5_context = cred->smb_krb5_context;
200 ret = smb_krb5_init_context(cred, lp_ctx,
201 &cred->smb_krb5_context);
203 cred->smb_krb5_context = NULL;
206 *smb_krb5_context = cred->smb_krb5_context;
210 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
211 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
213 _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
214 struct smb_krb5_context *smb_krb5_context)
216 if (smb_krb5_context == NULL) {
217 talloc_unlink(cred, cred->smb_krb5_context);
218 cred->smb_krb5_context = NULL;
222 if (!talloc_reference(cred, smb_krb5_context)) {
223 return NT_STATUS_NO_MEMORY;
225 cred->smb_krb5_context = smb_krb5_context;
229 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
230 struct ccache_container *ccache,
231 enum credentials_obtained obtained,
232 const char **error_string)
236 krb5_principal princ;
240 if (cred->ccache_obtained > obtained) {
244 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
245 ccache->ccache, &princ);
248 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
249 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
254 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
256 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
257 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
262 ok = cli_credentials_set_principal(cred, name, obtained);
264 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
269 realm = smb_krb5_principal_get_realm(ccache->smb_krb5_context->krb5_context,
271 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
275 ok = cli_credentials_set_realm(cred, realm, obtained);
281 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
282 cred->ccache_obtained = obtained;
287 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
288 struct loadparm_context *lp_ctx,
290 enum credentials_obtained obtained,
291 const char **error_string)
294 krb5_principal princ;
295 struct ccache_container *ccc;
296 if (cred->ccache_obtained > obtained) {
300 ccc = talloc(cred, struct ccache_container);
302 (*error_string) = error_message(ENOMEM);
306 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
307 &ccc->smb_krb5_context);
309 (*error_string) = error_message(ret);
313 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
315 (*error_string) = error_message(ENOMEM);
320 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
322 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
324 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
330 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
332 (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
333 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
340 talloc_set_destructor(ccc, free_dccache);
342 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
345 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
346 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
349 (*error_string) = error_message(ret);
354 cred->ccache_obtained = obtained;
355 talloc_steal(cred, ccc);
357 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
364 * Indicate the we failed to log in to this service/host with these
365 * credentials. The caller passes an unsigned int which they
366 * initialise to the number of times they would like to retry.
368 * This method is used to support re-trying with freshly fetched
369 * credentials in case a server is rebuilt while clients have
370 * non-expired tickets. When the client code gets a logon failure they
371 * throw away the existing credentials for the server and retry.
373 _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
374 const char *principal,
377 struct ccache_container *ccc;
378 krb5_creds creds, creds2;
381 if (principal == NULL) {
382 /* no way to delete if we don't know the principal */
388 /* not a kerberos connection */
393 /* We have already tried discarding the credentials */
399 ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
404 ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
406 /* don't retry - we didn't find these credentials to remove */
407 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
411 ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
412 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
413 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
415 /* don't retry - we didn't find these credentials to
416 * remove. Note that with the current backend this
417 * never happens, as it always returns 0 even if the
418 * creds don't exist, which is why we do a separate
419 * krb5_cc_retrieve_cred() above.
427 static int cli_credentials_new_ccache(struct cli_credentials *cred,
428 struct loadparm_context *lp_ctx,
430 struct ccache_container **_ccc,
431 const char **error_string)
433 bool must_free_cc_name = false;
435 struct ccache_container *ccc = talloc(cred, struct ccache_container);
440 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
441 &ccc->smb_krb5_context);
444 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
448 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
450 (*error_string) = strerror(ENOMEM);
455 must_free_cc_name = true;
457 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
458 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
459 (unsigned int)getpid(), ccc);
461 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
467 (*error_string) = strerror(ENOMEM);
472 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
475 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
477 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
479 talloc_free(ccache_name);
484 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
485 talloc_set_destructor(ccc, free_mccache);
487 talloc_set_destructor(ccc, free_dccache);
490 if (must_free_cc_name) {
491 talloc_free(ccache_name);
499 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
500 struct tevent_context *event_ctx,
501 struct loadparm_context *lp_ctx,
503 struct ccache_container **ccc,
504 const char **error_string)
507 enum credentials_obtained obtained;
509 if (cred->machine_account_pending) {
510 cli_credentials_set_machine_account(cred, lp_ctx);
513 if (cred->ccache_obtained >= cred->ccache_threshold &&
514 cred->ccache_obtained > CRED_UNINITIALISED) {
516 bool expired = false;
517 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
518 cred->ccache->ccache, &lifetime);
519 if (ret == KRB5_CC_END) {
520 /* If we have a particular ccache set, without
521 * an initial ticket, then assume there is a
523 } else if (ret == 0) {
525 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
526 cli_credentials_get_principal(cred, cred)));
528 } else if (lifetime < 300) {
529 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
530 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
534 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
535 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
540 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
541 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
548 if (cli_credentials_is_anonymous(cred)) {
549 (*error_string) = "Cannot get anonymous kerberos credentials";
553 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
558 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
563 ret = cli_credentials_set_from_ccache(cred, *ccc,
564 obtained, error_string);
567 cred->ccache_obtained = cred->principal_obtained;
571 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
575 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
576 struct tevent_context *event_ctx,
577 struct loadparm_context *lp_ctx,
578 struct ccache_container **ccc,
579 const char **error_string)
581 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
584 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
585 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
587 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
588 talloc_unlink(cred, cred->client_gss_creds);
589 cred->client_gss_creds = NULL;
591 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
594 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
595 enum credentials_obtained obtained)
597 /* If the caller just changed the username/password etc, then
598 * any cached credentials are now invalid */
599 if (obtained >= cred->client_gss_creds_obtained) {
600 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
601 talloc_unlink(cred, cred->client_gss_creds);
602 cred->client_gss_creds = NULL;
604 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
606 /* Now that we know that the data is 'this specified', then
607 * don't allow something less 'known' to be returned as a
608 * ccache. Ie, if the username is on the command line, we
609 * don't want to later guess to use a file-based ccache */
610 if (obtained > cred->client_gss_creds_threshold) {
611 cred->client_gss_creds_threshold = obtained;
615 /* We have good reason to think this CCACHE is invalid. Blow it away */
616 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
618 if (cred->ccache_obtained > CRED_UNINITIALISED) {
619 talloc_unlink(cred, cred->ccache);
622 cred->ccache_obtained = CRED_UNINITIALISED;
624 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
627 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
628 enum credentials_obtained obtained)
630 /* If the caller just changed the username/password etc, then
631 * any cached credentials are now invalid */
632 if (obtained >= cred->ccache_obtained) {
633 if (cred->ccache_obtained > CRED_UNINITIALISED) {
634 talloc_unlink(cred, cred->ccache);
637 cred->ccache_obtained = CRED_UNINITIALISED;
639 /* Now that we know that the data is 'this specified', then
640 * don't allow something less 'known' to be returned as a
641 * ccache. i.e, if the username is on the command line, we
642 * don't want to later guess to use a file-based ccache */
643 if (obtained > cred->ccache_threshold) {
644 cred->ccache_threshold = obtained;
647 cli_credentials_invalidate_client_gss_creds(cred,
651 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
654 (void)gss_release_cred(&min_stat, &gcc->creds);
658 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
659 struct tevent_context *event_ctx,
660 struct loadparm_context *lp_ctx,
661 struct gssapi_creds_container **_gcc,
662 const char **error_string)
665 OM_uint32 maj_stat, min_stat;
666 struct gssapi_creds_container *gcc;
667 struct ccache_container *ccache;
668 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
669 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
670 gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
672 krb5_enctype *etypes = NULL;
674 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
675 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
676 bool expired = false;
677 OM_uint32 lifetime = 0;
678 gss_cred_usage_t usage = 0;
679 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
680 NULL, &lifetime, &usage, NULL);
681 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
682 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
684 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
685 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
687 } else if (maj_stat != GSS_S_COMPLETE) {
688 *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
689 gssapi_error_string(cred, maj_stat, min_stat, NULL));
693 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
695 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
696 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
698 *_gcc = cred->client_gss_creds;
703 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
704 &ccache, error_string);
706 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
707 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
709 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
714 gcc = talloc(cred, struct gssapi_creds_container);
716 (*error_string) = error_message(ENOMEM);
720 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
722 if ((maj_stat == GSS_S_FAILURE) &&
723 (min_stat == (OM_uint32)KRB5_CC_END ||
724 min_stat == (OM_uint32)KRB5_CC_NOTFOUND ||
725 min_stat == (OM_uint32)KRB5_FCC_NOFILE))
727 /* This CCACHE is no good. Ensure we don't use it again */
728 cli_credentials_unconditionally_invalidate_ccache(cred);
730 /* Now try again to get a ccache */
731 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
732 &ccache, error_string);
734 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
738 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
750 (*error_string) = talloc_asprintf(cred, "gss_krb5_import_cred failed: %s", error_message(ret));
756 * transfer the enctypes from the smb_krb5_context to the gssapi layer
758 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
759 * to configure the enctypes via the krb5.conf.
761 * And the gss_init_sec_context() creates it's own krb5_context and
762 * the TGS-REQ had all enctypes in it and only the ones configured
763 * and used for the AS-REQ, so it wasn't possible to disable the usage
766 min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
769 OM_uint32 num_ktypes;
771 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
773 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
784 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
789 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
791 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
793 * This allows us to disable SIGN and SEAL on a TLS connection with
794 * GSS-SPNENO. For example ldaps:// connections.
796 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
797 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
799 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
809 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
813 cred->client_gss_creds_obtained = cred->ccache_obtained;
814 talloc_set_destructor(gcc, free_gssapi_creds);
815 cred->client_gss_creds = gcc;
821 Set a gssapi cred_id_t into the credentials system. (Client case)
823 This grabs the credentials both 'intact' and getting the krb5
824 ccache out of it. This routine can be generalised in future for
825 the case where we deal with GSSAPI mechs other than krb5.
827 On sucess, the caller must not free gssapi_cred, as it now belongs
828 to the credentials system.
831 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
832 struct loadparm_context *lp_ctx,
833 gss_cred_id_t gssapi_cred,
834 enum credentials_obtained obtained,
835 const char **error_string)
838 OM_uint32 maj_stat, min_stat;
839 struct ccache_container *ccc = NULL;
840 struct gssapi_creds_container *gcc = NULL;
841 if (cred->client_gss_creds_obtained > obtained) {
845 gcc = talloc(cred, struct gssapi_creds_container);
847 (*error_string) = error_message(ENOMEM);
851 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
856 maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
866 (*error_string) = error_message(ENOMEM);
871 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
874 cred->ccache_obtained = obtained;
876 gcc->creds = gssapi_cred;
877 talloc_set_destructor(gcc, free_gssapi_creds);
879 /* set the clinet_gss_creds_obtained here, as it just
880 got set to UNINITIALISED by the calls above */
881 cred->client_gss_creds_obtained = obtained;
882 cred->client_gss_creds = gcc;
887 static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
890 const struct ccache_container *old_ccc = NULL;
891 struct ccache_container *ccc = NULL;
892 char *ccache_name = NULL;
894 old_ccc = cred->ccache;
895 if (old_ccc == NULL) {
899 ccc = talloc(cred, struct ccache_container);
906 ccache_name = talloc_asprintf(ccc, "MEMORY:%p", ccc);
908 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
909 ccache_name, &ccc->ccache);
915 talloc_set_destructor(ccc, free_mccache);
917 TALLOC_FREE(ccache_name);
919 ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
920 old_ccc->ccache, ccc->ccache);
927 cred->client_gss_creds = NULL;
928 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
932 _PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
933 struct cli_credentials *src)
935 struct cli_credentials *dst;
938 dst = talloc(mem_ctx, struct cli_credentials);
945 ret = cli_credentials_shallow_ccache(dst);
954 static int smb_krb5_create_salt_principal(TALLOC_CTX *mem_ctx,
955 const char *samAccountName,
957 const char **salt_principal,
958 const char **error_string)
960 char *machine_username;
961 bool is_machine_account = false;
966 if (samAccountName == NULL) {
967 *error_string = "Cannot determine salt principal, no "
968 "saltPrincipal or samAccountName specified";
973 *error_string = "Cannot make principal without a realm";
977 tmp_ctx = talloc_new(mem_ctx);
978 if (tmp_ctx == NULL) {
979 *error_string = "Cannot allocate talloc context";
983 upper_realm = strupper_talloc(tmp_ctx, realm);
984 if (upper_realm == NULL) {
985 *error_string = "Cannot allocate to upper case realm";
989 machine_username = strlower_talloc(tmp_ctx, samAccountName);
990 if (!machine_username) {
991 *error_string = "Cannot duplicate samAccountName";
995 if (machine_username[strlen(machine_username) - 1] == '$') {
996 machine_username[strlen(machine_username) - 1] = '\0';
997 is_machine_account = true;
1000 if (is_machine_account) {
1003 lower_realm = strlower_talloc(tmp_ctx, realm);
1004 if (lower_realm == NULL) {
1005 *error_string = "Cannot allocate to lower case realm";
1009 *salt_principal = talloc_asprintf(mem_ctx,
1015 *salt_principal = talloc_asprintf(mem_ctx,
1020 if (*salt_principal == NULL) {
1021 *error_string = "Cannot create salt principal";
1027 talloc_free(tmp_ctx);
1031 /* Get the keytab (actually, a container containing the krb5_keytab)
1032 * attached to this context. If this hasn't been done or set before,
1033 * it will be generated from the password.
1035 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
1036 struct loadparm_context *lp_ctx,
1037 struct keytab_container **_ktc)
1039 krb5_error_code ret;
1040 struct keytab_container *ktc;
1041 struct smb_krb5_context *smb_krb5_context;
1042 const char *keytab_name;
1044 TALLOC_CTX *mem_ctx;
1045 const char *username = cli_credentials_get_username(cred);
1046 const char *realm = cli_credentials_get_realm(cred);
1047 const char *error_string;
1048 const char *salt_principal;
1050 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
1051 cred->username_obtained))) {
1052 *_ktc = cred->keytab;
1056 if (cli_credentials_is_anonymous(cred)) {
1060 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1066 mem_ctx = talloc_new(cred);
1072 * FIXME: Currently there is no better way than to create the correct
1073 * salt principal by checking if the username ends with a '$'. It would
1074 * be better if it is part of the credentials.
1076 ret = smb_krb5_create_salt_principal(mem_ctx,
1082 talloc_free(mem_ctx);
1086 ret = smb_krb5_create_memory_keytab(mem_ctx,
1087 smb_krb5_context->krb5_context,
1088 cli_credentials_get_password(cred),
1092 cli_credentials_get_kvno(cred),
1096 talloc_free(mem_ctx);
1100 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1101 keytab, keytab_name, &ktc);
1103 talloc_free(mem_ctx);
1107 cred->keytab_obtained = (MAX(cred->principal_obtained,
1108 cred->username_obtained));
1110 /* We make this keytab up based on a password. Therefore
1111 * match-by-key is acceptable, we can't match on the wrong
1113 ktc->password_based = true;
1115 talloc_steal(cred, ktc);
1117 *_ktc = cred->keytab;
1118 talloc_free(mem_ctx);
1122 /* Given the name of a keytab (presumably in the format
1123 * FILE:/etc/krb5.keytab), open it and attach it */
1125 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
1126 struct loadparm_context *lp_ctx,
1127 const char *keytab_name,
1128 enum credentials_obtained obtained)
1130 krb5_error_code ret;
1131 struct keytab_container *ktc;
1132 struct smb_krb5_context *smb_krb5_context;
1133 TALLOC_CTX *mem_ctx;
1135 if (cred->keytab_obtained >= obtained) {
1139 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1144 mem_ctx = talloc_new(cred);
1149 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1150 NULL, keytab_name, &ktc);
1155 cred->keytab_obtained = obtained;
1157 talloc_steal(cred, ktc);
1159 talloc_free(mem_ctx);
1164 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1166 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
1167 struct loadparm_context *lp_ctx,
1168 struct gssapi_creds_container **_gcc)
1171 OM_uint32 maj_stat, min_stat;
1172 struct gssapi_creds_container *gcc;
1173 struct keytab_container *ktc;
1174 struct smb_krb5_context *smb_krb5_context;
1175 TALLOC_CTX *mem_ctx;
1176 krb5_principal princ;
1177 const char *error_string;
1178 enum credentials_obtained obtained;
1180 mem_ctx = talloc_new(cred);
1185 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1190 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1192 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1194 talloc_free(mem_ctx);
1198 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1199 talloc_free(mem_ctx);
1200 *_gcc = cred->server_gss_creds;
1204 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1206 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1210 gcc = talloc(cred, struct gssapi_creds_container);
1212 talloc_free(mem_ctx);
1216 if (ktc->password_based || obtained < CRED_SPECIFIED) {
1217 /* This creates a GSSAPI cred_id_t for match-by-key with only the keytab set */
1218 maj_stat = gss_krb5_import_cred(&min_stat, NULL, NULL, ktc->keytab,
1221 /* This creates a GSSAPI cred_id_t with the principal and keytab set, matching by name */
1222 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
1233 cred->server_gss_creds_obtained = cred->keytab_obtained;
1234 talloc_set_destructor(gcc, free_gssapi_creds);
1235 cred->server_gss_creds = gcc;
1238 talloc_free(mem_ctx);
1246 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1253 * Return Kerberos KVNO
1256 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1262 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
1264 return cred->salt_principal;
1267 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
1269 talloc_free(cred->salt_principal);
1270 cred->salt_principal = talloc_strdup(cred, principal);
1273 /* The 'impersonate_principal' is used to allow one Kerberos principal
1274 * (and it's associated keytab etc) to impersonate another. The
1275 * ability to do this is controlled by the KDC, but it is generally
1276 * permitted to impersonate anyone to yourself. This allows any
1277 * member of the domain to get the groups of a user. This is also
1278 * known as S4U2Self */
1280 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1282 return cred->impersonate_principal;
1286 * The 'self_service' is the service principal that
1287 * represents the same object (by its objectSid)
1288 * as the client principal (typically our machine account).
1289 * When trying to impersonate 'impersonate_principal' with
1292 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1294 return cred->self_service;
1297 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1298 const char *principal,
1299 const char *self_service)
1301 talloc_free(cred->impersonate_principal);
1302 cred->impersonate_principal = talloc_strdup(cred, principal);
1303 talloc_free(cred->self_service);
1304 cred->self_service = talloc_strdup(cred, self_service);
1305 cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
1309 * when impersonating for S4U2proxy we need to set the target principal.
1310 * Similarly, we may only be authorized to do general impersonation to
1311 * some particular services.
1313 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1315 * NULL means that tickets will be obtained for the krbtgt service.
1318 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1320 return cred->target_service;
1323 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1325 talloc_free(cred->target_service);
1326 cred->target_service = talloc_strdup(cred, target_service);