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 "param/param.h"
32 _PUBLIC_
int cli_credentials_get_krb5_context(struct cli_credentials
*cred
,
33 struct tevent_context
*event_ctx
,
34 struct loadparm_context
*lp_ctx
,
35 struct smb_krb5_context
**smb_krb5_context
)
38 if (cred
->smb_krb5_context
) {
39 *smb_krb5_context
= cred
->smb_krb5_context
;
43 ret
= smb_krb5_init_context(cred
, event_ctx
, lp_ctx
, &cred
->smb_krb5_context
);
45 cred
->smb_krb5_context
= NULL
;
48 *smb_krb5_context
= cred
->smb_krb5_context
;
52 /* This needs to be called directly after the cli_credentials_init(),
53 * otherwise we might have problems with the krb5 context already
56 _PUBLIC_ NTSTATUS
cli_credentials_set_krb5_context(struct cli_credentials
*cred
,
57 struct smb_krb5_context
*smb_krb5_context
)
59 if (!talloc_reference(cred
, smb_krb5_context
)) {
60 return NT_STATUS_NO_MEMORY
;
62 cred
->smb_krb5_context
= smb_krb5_context
;
66 static int cli_credentials_set_from_ccache(struct cli_credentials
*cred
,
67 struct ccache_container
*ccache
,
68 enum credentials_obtained obtained
)
75 if (cred
->ccache_obtained
> obtained
) {
79 ret
= krb5_cc_get_principal(ccache
->smb_krb5_context
->krb5_context
,
80 ccache
->ccache
, &princ
);
83 char *err_mess
= smb_get_krb5_error_message(ccache
->smb_krb5_context
->krb5_context
,
85 DEBUG(1,("failed to get principal from ccache: %s\n",
87 talloc_free(err_mess
);
91 ret
= krb5_unparse_name(ccache
->smb_krb5_context
->krb5_context
, princ
, &name
);
93 char *err_mess
= smb_get_krb5_error_message(ccache
->smb_krb5_context
->krb5_context
, ret
, cred
);
94 DEBUG(1,("failed to unparse principal from ccache: %s\n",
96 talloc_free(err_mess
);
100 cli_credentials_set_principal(cred
, name
, obtained
);
104 krb5_free_principal(ccache
->smb_krb5_context
->krb5_context
, princ
);
106 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
107 cred
->ccache_obtained
= obtained
;
112 /* Free a memory ccache */
113 static int free_mccache(struct ccache_container
*ccc
)
115 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
);
120 /* Free a disk-based ccache */
121 static int free_dccache(struct ccache_container
*ccc
) {
122 krb5_cc_close(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
);
127 _PUBLIC_
int cli_credentials_set_ccache(struct cli_credentials
*cred
,
128 struct tevent_context
*event_ctx
,
129 struct loadparm_context
*lp_ctx
,
131 enum credentials_obtained obtained
)
134 krb5_principal princ
;
135 struct ccache_container
*ccc
;
136 if (cred
->ccache_obtained
> obtained
) {
140 ccc
= talloc(cred
, struct ccache_container
);
145 ret
= cli_credentials_get_krb5_context(cred
, event_ctx
, lp_ctx
,
146 &ccc
->smb_krb5_context
);
151 if (!talloc_reference(ccc
, ccc
->smb_krb5_context
)) {
157 ret
= krb5_cc_resolve(ccc
->smb_krb5_context
->krb5_context
, name
, &ccc
->ccache
);
159 DEBUG(1,("failed to read krb5 ccache: %s: %s\n",
161 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
, ret
, ccc
)));
166 ret
= krb5_cc_default(ccc
->smb_krb5_context
->krb5_context
, &ccc
->ccache
);
168 DEBUG(3,("failed to read default krb5 ccache: %s\n",
169 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
, ret
, ccc
)));
175 talloc_set_destructor(ccc
, free_dccache
);
177 ret
= krb5_cc_get_principal(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
, &princ
);
180 DEBUG(3,("failed to get principal from default ccache: %s\n",
181 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
, ret
, ccc
)));
186 krb5_free_principal(ccc
->smb_krb5_context
->krb5_context
, princ
);
188 ret
= cli_credentials_set_from_ccache(cred
, ccc
, obtained
);
195 cred
->ccache_obtained
= obtained
;
196 talloc_steal(cred
, ccc
);
198 cli_credentials_invalidate_client_gss_creds(cred
, cred
->ccache_obtained
);
203 static int cli_credentials_new_ccache(struct cli_credentials
*cred
,
204 struct tevent_context
*event_ctx
,
205 struct loadparm_context
*lp_ctx
,
206 struct ccache_container
**_ccc
)
209 struct ccache_container
*ccc
= talloc(cred
, struct ccache_container
);
215 ccache_name
= talloc_asprintf(ccc
, "MEMORY:%p",
223 ret
= cli_credentials_get_krb5_context(cred
, event_ctx
, lp_ctx
,
224 &ccc
->smb_krb5_context
);
229 if (!talloc_reference(ccc
, ccc
->smb_krb5_context
)) {
234 ret
= krb5_cc_resolve(ccc
->smb_krb5_context
->krb5_context
, ccache_name
,
237 DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n",
239 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
, ret
, ccc
)));
240 talloc_free(ccache_name
);
245 talloc_set_destructor(ccc
, free_mccache
);
247 talloc_free(ccache_name
);
254 _PUBLIC_
int cli_credentials_get_ccache(struct cli_credentials
*cred
,
255 struct tevent_context
*event_ctx
,
256 struct loadparm_context
*lp_ctx
,
257 struct ccache_container
**ccc
)
261 if (cred
->machine_account_pending
) {
262 cli_credentials_set_machine_account(cred
, lp_ctx
);
265 if (cred
->ccache_obtained
>= cred
->ccache_threshold
&&
266 cred
->ccache_obtained
> CRED_UNINITIALISED
) {
270 if (cli_credentials_is_anonymous(cred
)) {
274 ret
= cli_credentials_new_ccache(cred
, event_ctx
, lp_ctx
, ccc
);
279 ret
= kinit_to_ccache(cred
, cred
, (*ccc
)->smb_krb5_context
, (*ccc
)->ccache
);
284 ret
= cli_credentials_set_from_ccache(cred
, *ccc
,
285 (MAX(MAX(cred
->principal_obtained
,
286 cred
->username_obtained
),
287 cred
->password_obtained
)));
290 cred
->ccache_obtained
= cred
->principal_obtained
;
294 cli_credentials_invalidate_client_gss_creds(cred
, cred
->ccache_obtained
);
298 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials
*cred
,
299 enum credentials_obtained obtained
)
301 /* If the caller just changed the username/password etc, then
302 * any cached credentials are now invalid */
303 if (obtained
>= cred
->client_gss_creds_obtained
) {
304 if (cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
305 talloc_unlink(cred
, cred
->client_gss_creds
);
306 cred
->client_gss_creds
= NULL
;
308 cred
->client_gss_creds_obtained
= CRED_UNINITIALISED
;
310 /* Now that we know that the data is 'this specified', then
311 * don't allow something less 'known' to be returned as a
312 * ccache. Ie, if the username is on the commmand line, we
313 * don't want to later guess to use a file-based ccache */
314 if (obtained
> cred
->client_gss_creds_threshold
) {
315 cred
->client_gss_creds_threshold
= obtained
;
319 _PUBLIC_
void cli_credentials_invalidate_ccache(struct cli_credentials
*cred
,
320 enum credentials_obtained obtained
)
322 /* If the caller just changed the username/password etc, then
323 * any cached credentials are now invalid */
324 if (obtained
>= cred
->ccache_obtained
) {
325 if (cred
->ccache_obtained
> CRED_UNINITIALISED
) {
326 talloc_unlink(cred
, cred
->ccache
);
329 cred
->ccache_obtained
= CRED_UNINITIALISED
;
331 /* Now that we know that the data is 'this specified', then
332 * don't allow something less 'known' to be returned as a
333 * ccache. Ie, if the username is on the commmand line, we
334 * don't want to later guess to use a file-based ccache */
335 if (obtained
> cred
->ccache_threshold
) {
336 cred
->ccache_threshold
= obtained
;
339 cli_credentials_invalidate_client_gss_creds(cred
,
343 static int free_gssapi_creds(struct gssapi_creds_container
*gcc
)
345 OM_uint32 min_stat
, maj_stat
;
346 maj_stat
= gss_release_cred(&min_stat
, &gcc
->creds
);
350 _PUBLIC_
int cli_credentials_get_client_gss_creds(struct cli_credentials
*cred
,
351 struct tevent_context
*event_ctx
,
352 struct loadparm_context
*lp_ctx
,
353 struct gssapi_creds_container
**_gcc
)
356 OM_uint32 maj_stat
, min_stat
;
357 struct gssapi_creds_container
*gcc
;
358 struct ccache_container
*ccache
;
359 gss_buffer_desc empty_buffer
= GSS_C_EMPTY_BUFFER
;
360 krb5_enctype
*etypes
= NULL
;
362 if (cred
->client_gss_creds_obtained
>= cred
->client_gss_creds_threshold
&&
363 cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
364 *_gcc
= cred
->client_gss_creds
;
368 ret
= cli_credentials_get_ccache(cred
, event_ctx
, lp_ctx
,
371 DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret
)));
375 gcc
= talloc(cred
, struct gssapi_creds_container
);
380 maj_stat
= gss_krb5_import_cred(&min_stat
, ccache
->ccache
, NULL
, NULL
,
393 * transfer the enctypes from the smb_krb5_context to the gssapi layer
395 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
396 * to configure the enctypes via the krb5.conf.
398 * And the gss_init_sec_context() creates it's own krb5_context and
399 * the TGS-REQ had all enctypes in it and only the ones configured
400 * and used for the AS-REQ, so it wasn't possible to disable the usage
403 min_stat
= krb5_get_default_in_tkt_etypes(ccache
->smb_krb5_context
->krb5_context
,
406 OM_uint32 num_ktypes
;
408 for (num_ktypes
= 0; etypes
[num_ktypes
]; num_ktypes
++);
410 maj_stat
= gss_krb5_set_allowable_enctypes(&min_stat
, gcc
->creds
,
424 /* don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG */
425 maj_stat
= gss_set_cred_option(&min_stat
, &gcc
->creds
,
426 GSS_KRB5_CRED_NO_CI_FLAGS_X
,
438 cred
->client_gss_creds_obtained
= cred
->ccache_obtained
;
439 talloc_set_destructor(gcc
, free_gssapi_creds
);
440 cred
->client_gss_creds
= gcc
;
446 Set a gssapi cred_id_t into the credentials system. (Client case)
448 This grabs the credentials both 'intact' and getting the krb5
449 ccache out of it. This routine can be generalised in future for
450 the case where we deal with GSSAPI mechs other than krb5.
452 On sucess, the caller must not free gssapi_cred, as it now belongs
453 to the credentials system.
456 int cli_credentials_set_client_gss_creds(struct cli_credentials
*cred
,
457 struct tevent_context
*event_ctx
,
458 struct loadparm_context
*lp_ctx
,
459 gss_cred_id_t gssapi_cred
,
460 enum credentials_obtained obtained
)
463 OM_uint32 maj_stat
, min_stat
;
464 struct ccache_container
*ccc
;
465 struct gssapi_creds_container
*gcc
;
466 if (cred
->client_gss_creds_obtained
> obtained
) {
470 gcc
= talloc(cred
, struct gssapi_creds_container
);
475 ret
= cli_credentials_new_ccache(cred
, event_ctx
, lp_ctx
, &ccc
);
480 maj_stat
= gss_krb5_copy_ccache(&min_stat
,
481 gssapi_cred
, ccc
->ccache
);
491 ret
= cli_credentials_set_from_ccache(cred
, ccc
, obtained
);
494 cred
->ccache_obtained
= obtained
;
496 gcc
->creds
= gssapi_cred
;
497 talloc_set_destructor(gcc
, free_gssapi_creds
);
499 /* set the clinet_gss_creds_obtained here, as it just
500 got set to UNINITIALISED by the calls above */
501 cred
->client_gss_creds_obtained
= obtained
;
502 cred
->client_gss_creds
= gcc
;
507 /* Get the keytab (actually, a container containing the krb5_keytab)
508 * attached to this context. If this hasn't been done or set before,
509 * it will be generated from the password.
511 _PUBLIC_
int cli_credentials_get_keytab(struct cli_credentials
*cred
,
512 struct tevent_context
*event_ctx
,
513 struct loadparm_context
*lp_ctx
,
514 struct keytab_container
**_ktc
)
517 struct keytab_container
*ktc
;
518 struct smb_krb5_context
*smb_krb5_context
;
519 const char **enctype_strings
;
522 if (cred
->keytab_obtained
>= (MAX(cred
->principal_obtained
,
523 cred
->username_obtained
))) {
524 *_ktc
= cred
->keytab
;
528 if (cli_credentials_is_anonymous(cred
)) {
532 ret
= cli_credentials_get_krb5_context(cred
, event_ctx
, lp_ctx
,
538 mem_ctx
= talloc_new(cred
);
543 enctype_strings
= cli_credentials_get_enctype_strings(cred
);
545 ret
= smb_krb5_create_memory_keytab(mem_ctx
, cred
,
547 enctype_strings
, &ktc
);
549 talloc_free(mem_ctx
);
553 cred
->keytab_obtained
= (MAX(cred
->principal_obtained
,
554 cred
->username_obtained
));
556 talloc_steal(cred
, ktc
);
558 *_ktc
= cred
->keytab
;
559 talloc_free(mem_ctx
);
563 /* Given the name of a keytab (presumably in the format
564 * FILE:/etc/krb5.keytab), open it and attach it */
566 _PUBLIC_
int cli_credentials_set_keytab_name(struct cli_credentials
*cred
,
567 struct tevent_context
*event_ctx
,
568 struct loadparm_context
*lp_ctx
,
569 const char *keytab_name
,
570 enum credentials_obtained obtained
)
573 struct keytab_container
*ktc
;
574 struct smb_krb5_context
*smb_krb5_context
;
577 if (cred
->keytab_obtained
>= obtained
) {
581 ret
= cli_credentials_get_krb5_context(cred
, event_ctx
, lp_ctx
, &smb_krb5_context
);
586 mem_ctx
= talloc_new(cred
);
591 ret
= smb_krb5_open_keytab(mem_ctx
, smb_krb5_context
,
597 cred
->keytab_obtained
= obtained
;
599 talloc_steal(cred
, ktc
);
601 talloc_free(mem_ctx
);
606 _PUBLIC_
int cli_credentials_update_keytab(struct cli_credentials
*cred
,
607 struct tevent_context
*event_ctx
,
608 struct loadparm_context
*lp_ctx
)
611 struct keytab_container
*ktc
;
612 struct smb_krb5_context
*smb_krb5_context
;
613 const char **enctype_strings
;
616 mem_ctx
= talloc_new(cred
);
621 ret
= cli_credentials_get_krb5_context(cred
, event_ctx
, lp_ctx
, &smb_krb5_context
);
623 talloc_free(mem_ctx
);
627 enctype_strings
= cli_credentials_get_enctype_strings(cred
);
629 ret
= cli_credentials_get_keytab(cred
, event_ctx
, lp_ctx
, &ktc
);
631 talloc_free(mem_ctx
);
635 ret
= smb_krb5_update_keytab(mem_ctx
, cred
, smb_krb5_context
, enctype_strings
, ktc
);
637 talloc_free(mem_ctx
);
641 /* Get server gss credentials (in gsskrb5, this means the keytab) */
643 _PUBLIC_
int cli_credentials_get_server_gss_creds(struct cli_credentials
*cred
,
644 struct tevent_context
*event_ctx
,
645 struct loadparm_context
*lp_ctx
,
646 struct gssapi_creds_container
**_gcc
)
649 OM_uint32 maj_stat
, min_stat
;
650 struct gssapi_creds_container
*gcc
;
651 struct keytab_container
*ktc
;
652 struct smb_krb5_context
*smb_krb5_context
;
654 krb5_principal princ
;
656 if (cred
->server_gss_creds_obtained
>= (MAX(cred
->keytab_obtained
,
657 MAX(cred
->principal_obtained
,
658 cred
->username_obtained
)))) {
659 *_gcc
= cred
->server_gss_creds
;
663 ret
= cli_credentials_get_krb5_context(cred
, event_ctx
, lp_ctx
, &smb_krb5_context
);
668 ret
= cli_credentials_get_keytab(cred
, event_ctx
, lp_ctx
, &ktc
);
670 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret
)));
674 mem_ctx
= talloc_new(cred
);
679 ret
= principal_from_credentials(mem_ctx
, cred
, smb_krb5_context
, &princ
);
681 DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
682 smb_get_krb5_error_message(smb_krb5_context
->krb5_context
,
684 talloc_free(mem_ctx
);
688 gcc
= talloc(cred
, struct gssapi_creds_container
);
690 talloc_free(mem_ctx
);
694 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
695 maj_stat
= gss_krb5_import_cred(&min_stat
, NULL
, princ
, ktc
->keytab
,
705 cred
->server_gss_creds_obtained
= cred
->keytab_obtained
;
706 talloc_set_destructor(gcc
, free_gssapi_creds
);
707 cred
->server_gss_creds
= gcc
;
710 talloc_free(mem_ctx
);
718 _PUBLIC_
void cli_credentials_set_kvno(struct cli_credentials
*cred
,
725 * Return Kerberos KVNO
728 _PUBLIC_
int cli_credentials_get_kvno(struct cli_credentials
*cred
)
734 const char **cli_credentials_get_enctype_strings(struct cli_credentials
*cred
)
736 /* If this is ever made user-configurable, we need to add code
737 * to remove/hide the other entries from the generated
739 static const char *default_enctypes
[] = {
741 "aes256-cts-hmac-sha1-96",
746 return default_enctypes
;
749 const char *cli_credentials_get_salt_principal(struct cli_credentials
*cred
)
751 return cred
->salt_principal
;
754 _PUBLIC_
void cli_credentials_set_salt_principal(struct cli_credentials
*cred
, const char *principal
)
756 cred
->salt_principal
= talloc_strdup(cred
, principal
);