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;
234 * @brief Initialize a Kerberos credential cache for later use.
236 * This functions initializes a Kerberos credential cache. If ccache_name is not
237 * specified it will either use the default credential cache or create a memory
238 * credential cache. This depends on the authentication credentials specified,
239 * username and password.
241 * @param[in] cred The credentials structure to init the cache on.
243 * @param[in] lp_ctx The loadparm context to use.
245 * @param[in] ccache_name The name of the credential cache to open or
248 * @return true on success, false if an error occcured.
250 _PUBLIC_ bool cli_credentials_ccache_init(struct cli_credentials *cred,
251 struct loadparm_context *lp_ctx,
252 const char *ccache_name)
255 krb5_error_code code;
256 enum credentials_obtained principal_obtained = CRED_UNINITIALISED;
257 struct ccache_container *ccc = NULL;
258 const char *principal;
259 const char *password = NULL;
260 bool use_memory_ccache = false;
263 if (cred->krb5_ccache_obtained != CRED_UNINITIALISED) {
267 tmp_ctx = talloc_new(cred);
268 if (tmp_ctx == NULL) {
272 principal = cli_credentials_get_principal_and_obtained(cred,
274 &principal_obtained);
275 if (principal == NULL) {
279 ccc = talloc_zero(tmp_ctx, struct ccache_container);
284 code = cli_credentials_get_krb5_context(cred,
286 &ccc->smb_krb5_context);
287 if (principal_obtained == CRED_SPECIFIED) {
288 password = cli_credentials_get_password(cred);
289 if (password != NULL) {
290 use_memory_ccache = true;
294 if (ccache_name == NULL && use_memory_ccache) {
295 ccache_name = talloc_asprintf(tmp_ctx, "MEMORY:cli_creds/%p", ccc);
296 if (ccache_name == NULL) {
301 if (ccache_name != NULL) {
302 code = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
306 code = krb5_cc_default(ccc->smb_krb5_context->krb5_context,
313 if (use_memory_ccache) {
314 talloc_set_destructor(ccc, free_mccache);
316 talloc_set_destructor(ccc, free_dccache);
319 cred->krb5_ccache = talloc_steal(cred, ccc);
320 cred->krb5_ccache_obtained = CRED_SPECIFIED;
324 talloc_free(tmp_ctx);
329 * @brief Destroy a Kerberos credential cache.
331 * This function destroys any existing contents of a cache and closes it.
333 * @param[in] cred The cli_credentials structure.
335 * @return true on success, false otherwise.
337 _PUBLIC_ bool cli_credentials_ccache_destroy(struct cli_credentials *cred)
339 struct ccache_container *ccc = cred->krb5_ccache;
340 krb5_error_code code;
342 code = krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
349 TALLOC_FREE(cred->krb5_ccache);
350 cred->krb5_ccache_obtained = CRED_UNINITIALISED;
356 * @brief Reinitialize the Kerberos credential cache
358 * If the credential cache is a memory credential cache it will be destroyed
359 * and a new clean cache will be allocated. Existing caches will just be
362 * @param[in] cred The credential structure
364 * @param[in] lp_ctx The loadparm context.
366 * @return true on success, false otherwise.
368 _PUBLIC_ bool cli_credentials_ccache_reinit(struct cli_credentials *cred,
369 struct loadparm_context *lp_ctx)
371 krb5_context context;
372 krb5_error_code code;
373 char *tmp_name = NULL;
374 const char *ccache_name;
378 if (cred->krb5_ccache_obtained == CRED_UNINITIALISED) {
381 context = cred->krb5_ccache->smb_krb5_context->krb5_context;
383 code = krb5_cc_get_full_name(context,
384 cred->krb5_ccache->ccache,
390 ccache_name = tmp_name;
391 cmp = strncasecmp_m(ccache_name, "MEMORY:", 7);
396 TALLOC_FREE(cred->krb5_ccache);
397 cred->krb5_ccache_obtained = CRED_UNINITIALISED;
399 ok = cli_credentials_ccache_init(cred, lp_ctx, ccache_name);
400 krb5_free_string(context, tmp_name);
406 * @brief Get the credential cache containter
408 * @param[in] cred The cli_credentials to get the ccache from.
410 * @return A pointer to the credential cache containter or NULL on error.
412 _PUBLIC_ struct ccache_container *cli_credentials_ccache_get(struct cli_credentials *cred)
414 return cred->krb5_ccache;
417 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
418 struct ccache_container *ccache,
419 enum credentials_obtained obtained,
420 const char **error_string)
424 krb5_principal princ;
428 if (cred->ccache_obtained > obtained) {
432 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
433 ccache->ccache, &princ);
436 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
437 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
442 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
444 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
445 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
450 ok = cli_credentials_set_principal(cred, name, obtained);
451 krb5_free_unparsed_name(ccache->smb_krb5_context->krb5_context, name);
453 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
457 realm = smb_krb5_principal_get_realm(
458 cred, ccache->smb_krb5_context->krb5_context, princ);
459 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
463 ok = cli_credentials_set_realm(cred, realm, obtained);
469 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
470 cred->ccache_obtained = obtained;
475 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
476 struct loadparm_context *lp_ctx,
478 enum credentials_obtained obtained,
479 const char **error_string)
482 krb5_principal princ;
483 struct ccache_container *ccc;
484 if (cred->ccache_obtained > obtained) {
488 ccc = talloc(cred, struct ccache_container);
490 (*error_string) = error_message(ENOMEM);
494 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
495 &ccc->smb_krb5_context);
497 (*error_string) = error_message(ret);
501 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
503 (*error_string) = error_message(ENOMEM);
508 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
510 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
512 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
518 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
520 (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
521 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
528 talloc_set_destructor(ccc, free_dccache);
530 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
533 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
534 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
537 (*error_string) = error_message(ret);
544 cred->ccache_obtained = obtained;
546 cli_credentials_invalidate_client_gss_creds(
547 cred, cred->ccache_obtained);
553 * Indicate the we failed to log in to this service/host with these
554 * credentials. The caller passes an unsigned int which they
555 * initialise to the number of times they would like to retry.
557 * This method is used to support re-trying with freshly fetched
558 * credentials in case a server is rebuilt while clients have
559 * non-expired tickets. When the client code gets a logon failure they
560 * throw away the existing credentials for the server and retry.
562 _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
563 const char *principal,
566 struct ccache_container *ccc;
567 krb5_creds creds, creds2;
570 if (principal == NULL) {
571 /* no way to delete if we don't know the principal */
577 /* not a kerberos connection */
582 /* We have already tried discarding the credentials */
588 ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
593 ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
595 /* don't retry - we didn't find these credentials to remove */
596 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
600 ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
601 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
602 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
604 /* don't retry - we didn't find these credentials to
605 * remove. Note that with the current backend this
606 * never happens, as it always returns 0 even if the
607 * creds don't exist, which is why we do a separate
608 * krb5_cc_retrieve_cred() above.
616 static int cli_credentials_new_ccache(struct cli_credentials *cred,
617 struct loadparm_context *lp_ctx,
619 struct ccache_container **_ccc,
620 const char **error_string)
622 bool must_free_cc_name = false;
624 struct ccache_container *ccc = talloc(cred, struct ccache_container);
629 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
630 &ccc->smb_krb5_context);
633 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
637 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
639 (*error_string) = strerror(ENOMEM);
644 must_free_cc_name = true;
646 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
647 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
648 (unsigned int)getpid(), ccc);
650 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
656 (*error_string) = strerror(ENOMEM);
661 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
664 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
666 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
668 talloc_free(ccache_name);
673 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
674 talloc_set_destructor(ccc, free_mccache);
676 talloc_set_destructor(ccc, free_dccache);
679 if (must_free_cc_name) {
680 talloc_free(ccache_name);
688 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
689 struct tevent_context *event_ctx,
690 struct loadparm_context *lp_ctx,
692 struct ccache_container **ccc,
693 const char **error_string)
696 enum credentials_obtained obtained;
698 if (cred->machine_account_pending) {
699 cli_credentials_set_machine_account(cred, lp_ctx);
702 if (cred->ccache_obtained >= cred->ccache_threshold &&
703 cred->ccache_obtained > CRED_UNINITIALISED) {
705 bool expired = false;
706 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
707 cred->ccache->ccache, &lifetime);
708 if (ret == KRB5_CC_END) {
709 /* If we have a particular ccache set, without
710 * an initial ticket, then assume there is a
712 } else if (ret == 0) {
714 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
715 cli_credentials_get_principal(cred, cred)));
717 } else if (lifetime < 300) {
718 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
719 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
723 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
724 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
729 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
730 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
737 if (cli_credentials_is_anonymous(cred)) {
738 (*error_string) = "Cannot get anonymous kerberos credentials";
742 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
747 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
752 ret = cli_credentials_set_from_ccache(cred, *ccc,
753 obtained, error_string);
756 cred->ccache_obtained = cred->principal_obtained;
760 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
764 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
765 struct tevent_context *event_ctx,
766 struct loadparm_context *lp_ctx,
767 struct ccache_container **ccc,
768 const char **error_string)
770 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
773 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
774 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
776 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
777 talloc_unlink(cred, cred->client_gss_creds);
778 cred->client_gss_creds = NULL;
780 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
783 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
784 enum credentials_obtained obtained)
786 /* If the caller just changed the username/password etc, then
787 * any cached credentials are now invalid */
788 if (obtained >= cred->client_gss_creds_obtained) {
789 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
790 talloc_unlink(cred, cred->client_gss_creds);
791 cred->client_gss_creds = NULL;
793 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
795 /* Now that we know that the data is 'this specified', then
796 * don't allow something less 'known' to be returned as a
797 * ccache. Ie, if the username is on the command line, we
798 * don't want to later guess to use a file-based ccache */
799 if (obtained > cred->client_gss_creds_threshold) {
800 cred->client_gss_creds_threshold = obtained;
804 /* We have good reason to think this CCACHE is invalid. Blow it away */
805 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
807 if (cred->ccache_obtained > CRED_UNINITIALISED) {
808 talloc_unlink(cred, cred->ccache);
811 cred->ccache_obtained = CRED_UNINITIALISED;
813 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
816 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
817 enum credentials_obtained obtained)
819 /* If the caller just changed the username/password etc, then
820 * any cached credentials are now invalid */
821 if (obtained >= cred->ccache_obtained) {
822 if (cred->ccache_obtained > CRED_UNINITIALISED) {
823 talloc_unlink(cred, cred->ccache);
826 cred->ccache_obtained = CRED_UNINITIALISED;
828 /* Now that we know that the data is 'this specified', then
829 * don't allow something less 'known' to be returned as a
830 * ccache. i.e, if the username is on the command line, we
831 * don't want to later guess to use a file-based ccache */
832 if (obtained > cred->ccache_threshold) {
833 cred->ccache_threshold = obtained;
836 cli_credentials_invalidate_client_gss_creds(cred,
840 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
843 (void)gss_release_cred(&min_stat, &gcc->creds);
847 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
848 struct tevent_context *event_ctx,
849 struct loadparm_context *lp_ctx,
850 struct gssapi_creds_container **_gcc,
851 const char **error_string)
854 OM_uint32 maj_stat, min_stat;
855 struct gssapi_creds_container *gcc;
856 struct ccache_container *ccache;
857 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
858 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
859 gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
861 krb5_enctype *etypes = NULL;
863 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
864 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
865 bool expired = false;
866 OM_uint32 lifetime = 0;
867 gss_cred_usage_t usage = 0;
868 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
869 NULL, &lifetime, &usage, NULL);
870 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
871 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
873 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
874 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
876 } else if (maj_stat != GSS_S_COMPLETE) {
877 *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
878 gssapi_error_string(cred, maj_stat, min_stat, NULL));
882 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
884 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
885 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
887 *_gcc = cred->client_gss_creds;
892 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
893 &ccache, error_string);
895 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
896 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
898 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
903 gcc = talloc(cred, struct gssapi_creds_container);
905 (*error_string) = error_message(ENOMEM);
909 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
910 ccache->ccache, NULL, NULL,
912 if ((maj_stat == GSS_S_FAILURE) &&
913 (min_stat == (OM_uint32)KRB5_CC_END ||
914 min_stat == (OM_uint32)KRB5_CC_NOTFOUND ||
915 min_stat == (OM_uint32)KRB5_FCC_NOFILE))
917 /* This CCACHE is no good. Ensure we don't use it again */
918 cli_credentials_unconditionally_invalidate_ccache(cred);
920 /* Now try again to get a ccache */
921 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
922 &ccache, error_string);
924 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
928 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
929 ccache->ccache, NULL, NULL,
941 (*error_string) = talloc_asprintf(cred, "smb_gss_krb5_import_cred failed: %s", error_message(ret));
947 * transfer the enctypes from the smb_krb5_context to the gssapi layer
949 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
950 * to configure the enctypes via the krb5.conf.
952 * And the gss_init_sec_context() creates it's own krb5_context and
953 * the TGS-REQ had all enctypes in it and only the ones configured
954 * and used for the AS-REQ, so it wasn't possible to disable the usage
957 min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
960 OM_uint32 num_ktypes;
962 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
964 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
975 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
980 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
982 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
984 * This allows us to disable SIGN and SEAL on a TLS connection with
985 * GSS-SPNENO. For example ldaps:// connections.
987 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
988 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
990 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
1000 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
1004 cred->client_gss_creds_obtained = cred->ccache_obtained;
1005 talloc_set_destructor(gcc, free_gssapi_creds);
1006 cred->client_gss_creds = gcc;
1012 Set a gssapi cred_id_t into the credentials system. (Client case)
1014 This grabs the credentials both 'intact' and getting the krb5
1015 ccache out of it. This routine can be generalised in future for
1016 the case where we deal with GSSAPI mechs other than krb5.
1018 On sucess, the caller must not free gssapi_cred, as it now belongs
1019 to the credentials system.
1022 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
1023 struct loadparm_context *lp_ctx,
1024 gss_cred_id_t gssapi_cred,
1025 enum credentials_obtained obtained,
1026 const char **error_string)
1029 OM_uint32 maj_stat, min_stat;
1030 struct ccache_container *ccc = NULL;
1031 struct gssapi_creds_container *gcc = NULL;
1032 if (cred->client_gss_creds_obtained > obtained) {
1036 gcc = talloc(cred, struct gssapi_creds_container);
1038 (*error_string) = error_message(ENOMEM);
1042 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
1047 maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
1057 (*error_string) = error_message(ENOMEM);
1062 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
1065 cred->ccache_obtained = obtained;
1067 gcc->creds = gssapi_cred;
1068 talloc_set_destructor(gcc, free_gssapi_creds);
1070 /* set the clinet_gss_creds_obtained here, as it just
1071 got set to UNINITIALISED by the calls above */
1072 cred->client_gss_creds_obtained = obtained;
1073 cred->client_gss_creds = gcc;
1078 static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
1080 krb5_error_code ret;
1081 const struct ccache_container *old_ccc = NULL;
1082 struct ccache_container *ccc = NULL;
1083 char *ccache_name = NULL;
1084 krb5_principal princ;
1086 old_ccc = cred->ccache;
1087 if (old_ccc == NULL) {
1091 ret = krb5_cc_get_principal(
1092 old_ccc->smb_krb5_context->krb5_context,
1097 * This is an empty ccache. No point in copying anything.
1099 cred->ccache = NULL;
1102 krb5_free_principal(old_ccc->smb_krb5_context->krb5_context, princ);
1104 ccc = talloc(cred, struct ccache_container);
1111 ccache_name = talloc_asprintf(ccc, "MEMORY:%p", ccc);
1113 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
1114 ccache_name, &ccc->ccache);
1120 talloc_set_destructor(ccc, free_mccache);
1122 TALLOC_FREE(ccache_name);
1124 ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
1125 old_ccc->ccache, ccc->ccache);
1132 cred->client_gss_creds = NULL;
1133 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
1137 _PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
1138 struct cli_credentials *src)
1140 struct cli_credentials *dst;
1143 dst = talloc(mem_ctx, struct cli_credentials);
1150 ret = cli_credentials_shallow_ccache(dst);
1159 /* Get the keytab (actually, a container containing the krb5_keytab)
1160 * attached to this context. If this hasn't been done or set before,
1161 * it will be generated from the password.
1163 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
1164 struct loadparm_context *lp_ctx,
1165 struct keytab_container **_ktc)
1167 krb5_error_code ret;
1168 struct keytab_container *ktc;
1169 struct smb_krb5_context *smb_krb5_context;
1170 const char *keytab_name;
1172 TALLOC_CTX *mem_ctx;
1173 const char *username = cli_credentials_get_username(cred);
1174 const char *upn = NULL;
1175 const char *realm = cli_credentials_get_realm(cred);
1176 char *salt_principal = NULL;
1177 uint32_t uac_flags = 0;
1179 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
1180 cred->username_obtained))) {
1181 *_ktc = cred->keytab;
1185 if (cli_credentials_is_anonymous(cred)) {
1189 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1195 mem_ctx = talloc_new(cred);
1200 switch (cred->secure_channel_type) {
1201 case SEC_CHAN_WKSTA:
1203 uac_flags = UF_WORKSTATION_TRUST_ACCOUNT;
1206 uac_flags = UF_SERVER_TRUST_ACCOUNT;
1208 case SEC_CHAN_DOMAIN:
1209 case SEC_CHAN_DNS_DOMAIN:
1210 uac_flags = UF_INTERDOMAIN_TRUST_ACCOUNT;
1213 upn = cli_credentials_get_principal(cred, mem_ctx);
1215 TALLOC_FREE(mem_ctx);
1218 uac_flags = UF_NORMAL_ACCOUNT;
1222 ret = smb_krb5_salt_principal(realm,
1223 username, /* sAMAccountName */
1224 upn, /* userPrincipalName */
1229 talloc_free(mem_ctx);
1233 ret = smb_krb5_create_memory_keytab(mem_ctx,
1234 smb_krb5_context->krb5_context,
1235 cli_credentials_get_password(cred),
1239 cli_credentials_get_kvno(cred),
1243 talloc_free(mem_ctx);
1247 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1248 keytab, keytab_name, &ktc);
1250 talloc_free(mem_ctx);
1254 cred->keytab_obtained = (MAX(cred->principal_obtained,
1255 cred->username_obtained));
1257 /* We make this keytab up based on a password. Therefore
1258 * match-by-key is acceptable, we can't match on the wrong
1260 ktc->password_based = true;
1262 talloc_steal(cred, ktc);
1264 *_ktc = cred->keytab;
1265 talloc_free(mem_ctx);
1269 /* Given the name of a keytab (presumably in the format
1270 * FILE:/etc/krb5.keytab), open it and attach it */
1272 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
1273 struct loadparm_context *lp_ctx,
1274 const char *keytab_name,
1275 enum credentials_obtained obtained)
1277 krb5_error_code ret;
1278 struct keytab_container *ktc;
1279 struct smb_krb5_context *smb_krb5_context;
1280 TALLOC_CTX *mem_ctx;
1282 if (cred->keytab_obtained >= obtained) {
1286 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1291 mem_ctx = talloc_new(cred);
1296 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1297 NULL, keytab_name, &ktc);
1302 cred->keytab_obtained = obtained;
1304 talloc_steal(cred, ktc);
1306 talloc_free(mem_ctx);
1311 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1313 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
1314 struct loadparm_context *lp_ctx,
1315 struct gssapi_creds_container **_gcc)
1318 OM_uint32 maj_stat, min_stat;
1319 struct gssapi_creds_container *gcc;
1320 struct keytab_container *ktc;
1321 struct smb_krb5_context *smb_krb5_context;
1322 TALLOC_CTX *mem_ctx;
1323 krb5_principal princ;
1324 const char *error_string;
1325 enum credentials_obtained obtained;
1327 mem_ctx = talloc_new(cred);
1332 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1337 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1339 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1341 talloc_free(mem_ctx);
1345 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1346 talloc_free(mem_ctx);
1347 *_gcc = cred->server_gss_creds;
1351 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1353 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1357 gcc = talloc(cred, struct gssapi_creds_container);
1359 talloc_free(mem_ctx);
1363 if (ktc->password_based || obtained < CRED_SPECIFIED) {
1365 * This creates a GSSAPI cred_id_t for match-by-key with only
1370 maj_stat = smb_gss_krb5_import_cred(&min_stat,
1371 smb_krb5_context->krb5_context,
1383 cred->server_gss_creds_obtained = cred->keytab_obtained;
1384 talloc_set_destructor(gcc, free_gssapi_creds);
1385 cred->server_gss_creds = gcc;
1388 talloc_free(mem_ctx);
1396 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1403 * Return Kerberos KVNO
1406 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1412 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
1414 return cred->salt_principal;
1417 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
1419 talloc_free(cred->salt_principal);
1420 cred->salt_principal = talloc_strdup(cred, principal);
1423 /* The 'impersonate_principal' is used to allow one Kerberos principal
1424 * (and it's associated keytab etc) to impersonate another. The
1425 * ability to do this is controlled by the KDC, but it is generally
1426 * permitted to impersonate anyone to yourself. This allows any
1427 * member of the domain to get the groups of a user. This is also
1428 * known as S4U2Self */
1430 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1432 return cred->impersonate_principal;
1436 * The 'self_service' is the service principal that
1437 * represents the same object (by its objectSid)
1438 * as the client principal (typically our machine account).
1439 * When trying to impersonate 'impersonate_principal' with
1442 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1444 return cred->self_service;
1447 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1448 const char *principal,
1449 const char *self_service)
1451 talloc_free(cred->impersonate_principal);
1452 cred->impersonate_principal = talloc_strdup(cred, principal);
1453 talloc_free(cred->self_service);
1454 cred->self_service = talloc_strdup(cred, self_service);
1455 cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
1459 * when impersonating for S4U2proxy we need to set the target principal.
1460 * Similarly, we may only be authorized to do general impersonation to
1461 * some particular services.
1463 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1465 * NULL means that tickets will be obtained for the krbtgt service.
1468 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1470 return cred->target_service;
1473 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1475 talloc_free(cred->target_service);
1476 cred->target_service = talloc_strdup(cred, target_service);