2 Unix SMB/CIFS implementation.
4 Handle user credentials (as regards krb5)
6 Copyright (C) Jelmer Vernooij 2005
7 Copyright (C) Tim Potter 2001
8 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "system/kerberos.h"
26 #include "system/gssapi.h"
27 #include "auth/kerberos/kerberos.h"
28 #include "auth/credentials/credentials.h"
29 #include "auth/credentials/credentials_internal.h"
30 #include "auth/credentials/credentials_proto.h"
31 #include "auth/credentials/credentials_krb5.h"
32 #include "auth/kerberos/kerberos_credentials.h"
33 #include "auth/kerberos/kerberos_srv_keytab.h"
34 #include "auth/kerberos/kerberos_util.h"
35 #include "auth/kerberos/pac_utils.h"
36 #include "param/param.h"
38 static void cli_credentials_invalidate_client_gss_creds(
39 struct cli_credentials
*cred
,
40 enum credentials_obtained obtained
);
42 /* Free a memory ccache */
43 static int free_mccache(struct ccache_container
*ccc
)
45 if (ccc
->ccache
!= NULL
) {
46 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
54 /* Free a disk-based ccache */
55 static int free_dccache(struct ccache_container
*ccc
)
57 if (ccc
->ccache
!= NULL
) {
58 krb5_cc_close(ccc
->smb_krb5_context
->krb5_context
,
66 static uint32_t smb_gss_krb5_copy_ccache(uint32_t *min_stat
,
68 struct ccache_container
*ccc
)
70 #ifndef SAMBA4_USES_HEIMDAL /* MIT 1.10 */
71 krb5_context context
= ccc
->smb_krb5_context
->krb5_context
;
72 krb5_ccache dummy_ccache
= NULL
;
73 krb5_creds creds
= {0};
74 krb5_cc_cursor cursor
= NULL
;
75 krb5_principal princ
= NULL
;
78 uint32_t maj_stat
= GSS_S_FAILURE
;
80 dummy_name
= talloc_asprintf(ccc
,
81 "MEMORY:gss_krb5_copy_ccache-%p",
83 if (dummy_name
== NULL
) {
89 * Create a dummy ccache, so we can iterate over the credentials
90 * and find the default principal for the ccache we want to
91 * copy. The new ccache needs to be initialized with this
94 code
= krb5_cc_resolve(context
, dummy_name
, &dummy_ccache
);
95 TALLOC_FREE(dummy_name
);
102 * We do not need set a default principal on the temporary dummy
103 * ccache, as we do consume it at all in this function.
105 maj_stat
= gss_krb5_copy_ccache(min_stat
, cred
, dummy_ccache
);
107 krb5_cc_close(context
, dummy_ccache
);
111 code
= krb5_cc_start_seq_get(context
, dummy_ccache
, &cursor
);
113 krb5_cc_close(context
, dummy_ccache
);
115 return GSS_S_FAILURE
;
118 code
= krb5_cc_next_cred(context
,
123 krb5_cc_close(context
, dummy_ccache
);
125 return GSS_S_FAILURE
;
129 if (creds
.ticket_flags
& TKT_FLG_PRE_AUTH
) {
132 tgs
= krb5_princ_component(context
,
135 if (tgs
!= NULL
&& tgs
->length
>= 1) {
138 cmp
= memcmp(tgs
->data
,
141 if (cmp
== 0 && creds
.client
!= NULL
) {
142 princ
= creds
.client
;
149 krb5_free_cred_contents(context
, &creds
);
151 code
= krb5_cc_next_cred(context
,
157 if (code
== KRB5_CC_END
) {
158 krb5_cc_end_seq_get(context
, dummy_ccache
, &cursor
);
161 krb5_cc_close(context
, dummy_ccache
);
163 if (code
!= 0 || princ
== NULL
) {
164 krb5_free_cred_contents(context
, &creds
);
166 return GSS_S_FAILURE
;
170 * Set the default principal for the cache we copy
171 * into. This is needed to be able that other calls
172 * can read it with e.g. gss_acquire_cred() or
173 * krb5_cc_get_principal().
175 code
= krb5_cc_initialize(context
, ccc
->ccache
, princ
);
177 krb5_free_cred_contents(context
, &creds
);
179 return GSS_S_FAILURE
;
181 krb5_free_cred_contents(context
, &creds
);
183 #endif /* SAMBA4_USES_HEIMDAL */
185 return gss_krb5_copy_ccache(min_stat
,
190 _PUBLIC_
int cli_credentials_get_krb5_context(struct cli_credentials
*cred
,
191 struct loadparm_context
*lp_ctx
,
192 struct smb_krb5_context
**smb_krb5_context
)
195 if (cred
->smb_krb5_context
) {
196 *smb_krb5_context
= cred
->smb_krb5_context
;
200 ret
= smb_krb5_init_context(cred
, lp_ctx
,
201 &cred
->smb_krb5_context
);
203 cred
->smb_krb5_context
= NULL
;
206 *smb_krb5_context
= cred
->smb_krb5_context
;
210 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
211 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
213 _PUBLIC_ NTSTATUS
cli_credentials_set_krb5_context(struct cli_credentials
*cred
,
214 struct smb_krb5_context
*smb_krb5_context
)
216 if (smb_krb5_context
== NULL
) {
217 talloc_unlink(cred
, cred
->smb_krb5_context
);
218 cred
->smb_krb5_context
= NULL
;
222 if (!talloc_reference(cred
, smb_krb5_context
)) {
223 return NT_STATUS_NO_MEMORY
;
225 cred
->smb_krb5_context
= smb_krb5_context
;
229 static int cli_credentials_set_from_ccache(struct cli_credentials
*cred
,
230 struct ccache_container
*ccache
,
231 enum credentials_obtained obtained
,
232 const char **error_string
)
236 krb5_principal princ
;
240 if (cred
->ccache_obtained
> obtained
) {
244 ret
= krb5_cc_get_principal(ccache
->smb_krb5_context
->krb5_context
,
245 ccache
->ccache
, &princ
);
248 (*error_string
) = talloc_asprintf(cred
, "failed to get principal from ccache: %s\n",
249 smb_get_krb5_error_message(ccache
->smb_krb5_context
->krb5_context
,
254 ret
= krb5_unparse_name(ccache
->smb_krb5_context
->krb5_context
, princ
, &name
);
256 (*error_string
) = talloc_asprintf(cred
, "failed to unparse principal from ccache: %s\n",
257 smb_get_krb5_error_message(ccache
->smb_krb5_context
->krb5_context
,
262 ok
= cli_credentials_set_principal(cred
, name
, obtained
);
263 krb5_free_unparsed_name(ccache
->smb_krb5_context
->krb5_context
, name
);
265 krb5_free_principal(ccache
->smb_krb5_context
->krb5_context
, princ
);
269 realm
= smb_krb5_principal_get_realm(ccache
->smb_krb5_context
->krb5_context
,
271 krb5_free_principal(ccache
->smb_krb5_context
->krb5_context
, princ
);
275 ok
= cli_credentials_set_realm(cred
, realm
, obtained
);
281 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
282 cred
->ccache_obtained
= obtained
;
287 _PUBLIC_
int cli_credentials_set_ccache(struct cli_credentials
*cred
,
288 struct loadparm_context
*lp_ctx
,
290 enum credentials_obtained obtained
,
291 const char **error_string
)
294 krb5_principal princ
;
295 struct ccache_container
*ccc
;
296 if (cred
->ccache_obtained
> obtained
) {
300 ccc
= talloc(cred
, struct ccache_container
);
302 (*error_string
) = error_message(ENOMEM
);
306 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
307 &ccc
->smb_krb5_context
);
309 (*error_string
) = error_message(ret
);
313 if (!talloc_reference(ccc
, ccc
->smb_krb5_context
)) {
315 (*error_string
) = error_message(ENOMEM
);
320 ret
= krb5_cc_resolve(ccc
->smb_krb5_context
->krb5_context
, name
, &ccc
->ccache
);
322 (*error_string
) = talloc_asprintf(cred
, "failed to read krb5 ccache: %s: %s\n",
324 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
,
330 ret
= 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
);
354 cred
->ccache_obtained
= obtained
;
355 talloc_steal(cred
, ccc
);
357 cli_credentials_invalidate_client_gss_creds(cred
, cred
->ccache_obtained
);
364 * Indicate the we failed to log in to this service/host with these
365 * credentials. The caller passes an unsigned int which they
366 * initialise to the number of times they would like to retry.
368 * This method is used to support re-trying with freshly fetched
369 * credentials in case a server is rebuilt while clients have
370 * non-expired tickets. When the client code gets a logon failure they
371 * throw away the existing credentials for the server and retry.
373 _PUBLIC_
bool cli_credentials_failed_kerberos_login(struct cli_credentials
*cred
,
374 const char *principal
,
377 struct ccache_container
*ccc
;
378 krb5_creds creds
, creds2
;
381 if (principal
== NULL
) {
382 /* no way to delete if we don't know the principal */
388 /* not a kerberos connection */
393 /* We have already tried discarding the credentials */
399 ret
= krb5_parse_name(ccc
->smb_krb5_context
->krb5_context
, principal
, &creds
.server
);
404 ret
= krb5_cc_retrieve_cred(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
, KRB5_TC_MATCH_SRV_NAMEONLY
, &creds
, &creds2
);
406 /* don't retry - we didn't find these credentials to remove */
407 krb5_free_cred_contents(ccc
->smb_krb5_context
->krb5_context
, &creds
);
411 ret
= krb5_cc_remove_cred(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
, KRB5_TC_MATCH_SRV_NAMEONLY
, &creds
);
412 krb5_free_cred_contents(ccc
->smb_krb5_context
->krb5_context
, &creds
);
413 krb5_free_cred_contents(ccc
->smb_krb5_context
->krb5_context
, &creds2
);
415 /* don't retry - we didn't find these credentials to
416 * remove. Note that with the current backend this
417 * never happens, as it always returns 0 even if the
418 * creds don't exist, which is why we do a separate
419 * krb5_cc_retrieve_cred() above.
427 static int cli_credentials_new_ccache(struct cli_credentials
*cred
,
428 struct loadparm_context
*lp_ctx
,
430 struct ccache_container
**_ccc
,
431 const char **error_string
)
433 bool must_free_cc_name
= false;
435 struct ccache_container
*ccc
= talloc(cred
, struct ccache_container
);
440 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
441 &ccc
->smb_krb5_context
);
444 (*error_string
) = talloc_asprintf(cred
, "Failed to get krb5_context: %s",
448 if (!talloc_reference(ccc
, ccc
->smb_krb5_context
)) {
450 (*error_string
) = strerror(ENOMEM
);
455 must_free_cc_name
= true;
457 if (lpcfg_parm_bool(lp_ctx
, NULL
, "credentials", "krb5_cc_file", false)) {
458 ccache_name
= talloc_asprintf(ccc
, "FILE:/tmp/krb5_cc_samba_%u_%p",
459 (unsigned int)getpid(), ccc
);
461 ccache_name
= talloc_asprintf(ccc
, "MEMORY:%p",
467 (*error_string
) = strerror(ENOMEM
);
472 ret
= krb5_cc_resolve(ccc
->smb_krb5_context
->krb5_context
, ccache_name
,
475 (*error_string
) = talloc_asprintf(cred
, "failed to resolve a krb5 ccache (%s): %s\n",
477 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
,
479 talloc_free(ccache_name
);
484 if (strncasecmp(ccache_name
, "MEMORY:", 7) == 0) {
485 talloc_set_destructor(ccc
, free_mccache
);
487 talloc_set_destructor(ccc
, free_dccache
);
490 if (must_free_cc_name
) {
491 talloc_free(ccache_name
);
499 _PUBLIC_
int cli_credentials_get_named_ccache(struct cli_credentials
*cred
,
500 struct tevent_context
*event_ctx
,
501 struct loadparm_context
*lp_ctx
,
503 struct ccache_container
**ccc
,
504 const char **error_string
)
507 enum credentials_obtained obtained
;
509 if (cred
->machine_account_pending
) {
510 cli_credentials_set_machine_account(cred
, lp_ctx
);
513 if (cred
->ccache_obtained
>= cred
->ccache_threshold
&&
514 cred
->ccache_obtained
> CRED_UNINITIALISED
) {
516 bool expired
= false;
517 ret
= smb_krb5_cc_get_lifetime(cred
->ccache
->smb_krb5_context
->krb5_context
,
518 cred
->ccache
->ccache
, &lifetime
);
519 if (ret
== KRB5_CC_END
) {
520 /* If we have a particular ccache set, without
521 * an initial ticket, then assume there is a
523 } else if (ret
== 0) {
525 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
526 cli_credentials_get_principal(cred
, cred
)));
528 } else if (lifetime
< 300) {
529 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
530 cli_credentials_get_principal(cred
, cred
), (unsigned int)lifetime
));
534 (*error_string
) = talloc_asprintf(cred
, "failed to get ccache lifetime: %s\n",
535 smb_get_krb5_error_message(cred
->ccache
->smb_krb5_context
->krb5_context
,
540 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
541 cli_credentials_get_principal(cred
, cred
), (unsigned int)lifetime
));
548 if (cli_credentials_is_anonymous(cred
)) {
549 (*error_string
) = "Cannot get anonymous kerberos credentials";
553 ret
= cli_credentials_new_ccache(cred
, lp_ctx
, ccache_name
, ccc
, error_string
);
558 ret
= kinit_to_ccache(cred
, cred
, (*ccc
)->smb_krb5_context
, event_ctx
, (*ccc
)->ccache
, &obtained
, error_string
);
563 ret
= cli_credentials_set_from_ccache(cred
, *ccc
,
564 obtained
, error_string
);
567 cred
->ccache_obtained
= cred
->principal_obtained
;
571 cli_credentials_invalidate_client_gss_creds(cred
, cred
->ccache_obtained
);
575 _PUBLIC_
int cli_credentials_get_ccache(struct cli_credentials
*cred
,
576 struct tevent_context
*event_ctx
,
577 struct loadparm_context
*lp_ctx
,
578 struct ccache_container
**ccc
,
579 const char **error_string
)
581 return cli_credentials_get_named_ccache(cred
, event_ctx
, lp_ctx
, NULL
, ccc
, error_string
);
584 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
585 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials
*cred
)
587 if (cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
588 talloc_unlink(cred
, cred
->client_gss_creds
);
589 cred
->client_gss_creds
= NULL
;
591 cred
->client_gss_creds_obtained
= CRED_UNINITIALISED
;
594 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials
*cred
,
595 enum credentials_obtained obtained
)
597 /* If the caller just changed the username/password etc, then
598 * any cached credentials are now invalid */
599 if (obtained
>= cred
->client_gss_creds_obtained
) {
600 if (cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
601 talloc_unlink(cred
, cred
->client_gss_creds
);
602 cred
->client_gss_creds
= NULL
;
604 cred
->client_gss_creds_obtained
= CRED_UNINITIALISED
;
606 /* Now that we know that the data is 'this specified', then
607 * don't allow something less 'known' to be returned as a
608 * ccache. Ie, if the username is on the command line, we
609 * don't want to later guess to use a file-based ccache */
610 if (obtained
> cred
->client_gss_creds_threshold
) {
611 cred
->client_gss_creds_threshold
= obtained
;
615 /* We have good reason to think this CCACHE is invalid. Blow it away */
616 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials
*cred
)
618 if (cred
->ccache_obtained
> CRED_UNINITIALISED
) {
619 talloc_unlink(cred
, cred
->ccache
);
622 cred
->ccache_obtained
= CRED_UNINITIALISED
;
624 cli_credentials_unconditionally_invalidate_client_gss_creds(cred
);
627 _PUBLIC_
void cli_credentials_invalidate_ccache(struct cli_credentials
*cred
,
628 enum credentials_obtained obtained
)
630 /* If the caller just changed the username/password etc, then
631 * any cached credentials are now invalid */
632 if (obtained
>= cred
->ccache_obtained
) {
633 if (cred
->ccache_obtained
> CRED_UNINITIALISED
) {
634 talloc_unlink(cred
, cred
->ccache
);
637 cred
->ccache_obtained
= CRED_UNINITIALISED
;
639 /* Now that we know that the data is 'this specified', then
640 * don't allow something less 'known' to be returned as a
641 * ccache. i.e, if the username is on the command line, we
642 * don't want to later guess to use a file-based ccache */
643 if (obtained
> cred
->ccache_threshold
) {
644 cred
->ccache_threshold
= obtained
;
647 cli_credentials_invalidate_client_gss_creds(cred
,
651 static int free_gssapi_creds(struct gssapi_creds_container
*gcc
)
654 (void)gss_release_cred(&min_stat
, &gcc
->creds
);
658 _PUBLIC_
int cli_credentials_get_client_gss_creds(struct cli_credentials
*cred
,
659 struct tevent_context
*event_ctx
,
660 struct loadparm_context
*lp_ctx
,
661 struct gssapi_creds_container
**_gcc
,
662 const char **error_string
)
665 OM_uint32 maj_stat
, min_stat
;
666 struct gssapi_creds_container
*gcc
;
667 struct ccache_container
*ccache
;
668 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
669 gss_buffer_desc empty_buffer
= GSS_C_EMPTY_BUFFER
;
670 gss_OID oid
= discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X
);
672 krb5_enctype
*etypes
= NULL
;
674 if (cred
->client_gss_creds_obtained
>= cred
->client_gss_creds_threshold
&&
675 cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
676 bool expired
= false;
677 OM_uint32 lifetime
= 0;
678 gss_cred_usage_t usage
= 0;
679 maj_stat
= gss_inquire_cred(&min_stat
, cred
->client_gss_creds
->creds
,
680 NULL
, &lifetime
, &usage
, NULL
);
681 if (maj_stat
== GSS_S_CREDENTIALS_EXPIRED
) {
682 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred
, cred
)));
684 } else if (maj_stat
== GSS_S_COMPLETE
&& lifetime
< 300) {
685 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred
, cred
), lifetime
));
687 } else if (maj_stat
!= GSS_S_COMPLETE
) {
688 *error_string
= talloc_asprintf(cred
, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
689 gssapi_error_string(cred
, maj_stat
, min_stat
, NULL
));
693 cli_credentials_unconditionally_invalidate_client_gss_creds(cred
);
695 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
696 cli_credentials_get_principal(cred
, cred
), (unsigned int)lifetime
));
698 *_gcc
= cred
->client_gss_creds
;
703 ret
= cli_credentials_get_ccache(cred
, event_ctx
, lp_ctx
,
704 &ccache
, error_string
);
706 if (cli_credentials_get_kerberos_state(cred
) == CRED_MUST_USE_KERBEROS
) {
707 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string
));
709 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string
));
714 gcc
= talloc(cred
, struct gssapi_creds_container
);
716 (*error_string
) = error_message(ENOMEM
);
720 maj_stat
= smb_gss_krb5_import_cred(&min_stat
, ccache
->smb_krb5_context
->krb5_context
,
721 ccache
->ccache
, NULL
, NULL
,
723 if ((maj_stat
== GSS_S_FAILURE
) &&
724 (min_stat
== (OM_uint32
)KRB5_CC_END
||
725 min_stat
== (OM_uint32
)KRB5_CC_NOTFOUND
||
726 min_stat
== (OM_uint32
)KRB5_FCC_NOFILE
))
728 /* This CCACHE is no good. Ensure we don't use it again */
729 cli_credentials_unconditionally_invalidate_ccache(cred
);
731 /* Now try again to get a ccache */
732 ret
= cli_credentials_get_ccache(cred
, event_ctx
, lp_ctx
,
733 &ccache
, error_string
);
735 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret
)));
739 maj_stat
= smb_gss_krb5_import_cred(&min_stat
, ccache
->smb_krb5_context
->krb5_context
,
740 ccache
->ccache
, NULL
, NULL
,
752 (*error_string
) = talloc_asprintf(cred
, "smb_gss_krb5_import_cred failed: %s", error_message(ret
));
758 * transfer the enctypes from the smb_krb5_context to the gssapi layer
760 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
761 * to configure the enctypes via the krb5.conf.
763 * And the gss_init_sec_context() creates it's own krb5_context and
764 * the TGS-REQ had all enctypes in it and only the ones configured
765 * and used for the AS-REQ, so it wasn't possible to disable the usage
768 min_stat
= smb_krb5_get_allowed_etypes(ccache
->smb_krb5_context
->krb5_context
,
771 OM_uint32 num_ktypes
;
773 for (num_ktypes
= 0; etypes
[num_ktypes
]; num_ktypes
++);
775 maj_stat
= gss_krb5_set_allowable_enctypes(&min_stat
, gcc
->creds
,
786 (*error_string
) = talloc_asprintf(cred
, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret
));
791 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
793 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
795 * This allows us to disable SIGN and SEAL on a TLS connection with
796 * GSS-SPNENO. For example ldaps:// connections.
798 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
799 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
801 maj_stat
= gss_set_cred_option(&min_stat
, &gcc
->creds
,
811 (*error_string
) = talloc_asprintf(cred
, "gss_set_cred_option failed: %s", error_message(ret
));
815 cred
->client_gss_creds_obtained
= cred
->ccache_obtained
;
816 talloc_set_destructor(gcc
, free_gssapi_creds
);
817 cred
->client_gss_creds
= gcc
;
823 Set a gssapi cred_id_t into the credentials system. (Client case)
825 This grabs the credentials both 'intact' and getting the krb5
826 ccache out of it. This routine can be generalised in future for
827 the case where we deal with GSSAPI mechs other than krb5.
829 On sucess, the caller must not free gssapi_cred, as it now belongs
830 to the credentials system.
833 int cli_credentials_set_client_gss_creds(struct cli_credentials
*cred
,
834 struct loadparm_context
*lp_ctx
,
835 gss_cred_id_t gssapi_cred
,
836 enum credentials_obtained obtained
,
837 const char **error_string
)
840 OM_uint32 maj_stat
, min_stat
;
841 struct ccache_container
*ccc
= NULL
;
842 struct gssapi_creds_container
*gcc
= NULL
;
843 if (cred
->client_gss_creds_obtained
> obtained
) {
847 gcc
= talloc(cred
, struct gssapi_creds_container
);
849 (*error_string
) = error_message(ENOMEM
);
853 ret
= cli_credentials_new_ccache(cred
, lp_ctx
, NULL
, &ccc
, error_string
);
858 maj_stat
= smb_gss_krb5_copy_ccache(&min_stat
,
868 (*error_string
) = error_message(ENOMEM
);
873 ret
= cli_credentials_set_from_ccache(cred
, ccc
, obtained
, error_string
);
876 cred
->ccache_obtained
= obtained
;
878 gcc
->creds
= gssapi_cred
;
879 talloc_set_destructor(gcc
, free_gssapi_creds
);
881 /* set the clinet_gss_creds_obtained here, as it just
882 got set to UNINITIALISED by the calls above */
883 cred
->client_gss_creds_obtained
= obtained
;
884 cred
->client_gss_creds
= gcc
;
889 static int cli_credentials_shallow_ccache(struct cli_credentials
*cred
)
892 const struct ccache_container
*old_ccc
= NULL
;
893 struct ccache_container
*ccc
= NULL
;
894 char *ccache_name
= NULL
;
896 old_ccc
= cred
->ccache
;
897 if (old_ccc
== NULL
) {
901 ccc
= talloc(cred
, struct ccache_container
);
908 ccache_name
= talloc_asprintf(ccc
, "MEMORY:%p", ccc
);
910 ret
= krb5_cc_resolve(ccc
->smb_krb5_context
->krb5_context
,
911 ccache_name
, &ccc
->ccache
);
917 talloc_set_destructor(ccc
, free_mccache
);
919 TALLOC_FREE(ccache_name
);
921 ret
= smb_krb5_cc_copy_creds(ccc
->smb_krb5_context
->krb5_context
,
922 old_ccc
->ccache
, ccc
->ccache
);
929 cred
->client_gss_creds
= NULL
;
930 cred
->client_gss_creds_obtained
= CRED_UNINITIALISED
;
934 _PUBLIC_
struct cli_credentials
*cli_credentials_shallow_copy(TALLOC_CTX
*mem_ctx
,
935 struct cli_credentials
*src
)
937 struct cli_credentials
*dst
;
940 dst
= talloc(mem_ctx
, struct cli_credentials
);
947 ret
= cli_credentials_shallow_ccache(dst
);
956 /* Get the keytab (actually, a container containing the krb5_keytab)
957 * attached to this context. If this hasn't been done or set before,
958 * it will be generated from the password.
960 _PUBLIC_
int cli_credentials_get_keytab(struct cli_credentials
*cred
,
961 struct loadparm_context
*lp_ctx
,
962 struct keytab_container
**_ktc
)
965 struct keytab_container
*ktc
;
966 struct smb_krb5_context
*smb_krb5_context
;
967 const char *keytab_name
;
970 const char *username
= cli_credentials_get_username(cred
);
971 const char *upn
= NULL
;
972 const char *realm
= cli_credentials_get_realm(cred
);
973 char *salt_principal
= NULL
;
974 bool is_computer
= false;
976 if (cred
->keytab_obtained
>= (MAX(cred
->principal_obtained
,
977 cred
->username_obtained
))) {
978 *_ktc
= cred
->keytab
;
982 if (cli_credentials_is_anonymous(cred
)) {
986 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
992 mem_ctx
= talloc_new(cred
);
997 switch (cred
->secure_channel_type
) {
1004 upn
= cli_credentials_get_principal(cred
, mem_ctx
);
1006 TALLOC_FREE(mem_ctx
);
1012 ret
= smb_krb5_salt_principal(realm
,
1013 username
, /* sAMAccountName */
1014 upn
, /* userPrincipalName */
1019 talloc_free(mem_ctx
);
1023 ret
= smb_krb5_create_memory_keytab(mem_ctx
,
1024 smb_krb5_context
->krb5_context
,
1025 cli_credentials_get_password(cred
),
1029 cli_credentials_get_kvno(cred
),
1033 talloc_free(mem_ctx
);
1037 ret
= smb_krb5_get_keytab_container(mem_ctx
, smb_krb5_context
,
1038 keytab
, keytab_name
, &ktc
);
1040 talloc_free(mem_ctx
);
1044 cred
->keytab_obtained
= (MAX(cred
->principal_obtained
,
1045 cred
->username_obtained
));
1047 /* We make this keytab up based on a password. Therefore
1048 * match-by-key is acceptable, we can't match on the wrong
1050 ktc
->password_based
= true;
1052 talloc_steal(cred
, ktc
);
1054 *_ktc
= cred
->keytab
;
1055 talloc_free(mem_ctx
);
1059 /* Given the name of a keytab (presumably in the format
1060 * FILE:/etc/krb5.keytab), open it and attach it */
1062 _PUBLIC_
int cli_credentials_set_keytab_name(struct cli_credentials
*cred
,
1063 struct loadparm_context
*lp_ctx
,
1064 const char *keytab_name
,
1065 enum credentials_obtained obtained
)
1067 krb5_error_code ret
;
1068 struct keytab_container
*ktc
;
1069 struct smb_krb5_context
*smb_krb5_context
;
1070 TALLOC_CTX
*mem_ctx
;
1072 if (cred
->keytab_obtained
>= obtained
) {
1076 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
, &smb_krb5_context
);
1081 mem_ctx
= talloc_new(cred
);
1086 ret
= smb_krb5_get_keytab_container(mem_ctx
, smb_krb5_context
,
1087 NULL
, keytab_name
, &ktc
);
1092 cred
->keytab_obtained
= obtained
;
1094 talloc_steal(cred
, ktc
);
1096 talloc_free(mem_ctx
);
1101 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1103 _PUBLIC_
int cli_credentials_get_server_gss_creds(struct cli_credentials
*cred
,
1104 struct loadparm_context
*lp_ctx
,
1105 struct gssapi_creds_container
**_gcc
)
1108 OM_uint32 maj_stat
, min_stat
;
1109 struct gssapi_creds_container
*gcc
;
1110 struct keytab_container
*ktc
;
1111 struct smb_krb5_context
*smb_krb5_context
;
1112 TALLOC_CTX
*mem_ctx
;
1113 krb5_principal princ
;
1114 const char *error_string
;
1115 enum credentials_obtained obtained
;
1117 mem_ctx
= talloc_new(cred
);
1122 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
, &smb_krb5_context
);
1127 ret
= principal_from_credentials(mem_ctx
, cred
, smb_krb5_context
, &princ
, &obtained
, &error_string
);
1129 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1131 talloc_free(mem_ctx
);
1135 if (cred
->server_gss_creds_obtained
>= (MAX(cred
->keytab_obtained
, obtained
))) {
1136 talloc_free(mem_ctx
);
1137 *_gcc
= cred
->server_gss_creds
;
1141 ret
= cli_credentials_get_keytab(cred
, lp_ctx
, &ktc
);
1143 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret
)));
1147 gcc
= talloc(cred
, struct gssapi_creds_container
);
1149 talloc_free(mem_ctx
);
1153 if (ktc
->password_based
|| obtained
< CRED_SPECIFIED
) {
1154 /* This creates a GSSAPI cred_id_t for match-by-key with only the keytab set */
1155 maj_stat
= smb_gss_krb5_import_cred(&min_stat
, smb_krb5_context
->krb5_context
,
1156 NULL
, NULL
, ktc
->keytab
,
1159 /* This creates a GSSAPI cred_id_t with the principal and keytab set, matching by name */
1160 maj_stat
= smb_gss_krb5_import_cred(&min_stat
, smb_krb5_context
->krb5_context
,
1161 NULL
, princ
, ktc
->keytab
,
1172 cred
->server_gss_creds_obtained
= cred
->keytab_obtained
;
1173 talloc_set_destructor(gcc
, free_gssapi_creds
);
1174 cred
->server_gss_creds
= gcc
;
1177 talloc_free(mem_ctx
);
1185 _PUBLIC_
void cli_credentials_set_kvno(struct cli_credentials
*cred
,
1192 * Return Kerberos KVNO
1195 _PUBLIC_
int cli_credentials_get_kvno(struct cli_credentials
*cred
)
1201 const char *cli_credentials_get_salt_principal(struct cli_credentials
*cred
)
1203 return cred
->salt_principal
;
1206 _PUBLIC_
void cli_credentials_set_salt_principal(struct cli_credentials
*cred
, const char *principal
)
1208 talloc_free(cred
->salt_principal
);
1209 cred
->salt_principal
= talloc_strdup(cred
, principal
);
1212 /* The 'impersonate_principal' is used to allow one Kerberos principal
1213 * (and it's associated keytab etc) to impersonate another. The
1214 * ability to do this is controlled by the KDC, but it is generally
1215 * permitted to impersonate anyone to yourself. This allows any
1216 * member of the domain to get the groups of a user. This is also
1217 * known as S4U2Self */
1219 _PUBLIC_
const char *cli_credentials_get_impersonate_principal(struct cli_credentials
*cred
)
1221 return cred
->impersonate_principal
;
1225 * The 'self_service' is the service principal that
1226 * represents the same object (by its objectSid)
1227 * as the client principal (typically our machine account).
1228 * When trying to impersonate 'impersonate_principal' with
1231 _PUBLIC_
const char *cli_credentials_get_self_service(struct cli_credentials
*cred
)
1233 return cred
->self_service
;
1236 _PUBLIC_
void cli_credentials_set_impersonate_principal(struct cli_credentials
*cred
,
1237 const char *principal
,
1238 const char *self_service
)
1240 talloc_free(cred
->impersonate_principal
);
1241 cred
->impersonate_principal
= talloc_strdup(cred
, principal
);
1242 talloc_free(cred
->self_service
);
1243 cred
->self_service
= talloc_strdup(cred
, self_service
);
1244 cli_credentials_set_kerberos_state(cred
, CRED_MUST_USE_KERBEROS
);
1248 * when impersonating for S4U2proxy we need to set the target principal.
1249 * Similarly, we may only be authorized to do general impersonation to
1250 * some particular services.
1252 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1254 * NULL means that tickets will be obtained for the krbtgt service.
1257 const char *cli_credentials_get_target_service(struct cli_credentials
*cred
)
1259 return cred
->target_service
;
1262 _PUBLIC_
void cli_credentials_set_target_service(struct cli_credentials
*cred
, const char *target_service
)
1264 talloc_free(cred
->target_service
);
1265 cred
->target_service
= talloc_strdup(cred
, target_service
);