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 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
64 struct ccache_container *ccache,
65 enum credentials_obtained obtained)
73 if (cred->ccache_obtained > obtained) {
77 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
78 ccache->ccache, &princ);
81 char *err_mess = smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
83 DEBUG(1,("failed to get principal from ccache: %s\n",
85 talloc_free(err_mess);
89 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
91 char *err_mess = smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context, ret, cred);
92 DEBUG(1,("failed to unparse principal from ccache: %s\n",
94 talloc_free(err_mess);
98 realm = krb5_princ_realm(ccache->smb_krb5_context->krb5_context, princ);
100 cli_credentials_set_principal(cred, name, obtained);
104 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
106 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
107 cred->ccache_obtained = obtained;
112 /* Free a memory ccache */
113 static int free_mccache(struct ccache_container *ccc)
115 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
120 /* Free a disk-based ccache */
121 static int free_dccache(struct ccache_container *ccc) {
122 krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
127 int cli_credentials_set_ccache(struct cli_credentials *cred,
129 enum credentials_obtained obtained)
132 krb5_principal princ;
133 struct ccache_container *ccc;
134 if (cred->ccache_obtained > obtained) {
138 ccc = talloc(cred, struct ccache_container);
143 ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
148 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
154 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
156 DEBUG(1,("failed to read krb5 ccache: %s: %s\n",
158 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
163 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
165 DEBUG(3,("failed to read default krb5 ccache: %s\n",
166 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
172 talloc_set_destructor(ccc, free_dccache);
174 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
177 DEBUG(3,("failed to get principal from default ccache: %s\n",
178 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
183 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
185 ret = cli_credentials_set_from_ccache(cred, ccc, obtained);
192 cred->ccache_obtained = obtained;
193 talloc_steal(cred, ccc);
195 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
200 static int cli_credentials_new_ccache(struct cli_credentials *cred, struct ccache_container **_ccc)
204 struct ccache_container *ccc = talloc(cred, struct ccache_container);
210 rand_string = generate_random_str(NULL, 16);
216 ccache_name = talloc_asprintf(ccc, "MEMORY:%s",
218 talloc_free(rand_string);
225 ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
230 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
235 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, &ccc->ccache);
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 ccache_container **ccc)
259 if (cred->machine_account_pending) {
260 cli_credentials_set_machine_account(cred);
263 if (cred->ccache_obtained >= cred->ccache_threshold) {
267 if (cli_credentials_is_anonymous(cred)) {
271 ret = cli_credentials_new_ccache(cred, ccc);
276 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, (*ccc)->ccache);
281 ret = cli_credentials_set_from_ccache(cred, *ccc,
282 (MAX(MAX(cred->principal_obtained,
283 cred->username_obtained),
284 cred->password_obtained)));
287 cred->ccache_obtained = cred->principal_obtained;
291 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
295 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
296 enum credentials_obtained obtained)
298 /* If the caller just changed the username/password etc, then
299 * any cached credentials are now invalid */
300 if (obtained >= cred->client_gss_creds_obtained) {
301 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
302 talloc_free(cred->client_gss_creds);
303 cred->client_gss_creds = NULL;
305 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
307 /* Now that we know that the data is 'this specified', then
308 * don't allow something less 'known' to be returned as a
309 * ccache. Ie, if the username is on the commmand line, we
310 * don't want to later guess to use a file-based ccache */
311 if (obtained > cred->client_gss_creds_threshold) {
312 cred->client_gss_creds_threshold = obtained;
316 void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
317 enum credentials_obtained obtained)
319 /* If the caller just changed the username/password etc, then
320 * any cached credentials are now invalid */
321 if (obtained >= cred->ccache_obtained) {
322 if (cred->ccache_obtained > CRED_UNINITIALISED) {
323 talloc_free(cred->ccache);
326 cred->ccache_obtained = CRED_UNINITIALISED;
328 /* Now that we know that the data is 'this specified', then
329 * don't allow something less 'known' to be returned as a
330 * ccache. Ie, if the username is on the commmand line, we
331 * don't want to later guess to use a file-based ccache */
332 if (obtained > cred->ccache_threshold) {
333 cred->ccache_threshold = obtained;
336 cli_credentials_invalidate_client_gss_creds(cred,
340 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
342 OM_uint32 min_stat, maj_stat;
343 maj_stat = gss_release_cred(&min_stat, &gcc->creds);
347 int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
348 struct gssapi_creds_container **_gcc)
351 OM_uint32 maj_stat, min_stat;
352 struct gssapi_creds_container *gcc;
353 struct ccache_container *ccache;
354 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold) {
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 credentails 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, &smb_krb5_context);
476 mem_ctx = talloc_new(cred);
481 enctype_strings = cli_credentials_get_enctype_strings(cred);
483 ret = smb_krb5_create_memory_keytab(mem_ctx, cred,
485 enctype_strings, &ktc);
487 talloc_free(mem_ctx);
491 cred->keytab_obtained = (MAX(cred->principal_obtained,
492 cred->username_obtained));
494 talloc_steal(cred, ktc);
496 *_ktc = cred->keytab;
497 talloc_free(mem_ctx);
501 /* Given the name of a keytab (presumably in the format
502 * FILE:/etc/krb5.keytab), open it and attach it */
504 int cli_credentials_set_keytab_name(struct cli_credentials *cred,
505 const char *keytab_name,
506 enum credentials_obtained obtained)
509 struct keytab_container *ktc;
510 struct smb_krb5_context *smb_krb5_context;
513 if (cred->keytab_obtained >= obtained) {
517 ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
522 mem_ctx = talloc_new(cred);
527 ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context,
533 cred->keytab_obtained = obtained;
535 talloc_steal(cred, ktc);
537 talloc_free(mem_ctx);
542 int cli_credentials_update_keytab(struct cli_credentials *cred)
545 struct keytab_container *ktc;
546 struct smb_krb5_context *smb_krb5_context;
547 const char **enctype_strings;
550 mem_ctx = talloc_new(cred);
555 ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
557 talloc_free(mem_ctx);
561 enctype_strings = cli_credentials_get_enctype_strings(cred);
563 ret = cli_credentials_get_keytab(cred, &ktc);
565 talloc_free(mem_ctx);
569 ret = smb_krb5_update_keytab(mem_ctx, cred, smb_krb5_context, enctype_strings, ktc);
571 talloc_free(mem_ctx);
575 /* Get server gss credentials (in gsskrb5, this means the keytab) */
577 int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
578 struct gssapi_creds_container **_gcc)
581 OM_uint32 maj_stat, min_stat;
582 struct gssapi_creds_container *gcc;
583 struct keytab_container *ktc;
584 struct smb_krb5_context *smb_krb5_context;
586 krb5_principal princ;
588 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained,
589 MAX(cred->principal_obtained,
590 cred->username_obtained)))) {
591 *_gcc = cred->server_gss_creds;
595 ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
600 ret = cli_credentials_get_keytab(cred,
603 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
607 mem_ctx = talloc_new(cred);
612 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ);
614 DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
615 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
617 talloc_free(mem_ctx);
621 gcc = talloc(cred, struct gssapi_creds_container);
623 talloc_free(mem_ctx);
627 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
628 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
638 cred->server_gss_creds_obtained = cred->keytab_obtained;
639 talloc_set_destructor(gcc, free_gssapi_creds);
640 cred->server_gss_creds = gcc;
643 talloc_free(mem_ctx);
651 void cli_credentials_set_kvno(struct cli_credentials *cred,
658 * Return Kerberos KVNO
661 int cli_credentials_get_kvno(struct cli_credentials *cred)
667 const char **cli_credentials_get_enctype_strings(struct cli_credentials *cred)
669 /* If this is ever made user-configurable, we need to add code
670 * to remove/hide the other entries from the generated
672 static const char *default_enctypes[] = {
674 "aes256-cts-hmac-sha1-96",
679 return default_enctypes;
682 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
684 return cred->salt_principal;
687 void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
689 cred->salt_principal = talloc_strdup(cred, principal);