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 _PUBLIC_ bool cli_credentials_ccache_update_principal(struct cli_credentials *creds)
419 krb5_context context;
420 struct ccache_container *ccc = cli_credentials_ccache_get(creds);
421 krb5_principal cc_principal = NULL;
422 krb5_error_code code;
430 context = ccc->smb_krb5_context->krb5_context;
432 code = krb5_cc_get_principal(context,
438 case KRB5_CC_NOTFOUND:
439 case KRB5_FCC_NOFILE:
445 code = smb_krb5_unparse_name(creds,
453 ok = cli_credentials_set_principal(creds,
456 TALLOC_FREE(principal);
458 krb5_free_principal(context, cc_principal);
462 realm = smb_krb5_principal_get_realm(context, cc_principal);
463 krb5_free_principal(context, cc_principal);
467 ok = cli_credentials_set_realm(creds,
475 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
476 struct ccache_container *ccache,
477 enum credentials_obtained obtained,
478 const char **error_string)
482 krb5_principal princ;
486 if (cred->ccache_obtained > obtained) {
490 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
491 ccache->ccache, &princ);
494 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
495 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
500 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
502 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
503 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
508 ok = cli_credentials_set_principal(cred, name, obtained);
509 krb5_free_unparsed_name(ccache->smb_krb5_context->krb5_context, name);
511 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
515 realm = smb_krb5_principal_get_realm(
516 cred, ccache->smb_krb5_context->krb5_context, princ);
517 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
521 ok = cli_credentials_set_realm(cred, realm, obtained);
527 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
528 cred->ccache_obtained = obtained;
533 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
534 struct loadparm_context *lp_ctx,
536 enum credentials_obtained obtained,
537 const char **error_string)
540 krb5_principal princ;
541 struct ccache_container *ccc;
542 if (cred->ccache_obtained > obtained) {
546 ccc = talloc(cred, struct ccache_container);
548 (*error_string) = error_message(ENOMEM);
552 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
553 &ccc->smb_krb5_context);
555 (*error_string) = error_message(ret);
559 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
561 (*error_string) = error_message(ENOMEM);
566 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
568 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
570 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
576 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
578 (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
579 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
586 talloc_set_destructor(ccc, free_dccache);
588 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
591 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
592 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
595 (*error_string) = error_message(ret);
602 cred->ccache_obtained = obtained;
604 cli_credentials_invalidate_client_gss_creds(
605 cred, cred->ccache_obtained);
611 * Indicate the we failed to log in to this service/host with these
612 * credentials. The caller passes an unsigned int which they
613 * initialise to the number of times they would like to retry.
615 * This method is used to support re-trying with freshly fetched
616 * credentials in case a server is rebuilt while clients have
617 * non-expired tickets. When the client code gets a logon failure they
618 * throw away the existing credentials for the server and retry.
620 _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
621 const char *principal,
624 struct ccache_container *ccc;
625 krb5_creds creds, creds2;
628 if (principal == NULL) {
629 /* no way to delete if we don't know the principal */
635 /* not a kerberos connection */
640 /* We have already tried discarding the credentials */
646 ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
651 ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
653 /* don't retry - we didn't find these credentials to remove */
654 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
658 ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
659 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
660 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
662 /* don't retry - we didn't find these credentials to
663 * remove. Note that with the current backend this
664 * never happens, as it always returns 0 even if the
665 * creds don't exist, which is why we do a separate
666 * krb5_cc_retrieve_cred() above.
674 static int cli_credentials_new_ccache(struct cli_credentials *cred,
675 struct loadparm_context *lp_ctx,
677 struct ccache_container **_ccc,
678 const char **error_string)
680 bool must_free_cc_name = false;
682 struct ccache_container *ccc = talloc(cred, struct ccache_container);
687 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
688 &ccc->smb_krb5_context);
691 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
695 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
697 (*error_string) = strerror(ENOMEM);
702 must_free_cc_name = true;
704 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
705 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
706 (unsigned int)getpid(), ccc);
708 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
714 (*error_string) = strerror(ENOMEM);
719 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
722 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
724 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
726 talloc_free(ccache_name);
731 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
732 talloc_set_destructor(ccc, free_mccache);
734 talloc_set_destructor(ccc, free_dccache);
737 if (must_free_cc_name) {
738 talloc_free(ccache_name);
746 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
747 struct tevent_context *event_ctx,
748 struct loadparm_context *lp_ctx,
750 struct ccache_container **ccc,
751 const char **error_string)
754 enum credentials_obtained obtained;
756 if (cred->machine_account_pending) {
757 cli_credentials_set_machine_account(cred, lp_ctx);
760 if (cred->ccache_obtained >= cred->ccache_threshold &&
761 cred->ccache_obtained > CRED_UNINITIALISED) {
763 bool expired = false;
764 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
765 cred->ccache->ccache, &lifetime);
766 if (ret == KRB5_CC_END) {
767 /* If we have a particular ccache set, without
768 * an initial ticket, then assume there is a
770 } else if (ret == 0) {
772 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
773 cli_credentials_get_principal(cred, cred)));
775 } else if (lifetime < 300) {
776 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
777 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
781 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
782 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
787 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
788 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
795 if (cli_credentials_is_anonymous(cred)) {
796 (*error_string) = "Cannot get anonymous kerberos credentials";
800 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
805 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
810 ret = cli_credentials_set_from_ccache(cred, *ccc,
811 obtained, error_string);
814 cred->ccache_obtained = cred->principal_obtained;
818 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
822 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
823 struct tevent_context *event_ctx,
824 struct loadparm_context *lp_ctx,
825 struct ccache_container **ccc,
826 const char **error_string)
828 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
831 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
832 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
834 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
835 talloc_unlink(cred, cred->client_gss_creds);
836 cred->client_gss_creds = NULL;
838 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
841 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
842 enum credentials_obtained obtained)
844 /* If the caller just changed the username/password etc, then
845 * any cached credentials are now invalid */
846 if (obtained >= cred->client_gss_creds_obtained) {
847 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
848 talloc_unlink(cred, cred->client_gss_creds);
849 cred->client_gss_creds = NULL;
851 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
853 /* Now that we know that the data is 'this specified', then
854 * don't allow something less 'known' to be returned as a
855 * ccache. Ie, if the username is on the command line, we
856 * don't want to later guess to use a file-based ccache */
857 if (obtained > cred->client_gss_creds_threshold) {
858 cred->client_gss_creds_threshold = obtained;
862 /* We have good reason to think this CCACHE is invalid. Blow it away */
863 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
865 if (cred->ccache_obtained > CRED_UNINITIALISED) {
866 talloc_unlink(cred, cred->ccache);
869 cred->ccache_obtained = CRED_UNINITIALISED;
871 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
874 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
875 enum credentials_obtained obtained)
877 /* If the caller just changed the username/password etc, then
878 * any cached credentials are now invalid */
879 if (obtained >= cred->ccache_obtained) {
880 if (cred->ccache_obtained > CRED_UNINITIALISED) {
881 talloc_unlink(cred, cred->ccache);
884 cred->ccache_obtained = CRED_UNINITIALISED;
886 /* Now that we know that the data is 'this specified', then
887 * don't allow something less 'known' to be returned as a
888 * ccache. i.e, if the username is on the command line, we
889 * don't want to later guess to use a file-based ccache */
890 if (obtained > cred->ccache_threshold) {
891 cred->ccache_threshold = obtained;
894 cli_credentials_invalidate_client_gss_creds(cred,
898 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
901 (void)gss_release_cred(&min_stat, &gcc->creds);
905 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
906 struct tevent_context *event_ctx,
907 struct loadparm_context *lp_ctx,
908 struct gssapi_creds_container **_gcc,
909 const char **error_string)
912 OM_uint32 maj_stat, min_stat;
913 struct gssapi_creds_container *gcc;
914 struct ccache_container *ccache;
915 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
916 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
917 gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
919 krb5_enctype *etypes = NULL;
921 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
922 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
923 bool expired = false;
924 OM_uint32 lifetime = 0;
925 gss_cred_usage_t usage = 0;
926 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
927 NULL, &lifetime, &usage, NULL);
928 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
929 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
931 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
932 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
934 } else if (maj_stat != GSS_S_COMPLETE) {
935 *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
936 gssapi_error_string(cred, maj_stat, min_stat, NULL));
940 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
942 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
943 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
945 *_gcc = cred->client_gss_creds;
950 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
951 &ccache, error_string);
953 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
954 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
956 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
961 gcc = talloc(cred, struct gssapi_creds_container);
963 (*error_string) = error_message(ENOMEM);
967 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
968 ccache->ccache, NULL, NULL,
970 if ((maj_stat == GSS_S_FAILURE) &&
971 (min_stat == (OM_uint32)KRB5_CC_END ||
972 min_stat == (OM_uint32)KRB5_CC_NOTFOUND ||
973 min_stat == (OM_uint32)KRB5_FCC_NOFILE))
975 /* This CCACHE is no good. Ensure we don't use it again */
976 cli_credentials_unconditionally_invalidate_ccache(cred);
978 /* Now try again to get a ccache */
979 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
980 &ccache, error_string);
982 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
986 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
987 ccache->ccache, NULL, NULL,
999 (*error_string) = talloc_asprintf(cred, "smb_gss_krb5_import_cred failed: %s", error_message(ret));
1005 * transfer the enctypes from the smb_krb5_context to the gssapi layer
1007 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
1008 * to configure the enctypes via the krb5.conf.
1010 * And the gss_init_sec_context() creates it's own krb5_context and
1011 * the TGS-REQ had all enctypes in it and only the ones configured
1012 * and used for the AS-REQ, so it wasn't possible to disable the usage
1015 min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
1017 if (min_stat == 0) {
1018 OM_uint32 num_ktypes;
1020 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
1022 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
1024 (int32_t *) etypes);
1033 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
1038 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
1040 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
1042 * This allows us to disable SIGN and SEAL on a TLS connection with
1043 * GSS-SPNENO. For example ldaps:// connections.
1045 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
1046 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
1048 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
1058 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
1062 cred->client_gss_creds_obtained = cred->ccache_obtained;
1063 talloc_set_destructor(gcc, free_gssapi_creds);
1064 cred->client_gss_creds = gcc;
1070 Set a gssapi cred_id_t into the credentials system. (Client case)
1072 This grabs the credentials both 'intact' and getting the krb5
1073 ccache out of it. This routine can be generalised in future for
1074 the case where we deal with GSSAPI mechs other than krb5.
1076 On sucess, the caller must not free gssapi_cred, as it now belongs
1077 to the credentials system.
1080 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
1081 struct loadparm_context *lp_ctx,
1082 gss_cred_id_t gssapi_cred,
1083 enum credentials_obtained obtained,
1084 const char **error_string)
1087 OM_uint32 maj_stat, min_stat;
1088 struct ccache_container *ccc = NULL;
1089 struct gssapi_creds_container *gcc = NULL;
1090 if (cred->client_gss_creds_obtained > obtained) {
1094 gcc = talloc(cred, struct gssapi_creds_container);
1096 (*error_string) = error_message(ENOMEM);
1100 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
1105 maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
1115 (*error_string) = error_message(ENOMEM);
1120 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
1123 cred->ccache_obtained = obtained;
1125 gcc->creds = gssapi_cred;
1126 talloc_set_destructor(gcc, free_gssapi_creds);
1128 /* set the clinet_gss_creds_obtained here, as it just
1129 got set to UNINITIALISED by the calls above */
1130 cred->client_gss_creds_obtained = obtained;
1131 cred->client_gss_creds = gcc;
1136 static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
1138 krb5_error_code ret;
1139 const struct ccache_container *old_ccc = NULL;
1140 struct ccache_container *ccc = NULL;
1141 char *ccache_name = NULL;
1142 krb5_principal princ;
1144 old_ccc = cred->ccache;
1145 if (old_ccc == NULL) {
1149 ret = krb5_cc_get_principal(
1150 old_ccc->smb_krb5_context->krb5_context,
1155 * This is an empty ccache. No point in copying anything.
1157 cred->ccache = NULL;
1160 krb5_free_principal(old_ccc->smb_krb5_context->krb5_context, princ);
1162 ccc = talloc(cred, struct ccache_container);
1169 ccache_name = talloc_asprintf(ccc, "MEMORY:%p", ccc);
1171 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
1172 ccache_name, &ccc->ccache);
1178 talloc_set_destructor(ccc, free_mccache);
1180 TALLOC_FREE(ccache_name);
1182 ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
1183 old_ccc->ccache, ccc->ccache);
1190 cred->client_gss_creds = NULL;
1191 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
1195 _PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
1196 struct cli_credentials *src)
1198 struct cli_credentials *dst;
1201 dst = talloc(mem_ctx, struct cli_credentials);
1208 ret = cli_credentials_shallow_ccache(dst);
1217 /* Get the keytab (actually, a container containing the krb5_keytab)
1218 * attached to this context. If this hasn't been done or set before,
1219 * it will be generated from the password.
1221 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
1222 struct loadparm_context *lp_ctx,
1223 struct keytab_container **_ktc)
1225 krb5_error_code ret;
1226 struct keytab_container *ktc;
1227 struct smb_krb5_context *smb_krb5_context;
1228 const char *keytab_name;
1230 TALLOC_CTX *mem_ctx;
1231 const char *username = cli_credentials_get_username(cred);
1232 const char *upn = NULL;
1233 const char *realm = cli_credentials_get_realm(cred);
1234 char *salt_principal = NULL;
1235 uint32_t uac_flags = 0;
1237 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
1238 cred->username_obtained))) {
1239 *_ktc = cred->keytab;
1243 if (cli_credentials_is_anonymous(cred)) {
1247 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1253 mem_ctx = talloc_new(cred);
1258 switch (cred->secure_channel_type) {
1259 case SEC_CHAN_WKSTA:
1261 uac_flags = UF_WORKSTATION_TRUST_ACCOUNT;
1264 uac_flags = UF_SERVER_TRUST_ACCOUNT;
1266 case SEC_CHAN_DOMAIN:
1267 case SEC_CHAN_DNS_DOMAIN:
1268 uac_flags = UF_INTERDOMAIN_TRUST_ACCOUNT;
1271 upn = cli_credentials_get_principal(cred, mem_ctx);
1273 TALLOC_FREE(mem_ctx);
1276 uac_flags = UF_NORMAL_ACCOUNT;
1280 ret = smb_krb5_salt_principal(realm,
1281 username, /* sAMAccountName */
1282 upn, /* userPrincipalName */
1287 talloc_free(mem_ctx);
1291 ret = smb_krb5_create_memory_keytab(mem_ctx,
1292 smb_krb5_context->krb5_context,
1293 cli_credentials_get_password(cred),
1297 cli_credentials_get_kvno(cred),
1301 talloc_free(mem_ctx);
1305 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1306 keytab, keytab_name, &ktc);
1308 talloc_free(mem_ctx);
1312 cred->keytab_obtained = (MAX(cred->principal_obtained,
1313 cred->username_obtained));
1315 /* We make this keytab up based on a password. Therefore
1316 * match-by-key is acceptable, we can't match on the wrong
1318 ktc->password_based = true;
1320 talloc_steal(cred, ktc);
1322 *_ktc = cred->keytab;
1323 talloc_free(mem_ctx);
1327 /* Given the name of a keytab (presumably in the format
1328 * FILE:/etc/krb5.keytab), open it and attach it */
1330 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
1331 struct loadparm_context *lp_ctx,
1332 const char *keytab_name,
1333 enum credentials_obtained obtained)
1335 krb5_error_code ret;
1336 struct keytab_container *ktc;
1337 struct smb_krb5_context *smb_krb5_context;
1338 TALLOC_CTX *mem_ctx;
1340 if (cred->keytab_obtained >= obtained) {
1344 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1349 mem_ctx = talloc_new(cred);
1354 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1355 NULL, keytab_name, &ktc);
1360 cred->keytab_obtained = obtained;
1362 talloc_steal(cred, ktc);
1364 talloc_free(mem_ctx);
1369 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1371 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
1372 struct loadparm_context *lp_ctx,
1373 struct gssapi_creds_container **_gcc)
1376 OM_uint32 maj_stat, min_stat;
1377 struct gssapi_creds_container *gcc;
1378 struct keytab_container *ktc;
1379 struct smb_krb5_context *smb_krb5_context;
1380 TALLOC_CTX *mem_ctx;
1381 krb5_principal princ;
1382 const char *error_string;
1383 enum credentials_obtained obtained;
1385 mem_ctx = talloc_new(cred);
1390 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1395 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1397 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1399 talloc_free(mem_ctx);
1403 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1404 talloc_free(mem_ctx);
1405 *_gcc = cred->server_gss_creds;
1409 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1411 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1415 gcc = talloc(cred, struct gssapi_creds_container);
1417 talloc_free(mem_ctx);
1421 if (ktc->password_based || obtained < CRED_SPECIFIED) {
1423 * This creates a GSSAPI cred_id_t for match-by-key with only
1428 maj_stat = smb_gss_krb5_import_cred(&min_stat,
1429 smb_krb5_context->krb5_context,
1441 cred->server_gss_creds_obtained = cred->keytab_obtained;
1442 talloc_set_destructor(gcc, free_gssapi_creds);
1443 cred->server_gss_creds = gcc;
1446 talloc_free(mem_ctx);
1454 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1461 * Return Kerberos KVNO
1464 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1470 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
1472 return cred->salt_principal;
1475 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
1477 talloc_free(cred->salt_principal);
1478 cred->salt_principal = talloc_strdup(cred, principal);
1481 /* The 'impersonate_principal' is used to allow one Kerberos principal
1482 * (and it's associated keytab etc) to impersonate another. The
1483 * ability to do this is controlled by the KDC, but it is generally
1484 * permitted to impersonate anyone to yourself. This allows any
1485 * member of the domain to get the groups of a user. This is also
1486 * known as S4U2Self */
1488 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1490 return cred->impersonate_principal;
1494 * The 'self_service' is the service principal that
1495 * represents the same object (by its objectSid)
1496 * as the client principal (typically our machine account).
1497 * When trying to impersonate 'impersonate_principal' with
1500 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1502 return cred->self_service;
1505 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1506 const char *principal,
1507 const char *self_service)
1509 talloc_free(cred->impersonate_principal);
1510 cred->impersonate_principal = talloc_strdup(cred, principal);
1511 talloc_free(cred->self_service);
1512 cred->self_service = talloc_strdup(cred, self_service);
1513 cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
1517 * when impersonating for S4U2proxy we need to set the target principal.
1518 * Similarly, we may only be authorized to do general impersonation to
1519 * some particular services.
1521 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1523 * NULL means that tickets will be obtained for the krbtgt service.
1526 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1528 return cred->target_service;
1531 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1533 talloc_free(cred->target_service);
1534 cred->target_service = talloc_strdup(cred, target_service);