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 "auth/kerberos/kerberos.h"
27 #include "auth/credentials/credentials.h"
28 #include "auth/credentials/credentials_proto.h"
29 #include "auth/credentials/credentials_krb5.h"
30 #include "auth/kerberos/kerberos_credentials.h"
31 #include "auth/kerberos/kerberos_util.h"
32 #include "param/param.h"
34 _PUBLIC_
int cli_credentials_get_krb5_context(struct cli_credentials
*cred
,
35 struct loadparm_context
*lp_ctx
,
36 struct smb_krb5_context
**smb_krb5_context
)
39 if (cred
->smb_krb5_context
) {
40 *smb_krb5_context
= cred
->smb_krb5_context
;
44 ret
= smb_krb5_init_context(cred
, NULL
, lp_ctx
,
45 &cred
->smb_krb5_context
);
47 cred
->smb_krb5_context
= NULL
;
50 *smb_krb5_context
= cred
->smb_krb5_context
;
54 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
55 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
57 _PUBLIC_ NTSTATUS
cli_credentials_set_krb5_context(struct cli_credentials
*cred
,
58 struct smb_krb5_context
*smb_krb5_context
)
60 if (smb_krb5_context
== NULL
) {
61 talloc_unlink(cred
, cred
->smb_krb5_context
);
62 cred
->smb_krb5_context
= NULL
;
66 if (!talloc_reference(cred
, smb_krb5_context
)) {
67 return NT_STATUS_NO_MEMORY
;
69 cred
->smb_krb5_context
= smb_krb5_context
;
73 static int cli_credentials_set_from_ccache(struct cli_credentials
*cred
,
74 struct ccache_container
*ccache
,
75 enum credentials_obtained obtained
,
76 const char **error_string
)
83 if (cred
->ccache_obtained
> obtained
) {
87 ret
= krb5_cc_get_principal(ccache
->smb_krb5_context
->krb5_context
,
88 ccache
->ccache
, &princ
);
91 (*error_string
) = talloc_asprintf(cred
, "failed to get principal from ccache: %s\n",
92 smb_get_krb5_error_message(ccache
->smb_krb5_context
->krb5_context
,
97 ret
= krb5_unparse_name(ccache
->smb_krb5_context
->krb5_context
, princ
, &name
);
99 (*error_string
) = talloc_asprintf(cred
, "failed to unparse principal from ccache: %s\n",
100 smb_get_krb5_error_message(ccache
->smb_krb5_context
->krb5_context
,
105 cli_credentials_set_principal(cred
, name
, obtained
);
109 krb5_free_principal(ccache
->smb_krb5_context
->krb5_context
, princ
);
111 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
112 cred
->ccache_obtained
= obtained
;
117 /* Free a memory ccache */
118 static int free_mccache(struct ccache_container
*ccc
)
120 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
);
125 /* Free a disk-based ccache */
126 static int free_dccache(struct ccache_container
*ccc
) {
127 krb5_cc_close(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
);
132 _PUBLIC_
int cli_credentials_set_ccache(struct cli_credentials
*cred
,
133 struct loadparm_context
*lp_ctx
,
135 enum credentials_obtained obtained
,
136 const char **error_string
)
139 krb5_principal princ
;
140 struct ccache_container
*ccc
;
141 if (cred
->ccache_obtained
> obtained
) {
145 ccc
= talloc(cred
, struct ccache_container
);
147 (*error_string
) = error_message(ENOMEM
);
151 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
152 &ccc
->smb_krb5_context
);
154 (*error_string
) = error_message(ret
);
158 if (!talloc_reference(ccc
, ccc
->smb_krb5_context
)) {
160 (*error_string
) = error_message(ENOMEM
);
165 ret
= krb5_cc_resolve(ccc
->smb_krb5_context
->krb5_context
, name
, &ccc
->ccache
);
167 (*error_string
) = talloc_asprintf(cred
, "failed to read krb5 ccache: %s: %s\n",
169 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
,
175 ret
= krb5_cc_default(ccc
->smb_krb5_context
->krb5_context
, &ccc
->ccache
);
177 (*error_string
) = talloc_asprintf(cred
, "failed to read default krb5 ccache: %s\n",
178 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
,
185 talloc_set_destructor(ccc
, free_dccache
);
187 ret
= krb5_cc_get_principal(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
, &princ
);
190 krb5_free_principal(ccc
->smb_krb5_context
->krb5_context
, princ
);
191 ret
= cli_credentials_set_from_ccache(cred
, ccc
, obtained
, error_string
);
194 (*error_string
) = error_message(ret
);
199 cred
->ccache_obtained
= obtained
;
200 talloc_steal(cred
, ccc
);
202 cli_credentials_invalidate_client_gss_creds(cred
, cred
->ccache_obtained
);
209 static int cli_credentials_new_ccache(struct cli_credentials
*cred
,
210 struct loadparm_context
*lp_ctx
,
212 struct ccache_container
**_ccc
,
213 const char **error_string
)
215 bool must_free_cc_name
= false;
217 struct ccache_container
*ccc
= talloc(cred
, struct ccache_container
);
222 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
223 &ccc
->smb_krb5_context
);
226 (*error_string
) = talloc_asprintf(cred
, "Failed to get krb5_context: %s",
230 if (!talloc_reference(ccc
, ccc
->smb_krb5_context
)) {
232 (*error_string
) = strerror(ENOMEM
);
237 must_free_cc_name
= true;
238 ccache_name
= talloc_asprintf(ccc
, "MEMORY:%p",
243 (*error_string
) = strerror(ENOMEM
);
248 ret
= krb5_cc_resolve(ccc
->smb_krb5_context
->krb5_context
, ccache_name
,
251 (*error_string
) = talloc_asprintf(cred
, "failed to resolve a krb5 ccache (%s): %s\n",
253 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
,
255 talloc_free(ccache_name
);
260 if (strncasecmp(ccache_name
, "MEMORY:", 7) == 0) {
261 talloc_set_destructor(ccc
, free_mccache
);
263 talloc_set_destructor(ccc
, free_dccache
);
266 if (must_free_cc_name
) {
267 talloc_free(ccache_name
);
275 _PUBLIC_
int cli_credentials_get_named_ccache(struct cli_credentials
*cred
,
276 struct tevent_context
*event_ctx
,
277 struct loadparm_context
*lp_ctx
,
279 struct ccache_container
**ccc
,
280 const char **error_string
)
283 enum credentials_obtained obtained
;
285 if (cred
->machine_account_pending
) {
286 cli_credentials_set_machine_account(cred
, lp_ctx
);
289 if (cred
->ccache_obtained
>= cred
->ccache_threshold
&&
290 cred
->ccache_obtained
> CRED_UNINITIALISED
) {
294 if (cli_credentials_is_anonymous(cred
)) {
295 (*error_string
) = "Cannot get anonymous kerberos credentials";
299 ret
= cli_credentials_new_ccache(cred
, lp_ctx
, ccache_name
, ccc
, error_string
);
304 ret
= kinit_to_ccache(cred
, cred
, (*ccc
)->smb_krb5_context
, event_ctx
, (*ccc
)->ccache
, &obtained
, error_string
);
309 ret
= cli_credentials_set_from_ccache(cred
, *ccc
,
310 obtained
, error_string
);
313 cred
->ccache_obtained
= cred
->principal_obtained
;
317 cli_credentials_invalidate_client_gss_creds(cred
, cred
->ccache_obtained
);
321 _PUBLIC_
int cli_credentials_get_ccache(struct cli_credentials
*cred
,
322 struct tevent_context
*event_ctx
,
323 struct loadparm_context
*lp_ctx
,
324 struct ccache_container
**ccc
,
325 const char **error_string
)
327 return cli_credentials_get_named_ccache(cred
, event_ctx
, lp_ctx
, NULL
, ccc
, error_string
);
330 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
331 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials
*cred
)
333 if (cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
334 talloc_unlink(cred
, cred
->client_gss_creds
);
335 cred
->client_gss_creds
= NULL
;
337 cred
->client_gss_creds_obtained
= CRED_UNINITIALISED
;
340 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials
*cred
,
341 enum credentials_obtained obtained
)
343 /* If the caller just changed the username/password etc, then
344 * any cached credentials are now invalid */
345 if (obtained
>= cred
->client_gss_creds_obtained
) {
346 if (cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
347 talloc_unlink(cred
, cred
->client_gss_creds
);
348 cred
->client_gss_creds
= NULL
;
350 cred
->client_gss_creds_obtained
= CRED_UNINITIALISED
;
352 /* Now that we know that the data is 'this specified', then
353 * don't allow something less 'known' to be returned as a
354 * ccache. Ie, if the username is on the commmand line, we
355 * don't want to later guess to use a file-based ccache */
356 if (obtained
> cred
->client_gss_creds_threshold
) {
357 cred
->client_gss_creds_threshold
= obtained
;
361 /* We have good reason to think this CCACHE is invalid. Blow it away */
362 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials
*cred
)
364 if (cred
->ccache_obtained
> CRED_UNINITIALISED
) {
365 talloc_unlink(cred
, cred
->ccache
);
368 cred
->ccache_obtained
= CRED_UNINITIALISED
;
370 cli_credentials_unconditionally_invalidate_client_gss_creds(cred
);
373 _PUBLIC_
void cli_credentials_invalidate_ccache(struct cli_credentials
*cred
,
374 enum credentials_obtained obtained
)
376 /* If the caller just changed the username/password etc, then
377 * any cached credentials are now invalid */
378 if (obtained
>= cred
->ccache_obtained
) {
379 if (cred
->ccache_obtained
> CRED_UNINITIALISED
) {
380 talloc_unlink(cred
, cred
->ccache
);
383 cred
->ccache_obtained
= CRED_UNINITIALISED
;
385 /* Now that we know that the data is 'this specified', then
386 * don't allow something less 'known' to be returned as a
387 * ccache. Ie, if the username is on the commmand line, we
388 * don't want to later guess to use a file-based ccache */
389 if (obtained
> cred
->ccache_threshold
) {
390 cred
->ccache_threshold
= obtained
;
393 cli_credentials_invalidate_client_gss_creds(cred
,
397 static int free_gssapi_creds(struct gssapi_creds_container
*gcc
)
399 OM_uint32 min_stat
, maj_stat
;
400 maj_stat
= gss_release_cred(&min_stat
, &gcc
->creds
);
404 _PUBLIC_
int cli_credentials_get_client_gss_creds(struct cli_credentials
*cred
,
405 struct tevent_context
*event_ctx
,
406 struct loadparm_context
*lp_ctx
,
407 struct gssapi_creds_container
**_gcc
,
408 const char **error_string
)
411 OM_uint32 maj_stat
, min_stat
;
412 struct gssapi_creds_container
*gcc
;
413 struct ccache_container
*ccache
;
414 gss_buffer_desc empty_buffer
= GSS_C_EMPTY_BUFFER
;
415 krb5_enctype
*etypes
= NULL
;
417 if (cred
->client_gss_creds_obtained
>= cred
->client_gss_creds_threshold
&&
418 cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
419 *_gcc
= cred
->client_gss_creds
;
423 ret
= cli_credentials_get_ccache(cred
, event_ctx
, lp_ctx
,
424 &ccache
, error_string
);
426 DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret
)));
430 gcc
= talloc(cred
, struct gssapi_creds_container
);
432 (*error_string
) = error_message(ENOMEM
);
436 maj_stat
= gss_krb5_import_cred(&min_stat
, ccache
->ccache
, NULL
, NULL
,
438 if ((maj_stat
== GSS_S_FAILURE
) && (min_stat
== (OM_uint32
)KRB5_CC_END
|| min_stat
== (OM_uint32
) KRB5_CC_NOTFOUND
)) {
439 /* This CCACHE is no good. Ensure we don't use it again */
440 cli_credentials_unconditionally_invalidate_ccache(cred
);
442 /* Now try again to get a ccache */
443 ret
= cli_credentials_get_ccache(cred
, event_ctx
, lp_ctx
,
444 &ccache
, error_string
);
446 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret
)));
450 maj_stat
= gss_krb5_import_cred(&min_stat
, ccache
->ccache
, NULL
, NULL
,
462 (*error_string
) = talloc_asprintf(cred
, "gss_krb5_import_cred failed: %s", error_message(ret
));
467 * transfer the enctypes from the smb_krb5_context to the gssapi layer
469 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
470 * to configure the enctypes via the krb5.conf.
472 * And the gss_init_sec_context() creates it's own krb5_context and
473 * the TGS-REQ had all enctypes in it and only the ones configured
474 * and used for the AS-REQ, so it wasn't possible to disable the usage
477 min_stat
= krb5_get_default_in_tkt_etypes(ccache
->smb_krb5_context
->krb5_context
,
480 OM_uint32 num_ktypes
;
482 for (num_ktypes
= 0; etypes
[num_ktypes
]; num_ktypes
++);
484 maj_stat
= gss_krb5_set_allowable_enctypes(&min_stat
, gcc
->creds
,
495 (*error_string
) = talloc_asprintf(cred
, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret
));
500 /* don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG */
501 maj_stat
= gss_set_cred_option(&min_stat
, &gcc
->creds
,
502 GSS_KRB5_CRED_NO_CI_FLAGS_X
,
511 (*error_string
) = talloc_asprintf(cred
, "gss_set_cred_option failed: %s", error_message(ret
));
515 cred
->client_gss_creds_obtained
= cred
->ccache_obtained
;
516 talloc_set_destructor(gcc
, free_gssapi_creds
);
517 cred
->client_gss_creds
= gcc
;
523 Set a gssapi cred_id_t into the credentials system. (Client case)
525 This grabs the credentials both 'intact' and getting the krb5
526 ccache out of it. This routine can be generalised in future for
527 the case where we deal with GSSAPI mechs other than krb5.
529 On sucess, the caller must not free gssapi_cred, as it now belongs
530 to the credentials system.
533 int cli_credentials_set_client_gss_creds(struct cli_credentials
*cred
,
534 struct loadparm_context
*lp_ctx
,
535 gss_cred_id_t gssapi_cred
,
536 enum credentials_obtained obtained
,
537 const char **error_string
)
540 OM_uint32 maj_stat
, min_stat
;
541 struct ccache_container
*ccc
;
542 struct gssapi_creds_container
*gcc
;
543 if (cred
->client_gss_creds_obtained
> obtained
) {
547 gcc
= talloc(cred
, struct gssapi_creds_container
);
549 (*error_string
) = error_message(ENOMEM
);
553 ret
= cli_credentials_new_ccache(cred
, lp_ctx
, NULL
, &ccc
, error_string
);
558 maj_stat
= gss_krb5_copy_ccache(&min_stat
,
559 gssapi_cred
, ccc
->ccache
);
567 (*error_string
) = error_message(ENOMEM
);
572 ret
= cli_credentials_set_from_ccache(cred
, ccc
, obtained
, error_string
);
575 cred
->ccache_obtained
= obtained
;
577 gcc
->creds
= gssapi_cred
;
578 talloc_set_destructor(gcc
, free_gssapi_creds
);
580 /* set the clinet_gss_creds_obtained here, as it just
581 got set to UNINITIALISED by the calls above */
582 cred
->client_gss_creds_obtained
= obtained
;
583 cred
->client_gss_creds
= gcc
;
588 /* Get the keytab (actually, a container containing the krb5_keytab)
589 * attached to this context. If this hasn't been done or set before,
590 * it will be generated from the password.
592 _PUBLIC_
int cli_credentials_get_keytab(struct cli_credentials
*cred
,
593 struct loadparm_context
*lp_ctx
,
594 struct keytab_container
**_ktc
)
597 struct keytab_container
*ktc
;
598 struct smb_krb5_context
*smb_krb5_context
;
601 if (cred
->keytab_obtained
>= (MAX(cred
->principal_obtained
,
602 cred
->username_obtained
))) {
603 *_ktc
= cred
->keytab
;
607 if (cli_credentials_is_anonymous(cred
)) {
611 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
,
617 mem_ctx
= talloc_new(cred
);
622 ret
= smb_krb5_create_memory_keytab(mem_ctx
, cred
,
623 smb_krb5_context
, &ktc
);
625 talloc_free(mem_ctx
);
629 cred
->keytab_obtained
= (MAX(cred
->principal_obtained
,
630 cred
->username_obtained
));
632 talloc_steal(cred
, ktc
);
634 *_ktc
= cred
->keytab
;
635 talloc_free(mem_ctx
);
639 /* Given the name of a keytab (presumably in the format
640 * FILE:/etc/krb5.keytab), open it and attach it */
642 _PUBLIC_
int cli_credentials_set_keytab_name(struct cli_credentials
*cred
,
643 struct loadparm_context
*lp_ctx
,
644 const char *keytab_name
,
645 enum credentials_obtained obtained
)
648 struct keytab_container
*ktc
;
649 struct smb_krb5_context
*smb_krb5_context
;
652 if (cred
->keytab_obtained
>= obtained
) {
656 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
, &smb_krb5_context
);
661 mem_ctx
= talloc_new(cred
);
666 ret
= smb_krb5_open_keytab(mem_ctx
, smb_krb5_context
,
672 cred
->keytab_obtained
= obtained
;
674 talloc_steal(cred
, ktc
);
676 talloc_free(mem_ctx
);
681 /* Get server gss credentials (in gsskrb5, this means the keytab) */
683 _PUBLIC_
int cli_credentials_get_server_gss_creds(struct cli_credentials
*cred
,
684 struct loadparm_context
*lp_ctx
,
685 struct gssapi_creds_container
**_gcc
)
688 OM_uint32 maj_stat
, min_stat
;
689 struct gssapi_creds_container
*gcc
;
690 struct keytab_container
*ktc
;
691 struct smb_krb5_context
*smb_krb5_context
;
693 krb5_principal princ
;
694 const char *error_string
;
695 enum credentials_obtained obtained
;
697 mem_ctx
= talloc_new(cred
);
702 ret
= cli_credentials_get_krb5_context(cred
, lp_ctx
, &smb_krb5_context
);
707 ret
= principal_from_credentials(mem_ctx
, cred
, smb_krb5_context
, &princ
, &obtained
, &error_string
);
709 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
711 talloc_free(mem_ctx
);
715 if (cred
->server_gss_creds_obtained
>= (MAX(cred
->keytab_obtained
, obtained
))) {
716 talloc_free(mem_ctx
);
717 *_gcc
= cred
->server_gss_creds
;
721 ret
= cli_credentials_get_keytab(cred
, lp_ctx
, &ktc
);
723 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret
)));
727 gcc
= talloc(cred
, struct gssapi_creds_container
);
729 talloc_free(mem_ctx
);
733 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
734 maj_stat
= gss_krb5_import_cred(&min_stat
, NULL
, princ
, ktc
->keytab
,
744 cred
->server_gss_creds_obtained
= cred
->keytab_obtained
;
745 talloc_set_destructor(gcc
, free_gssapi_creds
);
746 cred
->server_gss_creds
= gcc
;
749 talloc_free(mem_ctx
);
757 _PUBLIC_
void cli_credentials_set_kvno(struct cli_credentials
*cred
,
764 * Return Kerberos KVNO
767 _PUBLIC_
int cli_credentials_get_kvno(struct cli_credentials
*cred
)
773 const char *cli_credentials_get_salt_principal(struct cli_credentials
*cred
)
775 return cred
->salt_principal
;
778 _PUBLIC_
void cli_credentials_set_salt_principal(struct cli_credentials
*cred
, const char *principal
)
780 talloc_free(cred
->salt_principal
);
781 cred
->salt_principal
= talloc_strdup(cred
, principal
);
784 /* The 'impersonate_principal' is used to allow on Kerberos principal
785 * (and it's associated keytab etc) to impersonate another. The
786 * ability to do this is controlled by the KDC, but it is generally
787 * permitted to impersonate anyone to yourself. This allows any
788 * member of the domain to get the groups of a user. This is also
789 * known as S4U2Self */
791 const char *cli_credentials_get_impersonate_principal(struct cli_credentials
*cred
)
793 return cred
->impersonate_principal
;
796 _PUBLIC_
void cli_credentials_set_impersonate_principal(struct cli_credentials
*cred
, const char *principal
)
798 talloc_free(cred
->impersonate_principal
);
799 cred
->impersonate_principal
= talloc_strdup(cred
, principal
);
802 /* when impersonating for S4U2Self we need to set the target principal
803 * to ourself, as otherwise we would need additional rights.
804 * Similarly, we may only be authorized to do general impersonation to
805 * some particular services.
807 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
809 * NULL means that tickets will be obtained for the krbtgt service.
812 const char *cli_credentials_get_target_service(struct cli_credentials
*cred
)
814 return cred
->target_service
;
817 _PUBLIC_
void cli_credentials_set_target_service(struct cli_credentials
*cred
, const char *target_service
)
819 talloc_free(cred
->target_service
);
820 cred
->target_service
= talloc_strdup(cred
, target_service
);