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"
37 #include "../libds/common/flags.h"
40 #define DBGC_CLASS DBGC_AUTH
42 static void cli_credentials_invalidate_client_gss_creds(
43 struct cli_credentials
*cred
,
44 enum credentials_obtained obtained
);
46 /* Free a memory ccache */
47 static int free_mccache(struct ccache_container
*ccc
)
49 if (ccc
->ccache
!= NULL
) {
50 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
,
58 /* Free a disk-based ccache */
59 static int free_dccache(struct ccache_container
*ccc
)
61 if (ccc
->ccache
!= NULL
) {
62 krb5_cc_close(ccc
->smb_krb5_context
->krb5_context
,
70 static uint32_t smb_gss_krb5_copy_ccache(uint32_t *min_stat
,
72 struct ccache_container
*ccc
)
74 #ifndef SAMBA4_USES_HEIMDAL /* MIT 1.10 */
75 krb5_context context
= ccc
->smb_krb5_context
->krb5_context
;
76 krb5_ccache dummy_ccache
= NULL
;
77 krb5_creds creds
= {0};
78 krb5_cc_cursor cursor
= NULL
;
79 krb5_principal princ
= NULL
;
82 uint32_t maj_stat
= GSS_S_FAILURE
;
84 dummy_name
= talloc_asprintf(ccc
,
85 "MEMORY:gss_krb5_copy_ccache-%p",
87 if (dummy_name
== NULL
) {
93 * Create a dummy ccache, so we can iterate over the credentials
94 * and find the default principal for the ccache we want to
95 * copy. The new ccache needs to be initialized with this
98 code
= krb5_cc_resolve(context
, dummy_name
, &dummy_ccache
);
99 TALLOC_FREE(dummy_name
);
102 return GSS_S_FAILURE
;
106 * We do not need set a default principal on the temporary dummy
107 * ccache, as we do consume it at all in this function.
109 maj_stat
= gss_krb5_copy_ccache(min_stat
, cred
, dummy_ccache
);
111 krb5_cc_close(context
, dummy_ccache
);
115 code
= krb5_cc_start_seq_get(context
, dummy_ccache
, &cursor
);
117 krb5_cc_close(context
, dummy_ccache
);
119 return GSS_S_FAILURE
;
122 code
= krb5_cc_next_cred(context
,
127 krb5_cc_close(context
, dummy_ccache
);
129 return GSS_S_FAILURE
;
133 if (creds
.ticket_flags
& TKT_FLG_PRE_AUTH
) {
136 tgs
= krb5_princ_component(context
,
139 if (tgs
!= NULL
&& tgs
->length
>= 1) {
142 cmp
= memcmp(tgs
->data
,
145 if (cmp
== 0 && creds
.client
!= NULL
) {
146 princ
= creds
.client
;
153 krb5_free_cred_contents(context
, &creds
);
155 code
= krb5_cc_next_cred(context
,
161 if (code
== KRB5_CC_END
) {
162 krb5_cc_end_seq_get(context
, dummy_ccache
, &cursor
);
165 krb5_cc_close(context
, dummy_ccache
);
167 if (code
!= 0 || princ
== NULL
) {
168 krb5_free_cred_contents(context
, &creds
);
170 return GSS_S_FAILURE
;
174 * Set the default principal for the cache we copy
175 * into. This is needed to be able that other calls
176 * can read it with e.g. gss_acquire_cred() or
177 * krb5_cc_get_principal().
179 code
= krb5_cc_initialize(context
, ccc
->ccache
, princ
);
181 krb5_free_cred_contents(context
, &creds
);
183 return GSS_S_FAILURE
;
185 krb5_free_cred_contents(context
, &creds
);
187 #endif /* SAMBA4_USES_HEIMDAL */
189 return gss_krb5_copy_ccache(min_stat
,
194 _PUBLIC_
int cli_credentials_get_krb5_context(struct cli_credentials
*cred
,
195 struct loadparm_context
*lp_ctx
,
196 struct smb_krb5_context
**smb_krb5_context
)
199 if (cred
->smb_krb5_context
) {
200 *smb_krb5_context
= cred
->smb_krb5_context
;
204 ret
= smb_krb5_init_context(cred
, lp_ctx
,
205 &cred
->smb_krb5_context
);
207 cred
->smb_krb5_context
= NULL
;
210 *smb_krb5_context
= cred
->smb_krb5_context
;
214 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
215 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
217 _PUBLIC_ NTSTATUS
cli_credentials_set_krb5_context(struct cli_credentials
*cred
,
218 struct smb_krb5_context
*smb_krb5_context
)
220 if (smb_krb5_context
== NULL
) {
221 talloc_unlink(cred
, cred
->smb_krb5_context
);
222 cred
->smb_krb5_context
= NULL
;
226 if (!talloc_reference(cred
, smb_krb5_context
)) {
227 return NT_STATUS_NO_MEMORY
;
229 cred
->smb_krb5_context
= smb_krb5_context
;
233 static int cli_credentials_set_from_ccache(struct cli_credentials
*cred
,
234 struct ccache_container
*ccache
,
235 enum credentials_obtained obtained
,
236 const char **error_string
)
240 krb5_principal princ
;
244 if (cred
->ccache_obtained
> obtained
) {
248 ret
= krb5_cc_get_principal(ccache
->smb_krb5_context
->krb5_context
,
249 ccache
->ccache
, &princ
);
252 (*error_string
) = talloc_asprintf(cred
, "failed to get principal from ccache: %s\n",
253 smb_get_krb5_error_message(ccache
->smb_krb5_context
->krb5_context
,
258 ret
= krb5_unparse_name(ccache
->smb_krb5_context
->krb5_context
, princ
, &name
);
260 (*error_string
) = talloc_asprintf(cred
, "failed to unparse principal from ccache: %s\n",
261 smb_get_krb5_error_message(ccache
->smb_krb5_context
->krb5_context
,
266 ok
= cli_credentials_set_principal(cred
, name
, obtained
);
267 krb5_free_unparsed_name(ccache
->smb_krb5_context
->krb5_context
, name
);
269 krb5_free_principal(ccache
->smb_krb5_context
->krb5_context
, princ
);
273 realm
= smb_krb5_principal_get_realm(ccache
->smb_krb5_context
->krb5_context
,
275 krb5_free_principal(ccache
->smb_krb5_context
->krb5_context
, princ
);
279 ok
= cli_credentials_set_realm(cred
, realm
, obtained
);
285 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
286 cred
->ccache_obtained
= obtained
;
291 _PUBLIC_
int cli_credentials_set_ccache(struct cli_credentials
*cred
,
292 struct loadparm_context
*lp_ctx
,
294 enum credentials_obtained obtained
,
295 const char **error_string
)
298 krb5_principal princ
;
299 struct ccache_container
*ccc
;
300 if (cred
->ccache_obtained
> obtained
) {
304 ccc
= talloc(cred
, struct ccache_container
);
306 (*error_string
) = error_message(ENOMEM
);
310 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
311 &ccc
->smb_krb5_context
);
313 (*error_string
) = error_message(ret
);
317 if (!talloc_reference(ccc
, ccc
->smb_krb5_context
)) {
319 (*error_string
) = error_message(ENOMEM
);
324 ret
= krb5_cc_resolve(ccc
->smb_krb5_context
->krb5_context
, name
, &ccc
->ccache
);
326 (*error_string
) = talloc_asprintf(cred
, "failed to read krb5 ccache: %s: %s\n",
328 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
,
334 ret
= krb5_cc_default(ccc
->smb_krb5_context
->krb5_context
, &ccc
->ccache
);
336 (*error_string
) = talloc_asprintf(cred
, "failed to read default krb5 ccache: %s\n",
337 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
,
344 talloc_set_destructor(ccc
, free_dccache
);
346 ret
= krb5_cc_get_principal(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
, &princ
);
349 krb5_free_principal(ccc
->smb_krb5_context
->krb5_context
, princ
);
350 ret
= cli_credentials_set_from_ccache(cred
, ccc
, obtained
, error_string
);
353 (*error_string
) = error_message(ret
);
358 cred
->ccache_obtained
= obtained
;
359 talloc_steal(cred
, ccc
);
361 cli_credentials_invalidate_client_gss_creds(cred
, cred
->ccache_obtained
);
368 * Indicate the we failed to log in to this service/host with these
369 * credentials. The caller passes an unsigned int which they
370 * initialise to the number of times they would like to retry.
372 * This method is used to support re-trying with freshly fetched
373 * credentials in case a server is rebuilt while clients have
374 * non-expired tickets. When the client code gets a logon failure they
375 * throw away the existing credentials for the server and retry.
377 _PUBLIC_
bool cli_credentials_failed_kerberos_login(struct cli_credentials
*cred
,
378 const char *principal
,
381 struct ccache_container
*ccc
;
382 krb5_creds creds
, creds2
;
385 if (principal
== NULL
) {
386 /* no way to delete if we don't know the principal */
392 /* not a kerberos connection */
397 /* We have already tried discarding the credentials */
403 ret
= krb5_parse_name(ccc
->smb_krb5_context
->krb5_context
, principal
, &creds
.server
);
408 ret
= krb5_cc_retrieve_cred(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
, KRB5_TC_MATCH_SRV_NAMEONLY
, &creds
, &creds2
);
410 /* don't retry - we didn't find these credentials to remove */
411 krb5_free_cred_contents(ccc
->smb_krb5_context
->krb5_context
, &creds
);
415 ret
= krb5_cc_remove_cred(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
, KRB5_TC_MATCH_SRV_NAMEONLY
, &creds
);
416 krb5_free_cred_contents(ccc
->smb_krb5_context
->krb5_context
, &creds
);
417 krb5_free_cred_contents(ccc
->smb_krb5_context
->krb5_context
, &creds2
);
419 /* don't retry - we didn't find these credentials to
420 * remove. Note that with the current backend this
421 * never happens, as it always returns 0 even if the
422 * creds don't exist, which is why we do a separate
423 * krb5_cc_retrieve_cred() above.
431 static int cli_credentials_new_ccache(struct cli_credentials
*cred
,
432 struct loadparm_context
*lp_ctx
,
434 struct ccache_container
**_ccc
,
435 const char **error_string
)
437 bool must_free_cc_name
= false;
439 struct ccache_container
*ccc
= talloc(cred
, struct ccache_container
);
444 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
445 &ccc
->smb_krb5_context
);
448 (*error_string
) = talloc_asprintf(cred
, "Failed to get krb5_context: %s",
452 if (!talloc_reference(ccc
, ccc
->smb_krb5_context
)) {
454 (*error_string
) = strerror(ENOMEM
);
459 must_free_cc_name
= true;
461 if (lpcfg_parm_bool(lp_ctx
, NULL
, "credentials", "krb5_cc_file", false)) {
462 ccache_name
= talloc_asprintf(ccc
, "FILE:/tmp/krb5_cc_samba_%u_%p",
463 (unsigned int)getpid(), ccc
);
465 ccache_name
= talloc_asprintf(ccc
, "MEMORY:%p",
471 (*error_string
) = strerror(ENOMEM
);
476 ret
= krb5_cc_resolve(ccc
->smb_krb5_context
->krb5_context
, ccache_name
,
479 (*error_string
) = talloc_asprintf(cred
, "failed to resolve a krb5 ccache (%s): %s\n",
481 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
,
483 talloc_free(ccache_name
);
488 if (strncasecmp(ccache_name
, "MEMORY:", 7) == 0) {
489 talloc_set_destructor(ccc
, free_mccache
);
491 talloc_set_destructor(ccc
, free_dccache
);
494 if (must_free_cc_name
) {
495 talloc_free(ccache_name
);
503 _PUBLIC_
int cli_credentials_get_named_ccache(struct cli_credentials
*cred
,
504 struct tevent_context
*event_ctx
,
505 struct loadparm_context
*lp_ctx
,
507 struct ccache_container
**ccc
,
508 const char **error_string
)
511 enum credentials_obtained obtained
;
513 if (cred
->machine_account_pending
) {
514 cli_credentials_set_machine_account(cred
, lp_ctx
);
517 if (cred
->ccache_obtained
>= cred
->ccache_threshold
&&
518 cred
->ccache_obtained
> CRED_UNINITIALISED
) {
520 bool expired
= false;
521 ret
= smb_krb5_cc_get_lifetime(cred
->ccache
->smb_krb5_context
->krb5_context
,
522 cred
->ccache
->ccache
, &lifetime
);
523 if (ret
== KRB5_CC_END
) {
524 /* If we have a particular ccache set, without
525 * an initial ticket, then assume there is a
527 } else if (ret
== 0) {
529 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
530 cli_credentials_get_principal(cred
, cred
)));
532 } else if (lifetime
< 300) {
533 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
534 cli_credentials_get_principal(cred
, cred
), (unsigned int)lifetime
));
538 (*error_string
) = talloc_asprintf(cred
, "failed to get ccache lifetime: %s\n",
539 smb_get_krb5_error_message(cred
->ccache
->smb_krb5_context
->krb5_context
,
544 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
545 cli_credentials_get_principal(cred
, cred
), (unsigned int)lifetime
));
552 if (cli_credentials_is_anonymous(cred
)) {
553 (*error_string
) = "Cannot get anonymous kerberos credentials";
557 ret
= cli_credentials_new_ccache(cred
, lp_ctx
, ccache_name
, ccc
, error_string
);
562 ret
= kinit_to_ccache(cred
, cred
, (*ccc
)->smb_krb5_context
, event_ctx
, (*ccc
)->ccache
, &obtained
, error_string
);
567 ret
= cli_credentials_set_from_ccache(cred
, *ccc
,
568 obtained
, error_string
);
571 cred
->ccache_obtained
= cred
->principal_obtained
;
575 cli_credentials_invalidate_client_gss_creds(cred
, cred
->ccache_obtained
);
579 _PUBLIC_
int cli_credentials_get_ccache(struct cli_credentials
*cred
,
580 struct tevent_context
*event_ctx
,
581 struct loadparm_context
*lp_ctx
,
582 struct ccache_container
**ccc
,
583 const char **error_string
)
585 return cli_credentials_get_named_ccache(cred
, event_ctx
, lp_ctx
, NULL
, ccc
, error_string
);
588 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
589 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials
*cred
)
591 if (cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
592 talloc_unlink(cred
, cred
->client_gss_creds
);
593 cred
->client_gss_creds
= NULL
;
595 cred
->client_gss_creds_obtained
= CRED_UNINITIALISED
;
598 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials
*cred
,
599 enum credentials_obtained obtained
)
601 /* If the caller just changed the username/password etc, then
602 * any cached credentials are now invalid */
603 if (obtained
>= cred
->client_gss_creds_obtained
) {
604 if (cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
605 talloc_unlink(cred
, cred
->client_gss_creds
);
606 cred
->client_gss_creds
= NULL
;
608 cred
->client_gss_creds_obtained
= CRED_UNINITIALISED
;
610 /* Now that we know that the data is 'this specified', then
611 * don't allow something less 'known' to be returned as a
612 * ccache. Ie, if the username is on the command line, we
613 * don't want to later guess to use a file-based ccache */
614 if (obtained
> cred
->client_gss_creds_threshold
) {
615 cred
->client_gss_creds_threshold
= obtained
;
619 /* We have good reason to think this CCACHE is invalid. Blow it away */
620 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials
*cred
)
622 if (cred
->ccache_obtained
> CRED_UNINITIALISED
) {
623 talloc_unlink(cred
, cred
->ccache
);
626 cred
->ccache_obtained
= CRED_UNINITIALISED
;
628 cli_credentials_unconditionally_invalidate_client_gss_creds(cred
);
631 _PUBLIC_
void cli_credentials_invalidate_ccache(struct cli_credentials
*cred
,
632 enum credentials_obtained obtained
)
634 /* If the caller just changed the username/password etc, then
635 * any cached credentials are now invalid */
636 if (obtained
>= cred
->ccache_obtained
) {
637 if (cred
->ccache_obtained
> CRED_UNINITIALISED
) {
638 talloc_unlink(cred
, cred
->ccache
);
641 cred
->ccache_obtained
= CRED_UNINITIALISED
;
643 /* Now that we know that the data is 'this specified', then
644 * don't allow something less 'known' to be returned as a
645 * ccache. i.e, if the username is on the command line, we
646 * don't want to later guess to use a file-based ccache */
647 if (obtained
> cred
->ccache_threshold
) {
648 cred
->ccache_threshold
= obtained
;
651 cli_credentials_invalidate_client_gss_creds(cred
,
655 static int free_gssapi_creds(struct gssapi_creds_container
*gcc
)
658 (void)gss_release_cred(&min_stat
, &gcc
->creds
);
662 _PUBLIC_
int cli_credentials_get_client_gss_creds(struct cli_credentials
*cred
,
663 struct tevent_context
*event_ctx
,
664 struct loadparm_context
*lp_ctx
,
665 struct gssapi_creds_container
**_gcc
,
666 const char **error_string
)
669 OM_uint32 maj_stat
, min_stat
;
670 struct gssapi_creds_container
*gcc
;
671 struct ccache_container
*ccache
;
672 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
673 gss_buffer_desc empty_buffer
= GSS_C_EMPTY_BUFFER
;
674 gss_OID oid
= discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X
);
676 krb5_enctype
*etypes
= NULL
;
678 if (cred
->client_gss_creds_obtained
>= cred
->client_gss_creds_threshold
&&
679 cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
680 bool expired
= false;
681 OM_uint32 lifetime
= 0;
682 gss_cred_usage_t usage
= 0;
683 maj_stat
= gss_inquire_cred(&min_stat
, cred
->client_gss_creds
->creds
,
684 NULL
, &lifetime
, &usage
, NULL
);
685 if (maj_stat
== GSS_S_CREDENTIALS_EXPIRED
) {
686 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred
, cred
)));
688 } else if (maj_stat
== GSS_S_COMPLETE
&& lifetime
< 300) {
689 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred
, cred
), lifetime
));
691 } else if (maj_stat
!= GSS_S_COMPLETE
) {
692 *error_string
= talloc_asprintf(cred
, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
693 gssapi_error_string(cred
, maj_stat
, min_stat
, NULL
));
697 cli_credentials_unconditionally_invalidate_client_gss_creds(cred
);
699 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
700 cli_credentials_get_principal(cred
, cred
), (unsigned int)lifetime
));
702 *_gcc
= cred
->client_gss_creds
;
707 ret
= cli_credentials_get_ccache(cred
, event_ctx
, lp_ctx
,
708 &ccache
, error_string
);
710 if (cli_credentials_get_kerberos_state(cred
) == CRED_MUST_USE_KERBEROS
) {
711 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string
));
713 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string
));
718 gcc
= talloc(cred
, struct gssapi_creds_container
);
720 (*error_string
) = error_message(ENOMEM
);
724 maj_stat
= smb_gss_krb5_import_cred(&min_stat
, ccache
->smb_krb5_context
->krb5_context
,
725 ccache
->ccache
, NULL
, NULL
,
727 if ((maj_stat
== GSS_S_FAILURE
) &&
728 (min_stat
== (OM_uint32
)KRB5_CC_END
||
729 min_stat
== (OM_uint32
)KRB5_CC_NOTFOUND
||
730 min_stat
== (OM_uint32
)KRB5_FCC_NOFILE
))
732 /* This CCACHE is no good. Ensure we don't use it again */
733 cli_credentials_unconditionally_invalidate_ccache(cred
);
735 /* Now try again to get a ccache */
736 ret
= cli_credentials_get_ccache(cred
, event_ctx
, lp_ctx
,
737 &ccache
, error_string
);
739 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret
)));
743 maj_stat
= smb_gss_krb5_import_cred(&min_stat
, ccache
->smb_krb5_context
->krb5_context
,
744 ccache
->ccache
, NULL
, NULL
,
756 (*error_string
) = talloc_asprintf(cred
, "smb_gss_krb5_import_cred failed: %s", error_message(ret
));
762 * transfer the enctypes from the smb_krb5_context to the gssapi layer
764 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
765 * to configure the enctypes via the krb5.conf.
767 * And the gss_init_sec_context() creates it's own krb5_context and
768 * the TGS-REQ had all enctypes in it and only the ones configured
769 * and used for the AS-REQ, so it wasn't possible to disable the usage
772 min_stat
= smb_krb5_get_allowed_etypes(ccache
->smb_krb5_context
->krb5_context
,
775 OM_uint32 num_ktypes
;
777 for (num_ktypes
= 0; etypes
[num_ktypes
]; num_ktypes
++);
779 maj_stat
= gss_krb5_set_allowable_enctypes(&min_stat
, gcc
->creds
,
790 (*error_string
) = talloc_asprintf(cred
, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret
));
795 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
797 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
799 * This allows us to disable SIGN and SEAL on a TLS connection with
800 * GSS-SPNENO. For example ldaps:// connections.
802 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
803 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
805 maj_stat
= gss_set_cred_option(&min_stat
, &gcc
->creds
,
815 (*error_string
) = talloc_asprintf(cred
, "gss_set_cred_option failed: %s", error_message(ret
));
819 cred
->client_gss_creds_obtained
= cred
->ccache_obtained
;
820 talloc_set_destructor(gcc
, free_gssapi_creds
);
821 cred
->client_gss_creds
= gcc
;
827 Set a gssapi cred_id_t into the credentials system. (Client case)
829 This grabs the credentials both 'intact' and getting the krb5
830 ccache out of it. This routine can be generalised in future for
831 the case where we deal with GSSAPI mechs other than krb5.
833 On sucess, the caller must not free gssapi_cred, as it now belongs
834 to the credentials system.
837 int cli_credentials_set_client_gss_creds(struct cli_credentials
*cred
,
838 struct loadparm_context
*lp_ctx
,
839 gss_cred_id_t gssapi_cred
,
840 enum credentials_obtained obtained
,
841 const char **error_string
)
844 OM_uint32 maj_stat
, min_stat
;
845 struct ccache_container
*ccc
= NULL
;
846 struct gssapi_creds_container
*gcc
= NULL
;
847 if (cred
->client_gss_creds_obtained
> obtained
) {
851 gcc
= talloc(cred
, struct gssapi_creds_container
);
853 (*error_string
) = error_message(ENOMEM
);
857 ret
= cli_credentials_new_ccache(cred
, lp_ctx
, NULL
, &ccc
, error_string
);
862 maj_stat
= smb_gss_krb5_copy_ccache(&min_stat
,
872 (*error_string
) = error_message(ENOMEM
);
877 ret
= cli_credentials_set_from_ccache(cred
, ccc
, obtained
, error_string
);
880 cred
->ccache_obtained
= obtained
;
882 gcc
->creds
= gssapi_cred
;
883 talloc_set_destructor(gcc
, free_gssapi_creds
);
885 /* set the clinet_gss_creds_obtained here, as it just
886 got set to UNINITIALISED by the calls above */
887 cred
->client_gss_creds_obtained
= obtained
;
888 cred
->client_gss_creds
= gcc
;
893 static int cli_credentials_shallow_ccache(struct cli_credentials
*cred
)
896 const struct ccache_container
*old_ccc
= NULL
;
897 struct ccache_container
*ccc
= NULL
;
898 char *ccache_name
= NULL
;
900 old_ccc
= cred
->ccache
;
901 if (old_ccc
== NULL
) {
905 ccc
= talloc(cred
, struct ccache_container
);
912 ccache_name
= talloc_asprintf(ccc
, "MEMORY:%p", ccc
);
914 ret
= krb5_cc_resolve(ccc
->smb_krb5_context
->krb5_context
,
915 ccache_name
, &ccc
->ccache
);
921 talloc_set_destructor(ccc
, free_mccache
);
923 TALLOC_FREE(ccache_name
);
925 ret
= smb_krb5_cc_copy_creds(ccc
->smb_krb5_context
->krb5_context
,
926 old_ccc
->ccache
, ccc
->ccache
);
933 cred
->client_gss_creds
= NULL
;
934 cred
->client_gss_creds_obtained
= CRED_UNINITIALISED
;
938 _PUBLIC_
struct cli_credentials
*cli_credentials_shallow_copy(TALLOC_CTX
*mem_ctx
,
939 struct cli_credentials
*src
)
941 struct cli_credentials
*dst
;
944 dst
= talloc(mem_ctx
, struct cli_credentials
);
951 ret
= cli_credentials_shallow_ccache(dst
);
960 /* Get the keytab (actually, a container containing the krb5_keytab)
961 * attached to this context. If this hasn't been done or set before,
962 * it will be generated from the password.
964 _PUBLIC_
int cli_credentials_get_keytab(struct cli_credentials
*cred
,
965 struct loadparm_context
*lp_ctx
,
966 struct keytab_container
**_ktc
)
969 struct keytab_container
*ktc
;
970 struct smb_krb5_context
*smb_krb5_context
;
971 const char *keytab_name
;
974 const char *username
= cli_credentials_get_username(cred
);
975 const char *upn
= NULL
;
976 const char *realm
= cli_credentials_get_realm(cred
);
977 char *salt_principal
= NULL
;
978 uint32_t uac_flags
= 0;
980 if (cred
->keytab_obtained
>= (MAX(cred
->principal_obtained
,
981 cred
->username_obtained
))) {
982 *_ktc
= cred
->keytab
;
986 if (cli_credentials_is_anonymous(cred
)) {
990 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
996 mem_ctx
= talloc_new(cred
);
1001 switch (cred
->secure_channel_type
) {
1002 case SEC_CHAN_WKSTA
:
1004 uac_flags
= UF_WORKSTATION_TRUST_ACCOUNT
;
1007 uac_flags
= UF_SERVER_TRUST_ACCOUNT
;
1009 case SEC_CHAN_DOMAIN
:
1010 case SEC_CHAN_DNS_DOMAIN
:
1011 uac_flags
= UF_INTERDOMAIN_TRUST_ACCOUNT
;
1014 upn
= cli_credentials_get_principal(cred
, mem_ctx
);
1016 TALLOC_FREE(mem_ctx
);
1019 uac_flags
= UF_NORMAL_ACCOUNT
;
1023 ret
= smb_krb5_salt_principal(realm
,
1024 username
, /* sAMAccountName */
1025 upn
, /* userPrincipalName */
1030 talloc_free(mem_ctx
);
1034 ret
= smb_krb5_create_memory_keytab(mem_ctx
,
1035 smb_krb5_context
->krb5_context
,
1036 cli_credentials_get_password(cred
),
1040 cli_credentials_get_kvno(cred
),
1044 talloc_free(mem_ctx
);
1048 ret
= smb_krb5_get_keytab_container(mem_ctx
, smb_krb5_context
,
1049 keytab
, keytab_name
, &ktc
);
1051 talloc_free(mem_ctx
);
1055 cred
->keytab_obtained
= (MAX(cred
->principal_obtained
,
1056 cred
->username_obtained
));
1058 /* We make this keytab up based on a password. Therefore
1059 * match-by-key is acceptable, we can't match on the wrong
1061 ktc
->password_based
= true;
1063 talloc_steal(cred
, ktc
);
1065 *_ktc
= cred
->keytab
;
1066 talloc_free(mem_ctx
);
1070 /* Given the name of a keytab (presumably in the format
1071 * FILE:/etc/krb5.keytab), open it and attach it */
1073 _PUBLIC_
int cli_credentials_set_keytab_name(struct cli_credentials
*cred
,
1074 struct loadparm_context
*lp_ctx
,
1075 const char *keytab_name
,
1076 enum credentials_obtained obtained
)
1078 krb5_error_code ret
;
1079 struct keytab_container
*ktc
;
1080 struct smb_krb5_context
*smb_krb5_context
;
1081 TALLOC_CTX
*mem_ctx
;
1083 if (cred
->keytab_obtained
>= obtained
) {
1087 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
, &smb_krb5_context
);
1092 mem_ctx
= talloc_new(cred
);
1097 ret
= smb_krb5_get_keytab_container(mem_ctx
, smb_krb5_context
,
1098 NULL
, keytab_name
, &ktc
);
1103 cred
->keytab_obtained
= obtained
;
1105 talloc_steal(cred
, ktc
);
1107 talloc_free(mem_ctx
);
1112 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1114 _PUBLIC_
int cli_credentials_get_server_gss_creds(struct cli_credentials
*cred
,
1115 struct loadparm_context
*lp_ctx
,
1116 struct gssapi_creds_container
**_gcc
)
1119 OM_uint32 maj_stat
, min_stat
;
1120 struct gssapi_creds_container
*gcc
;
1121 struct keytab_container
*ktc
;
1122 struct smb_krb5_context
*smb_krb5_context
;
1123 TALLOC_CTX
*mem_ctx
;
1124 krb5_principal princ
;
1125 const char *error_string
;
1126 enum credentials_obtained obtained
;
1128 mem_ctx
= talloc_new(cred
);
1133 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
, &smb_krb5_context
);
1138 ret
= principal_from_credentials(mem_ctx
, cred
, smb_krb5_context
, &princ
, &obtained
, &error_string
);
1140 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1142 talloc_free(mem_ctx
);
1146 if (cred
->server_gss_creds_obtained
>= (MAX(cred
->keytab_obtained
, obtained
))) {
1147 talloc_free(mem_ctx
);
1148 *_gcc
= cred
->server_gss_creds
;
1152 ret
= cli_credentials_get_keytab(cred
, lp_ctx
, &ktc
);
1154 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret
)));
1158 gcc
= talloc(cred
, struct gssapi_creds_container
);
1160 talloc_free(mem_ctx
);
1164 if (ktc
->password_based
|| obtained
< CRED_SPECIFIED
) {
1166 * This creates a GSSAPI cred_id_t for match-by-key with only
1171 maj_stat
= smb_gss_krb5_import_cred(&min_stat
,
1172 smb_krb5_context
->krb5_context
,
1184 cred
->server_gss_creds_obtained
= cred
->keytab_obtained
;
1185 talloc_set_destructor(gcc
, free_gssapi_creds
);
1186 cred
->server_gss_creds
= gcc
;
1189 talloc_free(mem_ctx
);
1197 _PUBLIC_
void cli_credentials_set_kvno(struct cli_credentials
*cred
,
1204 * Return Kerberos KVNO
1207 _PUBLIC_
int cli_credentials_get_kvno(struct cli_credentials
*cred
)
1213 const char *cli_credentials_get_salt_principal(struct cli_credentials
*cred
)
1215 return cred
->salt_principal
;
1218 _PUBLIC_
void cli_credentials_set_salt_principal(struct cli_credentials
*cred
, const char *principal
)
1220 talloc_free(cred
->salt_principal
);
1221 cred
->salt_principal
= talloc_strdup(cred
, principal
);
1224 /* The 'impersonate_principal' is used to allow one Kerberos principal
1225 * (and it's associated keytab etc) to impersonate another. The
1226 * ability to do this is controlled by the KDC, but it is generally
1227 * permitted to impersonate anyone to yourself. This allows any
1228 * member of the domain to get the groups of a user. This is also
1229 * known as S4U2Self */
1231 _PUBLIC_
const char *cli_credentials_get_impersonate_principal(struct cli_credentials
*cred
)
1233 return cred
->impersonate_principal
;
1237 * The 'self_service' is the service principal that
1238 * represents the same object (by its objectSid)
1239 * as the client principal (typically our machine account).
1240 * When trying to impersonate 'impersonate_principal' with
1243 _PUBLIC_
const char *cli_credentials_get_self_service(struct cli_credentials
*cred
)
1245 return cred
->self_service
;
1248 _PUBLIC_
void cli_credentials_set_impersonate_principal(struct cli_credentials
*cred
,
1249 const char *principal
,
1250 const char *self_service
)
1252 talloc_free(cred
->impersonate_principal
);
1253 cred
->impersonate_principal
= talloc_strdup(cred
, principal
);
1254 talloc_free(cred
->self_service
);
1255 cred
->self_service
= talloc_strdup(cred
, self_service
);
1256 cli_credentials_set_kerberos_state(cred
, CRED_MUST_USE_KERBEROS
);
1260 * when impersonating for S4U2proxy we need to set the target principal.
1261 * Similarly, we may only be authorized to do general impersonation to
1262 * some particular services.
1264 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1266 * NULL means that tickets will be obtained for the krbtgt service.
1269 const char *cli_credentials_get_target_service(struct cli_credentials
*cred
)
1271 return cred
->target_service
;
1274 _PUBLIC_
void cli_credentials_set_target_service(struct cli_credentials
*cred
, const char *target_service
)
1276 talloc_free(cred
->target_service
);
1277 cred
->target_service
= talloc_strdup(cred
, target_service
);