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
);
957 static int smb_krb5_create_salt_principal(TALLOC_CTX
*mem_ctx
,
958 const char *samAccountName
,
960 const char **salt_principal
,
961 const char **error_string
)
963 char *machine_username
;
964 bool is_machine_account
= false;
969 if (samAccountName
== NULL
) {
970 *error_string
= "Cannot determine salt principal, no "
971 "saltPrincipal or samAccountName specified";
976 *error_string
= "Cannot make principal without a realm";
980 tmp_ctx
= talloc_new(mem_ctx
);
981 if (tmp_ctx
== NULL
) {
982 *error_string
= "Cannot allocate talloc context";
986 upper_realm
= strupper_talloc(tmp_ctx
, realm
);
987 if (upper_realm
== NULL
) {
988 *error_string
= "Cannot allocate to upper case realm";
992 machine_username
= strlower_talloc(tmp_ctx
, samAccountName
);
993 if (!machine_username
) {
994 *error_string
= "Cannot duplicate samAccountName";
998 if (machine_username
[strlen(machine_username
) - 1] == '$') {
999 machine_username
[strlen(machine_username
) - 1] = '\0';
1000 is_machine_account
= true;
1003 if (is_machine_account
) {
1006 lower_realm
= strlower_talloc(tmp_ctx
, realm
);
1007 if (lower_realm
== NULL
) {
1008 *error_string
= "Cannot allocate to lower case realm";
1012 *salt_principal
= talloc_asprintf(mem_ctx
,
1018 *salt_principal
= talloc_asprintf(mem_ctx
,
1023 if (*salt_principal
== NULL
) {
1024 *error_string
= "Cannot create salt principal";
1030 talloc_free(tmp_ctx
);
1035 /* Get the keytab (actually, a container containing the krb5_keytab)
1036 * attached to this context. If this hasn't been done or set before,
1037 * it will be generated from the password.
1039 _PUBLIC_
int cli_credentials_get_keytab(struct cli_credentials
*cred
,
1040 struct loadparm_context
*lp_ctx
,
1041 struct keytab_container
**_ktc
)
1043 krb5_error_code ret
;
1044 struct keytab_container
*ktc
;
1045 struct smb_krb5_context
*smb_krb5_context
;
1046 const char *keytab_name
;
1048 TALLOC_CTX
*mem_ctx
;
1049 const char *username
= cli_credentials_get_username(cred
);
1050 const char *upn
= NULL
;
1051 const char *realm
= cli_credentials_get_realm(cred
);
1052 char *salt_principal
= NULL
;
1053 bool is_computer
= false;
1055 if (cred
->keytab_obtained
>= (MAX(cred
->principal_obtained
,
1056 cred
->username_obtained
))) {
1057 *_ktc
= cred
->keytab
;
1061 if (cli_credentials_is_anonymous(cred
)) {
1065 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
1071 mem_ctx
= talloc_new(cred
);
1076 switch (cred
->secure_channel_type
) {
1077 case SEC_CHAN_WKSTA
:
1083 upn
= cli_credentials_get_principal(cred
, mem_ctx
);
1085 TALLOC_FREE(mem_ctx
);
1091 ret
= smb_krb5_salt_principal(realm
,
1092 username
, /* sAMAccountName */
1093 upn
, /* userPrincipalName */
1098 talloc_free(mem_ctx
);
1102 ret
= smb_krb5_create_memory_keytab(mem_ctx
,
1103 smb_krb5_context
->krb5_context
,
1104 cli_credentials_get_password(cred
),
1108 cli_credentials_get_kvno(cred
),
1112 talloc_free(mem_ctx
);
1116 ret
= smb_krb5_get_keytab_container(mem_ctx
, smb_krb5_context
,
1117 keytab
, keytab_name
, &ktc
);
1119 talloc_free(mem_ctx
);
1123 cred
->keytab_obtained
= (MAX(cred
->principal_obtained
,
1124 cred
->username_obtained
));
1126 /* We make this keytab up based on a password. Therefore
1127 * match-by-key is acceptable, we can't match on the wrong
1129 ktc
->password_based
= true;
1131 talloc_steal(cred
, ktc
);
1133 *_ktc
= cred
->keytab
;
1134 talloc_free(mem_ctx
);
1138 /* Given the name of a keytab (presumably in the format
1139 * FILE:/etc/krb5.keytab), open it and attach it */
1141 _PUBLIC_
int cli_credentials_set_keytab_name(struct cli_credentials
*cred
,
1142 struct loadparm_context
*lp_ctx
,
1143 const char *keytab_name
,
1144 enum credentials_obtained obtained
)
1146 krb5_error_code ret
;
1147 struct keytab_container
*ktc
;
1148 struct smb_krb5_context
*smb_krb5_context
;
1149 TALLOC_CTX
*mem_ctx
;
1151 if (cred
->keytab_obtained
>= obtained
) {
1155 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
, &smb_krb5_context
);
1160 mem_ctx
= talloc_new(cred
);
1165 ret
= smb_krb5_get_keytab_container(mem_ctx
, smb_krb5_context
,
1166 NULL
, keytab_name
, &ktc
);
1171 cred
->keytab_obtained
= obtained
;
1173 talloc_steal(cred
, ktc
);
1175 talloc_free(mem_ctx
);
1180 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1182 _PUBLIC_
int cli_credentials_get_server_gss_creds(struct cli_credentials
*cred
,
1183 struct loadparm_context
*lp_ctx
,
1184 struct gssapi_creds_container
**_gcc
)
1187 OM_uint32 maj_stat
, min_stat
;
1188 struct gssapi_creds_container
*gcc
;
1189 struct keytab_container
*ktc
;
1190 struct smb_krb5_context
*smb_krb5_context
;
1191 TALLOC_CTX
*mem_ctx
;
1192 krb5_principal princ
;
1193 const char *error_string
;
1194 enum credentials_obtained obtained
;
1196 mem_ctx
= talloc_new(cred
);
1201 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
, &smb_krb5_context
);
1206 ret
= principal_from_credentials(mem_ctx
, cred
, smb_krb5_context
, &princ
, &obtained
, &error_string
);
1208 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1210 talloc_free(mem_ctx
);
1214 if (cred
->server_gss_creds_obtained
>= (MAX(cred
->keytab_obtained
, obtained
))) {
1215 talloc_free(mem_ctx
);
1216 *_gcc
= cred
->server_gss_creds
;
1220 ret
= cli_credentials_get_keytab(cred
, lp_ctx
, &ktc
);
1222 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret
)));
1226 gcc
= talloc(cred
, struct gssapi_creds_container
);
1228 talloc_free(mem_ctx
);
1232 if (ktc
->password_based
|| obtained
< CRED_SPECIFIED
) {
1233 /* This creates a GSSAPI cred_id_t for match-by-key with only the keytab set */
1234 maj_stat
= smb_gss_krb5_import_cred(&min_stat
, smb_krb5_context
->krb5_context
,
1235 NULL
, NULL
, ktc
->keytab
,
1238 /* This creates a GSSAPI cred_id_t with the principal and keytab set, matching by name */
1239 maj_stat
= smb_gss_krb5_import_cred(&min_stat
, smb_krb5_context
->krb5_context
,
1240 NULL
, princ
, ktc
->keytab
,
1251 cred
->server_gss_creds_obtained
= cred
->keytab_obtained
;
1252 talloc_set_destructor(gcc
, free_gssapi_creds
);
1253 cred
->server_gss_creds
= gcc
;
1256 talloc_free(mem_ctx
);
1264 _PUBLIC_
void cli_credentials_set_kvno(struct cli_credentials
*cred
,
1271 * Return Kerberos KVNO
1274 _PUBLIC_
int cli_credentials_get_kvno(struct cli_credentials
*cred
)
1280 const char *cli_credentials_get_salt_principal(struct cli_credentials
*cred
)
1282 return cred
->salt_principal
;
1285 _PUBLIC_
void cli_credentials_set_salt_principal(struct cli_credentials
*cred
, const char *principal
)
1287 talloc_free(cred
->salt_principal
);
1288 cred
->salt_principal
= talloc_strdup(cred
, principal
);
1291 /* The 'impersonate_principal' is used to allow one Kerberos principal
1292 * (and it's associated keytab etc) to impersonate another. The
1293 * ability to do this is controlled by the KDC, but it is generally
1294 * permitted to impersonate anyone to yourself. This allows any
1295 * member of the domain to get the groups of a user. This is also
1296 * known as S4U2Self */
1298 _PUBLIC_
const char *cli_credentials_get_impersonate_principal(struct cli_credentials
*cred
)
1300 return cred
->impersonate_principal
;
1304 * The 'self_service' is the service principal that
1305 * represents the same object (by its objectSid)
1306 * as the client principal (typically our machine account).
1307 * When trying to impersonate 'impersonate_principal' with
1310 _PUBLIC_
const char *cli_credentials_get_self_service(struct cli_credentials
*cred
)
1312 return cred
->self_service
;
1315 _PUBLIC_
void cli_credentials_set_impersonate_principal(struct cli_credentials
*cred
,
1316 const char *principal
,
1317 const char *self_service
)
1319 talloc_free(cred
->impersonate_principal
);
1320 cred
->impersonate_principal
= talloc_strdup(cred
, principal
);
1321 talloc_free(cred
->self_service
);
1322 cred
->self_service
= talloc_strdup(cred
, self_service
);
1323 cli_credentials_set_kerberos_state(cred
, CRED_MUST_USE_KERBEROS
);
1327 * when impersonating for S4U2proxy we need to set the target principal.
1328 * Similarly, we may only be authorized to do general impersonation to
1329 * some particular services.
1331 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1333 * NULL means that tickets will be obtained for the krbtgt service.
1336 const char *cli_credentials_get_target_service(struct cli_credentials
*cred
)
1338 return cred
->target_service
;
1341 _PUBLIC_
void cli_credentials_set_target_service(struct cli_credentials
*cred
, const char *target_service
)
1343 talloc_free(cred
->target_service
);
1344 cred
->target_service
= talloc_strdup(cred
, target_service
);