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 2 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, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "system/kerberos.h"
27 #include "auth/kerberos/kerberos.h"
28 #include "auth/credentials/credentials.h"
29 #include "auth/credentials/credentials_krb5.h"
31 int cli_credentials_get_krb5_context(struct cli_credentials
*cred
,
32 struct smb_krb5_context
**smb_krb5_context
)
35 if (cred
->smb_krb5_context
) {
36 *smb_krb5_context
= cred
->smb_krb5_context
;
40 ret
= smb_krb5_init_context(cred
, cli_credentials_get_event_context(cred
),
41 &cred
->smb_krb5_context
);
45 *smb_krb5_context
= cred
->smb_krb5_context
;
49 /* This needs to be called directly after the cli_credentials_init(),
50 * otherwise we might have problems with the krb5 context already
53 NTSTATUS
cli_credentials_set_krb5_context(struct cli_credentials
*cred
,
54 struct smb_krb5_context
*smb_krb5_context
)
56 if (!talloc_reference(cred
, smb_krb5_context
)) {
57 return NT_STATUS_NO_MEMORY
;
59 cred
->smb_krb5_context
= smb_krb5_context
;
63 int cli_credentials_set_from_ccache(struct cli_credentials
*cred
,
64 enum credentials_obtained obtained
)
72 if (cred
->ccache_obtained
> obtained
) {
76 ret
= krb5_cc_get_principal(cred
->ccache
->smb_krb5_context
->krb5_context
,
77 cred
->ccache
->ccache
, &princ
);
80 char *err_mess
= smb_get_krb5_error_message(cred
->ccache
->smb_krb5_context
->krb5_context
, ret
, cred
);
81 DEBUG(1,("failed to get principal from ccache: %s\n",
83 talloc_free(err_mess
);
87 ret
= krb5_unparse_name(cred
->ccache
->smb_krb5_context
->krb5_context
, princ
, &name
);
89 char *err_mess
= smb_get_krb5_error_message(cred
->ccache
->smb_krb5_context
->krb5_context
, ret
, cred
);
90 DEBUG(1,("failed to unparse principal from ccache: %s\n",
92 talloc_free(err_mess
);
96 realm
= krb5_princ_realm(cred
->ccache
->smb_krb5_context
->krb5_context
, princ
);
98 cli_credentials_set_principal(cred
, name
, obtained
);
102 krb5_free_principal(cred
->ccache
->smb_krb5_context
->krb5_context
, princ
);
104 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
105 cred
->ccache_obtained
= obtained
;
106 cli_credentials_invalidate_client_gss_creds(cred
, cred
->ccache_obtained
);
111 /* Free a memory ccache */
112 static int free_mccache(struct ccache_container
*ccc
)
114 krb5_cc_destroy(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
);
119 /* Free a disk-based ccache */
120 static int free_dccache(struct ccache_container
*ccc
) {
121 krb5_cc_close(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
);
126 int cli_credentials_set_ccache(struct cli_credentials
*cred
,
128 enum credentials_obtained obtained
)
131 krb5_principal princ
;
132 struct ccache_container
*ccc
;
133 if (cred
->ccache_obtained
> obtained
) {
137 ccc
= talloc(cred
, struct ccache_container
);
142 ret
= cli_credentials_get_krb5_context(cred
, &ccc
->smb_krb5_context
);
147 if (!talloc_reference(ccc
, ccc
->smb_krb5_context
)) {
153 ret
= krb5_cc_resolve(ccc
->smb_krb5_context
->krb5_context
, name
, &ccc
->ccache
);
155 DEBUG(1,("failed to read krb5 ccache: %s: %s\n",
157 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
, ret
, ccc
)));
162 ret
= krb5_cc_default(ccc
->smb_krb5_context
->krb5_context
, &ccc
->ccache
);
164 DEBUG(3,("failed to read default krb5 ccache: %s\n",
165 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
, ret
, ccc
)));
171 talloc_set_destructor(ccc
, free_dccache
);
173 ret
= krb5_cc_get_principal(ccc
->smb_krb5_context
->krb5_context
, ccc
->ccache
, &princ
);
176 DEBUG(3,("failed to get principal from default ccache: %s\n",
177 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
, ret
, ccc
)));
182 krb5_free_principal(ccc
->smb_krb5_context
->krb5_context
, princ
);
185 talloc_steal(cred
, ccc
);
187 ret
= cli_credentials_set_from_ccache(cred
, obtained
);
197 int cli_credentials_new_ccache(struct cli_credentials
*cred
, struct ccache_container
**_ccc
)
201 struct ccache_container
*ccc
= talloc(cred
, struct ccache_container
);
207 rand_string
= generate_random_str(NULL
, 16);
213 ccache_name
= talloc_asprintf(ccc
, "MEMORY:%s",
215 talloc_free(rand_string
);
222 ret
= cli_credentials_get_krb5_context(cred
, &ccc
->smb_krb5_context
);
227 if (!talloc_reference(ccc
, ccc
->smb_krb5_context
)) {
232 ret
= krb5_cc_resolve(ccc
->smb_krb5_context
->krb5_context
, ccache_name
, &ccc
->ccache
);
234 DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n",
236 smb_get_krb5_error_message(ccc
->smb_krb5_context
->krb5_context
, ret
, ccc
)));
237 talloc_free(ccache_name
);
242 talloc_set_destructor(ccc
, free_mccache
);
245 talloc_steal(cred
, ccc
);
246 talloc_free(ccache_name
);
252 cred
->ccache_obtained
= (MAX(MAX(cred
->principal_obtained
,
253 cred
->username_obtained
),
254 cred
->password_obtained
));
258 int cli_credentials_get_ccache(struct cli_credentials
*cred
,
259 struct ccache_container
**ccc
)
263 if (cred
->machine_account_pending
) {
264 cli_credentials_set_machine_account(cred
);
267 if (cred
->ccache_obtained
>= cred
->ccache_threshold
) {
271 if (cli_credentials_is_anonymous(cred
)) {
275 ret
= cli_credentials_new_ccache(cred
, NULL
);
279 ret
= kinit_to_ccache(cred
, cred
, cred
->ccache
->smb_krb5_context
, cred
->ccache
->ccache
);
283 ret
= cli_credentials_set_from_ccache(cred
, cred
->principal_obtained
);
292 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials
*cred
,
293 enum credentials_obtained obtained
)
295 /* If the caller just changed the username/password etc, then
296 * any cached credentials are now invalid */
297 if (obtained
>= cred
->client_gss_creds_obtained
) {
298 if (cred
->client_gss_creds_obtained
> CRED_UNINITIALISED
) {
299 talloc_free(cred
->client_gss_creds
);
301 cred
->client_gss_creds_obtained
= CRED_UNINITIALISED
;
303 /* Now that we know that the data is 'this specified', then
304 * don't allow something less 'known' to be returned as a
305 * ccache. Ie, if the username is on the commmand line, we
306 * don't want to later guess to use a file-based ccache */
307 if (obtained
> cred
->client_gss_creds_threshold
) {
308 cred
->client_gss_creds_threshold
= obtained
;
312 void cli_credentials_invalidate_ccache(struct cli_credentials
*cred
,
313 enum credentials_obtained obtained
)
315 /* If the caller just changed the username/password etc, then
316 * any cached credentials are now invalid */
317 if (obtained
>= cred
->ccache_obtained
) {
318 if (cred
->ccache_obtained
> CRED_UNINITIALISED
) {
319 talloc_free(cred
->ccache
);
321 cred
->ccache_obtained
= CRED_UNINITIALISED
;
323 /* Now that we know that the data is 'this specified', then
324 * don't allow something less 'known' to be returned as a
325 * ccache. Ie, if the username is on the commmand line, we
326 * don't want to later guess to use a file-based ccache */
327 if (obtained
> cred
->ccache_threshold
) {
328 cred
->ccache_threshold
= obtained
;
331 cli_credentials_invalidate_client_gss_creds(cred
,
335 static int free_gssapi_creds(struct gssapi_creds_container
*gcc
)
337 OM_uint32 min_stat
, maj_stat
;
338 maj_stat
= gss_release_cred(&min_stat
, &gcc
->creds
);
342 int cli_credentials_get_client_gss_creds(struct cli_credentials
*cred
,
343 struct gssapi_creds_container
**_gcc
)
346 OM_uint32 maj_stat
, min_stat
;
347 struct gssapi_creds_container
*gcc
;
348 struct ccache_container
*ccache
;
349 if (cred
->client_gss_creds_obtained
>= cred
->client_gss_creds_threshold
) {
350 *_gcc
= cred
->client_gss_creds
;
353 ret
= cli_credentials_get_ccache(cred
,
356 DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret
)));
360 gcc
= talloc(cred
, struct gssapi_creds_container
);
365 maj_stat
= gss_krb5_import_cred(&min_stat
, ccache
->ccache
, NULL
, NULL
,
375 cred
->client_gss_creds_obtained
= cred
->ccache_obtained
;
376 talloc_set_destructor(gcc
, free_gssapi_creds
);
377 cred
->client_gss_creds
= gcc
;
384 Set a gssapi cred_id_t into the credentails system. (Client case)
386 This grabs the credentials both 'intact' and getting the krb5
387 ccache out of it. This routine can be generalised in future for
388 the case where we deal with GSSAPI mechs other than krb5.
390 On sucess, the caller must not free gssapi_cred, as it now belongs
391 to the credentials system.
394 int cli_credentials_set_client_gss_creds(struct cli_credentials
*cred
,
395 gss_cred_id_t gssapi_cred
,
396 enum credentials_obtained obtained
)
399 OM_uint32 maj_stat
, min_stat
;
400 struct ccache_container
*ccc
;
401 struct gssapi_creds_container
*gcc
;
402 if (cred
->client_gss_creds_obtained
> obtained
) {
406 gcc
= talloc(cred
, struct gssapi_creds_container
);
411 ret
= cli_credentials_new_ccache(cred
, &ccc
);
416 maj_stat
= gss_krb5_copy_ccache(&min_stat
,
417 gssapi_cred
, ccc
->ccache
);
427 ret
= cli_credentials_set_from_ccache(cred
, obtained
);
430 gcc
->creds
= gssapi_cred
;
431 talloc_set_destructor(gcc
, free_gssapi_creds
);
433 /* set the clinet_gss_creds_obtained here, as it just
434 got set to UNINITIALISED by the calls above */
435 cred
->client_gss_creds_obtained
= obtained
;
436 cred
->client_gss_creds
= gcc
;
441 /* Get the keytab (actually, a container containing the krb5_keytab)
442 * attached to this context. If this hasn't been done or set before,
443 * it will be generated from the password.
445 int cli_credentials_get_keytab(struct cli_credentials
*cred
,
446 struct keytab_container
**_ktc
)
449 struct keytab_container
*ktc
;
450 struct smb_krb5_context
*smb_krb5_context
;
451 const char **enctype_strings
;
454 if (cred
->keytab_obtained
>= (MAX(cred
->principal_obtained
,
455 cred
->username_obtained
))) {
456 *_ktc
= cred
->keytab
;
460 if (cli_credentials_is_anonymous(cred
)) {
464 ret
= cli_credentials_get_krb5_context(cred
, &smb_krb5_context
);
469 mem_ctx
= talloc_new(cred
);
474 enctype_strings
= cli_credentials_get_enctype_strings(cred
);
476 ret
= smb_krb5_create_memory_keytab(mem_ctx
, cred
,
478 enctype_strings
, &ktc
);
480 talloc_free(mem_ctx
);
484 cred
->keytab_obtained
= (MAX(cred
->principal_obtained
,
485 cred
->username_obtained
));
487 talloc_steal(cred
, ktc
);
489 *_ktc
= cred
->keytab
;
490 talloc_free(mem_ctx
);
494 /* Given the name of a keytab (presumably in the format
495 * FILE:/etc/krb5.keytab), open it and attach it */
497 int cli_credentials_set_keytab_name(struct cli_credentials
*cred
,
498 const char *keytab_name
,
499 enum credentials_obtained obtained
)
502 struct keytab_container
*ktc
;
503 struct smb_krb5_context
*smb_krb5_context
;
506 if (cred
->keytab_obtained
>= obtained
) {
510 ret
= cli_credentials_get_krb5_context(cred
, &smb_krb5_context
);
515 mem_ctx
= talloc_new(cred
);
520 ret
= smb_krb5_open_keytab(mem_ctx
, smb_krb5_context
,
526 cred
->keytab_obtained
= obtained
;
528 talloc_steal(cred
, ktc
);
530 talloc_free(mem_ctx
);
535 int cli_credentials_update_keytab(struct cli_credentials
*cred
)
538 struct keytab_container
*ktc
;
539 struct smb_krb5_context
*smb_krb5_context
;
540 const char **enctype_strings
;
543 mem_ctx
= talloc_new(cred
);
548 ret
= cli_credentials_get_krb5_context(cred
, &smb_krb5_context
);
550 talloc_free(mem_ctx
);
554 enctype_strings
= cli_credentials_get_enctype_strings(cred
);
556 ret
= cli_credentials_get_keytab(cred
, &ktc
);
558 talloc_free(mem_ctx
);
562 ret
= smb_krb5_update_keytab(mem_ctx
, cred
, smb_krb5_context
, enctype_strings
, ktc
);
564 talloc_free(mem_ctx
);
568 /* Get server gss credentials (in gsskrb5, this means the keytab) */
570 int cli_credentials_get_server_gss_creds(struct cli_credentials
*cred
,
571 struct gssapi_creds_container
**_gcc
)
574 OM_uint32 maj_stat
, min_stat
;
575 struct gssapi_creds_container
*gcc
;
576 struct keytab_container
*ktc
;
577 struct smb_krb5_context
*smb_krb5_context
;
579 krb5_principal princ
;
581 if (cred
->server_gss_creds_obtained
>= (MAX(cred
->keytab_obtained
,
582 MAX(cred
->principal_obtained
,
583 cred
->username_obtained
)))) {
584 *_gcc
= cred
->server_gss_creds
;
588 ret
= cli_credentials_get_krb5_context(cred
, &smb_krb5_context
);
593 ret
= cli_credentials_get_keytab(cred
,
596 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret
)));
600 mem_ctx
= talloc_new(cred
);
605 ret
= principal_from_credentials(mem_ctx
, cred
, smb_krb5_context
, &princ
);
607 DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
608 smb_get_krb5_error_message(smb_krb5_context
->krb5_context
,
610 talloc_free(mem_ctx
);
614 gcc
= talloc(cred
, struct gssapi_creds_container
);
616 talloc_free(mem_ctx
);
620 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
621 maj_stat
= gss_krb5_import_cred(&min_stat
, NULL
, princ
, ktc
->keytab
,
631 cred
->server_gss_creds_obtained
= cred
->keytab_obtained
;
632 talloc_set_destructor(gcc
, free_gssapi_creds
);
633 cred
->server_gss_creds
= gcc
;
636 talloc_free(mem_ctx
);
644 void cli_credentials_set_kvno(struct cli_credentials
*cred
,
651 * Return Kerberos KVNO
654 int cli_credentials_get_kvno(struct cli_credentials
*cred
)
660 const char **cli_credentials_get_enctype_strings(struct cli_credentials
*cred
)
662 /* If this is ever made user-configurable, we need to add code
663 * to remove/hide the other entries from the generated
665 static const char *default_enctypes
[] = {
667 "aes256-cts-hmac-sha1-96",
672 return default_enctypes
;
675 const char *cli_credentials_get_salt_principal(struct cli_credentials
*cred
)
677 return cred
->salt_principal
;
680 void cli_credentials_set_salt_principal(struct cli_credentials
*cred
, const char *principal
)
682 cred
->salt_principal
= talloc_strdup(cred
, principal
);