ctdb-doc: Drop unnecessary, broken attempt at rpc.statd stack trace
[samba4-gss.git] / auth / credentials / credentials_krb5.c
blobce76b10361df9888c9f6e38fa60f49b1c5293f16
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_krb5.h"
31 #include "auth/kerberos/kerberos_credentials.h"
32 #include "auth/kerberos/kerberos_srv_keytab.h"
33 #include "auth/kerberos/kerberos_util.h"
34 #include "auth/kerberos/pac_utils.h"
35 #include "param/param.h"
36 #include "../libds/common/flags.h"
38 #undef DBGC_CLASS
39 #define DBGC_CLASS DBGC_AUTH
41 #undef strncasecmp
43 static void cli_credentials_invalidate_client_gss_creds(
44 struct cli_credentials *cred,
45 enum credentials_obtained obtained);
47 /* Free a memory ccache */
48 static int free_mccache(struct ccache_container *ccc)
50 if (ccc->ccache != NULL) {
51 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
52 ccc->ccache);
53 ccc->ccache = NULL;
56 return 0;
59 /* Free a disk-based ccache */
60 static int free_dccache(struct ccache_container *ccc)
62 if (ccc->ccache != NULL) {
63 krb5_cc_close(ccc->smb_krb5_context->krb5_context,
64 ccc->ccache);
65 ccc->ccache = NULL;
68 return 0;
71 static uint32_t smb_gss_krb5_copy_ccache(uint32_t *min_stat,
72 gss_cred_id_t cred,
73 struct ccache_container *ccc)
75 #ifndef SAMBA4_USES_HEIMDAL /* MIT 1.10 */
76 krb5_context context = ccc->smb_krb5_context->krb5_context;
77 krb5_ccache dummy_ccache = NULL;
78 krb5_creds creds = {0};
79 krb5_cc_cursor cursor = NULL;
80 krb5_principal princ = NULL;
81 krb5_error_code code;
82 uint32_t maj_stat = GSS_S_FAILURE;
85 * Create a dummy ccache, so we can iterate over the credentials
86 * and find the default principal for the ccache we want to
87 * copy. The new ccache needs to be initialized with this
88 * principal.
90 code = smb_krb5_cc_new_unique_memory(context, NULL, NULL, &dummy_ccache);
91 if (code != 0) {
92 *min_stat = code;
93 return GSS_S_FAILURE;
97 * We do not need set a default principal on the temporary dummy
98 * ccache, as we do consume it at all in this function.
100 maj_stat = gss_krb5_copy_ccache(min_stat, cred, dummy_ccache);
101 if (maj_stat != 0) {
102 krb5_cc_destroy(context, dummy_ccache);
103 return maj_stat;
106 code = krb5_cc_start_seq_get(context, dummy_ccache, &cursor);
107 if (code != 0) {
108 krb5_cc_destroy(context, dummy_ccache);
109 *min_stat = EINVAL;
110 return GSS_S_FAILURE;
113 code = krb5_cc_next_cred(context,
114 dummy_ccache,
115 &cursor,
116 &creds);
117 if (code != 0) {
118 krb5_cc_destroy(context, dummy_ccache);
119 *min_stat = EINVAL;
120 return GSS_S_FAILURE;
123 do {
124 if (creds.ticket_flags & TKT_FLG_PRE_AUTH) {
125 krb5_data *tgs;
127 tgs = krb5_princ_component(context,
128 creds.server,
130 if (tgs != NULL && tgs->length >= 1) {
131 int cmp;
133 cmp = memcmp(tgs->data,
134 KRB5_TGS_NAME,
135 tgs->length);
136 if (cmp == 0 && creds.client != NULL) {
137 princ = creds.client;
138 code = KRB5_CC_END;
139 break;
144 krb5_free_cred_contents(context, &creds);
146 code = krb5_cc_next_cred(context,
147 dummy_ccache,
148 &cursor,
149 &creds);
150 } while (code == 0);
152 if (code == KRB5_CC_END) {
153 krb5_cc_end_seq_get(context, dummy_ccache, &cursor);
154 code = 0;
156 krb5_cc_destroy(context, dummy_ccache);
158 if (code != 0 || princ == NULL) {
159 krb5_free_cred_contents(context, &creds);
160 *min_stat = EINVAL;
161 return GSS_S_FAILURE;
165 * Set the default principal for the cache we copy
166 * into. This is needed to be able that other calls
167 * can read it with e.g. gss_acquire_cred() or
168 * krb5_cc_get_principal().
170 code = krb5_cc_initialize(context, ccc->ccache, princ);
171 if (code != 0) {
172 krb5_free_cred_contents(context, &creds);
173 *min_stat = EINVAL;
174 return GSS_S_FAILURE;
176 krb5_free_cred_contents(context, &creds);
178 #endif /* SAMBA4_USES_HEIMDAL */
180 return gss_krb5_copy_ccache(min_stat,
181 cred,
182 ccc->ccache);
185 _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
186 struct loadparm_context *lp_ctx,
187 struct smb_krb5_context **smb_krb5_context)
189 int ret;
190 if (cred->smb_krb5_context) {
191 *smb_krb5_context = cred->smb_krb5_context;
192 return 0;
195 ret = smb_krb5_init_context(cred, lp_ctx,
196 &cred->smb_krb5_context);
197 if (ret) {
198 cred->smb_krb5_context = NULL;
199 return ret;
201 *smb_krb5_context = cred->smb_krb5_context;
202 return 0;
205 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
206 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
208 _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
209 struct smb_krb5_context *smb_krb5_context)
211 if (smb_krb5_context == NULL) {
212 talloc_unlink(cred, cred->smb_krb5_context);
213 cred->smb_krb5_context = NULL;
214 return NT_STATUS_OK;
217 if (!talloc_reference(cred, smb_krb5_context)) {
218 return NT_STATUS_NO_MEMORY;
220 cred->smb_krb5_context = smb_krb5_context;
221 return NT_STATUS_OK;
224 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
225 struct ccache_container *ccache,
226 enum credentials_obtained obtained,
227 const char **error_string)
229 bool ok;
230 char *realm;
231 krb5_principal princ;
232 krb5_error_code ret;
233 char *name;
235 if (cred->ccache_obtained > obtained) {
236 return 0;
239 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
240 ccache->ccache, &princ);
242 if (ret) {
243 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
244 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
245 ret, cred));
246 return ret;
249 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
250 if (ret) {
251 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
252 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
253 ret, cred));
254 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
255 return ret;
258 ok = cli_credentials_set_principal(cred, name, obtained);
259 krb5_free_unparsed_name(ccache->smb_krb5_context->krb5_context, name);
260 if (!ok) {
261 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
262 return ENOMEM;
265 realm = smb_krb5_principal_get_realm(
266 cred, ccache->smb_krb5_context->krb5_context, princ);
267 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
268 if (realm == NULL) {
269 return ENOMEM;
271 ok = cli_credentials_set_realm(cred, realm, obtained);
272 TALLOC_FREE(realm);
273 if (!ok) {
274 return ENOMEM;
277 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
278 cred->ccache_obtained = obtained;
280 return 0;
283 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
284 struct loadparm_context *lp_ctx,
285 const char *name,
286 enum credentials_obtained obtained,
287 const char **error_string)
289 krb5_error_code ret;
290 krb5_principal princ;
291 struct ccache_container *ccc;
292 if (cred->ccache_obtained > obtained) {
293 return 0;
296 ccc = talloc(cred, struct ccache_container);
297 if (!ccc) {
298 (*error_string) = error_message(ENOMEM);
299 return ENOMEM;
302 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
303 &ccc->smb_krb5_context);
304 if (ret) {
305 (*error_string) = error_message(ret);
306 talloc_free(ccc);
307 return ret;
309 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
310 talloc_free(ccc);
311 (*error_string) = error_message(ENOMEM);
312 return ENOMEM;
315 if (name) {
316 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
317 if (ret) {
318 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
319 name,
320 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
321 ret, ccc));
322 talloc_free(ccc);
323 return ret;
325 } else {
327 * This is where the caller really wants to use
328 * the default krb5 ccache.
330 ret = smb_force_krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
331 if (ret) {
332 (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
333 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
334 ret, ccc));
335 talloc_free(ccc);
336 return ret;
340 talloc_set_destructor(ccc, free_dccache);
342 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
344 if (ret == 0) {
345 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
346 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
348 if (ret) {
349 (*error_string) = error_message(ret);
350 TALLOC_FREE(ccc);
351 return ret;
355 cred->ccache = ccc;
356 cred->ccache_obtained = obtained;
358 cli_credentials_invalidate_client_gss_creds(
359 cred, cred->ccache_obtained);
361 return 0;
364 #ifndef SAMBA4_USES_HEIMDAL
366 * This function is a workaround for old MIT Kerberos versions which did not
367 * implement the krb5_cc_remove_cred function. It creates a temporary
368 * credentials cache to copy the credentials in the current cache
369 * except the one we want to remove and then overwrites the contents of the
370 * current cache with the temporary copy.
372 static krb5_error_code krb5_cc_remove_cred_wrap(struct ccache_container *ccc,
373 krb5_creds *creds)
375 krb5_ccache dummy_ccache = NULL;
376 krb5_creds cached_creds = {0};
377 krb5_cc_cursor cursor = NULL;
378 krb5_error_code code;
380 code = smb_krb5_cc_new_unique_memory(ccc->smb_krb5_context->krb5_context,
381 NULL, NULL,
382 &dummy_ccache);
383 if (code != 0) {
384 DBG_ERR("krb5_cc_resolve failed: %s\n",
385 smb_get_krb5_error_message(
386 ccc->smb_krb5_context->krb5_context,
387 code, ccc));
388 return code;
391 code = krb5_cc_start_seq_get(ccc->smb_krb5_context->krb5_context,
392 ccc->ccache,
393 &cursor);
394 if (code != 0) {
395 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
396 dummy_ccache);
398 DBG_ERR("krb5_cc_start_seq_get failed: %s\n",
399 smb_get_krb5_error_message(
400 ccc->smb_krb5_context->krb5_context,
401 code, ccc));
402 return code;
405 while ((code = krb5_cc_next_cred(ccc->smb_krb5_context->krb5_context,
406 ccc->ccache,
407 &cursor,
408 &cached_creds)) == 0) {
409 /* If the principal matches skip it and do not copy to the
410 * temporary cache as this is the one we want to remove */
411 if (krb5_principal_compare_flags(
412 ccc->smb_krb5_context->krb5_context,
413 creds->server,
414 cached_creds.server,
415 0)) {
416 continue;
419 code = krb5_cc_store_cred(
420 ccc->smb_krb5_context->krb5_context,
421 dummy_ccache,
422 &cached_creds);
423 if (code != 0) {
424 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
425 dummy_ccache);
426 DBG_ERR("krb5_cc_store_cred failed: %s\n",
427 smb_get_krb5_error_message(
428 ccc->smb_krb5_context->krb5_context,
429 code, ccc));
430 return code;
434 if (code == KRB5_CC_END) {
435 krb5_cc_end_seq_get(ccc->smb_krb5_context->krb5_context,
436 dummy_ccache,
437 &cursor);
438 code = 0;
441 if (code != 0) {
442 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
443 dummy_ccache);
444 DBG_ERR("krb5_cc_next_cred failed: %s\n",
445 smb_get_krb5_error_message(
446 ccc->smb_krb5_context->krb5_context,
447 code, ccc));
448 return code;
451 code = krb5_cc_initialize(ccc->smb_krb5_context->krb5_context,
452 ccc->ccache,
453 creds->client);
454 if (code != 0) {
455 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
456 dummy_ccache);
457 DBG_ERR("krb5_cc_initialize failed: %s\n",
458 smb_get_krb5_error_message(
459 ccc->smb_krb5_context->krb5_context,
460 code, ccc));
461 return code;
464 code = krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
465 dummy_ccache,
466 ccc->ccache);
467 if (code != 0) {
468 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
469 dummy_ccache);
470 DBG_ERR("krb5_cc_copy_creds failed: %s\n",
471 smb_get_krb5_error_message(
472 ccc->smb_krb5_context->krb5_context,
473 code, ccc));
474 return code;
477 code = krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
478 dummy_ccache);
479 if (code != 0) {
480 DBG_ERR("krb5_cc_destroy failed: %s\n",
481 smb_get_krb5_error_message(
482 ccc->smb_krb5_context->krb5_context,
483 code, ccc));
484 return code;
487 return code;
489 #endif
492 * Indicate that we failed to log in to this service/host with these
493 * credentials. The caller passes an unsigned int which they
494 * initialise to the number of times they would like to retry.
496 * This method is used to support re-trying with freshly fetched
497 * credentials in case a server is rebuilt while clients have
498 * non-expired tickets. When the client code gets a logon failure they
499 * throw away the existing credentials for the server and retry.
501 _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
502 const char *principal,
503 unsigned int *count)
505 struct ccache_container *ccc;
506 krb5_creds creds, creds2;
507 int ret;
509 if (principal == NULL) {
510 /* no way to delete if we don't know the principal */
511 return false;
514 ccc = cred->ccache;
515 if (ccc == NULL) {
516 /* not a kerberos connection */
517 return false;
520 if (*count > 0) {
521 /* We have already tried discarding the credentials */
522 return false;
524 (*count)++;
526 ZERO_STRUCT(creds);
527 ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
528 if (ret != 0) {
529 return false;
532 /* MIT kerberos requires creds.client to match against cached
533 * credentials */
534 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context,
535 ccc->ccache,
536 &creds.client);
537 if (ret != 0) {
538 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context,
539 &creds);
540 DBG_ERR("krb5_cc_get_principal failed: %s\n",
541 smb_get_krb5_error_message(
542 ccc->smb_krb5_context->krb5_context,
543 ret, ccc));
544 return false;
547 ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
548 if (ret != 0) {
549 /* don't retry - we didn't find these credentials to remove */
550 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
551 return false;
554 ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
555 #ifndef SAMBA4_USES_HEIMDAL
556 if (ret == KRB5_CC_NOSUPP) {
557 /* Old MIT kerberos versions did not implement
558 * krb5_cc_remove_cred */
559 ret = krb5_cc_remove_cred_wrap(ccc, &creds);
561 #endif
562 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
563 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
564 if (ret != 0) {
565 /* don't retry - we didn't find these credentials to
566 * remove. Note that with the current backend this
567 * never happens, as it always returns 0 even if the
568 * creds don't exist, which is why we do a separate
569 * krb5_cc_retrieve_cred() above.
571 DBG_ERR("krb5_cc_remove_cred failed: %s\n",
572 smb_get_krb5_error_message(
573 ccc->smb_krb5_context->krb5_context,
574 ret, ccc));
575 return false;
577 return true;
581 static int cli_credentials_new_ccache(struct cli_credentials *cred,
582 struct loadparm_context *lp_ctx,
583 char *given_ccache_name,
584 struct ccache_container **_ccc,
585 const char **error_string)
587 char *ccache_name = given_ccache_name;
588 bool must_free_cc_name = false;
589 krb5_error_code ret;
590 struct ccache_container *ccc = talloc(cred, struct ccache_container);
591 if (!ccc) {
592 return ENOMEM;
595 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
596 &ccc->smb_krb5_context);
597 if (ret) {
598 talloc_free(ccc);
599 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
600 error_message(ret));
601 return ret;
603 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
604 talloc_free(ccc);
605 (*error_string) = strerror(ENOMEM);
606 return ENOMEM;
609 if (!ccache_name) {
610 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
611 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
612 (unsigned int)getpid(), ccc);
613 if (ccache_name == NULL) {
614 talloc_free(ccc);
615 (*error_string) = strerror(ENOMEM);
616 return ENOMEM;
618 must_free_cc_name = true;
622 if (ccache_name != NULL) {
623 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
624 &ccc->ccache);
625 } else {
626 ret = smb_krb5_cc_new_unique_memory(ccc->smb_krb5_context->krb5_context,
627 ccc, &ccache_name,
628 &ccc->ccache);
629 must_free_cc_name = true;
631 if (ret) {
632 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
633 ccache_name,
634 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
635 ret, ccc));
636 talloc_free(ccc);
637 return ret;
640 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
641 talloc_set_destructor(ccc, free_mccache);
642 } else {
643 talloc_set_destructor(ccc, free_dccache);
646 if (must_free_cc_name) {
647 talloc_free(ccache_name);
650 *_ccc = ccc;
652 return 0;
655 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
656 struct tevent_context *event_ctx,
657 struct loadparm_context *lp_ctx,
658 char *ccache_name,
659 struct ccache_container **ccc,
660 const char **error_string)
662 krb5_error_code ret;
663 enum credentials_obtained obtained;
665 if (cred->machine_account_pending) {
666 cli_credentials_set_machine_account(cred, lp_ctx);
669 if (cred->ccache_obtained >= cred->ccache_threshold &&
670 cred->ccache_obtained > CRED_UNINITIALISED) {
671 time_t lifetime;
672 bool expired = false;
673 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
674 cred->ccache->ccache, &lifetime);
675 if (ret == KRB5_CC_END || ret == ENOENT) {
676 /* If we have a particular ccache set, without
677 * an initial ticket, then assume there is a
678 * good reason */
679 } else if (ret == 0) {
680 if (lifetime == 0) {
681 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
682 cli_credentials_get_principal(cred, cred)));
683 expired = true;
684 } else if (lifetime < 300) {
685 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
686 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
687 expired = true;
689 } else {
690 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
691 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
692 ret, cred));
693 return ret;
696 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
697 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
699 if (!expired) {
700 *ccc = cred->ccache;
701 return 0;
704 if (cli_credentials_is_anonymous(cred)) {
705 (*error_string) = "Cannot get anonymous kerberos credentials";
706 return EINVAL;
709 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
710 if (ret) {
711 return ret;
714 ret = kinit_to_ccache(cred,
715 cred,
716 (*ccc)->smb_krb5_context,
717 lp_ctx,
718 event_ctx,
719 (*ccc)->ccache,
720 &obtained,
721 error_string);
722 if (ret) {
723 return ret;
726 ret = cli_credentials_set_from_ccache(cred, *ccc,
727 obtained, error_string);
729 cred->ccache = *ccc;
730 cred->ccache_obtained = cred->principal_obtained;
731 if (ret) {
732 return ret;
734 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
735 return 0;
738 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
739 struct tevent_context *event_ctx,
740 struct loadparm_context *lp_ctx,
741 struct ccache_container **ccc,
742 const char **error_string)
744 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
748 * @brief Check if a valid Kerberos credential cache is attached.
750 * This will not ask for a password nor do a kinit.
752 * @param cred The credentials context.
754 * @param mem_ctx A memory context to allocate the ccache_name.
756 * @param ccache_name A pointer to a string to store the ccache name.
758 * @param obtained A pointer to store the information how the ccache was
759 * obtained.
761 * @return True if a credential cache is attached, false if not or an error
762 * occurred.
764 _PUBLIC_ bool cli_credentials_get_ccache_name_obtained(
765 struct cli_credentials *cred,
766 TALLOC_CTX *mem_ctx,
767 char **ccache_name,
768 enum credentials_obtained *obtained)
770 if (ccache_name != NULL) {
771 *ccache_name = NULL;
774 if (obtained != NULL) {
775 *obtained = CRED_UNINITIALISED;
778 if (cred->machine_account_pending) {
779 return false;
782 if (cred->ccache_obtained == CRED_UNINITIALISED) {
783 return false;
786 if (cred->ccache_obtained >= cred->ccache_threshold) {
787 krb5_context k5ctx = cred->ccache->smb_krb5_context->krb5_context;
788 krb5_ccache k5ccache = cred->ccache->ccache;
789 krb5_error_code ret;
790 time_t lifetime = 0;
792 ret = smb_krb5_cc_get_lifetime(k5ctx, k5ccache, &lifetime);
793 if (ret == KRB5_CC_END || ret == ENOENT) {
794 return false;
796 if (ret != 0) {
797 return false;
799 if (lifetime == 0) {
800 return false;
801 } else if (lifetime < 300) {
802 if (cred->password_obtained >= cred->ccache_obtained) {
804 * we have a password to re-kinit
805 * so let the caller try that.
807 return false;
811 if (ccache_name != NULL) {
812 char *name = NULL;
814 ret = krb5_cc_get_full_name(k5ctx, k5ccache, &name);
815 if (ret != 0) {
816 return false;
819 *ccache_name = talloc_strdup(mem_ctx, name);
820 SAFE_FREE(name);
821 if (*ccache_name == NULL) {
822 return false;
826 if (obtained != NULL) {
827 *obtained = cred->ccache_obtained;
830 return true;
833 return false;
836 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
837 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
839 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
840 talloc_unlink(cred, cred->client_gss_creds);
841 cred->client_gss_creds = NULL;
843 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
846 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
847 enum credentials_obtained obtained)
849 /* If the caller just changed the username/password etc, then
850 * any cached credentials are now invalid */
851 if (obtained >= cred->client_gss_creds_obtained) {
852 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
853 talloc_unlink(cred, cred->client_gss_creds);
854 cred->client_gss_creds = NULL;
856 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
858 /* Now that we know that the data is 'this specified', then
859 * don't allow something less 'known' to be returned as a
860 * ccache. Ie, if the username is on the command line, we
861 * don't want to later guess to use a file-based ccache */
862 if (obtained > cred->client_gss_creds_threshold) {
863 cred->client_gss_creds_threshold = obtained;
867 /* We have good reason to think this CCACHE is invalid. Blow it away */
868 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
870 if (cred->ccache_obtained > CRED_UNINITIALISED) {
871 talloc_unlink(cred, cred->ccache);
872 cred->ccache = NULL;
874 cred->ccache_obtained = CRED_UNINITIALISED;
876 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
879 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
880 enum credentials_obtained obtained)
882 /* If the caller just changed the username/password etc, then
883 * any cached credentials are now invalid */
884 if (obtained >= cred->ccache_obtained) {
885 if (cred->ccache_obtained > CRED_UNINITIALISED) {
886 talloc_unlink(cred, cred->ccache);
887 cred->ccache = NULL;
889 cred->ccache_obtained = CRED_UNINITIALISED;
891 /* Now that we know that the data is 'this specified', then
892 * don't allow something less 'known' to be returned as a
893 * ccache. i.e, if the username is on the command line, we
894 * don't want to later guess to use a file-based ccache */
895 if (obtained > cred->ccache_threshold) {
896 cred->ccache_threshold = obtained;
899 cli_credentials_invalidate_client_gss_creds(cred,
900 obtained);
903 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
905 OM_uint32 min_stat;
906 (void)gss_release_cred(&min_stat, &gcc->creds);
907 return 0;
910 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
911 struct tevent_context *event_ctx,
912 struct loadparm_context *lp_ctx,
913 struct gssapi_creds_container **_gcc,
914 const char **error_string)
916 int ret = 0;
917 OM_uint32 maj_stat, min_stat;
918 struct gssapi_creds_container *gcc;
919 struct ccache_container *ccache;
920 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
921 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
922 gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
923 #endif
924 krb5_enctype *etypes = NULL;
926 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
927 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
928 bool expired = false;
929 OM_uint32 lifetime = 0;
930 gss_cred_usage_t usage = 0;
931 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
932 NULL, &lifetime, &usage, NULL);
933 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
934 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
935 expired = true;
936 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
937 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
938 expired = true;
939 } else if (maj_stat != GSS_S_COMPLETE) {
940 *error_string = talloc_asprintf(cred, "inquiry of credential lifetime via GSSAPI gss_inquire_cred failed: %s\n",
941 gssapi_error_string(cred, maj_stat, min_stat, NULL));
942 return EINVAL;
944 if (expired) {
945 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
946 } else {
947 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
948 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
950 *_gcc = cred->client_gss_creds;
951 return 0;
955 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
956 &ccache, error_string);
957 if (ret) {
958 if (cli_credentials_get_kerberos_state(cred) == CRED_USE_KERBEROS_REQUIRED) {
959 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
960 } else {
961 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
963 return ret;
966 gcc = talloc(cred, struct gssapi_creds_container);
967 if (!gcc) {
968 (*error_string) = error_message(ENOMEM);
969 return ENOMEM;
972 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
973 ccache->ccache, NULL, NULL,
974 &gcc->creds);
975 if ((maj_stat == GSS_S_FAILURE) &&
976 (min_stat == (OM_uint32)KRB5_CC_END ||
977 min_stat == (OM_uint32)KRB5_CC_NOTFOUND ||
978 min_stat == (OM_uint32)KRB5_FCC_NOFILE))
980 /* This CCACHE is no good. Ensure we don't use it again */
981 cli_credentials_unconditionally_invalidate_ccache(cred);
983 /* Now try again to get a ccache */
984 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
985 &ccache, error_string);
986 if (ret) {
987 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
988 return ret;
991 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
992 ccache->ccache, NULL, NULL,
993 &gcc->creds);
997 if (maj_stat) {
998 talloc_free(gcc);
999 if (min_stat) {
1000 ret = min_stat;
1001 } else {
1002 ret = EINVAL;
1004 (*error_string) = talloc_asprintf(cred, "smb_gss_krb5_import_cred failed: %s", error_message(ret));
1005 return ret;
1010 * transfer the enctypes from the smb_krb5_context to the gssapi layer
1012 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
1013 * to configure the enctypes via the krb5.conf.
1015 * And the gss_init_sec_context() creates it's own krb5_context and
1016 * the TGS-REQ had all enctypes in it and only the ones configured
1017 * and used for the AS-REQ, so it wasn't possible to disable the usage
1018 * of AES keys.
1020 min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
1021 &etypes);
1022 if (min_stat == 0) {
1023 OM_uint32 num_ktypes;
1025 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
1027 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
1028 num_ktypes,
1029 (int32_t *) etypes);
1030 krb5_free_enctypes(ccache->smb_krb5_context->krb5_context,
1031 etypes);
1032 if (maj_stat) {
1033 talloc_free(gcc);
1034 if (min_stat) {
1035 ret = min_stat;
1036 } else {
1037 ret = EINVAL;
1039 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
1040 return ret;
1044 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
1046 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
1048 * This allows us to disable SIGN and SEAL on a TLS connection with
1049 * GSS-SPNENO. For example ldaps:// connections.
1051 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
1052 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
1054 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
1055 oid,
1056 &empty_buffer);
1057 if (maj_stat) {
1058 talloc_free(gcc);
1059 if (min_stat) {
1060 ret = min_stat;
1061 } else {
1062 ret = EINVAL;
1064 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
1065 return ret;
1067 #endif
1068 cred->client_gss_creds_obtained = cred->ccache_obtained;
1069 talloc_set_destructor(gcc, free_gssapi_creds);
1070 cred->client_gss_creds = gcc;
1071 *_gcc = gcc;
1072 return 0;
1076 Set a gssapi cred_id_t into the credentials system. (Client case)
1078 This grabs the credentials both 'intact' and getting the krb5
1079 ccache out of it. This routine can be generalised in future for
1080 the case where we deal with GSSAPI mechs other than krb5.
1082 On success, the caller must not free gssapi_cred, as it now belongs
1083 to the credentials system.
1086 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
1087 struct loadparm_context *lp_ctx,
1088 gss_cred_id_t gssapi_cred,
1089 enum credentials_obtained obtained,
1090 const char **error_string)
1092 int ret;
1093 OM_uint32 maj_stat, min_stat;
1094 struct ccache_container *ccc = NULL;
1095 struct gssapi_creds_container *gcc = NULL;
1096 if (cred->client_gss_creds_obtained > obtained) {
1097 return 0;
1100 gcc = talloc(cred, struct gssapi_creds_container);
1101 if (!gcc) {
1102 (*error_string) = error_message(ENOMEM);
1103 return ENOMEM;
1106 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
1107 if (ret != 0) {
1108 return ret;
1111 maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
1112 gssapi_cred,
1113 ccc);
1114 if (maj_stat) {
1115 if (min_stat) {
1116 ret = min_stat;
1117 } else {
1118 ret = EINVAL;
1120 if (ret) {
1121 (*error_string) = error_message(ENOMEM);
1125 if (ret == 0) {
1126 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
1128 cred->ccache = ccc;
1129 cred->ccache_obtained = obtained;
1130 if (ret == 0) {
1131 gcc->creds = gssapi_cred;
1132 talloc_set_destructor(gcc, free_gssapi_creds);
1134 /* set the client_gss_creds_obtained here, as it just
1135 got set to UNINITIALISED by the calls above */
1136 cred->client_gss_creds_obtained = obtained;
1137 cred->client_gss_creds = gcc;
1139 return ret;
1142 static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
1144 krb5_error_code ret;
1145 const struct ccache_container *old_ccc = NULL;
1146 enum credentials_obtained old_obtained;
1147 struct ccache_container *ccc = NULL;
1148 krb5_principal princ;
1150 old_obtained = cred->ccache_obtained;
1151 old_ccc = cred->ccache;
1152 if (old_ccc == NULL) {
1153 return 0;
1156 cred->ccache = NULL;
1157 cred->ccache_obtained = CRED_UNINITIALISED;
1158 cred->client_gss_creds = NULL;
1159 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
1161 ret = krb5_cc_get_principal(
1162 old_ccc->smb_krb5_context->krb5_context,
1163 old_ccc->ccache,
1164 &princ);
1165 if (ret != 0) {
1167 * This is an empty ccache. No point in copying anything.
1169 return 0;
1171 krb5_free_principal(old_ccc->smb_krb5_context->krb5_context, princ);
1173 ccc = talloc(cred, struct ccache_container);
1174 if (ccc == NULL) {
1175 return ENOMEM;
1177 *ccc = *old_ccc;
1178 ccc->ccache = NULL;
1180 ret = smb_krb5_cc_new_unique_memory(ccc->smb_krb5_context->krb5_context,
1181 NULL,
1182 NULL,
1183 &ccc->ccache);
1184 if (ret != 0) {
1185 TALLOC_FREE(ccc);
1186 return ret;
1189 talloc_set_destructor(ccc, free_mccache);
1191 ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
1192 old_ccc->ccache, ccc->ccache);
1193 if (ret != 0) {
1194 TALLOC_FREE(ccc);
1195 return ret;
1198 cred->ccache = ccc;
1199 cred->ccache_obtained = old_obtained;
1200 return ret;
1203 _PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
1204 struct cli_credentials *src)
1206 struct cli_credentials *dst, *armor_credentials;
1207 int ret;
1209 dst = talloc(mem_ctx, struct cli_credentials);
1210 if (dst == NULL) {
1211 return NULL;
1214 *dst = *src;
1216 if (dst->krb5_fast_armor_credentials != NULL) {
1217 armor_credentials = talloc_reference(dst, dst->krb5_fast_armor_credentials);
1218 if (armor_credentials == NULL) {
1219 TALLOC_FREE(dst);
1220 return NULL;
1224 ret = cli_credentials_shallow_ccache(dst);
1225 if (ret != 0) {
1226 TALLOC_FREE(dst);
1227 return NULL;
1230 return dst;
1233 /* Get the keytab (actually, a container containing the krb5_keytab)
1234 * attached to this context. If this hasn't been done or set before,
1235 * it will be generated from the password.
1237 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
1238 struct loadparm_context *lp_ctx,
1239 struct keytab_container **_ktc)
1241 krb5_error_code ret;
1242 struct keytab_container *ktc;
1243 struct smb_krb5_context *smb_krb5_context;
1244 const char *keytab_name;
1245 krb5_keytab keytab;
1246 TALLOC_CTX *mem_ctx;
1247 const char *username = cli_credentials_get_username(cred);
1248 const char *realm = cli_credentials_get_realm(cred);
1249 char *salt_principal = NULL;
1251 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
1252 cred->username_obtained))) {
1253 *_ktc = cred->keytab;
1254 return 0;
1257 if (cli_credentials_is_anonymous(cred)) {
1258 return EINVAL;
1261 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1262 &smb_krb5_context);
1263 if (ret) {
1264 return ret;
1267 mem_ctx = talloc_new(cred);
1268 if (!mem_ctx) {
1269 return ENOMEM;
1272 salt_principal = cli_credentials_get_salt_principal(cred, mem_ctx);
1273 if (salt_principal == NULL) {
1274 talloc_free(mem_ctx);
1275 return ENOMEM;
1278 ret = smb_krb5_create_memory_keytab(mem_ctx,
1279 smb_krb5_context->krb5_context,
1280 cli_credentials_get_password(cred),
1281 username,
1282 realm,
1283 salt_principal,
1284 cli_credentials_get_kvno(cred),
1285 &keytab,
1286 &keytab_name);
1287 if (ret) {
1288 talloc_free(mem_ctx);
1289 return ret;
1292 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1293 keytab, keytab_name, &ktc);
1294 if (ret) {
1295 talloc_free(mem_ctx);
1296 return ret;
1299 cred->keytab_obtained = (MAX(cred->principal_obtained,
1300 cred->username_obtained));
1302 /* We make this keytab up based on a password. Therefore
1303 * match-by-key is acceptable, we can't match on the wrong
1304 * principal */
1305 ktc->password_based = true;
1307 talloc_steal(cred, ktc);
1308 cred->keytab = ktc;
1309 *_ktc = cred->keytab;
1310 talloc_free(mem_ctx);
1311 return ret;
1314 /* Given the name of a keytab (presumably in the format
1315 * FILE:/etc/krb5.keytab), open it and attach it */
1317 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
1318 struct loadparm_context *lp_ctx,
1319 const char *keytab_name,
1320 enum credentials_obtained obtained)
1322 krb5_error_code ret;
1323 struct keytab_container *ktc;
1324 struct smb_krb5_context *smb_krb5_context;
1325 TALLOC_CTX *mem_ctx;
1327 if (cred->keytab_obtained >= obtained) {
1328 return 0;
1331 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1332 if (ret) {
1333 return ret;
1336 mem_ctx = talloc_new(cred);
1337 if (!mem_ctx) {
1338 return ENOMEM;
1341 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1342 NULL, keytab_name, &ktc);
1343 if (ret) {
1344 return ret;
1347 cred->keytab_obtained = obtained;
1349 talloc_steal(cred, ktc);
1350 cred->keytab = ktc;
1351 talloc_free(mem_ctx);
1353 return ret;
1356 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1358 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
1359 struct loadparm_context *lp_ctx,
1360 struct gssapi_creds_container **_gcc)
1362 int ret = 0;
1363 OM_uint32 maj_stat, min_stat;
1364 struct gssapi_creds_container *gcc;
1365 struct keytab_container *ktc;
1366 struct smb_krb5_context *smb_krb5_context;
1367 TALLOC_CTX *mem_ctx;
1368 krb5_principal princ;
1369 const char *error_string;
1370 enum credentials_obtained obtained;
1372 mem_ctx = talloc_new(cred);
1373 if (!mem_ctx) {
1374 return ENOMEM;
1377 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1378 if (ret) {
1379 return ret;
1382 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1383 if (ret) {
1384 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1385 error_string));
1386 talloc_free(mem_ctx);
1387 return ret;
1390 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1391 talloc_free(mem_ctx);
1392 *_gcc = cred->server_gss_creds;
1393 return 0;
1396 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1397 if (ret) {
1398 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1399 return ret;
1402 gcc = talloc(cred, struct gssapi_creds_container);
1403 if (!gcc) {
1404 talloc_free(mem_ctx);
1405 return ENOMEM;
1408 if (ktc->password_based || obtained < CRED_SPECIFIED) {
1410 * This creates a GSSAPI cred_id_t for match-by-key with only
1411 * the keytab set
1413 princ = NULL;
1415 maj_stat = smb_gss_krb5_import_cred(&min_stat,
1416 smb_krb5_context->krb5_context,
1417 NULL, princ,
1418 ktc->keytab,
1419 &gcc->creds);
1420 if (maj_stat) {
1421 if (min_stat) {
1422 ret = min_stat;
1423 } else {
1424 ret = EINVAL;
1427 if (ret == 0) {
1428 cred->server_gss_creds_obtained = cred->keytab_obtained;
1429 talloc_set_destructor(gcc, free_gssapi_creds);
1430 cred->server_gss_creds = gcc;
1431 *_gcc = gcc;
1433 talloc_free(mem_ctx);
1434 return ret;
1438 * Set Kerberos KVNO
1441 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1442 int kvno)
1444 cred->kvno = kvno;
1448 * Return Kerberos KVNO
1451 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1453 return cred->kvno;
1457 char *cli_credentials_get_salt_principal(struct cli_credentials *cred, TALLOC_CTX *mem_ctx)
1459 TALLOC_CTX *frame = NULL;
1460 const char *realm = NULL;
1461 const char *username = NULL;
1462 uint32_t uac_flags = 0;
1463 char *salt_principal = NULL;
1464 const char *upn = NULL;
1465 int ret;
1467 /* If specified, use the specified value */
1468 if (cred->salt_principal != NULL) {
1469 return talloc_strdup(mem_ctx, cred->salt_principal);
1472 frame = talloc_stackframe();
1474 switch (cred->secure_channel_type) {
1475 case SEC_CHAN_WKSTA:
1476 case SEC_CHAN_RODC:
1477 uac_flags = UF_WORKSTATION_TRUST_ACCOUNT;
1478 break;
1479 case SEC_CHAN_BDC:
1480 uac_flags = UF_SERVER_TRUST_ACCOUNT;
1481 break;
1482 case SEC_CHAN_DOMAIN:
1483 case SEC_CHAN_DNS_DOMAIN:
1484 uac_flags = UF_INTERDOMAIN_TRUST_ACCOUNT;
1485 break;
1486 default:
1487 upn = cli_credentials_get_principal(cred, frame);
1488 if (upn == NULL) {
1489 TALLOC_FREE(frame);
1490 return NULL;
1492 uac_flags = UF_NORMAL_ACCOUNT;
1493 break;
1496 realm = cli_credentials_get_realm(cred);
1497 username = cli_credentials_get_username(cred);
1499 ret = smb_krb5_salt_principal_str(realm,
1500 username, /* sAMAccountName */
1501 upn, /* userPrincipalName */
1502 uac_flags,
1503 mem_ctx,
1504 &salt_principal);
1505 if (ret) {
1506 TALLOC_FREE(frame);
1507 return NULL;
1510 TALLOC_FREE(frame);
1511 return salt_principal;
1514 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
1516 talloc_free(cred->salt_principal);
1517 cred->salt_principal = talloc_strdup(cred, principal);
1520 /* The 'impersonate_principal' is used to allow one Kerberos principal
1521 * (and it's associated keytab etc) to impersonate another. The
1522 * ability to do this is controlled by the KDC, but it is generally
1523 * permitted to impersonate anyone to yourself. This allows any
1524 * member of the domain to get the groups of a user. This is also
1525 * known as S4U2Self */
1527 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1529 return cred->impersonate_principal;
1533 * The 'self_service' is the service principal that
1534 * represents the same object (by its objectSid)
1535 * as the client principal (typically our machine account).
1536 * When trying to impersonate 'impersonate_principal' with
1537 * S4U2Self.
1539 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1541 return cred->self_service;
1544 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1545 const char *principal,
1546 const char *self_service)
1548 talloc_free(cred->impersonate_principal);
1549 cred->impersonate_principal = talloc_strdup(cred, principal);
1550 talloc_free(cred->self_service);
1551 cred->self_service = talloc_strdup(cred, self_service);
1552 cli_credentials_set_kerberos_state(cred,
1553 CRED_USE_KERBEROS_REQUIRED,
1554 CRED_SPECIFIED);
1558 * when impersonating for S4U2proxy we need to set the target principal.
1559 * Similarly, we may only be authorized to do general impersonation to
1560 * some particular services.
1562 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1564 * NULL means that tickets will be obtained for the krbtgt service.
1567 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1569 return cred->target_service;
1572 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1574 talloc_free(cred->target_service);
1575 cred->target_service = talloc_strdup(cred, target_service);
1578 _PUBLIC_ int cli_credentials_get_kerberos_key(struct cli_credentials *cred,
1579 TALLOC_CTX *mem_ctx,
1580 struct loadparm_context *lp_ctx,
1581 krb5_enctype enctype,
1582 bool previous,
1583 DATA_BLOB *key_blob)
1585 struct smb_krb5_context *smb_krb5_context = NULL;
1586 krb5_error_code krb5_ret;
1587 int ret;
1588 const char *password = NULL;
1589 const char *salt = NULL;
1590 krb5_data cleartext_data;
1591 krb5_data salt_data = {
1592 .length = 0,
1594 krb5_keyblock key;
1596 TALLOC_CTX *frame = talloc_stackframe();
1598 if ((int)enctype == (int)ENCTYPE_ARCFOUR_HMAC) {
1599 struct samr_Password *nt_hash;
1601 if (previous) {
1602 nt_hash = cli_credentials_get_old_nt_hash(cred, frame);
1603 } else {
1604 nt_hash = cli_credentials_get_nt_hash(cred, frame);
1607 if (nt_hash == NULL) {
1608 TALLOC_FREE(frame);
1609 return EINVAL;
1611 *key_blob = data_blob_talloc(mem_ctx,
1612 nt_hash->hash,
1613 sizeof(nt_hash->hash));
1614 if (key_blob->data == NULL) {
1615 TALLOC_FREE(frame);
1616 return ENOMEM;
1618 TALLOC_FREE(frame);
1619 return 0;
1622 if (cred->password_will_be_nt_hash) {
1623 DEBUG(1,("cli_credentials_get_kerberos_key: cannot generate Kerberos key using NT hash\n"));
1624 TALLOC_FREE(frame);
1625 return EINVAL;
1628 salt = cli_credentials_get_salt_principal(cred, frame);
1629 if (salt == NULL) {
1630 TALLOC_FREE(frame);
1631 return EINVAL;
1634 if (previous) {
1635 password = cli_credentials_get_old_password(cred);
1636 } else {
1637 password = cli_credentials_get_password(cred);
1639 if (password == NULL) {
1640 TALLOC_FREE(frame);
1641 return EINVAL;
1644 cleartext_data.data = discard_const_p(char, password);
1645 cleartext_data.length = strlen(password);
1647 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1648 &smb_krb5_context);
1649 if (ret != 0) {
1650 TALLOC_FREE(frame);
1651 return ret;
1654 salt_data.data = discard_const_p(char, salt);
1655 salt_data.length = strlen(salt);
1658 * create Kerberos key out of
1659 * the salt and the cleartext password
1661 krb5_ret = smb_krb5_create_key_from_string(smb_krb5_context->krb5_context,
1662 NULL,
1663 &salt_data,
1664 &cleartext_data,
1665 enctype,
1666 &key);
1667 if (krb5_ret != 0) {
1668 DEBUG(1,("cli_credentials_get_aes256_key: "
1669 "generation of a aes256-cts-hmac-sha1-96 key failed: %s\n",
1670 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
1671 krb5_ret, mem_ctx)));
1672 TALLOC_FREE(frame);
1673 return EINVAL;
1675 *key_blob = data_blob_talloc(mem_ctx,
1676 KRB5_KEY_DATA(&key),
1677 KRB5_KEY_LENGTH(&key));
1678 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &key);
1679 if (key_blob->data == NULL) {
1680 TALLOC_FREE(frame);
1681 return ENOMEM;
1683 talloc_keep_secret(key_blob->data);
1685 TALLOC_FREE(frame);
1686 return 0;
1689 /* This take a reference to the armor credentials to ensure the lifetime is appropriate */
1691 NTSTATUS cli_credentials_set_krb5_fast_armor_credentials(struct cli_credentials *creds,
1692 struct cli_credentials *armor_creds,
1693 bool require_fast_armor)
1695 talloc_unlink(creds, creds->krb5_fast_armor_credentials);
1696 if (armor_creds == NULL) {
1697 creds->krb5_fast_armor_credentials = NULL;
1698 return NT_STATUS_OK;
1701 creds->krb5_fast_armor_credentials = talloc_reference(creds, armor_creds);
1702 if (creds->krb5_fast_armor_credentials == NULL) {
1703 return NT_STATUS_NO_MEMORY;
1706 creds->krb5_require_fast_armor = require_fast_armor;
1708 return NT_STATUS_OK;
1711 struct cli_credentials *cli_credentials_get_krb5_fast_armor_credentials(struct cli_credentials *creds)
1713 return creds->krb5_fast_armor_credentials;
1716 bool cli_credentials_get_krb5_require_fast_armor(struct cli_credentials *creds)
1718 return creds->krb5_require_fast_armor;