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_krb5.h"
31 #include "auth/kerberos/kerberos_credentials.h"
32 #include "auth/kerberos/kerberos_srv_keytab.h"
33 #include "auth/kerberos/kerberos_util.h"
34 #include "auth/kerberos/pac_utils.h"
35 #include "param/param.h"
36 #include "../libds/common/flags.h"
39 #define DBGC_CLASS DBGC_AUTH
43 static void cli_credentials_invalidate_client_gss_creds(
44 struct cli_credentials *cred,
45 enum credentials_obtained obtained);
47 /* Free a memory ccache */
48 static int free_mccache(struct ccache_container *ccc)
50 if (ccc->ccache != NULL) {
51 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
59 /* Free a disk-based ccache */
60 static int free_dccache(struct ccache_container *ccc)
62 if (ccc->ccache != NULL) {
63 krb5_cc_close(ccc->smb_krb5_context->krb5_context,
71 static uint32_t smb_gss_krb5_copy_ccache(uint32_t *min_stat,
73 struct ccache_container *ccc)
75 #ifndef SAMBA4_USES_HEIMDAL /* MIT 1.10 */
76 krb5_context context = ccc->smb_krb5_context->krb5_context;
77 krb5_ccache dummy_ccache = NULL;
78 krb5_creds creds = {0};
79 krb5_cc_cursor cursor = NULL;
80 krb5_principal princ = NULL;
83 uint32_t maj_stat = GSS_S_FAILURE;
85 dummy_name = talloc_asprintf(ccc,
86 "MEMORY:gss_krb5_copy_ccache-%p",
88 if (dummy_name == NULL) {
94 * Create a dummy ccache, so we can iterate over the credentials
95 * and find the default principal for the ccache we want to
96 * copy. The new ccache needs to be initialized with this
99 code = krb5_cc_resolve(context, dummy_name, &dummy_ccache);
100 TALLOC_FREE(dummy_name);
103 return GSS_S_FAILURE;
107 * We do not need set a default principal on the temporary dummy
108 * ccache, as we do consume it at all in this function.
110 maj_stat = gss_krb5_copy_ccache(min_stat, cred, dummy_ccache);
112 krb5_cc_close(context, dummy_ccache);
116 code = krb5_cc_start_seq_get(context, dummy_ccache, &cursor);
118 krb5_cc_close(context, dummy_ccache);
120 return GSS_S_FAILURE;
123 code = krb5_cc_next_cred(context,
128 krb5_cc_close(context, dummy_ccache);
130 return GSS_S_FAILURE;
134 if (creds.ticket_flags & TKT_FLG_PRE_AUTH) {
137 tgs = krb5_princ_component(context,
140 if (tgs != NULL && tgs->length >= 1) {
143 cmp = memcmp(tgs->data,
146 if (cmp == 0 && creds.client != NULL) {
147 princ = creds.client;
154 krb5_free_cred_contents(context, &creds);
156 code = krb5_cc_next_cred(context,
162 if (code == KRB5_CC_END) {
163 krb5_cc_end_seq_get(context, dummy_ccache, &cursor);
166 krb5_cc_close(context, dummy_ccache);
168 if (code != 0 || princ == NULL) {
169 krb5_free_cred_contents(context, &creds);
171 return GSS_S_FAILURE;
175 * Set the default principal for the cache we copy
176 * into. This is needed to be able that other calls
177 * can read it with e.g. gss_acquire_cred() or
178 * krb5_cc_get_principal().
180 code = krb5_cc_initialize(context, ccc->ccache, princ);
182 krb5_free_cred_contents(context, &creds);
184 return GSS_S_FAILURE;
186 krb5_free_cred_contents(context, &creds);
188 #endif /* SAMBA4_USES_HEIMDAL */
190 return gss_krb5_copy_ccache(min_stat,
195 _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
196 struct loadparm_context *lp_ctx,
197 struct smb_krb5_context **smb_krb5_context)
200 if (cred->smb_krb5_context) {
201 *smb_krb5_context = cred->smb_krb5_context;
205 ret = smb_krb5_init_context(cred, lp_ctx,
206 &cred->smb_krb5_context);
208 cred->smb_krb5_context = NULL;
211 *smb_krb5_context = cred->smb_krb5_context;
215 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
216 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
218 _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
219 struct smb_krb5_context *smb_krb5_context)
221 if (smb_krb5_context == NULL) {
222 talloc_unlink(cred, cred->smb_krb5_context);
223 cred->smb_krb5_context = NULL;
227 if (!talloc_reference(cred, smb_krb5_context)) {
228 return NT_STATUS_NO_MEMORY;
230 cred->smb_krb5_context = smb_krb5_context;
234 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
235 struct ccache_container *ccache,
236 enum credentials_obtained obtained,
237 const char **error_string)
241 krb5_principal princ;
245 if (cred->ccache_obtained > obtained) {
249 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
250 ccache->ccache, &princ);
253 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
254 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
259 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
261 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
262 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
267 ok = cli_credentials_set_principal(cred, name, obtained);
268 krb5_free_unparsed_name(ccache->smb_krb5_context->krb5_context, name);
270 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
274 realm = smb_krb5_principal_get_realm(
275 cred, ccache->smb_krb5_context->krb5_context, princ);
276 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
280 ok = cli_credentials_set_realm(cred, realm, obtained);
286 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
287 cred->ccache_obtained = obtained;
292 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
293 struct loadparm_context *lp_ctx,
295 enum credentials_obtained obtained,
296 const char **error_string)
299 krb5_principal princ;
300 struct ccache_container *ccc;
301 if (cred->ccache_obtained > obtained) {
305 ccc = talloc(cred, struct ccache_container);
307 (*error_string) = error_message(ENOMEM);
311 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
312 &ccc->smb_krb5_context);
314 (*error_string) = error_message(ret);
318 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
320 (*error_string) = error_message(ENOMEM);
325 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
327 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
329 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
335 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
337 (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
338 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
345 talloc_set_destructor(ccc, free_dccache);
347 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
350 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
351 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
354 (*error_string) = error_message(ret);
361 cred->ccache_obtained = obtained;
363 cli_credentials_invalidate_client_gss_creds(
364 cred, cred->ccache_obtained);
369 #ifndef SAMBA4_USES_HEIMDAL
371 * This function is a workaround for old MIT Kerberos versions which did not
372 * implement the krb5_cc_remove_cred function. It creates a temporary
373 * credentials cache to copy the credentials in the current cache
374 * except the one we want to remove and then overwrites the contents of the
375 * current cache with the temporary copy.
377 static krb5_error_code krb5_cc_remove_cred_wrap(struct ccache_container *ccc,
380 krb5_ccache dummy_ccache = NULL;
381 krb5_creds cached_creds = {0};
382 krb5_cc_cursor cursor = NULL;
383 krb5_error_code code;
386 dummy_name = talloc_asprintf(ccc,
387 "MEMORY:copy_ccache-%p",
389 if (dummy_name == NULL) {
390 return KRB5_CC_NOMEM;
393 code = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
397 DBG_ERR("krb5_cc_resolve failed: %s\n",
398 smb_get_krb5_error_message(
399 ccc->smb_krb5_context->krb5_context,
401 TALLOC_FREE(dummy_name);
405 TALLOC_FREE(dummy_name);
407 code = krb5_cc_start_seq_get(ccc->smb_krb5_context->krb5_context,
411 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
414 DBG_ERR("krb5_cc_start_seq_get failed: %s\n",
415 smb_get_krb5_error_message(
416 ccc->smb_krb5_context->krb5_context,
421 while ((code = krb5_cc_next_cred(ccc->smb_krb5_context->krb5_context,
424 &cached_creds)) == 0) {
425 /* If the principal matches skip it and do not copy to the
426 * temporary cache as this is the one we want to remove */
427 if (krb5_principal_compare_flags(
428 ccc->smb_krb5_context->krb5_context,
435 code = krb5_cc_store_cred(
436 ccc->smb_krb5_context->krb5_context,
440 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
442 DBG_ERR("krb5_cc_store_cred failed: %s\n",
443 smb_get_krb5_error_message(
444 ccc->smb_krb5_context->krb5_context,
450 if (code == KRB5_CC_END) {
451 krb5_cc_end_seq_get(ccc->smb_krb5_context->krb5_context,
458 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
460 DBG_ERR("krb5_cc_next_cred failed: %s\n",
461 smb_get_krb5_error_message(
462 ccc->smb_krb5_context->krb5_context,
467 code = krb5_cc_initialize(ccc->smb_krb5_context->krb5_context,
471 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
473 DBG_ERR("krb5_cc_initialize failed: %s\n",
474 smb_get_krb5_error_message(
475 ccc->smb_krb5_context->krb5_context,
480 code = krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
484 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
486 DBG_ERR("krb5_cc_copy_creds failed: %s\n",
487 smb_get_krb5_error_message(
488 ccc->smb_krb5_context->krb5_context,
493 code = krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
496 DBG_ERR("krb5_cc_destroy failed: %s\n",
497 smb_get_krb5_error_message(
498 ccc->smb_krb5_context->krb5_context,
508 * Indicate the we failed to log in to this service/host with these
509 * credentials. The caller passes an unsigned int which they
510 * initialise to the number of times they would like to retry.
512 * This method is used to support re-trying with freshly fetched
513 * credentials in case a server is rebuilt while clients have
514 * non-expired tickets. When the client code gets a logon failure they
515 * throw away the existing credentials for the server and retry.
517 _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
518 const char *principal,
521 struct ccache_container *ccc;
522 krb5_creds creds, creds2;
525 if (principal == NULL) {
526 /* no way to delete if we don't know the principal */
532 /* not a kerberos connection */
537 /* We have already tried discarding the credentials */
543 ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
548 /* MIT kerberos requires creds.client to match against cached
550 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context,
554 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context,
556 DBG_ERR("krb5_cc_get_principal failed: %s\n",
557 smb_get_krb5_error_message(
558 ccc->smb_krb5_context->krb5_context,
563 ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
565 /* don't retry - we didn't find these credentials to remove */
566 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
570 ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
571 #ifndef SAMBA4_USES_HEIMDAL
572 if (ret == KRB5_CC_NOSUPP) {
573 /* Old MIT kerberos versions did not implement
574 * krb5_cc_remove_cred */
575 ret = krb5_cc_remove_cred_wrap(ccc, &creds);
578 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
579 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
581 /* don't retry - we didn't find these credentials to
582 * remove. Note that with the current backend this
583 * never happens, as it always returns 0 even if the
584 * creds don't exist, which is why we do a separate
585 * krb5_cc_retrieve_cred() above.
587 DBG_ERR("krb5_cc_remove_cred failed: %s\n",
588 smb_get_krb5_error_message(
589 ccc->smb_krb5_context->krb5_context,
597 static int cli_credentials_new_ccache(struct cli_credentials *cred,
598 struct loadparm_context *lp_ctx,
600 struct ccache_container **_ccc,
601 const char **error_string)
603 bool must_free_cc_name = false;
605 struct ccache_container *ccc = talloc(cred, struct ccache_container);
610 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
611 &ccc->smb_krb5_context);
614 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
618 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
620 (*error_string) = strerror(ENOMEM);
625 must_free_cc_name = true;
627 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
628 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
629 (unsigned int)getpid(), ccc);
631 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
637 (*error_string) = strerror(ENOMEM);
642 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
645 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
647 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
649 talloc_free(ccache_name);
654 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
655 talloc_set_destructor(ccc, free_mccache);
657 talloc_set_destructor(ccc, free_dccache);
660 if (must_free_cc_name) {
661 talloc_free(ccache_name);
669 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
670 struct tevent_context *event_ctx,
671 struct loadparm_context *lp_ctx,
673 struct ccache_container **ccc,
674 const char **error_string)
677 enum credentials_obtained obtained;
679 if (cred->machine_account_pending) {
680 cli_credentials_set_machine_account(cred, lp_ctx);
683 if (cred->ccache_obtained >= cred->ccache_threshold &&
684 cred->ccache_obtained > CRED_UNINITIALISED) {
686 bool expired = false;
687 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
688 cred->ccache->ccache, &lifetime);
689 if (ret == KRB5_CC_END || ret == ENOENT) {
690 /* If we have a particular ccache set, without
691 * an initial ticket, then assume there is a
693 } else if (ret == 0) {
695 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
696 cli_credentials_get_principal(cred, cred)));
698 } else if (lifetime < 300) {
699 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
700 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
704 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
705 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
710 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
711 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
718 if (cli_credentials_is_anonymous(cred)) {
719 (*error_string) = "Cannot get anonymous kerberos credentials";
723 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
728 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
733 ret = cli_credentials_set_from_ccache(cred, *ccc,
734 obtained, error_string);
737 cred->ccache_obtained = cred->principal_obtained;
741 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
745 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
746 struct tevent_context *event_ctx,
747 struct loadparm_context *lp_ctx,
748 struct ccache_container **ccc,
749 const char **error_string)
751 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
754 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
755 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
757 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
758 talloc_unlink(cred, cred->client_gss_creds);
759 cred->client_gss_creds = NULL;
761 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
764 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
765 enum credentials_obtained obtained)
767 /* If the caller just changed the username/password etc, then
768 * any cached credentials are now invalid */
769 if (obtained >= cred->client_gss_creds_obtained) {
770 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
771 talloc_unlink(cred, cred->client_gss_creds);
772 cred->client_gss_creds = NULL;
774 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
776 /* Now that we know that the data is 'this specified', then
777 * don't allow something less 'known' to be returned as a
778 * ccache. Ie, if the username is on the command line, we
779 * don't want to later guess to use a file-based ccache */
780 if (obtained > cred->client_gss_creds_threshold) {
781 cred->client_gss_creds_threshold = obtained;
785 /* We have good reason to think this CCACHE is invalid. Blow it away */
786 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
788 if (cred->ccache_obtained > CRED_UNINITIALISED) {
789 talloc_unlink(cred, cred->ccache);
792 cred->ccache_obtained = CRED_UNINITIALISED;
794 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
797 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
798 enum credentials_obtained obtained)
800 /* If the caller just changed the username/password etc, then
801 * any cached credentials are now invalid */
802 if (obtained >= cred->ccache_obtained) {
803 if (cred->ccache_obtained > CRED_UNINITIALISED) {
804 talloc_unlink(cred, cred->ccache);
807 cred->ccache_obtained = CRED_UNINITIALISED;
809 /* Now that we know that the data is 'this specified', then
810 * don't allow something less 'known' to be returned as a
811 * ccache. i.e, if the username is on the command line, we
812 * don't want to later guess to use a file-based ccache */
813 if (obtained > cred->ccache_threshold) {
814 cred->ccache_threshold = obtained;
817 cli_credentials_invalidate_client_gss_creds(cred,
821 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
824 (void)gss_release_cred(&min_stat, &gcc->creds);
828 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
829 struct tevent_context *event_ctx,
830 struct loadparm_context *lp_ctx,
831 struct gssapi_creds_container **_gcc,
832 const char **error_string)
835 OM_uint32 maj_stat, min_stat;
836 struct gssapi_creds_container *gcc;
837 struct ccache_container *ccache;
838 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
839 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
840 gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
842 krb5_enctype *etypes = NULL;
844 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
845 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
846 bool expired = false;
847 OM_uint32 lifetime = 0;
848 gss_cred_usage_t usage = 0;
849 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
850 NULL, &lifetime, &usage, NULL);
851 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
852 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
854 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
855 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
857 } else if (maj_stat != GSS_S_COMPLETE) {
858 *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
859 gssapi_error_string(cred, maj_stat, min_stat, NULL));
863 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
865 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
866 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
868 *_gcc = cred->client_gss_creds;
873 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
874 &ccache, error_string);
876 if (cli_credentials_get_kerberos_state(cred) == CRED_USE_KERBEROS_REQUIRED) {
877 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
879 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
884 gcc = talloc(cred, struct gssapi_creds_container);
886 (*error_string) = error_message(ENOMEM);
890 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
891 ccache->ccache, NULL, NULL,
893 if ((maj_stat == GSS_S_FAILURE) &&
894 (min_stat == (OM_uint32)KRB5_CC_END ||
895 min_stat == (OM_uint32)KRB5_CC_NOTFOUND ||
896 min_stat == (OM_uint32)KRB5_FCC_NOFILE))
898 /* This CCACHE is no good. Ensure we don't use it again */
899 cli_credentials_unconditionally_invalidate_ccache(cred);
901 /* Now try again to get a ccache */
902 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
903 &ccache, error_string);
905 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
909 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
910 ccache->ccache, NULL, NULL,
922 (*error_string) = talloc_asprintf(cred, "smb_gss_krb5_import_cred failed: %s", error_message(ret));
928 * transfer the enctypes from the smb_krb5_context to the gssapi layer
930 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
931 * to configure the enctypes via the krb5.conf.
933 * And the gss_init_sec_context() creates it's own krb5_context and
934 * the TGS-REQ had all enctypes in it and only the ones configured
935 * and used for the AS-REQ, so it wasn't possible to disable the usage
938 min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
941 OM_uint32 num_ktypes;
943 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
945 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
956 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
961 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
963 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
965 * This allows us to disable SIGN and SEAL on a TLS connection with
966 * GSS-SPNENO. For example ldaps:// connections.
968 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
969 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
971 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
981 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
985 cred->client_gss_creds_obtained = cred->ccache_obtained;
986 talloc_set_destructor(gcc, free_gssapi_creds);
987 cred->client_gss_creds = gcc;
993 Set a gssapi cred_id_t into the credentials system. (Client case)
995 This grabs the credentials both 'intact' and getting the krb5
996 ccache out of it. This routine can be generalised in future for
997 the case where we deal with GSSAPI mechs other than krb5.
999 On sucess, the caller must not free gssapi_cred, as it now belongs
1000 to the credentials system.
1003 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
1004 struct loadparm_context *lp_ctx,
1005 gss_cred_id_t gssapi_cred,
1006 enum credentials_obtained obtained,
1007 const char **error_string)
1010 OM_uint32 maj_stat, min_stat;
1011 struct ccache_container *ccc = NULL;
1012 struct gssapi_creds_container *gcc = NULL;
1013 if (cred->client_gss_creds_obtained > obtained) {
1017 gcc = talloc(cred, struct gssapi_creds_container);
1019 (*error_string) = error_message(ENOMEM);
1023 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
1028 maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
1038 (*error_string) = error_message(ENOMEM);
1043 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
1046 cred->ccache_obtained = obtained;
1048 gcc->creds = gssapi_cred;
1049 talloc_set_destructor(gcc, free_gssapi_creds);
1051 /* set the clinet_gss_creds_obtained here, as it just
1052 got set to UNINITIALISED by the calls above */
1053 cred->client_gss_creds_obtained = obtained;
1054 cred->client_gss_creds = gcc;
1059 static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
1061 krb5_error_code ret;
1062 const struct ccache_container *old_ccc = NULL;
1063 enum credentials_obtained old_obtained;
1064 struct ccache_container *ccc = NULL;
1065 char *ccache_name = NULL;
1066 krb5_principal princ;
1068 old_obtained = cred->ccache_obtained;
1069 old_ccc = cred->ccache;
1070 if (old_ccc == NULL) {
1074 cred->ccache = NULL;
1075 cred->ccache_obtained = CRED_UNINITIALISED;
1076 cred->client_gss_creds = NULL;
1077 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
1079 ret = krb5_cc_get_principal(
1080 old_ccc->smb_krb5_context->krb5_context,
1085 * This is an empty ccache. No point in copying anything.
1089 krb5_free_principal(old_ccc->smb_krb5_context->krb5_context, princ);
1091 ccc = talloc(cred, struct ccache_container);
1098 ccache_name = talloc_asprintf(ccc, "MEMORY:%p", ccc);
1100 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
1101 ccache_name, &ccc->ccache);
1107 talloc_set_destructor(ccc, free_mccache);
1109 TALLOC_FREE(ccache_name);
1111 ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
1112 old_ccc->ccache, ccc->ccache);
1119 cred->ccache_obtained = old_obtained;
1123 _PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
1124 struct cli_credentials *src)
1126 struct cli_credentials *dst;
1129 dst = talloc(mem_ctx, struct cli_credentials);
1136 ret = cli_credentials_shallow_ccache(dst);
1145 /* Get the keytab (actually, a container containing the krb5_keytab)
1146 * attached to this context. If this hasn't been done or set before,
1147 * it will be generated from the password.
1149 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
1150 struct loadparm_context *lp_ctx,
1151 struct keytab_container **_ktc)
1153 krb5_error_code ret;
1154 struct keytab_container *ktc;
1155 struct smb_krb5_context *smb_krb5_context;
1156 const char *keytab_name;
1158 TALLOC_CTX *mem_ctx;
1159 const char *username = cli_credentials_get_username(cred);
1160 const char *upn = NULL;
1161 const char *realm = cli_credentials_get_realm(cred);
1162 char *salt_principal = NULL;
1163 uint32_t uac_flags = 0;
1165 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
1166 cred->username_obtained))) {
1167 *_ktc = cred->keytab;
1171 if (cli_credentials_is_anonymous(cred)) {
1175 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1181 mem_ctx = talloc_new(cred);
1186 switch (cred->secure_channel_type) {
1187 case SEC_CHAN_WKSTA:
1189 uac_flags = UF_WORKSTATION_TRUST_ACCOUNT;
1192 uac_flags = UF_SERVER_TRUST_ACCOUNT;
1194 case SEC_CHAN_DOMAIN:
1195 case SEC_CHAN_DNS_DOMAIN:
1196 uac_flags = UF_INTERDOMAIN_TRUST_ACCOUNT;
1199 upn = cli_credentials_get_principal(cred, mem_ctx);
1201 TALLOC_FREE(mem_ctx);
1204 uac_flags = UF_NORMAL_ACCOUNT;
1208 ret = smb_krb5_salt_principal_str(realm,
1209 username, /* sAMAccountName */
1210 upn, /* userPrincipalName */
1215 talloc_free(mem_ctx);
1219 ret = smb_krb5_create_memory_keytab(mem_ctx,
1220 smb_krb5_context->krb5_context,
1221 cli_credentials_get_password(cred),
1225 cli_credentials_get_kvno(cred),
1229 talloc_free(mem_ctx);
1233 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1234 keytab, keytab_name, &ktc);
1236 talloc_free(mem_ctx);
1240 cred->keytab_obtained = (MAX(cred->principal_obtained,
1241 cred->username_obtained));
1243 /* We make this keytab up based on a password. Therefore
1244 * match-by-key is acceptable, we can't match on the wrong
1246 ktc->password_based = true;
1248 talloc_steal(cred, ktc);
1250 *_ktc = cred->keytab;
1251 talloc_free(mem_ctx);
1255 /* Given the name of a keytab (presumably in the format
1256 * FILE:/etc/krb5.keytab), open it and attach it */
1258 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
1259 struct loadparm_context *lp_ctx,
1260 const char *keytab_name,
1261 enum credentials_obtained obtained)
1263 krb5_error_code ret;
1264 struct keytab_container *ktc;
1265 struct smb_krb5_context *smb_krb5_context;
1266 TALLOC_CTX *mem_ctx;
1268 if (cred->keytab_obtained >= obtained) {
1272 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1277 mem_ctx = talloc_new(cred);
1282 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1283 NULL, keytab_name, &ktc);
1288 cred->keytab_obtained = obtained;
1290 talloc_steal(cred, ktc);
1292 talloc_free(mem_ctx);
1297 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1299 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
1300 struct loadparm_context *lp_ctx,
1301 struct gssapi_creds_container **_gcc)
1304 OM_uint32 maj_stat, min_stat;
1305 struct gssapi_creds_container *gcc;
1306 struct keytab_container *ktc;
1307 struct smb_krb5_context *smb_krb5_context;
1308 TALLOC_CTX *mem_ctx;
1309 krb5_principal princ;
1310 const char *error_string;
1311 enum credentials_obtained obtained;
1313 mem_ctx = talloc_new(cred);
1318 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1323 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1325 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1327 talloc_free(mem_ctx);
1331 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1332 talloc_free(mem_ctx);
1333 *_gcc = cred->server_gss_creds;
1337 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1339 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1343 gcc = talloc(cred, struct gssapi_creds_container);
1345 talloc_free(mem_ctx);
1349 if (ktc->password_based || obtained < CRED_SPECIFIED) {
1351 * This creates a GSSAPI cred_id_t for match-by-key with only
1356 maj_stat = smb_gss_krb5_import_cred(&min_stat,
1357 smb_krb5_context->krb5_context,
1369 cred->server_gss_creds_obtained = cred->keytab_obtained;
1370 talloc_set_destructor(gcc, free_gssapi_creds);
1371 cred->server_gss_creds = gcc;
1374 talloc_free(mem_ctx);
1382 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1389 * Return Kerberos KVNO
1392 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1398 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
1400 return cred->salt_principal;
1403 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
1405 talloc_free(cred->salt_principal);
1406 cred->salt_principal = talloc_strdup(cred, principal);
1409 /* The 'impersonate_principal' is used to allow one Kerberos principal
1410 * (and it's associated keytab etc) to impersonate another. The
1411 * ability to do this is controlled by the KDC, but it is generally
1412 * permitted to impersonate anyone to yourself. This allows any
1413 * member of the domain to get the groups of a user. This is also
1414 * known as S4U2Self */
1416 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1418 return cred->impersonate_principal;
1422 * The 'self_service' is the service principal that
1423 * represents the same object (by its objectSid)
1424 * as the client principal (typically our machine account).
1425 * When trying to impersonate 'impersonate_principal' with
1428 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1430 return cred->self_service;
1433 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1434 const char *principal,
1435 const char *self_service)
1437 talloc_free(cred->impersonate_principal);
1438 cred->impersonate_principal = talloc_strdup(cred, principal);
1439 talloc_free(cred->self_service);
1440 cred->self_service = talloc_strdup(cred, self_service);
1441 cli_credentials_set_kerberos_state(cred,
1442 CRED_USE_KERBEROS_REQUIRED,
1447 * when impersonating for S4U2proxy we need to set the target principal.
1448 * Similarly, we may only be authorized to do general impersonation to
1449 * some particular services.
1451 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1453 * NULL means that tickets will be obtained for the krbtgt service.
1456 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1458 return cred->target_service;
1461 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1463 talloc_free(cred->target_service);
1464 cred->target_service = talloc_strdup(cred, target_service);
1467 _PUBLIC_ int cli_credentials_get_aes256_key(struct cli_credentials *cred,
1468 TALLOC_CTX *mem_ctx,
1469 struct loadparm_context *lp_ctx,
1473 struct smb_krb5_context *smb_krb5_context = NULL;
1474 krb5_error_code krb5_ret;
1476 const char *password = NULL;
1477 krb5_data cleartext_data;
1478 krb5_data salt_data = {
1483 if (cred->password_will_be_nt_hash) {
1484 DEBUG(1,("cli_credentials_get_aes256_key: cannot generate AES256 key using NT hash\n"));
1488 password = cli_credentials_get_password(cred);
1489 if (password == NULL) {
1493 cleartext_data.data = discard_const_p(char, password);
1494 cleartext_data.length = strlen(password);
1496 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1502 salt_data.data = discard_const_p(char, salt);
1503 salt_data.length = strlen(salt);
1506 * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
1507 * the salt and the cleartext password
1509 krb5_ret = smb_krb5_create_key_from_string(smb_krb5_context->krb5_context,
1513 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
1515 if (krb5_ret != 0) {
1516 DEBUG(1,("cli_credentials_get_aes256_key: "
1517 "generation of a aes256-cts-hmac-sha1-96 key failed: %s",
1518 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
1519 krb5_ret, mem_ctx)));
1522 *aes_256 = data_blob_talloc(mem_ctx,
1523 KRB5_KEY_DATA(&key),
1524 KRB5_KEY_LENGTH(&key));
1525 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &key);
1526 if (aes_256->data == NULL) {
1529 talloc_keep_secret(aes_256->data);