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 event_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)
76 if (cred->ccache_obtained > obtained) {
80 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
81 ccache->ccache, &princ);
84 char *err_mess = smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
86 DEBUG(1,("failed to get principal from ccache: %s\n",
88 talloc_free(err_mess);
92 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
94 char *err_mess = smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context, ret, cred);
95 DEBUG(1,("failed to unparse principal from ccache: %s\n",
97 talloc_free(err_mess);
101 realm = krb5_princ_realm(ccache->smb_krb5_context->krb5_context, princ);
103 cli_credentials_set_principal(cred, name, obtained);
107 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
109 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
110 cred->ccache_obtained = obtained;
115 /* Free a memory ccache */
116 static int free_mccache(struct ccache_container *ccc)
118 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
123 /* Free a disk-based ccache */
124 static int free_dccache(struct ccache_container *ccc) {
125 krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
130 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
131 struct event_context *event_ctx,
132 struct loadparm_context *lp_ctx,
134 enum credentials_obtained obtained)
137 krb5_principal princ;
138 struct ccache_container *ccc;
139 if (cred->ccache_obtained > obtained) {
143 ccc = talloc(cred, struct ccache_container);
148 ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx,
149 &ccc->smb_krb5_context);
154 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
160 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
162 DEBUG(1,("failed to read krb5 ccache: %s: %s\n",
164 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
169 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
171 DEBUG(3,("failed to read default krb5 ccache: %s\n",
172 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
178 talloc_set_destructor(ccc, free_dccache);
180 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
183 DEBUG(3,("failed to get principal from default ccache: %s\n",
184 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
189 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
191 ret = cli_credentials_set_from_ccache(cred, ccc, obtained);
198 cred->ccache_obtained = obtained;
199 talloc_steal(cred, ccc);
201 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
206 static int cli_credentials_new_ccache(struct cli_credentials *cred,
207 struct event_context *event_ctx,
208 struct loadparm_context *lp_ctx,
209 struct ccache_container **_ccc)
212 struct ccache_container *ccc = talloc(cred, struct ccache_container);
218 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
226 ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx,
227 &ccc->smb_krb5_context);
232 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
237 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
240 DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n",
242 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
243 talloc_free(ccache_name);
248 talloc_set_destructor(ccc, free_mccache);
250 talloc_free(ccache_name);
257 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
258 struct event_context *event_ctx,
259 struct loadparm_context *lp_ctx,
260 struct ccache_container **ccc)
264 if (cred->machine_account_pending) {
265 cli_credentials_set_machine_account(cred, lp_ctx);
268 if (cred->ccache_obtained >= cred->ccache_threshold &&
269 cred->ccache_obtained > CRED_UNINITIALISED) {
273 if (cli_credentials_is_anonymous(cred)) {
277 ret = cli_credentials_new_ccache(cred, event_ctx, lp_ctx, ccc);
282 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, (*ccc)->ccache);
287 ret = cli_credentials_set_from_ccache(cred, *ccc,
288 (MAX(MAX(cred->principal_obtained,
289 cred->username_obtained),
290 cred->password_obtained)));
293 cred->ccache_obtained = cred->principal_obtained;
297 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
301 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
302 enum credentials_obtained obtained)
304 /* If the caller just changed the username/password etc, then
305 * any cached credentials are now invalid */
306 if (obtained >= cred->client_gss_creds_obtained) {
307 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
308 talloc_unlink(cred, cred->client_gss_creds);
309 cred->client_gss_creds = NULL;
311 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
313 /* Now that we know that the data is 'this specified', then
314 * don't allow something less 'known' to be returned as a
315 * ccache. Ie, if the username is on the commmand line, we
316 * don't want to later guess to use a file-based ccache */
317 if (obtained > cred->client_gss_creds_threshold) {
318 cred->client_gss_creds_threshold = obtained;
322 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
323 enum credentials_obtained obtained)
325 /* If the caller just changed the username/password etc, then
326 * any cached credentials are now invalid */
327 if (obtained >= cred->ccache_obtained) {
328 if (cred->ccache_obtained > CRED_UNINITIALISED) {
329 talloc_unlink(cred, cred->ccache);
332 cred->ccache_obtained = CRED_UNINITIALISED;
334 /* Now that we know that the data is 'this specified', then
335 * don't allow something less 'known' to be returned as a
336 * ccache. Ie, if the username is on the commmand line, we
337 * don't want to later guess to use a file-based ccache */
338 if (obtained > cred->ccache_threshold) {
339 cred->ccache_threshold = obtained;
342 cli_credentials_invalidate_client_gss_creds(cred,
346 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
348 OM_uint32 min_stat, maj_stat;
349 maj_stat = gss_release_cred(&min_stat, &gcc->creds);
353 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
354 struct event_context *event_ctx,
355 struct loadparm_context *lp_ctx,
356 struct gssapi_creds_container **_gcc)
359 OM_uint32 maj_stat, min_stat;
360 struct gssapi_creds_container *gcc;
361 struct ccache_container *ccache;
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;
367 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
370 DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret)));
374 gcc = talloc(cred, struct gssapi_creds_container);
379 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
391 /* don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG */
392 maj_stat = gss_set_cred_option(&min_stat, gcc->creds,
393 GSS_KRB5_CRED_NO_CI_FLAGS_X,
405 cred->client_gss_creds_obtained = cred->ccache_obtained;
406 talloc_set_destructor(gcc, free_gssapi_creds);
407 cred->client_gss_creds = gcc;
413 Set a gssapi cred_id_t into the credentials system. (Client case)
415 This grabs the credentials both 'intact' and getting the krb5
416 ccache out of it. This routine can be generalised in future for
417 the case where we deal with GSSAPI mechs other than krb5.
419 On sucess, the caller must not free gssapi_cred, as it now belongs
420 to the credentials system.
423 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
424 struct event_context *event_ctx,
425 struct loadparm_context *lp_ctx,
426 gss_cred_id_t gssapi_cred,
427 enum credentials_obtained obtained)
430 OM_uint32 maj_stat, min_stat;
431 struct ccache_container *ccc;
432 struct gssapi_creds_container *gcc;
433 if (cred->client_gss_creds_obtained > obtained) {
437 gcc = talloc(cred, struct gssapi_creds_container);
442 ret = cli_credentials_new_ccache(cred, event_ctx, lp_ctx, &ccc);
447 maj_stat = gss_krb5_copy_ccache(&min_stat,
448 gssapi_cred, ccc->ccache);
458 ret = cli_credentials_set_from_ccache(cred, ccc, obtained);
461 cred->ccache_obtained = obtained;
463 gcc->creds = gssapi_cred;
464 talloc_set_destructor(gcc, free_gssapi_creds);
466 /* set the clinet_gss_creds_obtained here, as it just
467 got set to UNINITIALISED by the calls above */
468 cred->client_gss_creds_obtained = obtained;
469 cred->client_gss_creds = gcc;
474 /* Get the keytab (actually, a container containing the krb5_keytab)
475 * attached to this context. If this hasn't been done or set before,
476 * it will be generated from the password.
478 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
479 struct event_context *event_ctx,
480 struct loadparm_context *lp_ctx,
481 struct keytab_container **_ktc)
484 struct keytab_container *ktc;
485 struct smb_krb5_context *smb_krb5_context;
486 const char **enctype_strings;
489 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
490 cred->username_obtained))) {
491 *_ktc = cred->keytab;
495 if (cli_credentials_is_anonymous(cred)) {
499 ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx,
505 mem_ctx = talloc_new(cred);
510 enctype_strings = cli_credentials_get_enctype_strings(cred);
512 ret = smb_krb5_create_memory_keytab(mem_ctx, cred,
514 enctype_strings, &ktc);
516 talloc_free(mem_ctx);
520 cred->keytab_obtained = (MAX(cred->principal_obtained,
521 cred->username_obtained));
523 talloc_steal(cred, ktc);
525 *_ktc = cred->keytab;
526 talloc_free(mem_ctx);
530 /* Given the name of a keytab (presumably in the format
531 * FILE:/etc/krb5.keytab), open it and attach it */
533 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
534 struct event_context *event_ctx,
535 struct loadparm_context *lp_ctx,
536 const char *keytab_name,
537 enum credentials_obtained obtained)
540 struct keytab_container *ktc;
541 struct smb_krb5_context *smb_krb5_context;
544 if (cred->keytab_obtained >= obtained) {
548 ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, &smb_krb5_context);
553 mem_ctx = talloc_new(cred);
558 ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context,
564 cred->keytab_obtained = obtained;
566 talloc_steal(cred, ktc);
568 talloc_free(mem_ctx);
573 _PUBLIC_ int cli_credentials_update_keytab(struct cli_credentials *cred,
574 struct event_context *event_ctx,
575 struct loadparm_context *lp_ctx)
578 struct keytab_container *ktc;
579 struct smb_krb5_context *smb_krb5_context;
580 const char **enctype_strings;
583 mem_ctx = talloc_new(cred);
588 ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, &smb_krb5_context);
590 talloc_free(mem_ctx);
594 enctype_strings = cli_credentials_get_enctype_strings(cred);
596 ret = cli_credentials_get_keytab(cred, event_ctx, lp_ctx, &ktc);
598 talloc_free(mem_ctx);
602 ret = smb_krb5_update_keytab(mem_ctx, cred, smb_krb5_context, enctype_strings, ktc);
604 talloc_free(mem_ctx);
608 /* Get server gss credentials (in gsskrb5, this means the keytab) */
610 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
611 struct event_context *event_ctx,
612 struct loadparm_context *lp_ctx,
613 struct gssapi_creds_container **_gcc)
616 OM_uint32 maj_stat, min_stat;
617 struct gssapi_creds_container *gcc;
618 struct keytab_container *ktc;
619 struct smb_krb5_context *smb_krb5_context;
621 krb5_principal princ;
623 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained,
624 MAX(cred->principal_obtained,
625 cred->username_obtained)))) {
626 *_gcc = cred->server_gss_creds;
630 ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, &smb_krb5_context);
635 ret = cli_credentials_get_keytab(cred, event_ctx, lp_ctx, &ktc);
637 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
641 mem_ctx = talloc_new(cred);
646 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ);
648 DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
649 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
651 talloc_free(mem_ctx);
655 gcc = talloc(cred, struct gssapi_creds_container);
657 talloc_free(mem_ctx);
661 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
662 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
672 cred->server_gss_creds_obtained = cred->keytab_obtained;
673 talloc_set_destructor(gcc, free_gssapi_creds);
674 cred->server_gss_creds = gcc;
677 talloc_free(mem_ctx);
685 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
692 * Return Kerberos KVNO
695 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
701 const char **cli_credentials_get_enctype_strings(struct cli_credentials *cred)
703 /* If this is ever made user-configurable, we need to add code
704 * to remove/hide the other entries from the generated
706 static const char *default_enctypes[] = {
708 "aes256-cts-hmac-sha1-96",
713 return default_enctypes;
716 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
718 return cred->salt_principal;
721 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
723 cred->salt_principal = talloc_strdup(cred, principal);