winbindd: add routing_domain as parameter to add_trusted_domain
[Samba.git] / auth / credentials / credentials_krb5.c
blob9da1aa09250db7b9e8201aab3306d153b7ef655c
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"
38 #undef DBGC_CLASS
39 #define DBGC_CLASS DBGC_AUTH
41 static void cli_credentials_invalidate_client_gss_creds(
42 struct cli_credentials *cred,
43 enum credentials_obtained obtained);
45 /* Free a memory ccache */
46 static int free_mccache(struct ccache_container *ccc)
48 if (ccc->ccache != NULL) {
49 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
50 ccc->ccache);
51 ccc->ccache = NULL;
54 return 0;
57 /* Free a disk-based ccache */
58 static int free_dccache(struct ccache_container *ccc)
60 if (ccc->ccache != NULL) {
61 krb5_cc_close(ccc->smb_krb5_context->krb5_context,
62 ccc->ccache);
63 ccc->ccache = NULL;
66 return 0;
69 static uint32_t smb_gss_krb5_copy_ccache(uint32_t *min_stat,
70 gss_cred_id_t cred,
71 struct ccache_container *ccc)
73 #ifndef SAMBA4_USES_HEIMDAL /* MIT 1.10 */
74 krb5_context context = ccc->smb_krb5_context->krb5_context;
75 krb5_ccache dummy_ccache = NULL;
76 krb5_creds creds = {0};
77 krb5_cc_cursor cursor = NULL;
78 krb5_principal princ = NULL;
79 krb5_error_code code;
80 char *dummy_name;
81 uint32_t maj_stat = GSS_S_FAILURE;
83 dummy_name = talloc_asprintf(ccc,
84 "MEMORY:gss_krb5_copy_ccache-%p",
85 &ccc->ccache);
86 if (dummy_name == NULL) {
87 *min_stat = ENOMEM;
88 return GSS_S_FAILURE;
92 * Create a dummy ccache, so we can iterate over the credentials
93 * and find the default principal for the ccache we want to
94 * copy. The new ccache needs to be initialized with this
95 * principal.
97 code = krb5_cc_resolve(context, dummy_name, &dummy_ccache);
98 TALLOC_FREE(dummy_name);
99 if (code != 0) {
100 *min_stat = code;
101 return GSS_S_FAILURE;
105 * We do not need set a default principal on the temporary dummy
106 * ccache, as we do consume it at all in this function.
108 maj_stat = gss_krb5_copy_ccache(min_stat, cred, dummy_ccache);
109 if (maj_stat != 0) {
110 krb5_cc_close(context, dummy_ccache);
111 return maj_stat;
114 code = krb5_cc_start_seq_get(context, dummy_ccache, &cursor);
115 if (code != 0) {
116 krb5_cc_close(context, dummy_ccache);
117 *min_stat = EINVAL;
118 return GSS_S_FAILURE;
121 code = krb5_cc_next_cred(context,
122 dummy_ccache,
123 &cursor,
124 &creds);
125 if (code != 0) {
126 krb5_cc_close(context, dummy_ccache);
127 *min_stat = EINVAL;
128 return GSS_S_FAILURE;
131 do {
132 if (creds.ticket_flags & TKT_FLG_PRE_AUTH) {
133 krb5_data *tgs;
135 tgs = krb5_princ_component(context,
136 creds.server,
138 if (tgs != NULL && tgs->length >= 1) {
139 int cmp;
141 cmp = memcmp(tgs->data,
142 KRB5_TGS_NAME,
143 tgs->length);
144 if (cmp == 0 && creds.client != NULL) {
145 princ = creds.client;
146 code = KRB5_CC_END;
147 break;
152 krb5_free_cred_contents(context, &creds);
154 code = krb5_cc_next_cred(context,
155 dummy_ccache,
156 &cursor,
157 &creds);
158 } while (code == 0);
160 if (code == KRB5_CC_END) {
161 krb5_cc_end_seq_get(context, dummy_ccache, &cursor);
162 code = 0;
164 krb5_cc_close(context, dummy_ccache);
166 if (code != 0 || princ == NULL) {
167 krb5_free_cred_contents(context, &creds);
168 *min_stat = EINVAL;
169 return GSS_S_FAILURE;
173 * Set the default principal for the cache we copy
174 * into. This is needed to be able that other calls
175 * can read it with e.g. gss_acquire_cred() or
176 * krb5_cc_get_principal().
178 code = krb5_cc_initialize(context, ccc->ccache, princ);
179 if (code != 0) {
180 krb5_free_cred_contents(context, &creds);
181 *min_stat = EINVAL;
182 return GSS_S_FAILURE;
184 krb5_free_cred_contents(context, &creds);
186 #endif /* SAMBA4_USES_HEIMDAL */
188 return gss_krb5_copy_ccache(min_stat,
189 cred,
190 ccc->ccache);
193 _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
194 struct loadparm_context *lp_ctx,
195 struct smb_krb5_context **smb_krb5_context)
197 int ret;
198 if (cred->smb_krb5_context) {
199 *smb_krb5_context = cred->smb_krb5_context;
200 return 0;
203 ret = smb_krb5_init_context(cred, lp_ctx,
204 &cred->smb_krb5_context);
205 if (ret) {
206 cred->smb_krb5_context = NULL;
207 return ret;
209 *smb_krb5_context = cred->smb_krb5_context;
210 return 0;
213 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
214 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
216 _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
217 struct smb_krb5_context *smb_krb5_context)
219 if (smb_krb5_context == NULL) {
220 talloc_unlink(cred, cred->smb_krb5_context);
221 cred->smb_krb5_context = NULL;
222 return NT_STATUS_OK;
225 if (!talloc_reference(cred, smb_krb5_context)) {
226 return NT_STATUS_NO_MEMORY;
228 cred->smb_krb5_context = smb_krb5_context;
229 return NT_STATUS_OK;
232 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
233 struct ccache_container *ccache,
234 enum credentials_obtained obtained,
235 const char **error_string)
237 bool ok;
238 char *realm;
239 krb5_principal princ;
240 krb5_error_code ret;
241 char *name;
243 if (cred->ccache_obtained > obtained) {
244 return 0;
247 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
248 ccache->ccache, &princ);
250 if (ret) {
251 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
252 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
253 ret, cred));
254 return ret;
257 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
258 if (ret) {
259 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
260 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
261 ret, cred));
262 return ret;
265 ok = cli_credentials_set_principal(cred, name, obtained);
266 krb5_free_unparsed_name(ccache->smb_krb5_context->krb5_context, name);
267 if (!ok) {
268 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
269 return ENOMEM;
272 realm = smb_krb5_principal_get_realm(ccache->smb_krb5_context->krb5_context,
273 princ);
274 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
275 if (realm == NULL) {
276 return ENOMEM;
278 ok = cli_credentials_set_realm(cred, realm, obtained);
279 SAFE_FREE(realm);
280 if (!ok) {
281 return ENOMEM;
284 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
285 cred->ccache_obtained = obtained;
287 return 0;
290 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
291 struct loadparm_context *lp_ctx,
292 const char *name,
293 enum credentials_obtained obtained,
294 const char **error_string)
296 krb5_error_code ret;
297 krb5_principal princ;
298 struct ccache_container *ccc;
299 if (cred->ccache_obtained > obtained) {
300 return 0;
303 ccc = talloc(cred, struct ccache_container);
304 if (!ccc) {
305 (*error_string) = error_message(ENOMEM);
306 return ENOMEM;
309 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
310 &ccc->smb_krb5_context);
311 if (ret) {
312 (*error_string) = error_message(ret);
313 talloc_free(ccc);
314 return ret;
316 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
317 talloc_free(ccc);
318 (*error_string) = error_message(ENOMEM);
319 return ENOMEM;
322 if (name) {
323 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
324 if (ret) {
325 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
326 name,
327 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
328 ret, ccc));
329 talloc_free(ccc);
330 return ret;
332 } else {
333 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
334 if (ret) {
335 (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
336 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
337 ret, ccc));
338 talloc_free(ccc);
339 return ret;
343 talloc_set_destructor(ccc, free_dccache);
345 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
347 if (ret == 0) {
348 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
349 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
351 if (ret) {
352 (*error_string) = error_message(ret);
353 return ret;
356 cred->ccache = ccc;
357 cred->ccache_obtained = obtained;
358 talloc_steal(cred, ccc);
360 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
361 return 0;
363 return 0;
367 * Indicate the we failed to log in to this service/host with these
368 * credentials. The caller passes an unsigned int which they
369 * initialise to the number of times they would like to retry.
371 * This method is used to support re-trying with freshly fetched
372 * credentials in case a server is rebuilt while clients have
373 * non-expired tickets. When the client code gets a logon failure they
374 * throw away the existing credentials for the server and retry.
376 _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
377 const char *principal,
378 unsigned int *count)
380 struct ccache_container *ccc;
381 krb5_creds creds, creds2;
382 int ret;
384 if (principal == NULL) {
385 /* no way to delete if we don't know the principal */
386 return false;
389 ccc = cred->ccache;
390 if (ccc == NULL) {
391 /* not a kerberos connection */
392 return false;
395 if (*count > 0) {
396 /* We have already tried discarding the credentials */
397 return false;
399 (*count)++;
401 ZERO_STRUCT(creds);
402 ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
403 if (ret != 0) {
404 return false;
407 ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
408 if (ret != 0) {
409 /* don't retry - we didn't find these credentials to remove */
410 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
411 return false;
414 ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
415 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
416 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
417 if (ret != 0) {
418 /* don't retry - we didn't find these credentials to
419 * remove. Note that with the current backend this
420 * never happens, as it always returns 0 even if the
421 * creds don't exist, which is why we do a separate
422 * krb5_cc_retrieve_cred() above.
424 return false;
426 return true;
430 static int cli_credentials_new_ccache(struct cli_credentials *cred,
431 struct loadparm_context *lp_ctx,
432 char *ccache_name,
433 struct ccache_container **_ccc,
434 const char **error_string)
436 bool must_free_cc_name = false;
437 krb5_error_code ret;
438 struct ccache_container *ccc = talloc(cred, struct ccache_container);
439 if (!ccc) {
440 return ENOMEM;
443 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
444 &ccc->smb_krb5_context);
445 if (ret) {
446 talloc_free(ccc);
447 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
448 error_message(ret));
449 return ret;
451 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
452 talloc_free(ccc);
453 (*error_string) = strerror(ENOMEM);
454 return ENOMEM;
457 if (!ccache_name) {
458 must_free_cc_name = true;
460 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
461 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
462 (unsigned int)getpid(), ccc);
463 } else {
464 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
465 ccc);
468 if (!ccache_name) {
469 talloc_free(ccc);
470 (*error_string) = strerror(ENOMEM);
471 return ENOMEM;
475 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
476 &ccc->ccache);
477 if (ret) {
478 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
479 ccache_name,
480 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
481 ret, ccc));
482 talloc_free(ccache_name);
483 talloc_free(ccc);
484 return ret;
487 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
488 talloc_set_destructor(ccc, free_mccache);
489 } else {
490 talloc_set_destructor(ccc, free_dccache);
493 if (must_free_cc_name) {
494 talloc_free(ccache_name);
497 *_ccc = ccc;
499 return 0;
502 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
503 struct tevent_context *event_ctx,
504 struct loadparm_context *lp_ctx,
505 char *ccache_name,
506 struct ccache_container **ccc,
507 const char **error_string)
509 krb5_error_code ret;
510 enum credentials_obtained obtained;
512 if (cred->machine_account_pending) {
513 cli_credentials_set_machine_account(cred, lp_ctx);
516 if (cred->ccache_obtained >= cred->ccache_threshold &&
517 cred->ccache_obtained > CRED_UNINITIALISED) {
518 time_t lifetime;
519 bool expired = false;
520 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
521 cred->ccache->ccache, &lifetime);
522 if (ret == KRB5_CC_END) {
523 /* If we have a particular ccache set, without
524 * an initial ticket, then assume there is a
525 * good reason */
526 } else if (ret == 0) {
527 if (lifetime == 0) {
528 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
529 cli_credentials_get_principal(cred, cred)));
530 expired = true;
531 } else if (lifetime < 300) {
532 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
533 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
534 expired = true;
536 } else {
537 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
538 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
539 ret, cred));
540 return ret;
543 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
544 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
546 if (!expired) {
547 *ccc = cred->ccache;
548 return 0;
551 if (cli_credentials_is_anonymous(cred)) {
552 (*error_string) = "Cannot get anonymous kerberos credentials";
553 return EINVAL;
556 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
557 if (ret) {
558 return ret;
561 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
562 if (ret) {
563 return ret;
566 ret = cli_credentials_set_from_ccache(cred, *ccc,
567 obtained, error_string);
569 cred->ccache = *ccc;
570 cred->ccache_obtained = cred->principal_obtained;
571 if (ret) {
572 return ret;
574 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
575 return 0;
578 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
579 struct tevent_context *event_ctx,
580 struct loadparm_context *lp_ctx,
581 struct ccache_container **ccc,
582 const char **error_string)
584 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
587 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
588 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
590 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
591 talloc_unlink(cred, cred->client_gss_creds);
592 cred->client_gss_creds = NULL;
594 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
597 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
598 enum credentials_obtained obtained)
600 /* If the caller just changed the username/password etc, then
601 * any cached credentials are now invalid */
602 if (obtained >= cred->client_gss_creds_obtained) {
603 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
604 talloc_unlink(cred, cred->client_gss_creds);
605 cred->client_gss_creds = NULL;
607 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
609 /* Now that we know that the data is 'this specified', then
610 * don't allow something less 'known' to be returned as a
611 * ccache. Ie, if the username is on the command line, we
612 * don't want to later guess to use a file-based ccache */
613 if (obtained > cred->client_gss_creds_threshold) {
614 cred->client_gss_creds_threshold = obtained;
618 /* We have good reason to think this CCACHE is invalid. Blow it away */
619 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
621 if (cred->ccache_obtained > CRED_UNINITIALISED) {
622 talloc_unlink(cred, cred->ccache);
623 cred->ccache = NULL;
625 cred->ccache_obtained = CRED_UNINITIALISED;
627 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
630 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
631 enum credentials_obtained obtained)
633 /* If the caller just changed the username/password etc, then
634 * any cached credentials are now invalid */
635 if (obtained >= cred->ccache_obtained) {
636 if (cred->ccache_obtained > CRED_UNINITIALISED) {
637 talloc_unlink(cred, cred->ccache);
638 cred->ccache = NULL;
640 cred->ccache_obtained = CRED_UNINITIALISED;
642 /* Now that we know that the data is 'this specified', then
643 * don't allow something less 'known' to be returned as a
644 * ccache. i.e, if the username is on the command line, we
645 * don't want to later guess to use a file-based ccache */
646 if (obtained > cred->ccache_threshold) {
647 cred->ccache_threshold = obtained;
650 cli_credentials_invalidate_client_gss_creds(cred,
651 obtained);
654 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
656 OM_uint32 min_stat;
657 (void)gss_release_cred(&min_stat, &gcc->creds);
658 return 0;
661 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
662 struct tevent_context *event_ctx,
663 struct loadparm_context *lp_ctx,
664 struct gssapi_creds_container **_gcc,
665 const char **error_string)
667 int ret = 0;
668 OM_uint32 maj_stat, min_stat;
669 struct gssapi_creds_container *gcc;
670 struct ccache_container *ccache;
671 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
672 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
673 gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
674 #endif
675 krb5_enctype *etypes = NULL;
677 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
678 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
679 bool expired = false;
680 OM_uint32 lifetime = 0;
681 gss_cred_usage_t usage = 0;
682 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
683 NULL, &lifetime, &usage, NULL);
684 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
685 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
686 expired = true;
687 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
688 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
689 expired = true;
690 } else if (maj_stat != GSS_S_COMPLETE) {
691 *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
692 gssapi_error_string(cred, maj_stat, min_stat, NULL));
693 return EINVAL;
695 if (expired) {
696 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
697 } else {
698 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
699 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
701 *_gcc = cred->client_gss_creds;
702 return 0;
706 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
707 &ccache, error_string);
708 if (ret) {
709 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
710 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
711 } else {
712 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
714 return ret;
717 gcc = talloc(cred, struct gssapi_creds_container);
718 if (!gcc) {
719 (*error_string) = error_message(ENOMEM);
720 return ENOMEM;
723 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
724 ccache->ccache, NULL, NULL,
725 &gcc->creds);
726 if ((maj_stat == GSS_S_FAILURE) &&
727 (min_stat == (OM_uint32)KRB5_CC_END ||
728 min_stat == (OM_uint32)KRB5_CC_NOTFOUND ||
729 min_stat == (OM_uint32)KRB5_FCC_NOFILE))
731 /* This CCACHE is no good. Ensure we don't use it again */
732 cli_credentials_unconditionally_invalidate_ccache(cred);
734 /* Now try again to get a ccache */
735 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
736 &ccache, error_string);
737 if (ret) {
738 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
739 return ret;
742 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
743 ccache->ccache, NULL, NULL,
744 &gcc->creds);
748 if (maj_stat) {
749 talloc_free(gcc);
750 if (min_stat) {
751 ret = min_stat;
752 } else {
753 ret = EINVAL;
755 (*error_string) = talloc_asprintf(cred, "smb_gss_krb5_import_cred failed: %s", error_message(ret));
756 return ret;
761 * transfer the enctypes from the smb_krb5_context to the gssapi layer
763 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
764 * to configure the enctypes via the krb5.conf.
766 * And the gss_init_sec_context() creates it's own krb5_context and
767 * the TGS-REQ had all enctypes in it and only the ones configured
768 * and used for the AS-REQ, so it wasn't possible to disable the usage
769 * of AES keys.
771 min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
772 &etypes);
773 if (min_stat == 0) {
774 OM_uint32 num_ktypes;
776 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
778 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
779 num_ktypes,
780 (int32_t *) etypes);
781 SAFE_FREE(etypes);
782 if (maj_stat) {
783 talloc_free(gcc);
784 if (min_stat) {
785 ret = min_stat;
786 } else {
787 ret = EINVAL;
789 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
790 return ret;
794 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
796 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
798 * This allows us to disable SIGN and SEAL on a TLS connection with
799 * GSS-SPNENO. For example ldaps:// connections.
801 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
802 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
804 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
805 oid,
806 &empty_buffer);
807 if (maj_stat) {
808 talloc_free(gcc);
809 if (min_stat) {
810 ret = min_stat;
811 } else {
812 ret = EINVAL;
814 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
815 return ret;
817 #endif
818 cred->client_gss_creds_obtained = cred->ccache_obtained;
819 talloc_set_destructor(gcc, free_gssapi_creds);
820 cred->client_gss_creds = gcc;
821 *_gcc = gcc;
822 return 0;
826 Set a gssapi cred_id_t into the credentials system. (Client case)
828 This grabs the credentials both 'intact' and getting the krb5
829 ccache out of it. This routine can be generalised in future for
830 the case where we deal with GSSAPI mechs other than krb5.
832 On sucess, the caller must not free gssapi_cred, as it now belongs
833 to the credentials system.
836 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
837 struct loadparm_context *lp_ctx,
838 gss_cred_id_t gssapi_cred,
839 enum credentials_obtained obtained,
840 const char **error_string)
842 int ret;
843 OM_uint32 maj_stat, min_stat;
844 struct ccache_container *ccc = NULL;
845 struct gssapi_creds_container *gcc = NULL;
846 if (cred->client_gss_creds_obtained > obtained) {
847 return 0;
850 gcc = talloc(cred, struct gssapi_creds_container);
851 if (!gcc) {
852 (*error_string) = error_message(ENOMEM);
853 return ENOMEM;
856 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
857 if (ret != 0) {
858 return ret;
861 maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
862 gssapi_cred,
863 ccc);
864 if (maj_stat) {
865 if (min_stat) {
866 ret = min_stat;
867 } else {
868 ret = EINVAL;
870 if (ret) {
871 (*error_string) = error_message(ENOMEM);
875 if (ret == 0) {
876 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
878 cred->ccache = ccc;
879 cred->ccache_obtained = obtained;
880 if (ret == 0) {
881 gcc->creds = gssapi_cred;
882 talloc_set_destructor(gcc, free_gssapi_creds);
884 /* set the clinet_gss_creds_obtained here, as it just
885 got set to UNINITIALISED by the calls above */
886 cred->client_gss_creds_obtained = obtained;
887 cred->client_gss_creds = gcc;
889 return ret;
892 static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
894 krb5_error_code ret;
895 const struct ccache_container *old_ccc = NULL;
896 struct ccache_container *ccc = NULL;
897 char *ccache_name = NULL;
899 old_ccc = cred->ccache;
900 if (old_ccc == NULL) {
901 return 0;
904 ccc = talloc(cred, struct ccache_container);
905 if (ccc == NULL) {
906 return ENOMEM;
908 *ccc = *old_ccc;
909 ccc->ccache = NULL;
911 ccache_name = talloc_asprintf(ccc, "MEMORY:%p", ccc);
913 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
914 ccache_name, &ccc->ccache);
915 if (ret != 0) {
916 TALLOC_FREE(ccc);
917 return ret;
920 talloc_set_destructor(ccc, free_mccache);
922 TALLOC_FREE(ccache_name);
924 ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
925 old_ccc->ccache, ccc->ccache);
926 if (ret != 0) {
927 TALLOC_FREE(ccc);
928 return ret;
931 cred->ccache = ccc;
932 cred->client_gss_creds = NULL;
933 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
934 return ret;
937 _PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
938 struct cli_credentials *src)
940 struct cli_credentials *dst;
941 int ret;
943 dst = talloc(mem_ctx, struct cli_credentials);
944 if (dst == NULL) {
945 return NULL;
948 *dst = *src;
950 ret = cli_credentials_shallow_ccache(dst);
951 if (ret != 0) {
952 TALLOC_FREE(dst);
953 return NULL;
956 return dst;
959 /* Get the keytab (actually, a container containing the krb5_keytab)
960 * attached to this context. If this hasn't been done or set before,
961 * it will be generated from the password.
963 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
964 struct loadparm_context *lp_ctx,
965 struct keytab_container **_ktc)
967 krb5_error_code ret;
968 struct keytab_container *ktc;
969 struct smb_krb5_context *smb_krb5_context;
970 const char *keytab_name;
971 krb5_keytab keytab;
972 TALLOC_CTX *mem_ctx;
973 const char *username = cli_credentials_get_username(cred);
974 const char *upn = NULL;
975 const char *realm = cli_credentials_get_realm(cred);
976 char *salt_principal = NULL;
977 bool is_computer = false;
979 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
980 cred->username_obtained))) {
981 *_ktc = cred->keytab;
982 return 0;
985 if (cli_credentials_is_anonymous(cred)) {
986 return EINVAL;
989 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
990 &smb_krb5_context);
991 if (ret) {
992 return ret;
995 mem_ctx = talloc_new(cred);
996 if (!mem_ctx) {
997 return ENOMEM;
1000 switch (cred->secure_channel_type) {
1001 case SEC_CHAN_WKSTA:
1002 case SEC_CHAN_BDC:
1003 case SEC_CHAN_RODC:
1004 is_computer = true;
1005 break;
1006 default:
1007 upn = cli_credentials_get_principal(cred, mem_ctx);
1008 if (upn == NULL) {
1009 TALLOC_FREE(mem_ctx);
1010 return ENOMEM;
1012 break;
1015 ret = smb_krb5_salt_principal(realm,
1016 username, /* sAMAccountName */
1017 upn, /* userPrincipalName */
1018 is_computer,
1019 mem_ctx,
1020 &salt_principal);
1021 if (ret) {
1022 talloc_free(mem_ctx);
1023 return ret;
1026 ret = smb_krb5_create_memory_keytab(mem_ctx,
1027 smb_krb5_context->krb5_context,
1028 cli_credentials_get_password(cred),
1029 username,
1030 realm,
1031 salt_principal,
1032 cli_credentials_get_kvno(cred),
1033 &keytab,
1034 &keytab_name);
1035 if (ret) {
1036 talloc_free(mem_ctx);
1037 return ret;
1040 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1041 keytab, keytab_name, &ktc);
1042 if (ret) {
1043 talloc_free(mem_ctx);
1044 return ret;
1047 cred->keytab_obtained = (MAX(cred->principal_obtained,
1048 cred->username_obtained));
1050 /* We make this keytab up based on a password. Therefore
1051 * match-by-key is acceptable, we can't match on the wrong
1052 * principal */
1053 ktc->password_based = true;
1055 talloc_steal(cred, ktc);
1056 cred->keytab = ktc;
1057 *_ktc = cred->keytab;
1058 talloc_free(mem_ctx);
1059 return ret;
1062 /* Given the name of a keytab (presumably in the format
1063 * FILE:/etc/krb5.keytab), open it and attach it */
1065 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
1066 struct loadparm_context *lp_ctx,
1067 const char *keytab_name,
1068 enum credentials_obtained obtained)
1070 krb5_error_code ret;
1071 struct keytab_container *ktc;
1072 struct smb_krb5_context *smb_krb5_context;
1073 TALLOC_CTX *mem_ctx;
1075 if (cred->keytab_obtained >= obtained) {
1076 return 0;
1079 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1080 if (ret) {
1081 return ret;
1084 mem_ctx = talloc_new(cred);
1085 if (!mem_ctx) {
1086 return ENOMEM;
1089 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1090 NULL, keytab_name, &ktc);
1091 if (ret) {
1092 return ret;
1095 cred->keytab_obtained = obtained;
1097 talloc_steal(cred, ktc);
1098 cred->keytab = ktc;
1099 talloc_free(mem_ctx);
1101 return ret;
1104 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1106 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
1107 struct loadparm_context *lp_ctx,
1108 struct gssapi_creds_container **_gcc)
1110 int ret = 0;
1111 OM_uint32 maj_stat, min_stat;
1112 struct gssapi_creds_container *gcc;
1113 struct keytab_container *ktc;
1114 struct smb_krb5_context *smb_krb5_context;
1115 TALLOC_CTX *mem_ctx;
1116 krb5_principal princ;
1117 const char *error_string;
1118 enum credentials_obtained obtained;
1120 mem_ctx = talloc_new(cred);
1121 if (!mem_ctx) {
1122 return ENOMEM;
1125 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1126 if (ret) {
1127 return ret;
1130 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1131 if (ret) {
1132 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1133 error_string));
1134 talloc_free(mem_ctx);
1135 return ret;
1138 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1139 talloc_free(mem_ctx);
1140 *_gcc = cred->server_gss_creds;
1141 return 0;
1144 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1145 if (ret) {
1146 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1147 return ret;
1150 gcc = talloc(cred, struct gssapi_creds_container);
1151 if (!gcc) {
1152 talloc_free(mem_ctx);
1153 return ENOMEM;
1156 if (ktc->password_based || obtained < CRED_SPECIFIED) {
1158 * This creates a GSSAPI cred_id_t for match-by-key with only
1159 * the keytab set
1161 princ = NULL;
1163 maj_stat = smb_gss_krb5_import_cred(&min_stat,
1164 smb_krb5_context->krb5_context,
1165 NULL, princ,
1166 ktc->keytab,
1167 &gcc->creds);
1168 if (maj_stat) {
1169 if (min_stat) {
1170 ret = min_stat;
1171 } else {
1172 ret = EINVAL;
1175 if (ret == 0) {
1176 cred->server_gss_creds_obtained = cred->keytab_obtained;
1177 talloc_set_destructor(gcc, free_gssapi_creds);
1178 cred->server_gss_creds = gcc;
1179 *_gcc = gcc;
1181 talloc_free(mem_ctx);
1182 return ret;
1185 /**
1186 * Set Kerberos KVNO
1189 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1190 int kvno)
1192 cred->kvno = kvno;
1196 * Return Kerberos KVNO
1199 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1201 return cred->kvno;
1205 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
1207 return cred->salt_principal;
1210 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
1212 talloc_free(cred->salt_principal);
1213 cred->salt_principal = talloc_strdup(cred, principal);
1216 /* The 'impersonate_principal' is used to allow one Kerberos principal
1217 * (and it's associated keytab etc) to impersonate another. The
1218 * ability to do this is controlled by the KDC, but it is generally
1219 * permitted to impersonate anyone to yourself. This allows any
1220 * member of the domain to get the groups of a user. This is also
1221 * known as S4U2Self */
1223 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1225 return cred->impersonate_principal;
1229 * The 'self_service' is the service principal that
1230 * represents the same object (by its objectSid)
1231 * as the client principal (typically our machine account).
1232 * When trying to impersonate 'impersonate_principal' with
1233 * S4U2Self.
1235 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1237 return cred->self_service;
1240 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1241 const char *principal,
1242 const char *self_service)
1244 talloc_free(cred->impersonate_principal);
1245 cred->impersonate_principal = talloc_strdup(cred, principal);
1246 talloc_free(cred->self_service);
1247 cred->self_service = talloc_strdup(cred, self_service);
1248 cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
1252 * when impersonating for S4U2proxy we need to set the target principal.
1253 * Similarly, we may only be authorized to do general impersonation to
1254 * some particular services.
1256 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1258 * NULL means that tickets will be obtained for the krbtgt service.
1261 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1263 return cred->target_service;
1266 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1268 talloc_free(cred->target_service);
1269 cred->target_service = talloc_strdup(cred, target_service);