auth/credentials: Add API to allow requesting a Kerberos ticket to be protected with...
[Samba.git] / auth / credentials / credentials_krb5.c
blob85ea97521d4b99164ae485c129db921f9c85887c
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 char *dummy_name;
83 uint32_t maj_stat = GSS_S_FAILURE;
85 dummy_name = talloc_asprintf(ccc,
86 "MEMORY:gss_krb5_copy_ccache-%p",
87 &ccc->ccache);
88 if (dummy_name == NULL) {
89 *min_stat = ENOMEM;
90 return GSS_S_FAILURE;
94 * Create a dummy ccache, so we can iterate over the credentials
95 * and find the default principal for the ccache we want to
96 * copy. The new ccache needs to be initialized with this
97 * principal.
99 code = krb5_cc_resolve(context, dummy_name, &dummy_ccache);
100 TALLOC_FREE(dummy_name);
101 if (code != 0) {
102 *min_stat = code;
103 return GSS_S_FAILURE;
107 * We do not need set a default principal on the temporary dummy
108 * ccache, as we do consume it at all in this function.
110 maj_stat = gss_krb5_copy_ccache(min_stat, cred, dummy_ccache);
111 if (maj_stat != 0) {
112 krb5_cc_close(context, dummy_ccache);
113 return maj_stat;
116 code = krb5_cc_start_seq_get(context, dummy_ccache, &cursor);
117 if (code != 0) {
118 krb5_cc_close(context, dummy_ccache);
119 *min_stat = EINVAL;
120 return GSS_S_FAILURE;
123 code = krb5_cc_next_cred(context,
124 dummy_ccache,
125 &cursor,
126 &creds);
127 if (code != 0) {
128 krb5_cc_close(context, dummy_ccache);
129 *min_stat = EINVAL;
130 return GSS_S_FAILURE;
133 do {
134 if (creds.ticket_flags & TKT_FLG_PRE_AUTH) {
135 krb5_data *tgs;
137 tgs = krb5_princ_component(context,
138 creds.server,
140 if (tgs != NULL && tgs->length >= 1) {
141 int cmp;
143 cmp = memcmp(tgs->data,
144 KRB5_TGS_NAME,
145 tgs->length);
146 if (cmp == 0 && creds.client != NULL) {
147 princ = creds.client;
148 code = KRB5_CC_END;
149 break;
154 krb5_free_cred_contents(context, &creds);
156 code = krb5_cc_next_cred(context,
157 dummy_ccache,
158 &cursor,
159 &creds);
160 } while (code == 0);
162 if (code == KRB5_CC_END) {
163 krb5_cc_end_seq_get(context, dummy_ccache, &cursor);
164 code = 0;
166 krb5_cc_close(context, dummy_ccache);
168 if (code != 0 || princ == NULL) {
169 krb5_free_cred_contents(context, &creds);
170 *min_stat = EINVAL;
171 return GSS_S_FAILURE;
175 * Set the default principal for the cache we copy
176 * into. This is needed to be able that other calls
177 * can read it with e.g. gss_acquire_cred() or
178 * krb5_cc_get_principal().
180 code = krb5_cc_initialize(context, ccc->ccache, princ);
181 if (code != 0) {
182 krb5_free_cred_contents(context, &creds);
183 *min_stat = EINVAL;
184 return GSS_S_FAILURE;
186 krb5_free_cred_contents(context, &creds);
188 #endif /* SAMBA4_USES_HEIMDAL */
190 return gss_krb5_copy_ccache(min_stat,
191 cred,
192 ccc->ccache);
195 _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
196 struct loadparm_context *lp_ctx,
197 struct smb_krb5_context **smb_krb5_context)
199 int ret;
200 if (cred->smb_krb5_context) {
201 *smb_krb5_context = cred->smb_krb5_context;
202 return 0;
205 ret = smb_krb5_init_context(cred, lp_ctx,
206 &cred->smb_krb5_context);
207 if (ret) {
208 cred->smb_krb5_context = NULL;
209 return ret;
211 *smb_krb5_context = cred->smb_krb5_context;
212 return 0;
215 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
216 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
218 _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
219 struct smb_krb5_context *smb_krb5_context)
221 if (smb_krb5_context == NULL) {
222 talloc_unlink(cred, cred->smb_krb5_context);
223 cred->smb_krb5_context = NULL;
224 return NT_STATUS_OK;
227 if (!talloc_reference(cred, smb_krb5_context)) {
228 return NT_STATUS_NO_MEMORY;
230 cred->smb_krb5_context = smb_krb5_context;
231 return NT_STATUS_OK;
234 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
235 struct ccache_container *ccache,
236 enum credentials_obtained obtained,
237 const char **error_string)
239 bool ok;
240 char *realm;
241 krb5_principal princ;
242 krb5_error_code ret;
243 char *name;
245 if (cred->ccache_obtained > obtained) {
246 return 0;
249 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
250 ccache->ccache, &princ);
252 if (ret) {
253 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
254 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
255 ret, cred));
256 return ret;
259 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
260 if (ret) {
261 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
262 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
263 ret, cred));
264 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
265 return ret;
268 ok = cli_credentials_set_principal(cred, name, obtained);
269 krb5_free_unparsed_name(ccache->smb_krb5_context->krb5_context, name);
270 if (!ok) {
271 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
272 return ENOMEM;
275 realm = smb_krb5_principal_get_realm(
276 cred, ccache->smb_krb5_context->krb5_context, princ);
277 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
278 if (realm == NULL) {
279 return ENOMEM;
281 ok = cli_credentials_set_realm(cred, realm, obtained);
282 TALLOC_FREE(realm);
283 if (!ok) {
284 return ENOMEM;
287 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
288 cred->ccache_obtained = obtained;
290 return 0;
293 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
294 struct loadparm_context *lp_ctx,
295 const char *name,
296 enum credentials_obtained obtained,
297 const char **error_string)
299 krb5_error_code ret;
300 krb5_principal princ;
301 struct ccache_container *ccc;
302 if (cred->ccache_obtained > obtained) {
303 return 0;
306 ccc = talloc(cred, struct ccache_container);
307 if (!ccc) {
308 (*error_string) = error_message(ENOMEM);
309 return ENOMEM;
312 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
313 &ccc->smb_krb5_context);
314 if (ret) {
315 (*error_string) = error_message(ret);
316 talloc_free(ccc);
317 return ret;
319 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
320 talloc_free(ccc);
321 (*error_string) = error_message(ENOMEM);
322 return ENOMEM;
325 if (name) {
326 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
327 if (ret) {
328 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
329 name,
330 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
331 ret, ccc));
332 talloc_free(ccc);
333 return ret;
335 } else {
336 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
337 if (ret) {
338 (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
339 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
340 ret, ccc));
341 talloc_free(ccc);
342 return ret;
346 talloc_set_destructor(ccc, free_dccache);
348 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
350 if (ret == 0) {
351 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
352 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
354 if (ret) {
355 (*error_string) = error_message(ret);
356 TALLOC_FREE(ccc);
357 return ret;
361 cred->ccache = ccc;
362 cred->ccache_obtained = obtained;
364 cli_credentials_invalidate_client_gss_creds(
365 cred, cred->ccache_obtained);
367 return 0;
370 #ifndef SAMBA4_USES_HEIMDAL
372 * This function is a workaround for old MIT Kerberos versions which did not
373 * implement the krb5_cc_remove_cred function. It creates a temporary
374 * credentials cache to copy the credentials in the current cache
375 * except the one we want to remove and then overwrites the contents of the
376 * current cache with the temporary copy.
378 static krb5_error_code krb5_cc_remove_cred_wrap(struct ccache_container *ccc,
379 krb5_creds *creds)
381 krb5_ccache dummy_ccache = NULL;
382 krb5_creds cached_creds = {0};
383 krb5_cc_cursor cursor = NULL;
384 krb5_error_code code;
385 char *dummy_name;
387 dummy_name = talloc_asprintf(ccc,
388 "MEMORY:copy_ccache-%p",
389 &ccc->ccache);
390 if (dummy_name == NULL) {
391 return KRB5_CC_NOMEM;
394 code = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
395 dummy_name,
396 &dummy_ccache);
397 if (code != 0) {
398 DBG_ERR("krb5_cc_resolve failed: %s\n",
399 smb_get_krb5_error_message(
400 ccc->smb_krb5_context->krb5_context,
401 code, ccc));
402 TALLOC_FREE(dummy_name);
403 return code;
406 TALLOC_FREE(dummy_name);
408 code = krb5_cc_start_seq_get(ccc->smb_krb5_context->krb5_context,
409 ccc->ccache,
410 &cursor);
411 if (code != 0) {
412 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
413 dummy_ccache);
415 DBG_ERR("krb5_cc_start_seq_get failed: %s\n",
416 smb_get_krb5_error_message(
417 ccc->smb_krb5_context->krb5_context,
418 code, ccc));
419 return code;
422 while ((code = krb5_cc_next_cred(ccc->smb_krb5_context->krb5_context,
423 ccc->ccache,
424 &cursor,
425 &cached_creds)) == 0) {
426 /* If the principal matches skip it and do not copy to the
427 * temporary cache as this is the one we want to remove */
428 if (krb5_principal_compare_flags(
429 ccc->smb_krb5_context->krb5_context,
430 creds->server,
431 cached_creds.server,
432 0)) {
433 continue;
436 code = krb5_cc_store_cred(
437 ccc->smb_krb5_context->krb5_context,
438 dummy_ccache,
439 &cached_creds);
440 if (code != 0) {
441 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
442 dummy_ccache);
443 DBG_ERR("krb5_cc_store_cred failed: %s\n",
444 smb_get_krb5_error_message(
445 ccc->smb_krb5_context->krb5_context,
446 code, ccc));
447 return code;
451 if (code == KRB5_CC_END) {
452 krb5_cc_end_seq_get(ccc->smb_krb5_context->krb5_context,
453 dummy_ccache,
454 &cursor);
455 code = 0;
458 if (code != 0) {
459 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
460 dummy_ccache);
461 DBG_ERR("krb5_cc_next_cred failed: %s\n",
462 smb_get_krb5_error_message(
463 ccc->smb_krb5_context->krb5_context,
464 code, ccc));
465 return code;
468 code = krb5_cc_initialize(ccc->smb_krb5_context->krb5_context,
469 ccc->ccache,
470 creds->client);
471 if (code != 0) {
472 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
473 dummy_ccache);
474 DBG_ERR("krb5_cc_initialize failed: %s\n",
475 smb_get_krb5_error_message(
476 ccc->smb_krb5_context->krb5_context,
477 code, ccc));
478 return code;
481 code = krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
482 dummy_ccache,
483 ccc->ccache);
484 if (code != 0) {
485 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
486 dummy_ccache);
487 DBG_ERR("krb5_cc_copy_creds failed: %s\n",
488 smb_get_krb5_error_message(
489 ccc->smb_krb5_context->krb5_context,
490 code, ccc));
491 return code;
494 code = krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
495 dummy_ccache);
496 if (code != 0) {
497 DBG_ERR("krb5_cc_destroy failed: %s\n",
498 smb_get_krb5_error_message(
499 ccc->smb_krb5_context->krb5_context,
500 code, ccc));
501 return code;
504 return code;
506 #endif
509 * Indicate that we failed to log in to this service/host with these
510 * credentials. The caller passes an unsigned int which they
511 * initialise to the number of times they would like to retry.
513 * This method is used to support re-trying with freshly fetched
514 * credentials in case a server is rebuilt while clients have
515 * non-expired tickets. When the client code gets a logon failure they
516 * throw away the existing credentials for the server and retry.
518 _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
519 const char *principal,
520 unsigned int *count)
522 struct ccache_container *ccc;
523 krb5_creds creds, creds2;
524 int ret;
526 if (principal == NULL) {
527 /* no way to delete if we don't know the principal */
528 return false;
531 ccc = cred->ccache;
532 if (ccc == NULL) {
533 /* not a kerberos connection */
534 return false;
537 if (*count > 0) {
538 /* We have already tried discarding the credentials */
539 return false;
541 (*count)++;
543 ZERO_STRUCT(creds);
544 ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
545 if (ret != 0) {
546 return false;
549 /* MIT kerberos requires creds.client to match against cached
550 * credentials */
551 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context,
552 ccc->ccache,
553 &creds.client);
554 if (ret != 0) {
555 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context,
556 &creds);
557 DBG_ERR("krb5_cc_get_principal failed: %s\n",
558 smb_get_krb5_error_message(
559 ccc->smb_krb5_context->krb5_context,
560 ret, ccc));
561 return false;
564 ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
565 if (ret != 0) {
566 /* don't retry - we didn't find these credentials to remove */
567 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
568 return false;
571 ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
572 #ifndef SAMBA4_USES_HEIMDAL
573 if (ret == KRB5_CC_NOSUPP) {
574 /* Old MIT kerberos versions did not implement
575 * krb5_cc_remove_cred */
576 ret = krb5_cc_remove_cred_wrap(ccc, &creds);
578 #endif
579 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
580 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
581 if (ret != 0) {
582 /* don't retry - we didn't find these credentials to
583 * remove. Note that with the current backend this
584 * never happens, as it always returns 0 even if the
585 * creds don't exist, which is why we do a separate
586 * krb5_cc_retrieve_cred() above.
588 DBG_ERR("krb5_cc_remove_cred failed: %s\n",
589 smb_get_krb5_error_message(
590 ccc->smb_krb5_context->krb5_context,
591 ret, ccc));
592 return false;
594 return true;
598 static int cli_credentials_new_ccache(struct cli_credentials *cred,
599 struct loadparm_context *lp_ctx,
600 char *ccache_name,
601 struct ccache_container **_ccc,
602 const char **error_string)
604 bool must_free_cc_name = false;
605 krb5_error_code ret;
606 struct ccache_container *ccc = talloc(cred, struct ccache_container);
607 if (!ccc) {
608 return ENOMEM;
611 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
612 &ccc->smb_krb5_context);
613 if (ret) {
614 talloc_free(ccc);
615 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
616 error_message(ret));
617 return ret;
619 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
620 talloc_free(ccc);
621 (*error_string) = strerror(ENOMEM);
622 return ENOMEM;
625 if (!ccache_name) {
626 must_free_cc_name = true;
628 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
629 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
630 (unsigned int)getpid(), ccc);
631 } else {
632 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
633 ccc);
636 if (!ccache_name) {
637 talloc_free(ccc);
638 (*error_string) = strerror(ENOMEM);
639 return ENOMEM;
643 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
644 &ccc->ccache);
645 if (ret) {
646 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
647 ccache_name,
648 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
649 ret, ccc));
650 talloc_free(ccache_name);
651 talloc_free(ccc);
652 return ret;
655 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
656 talloc_set_destructor(ccc, free_mccache);
657 } else {
658 talloc_set_destructor(ccc, free_dccache);
661 if (must_free_cc_name) {
662 talloc_free(ccache_name);
665 *_ccc = ccc;
667 return 0;
670 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
671 struct tevent_context *event_ctx,
672 struct loadparm_context *lp_ctx,
673 char *ccache_name,
674 struct ccache_container **ccc,
675 const char **error_string)
677 krb5_error_code ret;
678 enum credentials_obtained obtained;
680 if (cred->machine_account_pending) {
681 cli_credentials_set_machine_account(cred, lp_ctx);
684 if (cred->ccache_obtained >= cred->ccache_threshold &&
685 cred->ccache_obtained > CRED_UNINITIALISED) {
686 time_t lifetime;
687 bool expired = false;
688 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
689 cred->ccache->ccache, &lifetime);
690 if (ret == KRB5_CC_END || ret == ENOENT) {
691 /* If we have a particular ccache set, without
692 * an initial ticket, then assume there is a
693 * good reason */
694 } else if (ret == 0) {
695 if (lifetime == 0) {
696 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
697 cli_credentials_get_principal(cred, cred)));
698 expired = true;
699 } else if (lifetime < 300) {
700 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
701 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
702 expired = true;
704 } else {
705 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
706 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
707 ret, cred));
708 return ret;
711 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
712 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
714 if (!expired) {
715 *ccc = cred->ccache;
716 return 0;
719 if (cli_credentials_is_anonymous(cred)) {
720 (*error_string) = "Cannot get anonymous kerberos credentials";
721 return EINVAL;
724 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
725 if (ret) {
726 return ret;
729 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
730 if (ret) {
731 return ret;
734 ret = cli_credentials_set_from_ccache(cred, *ccc,
735 obtained, error_string);
737 cred->ccache = *ccc;
738 cred->ccache_obtained = cred->principal_obtained;
739 if (ret) {
740 return ret;
742 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
743 return 0;
746 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
747 struct tevent_context *event_ctx,
748 struct loadparm_context *lp_ctx,
749 struct ccache_container **ccc,
750 const char **error_string)
752 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
755 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
756 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
758 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
759 talloc_unlink(cred, cred->client_gss_creds);
760 cred->client_gss_creds = NULL;
762 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
765 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
766 enum credentials_obtained obtained)
768 /* If the caller just changed the username/password etc, then
769 * any cached credentials are now invalid */
770 if (obtained >= cred->client_gss_creds_obtained) {
771 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
772 talloc_unlink(cred, cred->client_gss_creds);
773 cred->client_gss_creds = NULL;
775 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
777 /* Now that we know that the data is 'this specified', then
778 * don't allow something less 'known' to be returned as a
779 * ccache. Ie, if the username is on the command line, we
780 * don't want to later guess to use a file-based ccache */
781 if (obtained > cred->client_gss_creds_threshold) {
782 cred->client_gss_creds_threshold = obtained;
786 /* We have good reason to think this CCACHE is invalid. Blow it away */
787 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
789 if (cred->ccache_obtained > CRED_UNINITIALISED) {
790 talloc_unlink(cred, cred->ccache);
791 cred->ccache = NULL;
793 cred->ccache_obtained = CRED_UNINITIALISED;
795 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
798 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
799 enum credentials_obtained obtained)
801 /* If the caller just changed the username/password etc, then
802 * any cached credentials are now invalid */
803 if (obtained >= cred->ccache_obtained) {
804 if (cred->ccache_obtained > CRED_UNINITIALISED) {
805 talloc_unlink(cred, cred->ccache);
806 cred->ccache = NULL;
808 cred->ccache_obtained = CRED_UNINITIALISED;
810 /* Now that we know that the data is 'this specified', then
811 * don't allow something less 'known' to be returned as a
812 * ccache. i.e, if the username is on the command line, we
813 * don't want to later guess to use a file-based ccache */
814 if (obtained > cred->ccache_threshold) {
815 cred->ccache_threshold = obtained;
818 cli_credentials_invalidate_client_gss_creds(cred,
819 obtained);
822 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
824 OM_uint32 min_stat;
825 (void)gss_release_cred(&min_stat, &gcc->creds);
826 return 0;
829 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
830 struct tevent_context *event_ctx,
831 struct loadparm_context *lp_ctx,
832 struct gssapi_creds_container **_gcc,
833 const char **error_string)
835 int ret = 0;
836 OM_uint32 maj_stat, min_stat;
837 struct gssapi_creds_container *gcc;
838 struct ccache_container *ccache;
839 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
840 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
841 gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
842 #endif
843 krb5_enctype *etypes = NULL;
845 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
846 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
847 bool expired = false;
848 OM_uint32 lifetime = 0;
849 gss_cred_usage_t usage = 0;
850 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
851 NULL, &lifetime, &usage, NULL);
852 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
853 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
854 expired = true;
855 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
856 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
857 expired = true;
858 } else if (maj_stat != GSS_S_COMPLETE) {
859 *error_string = talloc_asprintf(cred, "inquiry of credential lifetime via GSSAPI gss_inquire_cred failed: %s\n",
860 gssapi_error_string(cred, maj_stat, min_stat, NULL));
861 return EINVAL;
863 if (expired) {
864 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
865 } else {
866 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
867 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
869 *_gcc = cred->client_gss_creds;
870 return 0;
874 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
875 &ccache, error_string);
876 if (ret) {
877 if (cli_credentials_get_kerberos_state(cred) == CRED_USE_KERBEROS_REQUIRED) {
878 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
879 } else {
880 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
882 return ret;
885 gcc = talloc(cred, struct gssapi_creds_container);
886 if (!gcc) {
887 (*error_string) = error_message(ENOMEM);
888 return ENOMEM;
891 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
892 ccache->ccache, NULL, NULL,
893 &gcc->creds);
894 if ((maj_stat == GSS_S_FAILURE) &&
895 (min_stat == (OM_uint32)KRB5_CC_END ||
896 min_stat == (OM_uint32)KRB5_CC_NOTFOUND ||
897 min_stat == (OM_uint32)KRB5_FCC_NOFILE))
899 /* This CCACHE is no good. Ensure we don't use it again */
900 cli_credentials_unconditionally_invalidate_ccache(cred);
902 /* Now try again to get a ccache */
903 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
904 &ccache, error_string);
905 if (ret) {
906 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
907 return ret;
910 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
911 ccache->ccache, NULL, NULL,
912 &gcc->creds);
916 if (maj_stat) {
917 talloc_free(gcc);
918 if (min_stat) {
919 ret = min_stat;
920 } else {
921 ret = EINVAL;
923 (*error_string) = talloc_asprintf(cred, "smb_gss_krb5_import_cred failed: %s", error_message(ret));
924 return ret;
929 * transfer the enctypes from the smb_krb5_context to the gssapi layer
931 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
932 * to configure the enctypes via the krb5.conf.
934 * And the gss_init_sec_context() creates it's own krb5_context and
935 * the TGS-REQ had all enctypes in it and only the ones configured
936 * and used for the AS-REQ, so it wasn't possible to disable the usage
937 * of AES keys.
939 min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
940 &etypes);
941 if (min_stat == 0) {
942 OM_uint32 num_ktypes;
944 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
946 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
947 num_ktypes,
948 (int32_t *) etypes);
949 krb5_free_enctypes(ccache->smb_krb5_context->krb5_context,
950 etypes);
951 if (maj_stat) {
952 talloc_free(gcc);
953 if (min_stat) {
954 ret = min_stat;
955 } else {
956 ret = EINVAL;
958 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
959 return ret;
963 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
965 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
967 * This allows us to disable SIGN and SEAL on a TLS connection with
968 * GSS-SPNENO. For example ldaps:// connections.
970 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
971 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
973 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
974 oid,
975 &empty_buffer);
976 if (maj_stat) {
977 talloc_free(gcc);
978 if (min_stat) {
979 ret = min_stat;
980 } else {
981 ret = EINVAL;
983 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
984 return ret;
986 #endif
987 cred->client_gss_creds_obtained = cred->ccache_obtained;
988 talloc_set_destructor(gcc, free_gssapi_creds);
989 cred->client_gss_creds = gcc;
990 *_gcc = gcc;
991 return 0;
995 Set a gssapi cred_id_t into the credentials system. (Client case)
997 This grabs the credentials both 'intact' and getting the krb5
998 ccache out of it. This routine can be generalised in future for
999 the case where we deal with GSSAPI mechs other than krb5.
1001 On success, the caller must not free gssapi_cred, as it now belongs
1002 to the credentials system.
1005 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
1006 struct loadparm_context *lp_ctx,
1007 gss_cred_id_t gssapi_cred,
1008 enum credentials_obtained obtained,
1009 const char **error_string)
1011 int ret;
1012 OM_uint32 maj_stat, min_stat;
1013 struct ccache_container *ccc = NULL;
1014 struct gssapi_creds_container *gcc = NULL;
1015 if (cred->client_gss_creds_obtained > obtained) {
1016 return 0;
1019 gcc = talloc(cred, struct gssapi_creds_container);
1020 if (!gcc) {
1021 (*error_string) = error_message(ENOMEM);
1022 return ENOMEM;
1025 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
1026 if (ret != 0) {
1027 return ret;
1030 maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
1031 gssapi_cred,
1032 ccc);
1033 if (maj_stat) {
1034 if (min_stat) {
1035 ret = min_stat;
1036 } else {
1037 ret = EINVAL;
1039 if (ret) {
1040 (*error_string) = error_message(ENOMEM);
1044 if (ret == 0) {
1045 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
1047 cred->ccache = ccc;
1048 cred->ccache_obtained = obtained;
1049 if (ret == 0) {
1050 gcc->creds = gssapi_cred;
1051 talloc_set_destructor(gcc, free_gssapi_creds);
1053 /* set the client_gss_creds_obtained here, as it just
1054 got set to UNINITIALISED by the calls above */
1055 cred->client_gss_creds_obtained = obtained;
1056 cred->client_gss_creds = gcc;
1058 return ret;
1061 static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
1063 krb5_error_code ret;
1064 const struct ccache_container *old_ccc = NULL;
1065 enum credentials_obtained old_obtained;
1066 struct ccache_container *ccc = NULL;
1067 char *ccache_name = NULL;
1068 krb5_principal princ;
1070 old_obtained = cred->ccache_obtained;
1071 old_ccc = cred->ccache;
1072 if (old_ccc == NULL) {
1073 return 0;
1076 cred->ccache = NULL;
1077 cred->ccache_obtained = CRED_UNINITIALISED;
1078 cred->client_gss_creds = NULL;
1079 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
1081 ret = krb5_cc_get_principal(
1082 old_ccc->smb_krb5_context->krb5_context,
1083 old_ccc->ccache,
1084 &princ);
1085 if (ret != 0) {
1087 * This is an empty ccache. No point in copying anything.
1089 return 0;
1091 krb5_free_principal(old_ccc->smb_krb5_context->krb5_context, princ);
1093 ccc = talloc(cred, struct ccache_container);
1094 if (ccc == NULL) {
1095 return ENOMEM;
1097 *ccc = *old_ccc;
1098 ccc->ccache = NULL;
1100 ccache_name = talloc_asprintf(ccc, "MEMORY:%p", ccc);
1102 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
1103 ccache_name, &ccc->ccache);
1104 if (ret != 0) {
1105 TALLOC_FREE(ccc);
1106 return ret;
1109 talloc_set_destructor(ccc, free_mccache);
1111 TALLOC_FREE(ccache_name);
1113 ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
1114 old_ccc->ccache, ccc->ccache);
1115 if (ret != 0) {
1116 TALLOC_FREE(ccc);
1117 return ret;
1120 cred->ccache = ccc;
1121 cred->ccache_obtained = old_obtained;
1122 return ret;
1125 _PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
1126 struct cli_credentials *src)
1128 struct cli_credentials *dst, *armor_credentials;
1129 int ret;
1131 dst = talloc(mem_ctx, struct cli_credentials);
1132 if (dst == NULL) {
1133 return NULL;
1136 *dst = *src;
1138 if (dst->krb5_fast_armor_credentials != NULL) {
1139 armor_credentials = talloc_reference(dst, dst->krb5_fast_armor_credentials);
1140 if (armor_credentials == NULL) {
1141 TALLOC_FREE(dst);
1142 return NULL;
1146 ret = cli_credentials_shallow_ccache(dst);
1147 if (ret != 0) {
1148 TALLOC_FREE(dst);
1149 return NULL;
1152 return dst;
1155 /* Get the keytab (actually, a container containing the krb5_keytab)
1156 * attached to this context. If this hasn't been done or set before,
1157 * it will be generated from the password.
1159 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
1160 struct loadparm_context *lp_ctx,
1161 struct keytab_container **_ktc)
1163 krb5_error_code ret;
1164 struct keytab_container *ktc;
1165 struct smb_krb5_context *smb_krb5_context;
1166 const char *keytab_name;
1167 krb5_keytab keytab;
1168 TALLOC_CTX *mem_ctx;
1169 const char *username = cli_credentials_get_username(cred);
1170 const char *upn = NULL;
1171 const char *realm = cli_credentials_get_realm(cred);
1172 char *salt_principal = NULL;
1173 uint32_t uac_flags = 0;
1175 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
1176 cred->username_obtained))) {
1177 *_ktc = cred->keytab;
1178 return 0;
1181 if (cli_credentials_is_anonymous(cred)) {
1182 return EINVAL;
1185 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1186 &smb_krb5_context);
1187 if (ret) {
1188 return ret;
1191 mem_ctx = talloc_new(cred);
1192 if (!mem_ctx) {
1193 return ENOMEM;
1196 switch (cred->secure_channel_type) {
1197 case SEC_CHAN_WKSTA:
1198 case SEC_CHAN_RODC:
1199 uac_flags = UF_WORKSTATION_TRUST_ACCOUNT;
1200 break;
1201 case SEC_CHAN_BDC:
1202 uac_flags = UF_SERVER_TRUST_ACCOUNT;
1203 break;
1204 case SEC_CHAN_DOMAIN:
1205 case SEC_CHAN_DNS_DOMAIN:
1206 uac_flags = UF_INTERDOMAIN_TRUST_ACCOUNT;
1207 break;
1208 default:
1209 upn = cli_credentials_get_principal(cred, mem_ctx);
1210 if (upn == NULL) {
1211 TALLOC_FREE(mem_ctx);
1212 return ENOMEM;
1214 uac_flags = UF_NORMAL_ACCOUNT;
1215 break;
1218 ret = smb_krb5_salt_principal_str(realm,
1219 username, /* sAMAccountName */
1220 upn, /* userPrincipalName */
1221 uac_flags,
1222 mem_ctx,
1223 &salt_principal);
1224 if (ret) {
1225 talloc_free(mem_ctx);
1226 return ret;
1229 ret = smb_krb5_create_memory_keytab(mem_ctx,
1230 smb_krb5_context->krb5_context,
1231 cli_credentials_get_password(cred),
1232 username,
1233 realm,
1234 salt_principal,
1235 cli_credentials_get_kvno(cred),
1236 &keytab,
1237 &keytab_name);
1238 if (ret) {
1239 talloc_free(mem_ctx);
1240 return ret;
1243 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1244 keytab, keytab_name, &ktc);
1245 if (ret) {
1246 talloc_free(mem_ctx);
1247 return ret;
1250 cred->keytab_obtained = (MAX(cred->principal_obtained,
1251 cred->username_obtained));
1253 /* We make this keytab up based on a password. Therefore
1254 * match-by-key is acceptable, we can't match on the wrong
1255 * principal */
1256 ktc->password_based = true;
1258 talloc_steal(cred, ktc);
1259 cred->keytab = ktc;
1260 *_ktc = cred->keytab;
1261 talloc_free(mem_ctx);
1262 return ret;
1265 /* Given the name of a keytab (presumably in the format
1266 * FILE:/etc/krb5.keytab), open it and attach it */
1268 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
1269 struct loadparm_context *lp_ctx,
1270 const char *keytab_name,
1271 enum credentials_obtained obtained)
1273 krb5_error_code ret;
1274 struct keytab_container *ktc;
1275 struct smb_krb5_context *smb_krb5_context;
1276 TALLOC_CTX *mem_ctx;
1278 if (cred->keytab_obtained >= obtained) {
1279 return 0;
1282 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1283 if (ret) {
1284 return ret;
1287 mem_ctx = talloc_new(cred);
1288 if (!mem_ctx) {
1289 return ENOMEM;
1292 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1293 NULL, keytab_name, &ktc);
1294 if (ret) {
1295 return ret;
1298 cred->keytab_obtained = obtained;
1300 talloc_steal(cred, ktc);
1301 cred->keytab = ktc;
1302 talloc_free(mem_ctx);
1304 return ret;
1307 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1309 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
1310 struct loadparm_context *lp_ctx,
1311 struct gssapi_creds_container **_gcc)
1313 int ret = 0;
1314 OM_uint32 maj_stat, min_stat;
1315 struct gssapi_creds_container *gcc;
1316 struct keytab_container *ktc;
1317 struct smb_krb5_context *smb_krb5_context;
1318 TALLOC_CTX *mem_ctx;
1319 krb5_principal princ;
1320 const char *error_string;
1321 enum credentials_obtained obtained;
1323 mem_ctx = talloc_new(cred);
1324 if (!mem_ctx) {
1325 return ENOMEM;
1328 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1329 if (ret) {
1330 return ret;
1333 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1334 if (ret) {
1335 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1336 error_string));
1337 talloc_free(mem_ctx);
1338 return ret;
1341 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1342 talloc_free(mem_ctx);
1343 *_gcc = cred->server_gss_creds;
1344 return 0;
1347 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1348 if (ret) {
1349 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1350 return ret;
1353 gcc = talloc(cred, struct gssapi_creds_container);
1354 if (!gcc) {
1355 talloc_free(mem_ctx);
1356 return ENOMEM;
1359 if (ktc->password_based || obtained < CRED_SPECIFIED) {
1361 * This creates a GSSAPI cred_id_t for match-by-key with only
1362 * the keytab set
1364 princ = NULL;
1366 maj_stat = smb_gss_krb5_import_cred(&min_stat,
1367 smb_krb5_context->krb5_context,
1368 NULL, princ,
1369 ktc->keytab,
1370 &gcc->creds);
1371 if (maj_stat) {
1372 if (min_stat) {
1373 ret = min_stat;
1374 } else {
1375 ret = EINVAL;
1378 if (ret == 0) {
1379 cred->server_gss_creds_obtained = cred->keytab_obtained;
1380 talloc_set_destructor(gcc, free_gssapi_creds);
1381 cred->server_gss_creds = gcc;
1382 *_gcc = gcc;
1384 talloc_free(mem_ctx);
1385 return ret;
1389 * Set Kerberos KVNO
1392 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1393 int kvno)
1395 cred->kvno = kvno;
1399 * Return Kerberos KVNO
1402 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1404 return cred->kvno;
1408 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
1410 return cred->salt_principal;
1413 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
1415 talloc_free(cred->salt_principal);
1416 cred->salt_principal = talloc_strdup(cred, principal);
1419 /* The 'impersonate_principal' is used to allow one Kerberos principal
1420 * (and it's associated keytab etc) to impersonate another. The
1421 * ability to do this is controlled by the KDC, but it is generally
1422 * permitted to impersonate anyone to yourself. This allows any
1423 * member of the domain to get the groups of a user. This is also
1424 * known as S4U2Self */
1426 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1428 return cred->impersonate_principal;
1432 * The 'self_service' is the service principal that
1433 * represents the same object (by its objectSid)
1434 * as the client principal (typically our machine account).
1435 * When trying to impersonate 'impersonate_principal' with
1436 * S4U2Self.
1438 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1440 return cred->self_service;
1443 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1444 const char *principal,
1445 const char *self_service)
1447 talloc_free(cred->impersonate_principal);
1448 cred->impersonate_principal = talloc_strdup(cred, principal);
1449 talloc_free(cred->self_service);
1450 cred->self_service = talloc_strdup(cred, self_service);
1451 cli_credentials_set_kerberos_state(cred,
1452 CRED_USE_KERBEROS_REQUIRED,
1453 CRED_SPECIFIED);
1457 * when impersonating for S4U2proxy we need to set the target principal.
1458 * Similarly, we may only be authorized to do general impersonation to
1459 * some particular services.
1461 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1463 * NULL means that tickets will be obtained for the krbtgt service.
1466 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1468 return cred->target_service;
1471 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1473 talloc_free(cred->target_service);
1474 cred->target_service = talloc_strdup(cred, target_service);
1477 _PUBLIC_ int cli_credentials_get_aes256_key(struct cli_credentials *cred,
1478 TALLOC_CTX *mem_ctx,
1479 struct loadparm_context *lp_ctx,
1480 const char *salt,
1481 DATA_BLOB *aes_256)
1483 struct smb_krb5_context *smb_krb5_context = NULL;
1484 krb5_error_code krb5_ret;
1485 int ret;
1486 const char *password = NULL;
1487 krb5_data cleartext_data;
1488 krb5_data salt_data = {
1489 .length = 0,
1491 krb5_keyblock key;
1493 if (cred->password_will_be_nt_hash) {
1494 DEBUG(1,("cli_credentials_get_aes256_key: cannot generate AES256 key using NT hash\n"));
1495 return EINVAL;
1498 password = cli_credentials_get_password(cred);
1499 if (password == NULL) {
1500 return EINVAL;
1503 cleartext_data.data = discard_const_p(char, password);
1504 cleartext_data.length = strlen(password);
1506 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1507 &smb_krb5_context);
1508 if (ret != 0) {
1509 return ret;
1512 salt_data.data = discard_const_p(char, salt);
1513 salt_data.length = strlen(salt);
1516 * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
1517 * the salt and the cleartext password
1519 krb5_ret = smb_krb5_create_key_from_string(smb_krb5_context->krb5_context,
1520 NULL,
1521 &salt_data,
1522 &cleartext_data,
1523 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
1524 &key);
1525 if (krb5_ret != 0) {
1526 DEBUG(1,("cli_credentials_get_aes256_key: "
1527 "generation of a aes256-cts-hmac-sha1-96 key failed: %s\n",
1528 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
1529 krb5_ret, mem_ctx)));
1530 return EINVAL;
1532 *aes_256 = data_blob_talloc(mem_ctx,
1533 KRB5_KEY_DATA(&key),
1534 KRB5_KEY_LENGTH(&key));
1535 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &key);
1536 if (aes_256->data == NULL) {
1537 return ENOMEM;
1539 talloc_keep_secret(aes_256->data);
1541 return 0;
1544 /* This take a reference to the armor credentials to ensure the lifetime is appropriate */
1546 NTSTATUS cli_credentials_set_krb5_fast_armor_credentials(struct cli_credentials *creds,
1547 struct cli_credentials *armor_creds,
1548 bool require_fast_armor)
1550 talloc_unlink(creds, creds->krb5_fast_armor_credentials);
1551 if (armor_creds == NULL) {
1552 creds->krb5_fast_armor_credentials = NULL;
1553 return NT_STATUS_OK;
1556 creds->krb5_fast_armor_credentials = talloc_reference(creds, armor_creds);
1557 if (creds->krb5_fast_armor_credentials == NULL) {
1558 return NT_STATUS_NO_MEMORY;
1561 creds->krb5_require_fast_armor = require_fast_armor;
1563 return NT_STATUS_OK;
1566 struct cli_credentials *cli_credentials_get_krb5_fast_armor_credentials(struct cli_credentials *creds)
1568 return creds->krb5_fast_armor_credentials;
1571 bool cli_credentials_get_krb5_require_fast_armor(struct cli_credentials *creds)
1573 return creds->krb5_require_fast_armor;