waf heimdal: use absolute path to compile_et
[Samba.git] / auth / credentials / credentials_krb5.c
blobd36797bf0f37f952a7102c8a51050225777e9ec4
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 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/>.
24 #include "includes.h"
25 #include "system/kerberos.h"
26 #include "system/gssapi.h"
27 #include "auth/kerberos/kerberos.h"
28 #include "auth/credentials/credentials.h"
29 #include "auth/credentials/credentials_internal.h"
30 #include "auth/credentials/credentials_proto.h"
31 #include "auth/credentials/credentials_krb5.h"
32 #include "auth/kerberos/kerberos_credentials.h"
33 #include "auth/kerberos/kerberos_srv_keytab.h"
34 #include "auth/kerberos/kerberos_util.h"
35 #include "auth/kerberos/pac_utils.h"
36 #include "param/param.h"
37 #include "../libds/common/flags.h"
39 #undef DBGC_CLASS
40 #define DBGC_CLASS DBGC_AUTH
42 static void cli_credentials_invalidate_client_gss_creds(
43 struct cli_credentials *cred,
44 enum credentials_obtained obtained);
46 /* Free a memory ccache */
47 static int free_mccache(struct ccache_container *ccc)
49 if (ccc->ccache != NULL) {
50 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
51 ccc->ccache);
52 ccc->ccache = NULL;
55 return 0;
58 /* Free a disk-based ccache */
59 static int free_dccache(struct ccache_container *ccc)
61 if (ccc->ccache != NULL) {
62 krb5_cc_close(ccc->smb_krb5_context->krb5_context,
63 ccc->ccache);
64 ccc->ccache = NULL;
67 return 0;
70 static uint32_t smb_gss_krb5_copy_ccache(uint32_t *min_stat,
71 gss_cred_id_t cred,
72 struct ccache_container *ccc)
74 #ifndef SAMBA4_USES_HEIMDAL /* MIT 1.10 */
75 krb5_context context = ccc->smb_krb5_context->krb5_context;
76 krb5_ccache dummy_ccache = NULL;
77 krb5_creds creds = {0};
78 krb5_cc_cursor cursor = NULL;
79 krb5_principal princ = NULL;
80 krb5_error_code code;
81 char *dummy_name;
82 uint32_t maj_stat = GSS_S_FAILURE;
84 dummy_name = talloc_asprintf(ccc,
85 "MEMORY:gss_krb5_copy_ccache-%p",
86 &ccc->ccache);
87 if (dummy_name == NULL) {
88 *min_stat = ENOMEM;
89 return GSS_S_FAILURE;
93 * Create a dummy ccache, so we can iterate over the credentials
94 * and find the default principal for the ccache we want to
95 * copy. The new ccache needs to be initialized with this
96 * principal.
98 code = krb5_cc_resolve(context, dummy_name, &dummy_ccache);
99 TALLOC_FREE(dummy_name);
100 if (code != 0) {
101 *min_stat = code;
102 return GSS_S_FAILURE;
106 * We do not need set a default principal on the temporary dummy
107 * ccache, as we do consume it at all in this function.
109 maj_stat = gss_krb5_copy_ccache(min_stat, cred, dummy_ccache);
110 if (maj_stat != 0) {
111 krb5_cc_close(context, dummy_ccache);
112 return maj_stat;
115 code = krb5_cc_start_seq_get(context, dummy_ccache, &cursor);
116 if (code != 0) {
117 krb5_cc_close(context, dummy_ccache);
118 *min_stat = EINVAL;
119 return GSS_S_FAILURE;
122 code = krb5_cc_next_cred(context,
123 dummy_ccache,
124 &cursor,
125 &creds);
126 if (code != 0) {
127 krb5_cc_close(context, dummy_ccache);
128 *min_stat = EINVAL;
129 return GSS_S_FAILURE;
132 do {
133 if (creds.ticket_flags & TKT_FLG_PRE_AUTH) {
134 krb5_data *tgs;
136 tgs = krb5_princ_component(context,
137 creds.server,
139 if (tgs != NULL && tgs->length >= 1) {
140 int cmp;
142 cmp = memcmp(tgs->data,
143 KRB5_TGS_NAME,
144 tgs->length);
145 if (cmp == 0 && creds.client != NULL) {
146 princ = creds.client;
147 code = KRB5_CC_END;
148 break;
153 krb5_free_cred_contents(context, &creds);
155 code = krb5_cc_next_cred(context,
156 dummy_ccache,
157 &cursor,
158 &creds);
159 } while (code == 0);
161 if (code == KRB5_CC_END) {
162 krb5_cc_end_seq_get(context, dummy_ccache, &cursor);
163 code = 0;
165 krb5_cc_close(context, dummy_ccache);
167 if (code != 0 || princ == NULL) {
168 krb5_free_cred_contents(context, &creds);
169 *min_stat = EINVAL;
170 return GSS_S_FAILURE;
174 * Set the default principal for the cache we copy
175 * into. This is needed to be able that other calls
176 * can read it with e.g. gss_acquire_cred() or
177 * krb5_cc_get_principal().
179 code = krb5_cc_initialize(context, ccc->ccache, princ);
180 if (code != 0) {
181 krb5_free_cred_contents(context, &creds);
182 *min_stat = EINVAL;
183 return GSS_S_FAILURE;
185 krb5_free_cred_contents(context, &creds);
187 #endif /* SAMBA4_USES_HEIMDAL */
189 return gss_krb5_copy_ccache(min_stat,
190 cred,
191 ccc->ccache);
194 _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
195 struct loadparm_context *lp_ctx,
196 struct smb_krb5_context **smb_krb5_context)
198 int ret;
199 if (cred->smb_krb5_context) {
200 *smb_krb5_context = cred->smb_krb5_context;
201 return 0;
204 ret = smb_krb5_init_context(cred, lp_ctx,
205 &cred->smb_krb5_context);
206 if (ret) {
207 cred->smb_krb5_context = NULL;
208 return ret;
210 *smb_krb5_context = cred->smb_krb5_context;
211 return 0;
214 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
215 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
217 _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
218 struct smb_krb5_context *smb_krb5_context)
220 if (smb_krb5_context == NULL) {
221 talloc_unlink(cred, cred->smb_krb5_context);
222 cred->smb_krb5_context = NULL;
223 return NT_STATUS_OK;
226 if (!talloc_reference(cred, smb_krb5_context)) {
227 return NT_STATUS_NO_MEMORY;
229 cred->smb_krb5_context = smb_krb5_context;
230 return NT_STATUS_OK;
233 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
234 struct ccache_container *ccache,
235 enum credentials_obtained obtained,
236 const char **error_string)
238 bool ok;
239 char *realm;
240 krb5_principal princ;
241 krb5_error_code ret;
242 char *name;
244 if (cred->ccache_obtained > obtained) {
245 return 0;
248 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
249 ccache->ccache, &princ);
251 if (ret) {
252 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
253 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
254 ret, cred));
255 return ret;
258 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
259 if (ret) {
260 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
261 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
262 ret, cred));
263 return ret;
266 ok = cli_credentials_set_principal(cred, name, obtained);
267 krb5_free_unparsed_name(ccache->smb_krb5_context->krb5_context, name);
268 if (!ok) {
269 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
270 return ENOMEM;
273 realm = smb_krb5_principal_get_realm(ccache->smb_krb5_context->krb5_context,
274 princ);
275 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
276 if (realm == NULL) {
277 return ENOMEM;
279 ok = cli_credentials_set_realm(cred, realm, obtained);
280 SAFE_FREE(realm);
281 if (!ok) {
282 return ENOMEM;
285 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
286 cred->ccache_obtained = obtained;
288 return 0;
291 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
292 struct loadparm_context *lp_ctx,
293 const char *name,
294 enum credentials_obtained obtained,
295 const char **error_string)
297 krb5_error_code ret;
298 krb5_principal princ;
299 struct ccache_container *ccc;
300 if (cred->ccache_obtained > obtained) {
301 return 0;
304 ccc = talloc(cred, struct ccache_container);
305 if (!ccc) {
306 (*error_string) = error_message(ENOMEM);
307 return ENOMEM;
310 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
311 &ccc->smb_krb5_context);
312 if (ret) {
313 (*error_string) = error_message(ret);
314 talloc_free(ccc);
315 return ret;
317 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
318 talloc_free(ccc);
319 (*error_string) = error_message(ENOMEM);
320 return ENOMEM;
323 if (name) {
324 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
325 if (ret) {
326 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
327 name,
328 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
329 ret, ccc));
330 talloc_free(ccc);
331 return ret;
333 } else {
334 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
335 if (ret) {
336 (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
337 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
338 ret, ccc));
339 talloc_free(ccc);
340 return ret;
344 talloc_set_destructor(ccc, free_dccache);
346 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
348 if (ret == 0) {
349 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
350 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
352 if (ret) {
353 (*error_string) = error_message(ret);
354 return ret;
357 cred->ccache = ccc;
358 cred->ccache_obtained = obtained;
359 talloc_steal(cred, ccc);
361 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
362 return 0;
364 return 0;
368 * Indicate the we failed to log in to this service/host with these
369 * credentials. The caller passes an unsigned int which they
370 * initialise to the number of times they would like to retry.
372 * This method is used to support re-trying with freshly fetched
373 * credentials in case a server is rebuilt while clients have
374 * non-expired tickets. When the client code gets a logon failure they
375 * throw away the existing credentials for the server and retry.
377 _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
378 const char *principal,
379 unsigned int *count)
381 struct ccache_container *ccc;
382 krb5_creds creds, creds2;
383 int ret;
385 if (principal == NULL) {
386 /* no way to delete if we don't know the principal */
387 return false;
390 ccc = cred->ccache;
391 if (ccc == NULL) {
392 /* not a kerberos connection */
393 return false;
396 if (*count > 0) {
397 /* We have already tried discarding the credentials */
398 return false;
400 (*count)++;
402 ZERO_STRUCT(creds);
403 ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
404 if (ret != 0) {
405 return false;
408 ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
409 if (ret != 0) {
410 /* don't retry - we didn't find these credentials to remove */
411 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
412 return false;
415 ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
416 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
417 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
418 if (ret != 0) {
419 /* don't retry - we didn't find these credentials to
420 * remove. Note that with the current backend this
421 * never happens, as it always returns 0 even if the
422 * creds don't exist, which is why we do a separate
423 * krb5_cc_retrieve_cred() above.
425 return false;
427 return true;
431 static int cli_credentials_new_ccache(struct cli_credentials *cred,
432 struct loadparm_context *lp_ctx,
433 char *ccache_name,
434 struct ccache_container **_ccc,
435 const char **error_string)
437 bool must_free_cc_name = false;
438 krb5_error_code ret;
439 struct ccache_container *ccc = talloc(cred, struct ccache_container);
440 if (!ccc) {
441 return ENOMEM;
444 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
445 &ccc->smb_krb5_context);
446 if (ret) {
447 talloc_free(ccc);
448 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
449 error_message(ret));
450 return ret;
452 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
453 talloc_free(ccc);
454 (*error_string) = strerror(ENOMEM);
455 return ENOMEM;
458 if (!ccache_name) {
459 must_free_cc_name = true;
461 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
462 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
463 (unsigned int)getpid(), ccc);
464 } else {
465 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
466 ccc);
469 if (!ccache_name) {
470 talloc_free(ccc);
471 (*error_string) = strerror(ENOMEM);
472 return ENOMEM;
476 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
477 &ccc->ccache);
478 if (ret) {
479 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
480 ccache_name,
481 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
482 ret, ccc));
483 talloc_free(ccache_name);
484 talloc_free(ccc);
485 return ret;
488 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
489 talloc_set_destructor(ccc, free_mccache);
490 } else {
491 talloc_set_destructor(ccc, free_dccache);
494 if (must_free_cc_name) {
495 talloc_free(ccache_name);
498 *_ccc = ccc;
500 return 0;
503 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
504 struct tevent_context *event_ctx,
505 struct loadparm_context *lp_ctx,
506 char *ccache_name,
507 struct ccache_container **ccc,
508 const char **error_string)
510 krb5_error_code ret;
511 enum credentials_obtained obtained;
513 if (cred->machine_account_pending) {
514 cli_credentials_set_machine_account(cred, lp_ctx);
517 if (cred->ccache_obtained >= cred->ccache_threshold &&
518 cred->ccache_obtained > CRED_UNINITIALISED) {
519 time_t lifetime;
520 bool expired = false;
521 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
522 cred->ccache->ccache, &lifetime);
523 if (ret == KRB5_CC_END) {
524 /* If we have a particular ccache set, without
525 * an initial ticket, then assume there is a
526 * good reason */
527 } else if (ret == 0) {
528 if (lifetime == 0) {
529 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
530 cli_credentials_get_principal(cred, cred)));
531 expired = true;
532 } else if (lifetime < 300) {
533 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
534 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
535 expired = true;
537 } else {
538 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
539 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
540 ret, cred));
541 return ret;
544 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
545 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
547 if (!expired) {
548 *ccc = cred->ccache;
549 return 0;
552 if (cli_credentials_is_anonymous(cred)) {
553 (*error_string) = "Cannot get anonymous kerberos credentials";
554 return EINVAL;
557 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
558 if (ret) {
559 return ret;
562 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
563 if (ret) {
564 return ret;
567 ret = cli_credentials_set_from_ccache(cred, *ccc,
568 obtained, error_string);
570 cred->ccache = *ccc;
571 cred->ccache_obtained = cred->principal_obtained;
572 if (ret) {
573 return ret;
575 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
576 return 0;
579 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
580 struct tevent_context *event_ctx,
581 struct loadparm_context *lp_ctx,
582 struct ccache_container **ccc,
583 const char **error_string)
585 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
588 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
589 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
591 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
592 talloc_unlink(cred, cred->client_gss_creds);
593 cred->client_gss_creds = NULL;
595 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
598 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
599 enum credentials_obtained obtained)
601 /* If the caller just changed the username/password etc, then
602 * any cached credentials are now invalid */
603 if (obtained >= cred->client_gss_creds_obtained) {
604 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
605 talloc_unlink(cred, cred->client_gss_creds);
606 cred->client_gss_creds = NULL;
608 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
610 /* Now that we know that the data is 'this specified', then
611 * don't allow something less 'known' to be returned as a
612 * ccache. Ie, if the username is on the command line, we
613 * don't want to later guess to use a file-based ccache */
614 if (obtained > cred->client_gss_creds_threshold) {
615 cred->client_gss_creds_threshold = obtained;
619 /* We have good reason to think this CCACHE is invalid. Blow it away */
620 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
622 if (cred->ccache_obtained > CRED_UNINITIALISED) {
623 talloc_unlink(cred, cred->ccache);
624 cred->ccache = NULL;
626 cred->ccache_obtained = CRED_UNINITIALISED;
628 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
631 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
632 enum credentials_obtained obtained)
634 /* If the caller just changed the username/password etc, then
635 * any cached credentials are now invalid */
636 if (obtained >= cred->ccache_obtained) {
637 if (cred->ccache_obtained > CRED_UNINITIALISED) {
638 talloc_unlink(cred, cred->ccache);
639 cred->ccache = NULL;
641 cred->ccache_obtained = CRED_UNINITIALISED;
643 /* Now that we know that the data is 'this specified', then
644 * don't allow something less 'known' to be returned as a
645 * ccache. i.e, if the username is on the command line, we
646 * don't want to later guess to use a file-based ccache */
647 if (obtained > cred->ccache_threshold) {
648 cred->ccache_threshold = obtained;
651 cli_credentials_invalidate_client_gss_creds(cred,
652 obtained);
655 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
657 OM_uint32 min_stat;
658 (void)gss_release_cred(&min_stat, &gcc->creds);
659 return 0;
662 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
663 struct tevent_context *event_ctx,
664 struct loadparm_context *lp_ctx,
665 struct gssapi_creds_container **_gcc,
666 const char **error_string)
668 int ret = 0;
669 OM_uint32 maj_stat, min_stat;
670 struct gssapi_creds_container *gcc;
671 struct ccache_container *ccache;
672 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
673 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
674 gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
675 #endif
676 krb5_enctype *etypes = NULL;
678 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
679 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
680 bool expired = false;
681 OM_uint32 lifetime = 0;
682 gss_cred_usage_t usage = 0;
683 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
684 NULL, &lifetime, &usage, NULL);
685 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
686 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
687 expired = true;
688 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
689 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
690 expired = true;
691 } else if (maj_stat != GSS_S_COMPLETE) {
692 *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
693 gssapi_error_string(cred, maj_stat, min_stat, NULL));
694 return EINVAL;
696 if (expired) {
697 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
698 } else {
699 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
700 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
702 *_gcc = cred->client_gss_creds;
703 return 0;
707 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
708 &ccache, error_string);
709 if (ret) {
710 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
711 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
712 } else {
713 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
715 return ret;
718 gcc = talloc(cred, struct gssapi_creds_container);
719 if (!gcc) {
720 (*error_string) = error_message(ENOMEM);
721 return ENOMEM;
724 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
725 ccache->ccache, NULL, NULL,
726 &gcc->creds);
727 if ((maj_stat == GSS_S_FAILURE) &&
728 (min_stat == (OM_uint32)KRB5_CC_END ||
729 min_stat == (OM_uint32)KRB5_CC_NOTFOUND ||
730 min_stat == (OM_uint32)KRB5_FCC_NOFILE))
732 /* This CCACHE is no good. Ensure we don't use it again */
733 cli_credentials_unconditionally_invalidate_ccache(cred);
735 /* Now try again to get a ccache */
736 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
737 &ccache, error_string);
738 if (ret) {
739 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
740 return ret;
743 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
744 ccache->ccache, NULL, NULL,
745 &gcc->creds);
749 if (maj_stat) {
750 talloc_free(gcc);
751 if (min_stat) {
752 ret = min_stat;
753 } else {
754 ret = EINVAL;
756 (*error_string) = talloc_asprintf(cred, "smb_gss_krb5_import_cred failed: %s", error_message(ret));
757 return ret;
762 * transfer the enctypes from the smb_krb5_context to the gssapi layer
764 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
765 * to configure the enctypes via the krb5.conf.
767 * And the gss_init_sec_context() creates it's own krb5_context and
768 * the TGS-REQ had all enctypes in it and only the ones configured
769 * and used for the AS-REQ, so it wasn't possible to disable the usage
770 * of AES keys.
772 min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
773 &etypes);
774 if (min_stat == 0) {
775 OM_uint32 num_ktypes;
777 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
779 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
780 num_ktypes,
781 (int32_t *) etypes);
782 SAFE_FREE(etypes);
783 if (maj_stat) {
784 talloc_free(gcc);
785 if (min_stat) {
786 ret = min_stat;
787 } else {
788 ret = EINVAL;
790 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
791 return ret;
795 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
797 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
799 * This allows us to disable SIGN and SEAL on a TLS connection with
800 * GSS-SPNENO. For example ldaps:// connections.
802 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
803 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
805 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
806 oid,
807 &empty_buffer);
808 if (maj_stat) {
809 talloc_free(gcc);
810 if (min_stat) {
811 ret = min_stat;
812 } else {
813 ret = EINVAL;
815 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
816 return ret;
818 #endif
819 cred->client_gss_creds_obtained = cred->ccache_obtained;
820 talloc_set_destructor(gcc, free_gssapi_creds);
821 cred->client_gss_creds = gcc;
822 *_gcc = gcc;
823 return 0;
827 Set a gssapi cred_id_t into the credentials system. (Client case)
829 This grabs the credentials both 'intact' and getting the krb5
830 ccache out of it. This routine can be generalised in future for
831 the case where we deal with GSSAPI mechs other than krb5.
833 On sucess, the caller must not free gssapi_cred, as it now belongs
834 to the credentials system.
837 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
838 struct loadparm_context *lp_ctx,
839 gss_cred_id_t gssapi_cred,
840 enum credentials_obtained obtained,
841 const char **error_string)
843 int ret;
844 OM_uint32 maj_stat, min_stat;
845 struct ccache_container *ccc = NULL;
846 struct gssapi_creds_container *gcc = NULL;
847 if (cred->client_gss_creds_obtained > obtained) {
848 return 0;
851 gcc = talloc(cred, struct gssapi_creds_container);
852 if (!gcc) {
853 (*error_string) = error_message(ENOMEM);
854 return ENOMEM;
857 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
858 if (ret != 0) {
859 return ret;
862 maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
863 gssapi_cred,
864 ccc);
865 if (maj_stat) {
866 if (min_stat) {
867 ret = min_stat;
868 } else {
869 ret = EINVAL;
871 if (ret) {
872 (*error_string) = error_message(ENOMEM);
876 if (ret == 0) {
877 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
879 cred->ccache = ccc;
880 cred->ccache_obtained = obtained;
881 if (ret == 0) {
882 gcc->creds = gssapi_cred;
883 talloc_set_destructor(gcc, free_gssapi_creds);
885 /* set the clinet_gss_creds_obtained here, as it just
886 got set to UNINITIALISED by the calls above */
887 cred->client_gss_creds_obtained = obtained;
888 cred->client_gss_creds = gcc;
890 return ret;
893 static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
895 krb5_error_code ret;
896 const struct ccache_container *old_ccc = NULL;
897 struct ccache_container *ccc = NULL;
898 char *ccache_name = NULL;
900 old_ccc = cred->ccache;
901 if (old_ccc == NULL) {
902 return 0;
905 ccc = talloc(cred, struct ccache_container);
906 if (ccc == NULL) {
907 return ENOMEM;
909 *ccc = *old_ccc;
910 ccc->ccache = NULL;
912 ccache_name = talloc_asprintf(ccc, "MEMORY:%p", ccc);
914 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
915 ccache_name, &ccc->ccache);
916 if (ret != 0) {
917 TALLOC_FREE(ccc);
918 return ret;
921 talloc_set_destructor(ccc, free_mccache);
923 TALLOC_FREE(ccache_name);
925 ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
926 old_ccc->ccache, ccc->ccache);
927 if (ret != 0) {
928 TALLOC_FREE(ccc);
929 return ret;
932 cred->ccache = ccc;
933 cred->client_gss_creds = NULL;
934 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
935 return ret;
938 _PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
939 struct cli_credentials *src)
941 struct cli_credentials *dst;
942 int ret;
944 dst = talloc(mem_ctx, struct cli_credentials);
945 if (dst == NULL) {
946 return NULL;
949 *dst = *src;
951 ret = cli_credentials_shallow_ccache(dst);
952 if (ret != 0) {
953 TALLOC_FREE(dst);
954 return NULL;
957 return dst;
960 /* Get the keytab (actually, a container containing the krb5_keytab)
961 * attached to this context. If this hasn't been done or set before,
962 * it will be generated from the password.
964 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
965 struct loadparm_context *lp_ctx,
966 struct keytab_container **_ktc)
968 krb5_error_code ret;
969 struct keytab_container *ktc;
970 struct smb_krb5_context *smb_krb5_context;
971 const char *keytab_name;
972 krb5_keytab keytab;
973 TALLOC_CTX *mem_ctx;
974 const char *username = cli_credentials_get_username(cred);
975 const char *upn = NULL;
976 const char *realm = cli_credentials_get_realm(cred);
977 char *salt_principal = NULL;
978 uint32_t uac_flags = 0;
980 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
981 cred->username_obtained))) {
982 *_ktc = cred->keytab;
983 return 0;
986 if (cli_credentials_is_anonymous(cred)) {
987 return EINVAL;
990 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
991 &smb_krb5_context);
992 if (ret) {
993 return ret;
996 mem_ctx = talloc_new(cred);
997 if (!mem_ctx) {
998 return ENOMEM;
1001 switch (cred->secure_channel_type) {
1002 case SEC_CHAN_WKSTA:
1003 case SEC_CHAN_RODC:
1004 uac_flags = UF_WORKSTATION_TRUST_ACCOUNT;
1005 break;
1006 case SEC_CHAN_BDC:
1007 uac_flags = UF_SERVER_TRUST_ACCOUNT;
1008 break;
1009 case SEC_CHAN_DOMAIN:
1010 case SEC_CHAN_DNS_DOMAIN:
1011 uac_flags = UF_INTERDOMAIN_TRUST_ACCOUNT;
1012 break;
1013 default:
1014 upn = cli_credentials_get_principal(cred, mem_ctx);
1015 if (upn == NULL) {
1016 TALLOC_FREE(mem_ctx);
1017 return ENOMEM;
1019 uac_flags = UF_NORMAL_ACCOUNT;
1020 break;
1023 ret = smb_krb5_salt_principal(realm,
1024 username, /* sAMAccountName */
1025 upn, /* userPrincipalName */
1026 uac_flags,
1027 mem_ctx,
1028 &salt_principal);
1029 if (ret) {
1030 talloc_free(mem_ctx);
1031 return ret;
1034 ret = smb_krb5_create_memory_keytab(mem_ctx,
1035 smb_krb5_context->krb5_context,
1036 cli_credentials_get_password(cred),
1037 username,
1038 realm,
1039 salt_principal,
1040 cli_credentials_get_kvno(cred),
1041 &keytab,
1042 &keytab_name);
1043 if (ret) {
1044 talloc_free(mem_ctx);
1045 return ret;
1048 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1049 keytab, keytab_name, &ktc);
1050 if (ret) {
1051 talloc_free(mem_ctx);
1052 return ret;
1055 cred->keytab_obtained = (MAX(cred->principal_obtained,
1056 cred->username_obtained));
1058 /* We make this keytab up based on a password. Therefore
1059 * match-by-key is acceptable, we can't match on the wrong
1060 * principal */
1061 ktc->password_based = true;
1063 talloc_steal(cred, ktc);
1064 cred->keytab = ktc;
1065 *_ktc = cred->keytab;
1066 talloc_free(mem_ctx);
1067 return ret;
1070 /* Given the name of a keytab (presumably in the format
1071 * FILE:/etc/krb5.keytab), open it and attach it */
1073 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
1074 struct loadparm_context *lp_ctx,
1075 const char *keytab_name,
1076 enum credentials_obtained obtained)
1078 krb5_error_code ret;
1079 struct keytab_container *ktc;
1080 struct smb_krb5_context *smb_krb5_context;
1081 TALLOC_CTX *mem_ctx;
1083 if (cred->keytab_obtained >= obtained) {
1084 return 0;
1087 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1088 if (ret) {
1089 return ret;
1092 mem_ctx = talloc_new(cred);
1093 if (!mem_ctx) {
1094 return ENOMEM;
1097 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1098 NULL, keytab_name, &ktc);
1099 if (ret) {
1100 return ret;
1103 cred->keytab_obtained = obtained;
1105 talloc_steal(cred, ktc);
1106 cred->keytab = ktc;
1107 talloc_free(mem_ctx);
1109 return ret;
1112 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1114 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
1115 struct loadparm_context *lp_ctx,
1116 struct gssapi_creds_container **_gcc)
1118 int ret = 0;
1119 OM_uint32 maj_stat, min_stat;
1120 struct gssapi_creds_container *gcc;
1121 struct keytab_container *ktc;
1122 struct smb_krb5_context *smb_krb5_context;
1123 TALLOC_CTX *mem_ctx;
1124 krb5_principal princ;
1125 const char *error_string;
1126 enum credentials_obtained obtained;
1128 mem_ctx = talloc_new(cred);
1129 if (!mem_ctx) {
1130 return ENOMEM;
1133 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1134 if (ret) {
1135 return ret;
1138 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1139 if (ret) {
1140 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1141 error_string));
1142 talloc_free(mem_ctx);
1143 return ret;
1146 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1147 talloc_free(mem_ctx);
1148 *_gcc = cred->server_gss_creds;
1149 return 0;
1152 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1153 if (ret) {
1154 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1155 return ret;
1158 gcc = talloc(cred, struct gssapi_creds_container);
1159 if (!gcc) {
1160 talloc_free(mem_ctx);
1161 return ENOMEM;
1164 if (ktc->password_based || obtained < CRED_SPECIFIED) {
1166 * This creates a GSSAPI cred_id_t for match-by-key with only
1167 * the keytab set
1169 princ = NULL;
1171 maj_stat = smb_gss_krb5_import_cred(&min_stat,
1172 smb_krb5_context->krb5_context,
1173 NULL, princ,
1174 ktc->keytab,
1175 &gcc->creds);
1176 if (maj_stat) {
1177 if (min_stat) {
1178 ret = min_stat;
1179 } else {
1180 ret = EINVAL;
1183 if (ret == 0) {
1184 cred->server_gss_creds_obtained = cred->keytab_obtained;
1185 talloc_set_destructor(gcc, free_gssapi_creds);
1186 cred->server_gss_creds = gcc;
1187 *_gcc = gcc;
1189 talloc_free(mem_ctx);
1190 return ret;
1193 /**
1194 * Set Kerberos KVNO
1197 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1198 int kvno)
1200 cred->kvno = kvno;
1204 * Return Kerberos KVNO
1207 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1209 return cred->kvno;
1213 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
1215 return cred->salt_principal;
1218 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
1220 talloc_free(cred->salt_principal);
1221 cred->salt_principal = talloc_strdup(cred, principal);
1224 /* The 'impersonate_principal' is used to allow one Kerberos principal
1225 * (and it's associated keytab etc) to impersonate another. The
1226 * ability to do this is controlled by the KDC, but it is generally
1227 * permitted to impersonate anyone to yourself. This allows any
1228 * member of the domain to get the groups of a user. This is also
1229 * known as S4U2Self */
1231 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1233 return cred->impersonate_principal;
1237 * The 'self_service' is the service principal that
1238 * represents the same object (by its objectSid)
1239 * as the client principal (typically our machine account).
1240 * When trying to impersonate 'impersonate_principal' with
1241 * S4U2Self.
1243 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1245 return cred->self_service;
1248 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1249 const char *principal,
1250 const char *self_service)
1252 talloc_free(cred->impersonate_principal);
1253 cred->impersonate_principal = talloc_strdup(cred, principal);
1254 talloc_free(cred->self_service);
1255 cred->self_service = talloc_strdup(cred, self_service);
1256 cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
1260 * when impersonating for S4U2proxy we need to set the target principal.
1261 * Similarly, we may only be authorized to do general impersonation to
1262 * some particular services.
1264 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1266 * NULL means that tickets will be obtained for the krbtgt service.
1269 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1271 return cred->target_service;
1274 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1276 talloc_free(cred->target_service);
1277 cred->target_service = talloc_strdup(cred, target_service);