auth/credentials: use smb_krb5_cc_new_unique_memory() in cli_credentials_new_ccache()
[Samba.git] / auth / credentials / credentials_krb5.c
blob49077db23b3f2d6051b5778882488fccd1d15f08
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 {
326 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
327 if (ret) {
328 (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
329 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
330 ret, ccc));
331 talloc_free(ccc);
332 return ret;
336 talloc_set_destructor(ccc, free_dccache);
338 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
340 if (ret == 0) {
341 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
342 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
344 if (ret) {
345 (*error_string) = error_message(ret);
346 TALLOC_FREE(ccc);
347 return ret;
351 cred->ccache = ccc;
352 cred->ccache_obtained = obtained;
354 cli_credentials_invalidate_client_gss_creds(
355 cred, cred->ccache_obtained);
357 return 0;
360 #ifndef SAMBA4_USES_HEIMDAL
362 * This function is a workaround for old MIT Kerberos versions which did not
363 * implement the krb5_cc_remove_cred function. It creates a temporary
364 * credentials cache to copy the credentials in the current cache
365 * except the one we want to remove and then overwrites the contents of the
366 * current cache with the temporary copy.
368 static krb5_error_code krb5_cc_remove_cred_wrap(struct ccache_container *ccc,
369 krb5_creds *creds)
371 krb5_ccache dummy_ccache = NULL;
372 krb5_creds cached_creds = {0};
373 krb5_cc_cursor cursor = NULL;
374 krb5_error_code code;
376 code = smb_krb5_cc_new_unique_memory(ccc->smb_krb5_context->krb5_context,
377 NULL, NULL,
378 &dummy_ccache);
379 if (code != 0) {
380 DBG_ERR("krb5_cc_resolve failed: %s\n",
381 smb_get_krb5_error_message(
382 ccc->smb_krb5_context->krb5_context,
383 code, ccc));
384 return code;
387 code = krb5_cc_start_seq_get(ccc->smb_krb5_context->krb5_context,
388 ccc->ccache,
389 &cursor);
390 if (code != 0) {
391 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
392 dummy_ccache);
394 DBG_ERR("krb5_cc_start_seq_get failed: %s\n",
395 smb_get_krb5_error_message(
396 ccc->smb_krb5_context->krb5_context,
397 code, ccc));
398 return code;
401 while ((code = krb5_cc_next_cred(ccc->smb_krb5_context->krb5_context,
402 ccc->ccache,
403 &cursor,
404 &cached_creds)) == 0) {
405 /* If the principal matches skip it and do not copy to the
406 * temporary cache as this is the one we want to remove */
407 if (krb5_principal_compare_flags(
408 ccc->smb_krb5_context->krb5_context,
409 creds->server,
410 cached_creds.server,
411 0)) {
412 continue;
415 code = krb5_cc_store_cred(
416 ccc->smb_krb5_context->krb5_context,
417 dummy_ccache,
418 &cached_creds);
419 if (code != 0) {
420 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
421 dummy_ccache);
422 DBG_ERR("krb5_cc_store_cred failed: %s\n",
423 smb_get_krb5_error_message(
424 ccc->smb_krb5_context->krb5_context,
425 code, ccc));
426 return code;
430 if (code == KRB5_CC_END) {
431 krb5_cc_end_seq_get(ccc->smb_krb5_context->krb5_context,
432 dummy_ccache,
433 &cursor);
434 code = 0;
437 if (code != 0) {
438 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
439 dummy_ccache);
440 DBG_ERR("krb5_cc_next_cred failed: %s\n",
441 smb_get_krb5_error_message(
442 ccc->smb_krb5_context->krb5_context,
443 code, ccc));
444 return code;
447 code = krb5_cc_initialize(ccc->smb_krb5_context->krb5_context,
448 ccc->ccache,
449 creds->client);
450 if (code != 0) {
451 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
452 dummy_ccache);
453 DBG_ERR("krb5_cc_initialize failed: %s\n",
454 smb_get_krb5_error_message(
455 ccc->smb_krb5_context->krb5_context,
456 code, ccc));
457 return code;
460 code = krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
461 dummy_ccache,
462 ccc->ccache);
463 if (code != 0) {
464 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
465 dummy_ccache);
466 DBG_ERR("krb5_cc_copy_creds failed: %s\n",
467 smb_get_krb5_error_message(
468 ccc->smb_krb5_context->krb5_context,
469 code, ccc));
470 return code;
473 code = krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
474 dummy_ccache);
475 if (code != 0) {
476 DBG_ERR("krb5_cc_destroy failed: %s\n",
477 smb_get_krb5_error_message(
478 ccc->smb_krb5_context->krb5_context,
479 code, ccc));
480 return code;
483 return code;
485 #endif
488 * Indicate that we failed to log in to this service/host with these
489 * credentials. The caller passes an unsigned int which they
490 * initialise to the number of times they would like to retry.
492 * This method is used to support re-trying with freshly fetched
493 * credentials in case a server is rebuilt while clients have
494 * non-expired tickets. When the client code gets a logon failure they
495 * throw away the existing credentials for the server and retry.
497 _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
498 const char *principal,
499 unsigned int *count)
501 struct ccache_container *ccc;
502 krb5_creds creds, creds2;
503 int ret;
505 if (principal == NULL) {
506 /* no way to delete if we don't know the principal */
507 return false;
510 ccc = cred->ccache;
511 if (ccc == NULL) {
512 /* not a kerberos connection */
513 return false;
516 if (*count > 0) {
517 /* We have already tried discarding the credentials */
518 return false;
520 (*count)++;
522 ZERO_STRUCT(creds);
523 ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
524 if (ret != 0) {
525 return false;
528 /* MIT kerberos requires creds.client to match against cached
529 * credentials */
530 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context,
531 ccc->ccache,
532 &creds.client);
533 if (ret != 0) {
534 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context,
535 &creds);
536 DBG_ERR("krb5_cc_get_principal failed: %s\n",
537 smb_get_krb5_error_message(
538 ccc->smb_krb5_context->krb5_context,
539 ret, ccc));
540 return false;
543 ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
544 if (ret != 0) {
545 /* don't retry - we didn't find these credentials to remove */
546 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
547 return false;
550 ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
551 #ifndef SAMBA4_USES_HEIMDAL
552 if (ret == KRB5_CC_NOSUPP) {
553 /* Old MIT kerberos versions did not implement
554 * krb5_cc_remove_cred */
555 ret = krb5_cc_remove_cred_wrap(ccc, &creds);
557 #endif
558 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
559 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
560 if (ret != 0) {
561 /* don't retry - we didn't find these credentials to
562 * remove. Note that with the current backend this
563 * never happens, as it always returns 0 even if the
564 * creds don't exist, which is why we do a separate
565 * krb5_cc_retrieve_cred() above.
567 DBG_ERR("krb5_cc_remove_cred failed: %s\n",
568 smb_get_krb5_error_message(
569 ccc->smb_krb5_context->krb5_context,
570 ret, ccc));
571 return false;
573 return true;
577 static int cli_credentials_new_ccache(struct cli_credentials *cred,
578 struct loadparm_context *lp_ctx,
579 char *given_ccache_name,
580 struct ccache_container **_ccc,
581 const char **error_string)
583 char *ccache_name = given_ccache_name;
584 bool must_free_cc_name = false;
585 krb5_error_code ret;
586 struct ccache_container *ccc = talloc(cred, struct ccache_container);
587 if (!ccc) {
588 return ENOMEM;
591 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
592 &ccc->smb_krb5_context);
593 if (ret) {
594 talloc_free(ccc);
595 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
596 error_message(ret));
597 return ret;
599 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
600 talloc_free(ccc);
601 (*error_string) = strerror(ENOMEM);
602 return ENOMEM;
605 if (!ccache_name) {
606 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
607 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
608 (unsigned int)getpid(), ccc);
609 if (ccache_name == NULL) {
610 talloc_free(ccc);
611 (*error_string) = strerror(ENOMEM);
612 return ENOMEM;
614 must_free_cc_name = true;
618 if (ccache_name != NULL) {
619 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
620 &ccc->ccache);
621 } else {
622 ret = smb_krb5_cc_new_unique_memory(ccc->smb_krb5_context->krb5_context,
623 ccc, &ccache_name,
624 &ccc->ccache);
625 must_free_cc_name = true;
627 if (ret) {
628 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
629 ccache_name,
630 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
631 ret, ccc));
632 talloc_free(ccc);
633 return ret;
636 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
637 talloc_set_destructor(ccc, free_mccache);
638 } else {
639 talloc_set_destructor(ccc, free_dccache);
642 if (must_free_cc_name) {
643 talloc_free(ccache_name);
646 *_ccc = ccc;
648 return 0;
651 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
652 struct tevent_context *event_ctx,
653 struct loadparm_context *lp_ctx,
654 char *ccache_name,
655 struct ccache_container **ccc,
656 const char **error_string)
658 krb5_error_code ret;
659 enum credentials_obtained obtained;
661 if (cred->machine_account_pending) {
662 cli_credentials_set_machine_account(cred, lp_ctx);
665 if (cred->ccache_obtained >= cred->ccache_threshold &&
666 cred->ccache_obtained > CRED_UNINITIALISED) {
667 time_t lifetime;
668 bool expired = false;
669 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
670 cred->ccache->ccache, &lifetime);
671 if (ret == KRB5_CC_END || ret == ENOENT) {
672 /* If we have a particular ccache set, without
673 * an initial ticket, then assume there is a
674 * good reason */
675 } else if (ret == 0) {
676 if (lifetime == 0) {
677 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
678 cli_credentials_get_principal(cred, cred)));
679 expired = true;
680 } else if (lifetime < 300) {
681 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
682 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
683 expired = true;
685 } else {
686 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
687 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
688 ret, cred));
689 return ret;
692 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
693 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
695 if (!expired) {
696 *ccc = cred->ccache;
697 return 0;
700 if (cli_credentials_is_anonymous(cred)) {
701 (*error_string) = "Cannot get anonymous kerberos credentials";
702 return EINVAL;
705 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
706 if (ret) {
707 return ret;
710 ret = kinit_to_ccache(cred,
711 cred,
712 (*ccc)->smb_krb5_context,
713 lp_ctx,
714 event_ctx,
715 (*ccc)->ccache,
716 &obtained,
717 error_string);
718 if (ret) {
719 return ret;
722 ret = cli_credentials_set_from_ccache(cred, *ccc,
723 obtained, error_string);
725 cred->ccache = *ccc;
726 cred->ccache_obtained = cred->principal_obtained;
727 if (ret) {
728 return ret;
730 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
731 return 0;
734 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
735 struct tevent_context *event_ctx,
736 struct loadparm_context *lp_ctx,
737 struct ccache_container **ccc,
738 const char **error_string)
740 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
744 * @brief Check if a valid Kerberos credential cache is attached.
746 * This will not ask for a password nor do a kinit.
748 * @param cred The credentials context.
750 * @param mem_ctx A memory context to allocate the ccache_name.
752 * @param ccache_name A pointer to a string to store the ccache name.
754 * @param obtained A pointer to store the information how the ccache was
755 * obtained.
757 * @return True if a credential cache is attached, false if not or an error
758 * occurred.
760 _PUBLIC_ bool cli_credentials_get_ccache_name_obtained(
761 struct cli_credentials *cred,
762 TALLOC_CTX *mem_ctx,
763 char **ccache_name,
764 enum credentials_obtained *obtained)
766 if (ccache_name != NULL) {
767 *ccache_name = NULL;
770 if (obtained != NULL) {
771 *obtained = CRED_UNINITIALISED;
774 if (cred->machine_account_pending) {
775 return false;
778 if (cred->ccache_obtained == CRED_UNINITIALISED) {
779 return false;
782 if (cred->ccache_obtained >= cred->ccache_threshold) {
783 krb5_context k5ctx = cred->ccache->smb_krb5_context->krb5_context;
784 krb5_ccache k5ccache = cred->ccache->ccache;
785 krb5_error_code ret;
786 time_t lifetime = 0;
788 ret = smb_krb5_cc_get_lifetime(k5ctx, k5ccache, &lifetime);
789 if (ret == KRB5_CC_END || ret == ENOENT) {
790 return false;
792 if (ret != 0) {
793 return false;
795 if (lifetime == 0) {
796 return false;
797 } else if (lifetime < 300) {
798 if (cred->password_obtained >= cred->ccache_obtained) {
800 * we have a password to re-kinit
801 * so let the caller try that.
803 return false;
807 if (ccache_name != NULL) {
808 char *name = NULL;
810 ret = krb5_cc_get_full_name(k5ctx, k5ccache, &name);
811 if (ret != 0) {
812 return false;
815 *ccache_name = talloc_strdup(mem_ctx, name);
816 SAFE_FREE(name);
817 if (*ccache_name == NULL) {
818 return false;
822 if (obtained != NULL) {
823 *obtained = cred->ccache_obtained;
826 return true;
829 return false;
832 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
833 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
835 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
836 talloc_unlink(cred, cred->client_gss_creds);
837 cred->client_gss_creds = NULL;
839 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
842 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
843 enum credentials_obtained obtained)
845 /* If the caller just changed the username/password etc, then
846 * any cached credentials are now invalid */
847 if (obtained >= cred->client_gss_creds_obtained) {
848 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
849 talloc_unlink(cred, cred->client_gss_creds);
850 cred->client_gss_creds = NULL;
852 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
854 /* Now that we know that the data is 'this specified', then
855 * don't allow something less 'known' to be returned as a
856 * ccache. Ie, if the username is on the command line, we
857 * don't want to later guess to use a file-based ccache */
858 if (obtained > cred->client_gss_creds_threshold) {
859 cred->client_gss_creds_threshold = obtained;
863 /* We have good reason to think this CCACHE is invalid. Blow it away */
864 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
866 if (cred->ccache_obtained > CRED_UNINITIALISED) {
867 talloc_unlink(cred, cred->ccache);
868 cred->ccache = NULL;
870 cred->ccache_obtained = CRED_UNINITIALISED;
872 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
875 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
876 enum credentials_obtained obtained)
878 /* If the caller just changed the username/password etc, then
879 * any cached credentials are now invalid */
880 if (obtained >= cred->ccache_obtained) {
881 if (cred->ccache_obtained > CRED_UNINITIALISED) {
882 talloc_unlink(cred, cred->ccache);
883 cred->ccache = NULL;
885 cred->ccache_obtained = CRED_UNINITIALISED;
887 /* Now that we know that the data is 'this specified', then
888 * don't allow something less 'known' to be returned as a
889 * ccache. i.e, if the username is on the command line, we
890 * don't want to later guess to use a file-based ccache */
891 if (obtained > cred->ccache_threshold) {
892 cred->ccache_threshold = obtained;
895 cli_credentials_invalidate_client_gss_creds(cred,
896 obtained);
899 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
901 OM_uint32 min_stat;
902 (void)gss_release_cred(&min_stat, &gcc->creds);
903 return 0;
906 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
907 struct tevent_context *event_ctx,
908 struct loadparm_context *lp_ctx,
909 struct gssapi_creds_container **_gcc,
910 const char **error_string)
912 int ret = 0;
913 OM_uint32 maj_stat, min_stat;
914 struct gssapi_creds_container *gcc;
915 struct ccache_container *ccache;
916 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
917 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
918 gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
919 #endif
920 krb5_enctype *etypes = NULL;
922 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
923 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
924 bool expired = false;
925 OM_uint32 lifetime = 0;
926 gss_cred_usage_t usage = 0;
927 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
928 NULL, &lifetime, &usage, NULL);
929 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
930 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
931 expired = true;
932 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
933 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
934 expired = true;
935 } else if (maj_stat != GSS_S_COMPLETE) {
936 *error_string = talloc_asprintf(cred, "inquiry of credential lifetime via GSSAPI gss_inquire_cred failed: %s\n",
937 gssapi_error_string(cred, maj_stat, min_stat, NULL));
938 return EINVAL;
940 if (expired) {
941 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
942 } else {
943 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
944 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
946 *_gcc = cred->client_gss_creds;
947 return 0;
951 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
952 &ccache, error_string);
953 if (ret) {
954 if (cli_credentials_get_kerberos_state(cred) == CRED_USE_KERBEROS_REQUIRED) {
955 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
956 } else {
957 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
959 return ret;
962 gcc = talloc(cred, struct gssapi_creds_container);
963 if (!gcc) {
964 (*error_string) = error_message(ENOMEM);
965 return ENOMEM;
968 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
969 ccache->ccache, NULL, NULL,
970 &gcc->creds);
971 if ((maj_stat == GSS_S_FAILURE) &&
972 (min_stat == (OM_uint32)KRB5_CC_END ||
973 min_stat == (OM_uint32)KRB5_CC_NOTFOUND ||
974 min_stat == (OM_uint32)KRB5_FCC_NOFILE))
976 /* This CCACHE is no good. Ensure we don't use it again */
977 cli_credentials_unconditionally_invalidate_ccache(cred);
979 /* Now try again to get a ccache */
980 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
981 &ccache, error_string);
982 if (ret) {
983 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
984 return ret;
987 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
988 ccache->ccache, NULL, NULL,
989 &gcc->creds);
993 if (maj_stat) {
994 talloc_free(gcc);
995 if (min_stat) {
996 ret = min_stat;
997 } else {
998 ret = EINVAL;
1000 (*error_string) = talloc_asprintf(cred, "smb_gss_krb5_import_cred failed: %s", error_message(ret));
1001 return ret;
1006 * transfer the enctypes from the smb_krb5_context to the gssapi layer
1008 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
1009 * to configure the enctypes via the krb5.conf.
1011 * And the gss_init_sec_context() creates it's own krb5_context and
1012 * the TGS-REQ had all enctypes in it and only the ones configured
1013 * and used for the AS-REQ, so it wasn't possible to disable the usage
1014 * of AES keys.
1016 min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
1017 &etypes);
1018 if (min_stat == 0) {
1019 OM_uint32 num_ktypes;
1021 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
1023 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
1024 num_ktypes,
1025 (int32_t *) etypes);
1026 krb5_free_enctypes(ccache->smb_krb5_context->krb5_context,
1027 etypes);
1028 if (maj_stat) {
1029 talloc_free(gcc);
1030 if (min_stat) {
1031 ret = min_stat;
1032 } else {
1033 ret = EINVAL;
1035 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
1036 return ret;
1040 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
1042 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
1044 * This allows us to disable SIGN and SEAL on a TLS connection with
1045 * GSS-SPNENO. For example ldaps:// connections.
1047 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
1048 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
1050 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
1051 oid,
1052 &empty_buffer);
1053 if (maj_stat) {
1054 talloc_free(gcc);
1055 if (min_stat) {
1056 ret = min_stat;
1057 } else {
1058 ret = EINVAL;
1060 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
1061 return ret;
1063 #endif
1064 cred->client_gss_creds_obtained = cred->ccache_obtained;
1065 talloc_set_destructor(gcc, free_gssapi_creds);
1066 cred->client_gss_creds = gcc;
1067 *_gcc = gcc;
1068 return 0;
1072 Set a gssapi cred_id_t into the credentials system. (Client case)
1074 This grabs the credentials both 'intact' and getting the krb5
1075 ccache out of it. This routine can be generalised in future for
1076 the case where we deal with GSSAPI mechs other than krb5.
1078 On success, the caller must not free gssapi_cred, as it now belongs
1079 to the credentials system.
1082 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
1083 struct loadparm_context *lp_ctx,
1084 gss_cred_id_t gssapi_cred,
1085 enum credentials_obtained obtained,
1086 const char **error_string)
1088 int ret;
1089 OM_uint32 maj_stat, min_stat;
1090 struct ccache_container *ccc = NULL;
1091 struct gssapi_creds_container *gcc = NULL;
1092 if (cred->client_gss_creds_obtained > obtained) {
1093 return 0;
1096 gcc = talloc(cred, struct gssapi_creds_container);
1097 if (!gcc) {
1098 (*error_string) = error_message(ENOMEM);
1099 return ENOMEM;
1102 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
1103 if (ret != 0) {
1104 return ret;
1107 maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
1108 gssapi_cred,
1109 ccc);
1110 if (maj_stat) {
1111 if (min_stat) {
1112 ret = min_stat;
1113 } else {
1114 ret = EINVAL;
1116 if (ret) {
1117 (*error_string) = error_message(ENOMEM);
1121 if (ret == 0) {
1122 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
1124 cred->ccache = ccc;
1125 cred->ccache_obtained = obtained;
1126 if (ret == 0) {
1127 gcc->creds = gssapi_cred;
1128 talloc_set_destructor(gcc, free_gssapi_creds);
1130 /* set the client_gss_creds_obtained here, as it just
1131 got set to UNINITIALISED by the calls above */
1132 cred->client_gss_creds_obtained = obtained;
1133 cred->client_gss_creds = gcc;
1135 return ret;
1138 static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
1140 krb5_error_code ret;
1141 const struct ccache_container *old_ccc = NULL;
1142 enum credentials_obtained old_obtained;
1143 struct ccache_container *ccc = NULL;
1144 krb5_principal princ;
1146 old_obtained = cred->ccache_obtained;
1147 old_ccc = cred->ccache;
1148 if (old_ccc == NULL) {
1149 return 0;
1152 cred->ccache = NULL;
1153 cred->ccache_obtained = CRED_UNINITIALISED;
1154 cred->client_gss_creds = NULL;
1155 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
1157 ret = krb5_cc_get_principal(
1158 old_ccc->smb_krb5_context->krb5_context,
1159 old_ccc->ccache,
1160 &princ);
1161 if (ret != 0) {
1163 * This is an empty ccache. No point in copying anything.
1165 return 0;
1167 krb5_free_principal(old_ccc->smb_krb5_context->krb5_context, princ);
1169 ccc = talloc(cred, struct ccache_container);
1170 if (ccc == NULL) {
1171 return ENOMEM;
1173 *ccc = *old_ccc;
1174 ccc->ccache = NULL;
1176 ret = smb_krb5_cc_new_unique_memory(ccc->smb_krb5_context->krb5_context,
1177 NULL,
1178 NULL,
1179 &ccc->ccache);
1180 if (ret != 0) {
1181 TALLOC_FREE(ccc);
1182 return ret;
1185 talloc_set_destructor(ccc, free_mccache);
1187 ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
1188 old_ccc->ccache, ccc->ccache);
1189 if (ret != 0) {
1190 TALLOC_FREE(ccc);
1191 return ret;
1194 cred->ccache = ccc;
1195 cred->ccache_obtained = old_obtained;
1196 return ret;
1199 _PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
1200 struct cli_credentials *src)
1202 struct cli_credentials *dst, *armor_credentials;
1203 int ret;
1205 dst = talloc(mem_ctx, struct cli_credentials);
1206 if (dst == NULL) {
1207 return NULL;
1210 *dst = *src;
1212 if (dst->krb5_fast_armor_credentials != NULL) {
1213 armor_credentials = talloc_reference(dst, dst->krb5_fast_armor_credentials);
1214 if (armor_credentials == NULL) {
1215 TALLOC_FREE(dst);
1216 return NULL;
1220 ret = cli_credentials_shallow_ccache(dst);
1221 if (ret != 0) {
1222 TALLOC_FREE(dst);
1223 return NULL;
1226 return dst;
1229 /* Get the keytab (actually, a container containing the krb5_keytab)
1230 * attached to this context. If this hasn't been done or set before,
1231 * it will be generated from the password.
1233 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
1234 struct loadparm_context *lp_ctx,
1235 struct keytab_container **_ktc)
1237 krb5_error_code ret;
1238 struct keytab_container *ktc;
1239 struct smb_krb5_context *smb_krb5_context;
1240 const char *keytab_name;
1241 krb5_keytab keytab;
1242 TALLOC_CTX *mem_ctx;
1243 const char *username = cli_credentials_get_username(cred);
1244 const char *realm = cli_credentials_get_realm(cred);
1245 char *salt_principal = NULL;
1247 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
1248 cred->username_obtained))) {
1249 *_ktc = cred->keytab;
1250 return 0;
1253 if (cli_credentials_is_anonymous(cred)) {
1254 return EINVAL;
1257 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1258 &smb_krb5_context);
1259 if (ret) {
1260 return ret;
1263 mem_ctx = talloc_new(cred);
1264 if (!mem_ctx) {
1265 return ENOMEM;
1268 salt_principal = cli_credentials_get_salt_principal(cred, mem_ctx);
1269 if (salt_principal == NULL) {
1270 talloc_free(mem_ctx);
1271 return ENOMEM;
1274 ret = smb_krb5_create_memory_keytab(mem_ctx,
1275 smb_krb5_context->krb5_context,
1276 cli_credentials_get_password(cred),
1277 username,
1278 realm,
1279 salt_principal,
1280 cli_credentials_get_kvno(cred),
1281 &keytab,
1282 &keytab_name);
1283 if (ret) {
1284 talloc_free(mem_ctx);
1285 return ret;
1288 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1289 keytab, keytab_name, &ktc);
1290 if (ret) {
1291 talloc_free(mem_ctx);
1292 return ret;
1295 cred->keytab_obtained = (MAX(cred->principal_obtained,
1296 cred->username_obtained));
1298 /* We make this keytab up based on a password. Therefore
1299 * match-by-key is acceptable, we can't match on the wrong
1300 * principal */
1301 ktc->password_based = true;
1303 talloc_steal(cred, ktc);
1304 cred->keytab = ktc;
1305 *_ktc = cred->keytab;
1306 talloc_free(mem_ctx);
1307 return ret;
1310 /* Given the name of a keytab (presumably in the format
1311 * FILE:/etc/krb5.keytab), open it and attach it */
1313 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
1314 struct loadparm_context *lp_ctx,
1315 const char *keytab_name,
1316 enum credentials_obtained obtained)
1318 krb5_error_code ret;
1319 struct keytab_container *ktc;
1320 struct smb_krb5_context *smb_krb5_context;
1321 TALLOC_CTX *mem_ctx;
1323 if (cred->keytab_obtained >= obtained) {
1324 return 0;
1327 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1328 if (ret) {
1329 return ret;
1332 mem_ctx = talloc_new(cred);
1333 if (!mem_ctx) {
1334 return ENOMEM;
1337 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1338 NULL, keytab_name, &ktc);
1339 if (ret) {
1340 return ret;
1343 cred->keytab_obtained = obtained;
1345 talloc_steal(cred, ktc);
1346 cred->keytab = ktc;
1347 talloc_free(mem_ctx);
1349 return ret;
1352 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1354 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
1355 struct loadparm_context *lp_ctx,
1356 struct gssapi_creds_container **_gcc)
1358 int ret = 0;
1359 OM_uint32 maj_stat, min_stat;
1360 struct gssapi_creds_container *gcc;
1361 struct keytab_container *ktc;
1362 struct smb_krb5_context *smb_krb5_context;
1363 TALLOC_CTX *mem_ctx;
1364 krb5_principal princ;
1365 const char *error_string;
1366 enum credentials_obtained obtained;
1368 mem_ctx = talloc_new(cred);
1369 if (!mem_ctx) {
1370 return ENOMEM;
1373 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1374 if (ret) {
1375 return ret;
1378 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1379 if (ret) {
1380 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1381 error_string));
1382 talloc_free(mem_ctx);
1383 return ret;
1386 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1387 talloc_free(mem_ctx);
1388 *_gcc = cred->server_gss_creds;
1389 return 0;
1392 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1393 if (ret) {
1394 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1395 return ret;
1398 gcc = talloc(cred, struct gssapi_creds_container);
1399 if (!gcc) {
1400 talloc_free(mem_ctx);
1401 return ENOMEM;
1404 if (ktc->password_based || obtained < CRED_SPECIFIED) {
1406 * This creates a GSSAPI cred_id_t for match-by-key with only
1407 * the keytab set
1409 princ = NULL;
1411 maj_stat = smb_gss_krb5_import_cred(&min_stat,
1412 smb_krb5_context->krb5_context,
1413 NULL, princ,
1414 ktc->keytab,
1415 &gcc->creds);
1416 if (maj_stat) {
1417 if (min_stat) {
1418 ret = min_stat;
1419 } else {
1420 ret = EINVAL;
1423 if (ret == 0) {
1424 cred->server_gss_creds_obtained = cred->keytab_obtained;
1425 talloc_set_destructor(gcc, free_gssapi_creds);
1426 cred->server_gss_creds = gcc;
1427 *_gcc = gcc;
1429 talloc_free(mem_ctx);
1430 return ret;
1434 * Set Kerberos KVNO
1437 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1438 int kvno)
1440 cred->kvno = kvno;
1444 * Return Kerberos KVNO
1447 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1449 return cred->kvno;
1453 char *cli_credentials_get_salt_principal(struct cli_credentials *cred, TALLOC_CTX *mem_ctx)
1455 TALLOC_CTX *frame = NULL;
1456 const char *realm = NULL;
1457 const char *username = NULL;
1458 uint32_t uac_flags = 0;
1459 char *salt_principal = NULL;
1460 const char *upn = NULL;
1461 int ret;
1463 /* If specified, use the specified value */
1464 if (cred->salt_principal != NULL) {
1465 return talloc_strdup(mem_ctx, cred->salt_principal);
1468 frame = talloc_stackframe();
1470 switch (cred->secure_channel_type) {
1471 case SEC_CHAN_WKSTA:
1472 case SEC_CHAN_RODC:
1473 uac_flags = UF_WORKSTATION_TRUST_ACCOUNT;
1474 break;
1475 case SEC_CHAN_BDC:
1476 uac_flags = UF_SERVER_TRUST_ACCOUNT;
1477 break;
1478 case SEC_CHAN_DOMAIN:
1479 case SEC_CHAN_DNS_DOMAIN:
1480 uac_flags = UF_INTERDOMAIN_TRUST_ACCOUNT;
1481 break;
1482 default:
1483 upn = cli_credentials_get_principal(cred, frame);
1484 if (upn == NULL) {
1485 TALLOC_FREE(frame);
1486 return NULL;
1488 uac_flags = UF_NORMAL_ACCOUNT;
1489 break;
1492 realm = cli_credentials_get_realm(cred);
1493 username = cli_credentials_get_username(cred);
1495 ret = smb_krb5_salt_principal_str(realm,
1496 username, /* sAMAccountName */
1497 upn, /* userPrincipalName */
1498 uac_flags,
1499 mem_ctx,
1500 &salt_principal);
1501 if (ret) {
1502 TALLOC_FREE(frame);
1503 return NULL;
1506 TALLOC_FREE(frame);
1507 return salt_principal;
1510 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
1512 talloc_free(cred->salt_principal);
1513 cred->salt_principal = talloc_strdup(cred, principal);
1516 /* The 'impersonate_principal' is used to allow one Kerberos principal
1517 * (and it's associated keytab etc) to impersonate another. The
1518 * ability to do this is controlled by the KDC, but it is generally
1519 * permitted to impersonate anyone to yourself. This allows any
1520 * member of the domain to get the groups of a user. This is also
1521 * known as S4U2Self */
1523 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1525 return cred->impersonate_principal;
1529 * The 'self_service' is the service principal that
1530 * represents the same object (by its objectSid)
1531 * as the client principal (typically our machine account).
1532 * When trying to impersonate 'impersonate_principal' with
1533 * S4U2Self.
1535 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1537 return cred->self_service;
1540 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1541 const char *principal,
1542 const char *self_service)
1544 talloc_free(cred->impersonate_principal);
1545 cred->impersonate_principal = talloc_strdup(cred, principal);
1546 talloc_free(cred->self_service);
1547 cred->self_service = talloc_strdup(cred, self_service);
1548 cli_credentials_set_kerberos_state(cred,
1549 CRED_USE_KERBEROS_REQUIRED,
1550 CRED_SPECIFIED);
1554 * when impersonating for S4U2proxy we need to set the target principal.
1555 * Similarly, we may only be authorized to do general impersonation to
1556 * some particular services.
1558 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1560 * NULL means that tickets will be obtained for the krbtgt service.
1563 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1565 return cred->target_service;
1568 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1570 talloc_free(cred->target_service);
1571 cred->target_service = talloc_strdup(cred, target_service);
1574 _PUBLIC_ int cli_credentials_get_kerberos_key(struct cli_credentials *cred,
1575 TALLOC_CTX *mem_ctx,
1576 struct loadparm_context *lp_ctx,
1577 krb5_enctype enctype,
1578 bool previous,
1579 DATA_BLOB *key_blob)
1581 struct smb_krb5_context *smb_krb5_context = NULL;
1582 krb5_error_code krb5_ret;
1583 int ret;
1584 const char *password = NULL;
1585 const char *salt = NULL;
1586 krb5_data cleartext_data;
1587 krb5_data salt_data = {
1588 .length = 0,
1590 krb5_keyblock key;
1592 TALLOC_CTX *frame = talloc_stackframe();
1594 if ((int)enctype == (int)ENCTYPE_ARCFOUR_HMAC) {
1595 struct samr_Password *nt_hash;
1597 if (previous) {
1598 nt_hash = cli_credentials_get_old_nt_hash(cred, frame);
1599 } else {
1600 nt_hash = cli_credentials_get_nt_hash(cred, frame);
1603 if (nt_hash == NULL) {
1604 TALLOC_FREE(frame);
1605 return EINVAL;
1607 *key_blob = data_blob_talloc(mem_ctx,
1608 nt_hash->hash,
1609 sizeof(nt_hash->hash));
1610 if (key_blob->data == NULL) {
1611 TALLOC_FREE(frame);
1612 return ENOMEM;
1614 TALLOC_FREE(frame);
1615 return 0;
1618 if (cred->password_will_be_nt_hash) {
1619 DEBUG(1,("cli_credentials_get_kerberos_key: cannot generate Kerberos key using NT hash\n"));
1620 TALLOC_FREE(frame);
1621 return EINVAL;
1624 salt = cli_credentials_get_salt_principal(cred, frame);
1625 if (salt == NULL) {
1626 TALLOC_FREE(frame);
1627 return EINVAL;
1630 if (previous) {
1631 password = cli_credentials_get_old_password(cred);
1632 } else {
1633 password = cli_credentials_get_password(cred);
1635 if (password == NULL) {
1636 TALLOC_FREE(frame);
1637 return EINVAL;
1640 cleartext_data.data = discard_const_p(char, password);
1641 cleartext_data.length = strlen(password);
1643 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1644 &smb_krb5_context);
1645 if (ret != 0) {
1646 TALLOC_FREE(frame);
1647 return ret;
1650 salt_data.data = discard_const_p(char, salt);
1651 salt_data.length = strlen(salt);
1654 * create Kerberos key out of
1655 * the salt and the cleartext password
1657 krb5_ret = smb_krb5_create_key_from_string(smb_krb5_context->krb5_context,
1658 NULL,
1659 &salt_data,
1660 &cleartext_data,
1661 enctype,
1662 &key);
1663 if (krb5_ret != 0) {
1664 DEBUG(1,("cli_credentials_get_aes256_key: "
1665 "generation of a aes256-cts-hmac-sha1-96 key failed: %s\n",
1666 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
1667 krb5_ret, mem_ctx)));
1668 TALLOC_FREE(frame);
1669 return EINVAL;
1671 *key_blob = data_blob_talloc(mem_ctx,
1672 KRB5_KEY_DATA(&key),
1673 KRB5_KEY_LENGTH(&key));
1674 krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &key);
1675 if (key_blob->data == NULL) {
1676 TALLOC_FREE(frame);
1677 return ENOMEM;
1679 talloc_keep_secret(key_blob->data);
1681 TALLOC_FREE(frame);
1682 return 0;
1685 /* This take a reference to the armor credentials to ensure the lifetime is appropriate */
1687 NTSTATUS cli_credentials_set_krb5_fast_armor_credentials(struct cli_credentials *creds,
1688 struct cli_credentials *armor_creds,
1689 bool require_fast_armor)
1691 talloc_unlink(creds, creds->krb5_fast_armor_credentials);
1692 if (armor_creds == NULL) {
1693 creds->krb5_fast_armor_credentials = NULL;
1694 return NT_STATUS_OK;
1697 creds->krb5_fast_armor_credentials = talloc_reference(creds, armor_creds);
1698 if (creds->krb5_fast_armor_credentials == NULL) {
1699 return NT_STATUS_NO_MEMORY;
1702 creds->krb5_require_fast_armor = require_fast_armor;
1704 return NT_STATUS_OK;
1707 struct cli_credentials *cli_credentials_get_krb5_fast_armor_credentials(struct cli_credentials *creds)
1709 return creds->krb5_fast_armor_credentials;
1712 bool cli_credentials_get_krb5_require_fast_armor(struct cli_credentials *creds)
1714 return creds->krb5_require_fast_armor;