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"
29 #include "param/param.h"
31 int cli_credentials_get_krb5_context(struct cli_credentials *cred,
32 struct loadparm_context *lp_ctx,
33 struct smb_krb5_context **smb_krb5_context)
36 if (cred->smb_krb5_context) {
37 *smb_krb5_context = cred->smb_krb5_context;
41 ret = smb_krb5_init_context(cred, cli_credentials_get_event_context(cred),
42 lp_ctx, &cred->smb_krb5_context);
44 cred->smb_krb5_context = NULL;
47 *smb_krb5_context = cred->smb_krb5_context;
51 /* This needs to be called directly after the cli_credentials_init(),
52 * otherwise we might have problems with the krb5 context already
55 NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
56 struct smb_krb5_context *smb_krb5_context)
58 if (!talloc_reference(cred, smb_krb5_context)) {
59 return NT_STATUS_NO_MEMORY;
61 cred->smb_krb5_context = smb_krb5_context;
65 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
66 struct ccache_container *ccache,
67 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 realm = krb5_princ_realm(ccache->smb_krb5_context->krb5_context, princ);
102 cli_credentials_set_principal(cred, name, obtained);
106 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
108 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
109 cred->ccache_obtained = obtained;
114 /* Free a memory ccache */
115 static int free_mccache(struct ccache_container *ccc)
117 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
122 /* Free a disk-based ccache */
123 static int free_dccache(struct ccache_container *ccc) {
124 krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
129 int cli_credentials_set_ccache(struct cli_credentials *cred,
130 struct loadparm_context *lp_ctx,
132 enum credentials_obtained obtained)
135 krb5_principal princ;
136 struct ccache_container *ccc;
137 if (cred->ccache_obtained > obtained) {
141 ccc = talloc(cred, struct ccache_container);
146 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
147 &ccc->smb_krb5_context);
152 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
158 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
160 DEBUG(1,("failed to read krb5 ccache: %s: %s\n",
162 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
167 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
169 DEBUG(3,("failed to read default krb5 ccache: %s\n",
170 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
176 talloc_set_destructor(ccc, free_dccache);
178 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
181 DEBUG(3,("failed to get principal from default ccache: %s\n",
182 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
187 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
189 ret = cli_credentials_set_from_ccache(cred, ccc, obtained);
196 cred->ccache_obtained = obtained;
197 talloc_steal(cred, ccc);
199 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
204 static int cli_credentials_new_ccache(struct cli_credentials *cred,
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, 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 int cli_credentials_get_ccache(struct cli_credentials *cred,
255 struct loadparm_context *lp_ctx,
256 struct ccache_container **ccc)
260 if (cred->machine_account_pending) {
261 cli_credentials_set_machine_account(cred, lp_ctx);
264 if (cred->ccache_obtained >= cred->ccache_threshold &&
265 cred->ccache_obtained > CRED_UNINITIALISED) {
269 if (cli_credentials_is_anonymous(cred)) {
273 ret = cli_credentials_new_ccache(cred, lp_ctx, ccc);
278 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, (*ccc)->ccache);
283 ret = cli_credentials_set_from_ccache(cred, *ccc,
284 (MAX(MAX(cred->principal_obtained,
285 cred->username_obtained),
286 cred->password_obtained)));
289 cred->ccache_obtained = cred->principal_obtained;
293 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
297 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
298 enum credentials_obtained obtained)
300 /* If the caller just changed the username/password etc, then
301 * any cached credentials are now invalid */
302 if (obtained >= cred->client_gss_creds_obtained) {
303 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
304 talloc_unlink(cred, cred->client_gss_creds);
305 cred->client_gss_creds = NULL;
307 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
309 /* Now that we know that the data is 'this specified', then
310 * don't allow something less 'known' to be returned as a
311 * ccache. Ie, if the username is on the commmand line, we
312 * don't want to later guess to use a file-based ccache */
313 if (obtained > cred->client_gss_creds_threshold) {
314 cred->client_gss_creds_threshold = obtained;
318 void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
319 enum credentials_obtained obtained)
321 /* If the caller just changed the username/password etc, then
322 * any cached credentials are now invalid */
323 if (obtained >= cred->ccache_obtained) {
324 if (cred->ccache_obtained > CRED_UNINITIALISED) {
325 talloc_unlink(cred, cred->ccache);
328 cred->ccache_obtained = CRED_UNINITIALISED;
330 /* Now that we know that the data is 'this specified', then
331 * don't allow something less 'known' to be returned as a
332 * ccache. Ie, if the username is on the commmand line, we
333 * don't want to later guess to use a file-based ccache */
334 if (obtained > cred->ccache_threshold) {
335 cred->ccache_threshold = obtained;
338 cli_credentials_invalidate_client_gss_creds(cred,
342 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
344 OM_uint32 min_stat, maj_stat;
345 maj_stat = gss_release_cred(&min_stat, &gcc->creds);
349 int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
350 struct loadparm_context *lp_ctx,
351 struct gssapi_creds_container **_gcc)
354 OM_uint32 maj_stat, min_stat;
355 struct gssapi_creds_container *gcc;
356 struct ccache_container *ccache;
357 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
358 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
359 *_gcc = cred->client_gss_creds;
362 ret = cli_credentials_get_ccache(cred, lp_ctx,
365 DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret)));
369 gcc = talloc(cred, struct gssapi_creds_container);
374 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
384 cred->client_gss_creds_obtained = cred->ccache_obtained;
385 talloc_set_destructor(gcc, free_gssapi_creds);
386 cred->client_gss_creds = gcc;
393 Set a gssapi cred_id_t into the credentials system. (Client case)
395 This grabs the credentials both 'intact' and getting the krb5
396 ccache out of it. This routine can be generalised in future for
397 the case where we deal with GSSAPI mechs other than krb5.
399 On sucess, the caller must not free gssapi_cred, as it now belongs
400 to the credentials system.
403 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
404 struct loadparm_context *lp_ctx,
405 gss_cred_id_t gssapi_cred,
406 enum credentials_obtained obtained)
409 OM_uint32 maj_stat, min_stat;
410 struct ccache_container *ccc;
411 struct gssapi_creds_container *gcc;
412 if (cred->client_gss_creds_obtained > obtained) {
416 gcc = talloc(cred, struct gssapi_creds_container);
421 ret = cli_credentials_new_ccache(cred, lp_ctx, &ccc);
426 maj_stat = gss_krb5_copy_ccache(&min_stat,
427 gssapi_cred, ccc->ccache);
437 ret = cli_credentials_set_from_ccache(cred, ccc, obtained);
440 cred->ccache_obtained = obtained;
442 gcc->creds = gssapi_cred;
443 talloc_set_destructor(gcc, free_gssapi_creds);
445 /* set the clinet_gss_creds_obtained here, as it just
446 got set to UNINITIALISED by the calls above */
447 cred->client_gss_creds_obtained = obtained;
448 cred->client_gss_creds = gcc;
453 /* Get the keytab (actually, a container containing the krb5_keytab)
454 * attached to this context. If this hasn't been done or set before,
455 * it will be generated from the password.
457 int cli_credentials_get_keytab(struct cli_credentials *cred,
458 struct loadparm_context *lp_ctx,
459 struct keytab_container **_ktc)
462 struct keytab_container *ktc;
463 struct smb_krb5_context *smb_krb5_context;
464 const char **enctype_strings;
467 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
468 cred->username_obtained))) {
469 *_ktc = cred->keytab;
473 if (cli_credentials_is_anonymous(cred)) {
477 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
483 mem_ctx = talloc_new(cred);
488 enctype_strings = cli_credentials_get_enctype_strings(cred);
490 ret = smb_krb5_create_memory_keytab(mem_ctx, cred,
492 enctype_strings, &ktc);
494 talloc_free(mem_ctx);
498 cred->keytab_obtained = (MAX(cred->principal_obtained,
499 cred->username_obtained));
501 talloc_steal(cred, ktc);
503 *_ktc = cred->keytab;
504 talloc_free(mem_ctx);
508 /* Given the name of a keytab (presumably in the format
509 * FILE:/etc/krb5.keytab), open it and attach it */
511 int cli_credentials_set_keytab_name(struct cli_credentials *cred,
512 struct loadparm_context *lp_ctx,
513 const char *keytab_name,
514 enum credentials_obtained obtained)
517 struct keytab_container *ktc;
518 struct smb_krb5_context *smb_krb5_context;
521 if (cred->keytab_obtained >= obtained) {
525 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
530 mem_ctx = talloc_new(cred);
535 ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context,
541 cred->keytab_obtained = obtained;
543 talloc_steal(cred, ktc);
545 talloc_free(mem_ctx);
550 int cli_credentials_update_keytab(struct cli_credentials *cred,
551 struct loadparm_context *lp_ctx)
554 struct keytab_container *ktc;
555 struct smb_krb5_context *smb_krb5_context;
556 const char **enctype_strings;
559 mem_ctx = talloc_new(cred);
564 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
566 talloc_free(mem_ctx);
570 enctype_strings = cli_credentials_get_enctype_strings(cred);
572 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
574 talloc_free(mem_ctx);
578 ret = smb_krb5_update_keytab(mem_ctx, cred, smb_krb5_context, enctype_strings, ktc);
580 talloc_free(mem_ctx);
584 /* Get server gss credentials (in gsskrb5, this means the keytab) */
586 int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
587 struct loadparm_context *lp_ctx,
588 struct gssapi_creds_container **_gcc)
591 OM_uint32 maj_stat, min_stat;
592 struct gssapi_creds_container *gcc;
593 struct keytab_container *ktc;
594 struct smb_krb5_context *smb_krb5_context;
596 krb5_principal princ;
598 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained,
599 MAX(cred->principal_obtained,
600 cred->username_obtained)))) {
601 *_gcc = cred->server_gss_creds;
605 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
610 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
612 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
616 mem_ctx = talloc_new(cred);
621 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ);
623 DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
624 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
626 talloc_free(mem_ctx);
630 gcc = talloc(cred, struct gssapi_creds_container);
632 talloc_free(mem_ctx);
636 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
637 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
647 cred->server_gss_creds_obtained = cred->keytab_obtained;
648 talloc_set_destructor(gcc, free_gssapi_creds);
649 cred->server_gss_creds = gcc;
652 talloc_free(mem_ctx);
660 void cli_credentials_set_kvno(struct cli_credentials *cred,
667 * Return Kerberos KVNO
670 int cli_credentials_get_kvno(struct cli_credentials *cred)
676 const char **cli_credentials_get_enctype_strings(struct cli_credentials *cred)
678 /* If this is ever made user-configurable, we need to add code
679 * to remove/hide the other entries from the generated
681 static const char *default_enctypes[] = {
683 "aes256-cts-hmac-sha1-96",
688 return default_enctypes;
691 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
693 return cred->salt_principal;
696 void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
698 cred->salt_principal = talloc_strdup(cred, principal);