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,
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, global_loadparm,
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 ccache_container **_ccc)
207 struct ccache_container *ccc = talloc(cred, struct ccache_container);
213 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
221 ret = cli_credentials_get_krb5_context(cred, global_loadparm,
222 &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,
235 DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n",
237 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
238 talloc_free(ccache_name);
243 talloc_set_destructor(ccc, free_mccache);
245 talloc_free(ccache_name);
252 int cli_credentials_get_ccache(struct cli_credentials *cred,
253 struct ccache_container **ccc)
257 if (cred->machine_account_pending) {
258 cli_credentials_set_machine_account(cred);
261 if (cred->ccache_obtained >= cred->ccache_threshold &&
262 cred->ccache_obtained > CRED_UNINITIALISED) {
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_unlink(cred, 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_unlink(cred, 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 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
355 *_gcc = cred->client_gss_creds;
358 ret = cli_credentials_get_ccache(cred,
361 DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret)));
365 gcc = talloc(cred, struct gssapi_creds_container);
370 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
380 cred->client_gss_creds_obtained = cred->ccache_obtained;
381 talloc_set_destructor(gcc, free_gssapi_creds);
382 cred->client_gss_creds = gcc;
389 Set a gssapi cred_id_t into the credentials system. (Client case)
391 This grabs the credentials both 'intact' and getting the krb5
392 ccache out of it. This routine can be generalised in future for
393 the case where we deal with GSSAPI mechs other than krb5.
395 On sucess, the caller must not free gssapi_cred, as it now belongs
396 to the credentials system.
399 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
400 gss_cred_id_t gssapi_cred,
401 enum credentials_obtained obtained)
404 OM_uint32 maj_stat, min_stat;
405 struct ccache_container *ccc;
406 struct gssapi_creds_container *gcc;
407 if (cred->client_gss_creds_obtained > obtained) {
411 gcc = talloc(cred, struct gssapi_creds_container);
416 ret = cli_credentials_new_ccache(cred, &ccc);
421 maj_stat = gss_krb5_copy_ccache(&min_stat,
422 gssapi_cred, ccc->ccache);
432 ret = cli_credentials_set_from_ccache(cred, ccc, obtained);
435 cred->ccache_obtained = obtained;
437 gcc->creds = gssapi_cred;
438 talloc_set_destructor(gcc, free_gssapi_creds);
440 /* set the clinet_gss_creds_obtained here, as it just
441 got set to UNINITIALISED by the calls above */
442 cred->client_gss_creds_obtained = obtained;
443 cred->client_gss_creds = gcc;
448 /* Get the keytab (actually, a container containing the krb5_keytab)
449 * attached to this context. If this hasn't been done or set before,
450 * it will be generated from the password.
452 int cli_credentials_get_keytab(struct cli_credentials *cred,
453 struct keytab_container **_ktc)
456 struct keytab_container *ktc;
457 struct smb_krb5_context *smb_krb5_context;
458 const char **enctype_strings;
461 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
462 cred->username_obtained))) {
463 *_ktc = cred->keytab;
467 if (cli_credentials_is_anonymous(cred)) {
471 ret = cli_credentials_get_krb5_context(cred, global_loadparm,
477 mem_ctx = talloc_new(cred);
482 enctype_strings = cli_credentials_get_enctype_strings(cred);
484 ret = smb_krb5_create_memory_keytab(mem_ctx, cred,
486 enctype_strings, &ktc);
488 talloc_free(mem_ctx);
492 cred->keytab_obtained = (MAX(cred->principal_obtained,
493 cred->username_obtained));
495 talloc_steal(cred, ktc);
497 *_ktc = cred->keytab;
498 talloc_free(mem_ctx);
502 /* Given the name of a keytab (presumably in the format
503 * FILE:/etc/krb5.keytab), open it and attach it */
505 int cli_credentials_set_keytab_name(struct cli_credentials *cred,
506 const char *keytab_name,
507 enum credentials_obtained obtained)
510 struct keytab_container *ktc;
511 struct smb_krb5_context *smb_krb5_context;
514 if (cred->keytab_obtained >= obtained) {
518 ret = cli_credentials_get_krb5_context(cred, global_loadparm, &smb_krb5_context);
523 mem_ctx = talloc_new(cred);
528 ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context,
534 cred->keytab_obtained = obtained;
536 talloc_steal(cred, ktc);
538 talloc_free(mem_ctx);
543 int cli_credentials_update_keytab(struct cli_credentials *cred)
546 struct keytab_container *ktc;
547 struct smb_krb5_context *smb_krb5_context;
548 const char **enctype_strings;
551 mem_ctx = talloc_new(cred);
556 ret = cli_credentials_get_krb5_context(cred, global_loadparm, &smb_krb5_context);
558 talloc_free(mem_ctx);
562 enctype_strings = cli_credentials_get_enctype_strings(cred);
564 ret = cli_credentials_get_keytab(cred, &ktc);
566 talloc_free(mem_ctx);
570 ret = smb_krb5_update_keytab(mem_ctx, cred, smb_krb5_context, enctype_strings, ktc);
572 talloc_free(mem_ctx);
576 /* Get server gss credentials (in gsskrb5, this means the keytab) */
578 int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
579 struct gssapi_creds_container **_gcc)
582 OM_uint32 maj_stat, min_stat;
583 struct gssapi_creds_container *gcc;
584 struct keytab_container *ktc;
585 struct smb_krb5_context *smb_krb5_context;
587 krb5_principal princ;
589 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained,
590 MAX(cred->principal_obtained,
591 cred->username_obtained)))) {
592 *_gcc = cred->server_gss_creds;
596 ret = cli_credentials_get_krb5_context(cred, global_loadparm, &smb_krb5_context);
601 ret = cli_credentials_get_keytab(cred,
604 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
608 mem_ctx = talloc_new(cred);
613 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ);
615 DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
616 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
618 talloc_free(mem_ctx);
622 gcc = talloc(cred, struct gssapi_creds_container);
624 talloc_free(mem_ctx);
628 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
629 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
639 cred->server_gss_creds_obtained = cred->keytab_obtained;
640 talloc_set_destructor(gcc, free_gssapi_creds);
641 cred->server_gss_creds = gcc;
644 talloc_free(mem_ctx);
652 void cli_credentials_set_kvno(struct cli_credentials *cred,
659 * Return Kerberos KVNO
662 int cli_credentials_get_kvno(struct cli_credentials *cred)
668 const char **cli_credentials_get_enctype_strings(struct cli_credentials *cred)
670 /* If this is ever made user-configurable, we need to add code
671 * to remove/hide the other entries from the generated
673 static const char *default_enctypes[] = {
675 "aes256-cts-hmac-sha1-96",
680 return default_enctypes;
683 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
685 return cred->salt_principal;
688 void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
690 cred->salt_principal = talloc_strdup(cred, principal);