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 loadparm_context *lp_ctx,
34 struct smb_krb5_context **smb_krb5_context)
37 if (cred->smb_krb5_context) {
38 *smb_krb5_context = cred->smb_krb5_context;
42 ret = smb_krb5_init_context(cred, cli_credentials_get_event_context(cred),
43 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 loadparm_context *lp_ctx,
133 enum credentials_obtained obtained)
136 krb5_principal princ;
137 struct ccache_container *ccc;
138 if (cred->ccache_obtained > obtained) {
142 ccc = talloc(cred, struct ccache_container);
147 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
148 &ccc->smb_krb5_context);
153 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
159 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
161 DEBUG(1,("failed to read krb5 ccache: %s: %s\n",
163 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
168 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
170 DEBUG(3,("failed to read default krb5 ccache: %s\n",
171 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
177 talloc_set_destructor(ccc, free_dccache);
179 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
182 DEBUG(3,("failed to get principal from default ccache: %s\n",
183 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
188 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
190 ret = cli_credentials_set_from_ccache(cred, ccc, obtained);
197 cred->ccache_obtained = obtained;
198 talloc_steal(cred, ccc);
200 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
205 static int cli_credentials_new_ccache(struct cli_credentials *cred,
206 struct loadparm_context *lp_ctx,
207 struct ccache_container **_ccc)
210 struct ccache_container *ccc = talloc(cred, struct ccache_container);
216 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
224 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
225 &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,
238 DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n",
240 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
241 talloc_free(ccache_name);
246 talloc_set_destructor(ccc, free_mccache);
248 talloc_free(ccache_name);
255 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
256 struct loadparm_context *lp_ctx,
257 struct ccache_container **ccc)
261 if (cred->machine_account_pending) {
262 cli_credentials_set_machine_account(cred, lp_ctx);
265 if (cred->ccache_obtained >= cred->ccache_threshold &&
266 cred->ccache_obtained > CRED_UNINITIALISED) {
270 if (cli_credentials_is_anonymous(cred)) {
274 ret = cli_credentials_new_ccache(cred, lp_ctx, ccc);
279 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, (*ccc)->ccache);
284 ret = cli_credentials_set_from_ccache(cred, *ccc,
285 (MAX(MAX(cred->principal_obtained,
286 cred->username_obtained),
287 cred->password_obtained)));
290 cred->ccache_obtained = cred->principal_obtained;
294 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
298 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
299 enum credentials_obtained obtained)
301 /* If the caller just changed the username/password etc, then
302 * any cached credentials are now invalid */
303 if (obtained >= cred->client_gss_creds_obtained) {
304 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
305 talloc_unlink(cred, cred->client_gss_creds);
306 cred->client_gss_creds = NULL;
308 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
310 /* Now that we know that the data is 'this specified', then
311 * don't allow something less 'known' to be returned as a
312 * ccache. Ie, if the username is on the commmand line, we
313 * don't want to later guess to use a file-based ccache */
314 if (obtained > cred->client_gss_creds_threshold) {
315 cred->client_gss_creds_threshold = obtained;
319 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
320 enum credentials_obtained obtained)
322 /* If the caller just changed the username/password etc, then
323 * any cached credentials are now invalid */
324 if (obtained >= cred->ccache_obtained) {
325 if (cred->ccache_obtained > CRED_UNINITIALISED) {
326 talloc_unlink(cred, cred->ccache);
329 cred->ccache_obtained = CRED_UNINITIALISED;
331 /* Now that we know that the data is 'this specified', then
332 * don't allow something less 'known' to be returned as a
333 * ccache. Ie, if the username is on the commmand line, we
334 * don't want to later guess to use a file-based ccache */
335 if (obtained > cred->ccache_threshold) {
336 cred->ccache_threshold = obtained;
339 cli_credentials_invalidate_client_gss_creds(cred,
343 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
345 OM_uint32 min_stat, maj_stat;
346 maj_stat = gss_release_cred(&min_stat, &gcc->creds);
350 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
351 struct loadparm_context *lp_ctx,
352 struct gssapi_creds_container **_gcc)
355 OM_uint32 maj_stat, min_stat;
356 struct gssapi_creds_container *gcc;
357 struct ccache_container *ccache;
358 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
359 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
360 *_gcc = cred->client_gss_creds;
363 ret = cli_credentials_get_ccache(cred, lp_ctx,
366 DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret)));
370 gcc = talloc(cred, struct gssapi_creds_container);
375 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
385 cred->client_gss_creds_obtained = cred->ccache_obtained;
386 talloc_set_destructor(gcc, free_gssapi_creds);
387 cred->client_gss_creds = gcc;
394 Set a gssapi cred_id_t into the credentials system. (Client case)
396 This grabs the credentials both 'intact' and getting the krb5
397 ccache out of it. This routine can be generalised in future for
398 the case where we deal with GSSAPI mechs other than krb5.
400 On sucess, the caller must not free gssapi_cred, as it now belongs
401 to the credentials system.
404 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
405 struct loadparm_context *lp_ctx,
406 gss_cred_id_t gssapi_cred,
407 enum credentials_obtained obtained)
410 OM_uint32 maj_stat, min_stat;
411 struct ccache_container *ccc;
412 struct gssapi_creds_container *gcc;
413 if (cred->client_gss_creds_obtained > obtained) {
417 gcc = talloc(cred, struct gssapi_creds_container);
422 ret = cli_credentials_new_ccache(cred, lp_ctx, &ccc);
427 maj_stat = gss_krb5_copy_ccache(&min_stat,
428 gssapi_cred, ccc->ccache);
438 ret = cli_credentials_set_from_ccache(cred, ccc, obtained);
441 cred->ccache_obtained = obtained;
443 gcc->creds = gssapi_cred;
444 talloc_set_destructor(gcc, free_gssapi_creds);
446 /* set the clinet_gss_creds_obtained here, as it just
447 got set to UNINITIALISED by the calls above */
448 cred->client_gss_creds_obtained = obtained;
449 cred->client_gss_creds = gcc;
454 /* Get the keytab (actually, a container containing the krb5_keytab)
455 * attached to this context. If this hasn't been done or set before,
456 * it will be generated from the password.
458 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
459 struct loadparm_context *lp_ctx,
460 struct keytab_container **_ktc)
463 struct keytab_container *ktc;
464 struct smb_krb5_context *smb_krb5_context;
465 const char **enctype_strings;
468 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
469 cred->username_obtained))) {
470 *_ktc = cred->keytab;
474 if (cli_credentials_is_anonymous(cred)) {
478 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
484 mem_ctx = talloc_new(cred);
489 enctype_strings = cli_credentials_get_enctype_strings(cred);
491 ret = smb_krb5_create_memory_keytab(mem_ctx, cred,
493 enctype_strings, &ktc);
495 talloc_free(mem_ctx);
499 cred->keytab_obtained = (MAX(cred->principal_obtained,
500 cred->username_obtained));
502 talloc_steal(cred, ktc);
504 *_ktc = cred->keytab;
505 talloc_free(mem_ctx);
509 /* Given the name of a keytab (presumably in the format
510 * FILE:/etc/krb5.keytab), open it and attach it */
512 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
513 struct loadparm_context *lp_ctx,
514 const char *keytab_name,
515 enum credentials_obtained obtained)
518 struct keytab_container *ktc;
519 struct smb_krb5_context *smb_krb5_context;
522 if (cred->keytab_obtained >= obtained) {
526 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
531 mem_ctx = talloc_new(cred);
536 ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context,
542 cred->keytab_obtained = obtained;
544 talloc_steal(cred, ktc);
546 talloc_free(mem_ctx);
551 _PUBLIC_ int cli_credentials_update_keytab(struct cli_credentials *cred,
552 struct loadparm_context *lp_ctx)
555 struct keytab_container *ktc;
556 struct smb_krb5_context *smb_krb5_context;
557 const char **enctype_strings;
560 mem_ctx = talloc_new(cred);
565 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
567 talloc_free(mem_ctx);
571 enctype_strings = cli_credentials_get_enctype_strings(cred);
573 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
575 talloc_free(mem_ctx);
579 ret = smb_krb5_update_keytab(mem_ctx, cred, smb_krb5_context, enctype_strings, ktc);
581 talloc_free(mem_ctx);
585 /* Get server gss credentials (in gsskrb5, this means the keytab) */
587 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
588 struct loadparm_context *lp_ctx,
589 struct gssapi_creds_container **_gcc)
592 OM_uint32 maj_stat, min_stat;
593 struct gssapi_creds_container *gcc;
594 struct keytab_container *ktc;
595 struct smb_krb5_context *smb_krb5_context;
597 krb5_principal princ;
599 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained,
600 MAX(cred->principal_obtained,
601 cred->username_obtained)))) {
602 *_gcc = cred->server_gss_creds;
606 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
611 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
613 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
617 mem_ctx = talloc_new(cred);
622 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ);
624 DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
625 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
627 talloc_free(mem_ctx);
631 gcc = talloc(cred, struct gssapi_creds_container);
633 talloc_free(mem_ctx);
637 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
638 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
648 cred->server_gss_creds_obtained = cred->keytab_obtained;
649 talloc_set_destructor(gcc, free_gssapi_creds);
650 cred->server_gss_creds = gcc;
653 talloc_free(mem_ctx);
661 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
668 * Return Kerberos KVNO
671 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
677 const char **cli_credentials_get_enctype_strings(struct cli_credentials *cred)
679 /* If this is ever made user-configurable, we need to add code
680 * to remove/hide the other entries from the generated
682 static const char *default_enctypes[] = {
684 "aes256-cts-hmac-sha1-96",
689 return default_enctypes;
692 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
694 return cred->salt_principal;
697 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
699 cred->salt_principal = talloc_strdup(cred, principal);