2 Unix SMB/CIFS implementation.
4 Handle user credentials (as regards krb5)
6 Copyright (C) Jelmer Vernooij 2005
7 Copyright (C) Tim Potter 2001
8 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "system/kerberos.h"
26 #include "system/gssapi.h"
27 #include "auth/kerberos/kerberos.h"
28 #include "auth/credentials/credentials.h"
29 #include "auth/credentials/credentials_internal.h"
30 #include "auth/credentials/credentials_krb5.h"
31 #include "auth/kerberos/kerberos_credentials.h"
32 #include "auth/kerberos/kerberos_srv_keytab.h"
33 #include "auth/kerberos/kerberos_util.h"
34 #include "auth/kerberos/pac_utils.h"
35 #include "param/param.h"
36 #include "../libds/common/flags.h"
39 #define DBGC_CLASS DBGC_AUTH
43 static void cli_credentials_invalidate_client_gss_creds(
44 struct cli_credentials
*cred
,
45 enum credentials_obtained obtained
);
47 /* Free a memory ccache */
48 static int free_mccache(struct ccache_container
*ccc
)
50 if (ccc
->ccache
!= NULL
) {
51 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
59 /* Free a disk-based ccache */
60 static int free_dccache(struct ccache_container
*ccc
)
62 if (ccc
->ccache
!= NULL
) {
63 krb5_cc_close(ccc
->smb_krb5_context
->krb5_context
,
71 static uint32_t smb_gss_krb5_copy_ccache(uint32_t *min_stat
,
73 struct ccache_container
*ccc
)
75 #ifndef SAMBA4_USES_HEIMDAL /* MIT 1.10 */
76 krb5_context context
= ccc
->smb_krb5_context
->krb5_context
;
77 krb5_ccache dummy_ccache
= NULL
;
78 krb5_creds creds
= {0};
79 krb5_cc_cursor cursor
= NULL
;
80 krb5_principal princ
= NULL
;
83 uint32_t maj_stat
= GSS_S_FAILURE
;
85 dummy_name
= talloc_asprintf(ccc
,
86 "MEMORY:gss_krb5_copy_ccache-%p",
88 if (dummy_name
== NULL
) {
94 * Create a dummy ccache, so we can iterate over the credentials
95 * and find the default principal for the ccache we want to
96 * copy. The new ccache needs to be initialized with this
99 code
= krb5_cc_resolve(context
, dummy_name
, &dummy_ccache
);
100 TALLOC_FREE(dummy_name
);
103 return GSS_S_FAILURE
;
107 * We do not need set a default principal on the temporary dummy
108 * ccache, as we do consume it at all in this function.
110 maj_stat
= gss_krb5_copy_ccache(min_stat
, cred
, dummy_ccache
);
112 krb5_cc_close(context
, dummy_ccache
);
116 code
= krb5_cc_start_seq_get(context
, dummy_ccache
, &cursor
);
118 krb5_cc_close(context
, dummy_ccache
);
120 return GSS_S_FAILURE
;
123 code
= krb5_cc_next_cred(context
,
128 krb5_cc_close(context
, dummy_ccache
);
130 return GSS_S_FAILURE
;
134 if (creds
.ticket_flags
& TKT_FLG_PRE_AUTH
) {
137 tgs
= krb5_princ_component(context
,
140 if (tgs
!= NULL
&& tgs
->length
>= 1) {
143 cmp
= memcmp(tgs
->data
,
146 if (cmp
== 0 && creds
.client
!= NULL
) {
147 princ
= creds
.client
;
154 krb5_free_cred_contents(context
, &creds
);
156 code
= krb5_cc_next_cred(context
,
162 if (code
== KRB5_CC_END
) {
163 krb5_cc_end_seq_get(context
, dummy_ccache
, &cursor
);
166 krb5_cc_close(context
, dummy_ccache
);
168 if (code
!= 0 || princ
== NULL
) {
169 krb5_free_cred_contents(context
, &creds
);
171 return GSS_S_FAILURE
;
175 * Set the default principal for the cache we copy
176 * into. This is needed to be able that other calls
177 * can read it with e.g. gss_acquire_cred() or
178 * krb5_cc_get_principal().
180 code
= krb5_cc_initialize(context
, ccc
->ccache
, princ
);
182 krb5_free_cred_contents(context
, &creds
);
184 return GSS_S_FAILURE
;
186 krb5_free_cred_contents(context
, &creds
);
188 #endif /* SAMBA4_USES_HEIMDAL */
190 return gss_krb5_copy_ccache(min_stat
,
195 _PUBLIC_
int cli_credentials_get_krb5_context(struct cli_credentials
*cred
,
196 struct loadparm_context
*lp_ctx
,
197 struct smb_krb5_context
**smb_krb5_context
)
200 if (cred
->smb_krb5_context
) {
201 *smb_krb5_context
= cred
->smb_krb5_context
;
205 ret
= smb_krb5_init_context(cred
, lp_ctx
,
206 &cred
->smb_krb5_context
);
208 cred
->smb_krb5_context
= NULL
;
211 *smb_krb5_context
= cred
->smb_krb5_context
;
215 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
216 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
218 _PUBLIC_ NTSTATUS
cli_credentials_set_krb5_context(struct cli_credentials
*cred
,
219 struct smb_krb5_context
*smb_krb5_context
)
221 if (smb_krb5_context
== NULL
) {
222 talloc_unlink(cred
, cred
->smb_krb5_context
);
223 cred
->smb_krb5_context
= NULL
;
227 if (!talloc_reference(cred
, smb_krb5_context
)) {
228 return NT_STATUS_NO_MEMORY
;
230 cred
->smb_krb5_context
= smb_krb5_context
;
234 static int cli_credentials_set_from_ccache(struct cli_credentials
*cred
,
235 struct ccache_container
*ccache
,
236 enum credentials_obtained obtained
,
237 const char **error_string
)
241 krb5_principal princ
;
245 if (cred
->ccache_obtained
> obtained
) {
249 ret
= krb5_cc_get_principal(ccache
->smb_krb5_context
->krb5_context
,
250 ccache
->ccache
, &princ
);
253 (*error_string
) = talloc_asprintf(cred
, "failed to get principal from ccache: %s\n",
254 smb_get_krb5_error_message(ccache
->smb_krb5_context
->krb5_context
,
259 ret
= krb5_unparse_name(ccache
->smb_krb5_context
->krb5_context
, princ
, &name
);
261 (*error_string
) = talloc_asprintf(cred
, "failed to unparse principal from ccache: %s\n",
262 smb_get_krb5_error_message(ccache
->smb_krb5_context
->krb5_context
,
264 krb5_free_principal(ccache
->smb_krb5_context
->krb5_context
, princ
);
268 ok
= cli_credentials_set_principal(cred
, name
, obtained
);
269 krb5_free_unparsed_name(ccache
->smb_krb5_context
->krb5_context
, name
);
271 krb5_free_principal(ccache
->smb_krb5_context
->krb5_context
, princ
);
275 realm
= smb_krb5_principal_get_realm(
276 cred
, ccache
->smb_krb5_context
->krb5_context
, princ
);
277 krb5_free_principal(ccache
->smb_krb5_context
->krb5_context
, princ
);
281 ok
= cli_credentials_set_realm(cred
, realm
, obtained
);
287 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
288 cred
->ccache_obtained
= obtained
;
293 _PUBLIC_
int cli_credentials_set_ccache(struct cli_credentials
*cred
,
294 struct loadparm_context
*lp_ctx
,
296 enum credentials_obtained obtained
,
297 const char **error_string
)
300 krb5_principal princ
;
301 struct ccache_container
*ccc
;
302 if (cred
->ccache_obtained
> obtained
) {
306 ccc
= talloc(cred
, struct ccache_container
);
308 (*error_string
) = error_message(ENOMEM
);
312 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
313 &ccc
->smb_krb5_context
);
315 (*error_string
) = error_message(ret
);
319 if (!talloc_reference(ccc
, ccc
->smb_krb5_context
)) {
321 (*error_string
) = error_message(ENOMEM
);
326 ret
= krb5_cc_resolve(ccc
->smb_krb5_context
->krb5_context
, name
, &ccc
->ccache
);
328 (*error_string
) = talloc_asprintf(cred
, "failed to read krb5 ccache: %s: %s\n",
330 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
,
336 ret
= krb5_cc_default(ccc
->smb_krb5_context
->krb5_context
, &ccc
->ccache
);
338 (*error_string
) = talloc_asprintf(cred
, "failed to read default krb5 ccache: %s\n",
339 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
,
346 talloc_set_destructor(ccc
, free_dccache
);
348 ret
= krb5_cc_get_principal(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
, &princ
);
351 krb5_free_principal(ccc
->smb_krb5_context
->krb5_context
, princ
);
352 ret
= cli_credentials_set_from_ccache(cred
, ccc
, obtained
, error_string
);
355 (*error_string
) = error_message(ret
);
362 cred
->ccache_obtained
= obtained
;
364 cli_credentials_invalidate_client_gss_creds(
365 cred
, cred
->ccache_obtained
);
370 #ifndef SAMBA4_USES_HEIMDAL
372 * This function is a workaround for old MIT Kerberos versions which did not
373 * implement the krb5_cc_remove_cred function. It creates a temporary
374 * credentials cache to copy the credentials in the current cache
375 * except the one we want to remove and then overwrites the contents of the
376 * current cache with the temporary copy.
378 static krb5_error_code
krb5_cc_remove_cred_wrap(struct ccache_container
*ccc
,
381 krb5_ccache dummy_ccache
= NULL
;
382 krb5_creds cached_creds
= {0};
383 krb5_cc_cursor cursor
= NULL
;
384 krb5_error_code code
;
387 dummy_name
= talloc_asprintf(ccc
,
388 "MEMORY:copy_ccache-%p",
390 if (dummy_name
== NULL
) {
391 return KRB5_CC_NOMEM
;
394 code
= krb5_cc_resolve(ccc
->smb_krb5_context
->krb5_context
,
398 DBG_ERR("krb5_cc_resolve failed: %s\n",
399 smb_get_krb5_error_message(
400 ccc
->smb_krb5_context
->krb5_context
,
402 TALLOC_FREE(dummy_name
);
406 TALLOC_FREE(dummy_name
);
408 code
= krb5_cc_start_seq_get(ccc
->smb_krb5_context
->krb5_context
,
412 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
415 DBG_ERR("krb5_cc_start_seq_get failed: %s\n",
416 smb_get_krb5_error_message(
417 ccc
->smb_krb5_context
->krb5_context
,
422 while ((code
= krb5_cc_next_cred(ccc
->smb_krb5_context
->krb5_context
,
425 &cached_creds
)) == 0) {
426 /* If the principal matches skip it and do not copy to the
427 * temporary cache as this is the one we want to remove */
428 if (krb5_principal_compare_flags(
429 ccc
->smb_krb5_context
->krb5_context
,
436 code
= krb5_cc_store_cred(
437 ccc
->smb_krb5_context
->krb5_context
,
441 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
443 DBG_ERR("krb5_cc_store_cred failed: %s\n",
444 smb_get_krb5_error_message(
445 ccc
->smb_krb5_context
->krb5_context
,
451 if (code
== KRB5_CC_END
) {
452 krb5_cc_end_seq_get(ccc
->smb_krb5_context
->krb5_context
,
459 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
461 DBG_ERR("krb5_cc_next_cred failed: %s\n",
462 smb_get_krb5_error_message(
463 ccc
->smb_krb5_context
->krb5_context
,
468 code
= krb5_cc_initialize(ccc
->smb_krb5_context
->krb5_context
,
472 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
474 DBG_ERR("krb5_cc_initialize failed: %s\n",
475 smb_get_krb5_error_message(
476 ccc
->smb_krb5_context
->krb5_context
,
481 code
= krb5_cc_copy_creds(ccc
->smb_krb5_context
->krb5_context
,
485 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
487 DBG_ERR("krb5_cc_copy_creds failed: %s\n",
488 smb_get_krb5_error_message(
489 ccc
->smb_krb5_context
->krb5_context
,
494 code
= krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
497 DBG_ERR("krb5_cc_destroy failed: %s\n",
498 smb_get_krb5_error_message(
499 ccc
->smb_krb5_context
->krb5_context
,
509 * Indicate that we failed to log in to this service/host with these
510 * credentials. The caller passes an unsigned int which they
511 * initialise to the number of times they would like to retry.
513 * This method is used to support re-trying with freshly fetched
514 * credentials in case a server is rebuilt while clients have
515 * non-expired tickets. When the client code gets a logon failure they
516 * throw away the existing credentials for the server and retry.
518 _PUBLIC_
bool cli_credentials_failed_kerberos_login(struct cli_credentials
*cred
,
519 const char *principal
,
522 struct ccache_container
*ccc
;
523 krb5_creds creds
, creds2
;
526 if (principal
== NULL
) {
527 /* no way to delete if we don't know the principal */
533 /* not a kerberos connection */
538 /* We have already tried discarding the credentials */
544 ret
= krb5_parse_name(ccc
->smb_krb5_context
->krb5_context
, principal
, &creds
.server
);
549 /* MIT kerberos requires creds.client to match against cached
551 ret
= krb5_cc_get_principal(ccc
->smb_krb5_context
->krb5_context
,
555 krb5_free_cred_contents(ccc
->smb_krb5_context
->krb5_context
,
557 DBG_ERR("krb5_cc_get_principal failed: %s\n",
558 smb_get_krb5_error_message(
559 ccc
->smb_krb5_context
->krb5_context
,
564 ret
= krb5_cc_retrieve_cred(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
, KRB5_TC_MATCH_SRV_NAMEONLY
, &creds
, &creds2
);
566 /* don't retry - we didn't find these credentials to remove */
567 krb5_free_cred_contents(ccc
->smb_krb5_context
->krb5_context
, &creds
);
571 ret
= krb5_cc_remove_cred(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
, KRB5_TC_MATCH_SRV_NAMEONLY
, &creds
);
572 #ifndef SAMBA4_USES_HEIMDAL
573 if (ret
== KRB5_CC_NOSUPP
) {
574 /* Old MIT kerberos versions did not implement
575 * krb5_cc_remove_cred */
576 ret
= krb5_cc_remove_cred_wrap(ccc
, &creds
);
579 krb5_free_cred_contents(ccc
->smb_krb5_context
->krb5_context
, &creds
);
580 krb5_free_cred_contents(ccc
->smb_krb5_context
->krb5_context
, &creds2
);
582 /* don't retry - we didn't find these credentials to
583 * remove. Note that with the current backend this
584 * never happens, as it always returns 0 even if the
585 * creds don't exist, which is why we do a separate
586 * krb5_cc_retrieve_cred() above.
588 DBG_ERR("krb5_cc_remove_cred failed: %s\n",
589 smb_get_krb5_error_message(
590 ccc
->smb_krb5_context
->krb5_context
,
598 static int cli_credentials_new_ccache(struct cli_credentials
*cred
,
599 struct loadparm_context
*lp_ctx
,
601 struct ccache_container
**_ccc
,
602 const char **error_string
)
604 bool must_free_cc_name
= false;
606 struct ccache_container
*ccc
= talloc(cred
, struct ccache_container
);
611 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
612 &ccc
->smb_krb5_context
);
615 (*error_string
) = talloc_asprintf(cred
, "Failed to get krb5_context: %s",
619 if (!talloc_reference(ccc
, ccc
->smb_krb5_context
)) {
621 (*error_string
) = strerror(ENOMEM
);
626 must_free_cc_name
= true;
628 if (lpcfg_parm_bool(lp_ctx
, NULL
, "credentials", "krb5_cc_file", false)) {
629 ccache_name
= talloc_asprintf(ccc
, "FILE:/tmp/krb5_cc_samba_%u_%p",
630 (unsigned int)getpid(), ccc
);
632 ccache_name
= talloc_asprintf(ccc
, "MEMORY:%p",
638 (*error_string
) = strerror(ENOMEM
);
643 ret
= krb5_cc_resolve(ccc
->smb_krb5_context
->krb5_context
, ccache_name
,
646 (*error_string
) = talloc_asprintf(cred
, "failed to resolve a krb5 ccache (%s): %s\n",
648 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
,
650 talloc_free(ccache_name
);
655 if (strncasecmp(ccache_name
, "MEMORY:", 7) == 0) {
656 talloc_set_destructor(ccc
, free_mccache
);
658 talloc_set_destructor(ccc
, free_dccache
);
661 if (must_free_cc_name
) {
662 talloc_free(ccache_name
);
670 _PUBLIC_
int cli_credentials_get_named_ccache(struct cli_credentials
*cred
,
671 struct tevent_context
*event_ctx
,
672 struct loadparm_context
*lp_ctx
,
674 struct ccache_container
**ccc
,
675 const char **error_string
)
678 enum credentials_obtained obtained
;
680 if (cred
->machine_account_pending
) {
681 cli_credentials_set_machine_account(cred
, lp_ctx
);
684 if (cred
->ccache_obtained
>= cred
->ccache_threshold
&&
685 cred
->ccache_obtained
> CRED_UNINITIALISED
) {
687 bool expired
= false;
688 ret
= smb_krb5_cc_get_lifetime(cred
->ccache
->smb_krb5_context
->krb5_context
,
689 cred
->ccache
->ccache
, &lifetime
);
690 if (ret
== KRB5_CC_END
|| ret
== ENOENT
) {
691 /* If we have a particular ccache set, without
692 * an initial ticket, then assume there is a
694 } else if (ret
== 0) {
696 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
697 cli_credentials_get_principal(cred
, cred
)));
699 } else if (lifetime
< 300) {
700 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
701 cli_credentials_get_principal(cred
, cred
), (unsigned int)lifetime
));
705 (*error_string
) = talloc_asprintf(cred
, "failed to get ccache lifetime: %s\n",
706 smb_get_krb5_error_message(cred
->ccache
->smb_krb5_context
->krb5_context
,
711 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
712 cli_credentials_get_principal(cred
, cred
), (unsigned int)lifetime
));
719 if (cli_credentials_is_anonymous(cred
)) {
720 (*error_string
) = "Cannot get anonymous kerberos credentials";
724 ret
= cli_credentials_new_ccache(cred
, lp_ctx
, ccache_name
, ccc
, error_string
);
729 ret
= kinit_to_ccache(cred
, cred
, (*ccc
)->smb_krb5_context
, event_ctx
, (*ccc
)->ccache
, &obtained
, error_string
);
734 ret
= cli_credentials_set_from_ccache(cred
, *ccc
,
735 obtained
, error_string
);
738 cred
->ccache_obtained
= cred
->principal_obtained
;
742 cli_credentials_invalidate_client_gss_creds(cred
, cred
->ccache_obtained
);
746 _PUBLIC_
int cli_credentials_get_ccache(struct cli_credentials
*cred
,
747 struct tevent_context
*event_ctx
,
748 struct loadparm_context
*lp_ctx
,
749 struct ccache_container
**ccc
,
750 const char **error_string
)
752 return cli_credentials_get_named_ccache(cred
, event_ctx
, lp_ctx
, NULL
, ccc
, error_string
);
755 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
756 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials
*cred
)
758 if (cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
759 talloc_unlink(cred
, cred
->client_gss_creds
);
760 cred
->client_gss_creds
= NULL
;
762 cred
->client_gss_creds_obtained
= CRED_UNINITIALISED
;
765 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials
*cred
,
766 enum credentials_obtained obtained
)
768 /* If the caller just changed the username/password etc, then
769 * any cached credentials are now invalid */
770 if (obtained
>= cred
->client_gss_creds_obtained
) {
771 if (cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
772 talloc_unlink(cred
, cred
->client_gss_creds
);
773 cred
->client_gss_creds
= NULL
;
775 cred
->client_gss_creds_obtained
= CRED_UNINITIALISED
;
777 /* Now that we know that the data is 'this specified', then
778 * don't allow something less 'known' to be returned as a
779 * ccache. Ie, if the username is on the command line, we
780 * don't want to later guess to use a file-based ccache */
781 if (obtained
> cred
->client_gss_creds_threshold
) {
782 cred
->client_gss_creds_threshold
= obtained
;
786 /* We have good reason to think this CCACHE is invalid. Blow it away */
787 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials
*cred
)
789 if (cred
->ccache_obtained
> CRED_UNINITIALISED
) {
790 talloc_unlink(cred
, cred
->ccache
);
793 cred
->ccache_obtained
= CRED_UNINITIALISED
;
795 cli_credentials_unconditionally_invalidate_client_gss_creds(cred
);
798 _PUBLIC_
void cli_credentials_invalidate_ccache(struct cli_credentials
*cred
,
799 enum credentials_obtained obtained
)
801 /* If the caller just changed the username/password etc, then
802 * any cached credentials are now invalid */
803 if (obtained
>= cred
->ccache_obtained
) {
804 if (cred
->ccache_obtained
> CRED_UNINITIALISED
) {
805 talloc_unlink(cred
, cred
->ccache
);
808 cred
->ccache_obtained
= CRED_UNINITIALISED
;
810 /* Now that we know that the data is 'this specified', then
811 * don't allow something less 'known' to be returned as a
812 * ccache. i.e, if the username is on the command line, we
813 * don't want to later guess to use a file-based ccache */
814 if (obtained
> cred
->ccache_threshold
) {
815 cred
->ccache_threshold
= obtained
;
818 cli_credentials_invalidate_client_gss_creds(cred
,
822 static int free_gssapi_creds(struct gssapi_creds_container
*gcc
)
825 (void)gss_release_cred(&min_stat
, &gcc
->creds
);
829 _PUBLIC_
int cli_credentials_get_client_gss_creds(struct cli_credentials
*cred
,
830 struct tevent_context
*event_ctx
,
831 struct loadparm_context
*lp_ctx
,
832 struct gssapi_creds_container
**_gcc
,
833 const char **error_string
)
836 OM_uint32 maj_stat
, min_stat
;
837 struct gssapi_creds_container
*gcc
;
838 struct ccache_container
*ccache
;
839 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
840 gss_buffer_desc empty_buffer
= GSS_C_EMPTY_BUFFER
;
841 gss_OID oid
= discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X
);
843 krb5_enctype
*etypes
= NULL
;
845 if (cred
->client_gss_creds_obtained
>= cred
->client_gss_creds_threshold
&&
846 cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
847 bool expired
= false;
848 OM_uint32 lifetime
= 0;
849 gss_cred_usage_t usage
= 0;
850 maj_stat
= gss_inquire_cred(&min_stat
, cred
->client_gss_creds
->creds
,
851 NULL
, &lifetime
, &usage
, NULL
);
852 if (maj_stat
== GSS_S_CREDENTIALS_EXPIRED
) {
853 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred
, cred
)));
855 } else if (maj_stat
== GSS_S_COMPLETE
&& lifetime
< 300) {
856 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred
, cred
), lifetime
));
858 } else if (maj_stat
!= GSS_S_COMPLETE
) {
859 *error_string
= talloc_asprintf(cred
, "inquiry of credential lifetime via GSSAPI gss_inquire_cred failed: %s\n",
860 gssapi_error_string(cred
, maj_stat
, min_stat
, NULL
));
864 cli_credentials_unconditionally_invalidate_client_gss_creds(cred
);
866 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
867 cli_credentials_get_principal(cred
, cred
), (unsigned int)lifetime
));
869 *_gcc
= cred
->client_gss_creds
;
874 ret
= cli_credentials_get_ccache(cred
, event_ctx
, lp_ctx
,
875 &ccache
, error_string
);
877 if (cli_credentials_get_kerberos_state(cred
) == CRED_USE_KERBEROS_REQUIRED
) {
878 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string
));
880 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string
));
885 gcc
= talloc(cred
, struct gssapi_creds_container
);
887 (*error_string
) = error_message(ENOMEM
);
891 maj_stat
= smb_gss_krb5_import_cred(&min_stat
, ccache
->smb_krb5_context
->krb5_context
,
892 ccache
->ccache
, NULL
, NULL
,
894 if ((maj_stat
== GSS_S_FAILURE
) &&
895 (min_stat
== (OM_uint32
)KRB5_CC_END
||
896 min_stat
== (OM_uint32
)KRB5_CC_NOTFOUND
||
897 min_stat
== (OM_uint32
)KRB5_FCC_NOFILE
))
899 /* This CCACHE is no good. Ensure we don't use it again */
900 cli_credentials_unconditionally_invalidate_ccache(cred
);
902 /* Now try again to get a ccache */
903 ret
= cli_credentials_get_ccache(cred
, event_ctx
, lp_ctx
,
904 &ccache
, error_string
);
906 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret
)));
910 maj_stat
= smb_gss_krb5_import_cred(&min_stat
, ccache
->smb_krb5_context
->krb5_context
,
911 ccache
->ccache
, NULL
, NULL
,
923 (*error_string
) = talloc_asprintf(cred
, "smb_gss_krb5_import_cred failed: %s", error_message(ret
));
929 * transfer the enctypes from the smb_krb5_context to the gssapi layer
931 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
932 * to configure the enctypes via the krb5.conf.
934 * And the gss_init_sec_context() creates it's own krb5_context and
935 * the TGS-REQ had all enctypes in it and only the ones configured
936 * and used for the AS-REQ, so it wasn't possible to disable the usage
939 min_stat
= smb_krb5_get_allowed_etypes(ccache
->smb_krb5_context
->krb5_context
,
942 OM_uint32 num_ktypes
;
944 for (num_ktypes
= 0; etypes
[num_ktypes
]; num_ktypes
++);
946 maj_stat
= gss_krb5_set_allowable_enctypes(&min_stat
, gcc
->creds
,
949 krb5_free_enctypes(ccache
->smb_krb5_context
->krb5_context
,
958 (*error_string
) = talloc_asprintf(cred
, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret
));
963 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
965 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
967 * This allows us to disable SIGN and SEAL on a TLS connection with
968 * GSS-SPNENO. For example ldaps:// connections.
970 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
971 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
973 maj_stat
= gss_set_cred_option(&min_stat
, &gcc
->creds
,
983 (*error_string
) = talloc_asprintf(cred
, "gss_set_cred_option failed: %s", error_message(ret
));
987 cred
->client_gss_creds_obtained
= cred
->ccache_obtained
;
988 talloc_set_destructor(gcc
, free_gssapi_creds
);
989 cred
->client_gss_creds
= gcc
;
995 Set a gssapi cred_id_t into the credentials system. (Client case)
997 This grabs the credentials both 'intact' and getting the krb5
998 ccache out of it. This routine can be generalised in future for
999 the case where we deal with GSSAPI mechs other than krb5.
1001 On success, the caller must not free gssapi_cred, as it now belongs
1002 to the credentials system.
1005 int cli_credentials_set_client_gss_creds(struct cli_credentials
*cred
,
1006 struct loadparm_context
*lp_ctx
,
1007 gss_cred_id_t gssapi_cred
,
1008 enum credentials_obtained obtained
,
1009 const char **error_string
)
1012 OM_uint32 maj_stat
, min_stat
;
1013 struct ccache_container
*ccc
= NULL
;
1014 struct gssapi_creds_container
*gcc
= NULL
;
1015 if (cred
->client_gss_creds_obtained
> obtained
) {
1019 gcc
= talloc(cred
, struct gssapi_creds_container
);
1021 (*error_string
) = error_message(ENOMEM
);
1025 ret
= cli_credentials_new_ccache(cred
, lp_ctx
, NULL
, &ccc
, error_string
);
1030 maj_stat
= smb_gss_krb5_copy_ccache(&min_stat
,
1040 (*error_string
) = error_message(ENOMEM
);
1045 ret
= cli_credentials_set_from_ccache(cred
, ccc
, obtained
, error_string
);
1048 cred
->ccache_obtained
= obtained
;
1050 gcc
->creds
= gssapi_cred
;
1051 talloc_set_destructor(gcc
, free_gssapi_creds
);
1053 /* set the client_gss_creds_obtained here, as it just
1054 got set to UNINITIALISED by the calls above */
1055 cred
->client_gss_creds_obtained
= obtained
;
1056 cred
->client_gss_creds
= gcc
;
1061 static int cli_credentials_shallow_ccache(struct cli_credentials
*cred
)
1063 krb5_error_code ret
;
1064 const struct ccache_container
*old_ccc
= NULL
;
1065 enum credentials_obtained old_obtained
;
1066 struct ccache_container
*ccc
= NULL
;
1067 char *ccache_name
= NULL
;
1068 krb5_principal princ
;
1070 old_obtained
= cred
->ccache_obtained
;
1071 old_ccc
= cred
->ccache
;
1072 if (old_ccc
== NULL
) {
1076 cred
->ccache
= NULL
;
1077 cred
->ccache_obtained
= CRED_UNINITIALISED
;
1078 cred
->client_gss_creds
= NULL
;
1079 cred
->client_gss_creds_obtained
= CRED_UNINITIALISED
;
1081 ret
= krb5_cc_get_principal(
1082 old_ccc
->smb_krb5_context
->krb5_context
,
1087 * This is an empty ccache. No point in copying anything.
1091 krb5_free_principal(old_ccc
->smb_krb5_context
->krb5_context
, princ
);
1093 ccc
= talloc(cred
, struct ccache_container
);
1100 ccache_name
= talloc_asprintf(ccc
, "MEMORY:%p", ccc
);
1102 ret
= krb5_cc_resolve(ccc
->smb_krb5_context
->krb5_context
,
1103 ccache_name
, &ccc
->ccache
);
1109 talloc_set_destructor(ccc
, free_mccache
);
1111 TALLOC_FREE(ccache_name
);
1113 ret
= smb_krb5_cc_copy_creds(ccc
->smb_krb5_context
->krb5_context
,
1114 old_ccc
->ccache
, ccc
->ccache
);
1121 cred
->ccache_obtained
= old_obtained
;
1125 _PUBLIC_
struct cli_credentials
*cli_credentials_shallow_copy(TALLOC_CTX
*mem_ctx
,
1126 struct cli_credentials
*src
)
1128 struct cli_credentials
*dst
, *armor_credentials
;
1131 dst
= talloc(mem_ctx
, struct cli_credentials
);
1138 if (dst
->krb5_fast_armor_credentials
!= NULL
) {
1139 armor_credentials
= talloc_reference(dst
, dst
->krb5_fast_armor_credentials
);
1140 if (armor_credentials
== NULL
) {
1146 ret
= cli_credentials_shallow_ccache(dst
);
1155 /* Get the keytab (actually, a container containing the krb5_keytab)
1156 * attached to this context. If this hasn't been done or set before,
1157 * it will be generated from the password.
1159 _PUBLIC_
int cli_credentials_get_keytab(struct cli_credentials
*cred
,
1160 struct loadparm_context
*lp_ctx
,
1161 struct keytab_container
**_ktc
)
1163 krb5_error_code ret
;
1164 struct keytab_container
*ktc
;
1165 struct smb_krb5_context
*smb_krb5_context
;
1166 const char *keytab_name
;
1168 TALLOC_CTX
*mem_ctx
;
1169 const char *username
= cli_credentials_get_username(cred
);
1170 const char *upn
= NULL
;
1171 const char *realm
= cli_credentials_get_realm(cred
);
1172 char *salt_principal
= NULL
;
1173 uint32_t uac_flags
= 0;
1175 if (cred
->keytab_obtained
>= (MAX(cred
->principal_obtained
,
1176 cred
->username_obtained
))) {
1177 *_ktc
= cred
->keytab
;
1181 if (cli_credentials_is_anonymous(cred
)) {
1185 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
1191 mem_ctx
= talloc_new(cred
);
1196 switch (cred
->secure_channel_type
) {
1197 case SEC_CHAN_WKSTA
:
1199 uac_flags
= UF_WORKSTATION_TRUST_ACCOUNT
;
1202 uac_flags
= UF_SERVER_TRUST_ACCOUNT
;
1204 case SEC_CHAN_DOMAIN
:
1205 case SEC_CHAN_DNS_DOMAIN
:
1206 uac_flags
= UF_INTERDOMAIN_TRUST_ACCOUNT
;
1209 upn
= cli_credentials_get_principal(cred
, mem_ctx
);
1211 TALLOC_FREE(mem_ctx
);
1214 uac_flags
= UF_NORMAL_ACCOUNT
;
1218 ret
= smb_krb5_salt_principal_str(realm
,
1219 username
, /* sAMAccountName */
1220 upn
, /* userPrincipalName */
1225 talloc_free(mem_ctx
);
1229 ret
= smb_krb5_create_memory_keytab(mem_ctx
,
1230 smb_krb5_context
->krb5_context
,
1231 cli_credentials_get_password(cred
),
1235 cli_credentials_get_kvno(cred
),
1239 talloc_free(mem_ctx
);
1243 ret
= smb_krb5_get_keytab_container(mem_ctx
, smb_krb5_context
,
1244 keytab
, keytab_name
, &ktc
);
1246 talloc_free(mem_ctx
);
1250 cred
->keytab_obtained
= (MAX(cred
->principal_obtained
,
1251 cred
->username_obtained
));
1253 /* We make this keytab up based on a password. Therefore
1254 * match-by-key is acceptable, we can't match on the wrong
1256 ktc
->password_based
= true;
1258 talloc_steal(cred
, ktc
);
1260 *_ktc
= cred
->keytab
;
1261 talloc_free(mem_ctx
);
1265 /* Given the name of a keytab (presumably in the format
1266 * FILE:/etc/krb5.keytab), open it and attach it */
1268 _PUBLIC_
int cli_credentials_set_keytab_name(struct cli_credentials
*cred
,
1269 struct loadparm_context
*lp_ctx
,
1270 const char *keytab_name
,
1271 enum credentials_obtained obtained
)
1273 krb5_error_code ret
;
1274 struct keytab_container
*ktc
;
1275 struct smb_krb5_context
*smb_krb5_context
;
1276 TALLOC_CTX
*mem_ctx
;
1278 if (cred
->keytab_obtained
>= obtained
) {
1282 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
, &smb_krb5_context
);
1287 mem_ctx
= talloc_new(cred
);
1292 ret
= smb_krb5_get_keytab_container(mem_ctx
, smb_krb5_context
,
1293 NULL
, keytab_name
, &ktc
);
1298 cred
->keytab_obtained
= obtained
;
1300 talloc_steal(cred
, ktc
);
1302 talloc_free(mem_ctx
);
1307 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1309 _PUBLIC_
int cli_credentials_get_server_gss_creds(struct cli_credentials
*cred
,
1310 struct loadparm_context
*lp_ctx
,
1311 struct gssapi_creds_container
**_gcc
)
1314 OM_uint32 maj_stat
, min_stat
;
1315 struct gssapi_creds_container
*gcc
;
1316 struct keytab_container
*ktc
;
1317 struct smb_krb5_context
*smb_krb5_context
;
1318 TALLOC_CTX
*mem_ctx
;
1319 krb5_principal princ
;
1320 const char *error_string
;
1321 enum credentials_obtained obtained
;
1323 mem_ctx
= talloc_new(cred
);
1328 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
, &smb_krb5_context
);
1333 ret
= principal_from_credentials(mem_ctx
, cred
, smb_krb5_context
, &princ
, &obtained
, &error_string
);
1335 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1337 talloc_free(mem_ctx
);
1341 if (cred
->server_gss_creds_obtained
>= (MAX(cred
->keytab_obtained
, obtained
))) {
1342 talloc_free(mem_ctx
);
1343 *_gcc
= cred
->server_gss_creds
;
1347 ret
= cli_credentials_get_keytab(cred
, lp_ctx
, &ktc
);
1349 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret
)));
1353 gcc
= talloc(cred
, struct gssapi_creds_container
);
1355 talloc_free(mem_ctx
);
1359 if (ktc
->password_based
|| obtained
< CRED_SPECIFIED
) {
1361 * This creates a GSSAPI cred_id_t for match-by-key with only
1366 maj_stat
= smb_gss_krb5_import_cred(&min_stat
,
1367 smb_krb5_context
->krb5_context
,
1379 cred
->server_gss_creds_obtained
= cred
->keytab_obtained
;
1380 talloc_set_destructor(gcc
, free_gssapi_creds
);
1381 cred
->server_gss_creds
= gcc
;
1384 talloc_free(mem_ctx
);
1392 _PUBLIC_
void cli_credentials_set_kvno(struct cli_credentials
*cred
,
1399 * Return Kerberos KVNO
1402 _PUBLIC_
int cli_credentials_get_kvno(struct cli_credentials
*cred
)
1408 const char *cli_credentials_get_salt_principal(struct cli_credentials
*cred
)
1410 return cred
->salt_principal
;
1413 _PUBLIC_
void cli_credentials_set_salt_principal(struct cli_credentials
*cred
, const char *principal
)
1415 talloc_free(cred
->salt_principal
);
1416 cred
->salt_principal
= talloc_strdup(cred
, principal
);
1419 /* The 'impersonate_principal' is used to allow one Kerberos principal
1420 * (and it's associated keytab etc) to impersonate another. The
1421 * ability to do this is controlled by the KDC, but it is generally
1422 * permitted to impersonate anyone to yourself. This allows any
1423 * member of the domain to get the groups of a user. This is also
1424 * known as S4U2Self */
1426 _PUBLIC_
const char *cli_credentials_get_impersonate_principal(struct cli_credentials
*cred
)
1428 return cred
->impersonate_principal
;
1432 * The 'self_service' is the service principal that
1433 * represents the same object (by its objectSid)
1434 * as the client principal (typically our machine account).
1435 * When trying to impersonate 'impersonate_principal' with
1438 _PUBLIC_
const char *cli_credentials_get_self_service(struct cli_credentials
*cred
)
1440 return cred
->self_service
;
1443 _PUBLIC_
void cli_credentials_set_impersonate_principal(struct cli_credentials
*cred
,
1444 const char *principal
,
1445 const char *self_service
)
1447 talloc_free(cred
->impersonate_principal
);
1448 cred
->impersonate_principal
= talloc_strdup(cred
, principal
);
1449 talloc_free(cred
->self_service
);
1450 cred
->self_service
= talloc_strdup(cred
, self_service
);
1451 cli_credentials_set_kerberos_state(cred
,
1452 CRED_USE_KERBEROS_REQUIRED
,
1457 * when impersonating for S4U2proxy we need to set the target principal.
1458 * Similarly, we may only be authorized to do general impersonation to
1459 * some particular services.
1461 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1463 * NULL means that tickets will be obtained for the krbtgt service.
1466 const char *cli_credentials_get_target_service(struct cli_credentials
*cred
)
1468 return cred
->target_service
;
1471 _PUBLIC_
void cli_credentials_set_target_service(struct cli_credentials
*cred
, const char *target_service
)
1473 talloc_free(cred
->target_service
);
1474 cred
->target_service
= talloc_strdup(cred
, target_service
);
1477 _PUBLIC_
int cli_credentials_get_aes256_key(struct cli_credentials
*cred
,
1478 TALLOC_CTX
*mem_ctx
,
1479 struct loadparm_context
*lp_ctx
,
1483 struct smb_krb5_context
*smb_krb5_context
= NULL
;
1484 krb5_error_code krb5_ret
;
1486 const char *password
= NULL
;
1487 krb5_data cleartext_data
;
1488 krb5_data salt_data
= {
1493 if (cred
->password_will_be_nt_hash
) {
1494 DEBUG(1,("cli_credentials_get_aes256_key: cannot generate AES256 key using NT hash\n"));
1498 password
= cli_credentials_get_password(cred
);
1499 if (password
== NULL
) {
1503 cleartext_data
.data
= discard_const_p(char, password
);
1504 cleartext_data
.length
= strlen(password
);
1506 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
1512 salt_data
.data
= discard_const_p(char, salt
);
1513 salt_data
.length
= strlen(salt
);
1516 * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
1517 * the salt and the cleartext password
1519 krb5_ret
= smb_krb5_create_key_from_string(smb_krb5_context
->krb5_context
,
1523 ENCTYPE_AES256_CTS_HMAC_SHA1_96
,
1525 if (krb5_ret
!= 0) {
1526 DEBUG(1,("cli_credentials_get_aes256_key: "
1527 "generation of a aes256-cts-hmac-sha1-96 key failed: %s\n",
1528 smb_get_krb5_error_message(smb_krb5_context
->krb5_context
,
1529 krb5_ret
, mem_ctx
)));
1532 *aes_256
= data_blob_talloc(mem_ctx
,
1533 KRB5_KEY_DATA(&key
),
1534 KRB5_KEY_LENGTH(&key
));
1535 krb5_free_keyblock_contents(smb_krb5_context
->krb5_context
, &key
);
1536 if (aes_256
->data
== NULL
) {
1539 talloc_keep_secret(aes_256
->data
);
1544 /* This take a reference to the armor credentials to ensure the lifetime is appropriate */
1546 NTSTATUS
cli_credentials_set_krb5_fast_armor_credentials(struct cli_credentials
*creds
,
1547 struct cli_credentials
*armor_creds
,
1548 bool require_fast_armor
)
1550 talloc_unlink(creds
, creds
->krb5_fast_armor_credentials
);
1551 if (armor_creds
== NULL
) {
1552 creds
->krb5_fast_armor_credentials
= NULL
;
1553 return NT_STATUS_OK
;
1556 creds
->krb5_fast_armor_credentials
= talloc_reference(creds
, armor_creds
);
1557 if (creds
->krb5_fast_armor_credentials
== NULL
) {
1558 return NT_STATUS_NO_MEMORY
;
1561 creds
->krb5_require_fast_armor
= require_fast_armor
;
1563 return NT_STATUS_OK
;
1566 struct cli_credentials
*cli_credentials_get_krb5_fast_armor_credentials(struct cli_credentials
*creds
)
1568 return creds
->krb5_fast_armor_credentials
;
1571 bool cli_credentials_get_krb5_require_fast_armor(struct cli_credentials
*creds
)
1573 return creds
->krb5_require_fast_armor
;