r23063: Make sure to invalidate the ccache when we set a
[Samba.git] / source / auth / credentials / credentials_krb5.c
blob2f0ca35d76733fc03ab36148fdf4e8f00e894848
1 /*
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.
25 #include "includes.h"
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)
34 int ret;
35 if (cred->smb_krb5_context) {
36 *smb_krb5_context = cred->smb_krb5_context;
37 return 0;
40 ret = smb_krb5_init_context(cred, cli_credentials_get_event_context(cred),
41 &cred->smb_krb5_context);
42 if (ret) {
43 return ret;
45 *smb_krb5_context = cred->smb_krb5_context;
46 return 0;
49 /* This needs to be called directly after the cli_credentials_init(),
50 * otherwise we might have problems with the krb5 context already
51 * being here.
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;
60 return NT_STATUS_OK;
63 int cli_credentials_set_from_ccache(struct cli_credentials *cred,
64 enum credentials_obtained obtained)
67 krb5_principal princ;
68 krb5_error_code ret;
69 char *name;
70 char **realm;
72 if (cred->ccache_obtained > obtained) {
73 return 0;
76 ret = krb5_cc_get_principal(cred->ccache->smb_krb5_context->krb5_context,
77 cred->ccache->ccache, &princ);
79 if (ret) {
80 char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred);
81 DEBUG(1,("failed to get principal from ccache: %s\n",
82 err_mess));
83 talloc_free(err_mess);
84 return ret;
87 ret = krb5_unparse_name(cred->ccache->smb_krb5_context->krb5_context, princ, &name);
88 if (ret) {
89 char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred);
90 DEBUG(1,("failed to unparse principal from ccache: %s\n",
91 err_mess));
92 talloc_free(err_mess);
93 return ret;
96 realm = krb5_princ_realm(cred->ccache->smb_krb5_context->krb5_context, princ);
98 cli_credentials_set_principal(cred, name, obtained);
100 free(name);
102 krb5_free_principal(cred->ccache->smb_krb5_context->krb5_context, princ);
104 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
105 cred->ccache_obtained = obtained;
106 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
108 return 0;
111 /* Free a memory ccache */
112 static int free_mccache(struct ccache_container *ccc)
114 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
116 return 0;
119 /* Free a disk-based ccache */
120 static int free_dccache(struct ccache_container *ccc) {
121 krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
123 return 0;
126 int cli_credentials_set_ccache(struct cli_credentials *cred,
127 const char *name,
128 enum credentials_obtained obtained)
130 krb5_error_code ret;
131 krb5_principal princ;
132 struct ccache_container *ccc;
133 if (cred->ccache_obtained > obtained) {
134 return 0;
137 ccc = talloc(cred, struct ccache_container);
138 if (!ccc) {
139 return ENOMEM;
142 ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
143 if (ret) {
144 talloc_free(ccc);
145 return ret;
147 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
148 talloc_free(ccc);
149 return ENOMEM;
152 if (name) {
153 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
154 if (ret) {
155 DEBUG(1,("failed to read krb5 ccache: %s: %s\n",
156 name,
157 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
158 talloc_free(ccc);
159 return ret;
161 } else {
162 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
163 if (ret) {
164 DEBUG(3,("failed to read default krb5 ccache: %s\n",
165 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
166 talloc_free(ccc);
167 return ret;
171 talloc_set_destructor(ccc, free_dccache);
173 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
175 if (ret) {
176 DEBUG(3,("failed to get principal from default ccache: %s\n",
177 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
178 talloc_free(ccc);
179 return ret;
182 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
184 cred->ccache = ccc;
185 talloc_steal(cred, ccc);
187 ret = cli_credentials_set_from_ccache(cred, obtained);
189 if (ret) {
190 return ret;
193 return 0;
197 int cli_credentials_new_ccache(struct cli_credentials *cred, struct ccache_container **_ccc)
199 krb5_error_code ret;
200 char *rand_string;
201 struct ccache_container *ccc = talloc(cred, struct ccache_container);
202 char *ccache_name;
203 if (!ccc) {
204 return ENOMEM;
207 rand_string = generate_random_str(NULL, 16);
208 if (!rand_string) {
209 talloc_free(ccc);
210 return ENOMEM;
213 ccache_name = talloc_asprintf(ccc, "MEMORY:%s",
214 rand_string);
215 talloc_free(rand_string);
217 if (!ccache_name) {
218 talloc_free(ccc);
219 return ENOMEM;
222 ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
223 if (ret) {
224 talloc_free(ccc);
225 return ret;
227 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
228 talloc_free(ccc);
229 return ENOMEM;
232 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, &ccc->ccache);
233 if (ret) {
234 DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n",
235 ccache_name,
236 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
237 talloc_free(ccache_name);
238 talloc_free(ccc);
239 return ret;
242 talloc_set_destructor(ccc, free_mccache);
244 cred->ccache = ccc;
245 talloc_steal(cred, ccc);
246 talloc_free(ccache_name);
248 if (_ccc) {
249 *_ccc = ccc;
252 cred->ccache_obtained = (MAX(MAX(cred->principal_obtained,
253 cred->username_obtained),
254 cred->password_obtained));
255 return ret;
258 int cli_credentials_get_ccache(struct cli_credentials *cred,
259 struct ccache_container **ccc)
261 krb5_error_code ret;
263 if (cred->machine_account_pending) {
264 cli_credentials_set_machine_account(cred);
267 if (cred->ccache_obtained >= cred->ccache_threshold) {
268 *ccc = cred->ccache;
269 return 0;
271 if (cli_credentials_is_anonymous(cred)) {
272 return EINVAL;
275 ret = cli_credentials_new_ccache(cred, NULL);
276 if (ret) {
277 return ret;
279 ret = kinit_to_ccache(cred, cred, cred->ccache->smb_krb5_context, cred->ccache->ccache);
280 if (ret) {
281 return ret;
283 ret = cli_credentials_set_from_ccache(cred, cred->principal_obtained);
285 if (ret) {
286 return ret;
288 *ccc = cred->ccache;
289 return ret;
292 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
293 enum credentials_obtained obtained)
295 /* If the caller just changed the username/password etc, then
296 * any cached credentials are now invalid */
297 if (obtained >= cred->client_gss_creds_obtained) {
298 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
299 talloc_free(cred->client_gss_creds);
301 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
303 /* Now that we know that the data is 'this specified', then
304 * don't allow something less 'known' to be returned as a
305 * ccache. Ie, if the username is on the commmand line, we
306 * don't want to later guess to use a file-based ccache */
307 if (obtained > cred->client_gss_creds_threshold) {
308 cred->client_gss_creds_threshold = obtained;
312 void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
313 enum credentials_obtained obtained)
315 /* If the caller just changed the username/password etc, then
316 * any cached credentials are now invalid */
317 if (obtained >= cred->ccache_obtained) {
318 if (cred->ccache_obtained > CRED_UNINITIALISED) {
319 talloc_free(cred->ccache);
321 cred->ccache_obtained = CRED_UNINITIALISED;
323 /* Now that we know that the data is 'this specified', then
324 * don't allow something less 'known' to be returned as a
325 * ccache. Ie, if the username is on the commmand line, we
326 * don't want to later guess to use a file-based ccache */
327 if (obtained > cred->ccache_threshold) {
328 cred->ccache_threshold = obtained;
331 cli_credentials_invalidate_client_gss_creds(cred,
332 obtained);
335 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
337 OM_uint32 min_stat, maj_stat;
338 maj_stat = gss_release_cred(&min_stat, &gcc->creds);
339 return 0;
342 int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
343 struct gssapi_creds_container **_gcc)
345 int ret = 0;
346 OM_uint32 maj_stat, min_stat;
347 struct gssapi_creds_container *gcc;
348 struct ccache_container *ccache;
349 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold) {
350 *_gcc = cred->client_gss_creds;
351 return 0;
353 ret = cli_credentials_get_ccache(cred,
354 &ccache);
355 if (ret) {
356 DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret)));
357 return ret;
360 gcc = talloc(cred, struct gssapi_creds_container);
361 if (!gcc) {
362 return ENOMEM;
365 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
366 &gcc->creds);
367 if (maj_stat) {
368 if (min_stat) {
369 ret = min_stat;
370 } else {
371 ret = EINVAL;
374 if (ret == 0) {
375 cred->client_gss_creds_obtained = cred->ccache_obtained;
376 talloc_set_destructor(gcc, free_gssapi_creds);
377 cred->client_gss_creds = gcc;
378 *_gcc = gcc;
380 return ret;
384 Set a gssapi cred_id_t into the credentails system. (Client case)
386 This grabs the credentials both 'intact' and getting the krb5
387 ccache out of it. This routine can be generalised in future for
388 the case where we deal with GSSAPI mechs other than krb5.
390 On sucess, the caller must not free gssapi_cred, as it now belongs
391 to the credentials system.
394 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
395 gss_cred_id_t gssapi_cred,
396 enum credentials_obtained obtained)
398 int ret;
399 OM_uint32 maj_stat, min_stat;
400 struct ccache_container *ccc;
401 struct gssapi_creds_container *gcc;
402 if (cred->client_gss_creds_obtained > obtained) {
403 return 0;
406 gcc = talloc(cred, struct gssapi_creds_container);
407 if (!gcc) {
408 return ENOMEM;
411 ret = cli_credentials_new_ccache(cred, &ccc);
412 if (ret != 0) {
413 return ret;
416 maj_stat = gss_krb5_copy_ccache(&min_stat,
417 gssapi_cred, ccc->ccache);
418 if (maj_stat) {
419 if (min_stat) {
420 ret = min_stat;
421 } else {
422 ret = EINVAL;
426 if (ret == 0) {
427 ret = cli_credentials_set_from_ccache(cred, obtained);
429 if (ret == 0) {
430 gcc->creds = gssapi_cred;
431 talloc_set_destructor(gcc, free_gssapi_creds);
433 /* set the clinet_gss_creds_obtained here, as it just
434 got set to UNINITIALISED by the calls above */
435 cred->client_gss_creds_obtained = obtained;
436 cred->client_gss_creds = gcc;
438 return ret;
441 /* Get the keytab (actually, a container containing the krb5_keytab)
442 * attached to this context. If this hasn't been done or set before,
443 * it will be generated from the password.
445 int cli_credentials_get_keytab(struct cli_credentials *cred,
446 struct keytab_container **_ktc)
448 krb5_error_code ret;
449 struct keytab_container *ktc;
450 struct smb_krb5_context *smb_krb5_context;
451 const char **enctype_strings;
452 TALLOC_CTX *mem_ctx;
454 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
455 cred->username_obtained))) {
456 *_ktc = cred->keytab;
457 return 0;
460 if (cli_credentials_is_anonymous(cred)) {
461 return EINVAL;
464 ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
465 if (ret) {
466 return ret;
469 mem_ctx = talloc_new(cred);
470 if (!mem_ctx) {
471 return ENOMEM;
474 enctype_strings = cli_credentials_get_enctype_strings(cred);
476 ret = smb_krb5_create_memory_keytab(mem_ctx, cred,
477 smb_krb5_context,
478 enctype_strings, &ktc);
479 if (ret) {
480 talloc_free(mem_ctx);
481 return ret;
484 cred->keytab_obtained = (MAX(cred->principal_obtained,
485 cred->username_obtained));
487 talloc_steal(cred, ktc);
488 cred->keytab = ktc;
489 *_ktc = cred->keytab;
490 talloc_free(mem_ctx);
491 return ret;
494 /* Given the name of a keytab (presumably in the format
495 * FILE:/etc/krb5.keytab), open it and attach it */
497 int cli_credentials_set_keytab_name(struct cli_credentials *cred,
498 const char *keytab_name,
499 enum credentials_obtained obtained)
501 krb5_error_code ret;
502 struct keytab_container *ktc;
503 struct smb_krb5_context *smb_krb5_context;
504 TALLOC_CTX *mem_ctx;
506 if (cred->keytab_obtained >= obtained) {
507 return 0;
510 ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
511 if (ret) {
512 return ret;
515 mem_ctx = talloc_new(cred);
516 if (!mem_ctx) {
517 return ENOMEM;
520 ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context,
521 keytab_name, &ktc);
522 if (ret) {
523 return ret;
526 cred->keytab_obtained = obtained;
528 talloc_steal(cred, ktc);
529 cred->keytab = ktc;
530 talloc_free(mem_ctx);
532 return ret;
535 int cli_credentials_update_keytab(struct cli_credentials *cred)
537 krb5_error_code ret;
538 struct keytab_container *ktc;
539 struct smb_krb5_context *smb_krb5_context;
540 const char **enctype_strings;
541 TALLOC_CTX *mem_ctx;
543 mem_ctx = talloc_new(cred);
544 if (!mem_ctx) {
545 return ENOMEM;
548 ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
549 if (ret) {
550 talloc_free(mem_ctx);
551 return ret;
554 enctype_strings = cli_credentials_get_enctype_strings(cred);
556 ret = cli_credentials_get_keytab(cred, &ktc);
557 if (ret != 0) {
558 talloc_free(mem_ctx);
559 return ret;
562 ret = smb_krb5_update_keytab(mem_ctx, cred, smb_krb5_context, enctype_strings, ktc);
564 talloc_free(mem_ctx);
565 return ret;
568 /* Get server gss credentials (in gsskrb5, this means the keytab) */
570 int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
571 struct gssapi_creds_container **_gcc)
573 int ret = 0;
574 OM_uint32 maj_stat, min_stat;
575 struct gssapi_creds_container *gcc;
576 struct keytab_container *ktc;
577 struct smb_krb5_context *smb_krb5_context;
578 TALLOC_CTX *mem_ctx;
579 krb5_principal princ;
581 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained,
582 MAX(cred->principal_obtained,
583 cred->username_obtained)))) {
584 *_gcc = cred->server_gss_creds;
585 return 0;
588 ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
589 if (ret) {
590 return ret;
593 ret = cli_credentials_get_keytab(cred,
594 &ktc);
595 if (ret) {
596 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
597 return ret;
600 mem_ctx = talloc_new(cred);
601 if (!mem_ctx) {
602 return ENOMEM;
605 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ);
606 if (ret) {
607 DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
608 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
609 ret, mem_ctx)));
610 talloc_free(mem_ctx);
611 return ret;
614 gcc = talloc(cred, struct gssapi_creds_container);
615 if (!gcc) {
616 talloc_free(mem_ctx);
617 return ENOMEM;
620 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
621 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
622 &gcc->creds);
623 if (maj_stat) {
624 if (min_stat) {
625 ret = min_stat;
626 } else {
627 ret = EINVAL;
630 if (ret == 0) {
631 cred->server_gss_creds_obtained = cred->keytab_obtained;
632 talloc_set_destructor(gcc, free_gssapi_creds);
633 cred->server_gss_creds = gcc;
634 *_gcc = gcc;
636 talloc_free(mem_ctx);
637 return ret;
640 /**
641 * Set Kerberos KVNO
644 void cli_credentials_set_kvno(struct cli_credentials *cred,
645 int kvno)
647 cred->kvno = kvno;
651 * Return Kerberos KVNO
654 int cli_credentials_get_kvno(struct cli_credentials *cred)
656 return cred->kvno;
660 const char **cli_credentials_get_enctype_strings(struct cli_credentials *cred)
662 /* If this is ever made user-configurable, we need to add code
663 * to remove/hide the other entries from the generated
664 * keytab */
665 static const char *default_enctypes[] = {
666 "des-cbc-md5",
667 "aes256-cts-hmac-sha1-96",
668 "des3-cbc-sha1",
669 "arcfour-hmac-md5",
670 NULL
672 return default_enctypes;
675 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
677 return cred->salt_principal;
680 void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
682 cred->salt_principal = talloc_strdup(cred, principal);