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_krb5.h"
30 int cli_credentials_get_krb5_context(struct cli_credentials *cred,
31 struct smb_krb5_context **smb_krb5_context)
34 if (cred->smb_krb5_context) {
35 *smb_krb5_context = cred->smb_krb5_context;
39 ret = smb_krb5_init_context(cred, cli_credentials_get_event_context(cred),
40 &cred->smb_krb5_context);
44 *smb_krb5_context = cred->smb_krb5_context;
48 /* This needs to be called directly after the cli_credentials_init(),
49 * otherwise we might have problems with the krb5 context already
52 NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
53 struct smb_krb5_context *smb_krb5_context)
55 if (!talloc_reference(cred, smb_krb5_context)) {
56 return NT_STATUS_NO_MEMORY;
58 cred->smb_krb5_context = smb_krb5_context;
62 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
63 struct ccache_container *ccache,
64 enum credentials_obtained obtained)
72 if (cred->ccache_obtained > obtained) {
76 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
77 ccache->ccache, &princ);
80 char *err_mess = smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
82 DEBUG(1,("failed to get principal from ccache: %s\n",
84 talloc_free(err_mess);
88 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
90 char *err_mess = smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context, ret, cred);
91 DEBUG(1,("failed to unparse principal from ccache: %s\n",
93 talloc_free(err_mess);
97 realm = krb5_princ_realm(ccache->smb_krb5_context->krb5_context, princ);
99 cli_credentials_set_principal(cred, name, obtained);
103 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
105 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
106 cred->ccache_obtained = 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);
184 ret = cli_credentials_set_from_ccache(cred, ccc, obtained);
191 cred->ccache_obtained = obtained;
192 talloc_steal(cred, ccc);
194 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
199 static int cli_credentials_new_ccache(struct cli_credentials *cred, struct ccache_container **_ccc)
203 struct ccache_container *ccc = talloc(cred, struct ccache_container);
209 rand_string = generate_random_str(NULL, 16);
215 ccache_name = talloc_asprintf(ccc, "MEMORY:%s",
217 talloc_free(rand_string);
224 ret = cli_credentials_get_krb5_context(cred, &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, &ccc->ccache);
236 DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n",
238 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
239 talloc_free(ccache_name);
244 talloc_set_destructor(ccc, free_mccache);
246 talloc_free(ccache_name);
253 int cli_credentials_get_ccache(struct cli_credentials *cred,
254 struct ccache_container **ccc)
258 if (cred->machine_account_pending) {
259 cli_credentials_set_machine_account(cred);
262 if (cred->ccache_obtained >= cred->ccache_threshold) {
266 if (cli_credentials_is_anonymous(cred)) {
270 ret = cli_credentials_new_ccache(cred, ccc);
275 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, (*ccc)->ccache);
280 ret = cli_credentials_set_from_ccache(cred, *ccc,
281 (MAX(MAX(cred->principal_obtained,
282 cred->username_obtained),
283 cred->password_obtained)));
286 cred->ccache_obtained = cred->principal_obtained;
290 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
294 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
295 enum credentials_obtained obtained)
297 /* If the caller just changed the username/password etc, then
298 * any cached credentials are now invalid */
299 if (obtained >= cred->client_gss_creds_obtained) {
300 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
301 talloc_free(cred->client_gss_creds);
302 cred->client_gss_creds = NULL;
304 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
306 /* Now that we know that the data is 'this specified', then
307 * don't allow something less 'known' to be returned as a
308 * ccache. Ie, if the username is on the commmand line, we
309 * don't want to later guess to use a file-based ccache */
310 if (obtained > cred->client_gss_creds_threshold) {
311 cred->client_gss_creds_threshold = obtained;
315 void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
316 enum credentials_obtained obtained)
318 /* If the caller just changed the username/password etc, then
319 * any cached credentials are now invalid */
320 if (obtained >= cred->ccache_obtained) {
321 if (cred->ccache_obtained > CRED_UNINITIALISED) {
322 talloc_free(cred->ccache);
325 cred->ccache_obtained = CRED_UNINITIALISED;
327 /* Now that we know that the data is 'this specified', then
328 * don't allow something less 'known' to be returned as a
329 * ccache. Ie, if the username is on the commmand line, we
330 * don't want to later guess to use a file-based ccache */
331 if (obtained > cred->ccache_threshold) {
332 cred->ccache_threshold = obtained;
335 cli_credentials_invalidate_client_gss_creds(cred,
339 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
341 OM_uint32 min_stat, maj_stat;
342 maj_stat = gss_release_cred(&min_stat, &gcc->creds);
346 int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
347 struct gssapi_creds_container **_gcc)
350 OM_uint32 maj_stat, min_stat;
351 struct gssapi_creds_container *gcc;
352 struct ccache_container *ccache;
353 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold) {
354 *_gcc = cred->client_gss_creds;
357 ret = cli_credentials_get_ccache(cred,
360 DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret)));
364 gcc = talloc(cred, struct gssapi_creds_container);
369 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
379 cred->client_gss_creds_obtained = cred->ccache_obtained;
380 talloc_set_destructor(gcc, free_gssapi_creds);
381 cred->client_gss_creds = gcc;
388 Set a gssapi cred_id_t into the credentails system. (Client case)
390 This grabs the credentials both 'intact' and getting the krb5
391 ccache out of it. This routine can be generalised in future for
392 the case where we deal with GSSAPI mechs other than krb5.
394 On sucess, the caller must not free gssapi_cred, as it now belongs
395 to the credentials system.
398 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
399 gss_cred_id_t gssapi_cred,
400 enum credentials_obtained obtained)
403 OM_uint32 maj_stat, min_stat;
404 struct ccache_container *ccc;
405 struct gssapi_creds_container *gcc;
406 if (cred->client_gss_creds_obtained > obtained) {
410 gcc = talloc(cred, struct gssapi_creds_container);
415 ret = cli_credentials_new_ccache(cred, &ccc);
420 maj_stat = gss_krb5_copy_ccache(&min_stat,
421 gssapi_cred, ccc->ccache);
431 ret = cli_credentials_set_from_ccache(cred, ccc, obtained);
434 cred->ccache_obtained = obtained;
436 gcc->creds = gssapi_cred;
437 talloc_set_destructor(gcc, free_gssapi_creds);
439 /* set the clinet_gss_creds_obtained here, as it just
440 got set to UNINITIALISED by the calls above */
441 cred->client_gss_creds_obtained = obtained;
442 cred->client_gss_creds = gcc;
447 /* Get the keytab (actually, a container containing the krb5_keytab)
448 * attached to this context. If this hasn't been done or set before,
449 * it will be generated from the password.
451 int cli_credentials_get_keytab(struct cli_credentials *cred,
452 struct keytab_container **_ktc)
455 struct keytab_container *ktc;
456 struct smb_krb5_context *smb_krb5_context;
457 const char **enctype_strings;
460 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
461 cred->username_obtained))) {
462 *_ktc = cred->keytab;
466 if (cli_credentials_is_anonymous(cred)) {
470 ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
475 mem_ctx = talloc_new(cred);
480 enctype_strings = cli_credentials_get_enctype_strings(cred);
482 ret = smb_krb5_create_memory_keytab(mem_ctx, cred,
484 enctype_strings, &ktc);
486 talloc_free(mem_ctx);
490 cred->keytab_obtained = (MAX(cred->principal_obtained,
491 cred->username_obtained));
493 talloc_steal(cred, ktc);
495 *_ktc = cred->keytab;
496 talloc_free(mem_ctx);
500 /* Given the name of a keytab (presumably in the format
501 * FILE:/etc/krb5.keytab), open it and attach it */
503 int cli_credentials_set_keytab_name(struct cli_credentials *cred,
504 const char *keytab_name,
505 enum credentials_obtained obtained)
508 struct keytab_container *ktc;
509 struct smb_krb5_context *smb_krb5_context;
512 if (cred->keytab_obtained >= obtained) {
516 ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
521 mem_ctx = talloc_new(cred);
526 ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context,
532 cred->keytab_obtained = obtained;
534 talloc_steal(cred, ktc);
536 talloc_free(mem_ctx);
541 int cli_credentials_update_keytab(struct cli_credentials *cred)
544 struct keytab_container *ktc;
545 struct smb_krb5_context *smb_krb5_context;
546 const char **enctype_strings;
549 mem_ctx = talloc_new(cred);
554 ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
556 talloc_free(mem_ctx);
560 enctype_strings = cli_credentials_get_enctype_strings(cred);
562 ret = cli_credentials_get_keytab(cred, &ktc);
564 talloc_free(mem_ctx);
568 ret = smb_krb5_update_keytab(mem_ctx, cred, smb_krb5_context, enctype_strings, ktc);
570 talloc_free(mem_ctx);
574 /* Get server gss credentials (in gsskrb5, this means the keytab) */
576 int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
577 struct gssapi_creds_container **_gcc)
580 OM_uint32 maj_stat, min_stat;
581 struct gssapi_creds_container *gcc;
582 struct keytab_container *ktc;
583 struct smb_krb5_context *smb_krb5_context;
585 krb5_principal princ;
587 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained,
588 MAX(cred->principal_obtained,
589 cred->username_obtained)))) {
590 *_gcc = cred->server_gss_creds;
594 ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
599 ret = cli_credentials_get_keytab(cred,
602 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
606 mem_ctx = talloc_new(cred);
611 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ);
613 DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
614 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
616 talloc_free(mem_ctx);
620 gcc = talloc(cred, struct gssapi_creds_container);
622 talloc_free(mem_ctx);
626 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
627 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
637 cred->server_gss_creds_obtained = cred->keytab_obtained;
638 talloc_set_destructor(gcc, free_gssapi_creds);
639 cred->server_gss_creds = gcc;
642 talloc_free(mem_ctx);
650 void cli_credentials_set_kvno(struct cli_credentials *cred,
657 * Return Kerberos KVNO
660 int cli_credentials_get_kvno(struct cli_credentials *cred)
666 const char **cli_credentials_get_enctype_strings(struct cli_credentials *cred)
668 /* If this is ever made user-configurable, we need to add code
669 * to remove/hide the other entries from the generated
671 static const char *default_enctypes[] = {
673 "aes256-cts-hmac-sha1-96",
678 return default_enctypes;
681 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
683 return cred->salt_principal;
686 void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
688 cred->salt_principal = talloc_strdup(cred, principal);