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
;
82 uint32_t maj_stat
= GSS_S_FAILURE
;
85 * Create a dummy ccache, so we can iterate over the credentials
86 * and find the default principal for the ccache we want to
87 * copy. The new ccache needs to be initialized with this
90 code
= smb_krb5_cc_new_unique_memory(context
, NULL
, NULL
, &dummy_ccache
);
97 * We do not need set a default principal on the temporary dummy
98 * ccache, as we do consume it at all in this function.
100 maj_stat
= gss_krb5_copy_ccache(min_stat
, cred
, dummy_ccache
);
102 krb5_cc_destroy(context
, dummy_ccache
);
106 code
= krb5_cc_start_seq_get(context
, dummy_ccache
, &cursor
);
108 krb5_cc_destroy(context
, dummy_ccache
);
110 return GSS_S_FAILURE
;
113 code
= krb5_cc_next_cred(context
,
118 krb5_cc_destroy(context
, dummy_ccache
);
120 return GSS_S_FAILURE
;
124 if (creds
.ticket_flags
& TKT_FLG_PRE_AUTH
) {
127 tgs
= krb5_princ_component(context
,
130 if (tgs
!= NULL
&& tgs
->length
>= 1) {
133 cmp
= memcmp(tgs
->data
,
136 if (cmp
== 0 && creds
.client
!= NULL
) {
137 princ
= creds
.client
;
144 krb5_free_cred_contents(context
, &creds
);
146 code
= krb5_cc_next_cred(context
,
152 if (code
== KRB5_CC_END
) {
153 krb5_cc_end_seq_get(context
, dummy_ccache
, &cursor
);
156 krb5_cc_destroy(context
, dummy_ccache
);
158 if (code
!= 0 || princ
== NULL
) {
159 krb5_free_cred_contents(context
, &creds
);
161 return GSS_S_FAILURE
;
165 * Set the default principal for the cache we copy
166 * into. This is needed to be able that other calls
167 * can read it with e.g. gss_acquire_cred() or
168 * krb5_cc_get_principal().
170 code
= krb5_cc_initialize(context
, ccc
->ccache
, princ
);
172 krb5_free_cred_contents(context
, &creds
);
174 return GSS_S_FAILURE
;
176 krb5_free_cred_contents(context
, &creds
);
178 #endif /* SAMBA4_USES_HEIMDAL */
180 return gss_krb5_copy_ccache(min_stat
,
185 _PUBLIC_
int cli_credentials_get_krb5_context(struct cli_credentials
*cred
,
186 struct loadparm_context
*lp_ctx
,
187 struct smb_krb5_context
**smb_krb5_context
)
190 if (cred
->smb_krb5_context
) {
191 *smb_krb5_context
= cred
->smb_krb5_context
;
195 ret
= smb_krb5_init_context(cred
, lp_ctx
,
196 &cred
->smb_krb5_context
);
198 cred
->smb_krb5_context
= NULL
;
201 *smb_krb5_context
= cred
->smb_krb5_context
;
205 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
206 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
208 _PUBLIC_ NTSTATUS
cli_credentials_set_krb5_context(struct cli_credentials
*cred
,
209 struct smb_krb5_context
*smb_krb5_context
)
211 if (smb_krb5_context
== NULL
) {
212 talloc_unlink(cred
, cred
->smb_krb5_context
);
213 cred
->smb_krb5_context
= NULL
;
217 if (!talloc_reference(cred
, smb_krb5_context
)) {
218 return NT_STATUS_NO_MEMORY
;
220 cred
->smb_krb5_context
= smb_krb5_context
;
224 static int cli_credentials_set_from_ccache(struct cli_credentials
*cred
,
225 struct ccache_container
*ccache
,
226 enum credentials_obtained obtained
,
227 const char **error_string
)
231 krb5_principal princ
;
235 if (cred
->ccache_obtained
> obtained
) {
239 ret
= krb5_cc_get_principal(ccache
->smb_krb5_context
->krb5_context
,
240 ccache
->ccache
, &princ
);
243 (*error_string
) = talloc_asprintf(cred
, "failed to get principal from ccache: %s\n",
244 smb_get_krb5_error_message(ccache
->smb_krb5_context
->krb5_context
,
249 ret
= krb5_unparse_name(ccache
->smb_krb5_context
->krb5_context
, princ
, &name
);
251 (*error_string
) = talloc_asprintf(cred
, "failed to unparse principal from ccache: %s\n",
252 smb_get_krb5_error_message(ccache
->smb_krb5_context
->krb5_context
,
254 krb5_free_principal(ccache
->smb_krb5_context
->krb5_context
, princ
);
258 ok
= cli_credentials_set_principal(cred
, name
, obtained
);
259 krb5_free_unparsed_name(ccache
->smb_krb5_context
->krb5_context
, name
);
261 krb5_free_principal(ccache
->smb_krb5_context
->krb5_context
, princ
);
265 realm
= smb_krb5_principal_get_realm(
266 cred
, ccache
->smb_krb5_context
->krb5_context
, princ
);
267 krb5_free_principal(ccache
->smb_krb5_context
->krb5_context
, princ
);
271 ok
= cli_credentials_set_realm(cred
, realm
, obtained
);
277 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
278 cred
->ccache_obtained
= obtained
;
283 _PUBLIC_
int cli_credentials_set_ccache(struct cli_credentials
*cred
,
284 struct loadparm_context
*lp_ctx
,
286 enum credentials_obtained obtained
,
287 const char **error_string
)
290 krb5_principal princ
;
291 struct ccache_container
*ccc
;
292 if (cred
->ccache_obtained
> obtained
) {
296 ccc
= talloc(cred
, struct ccache_container
);
298 (*error_string
) = error_message(ENOMEM
);
302 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
303 &ccc
->smb_krb5_context
);
305 (*error_string
) = error_message(ret
);
309 if (!talloc_reference(ccc
, ccc
->smb_krb5_context
)) {
311 (*error_string
) = error_message(ENOMEM
);
316 ret
= krb5_cc_resolve(ccc
->smb_krb5_context
->krb5_context
, name
, &ccc
->ccache
);
318 (*error_string
) = talloc_asprintf(cred
, "failed to read krb5 ccache: %s: %s\n",
320 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
,
327 * This is where the caller really wants to use
328 * the default krb5 ccache.
330 ret
= smb_force_krb5_cc_default(ccc
->smb_krb5_context
->krb5_context
, &ccc
->ccache
);
332 (*error_string
) = talloc_asprintf(cred
, "failed to read default krb5 ccache: %s\n",
333 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
,
340 talloc_set_destructor(ccc
, free_dccache
);
342 ret
= krb5_cc_get_principal(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
, &princ
);
345 krb5_free_principal(ccc
->smb_krb5_context
->krb5_context
, princ
);
346 ret
= cli_credentials_set_from_ccache(cred
, ccc
, obtained
, error_string
);
349 (*error_string
) = error_message(ret
);
356 cred
->ccache_obtained
= obtained
;
358 cli_credentials_invalidate_client_gss_creds(
359 cred
, cred
->ccache_obtained
);
364 #ifndef SAMBA4_USES_HEIMDAL
366 * This function is a workaround for old MIT Kerberos versions which did not
367 * implement the krb5_cc_remove_cred function. It creates a temporary
368 * credentials cache to copy the credentials in the current cache
369 * except the one we want to remove and then overwrites the contents of the
370 * current cache with the temporary copy.
372 static krb5_error_code
krb5_cc_remove_cred_wrap(struct ccache_container
*ccc
,
375 krb5_ccache dummy_ccache
= NULL
;
376 krb5_creds cached_creds
= {0};
377 krb5_cc_cursor cursor
= NULL
;
378 krb5_error_code code
;
380 code
= smb_krb5_cc_new_unique_memory(ccc
->smb_krb5_context
->krb5_context
,
384 DBG_ERR("krb5_cc_resolve failed: %s\n",
385 smb_get_krb5_error_message(
386 ccc
->smb_krb5_context
->krb5_context
,
391 code
= krb5_cc_start_seq_get(ccc
->smb_krb5_context
->krb5_context
,
395 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
398 DBG_ERR("krb5_cc_start_seq_get failed: %s\n",
399 smb_get_krb5_error_message(
400 ccc
->smb_krb5_context
->krb5_context
,
405 while ((code
= krb5_cc_next_cred(ccc
->smb_krb5_context
->krb5_context
,
408 &cached_creds
)) == 0) {
409 /* If the principal matches skip it and do not copy to the
410 * temporary cache as this is the one we want to remove */
411 if (krb5_principal_compare_flags(
412 ccc
->smb_krb5_context
->krb5_context
,
419 code
= krb5_cc_store_cred(
420 ccc
->smb_krb5_context
->krb5_context
,
424 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
426 DBG_ERR("krb5_cc_store_cred failed: %s\n",
427 smb_get_krb5_error_message(
428 ccc
->smb_krb5_context
->krb5_context
,
434 if (code
== KRB5_CC_END
) {
435 krb5_cc_end_seq_get(ccc
->smb_krb5_context
->krb5_context
,
442 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
444 DBG_ERR("krb5_cc_next_cred failed: %s\n",
445 smb_get_krb5_error_message(
446 ccc
->smb_krb5_context
->krb5_context
,
451 code
= krb5_cc_initialize(ccc
->smb_krb5_context
->krb5_context
,
455 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
457 DBG_ERR("krb5_cc_initialize failed: %s\n",
458 smb_get_krb5_error_message(
459 ccc
->smb_krb5_context
->krb5_context
,
464 code
= krb5_cc_copy_creds(ccc
->smb_krb5_context
->krb5_context
,
468 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
470 DBG_ERR("krb5_cc_copy_creds failed: %s\n",
471 smb_get_krb5_error_message(
472 ccc
->smb_krb5_context
->krb5_context
,
477 code
= krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
480 DBG_ERR("krb5_cc_destroy failed: %s\n",
481 smb_get_krb5_error_message(
482 ccc
->smb_krb5_context
->krb5_context
,
492 * Indicate that we failed to log in to this service/host with these
493 * credentials. The caller passes an unsigned int which they
494 * initialise to the number of times they would like to retry.
496 * This method is used to support re-trying with freshly fetched
497 * credentials in case a server is rebuilt while clients have
498 * non-expired tickets. When the client code gets a logon failure they
499 * throw away the existing credentials for the server and retry.
501 _PUBLIC_
bool cli_credentials_failed_kerberos_login(struct cli_credentials
*cred
,
502 const char *principal
,
505 struct ccache_container
*ccc
;
506 krb5_creds creds
, creds2
;
509 if (principal
== NULL
) {
510 /* no way to delete if we don't know the principal */
516 /* not a kerberos connection */
521 /* We have already tried discarding the credentials */
527 ret
= krb5_parse_name(ccc
->smb_krb5_context
->krb5_context
, principal
, &creds
.server
);
532 /* MIT kerberos requires creds.client to match against cached
534 ret
= krb5_cc_get_principal(ccc
->smb_krb5_context
->krb5_context
,
538 krb5_free_cred_contents(ccc
->smb_krb5_context
->krb5_context
,
540 DBG_ERR("krb5_cc_get_principal failed: %s\n",
541 smb_get_krb5_error_message(
542 ccc
->smb_krb5_context
->krb5_context
,
547 ret
= krb5_cc_retrieve_cred(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
, KRB5_TC_MATCH_SRV_NAMEONLY
, &creds
, &creds2
);
549 /* don't retry - we didn't find these credentials to remove */
550 krb5_free_cred_contents(ccc
->smb_krb5_context
->krb5_context
, &creds
);
554 ret
= krb5_cc_remove_cred(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
, KRB5_TC_MATCH_SRV_NAMEONLY
, &creds
);
555 #ifndef SAMBA4_USES_HEIMDAL
556 if (ret
== KRB5_CC_NOSUPP
) {
557 /* Old MIT kerberos versions did not implement
558 * krb5_cc_remove_cred */
559 ret
= krb5_cc_remove_cred_wrap(ccc
, &creds
);
562 krb5_free_cred_contents(ccc
->smb_krb5_context
->krb5_context
, &creds
);
563 krb5_free_cred_contents(ccc
->smb_krb5_context
->krb5_context
, &creds2
);
565 /* don't retry - we didn't find these credentials to
566 * remove. Note that with the current backend this
567 * never happens, as it always returns 0 even if the
568 * creds don't exist, which is why we do a separate
569 * krb5_cc_retrieve_cred() above.
571 DBG_ERR("krb5_cc_remove_cred failed: %s\n",
572 smb_get_krb5_error_message(
573 ccc
->smb_krb5_context
->krb5_context
,
581 static int cli_credentials_new_ccache(struct cli_credentials
*cred
,
582 struct loadparm_context
*lp_ctx
,
583 char *given_ccache_name
,
584 struct ccache_container
**_ccc
,
585 const char **error_string
)
587 char *ccache_name
= given_ccache_name
;
588 bool must_free_cc_name
= false;
590 struct ccache_container
*ccc
= talloc(cred
, struct ccache_container
);
595 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
596 &ccc
->smb_krb5_context
);
599 (*error_string
) = talloc_asprintf(cred
, "Failed to get krb5_context: %s",
603 if (!talloc_reference(ccc
, ccc
->smb_krb5_context
)) {
605 (*error_string
) = strerror(ENOMEM
);
610 if (lpcfg_parm_bool(lp_ctx
, NULL
, "credentials", "krb5_cc_file", false)) {
611 ccache_name
= talloc_asprintf(ccc
, "FILE:/tmp/krb5_cc_samba_%u_%p",
612 (unsigned int)getpid(), ccc
);
613 if (ccache_name
== NULL
) {
615 (*error_string
) = strerror(ENOMEM
);
618 must_free_cc_name
= true;
622 if (ccache_name
!= NULL
) {
623 ret
= krb5_cc_resolve(ccc
->smb_krb5_context
->krb5_context
, ccache_name
,
626 ret
= smb_krb5_cc_new_unique_memory(ccc
->smb_krb5_context
->krb5_context
,
629 must_free_cc_name
= true;
632 (*error_string
) = talloc_asprintf(cred
, "failed to resolve a krb5 ccache (%s): %s\n",
634 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
,
640 if (strncasecmp(ccache_name
, "MEMORY:", 7) == 0) {
641 talloc_set_destructor(ccc
, free_mccache
);
643 talloc_set_destructor(ccc
, free_dccache
);
646 if (must_free_cc_name
) {
647 talloc_free(ccache_name
);
655 _PUBLIC_
int cli_credentials_get_named_ccache(struct cli_credentials
*cred
,
656 struct tevent_context
*event_ctx
,
657 struct loadparm_context
*lp_ctx
,
659 struct ccache_container
**ccc
,
660 const char **error_string
)
663 enum credentials_obtained obtained
;
665 if (cred
->machine_account_pending
) {
666 cli_credentials_set_machine_account(cred
, lp_ctx
);
669 if (cred
->ccache_obtained
>= cred
->ccache_threshold
&&
670 cred
->ccache_obtained
> CRED_UNINITIALISED
) {
672 bool expired
= false;
673 ret
= smb_krb5_cc_get_lifetime(cred
->ccache
->smb_krb5_context
->krb5_context
,
674 cred
->ccache
->ccache
, &lifetime
);
675 if (ret
== KRB5_CC_END
|| ret
== ENOENT
) {
676 /* If we have a particular ccache set, without
677 * an initial ticket, then assume there is a
679 } else if (ret
== 0) {
681 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
682 cli_credentials_get_principal(cred
, cred
)));
684 } else if (lifetime
< 300) {
685 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
686 cli_credentials_get_principal(cred
, cred
), (unsigned int)lifetime
));
690 (*error_string
) = talloc_asprintf(cred
, "failed to get ccache lifetime: %s\n",
691 smb_get_krb5_error_message(cred
->ccache
->smb_krb5_context
->krb5_context
,
696 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
697 cli_credentials_get_principal(cred
, cred
), (unsigned int)lifetime
));
704 if (cli_credentials_is_anonymous(cred
)) {
705 (*error_string
) = "Cannot get anonymous kerberos credentials";
709 ret
= cli_credentials_new_ccache(cred
, lp_ctx
, ccache_name
, ccc
, error_string
);
714 ret
= kinit_to_ccache(cred
,
716 (*ccc
)->smb_krb5_context
,
726 ret
= cli_credentials_set_from_ccache(cred
, *ccc
,
727 obtained
, error_string
);
730 cred
->ccache_obtained
= cred
->principal_obtained
;
734 cli_credentials_invalidate_client_gss_creds(cred
, cred
->ccache_obtained
);
738 _PUBLIC_
int cli_credentials_get_ccache(struct cli_credentials
*cred
,
739 struct tevent_context
*event_ctx
,
740 struct loadparm_context
*lp_ctx
,
741 struct ccache_container
**ccc
,
742 const char **error_string
)
744 return cli_credentials_get_named_ccache(cred
, event_ctx
, lp_ctx
, NULL
, ccc
, error_string
);
748 * @brief Check if a valid Kerberos credential cache is attached.
750 * This will not ask for a password nor do a kinit.
752 * @param cred The credentials context.
754 * @param mem_ctx A memory context to allocate the ccache_name.
756 * @param ccache_name A pointer to a string to store the ccache name.
758 * @param obtained A pointer to store the information how the ccache was
761 * @return True if a credential cache is attached, false if not or an error
764 _PUBLIC_
bool cli_credentials_get_ccache_name_obtained(
765 struct cli_credentials
*cred
,
768 enum credentials_obtained
*obtained
)
770 if (ccache_name
!= NULL
) {
774 if (obtained
!= NULL
) {
775 *obtained
= CRED_UNINITIALISED
;
778 if (cred
->machine_account_pending
) {
782 if (cred
->ccache_obtained
== CRED_UNINITIALISED
) {
786 if (cred
->ccache_obtained
>= cred
->ccache_threshold
) {
787 krb5_context k5ctx
= cred
->ccache
->smb_krb5_context
->krb5_context
;
788 krb5_ccache k5ccache
= cred
->ccache
->ccache
;
792 ret
= smb_krb5_cc_get_lifetime(k5ctx
, k5ccache
, &lifetime
);
793 if (ret
== KRB5_CC_END
|| ret
== ENOENT
) {
801 } else if (lifetime
< 300) {
802 if (cred
->password_obtained
>= cred
->ccache_obtained
) {
804 * we have a password to re-kinit
805 * so let the caller try that.
811 if (ccache_name
!= NULL
) {
814 ret
= krb5_cc_get_full_name(k5ctx
, k5ccache
, &name
);
819 *ccache_name
= talloc_strdup(mem_ctx
, name
);
821 if (*ccache_name
== NULL
) {
826 if (obtained
!= NULL
) {
827 *obtained
= cred
->ccache_obtained
;
836 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
837 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials
*cred
)
839 if (cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
840 talloc_unlink(cred
, cred
->client_gss_creds
);
841 cred
->client_gss_creds
= NULL
;
843 cred
->client_gss_creds_obtained
= CRED_UNINITIALISED
;
846 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials
*cred
,
847 enum credentials_obtained obtained
)
849 /* If the caller just changed the username/password etc, then
850 * any cached credentials are now invalid */
851 if (obtained
>= cred
->client_gss_creds_obtained
) {
852 if (cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
853 talloc_unlink(cred
, cred
->client_gss_creds
);
854 cred
->client_gss_creds
= NULL
;
856 cred
->client_gss_creds_obtained
= CRED_UNINITIALISED
;
858 /* Now that we know that the data is 'this specified', then
859 * don't allow something less 'known' to be returned as a
860 * ccache. Ie, if the username is on the command line, we
861 * don't want to later guess to use a file-based ccache */
862 if (obtained
> cred
->client_gss_creds_threshold
) {
863 cred
->client_gss_creds_threshold
= obtained
;
867 /* We have good reason to think this CCACHE is invalid. Blow it away */
868 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials
*cred
)
870 if (cred
->ccache_obtained
> CRED_UNINITIALISED
) {
871 talloc_unlink(cred
, cred
->ccache
);
874 cred
->ccache_obtained
= CRED_UNINITIALISED
;
876 cli_credentials_unconditionally_invalidate_client_gss_creds(cred
);
879 _PUBLIC_
void cli_credentials_invalidate_ccache(struct cli_credentials
*cred
,
880 enum credentials_obtained obtained
)
882 /* If the caller just changed the username/password etc, then
883 * any cached credentials are now invalid */
884 if (obtained
>= cred
->ccache_obtained
) {
885 if (cred
->ccache_obtained
> CRED_UNINITIALISED
) {
886 talloc_unlink(cred
, cred
->ccache
);
889 cred
->ccache_obtained
= CRED_UNINITIALISED
;
891 /* Now that we know that the data is 'this specified', then
892 * don't allow something less 'known' to be returned as a
893 * ccache. i.e, if the username is on the command line, we
894 * don't want to later guess to use a file-based ccache */
895 if (obtained
> cred
->ccache_threshold
) {
896 cred
->ccache_threshold
= obtained
;
899 cli_credentials_invalidate_client_gss_creds(cred
,
903 static int free_gssapi_creds(struct gssapi_creds_container
*gcc
)
906 (void)gss_release_cred(&min_stat
, &gcc
->creds
);
910 _PUBLIC_
int cli_credentials_get_client_gss_creds(struct cli_credentials
*cred
,
911 struct tevent_context
*event_ctx
,
912 struct loadparm_context
*lp_ctx
,
913 struct gssapi_creds_container
**_gcc
,
914 const char **error_string
)
917 OM_uint32 maj_stat
, min_stat
;
918 struct gssapi_creds_container
*gcc
;
919 struct ccache_container
*ccache
;
920 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
921 gss_buffer_desc empty_buffer
= GSS_C_EMPTY_BUFFER
;
922 gss_OID oid
= discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X
);
924 krb5_enctype
*etypes
= NULL
;
926 if (cred
->client_gss_creds_obtained
>= cred
->client_gss_creds_threshold
&&
927 cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
928 bool expired
= false;
929 OM_uint32 lifetime
= 0;
930 gss_cred_usage_t usage
= 0;
931 maj_stat
= gss_inquire_cred(&min_stat
, cred
->client_gss_creds
->creds
,
932 NULL
, &lifetime
, &usage
, NULL
);
933 if (maj_stat
== GSS_S_CREDENTIALS_EXPIRED
) {
934 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred
, cred
)));
936 } else if (maj_stat
== GSS_S_COMPLETE
&& lifetime
< 300) {
937 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred
, cred
), lifetime
));
939 } else if (maj_stat
!= GSS_S_COMPLETE
) {
940 *error_string
= talloc_asprintf(cred
, "inquiry of credential lifetime via GSSAPI gss_inquire_cred failed: %s\n",
941 gssapi_error_string(cred
, maj_stat
, min_stat
, NULL
));
945 cli_credentials_unconditionally_invalidate_client_gss_creds(cred
);
947 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
948 cli_credentials_get_principal(cred
, cred
), (unsigned int)lifetime
));
950 *_gcc
= cred
->client_gss_creds
;
955 ret
= cli_credentials_get_ccache(cred
, event_ctx
, lp_ctx
,
956 &ccache
, error_string
);
958 if (cli_credentials_get_kerberos_state(cred
) == CRED_USE_KERBEROS_REQUIRED
) {
959 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string
));
961 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string
));
966 gcc
= talloc(cred
, struct gssapi_creds_container
);
968 (*error_string
) = error_message(ENOMEM
);
972 maj_stat
= smb_gss_krb5_import_cred(&min_stat
, ccache
->smb_krb5_context
->krb5_context
,
973 ccache
->ccache
, NULL
, NULL
,
975 if ((maj_stat
== GSS_S_FAILURE
) &&
976 (min_stat
== (OM_uint32
)KRB5_CC_END
||
977 min_stat
== (OM_uint32
)KRB5_CC_NOTFOUND
||
978 min_stat
== (OM_uint32
)KRB5_FCC_NOFILE
))
980 /* This CCACHE is no good. Ensure we don't use it again */
981 cli_credentials_unconditionally_invalidate_ccache(cred
);
983 /* Now try again to get a ccache */
984 ret
= cli_credentials_get_ccache(cred
, event_ctx
, lp_ctx
,
985 &ccache
, error_string
);
987 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret
)));
991 maj_stat
= smb_gss_krb5_import_cred(&min_stat
, ccache
->smb_krb5_context
->krb5_context
,
992 ccache
->ccache
, NULL
, NULL
,
1004 (*error_string
) = talloc_asprintf(cred
, "smb_gss_krb5_import_cred failed: %s", error_message(ret
));
1010 * transfer the enctypes from the smb_krb5_context to the gssapi layer
1012 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
1013 * to configure the enctypes via the krb5.conf.
1015 * And the gss_init_sec_context() creates it's own krb5_context and
1016 * the TGS-REQ had all enctypes in it and only the ones configured
1017 * and used for the AS-REQ, so it wasn't possible to disable the usage
1020 min_stat
= smb_krb5_get_allowed_etypes(ccache
->smb_krb5_context
->krb5_context
,
1022 if (min_stat
== 0) {
1023 OM_uint32 num_ktypes
;
1025 for (num_ktypes
= 0; etypes
[num_ktypes
]; num_ktypes
++);
1027 maj_stat
= gss_krb5_set_allowable_enctypes(&min_stat
, gcc
->creds
,
1029 (int32_t *) etypes
);
1030 krb5_free_enctypes(ccache
->smb_krb5_context
->krb5_context
,
1039 (*error_string
) = talloc_asprintf(cred
, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret
));
1044 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
1046 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
1048 * This allows us to disable SIGN and SEAL on a TLS connection with
1049 * GSS-SPNENO. For example ldaps:// connections.
1051 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
1052 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
1054 maj_stat
= gss_set_cred_option(&min_stat
, &gcc
->creds
,
1064 (*error_string
) = talloc_asprintf(cred
, "gss_set_cred_option failed: %s", error_message(ret
));
1068 cred
->client_gss_creds_obtained
= cred
->ccache_obtained
;
1069 talloc_set_destructor(gcc
, free_gssapi_creds
);
1070 cred
->client_gss_creds
= gcc
;
1076 Set a gssapi cred_id_t into the credentials system. (Client case)
1078 This grabs the credentials both 'intact' and getting the krb5
1079 ccache out of it. This routine can be generalised in future for
1080 the case where we deal with GSSAPI mechs other than krb5.
1082 On success, the caller must not free gssapi_cred, as it now belongs
1083 to the credentials system.
1086 int cli_credentials_set_client_gss_creds(struct cli_credentials
*cred
,
1087 struct loadparm_context
*lp_ctx
,
1088 gss_cred_id_t gssapi_cred
,
1089 enum credentials_obtained obtained
,
1090 const char **error_string
)
1093 OM_uint32 maj_stat
, min_stat
;
1094 struct ccache_container
*ccc
= NULL
;
1095 struct gssapi_creds_container
*gcc
= NULL
;
1096 if (cred
->client_gss_creds_obtained
> obtained
) {
1100 gcc
= talloc(cred
, struct gssapi_creds_container
);
1102 (*error_string
) = error_message(ENOMEM
);
1106 ret
= cli_credentials_new_ccache(cred
, lp_ctx
, NULL
, &ccc
, error_string
);
1111 maj_stat
= smb_gss_krb5_copy_ccache(&min_stat
,
1121 (*error_string
) = error_message(ENOMEM
);
1126 ret
= cli_credentials_set_from_ccache(cred
, ccc
, obtained
, error_string
);
1129 cred
->ccache_obtained
= obtained
;
1131 gcc
->creds
= gssapi_cred
;
1132 talloc_set_destructor(gcc
, free_gssapi_creds
);
1134 /* set the client_gss_creds_obtained here, as it just
1135 got set to UNINITIALISED by the calls above */
1136 cred
->client_gss_creds_obtained
= obtained
;
1137 cred
->client_gss_creds
= gcc
;
1142 static int cli_credentials_shallow_ccache(struct cli_credentials
*cred
)
1144 krb5_error_code ret
;
1145 const struct ccache_container
*old_ccc
= NULL
;
1146 enum credentials_obtained old_obtained
;
1147 struct ccache_container
*ccc
= NULL
;
1148 krb5_principal princ
;
1150 old_obtained
= cred
->ccache_obtained
;
1151 old_ccc
= cred
->ccache
;
1152 if (old_ccc
== NULL
) {
1156 cred
->ccache
= NULL
;
1157 cred
->ccache_obtained
= CRED_UNINITIALISED
;
1158 cred
->client_gss_creds
= NULL
;
1159 cred
->client_gss_creds_obtained
= CRED_UNINITIALISED
;
1161 ret
= krb5_cc_get_principal(
1162 old_ccc
->smb_krb5_context
->krb5_context
,
1167 * This is an empty ccache. No point in copying anything.
1171 krb5_free_principal(old_ccc
->smb_krb5_context
->krb5_context
, princ
);
1173 ccc
= talloc(cred
, struct ccache_container
);
1180 ret
= smb_krb5_cc_new_unique_memory(ccc
->smb_krb5_context
->krb5_context
,
1189 talloc_set_destructor(ccc
, free_mccache
);
1191 ret
= smb_krb5_cc_copy_creds(ccc
->smb_krb5_context
->krb5_context
,
1192 old_ccc
->ccache
, ccc
->ccache
);
1199 cred
->ccache_obtained
= old_obtained
;
1203 _PUBLIC_
struct cli_credentials
*cli_credentials_shallow_copy(TALLOC_CTX
*mem_ctx
,
1204 struct cli_credentials
*src
)
1206 struct cli_credentials
*dst
, *armor_credentials
;
1209 dst
= talloc(mem_ctx
, struct cli_credentials
);
1216 if (dst
->krb5_fast_armor_credentials
!= NULL
) {
1217 armor_credentials
= talloc_reference(dst
, dst
->krb5_fast_armor_credentials
);
1218 if (armor_credentials
== NULL
) {
1224 ret
= cli_credentials_shallow_ccache(dst
);
1233 /* Get the keytab (actually, a container containing the krb5_keytab)
1234 * attached to this context. If this hasn't been done or set before,
1235 * it will be generated from the password.
1237 _PUBLIC_
int cli_credentials_get_keytab(struct cli_credentials
*cred
,
1238 struct loadparm_context
*lp_ctx
,
1239 struct keytab_container
**_ktc
)
1241 krb5_error_code ret
;
1242 struct keytab_container
*ktc
;
1243 struct smb_krb5_context
*smb_krb5_context
;
1244 const char *keytab_name
;
1246 TALLOC_CTX
*mem_ctx
;
1247 const char *username
= cli_credentials_get_username(cred
);
1248 const char *realm
= cli_credentials_get_realm(cred
);
1249 char *salt_principal
= NULL
;
1251 if (cred
->keytab_obtained
>= (MAX(cred
->principal_obtained
,
1252 cred
->username_obtained
))) {
1253 *_ktc
= cred
->keytab
;
1257 if (cli_credentials_is_anonymous(cred
)) {
1261 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
1267 mem_ctx
= talloc_new(cred
);
1272 salt_principal
= cli_credentials_get_salt_principal(cred
, mem_ctx
);
1273 if (salt_principal
== NULL
) {
1274 talloc_free(mem_ctx
);
1278 ret
= smb_krb5_create_memory_keytab(mem_ctx
,
1279 smb_krb5_context
->krb5_context
,
1280 cli_credentials_get_password(cred
),
1284 cli_credentials_get_kvno(cred
),
1288 talloc_free(mem_ctx
);
1292 ret
= smb_krb5_get_keytab_container(mem_ctx
, smb_krb5_context
,
1293 keytab
, keytab_name
, &ktc
);
1295 talloc_free(mem_ctx
);
1299 cred
->keytab_obtained
= (MAX(cred
->principal_obtained
,
1300 cred
->username_obtained
));
1302 /* We make this keytab up based on a password. Therefore
1303 * match-by-key is acceptable, we can't match on the wrong
1305 ktc
->password_based
= true;
1307 talloc_steal(cred
, ktc
);
1309 *_ktc
= cred
->keytab
;
1310 talloc_free(mem_ctx
);
1314 /* Given the name of a keytab (presumably in the format
1315 * FILE:/etc/krb5.keytab), open it and attach it */
1317 _PUBLIC_
int cli_credentials_set_keytab_name(struct cli_credentials
*cred
,
1318 struct loadparm_context
*lp_ctx
,
1319 const char *keytab_name
,
1320 enum credentials_obtained obtained
)
1322 krb5_error_code ret
;
1323 struct keytab_container
*ktc
;
1324 struct smb_krb5_context
*smb_krb5_context
;
1325 TALLOC_CTX
*mem_ctx
;
1327 if (cred
->keytab_obtained
>= obtained
) {
1331 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
, &smb_krb5_context
);
1336 mem_ctx
= talloc_new(cred
);
1341 ret
= smb_krb5_get_keytab_container(mem_ctx
, smb_krb5_context
,
1342 NULL
, keytab_name
, &ktc
);
1347 cred
->keytab_obtained
= obtained
;
1349 talloc_steal(cred
, ktc
);
1351 talloc_free(mem_ctx
);
1356 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1358 _PUBLIC_
int cli_credentials_get_server_gss_creds(struct cli_credentials
*cred
,
1359 struct loadparm_context
*lp_ctx
,
1360 struct gssapi_creds_container
**_gcc
)
1363 OM_uint32 maj_stat
, min_stat
;
1364 struct gssapi_creds_container
*gcc
;
1365 struct keytab_container
*ktc
;
1366 struct smb_krb5_context
*smb_krb5_context
;
1367 TALLOC_CTX
*mem_ctx
;
1368 krb5_principal princ
;
1369 const char *error_string
;
1370 enum credentials_obtained obtained
;
1372 mem_ctx
= talloc_new(cred
);
1377 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
, &smb_krb5_context
);
1382 ret
= principal_from_credentials(mem_ctx
, cred
, smb_krb5_context
, &princ
, &obtained
, &error_string
);
1384 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1386 talloc_free(mem_ctx
);
1390 if (cred
->server_gss_creds_obtained
>= (MAX(cred
->keytab_obtained
, obtained
))) {
1391 talloc_free(mem_ctx
);
1392 *_gcc
= cred
->server_gss_creds
;
1396 ret
= cli_credentials_get_keytab(cred
, lp_ctx
, &ktc
);
1398 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret
)));
1402 gcc
= talloc(cred
, struct gssapi_creds_container
);
1404 talloc_free(mem_ctx
);
1408 if (ktc
->password_based
|| obtained
< CRED_SPECIFIED
) {
1410 * This creates a GSSAPI cred_id_t for match-by-key with only
1415 maj_stat
= smb_gss_krb5_import_cred(&min_stat
,
1416 smb_krb5_context
->krb5_context
,
1428 cred
->server_gss_creds_obtained
= cred
->keytab_obtained
;
1429 talloc_set_destructor(gcc
, free_gssapi_creds
);
1430 cred
->server_gss_creds
= gcc
;
1433 talloc_free(mem_ctx
);
1441 _PUBLIC_
void cli_credentials_set_kvno(struct cli_credentials
*cred
,
1448 * Return Kerberos KVNO
1451 _PUBLIC_
int cli_credentials_get_kvno(struct cli_credentials
*cred
)
1457 char *cli_credentials_get_salt_principal(struct cli_credentials
*cred
, TALLOC_CTX
*mem_ctx
)
1459 TALLOC_CTX
*frame
= NULL
;
1460 const char *realm
= NULL
;
1461 const char *username
= NULL
;
1462 uint32_t uac_flags
= 0;
1463 char *salt_principal
= NULL
;
1464 const char *upn
= NULL
;
1467 /* If specified, use the specified value */
1468 if (cred
->salt_principal
!= NULL
) {
1469 return talloc_strdup(mem_ctx
, cred
->salt_principal
);
1472 frame
= talloc_stackframe();
1474 switch (cred
->secure_channel_type
) {
1475 case SEC_CHAN_WKSTA
:
1477 uac_flags
= UF_WORKSTATION_TRUST_ACCOUNT
;
1480 uac_flags
= UF_SERVER_TRUST_ACCOUNT
;
1482 case SEC_CHAN_DOMAIN
:
1483 case SEC_CHAN_DNS_DOMAIN
:
1484 uac_flags
= UF_INTERDOMAIN_TRUST_ACCOUNT
;
1487 upn
= cli_credentials_get_principal(cred
, frame
);
1492 uac_flags
= UF_NORMAL_ACCOUNT
;
1496 realm
= cli_credentials_get_realm(cred
);
1497 username
= cli_credentials_get_username(cred
);
1499 ret
= smb_krb5_salt_principal_str(realm
,
1500 username
, /* sAMAccountName */
1501 upn
, /* userPrincipalName */
1511 return salt_principal
;
1514 _PUBLIC_
void cli_credentials_set_salt_principal(struct cli_credentials
*cred
, const char *principal
)
1516 talloc_free(cred
->salt_principal
);
1517 cred
->salt_principal
= talloc_strdup(cred
, principal
);
1520 /* The 'impersonate_principal' is used to allow one Kerberos principal
1521 * (and it's associated keytab etc) to impersonate another. The
1522 * ability to do this is controlled by the KDC, but it is generally
1523 * permitted to impersonate anyone to yourself. This allows any
1524 * member of the domain to get the groups of a user. This is also
1525 * known as S4U2Self */
1527 _PUBLIC_
const char *cli_credentials_get_impersonate_principal(struct cli_credentials
*cred
)
1529 return cred
->impersonate_principal
;
1533 * The 'self_service' is the service principal that
1534 * represents the same object (by its objectSid)
1535 * as the client principal (typically our machine account).
1536 * When trying to impersonate 'impersonate_principal' with
1539 _PUBLIC_
const char *cli_credentials_get_self_service(struct cli_credentials
*cred
)
1541 return cred
->self_service
;
1544 _PUBLIC_
void cli_credentials_set_impersonate_principal(struct cli_credentials
*cred
,
1545 const char *principal
,
1546 const char *self_service
)
1548 talloc_free(cred
->impersonate_principal
);
1549 cred
->impersonate_principal
= talloc_strdup(cred
, principal
);
1550 talloc_free(cred
->self_service
);
1551 cred
->self_service
= talloc_strdup(cred
, self_service
);
1552 cli_credentials_set_kerberos_state(cred
,
1553 CRED_USE_KERBEROS_REQUIRED
,
1558 * when impersonating for S4U2proxy we need to set the target principal.
1559 * Similarly, we may only be authorized to do general impersonation to
1560 * some particular services.
1562 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1564 * NULL means that tickets will be obtained for the krbtgt service.
1567 const char *cli_credentials_get_target_service(struct cli_credentials
*cred
)
1569 return cred
->target_service
;
1572 _PUBLIC_
void cli_credentials_set_target_service(struct cli_credentials
*cred
, const char *target_service
)
1574 talloc_free(cred
->target_service
);
1575 cred
->target_service
= talloc_strdup(cred
, target_service
);
1578 _PUBLIC_
int cli_credentials_get_kerberos_key(struct cli_credentials
*cred
,
1579 TALLOC_CTX
*mem_ctx
,
1580 struct loadparm_context
*lp_ctx
,
1581 krb5_enctype enctype
,
1583 DATA_BLOB
*key_blob
)
1585 struct smb_krb5_context
*smb_krb5_context
= NULL
;
1586 krb5_error_code krb5_ret
;
1588 const char *password
= NULL
;
1589 const char *salt
= NULL
;
1590 krb5_data cleartext_data
;
1591 krb5_data salt_data
= {
1596 TALLOC_CTX
*frame
= talloc_stackframe();
1598 if ((int)enctype
== (int)ENCTYPE_ARCFOUR_HMAC
) {
1599 struct samr_Password
*nt_hash
;
1602 nt_hash
= cli_credentials_get_old_nt_hash(cred
, frame
);
1604 nt_hash
= cli_credentials_get_nt_hash(cred
, frame
);
1607 if (nt_hash
== NULL
) {
1611 *key_blob
= data_blob_talloc(mem_ctx
,
1613 sizeof(nt_hash
->hash
));
1614 if (key_blob
->data
== NULL
) {
1622 if (cred
->password_will_be_nt_hash
) {
1623 DEBUG(1,("cli_credentials_get_kerberos_key: cannot generate Kerberos key using NT hash\n"));
1628 salt
= cli_credentials_get_salt_principal(cred
, frame
);
1635 password
= cli_credentials_get_old_password(cred
);
1637 password
= cli_credentials_get_password(cred
);
1639 if (password
== NULL
) {
1644 cleartext_data
.data
= discard_const_p(char, password
);
1645 cleartext_data
.length
= strlen(password
);
1647 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
1654 salt_data
.data
= discard_const_p(char, salt
);
1655 salt_data
.length
= strlen(salt
);
1658 * create Kerberos key out of
1659 * the salt and the cleartext password
1661 krb5_ret
= smb_krb5_create_key_from_string(smb_krb5_context
->krb5_context
,
1667 if (krb5_ret
!= 0) {
1668 DEBUG(1,("cli_credentials_get_aes256_key: "
1669 "generation of a aes256-cts-hmac-sha1-96 key failed: %s\n",
1670 smb_get_krb5_error_message(smb_krb5_context
->krb5_context
,
1671 krb5_ret
, mem_ctx
)));
1675 *key_blob
= data_blob_talloc(mem_ctx
,
1676 KRB5_KEY_DATA(&key
),
1677 KRB5_KEY_LENGTH(&key
));
1678 krb5_free_keyblock_contents(smb_krb5_context
->krb5_context
, &key
);
1679 if (key_blob
->data
== NULL
) {
1683 talloc_keep_secret(key_blob
->data
);
1689 /* This take a reference to the armor credentials to ensure the lifetime is appropriate */
1691 NTSTATUS
cli_credentials_set_krb5_fast_armor_credentials(struct cli_credentials
*creds
,
1692 struct cli_credentials
*armor_creds
,
1693 bool require_fast_armor
)
1695 talloc_unlink(creds
, creds
->krb5_fast_armor_credentials
);
1696 if (armor_creds
== NULL
) {
1697 creds
->krb5_fast_armor_credentials
= NULL
;
1698 return NT_STATUS_OK
;
1701 creds
->krb5_fast_armor_credentials
= talloc_reference(creds
, armor_creds
);
1702 if (creds
->krb5_fast_armor_credentials
== NULL
) {
1703 return NT_STATUS_NO_MEMORY
;
1706 creds
->krb5_require_fast_armor
= require_fast_armor
;
1708 return NT_STATUS_OK
;
1711 struct cli_credentials
*cli_credentials_get_krb5_fast_armor_credentials(struct cli_credentials
*creds
)
1713 return creds
->krb5_fast_armor_credentials
;
1716 bool cli_credentials_get_krb5_require_fast_armor(struct cli_credentials
*creds
)
1718 return creds
->krb5_require_fast_armor
;