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
,
326 ret
= krb5_cc_default(ccc
->smb_krb5_context
->krb5_context
, &ccc
->ccache
);
328 (*error_string
) = talloc_asprintf(cred
, "failed to read default krb5 ccache: %s\n",
329 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
,
336 talloc_set_destructor(ccc
, free_dccache
);
338 ret
= krb5_cc_get_principal(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
, &princ
);
341 krb5_free_principal(ccc
->smb_krb5_context
->krb5_context
, princ
);
342 ret
= cli_credentials_set_from_ccache(cred
, ccc
, obtained
, error_string
);
345 (*error_string
) = error_message(ret
);
352 cred
->ccache_obtained
= obtained
;
354 cli_credentials_invalidate_client_gss_creds(
355 cred
, cred
->ccache_obtained
);
360 #ifndef SAMBA4_USES_HEIMDAL
362 * This function is a workaround for old MIT Kerberos versions which did not
363 * implement the krb5_cc_remove_cred function. It creates a temporary
364 * credentials cache to copy the credentials in the current cache
365 * except the one we want to remove and then overwrites the contents of the
366 * current cache with the temporary copy.
368 static krb5_error_code
krb5_cc_remove_cred_wrap(struct ccache_container
*ccc
,
371 krb5_ccache dummy_ccache
= NULL
;
372 krb5_creds cached_creds
= {0};
373 krb5_cc_cursor cursor
= NULL
;
374 krb5_error_code code
;
376 code
= smb_krb5_cc_new_unique_memory(ccc
->smb_krb5_context
->krb5_context
,
380 DBG_ERR("krb5_cc_resolve failed: %s\n",
381 smb_get_krb5_error_message(
382 ccc
->smb_krb5_context
->krb5_context
,
387 code
= krb5_cc_start_seq_get(ccc
->smb_krb5_context
->krb5_context
,
391 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
394 DBG_ERR("krb5_cc_start_seq_get failed: %s\n",
395 smb_get_krb5_error_message(
396 ccc
->smb_krb5_context
->krb5_context
,
401 while ((code
= krb5_cc_next_cred(ccc
->smb_krb5_context
->krb5_context
,
404 &cached_creds
)) == 0) {
405 /* If the principal matches skip it and do not copy to the
406 * temporary cache as this is the one we want to remove */
407 if (krb5_principal_compare_flags(
408 ccc
->smb_krb5_context
->krb5_context
,
415 code
= krb5_cc_store_cred(
416 ccc
->smb_krb5_context
->krb5_context
,
420 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
422 DBG_ERR("krb5_cc_store_cred failed: %s\n",
423 smb_get_krb5_error_message(
424 ccc
->smb_krb5_context
->krb5_context
,
430 if (code
== KRB5_CC_END
) {
431 krb5_cc_end_seq_get(ccc
->smb_krb5_context
->krb5_context
,
438 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
440 DBG_ERR("krb5_cc_next_cred failed: %s\n",
441 smb_get_krb5_error_message(
442 ccc
->smb_krb5_context
->krb5_context
,
447 code
= krb5_cc_initialize(ccc
->smb_krb5_context
->krb5_context
,
451 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
453 DBG_ERR("krb5_cc_initialize failed: %s\n",
454 smb_get_krb5_error_message(
455 ccc
->smb_krb5_context
->krb5_context
,
460 code
= krb5_cc_copy_creds(ccc
->smb_krb5_context
->krb5_context
,
464 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
466 DBG_ERR("krb5_cc_copy_creds failed: %s\n",
467 smb_get_krb5_error_message(
468 ccc
->smb_krb5_context
->krb5_context
,
473 code
= krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
476 DBG_ERR("krb5_cc_destroy failed: %s\n",
477 smb_get_krb5_error_message(
478 ccc
->smb_krb5_context
->krb5_context
,
488 * Indicate that we failed to log in to this service/host with these
489 * credentials. The caller passes an unsigned int which they
490 * initialise to the number of times they would like to retry.
492 * This method is used to support re-trying with freshly fetched
493 * credentials in case a server is rebuilt while clients have
494 * non-expired tickets. When the client code gets a logon failure they
495 * throw away the existing credentials for the server and retry.
497 _PUBLIC_
bool cli_credentials_failed_kerberos_login(struct cli_credentials
*cred
,
498 const char *principal
,
501 struct ccache_container
*ccc
;
502 krb5_creds creds
, creds2
;
505 if (principal
== NULL
) {
506 /* no way to delete if we don't know the principal */
512 /* not a kerberos connection */
517 /* We have already tried discarding the credentials */
523 ret
= krb5_parse_name(ccc
->smb_krb5_context
->krb5_context
, principal
, &creds
.server
);
528 /* MIT kerberos requires creds.client to match against cached
530 ret
= krb5_cc_get_principal(ccc
->smb_krb5_context
->krb5_context
,
534 krb5_free_cred_contents(ccc
->smb_krb5_context
->krb5_context
,
536 DBG_ERR("krb5_cc_get_principal failed: %s\n",
537 smb_get_krb5_error_message(
538 ccc
->smb_krb5_context
->krb5_context
,
543 ret
= krb5_cc_retrieve_cred(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
, KRB5_TC_MATCH_SRV_NAMEONLY
, &creds
, &creds2
);
545 /* don't retry - we didn't find these credentials to remove */
546 krb5_free_cred_contents(ccc
->smb_krb5_context
->krb5_context
, &creds
);
550 ret
= krb5_cc_remove_cred(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
, KRB5_TC_MATCH_SRV_NAMEONLY
, &creds
);
551 #ifndef SAMBA4_USES_HEIMDAL
552 if (ret
== KRB5_CC_NOSUPP
) {
553 /* Old MIT kerberos versions did not implement
554 * krb5_cc_remove_cred */
555 ret
= krb5_cc_remove_cred_wrap(ccc
, &creds
);
558 krb5_free_cred_contents(ccc
->smb_krb5_context
->krb5_context
, &creds
);
559 krb5_free_cred_contents(ccc
->smb_krb5_context
->krb5_context
, &creds2
);
561 /* don't retry - we didn't find these credentials to
562 * remove. Note that with the current backend this
563 * never happens, as it always returns 0 even if the
564 * creds don't exist, which is why we do a separate
565 * krb5_cc_retrieve_cred() above.
567 DBG_ERR("krb5_cc_remove_cred failed: %s\n",
568 smb_get_krb5_error_message(
569 ccc
->smb_krb5_context
->krb5_context
,
577 static int cli_credentials_new_ccache(struct cli_credentials
*cred
,
578 struct loadparm_context
*lp_ctx
,
579 char *given_ccache_name
,
580 struct ccache_container
**_ccc
,
581 const char **error_string
)
583 char *ccache_name
= given_ccache_name
;
584 bool must_free_cc_name
= false;
586 struct ccache_container
*ccc
= talloc(cred
, struct ccache_container
);
591 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
592 &ccc
->smb_krb5_context
);
595 (*error_string
) = talloc_asprintf(cred
, "Failed to get krb5_context: %s",
599 if (!talloc_reference(ccc
, ccc
->smb_krb5_context
)) {
601 (*error_string
) = strerror(ENOMEM
);
606 if (lpcfg_parm_bool(lp_ctx
, NULL
, "credentials", "krb5_cc_file", false)) {
607 ccache_name
= talloc_asprintf(ccc
, "FILE:/tmp/krb5_cc_samba_%u_%p",
608 (unsigned int)getpid(), ccc
);
609 if (ccache_name
== NULL
) {
611 (*error_string
) = strerror(ENOMEM
);
614 must_free_cc_name
= true;
618 if (ccache_name
!= NULL
) {
619 ret
= krb5_cc_resolve(ccc
->smb_krb5_context
->krb5_context
, ccache_name
,
622 ret
= smb_krb5_cc_new_unique_memory(ccc
->smb_krb5_context
->krb5_context
,
625 must_free_cc_name
= true;
628 (*error_string
) = talloc_asprintf(cred
, "failed to resolve a krb5 ccache (%s): %s\n",
630 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
,
636 if (strncasecmp(ccache_name
, "MEMORY:", 7) == 0) {
637 talloc_set_destructor(ccc
, free_mccache
);
639 talloc_set_destructor(ccc
, free_dccache
);
642 if (must_free_cc_name
) {
643 talloc_free(ccache_name
);
651 _PUBLIC_
int cli_credentials_get_named_ccache(struct cli_credentials
*cred
,
652 struct tevent_context
*event_ctx
,
653 struct loadparm_context
*lp_ctx
,
655 struct ccache_container
**ccc
,
656 const char **error_string
)
659 enum credentials_obtained obtained
;
661 if (cred
->machine_account_pending
) {
662 cli_credentials_set_machine_account(cred
, lp_ctx
);
665 if (cred
->ccache_obtained
>= cred
->ccache_threshold
&&
666 cred
->ccache_obtained
> CRED_UNINITIALISED
) {
668 bool expired
= false;
669 ret
= smb_krb5_cc_get_lifetime(cred
->ccache
->smb_krb5_context
->krb5_context
,
670 cred
->ccache
->ccache
, &lifetime
);
671 if (ret
== KRB5_CC_END
|| ret
== ENOENT
) {
672 /* If we have a particular ccache set, without
673 * an initial ticket, then assume there is a
675 } else if (ret
== 0) {
677 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
678 cli_credentials_get_principal(cred
, cred
)));
680 } else if (lifetime
< 300) {
681 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
682 cli_credentials_get_principal(cred
, cred
), (unsigned int)lifetime
));
686 (*error_string
) = talloc_asprintf(cred
, "failed to get ccache lifetime: %s\n",
687 smb_get_krb5_error_message(cred
->ccache
->smb_krb5_context
->krb5_context
,
692 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
693 cli_credentials_get_principal(cred
, cred
), (unsigned int)lifetime
));
700 if (cli_credentials_is_anonymous(cred
)) {
701 (*error_string
) = "Cannot get anonymous kerberos credentials";
705 ret
= cli_credentials_new_ccache(cred
, lp_ctx
, ccache_name
, ccc
, error_string
);
710 ret
= kinit_to_ccache(cred
,
712 (*ccc
)->smb_krb5_context
,
722 ret
= cli_credentials_set_from_ccache(cred
, *ccc
,
723 obtained
, error_string
);
726 cred
->ccache_obtained
= cred
->principal_obtained
;
730 cli_credentials_invalidate_client_gss_creds(cred
, cred
->ccache_obtained
);
734 _PUBLIC_
int cli_credentials_get_ccache(struct cli_credentials
*cred
,
735 struct tevent_context
*event_ctx
,
736 struct loadparm_context
*lp_ctx
,
737 struct ccache_container
**ccc
,
738 const char **error_string
)
740 return cli_credentials_get_named_ccache(cred
, event_ctx
, lp_ctx
, NULL
, ccc
, error_string
);
744 * @brief Check if a valid Kerberos credential cache is attached.
746 * This will not ask for a password nor do a kinit.
748 * @param cred The credentials context.
750 * @param mem_ctx A memory context to allocate the ccache_name.
752 * @param ccache_name A pointer to a string to store the ccache name.
754 * @param obtained A pointer to store the information how the ccache was
757 * @return True if a credential cache is attached, false if not or an error
760 _PUBLIC_
bool cli_credentials_get_ccache_name_obtained(
761 struct cli_credentials
*cred
,
764 enum credentials_obtained
*obtained
)
766 if (ccache_name
!= NULL
) {
770 if (obtained
!= NULL
) {
771 *obtained
= CRED_UNINITIALISED
;
774 if (cred
->machine_account_pending
) {
778 if (cred
->ccache_obtained
== CRED_UNINITIALISED
) {
782 if (cred
->ccache_obtained
>= cred
->ccache_threshold
) {
783 krb5_context k5ctx
= cred
->ccache
->smb_krb5_context
->krb5_context
;
784 krb5_ccache k5ccache
= cred
->ccache
->ccache
;
788 ret
= smb_krb5_cc_get_lifetime(k5ctx
, k5ccache
, &lifetime
);
789 if (ret
== KRB5_CC_END
|| ret
== ENOENT
) {
797 } else if (lifetime
< 300) {
798 if (cred
->password_obtained
>= cred
->ccache_obtained
) {
800 * we have a password to re-kinit
801 * so let the caller try that.
807 if (ccache_name
!= NULL
) {
810 ret
= krb5_cc_get_full_name(k5ctx
, k5ccache
, &name
);
815 *ccache_name
= talloc_strdup(mem_ctx
, name
);
817 if (*ccache_name
== NULL
) {
822 if (obtained
!= NULL
) {
823 *obtained
= cred
->ccache_obtained
;
832 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
833 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials
*cred
)
835 if (cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
836 talloc_unlink(cred
, cred
->client_gss_creds
);
837 cred
->client_gss_creds
= NULL
;
839 cred
->client_gss_creds_obtained
= CRED_UNINITIALISED
;
842 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials
*cred
,
843 enum credentials_obtained obtained
)
845 /* If the caller just changed the username/password etc, then
846 * any cached credentials are now invalid */
847 if (obtained
>= cred
->client_gss_creds_obtained
) {
848 if (cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
849 talloc_unlink(cred
, cred
->client_gss_creds
);
850 cred
->client_gss_creds
= NULL
;
852 cred
->client_gss_creds_obtained
= CRED_UNINITIALISED
;
854 /* Now that we know that the data is 'this specified', then
855 * don't allow something less 'known' to be returned as a
856 * ccache. Ie, if the username is on the command line, we
857 * don't want to later guess to use a file-based ccache */
858 if (obtained
> cred
->client_gss_creds_threshold
) {
859 cred
->client_gss_creds_threshold
= obtained
;
863 /* We have good reason to think this CCACHE is invalid. Blow it away */
864 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials
*cred
)
866 if (cred
->ccache_obtained
> CRED_UNINITIALISED
) {
867 talloc_unlink(cred
, cred
->ccache
);
870 cred
->ccache_obtained
= CRED_UNINITIALISED
;
872 cli_credentials_unconditionally_invalidate_client_gss_creds(cred
);
875 _PUBLIC_
void cli_credentials_invalidate_ccache(struct cli_credentials
*cred
,
876 enum credentials_obtained obtained
)
878 /* If the caller just changed the username/password etc, then
879 * any cached credentials are now invalid */
880 if (obtained
>= cred
->ccache_obtained
) {
881 if (cred
->ccache_obtained
> CRED_UNINITIALISED
) {
882 talloc_unlink(cred
, cred
->ccache
);
885 cred
->ccache_obtained
= CRED_UNINITIALISED
;
887 /* Now that we know that the data is 'this specified', then
888 * don't allow something less 'known' to be returned as a
889 * ccache. i.e, if the username is on the command line, we
890 * don't want to later guess to use a file-based ccache */
891 if (obtained
> cred
->ccache_threshold
) {
892 cred
->ccache_threshold
= obtained
;
895 cli_credentials_invalidate_client_gss_creds(cred
,
899 static int free_gssapi_creds(struct gssapi_creds_container
*gcc
)
902 (void)gss_release_cred(&min_stat
, &gcc
->creds
);
906 _PUBLIC_
int cli_credentials_get_client_gss_creds(struct cli_credentials
*cred
,
907 struct tevent_context
*event_ctx
,
908 struct loadparm_context
*lp_ctx
,
909 struct gssapi_creds_container
**_gcc
,
910 const char **error_string
)
913 OM_uint32 maj_stat
, min_stat
;
914 struct gssapi_creds_container
*gcc
;
915 struct ccache_container
*ccache
;
916 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
917 gss_buffer_desc empty_buffer
= GSS_C_EMPTY_BUFFER
;
918 gss_OID oid
= discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X
);
920 krb5_enctype
*etypes
= NULL
;
922 if (cred
->client_gss_creds_obtained
>= cred
->client_gss_creds_threshold
&&
923 cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
924 bool expired
= false;
925 OM_uint32 lifetime
= 0;
926 gss_cred_usage_t usage
= 0;
927 maj_stat
= gss_inquire_cred(&min_stat
, cred
->client_gss_creds
->creds
,
928 NULL
, &lifetime
, &usage
, NULL
);
929 if (maj_stat
== GSS_S_CREDENTIALS_EXPIRED
) {
930 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred
, cred
)));
932 } else if (maj_stat
== GSS_S_COMPLETE
&& lifetime
< 300) {
933 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred
, cred
), lifetime
));
935 } else if (maj_stat
!= GSS_S_COMPLETE
) {
936 *error_string
= talloc_asprintf(cred
, "inquiry of credential lifetime via GSSAPI gss_inquire_cred failed: %s\n",
937 gssapi_error_string(cred
, maj_stat
, min_stat
, NULL
));
941 cli_credentials_unconditionally_invalidate_client_gss_creds(cred
);
943 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
944 cli_credentials_get_principal(cred
, cred
), (unsigned int)lifetime
));
946 *_gcc
= cred
->client_gss_creds
;
951 ret
= cli_credentials_get_ccache(cred
, event_ctx
, lp_ctx
,
952 &ccache
, error_string
);
954 if (cli_credentials_get_kerberos_state(cred
) == CRED_USE_KERBEROS_REQUIRED
) {
955 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string
));
957 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string
));
962 gcc
= talloc(cred
, struct gssapi_creds_container
);
964 (*error_string
) = error_message(ENOMEM
);
968 maj_stat
= smb_gss_krb5_import_cred(&min_stat
, ccache
->smb_krb5_context
->krb5_context
,
969 ccache
->ccache
, NULL
, NULL
,
971 if ((maj_stat
== GSS_S_FAILURE
) &&
972 (min_stat
== (OM_uint32
)KRB5_CC_END
||
973 min_stat
== (OM_uint32
)KRB5_CC_NOTFOUND
||
974 min_stat
== (OM_uint32
)KRB5_FCC_NOFILE
))
976 /* This CCACHE is no good. Ensure we don't use it again */
977 cli_credentials_unconditionally_invalidate_ccache(cred
);
979 /* Now try again to get a ccache */
980 ret
= cli_credentials_get_ccache(cred
, event_ctx
, lp_ctx
,
981 &ccache
, error_string
);
983 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret
)));
987 maj_stat
= smb_gss_krb5_import_cred(&min_stat
, ccache
->smb_krb5_context
->krb5_context
,
988 ccache
->ccache
, NULL
, NULL
,
1000 (*error_string
) = talloc_asprintf(cred
, "smb_gss_krb5_import_cred failed: %s", error_message(ret
));
1006 * transfer the enctypes from the smb_krb5_context to the gssapi layer
1008 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
1009 * to configure the enctypes via the krb5.conf.
1011 * And the gss_init_sec_context() creates it's own krb5_context and
1012 * the TGS-REQ had all enctypes in it and only the ones configured
1013 * and used for the AS-REQ, so it wasn't possible to disable the usage
1016 min_stat
= smb_krb5_get_allowed_etypes(ccache
->smb_krb5_context
->krb5_context
,
1018 if (min_stat
== 0) {
1019 OM_uint32 num_ktypes
;
1021 for (num_ktypes
= 0; etypes
[num_ktypes
]; num_ktypes
++);
1023 maj_stat
= gss_krb5_set_allowable_enctypes(&min_stat
, gcc
->creds
,
1025 (int32_t *) etypes
);
1026 krb5_free_enctypes(ccache
->smb_krb5_context
->krb5_context
,
1035 (*error_string
) = talloc_asprintf(cred
, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret
));
1040 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
1042 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
1044 * This allows us to disable SIGN and SEAL on a TLS connection with
1045 * GSS-SPNENO. For example ldaps:// connections.
1047 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
1048 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
1050 maj_stat
= gss_set_cred_option(&min_stat
, &gcc
->creds
,
1060 (*error_string
) = talloc_asprintf(cred
, "gss_set_cred_option failed: %s", error_message(ret
));
1064 cred
->client_gss_creds_obtained
= cred
->ccache_obtained
;
1065 talloc_set_destructor(gcc
, free_gssapi_creds
);
1066 cred
->client_gss_creds
= gcc
;
1072 Set a gssapi cred_id_t into the credentials system. (Client case)
1074 This grabs the credentials both 'intact' and getting the krb5
1075 ccache out of it. This routine can be generalised in future for
1076 the case where we deal with GSSAPI mechs other than krb5.
1078 On success, the caller must not free gssapi_cred, as it now belongs
1079 to the credentials system.
1082 int cli_credentials_set_client_gss_creds(struct cli_credentials
*cred
,
1083 struct loadparm_context
*lp_ctx
,
1084 gss_cred_id_t gssapi_cred
,
1085 enum credentials_obtained obtained
,
1086 const char **error_string
)
1089 OM_uint32 maj_stat
, min_stat
;
1090 struct ccache_container
*ccc
= NULL
;
1091 struct gssapi_creds_container
*gcc
= NULL
;
1092 if (cred
->client_gss_creds_obtained
> obtained
) {
1096 gcc
= talloc(cred
, struct gssapi_creds_container
);
1098 (*error_string
) = error_message(ENOMEM
);
1102 ret
= cli_credentials_new_ccache(cred
, lp_ctx
, NULL
, &ccc
, error_string
);
1107 maj_stat
= smb_gss_krb5_copy_ccache(&min_stat
,
1117 (*error_string
) = error_message(ENOMEM
);
1122 ret
= cli_credentials_set_from_ccache(cred
, ccc
, obtained
, error_string
);
1125 cred
->ccache_obtained
= obtained
;
1127 gcc
->creds
= gssapi_cred
;
1128 talloc_set_destructor(gcc
, free_gssapi_creds
);
1130 /* set the client_gss_creds_obtained here, as it just
1131 got set to UNINITIALISED by the calls above */
1132 cred
->client_gss_creds_obtained
= obtained
;
1133 cred
->client_gss_creds
= gcc
;
1138 static int cli_credentials_shallow_ccache(struct cli_credentials
*cred
)
1140 krb5_error_code ret
;
1141 const struct ccache_container
*old_ccc
= NULL
;
1142 enum credentials_obtained old_obtained
;
1143 struct ccache_container
*ccc
= NULL
;
1144 krb5_principal princ
;
1146 old_obtained
= cred
->ccache_obtained
;
1147 old_ccc
= cred
->ccache
;
1148 if (old_ccc
== NULL
) {
1152 cred
->ccache
= NULL
;
1153 cred
->ccache_obtained
= CRED_UNINITIALISED
;
1154 cred
->client_gss_creds
= NULL
;
1155 cred
->client_gss_creds_obtained
= CRED_UNINITIALISED
;
1157 ret
= krb5_cc_get_principal(
1158 old_ccc
->smb_krb5_context
->krb5_context
,
1163 * This is an empty ccache. No point in copying anything.
1167 krb5_free_principal(old_ccc
->smb_krb5_context
->krb5_context
, princ
);
1169 ccc
= talloc(cred
, struct ccache_container
);
1176 ret
= smb_krb5_cc_new_unique_memory(ccc
->smb_krb5_context
->krb5_context
,
1185 talloc_set_destructor(ccc
, free_mccache
);
1187 ret
= smb_krb5_cc_copy_creds(ccc
->smb_krb5_context
->krb5_context
,
1188 old_ccc
->ccache
, ccc
->ccache
);
1195 cred
->ccache_obtained
= old_obtained
;
1199 _PUBLIC_
struct cli_credentials
*cli_credentials_shallow_copy(TALLOC_CTX
*mem_ctx
,
1200 struct cli_credentials
*src
)
1202 struct cli_credentials
*dst
, *armor_credentials
;
1205 dst
= talloc(mem_ctx
, struct cli_credentials
);
1212 if (dst
->krb5_fast_armor_credentials
!= NULL
) {
1213 armor_credentials
= talloc_reference(dst
, dst
->krb5_fast_armor_credentials
);
1214 if (armor_credentials
== NULL
) {
1220 ret
= cli_credentials_shallow_ccache(dst
);
1229 /* Get the keytab (actually, a container containing the krb5_keytab)
1230 * attached to this context. If this hasn't been done or set before,
1231 * it will be generated from the password.
1233 _PUBLIC_
int cli_credentials_get_keytab(struct cli_credentials
*cred
,
1234 struct loadparm_context
*lp_ctx
,
1235 struct keytab_container
**_ktc
)
1237 krb5_error_code ret
;
1238 struct keytab_container
*ktc
;
1239 struct smb_krb5_context
*smb_krb5_context
;
1240 const char *keytab_name
;
1242 TALLOC_CTX
*mem_ctx
;
1243 const char *username
= cli_credentials_get_username(cred
);
1244 const char *realm
= cli_credentials_get_realm(cred
);
1245 char *salt_principal
= NULL
;
1247 if (cred
->keytab_obtained
>= (MAX(cred
->principal_obtained
,
1248 cred
->username_obtained
))) {
1249 *_ktc
= cred
->keytab
;
1253 if (cli_credentials_is_anonymous(cred
)) {
1257 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
1263 mem_ctx
= talloc_new(cred
);
1268 salt_principal
= cli_credentials_get_salt_principal(cred
, mem_ctx
);
1269 if (salt_principal
== NULL
) {
1270 talloc_free(mem_ctx
);
1274 ret
= smb_krb5_create_memory_keytab(mem_ctx
,
1275 smb_krb5_context
->krb5_context
,
1276 cli_credentials_get_password(cred
),
1280 cli_credentials_get_kvno(cred
),
1284 talloc_free(mem_ctx
);
1288 ret
= smb_krb5_get_keytab_container(mem_ctx
, smb_krb5_context
,
1289 keytab
, keytab_name
, &ktc
);
1291 talloc_free(mem_ctx
);
1295 cred
->keytab_obtained
= (MAX(cred
->principal_obtained
,
1296 cred
->username_obtained
));
1298 /* We make this keytab up based on a password. Therefore
1299 * match-by-key is acceptable, we can't match on the wrong
1301 ktc
->password_based
= true;
1303 talloc_steal(cred
, ktc
);
1305 *_ktc
= cred
->keytab
;
1306 talloc_free(mem_ctx
);
1310 /* Given the name of a keytab (presumably in the format
1311 * FILE:/etc/krb5.keytab), open it and attach it */
1313 _PUBLIC_
int cli_credentials_set_keytab_name(struct cli_credentials
*cred
,
1314 struct loadparm_context
*lp_ctx
,
1315 const char *keytab_name
,
1316 enum credentials_obtained obtained
)
1318 krb5_error_code ret
;
1319 struct keytab_container
*ktc
;
1320 struct smb_krb5_context
*smb_krb5_context
;
1321 TALLOC_CTX
*mem_ctx
;
1323 if (cred
->keytab_obtained
>= obtained
) {
1327 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
, &smb_krb5_context
);
1332 mem_ctx
= talloc_new(cred
);
1337 ret
= smb_krb5_get_keytab_container(mem_ctx
, smb_krb5_context
,
1338 NULL
, keytab_name
, &ktc
);
1343 cred
->keytab_obtained
= obtained
;
1345 talloc_steal(cred
, ktc
);
1347 talloc_free(mem_ctx
);
1352 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1354 _PUBLIC_
int cli_credentials_get_server_gss_creds(struct cli_credentials
*cred
,
1355 struct loadparm_context
*lp_ctx
,
1356 struct gssapi_creds_container
**_gcc
)
1359 OM_uint32 maj_stat
, min_stat
;
1360 struct gssapi_creds_container
*gcc
;
1361 struct keytab_container
*ktc
;
1362 struct smb_krb5_context
*smb_krb5_context
;
1363 TALLOC_CTX
*mem_ctx
;
1364 krb5_principal princ
;
1365 const char *error_string
;
1366 enum credentials_obtained obtained
;
1368 mem_ctx
= talloc_new(cred
);
1373 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
, &smb_krb5_context
);
1378 ret
= principal_from_credentials(mem_ctx
, cred
, smb_krb5_context
, &princ
, &obtained
, &error_string
);
1380 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1382 talloc_free(mem_ctx
);
1386 if (cred
->server_gss_creds_obtained
>= (MAX(cred
->keytab_obtained
, obtained
))) {
1387 talloc_free(mem_ctx
);
1388 *_gcc
= cred
->server_gss_creds
;
1392 ret
= cli_credentials_get_keytab(cred
, lp_ctx
, &ktc
);
1394 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret
)));
1398 gcc
= talloc(cred
, struct gssapi_creds_container
);
1400 talloc_free(mem_ctx
);
1404 if (ktc
->password_based
|| obtained
< CRED_SPECIFIED
) {
1406 * This creates a GSSAPI cred_id_t for match-by-key with only
1411 maj_stat
= smb_gss_krb5_import_cred(&min_stat
,
1412 smb_krb5_context
->krb5_context
,
1424 cred
->server_gss_creds_obtained
= cred
->keytab_obtained
;
1425 talloc_set_destructor(gcc
, free_gssapi_creds
);
1426 cred
->server_gss_creds
= gcc
;
1429 talloc_free(mem_ctx
);
1437 _PUBLIC_
void cli_credentials_set_kvno(struct cli_credentials
*cred
,
1444 * Return Kerberos KVNO
1447 _PUBLIC_
int cli_credentials_get_kvno(struct cli_credentials
*cred
)
1453 char *cli_credentials_get_salt_principal(struct cli_credentials
*cred
, TALLOC_CTX
*mem_ctx
)
1455 TALLOC_CTX
*frame
= NULL
;
1456 const char *realm
= NULL
;
1457 const char *username
= NULL
;
1458 uint32_t uac_flags
= 0;
1459 char *salt_principal
= NULL
;
1460 const char *upn
= NULL
;
1463 /* If specified, use the specified value */
1464 if (cred
->salt_principal
!= NULL
) {
1465 return talloc_strdup(mem_ctx
, cred
->salt_principal
);
1468 frame
= talloc_stackframe();
1470 switch (cred
->secure_channel_type
) {
1471 case SEC_CHAN_WKSTA
:
1473 uac_flags
= UF_WORKSTATION_TRUST_ACCOUNT
;
1476 uac_flags
= UF_SERVER_TRUST_ACCOUNT
;
1478 case SEC_CHAN_DOMAIN
:
1479 case SEC_CHAN_DNS_DOMAIN
:
1480 uac_flags
= UF_INTERDOMAIN_TRUST_ACCOUNT
;
1483 upn
= cli_credentials_get_principal(cred
, frame
);
1488 uac_flags
= UF_NORMAL_ACCOUNT
;
1492 realm
= cli_credentials_get_realm(cred
);
1493 username
= cli_credentials_get_username(cred
);
1495 ret
= smb_krb5_salt_principal_str(realm
,
1496 username
, /* sAMAccountName */
1497 upn
, /* userPrincipalName */
1507 return salt_principal
;
1510 _PUBLIC_
void cli_credentials_set_salt_principal(struct cli_credentials
*cred
, const char *principal
)
1512 talloc_free(cred
->salt_principal
);
1513 cred
->salt_principal
= talloc_strdup(cred
, principal
);
1516 /* The 'impersonate_principal' is used to allow one Kerberos principal
1517 * (and it's associated keytab etc) to impersonate another. The
1518 * ability to do this is controlled by the KDC, but it is generally
1519 * permitted to impersonate anyone to yourself. This allows any
1520 * member of the domain to get the groups of a user. This is also
1521 * known as S4U2Self */
1523 _PUBLIC_
const char *cli_credentials_get_impersonate_principal(struct cli_credentials
*cred
)
1525 return cred
->impersonate_principal
;
1529 * The 'self_service' is the service principal that
1530 * represents the same object (by its objectSid)
1531 * as the client principal (typically our machine account).
1532 * When trying to impersonate 'impersonate_principal' with
1535 _PUBLIC_
const char *cli_credentials_get_self_service(struct cli_credentials
*cred
)
1537 return cred
->self_service
;
1540 _PUBLIC_
void cli_credentials_set_impersonate_principal(struct cli_credentials
*cred
,
1541 const char *principal
,
1542 const char *self_service
)
1544 talloc_free(cred
->impersonate_principal
);
1545 cred
->impersonate_principal
= talloc_strdup(cred
, principal
);
1546 talloc_free(cred
->self_service
);
1547 cred
->self_service
= talloc_strdup(cred
, self_service
);
1548 cli_credentials_set_kerberos_state(cred
,
1549 CRED_USE_KERBEROS_REQUIRED
,
1554 * when impersonating for S4U2proxy we need to set the target principal.
1555 * Similarly, we may only be authorized to do general impersonation to
1556 * some particular services.
1558 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1560 * NULL means that tickets will be obtained for the krbtgt service.
1563 const char *cli_credentials_get_target_service(struct cli_credentials
*cred
)
1565 return cred
->target_service
;
1568 _PUBLIC_
void cli_credentials_set_target_service(struct cli_credentials
*cred
, const char *target_service
)
1570 talloc_free(cred
->target_service
);
1571 cred
->target_service
= talloc_strdup(cred
, target_service
);
1574 _PUBLIC_
int cli_credentials_get_kerberos_key(struct cli_credentials
*cred
,
1575 TALLOC_CTX
*mem_ctx
,
1576 struct loadparm_context
*lp_ctx
,
1577 krb5_enctype enctype
,
1579 DATA_BLOB
*key_blob
)
1581 struct smb_krb5_context
*smb_krb5_context
= NULL
;
1582 krb5_error_code krb5_ret
;
1584 const char *password
= NULL
;
1585 const char *salt
= NULL
;
1586 krb5_data cleartext_data
;
1587 krb5_data salt_data
= {
1592 TALLOC_CTX
*frame
= talloc_stackframe();
1594 if ((int)enctype
== (int)ENCTYPE_ARCFOUR_HMAC
) {
1595 struct samr_Password
*nt_hash
;
1598 nt_hash
= cli_credentials_get_old_nt_hash(cred
, frame
);
1600 nt_hash
= cli_credentials_get_nt_hash(cred
, frame
);
1603 if (nt_hash
== NULL
) {
1607 *key_blob
= data_blob_talloc(mem_ctx
,
1609 sizeof(nt_hash
->hash
));
1610 if (key_blob
->data
== NULL
) {
1618 if (cred
->password_will_be_nt_hash
) {
1619 DEBUG(1,("cli_credentials_get_kerberos_key: cannot generate Kerberos key using NT hash\n"));
1624 salt
= cli_credentials_get_salt_principal(cred
, frame
);
1631 password
= cli_credentials_get_old_password(cred
);
1633 password
= cli_credentials_get_password(cred
);
1635 if (password
== NULL
) {
1640 cleartext_data
.data
= discard_const_p(char, password
);
1641 cleartext_data
.length
= strlen(password
);
1643 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
1650 salt_data
.data
= discard_const_p(char, salt
);
1651 salt_data
.length
= strlen(salt
);
1654 * create Kerberos key out of
1655 * the salt and the cleartext password
1657 krb5_ret
= smb_krb5_create_key_from_string(smb_krb5_context
->krb5_context
,
1663 if (krb5_ret
!= 0) {
1664 DEBUG(1,("cli_credentials_get_aes256_key: "
1665 "generation of a aes256-cts-hmac-sha1-96 key failed: %s\n",
1666 smb_get_krb5_error_message(smb_krb5_context
->krb5_context
,
1667 krb5_ret
, mem_ctx
)));
1671 *key_blob
= data_blob_talloc(mem_ctx
,
1672 KRB5_KEY_DATA(&key
),
1673 KRB5_KEY_LENGTH(&key
));
1674 krb5_free_keyblock_contents(smb_krb5_context
->krb5_context
, &key
);
1675 if (key_blob
->data
== NULL
) {
1679 talloc_keep_secret(key_blob
->data
);
1685 /* This take a reference to the armor credentials to ensure the lifetime is appropriate */
1687 NTSTATUS
cli_credentials_set_krb5_fast_armor_credentials(struct cli_credentials
*creds
,
1688 struct cli_credentials
*armor_creds
,
1689 bool require_fast_armor
)
1691 talloc_unlink(creds
, creds
->krb5_fast_armor_credentials
);
1692 if (armor_creds
== NULL
) {
1693 creds
->krb5_fast_armor_credentials
= NULL
;
1694 return NT_STATUS_OK
;
1697 creds
->krb5_fast_armor_credentials
= talloc_reference(creds
, armor_creds
);
1698 if (creds
->krb5_fast_armor_credentials
== NULL
) {
1699 return NT_STATUS_NO_MEMORY
;
1702 creds
->krb5_require_fast_armor
= require_fast_armor
;
1704 return NT_STATUS_OK
;
1707 struct cli_credentials
*cli_credentials_get_krb5_fast_armor_credentials(struct cli_credentials
*creds
)
1709 return creds
->krb5_fast_armor_credentials
;
1712 bool cli_credentials_get_krb5_require_fast_armor(struct cli_credentials
*creds
)
1714 return creds
->krb5_require_fast_armor
;