auth/credentials: make use of smb_krb5_salt_principal() in cli_credentials_get_keytab()
[Samba.git] / auth / credentials / credentials_krb5.c
blob6db294bfcc3de7192876f6b2ee3637b855be5070
1 /*
2 Unix SMB/CIFS implementation.
4 Handle user credentials (as regards krb5)
6 Copyright (C) Jelmer Vernooij 2005
7 Copyright (C) Tim Potter 2001
8 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "system/kerberos.h"
26 #include "system/gssapi.h"
27 #include "auth/kerberos/kerberos.h"
28 #include "auth/credentials/credentials.h"
29 #include "auth/credentials/credentials_internal.h"
30 #include "auth/credentials/credentials_proto.h"
31 #include "auth/credentials/credentials_krb5.h"
32 #include "auth/kerberos/kerberos_credentials.h"
33 #include "auth/kerberos/kerberos_srv_keytab.h"
34 #include "auth/kerberos/kerberos_util.h"
35 #include "auth/kerberos/pac_utils.h"
36 #include "param/param.h"
38 static void cli_credentials_invalidate_client_gss_creds(
39 struct cli_credentials *cred,
40 enum credentials_obtained obtained);
42 /* Free a memory ccache */
43 static int free_mccache(struct ccache_container *ccc)
45 if (ccc->ccache != NULL) {
46 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
47 ccc->ccache);
48 ccc->ccache = NULL;
51 return 0;
54 /* Free a disk-based ccache */
55 static int free_dccache(struct ccache_container *ccc)
57 if (ccc->ccache != NULL) {
58 krb5_cc_close(ccc->smb_krb5_context->krb5_context,
59 ccc->ccache);
60 ccc->ccache = NULL;
63 return 0;
66 static uint32_t smb_gss_krb5_copy_ccache(uint32_t *min_stat,
67 gss_cred_id_t cred,
68 struct ccache_container *ccc)
70 #ifndef SAMBA4_USES_HEIMDAL /* MIT 1.10 */
71 krb5_context context = ccc->smb_krb5_context->krb5_context;
72 krb5_ccache dummy_ccache = NULL;
73 krb5_creds creds = {0};
74 krb5_cc_cursor cursor = NULL;
75 krb5_principal princ = NULL;
76 krb5_error_code code;
77 char *dummy_name;
78 uint32_t maj_stat = GSS_S_FAILURE;
80 dummy_name = talloc_asprintf(ccc,
81 "MEMORY:gss_krb5_copy_ccache-%p",
82 &ccc->ccache);
83 if (dummy_name == NULL) {
84 *min_stat = ENOMEM;
85 return GSS_S_FAILURE;
89 * Create a dummy ccache, so we can iterate over the credentials
90 * and find the default principal for the ccache we want to
91 * copy. The new ccache needs to be initialized with this
92 * principal.
94 code = krb5_cc_resolve(context, dummy_name, &dummy_ccache);
95 TALLOC_FREE(dummy_name);
96 if (code != 0) {
97 *min_stat = code;
98 return GSS_S_FAILURE;
102 * We do not need set a default principal on the temporary dummy
103 * ccache, as we do consume it at all in this function.
105 maj_stat = gss_krb5_copy_ccache(min_stat, cred, dummy_ccache);
106 if (maj_stat != 0) {
107 krb5_cc_close(context, dummy_ccache);
108 return maj_stat;
111 code = krb5_cc_start_seq_get(context, dummy_ccache, &cursor);
112 if (code != 0) {
113 krb5_cc_close(context, dummy_ccache);
114 *min_stat = EINVAL;
115 return GSS_S_FAILURE;
118 code = krb5_cc_next_cred(context,
119 dummy_ccache,
120 &cursor,
121 &creds);
122 if (code != 0) {
123 krb5_cc_close(context, dummy_ccache);
124 *min_stat = EINVAL;
125 return GSS_S_FAILURE;
128 do {
129 if (creds.ticket_flags & TKT_FLG_PRE_AUTH) {
130 krb5_data *tgs;
132 tgs = krb5_princ_component(context,
133 creds.server,
135 if (tgs != NULL && tgs->length >= 1) {
136 int cmp;
138 cmp = memcmp(tgs->data,
139 KRB5_TGS_NAME,
140 tgs->length);
141 if (cmp == 0 && creds.client != NULL) {
142 princ = creds.client;
143 code = KRB5_CC_END;
144 break;
149 krb5_free_cred_contents(context, &creds);
151 code = krb5_cc_next_cred(context,
152 dummy_ccache,
153 &cursor,
154 &creds);
155 } while (code == 0);
157 if (code == KRB5_CC_END) {
158 krb5_cc_end_seq_get(context, dummy_ccache, &cursor);
159 code = 0;
161 krb5_cc_close(context, dummy_ccache);
163 if (code != 0 || princ == NULL) {
164 krb5_free_cred_contents(context, &creds);
165 *min_stat = EINVAL;
166 return GSS_S_FAILURE;
170 * Set the default principal for the cache we copy
171 * into. This is needed to be able that other calls
172 * can read it with e.g. gss_acquire_cred() or
173 * krb5_cc_get_principal().
175 code = krb5_cc_initialize(context, ccc->ccache, princ);
176 if (code != 0) {
177 krb5_free_cred_contents(context, &creds);
178 *min_stat = EINVAL;
179 return GSS_S_FAILURE;
181 krb5_free_cred_contents(context, &creds);
183 #endif /* SAMBA4_USES_HEIMDAL */
185 return gss_krb5_copy_ccache(min_stat,
186 cred,
187 ccc->ccache);
190 _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
191 struct loadparm_context *lp_ctx,
192 struct smb_krb5_context **smb_krb5_context)
194 int ret;
195 if (cred->smb_krb5_context) {
196 *smb_krb5_context = cred->smb_krb5_context;
197 return 0;
200 ret = smb_krb5_init_context(cred, lp_ctx,
201 &cred->smb_krb5_context);
202 if (ret) {
203 cred->smb_krb5_context = NULL;
204 return ret;
206 *smb_krb5_context = cred->smb_krb5_context;
207 return 0;
210 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
211 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
213 _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
214 struct smb_krb5_context *smb_krb5_context)
216 if (smb_krb5_context == NULL) {
217 talloc_unlink(cred, cred->smb_krb5_context);
218 cred->smb_krb5_context = NULL;
219 return NT_STATUS_OK;
222 if (!talloc_reference(cred, smb_krb5_context)) {
223 return NT_STATUS_NO_MEMORY;
225 cred->smb_krb5_context = smb_krb5_context;
226 return NT_STATUS_OK;
229 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
230 struct ccache_container *ccache,
231 enum credentials_obtained obtained,
232 const char **error_string)
234 bool ok;
235 char *realm;
236 krb5_principal princ;
237 krb5_error_code ret;
238 char *name;
240 if (cred->ccache_obtained > obtained) {
241 return 0;
244 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
245 ccache->ccache, &princ);
247 if (ret) {
248 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
249 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
250 ret, cred));
251 return ret;
254 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
255 if (ret) {
256 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
257 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
258 ret, cred));
259 return ret;
262 ok = cli_credentials_set_principal(cred, name, obtained);
263 krb5_free_unparsed_name(ccache->smb_krb5_context->krb5_context, name);
264 if (!ok) {
265 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
266 return ENOMEM;
269 realm = smb_krb5_principal_get_realm(ccache->smb_krb5_context->krb5_context,
270 princ);
271 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
272 if (realm == NULL) {
273 return ENOMEM;
275 ok = cli_credentials_set_realm(cred, realm, obtained);
276 SAFE_FREE(realm);
277 if (!ok) {
278 return ENOMEM;
281 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
282 cred->ccache_obtained = obtained;
284 return 0;
287 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
288 struct loadparm_context *lp_ctx,
289 const char *name,
290 enum credentials_obtained obtained,
291 const char **error_string)
293 krb5_error_code ret;
294 krb5_principal princ;
295 struct ccache_container *ccc;
296 if (cred->ccache_obtained > obtained) {
297 return 0;
300 ccc = talloc(cred, struct ccache_container);
301 if (!ccc) {
302 (*error_string) = error_message(ENOMEM);
303 return ENOMEM;
306 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
307 &ccc->smb_krb5_context);
308 if (ret) {
309 (*error_string) = error_message(ret);
310 talloc_free(ccc);
311 return ret;
313 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
314 talloc_free(ccc);
315 (*error_string) = error_message(ENOMEM);
316 return ENOMEM;
319 if (name) {
320 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
321 if (ret) {
322 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
323 name,
324 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
325 ret, ccc));
326 talloc_free(ccc);
327 return ret;
329 } else {
330 ret = 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 return ret;
353 cred->ccache = ccc;
354 cred->ccache_obtained = obtained;
355 talloc_steal(cred, ccc);
357 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
358 return 0;
360 return 0;
364 * Indicate the we failed to log in to this service/host with these
365 * credentials. The caller passes an unsigned int which they
366 * initialise to the number of times they would like to retry.
368 * This method is used to support re-trying with freshly fetched
369 * credentials in case a server is rebuilt while clients have
370 * non-expired tickets. When the client code gets a logon failure they
371 * throw away the existing credentials for the server and retry.
373 _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
374 const char *principal,
375 unsigned int *count)
377 struct ccache_container *ccc;
378 krb5_creds creds, creds2;
379 int ret;
381 if (principal == NULL) {
382 /* no way to delete if we don't know the principal */
383 return false;
386 ccc = cred->ccache;
387 if (ccc == NULL) {
388 /* not a kerberos connection */
389 return false;
392 if (*count > 0) {
393 /* We have already tried discarding the credentials */
394 return false;
396 (*count)++;
398 ZERO_STRUCT(creds);
399 ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
400 if (ret != 0) {
401 return false;
404 ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
405 if (ret != 0) {
406 /* don't retry - we didn't find these credentials to remove */
407 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
408 return false;
411 ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
412 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
413 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
414 if (ret != 0) {
415 /* don't retry - we didn't find these credentials to
416 * remove. Note that with the current backend this
417 * never happens, as it always returns 0 even if the
418 * creds don't exist, which is why we do a separate
419 * krb5_cc_retrieve_cred() above.
421 return false;
423 return true;
427 static int cli_credentials_new_ccache(struct cli_credentials *cred,
428 struct loadparm_context *lp_ctx,
429 char *ccache_name,
430 struct ccache_container **_ccc,
431 const char **error_string)
433 bool must_free_cc_name = false;
434 krb5_error_code ret;
435 struct ccache_container *ccc = talloc(cred, struct ccache_container);
436 if (!ccc) {
437 return ENOMEM;
440 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
441 &ccc->smb_krb5_context);
442 if (ret) {
443 talloc_free(ccc);
444 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
445 error_message(ret));
446 return ret;
448 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
449 talloc_free(ccc);
450 (*error_string) = strerror(ENOMEM);
451 return ENOMEM;
454 if (!ccache_name) {
455 must_free_cc_name = true;
457 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
458 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
459 (unsigned int)getpid(), ccc);
460 } else {
461 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
462 ccc);
465 if (!ccache_name) {
466 talloc_free(ccc);
467 (*error_string) = strerror(ENOMEM);
468 return ENOMEM;
472 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
473 &ccc->ccache);
474 if (ret) {
475 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
476 ccache_name,
477 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
478 ret, ccc));
479 talloc_free(ccache_name);
480 talloc_free(ccc);
481 return ret;
484 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
485 talloc_set_destructor(ccc, free_mccache);
486 } else {
487 talloc_set_destructor(ccc, free_dccache);
490 if (must_free_cc_name) {
491 talloc_free(ccache_name);
494 *_ccc = ccc;
496 return 0;
499 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
500 struct tevent_context *event_ctx,
501 struct loadparm_context *lp_ctx,
502 char *ccache_name,
503 struct ccache_container **ccc,
504 const char **error_string)
506 krb5_error_code ret;
507 enum credentials_obtained obtained;
509 if (cred->machine_account_pending) {
510 cli_credentials_set_machine_account(cred, lp_ctx);
513 if (cred->ccache_obtained >= cred->ccache_threshold &&
514 cred->ccache_obtained > CRED_UNINITIALISED) {
515 time_t lifetime;
516 bool expired = false;
517 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
518 cred->ccache->ccache, &lifetime);
519 if (ret == KRB5_CC_END) {
520 /* If we have a particular ccache set, without
521 * an initial ticket, then assume there is a
522 * good reason */
523 } else if (ret == 0) {
524 if (lifetime == 0) {
525 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
526 cli_credentials_get_principal(cred, cred)));
527 expired = true;
528 } else if (lifetime < 300) {
529 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
530 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
531 expired = true;
533 } else {
534 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
535 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
536 ret, cred));
537 return ret;
540 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
541 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
543 if (!expired) {
544 *ccc = cred->ccache;
545 return 0;
548 if (cli_credentials_is_anonymous(cred)) {
549 (*error_string) = "Cannot get anonymous kerberos credentials";
550 return EINVAL;
553 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
554 if (ret) {
555 return ret;
558 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
559 if (ret) {
560 return ret;
563 ret = cli_credentials_set_from_ccache(cred, *ccc,
564 obtained, error_string);
566 cred->ccache = *ccc;
567 cred->ccache_obtained = cred->principal_obtained;
568 if (ret) {
569 return ret;
571 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
572 return 0;
575 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
576 struct tevent_context *event_ctx,
577 struct loadparm_context *lp_ctx,
578 struct ccache_container **ccc,
579 const char **error_string)
581 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
584 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
585 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
587 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
588 talloc_unlink(cred, cred->client_gss_creds);
589 cred->client_gss_creds = NULL;
591 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
594 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
595 enum credentials_obtained obtained)
597 /* If the caller just changed the username/password etc, then
598 * any cached credentials are now invalid */
599 if (obtained >= cred->client_gss_creds_obtained) {
600 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
601 talloc_unlink(cred, cred->client_gss_creds);
602 cred->client_gss_creds = NULL;
604 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
606 /* Now that we know that the data is 'this specified', then
607 * don't allow something less 'known' to be returned as a
608 * ccache. Ie, if the username is on the command line, we
609 * don't want to later guess to use a file-based ccache */
610 if (obtained > cred->client_gss_creds_threshold) {
611 cred->client_gss_creds_threshold = obtained;
615 /* We have good reason to think this CCACHE is invalid. Blow it away */
616 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
618 if (cred->ccache_obtained > CRED_UNINITIALISED) {
619 talloc_unlink(cred, cred->ccache);
620 cred->ccache = NULL;
622 cred->ccache_obtained = CRED_UNINITIALISED;
624 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
627 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
628 enum credentials_obtained obtained)
630 /* If the caller just changed the username/password etc, then
631 * any cached credentials are now invalid */
632 if (obtained >= cred->ccache_obtained) {
633 if (cred->ccache_obtained > CRED_UNINITIALISED) {
634 talloc_unlink(cred, cred->ccache);
635 cred->ccache = NULL;
637 cred->ccache_obtained = CRED_UNINITIALISED;
639 /* Now that we know that the data is 'this specified', then
640 * don't allow something less 'known' to be returned as a
641 * ccache. i.e, if the username is on the command line, we
642 * don't want to later guess to use a file-based ccache */
643 if (obtained > cred->ccache_threshold) {
644 cred->ccache_threshold = obtained;
647 cli_credentials_invalidate_client_gss_creds(cred,
648 obtained);
651 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
653 OM_uint32 min_stat;
654 (void)gss_release_cred(&min_stat, &gcc->creds);
655 return 0;
658 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
659 struct tevent_context *event_ctx,
660 struct loadparm_context *lp_ctx,
661 struct gssapi_creds_container **_gcc,
662 const char **error_string)
664 int ret = 0;
665 OM_uint32 maj_stat, min_stat;
666 struct gssapi_creds_container *gcc;
667 struct ccache_container *ccache;
668 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
669 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
670 gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
671 #endif
672 krb5_enctype *etypes = NULL;
674 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
675 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
676 bool expired = false;
677 OM_uint32 lifetime = 0;
678 gss_cred_usage_t usage = 0;
679 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
680 NULL, &lifetime, &usage, NULL);
681 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
682 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
683 expired = true;
684 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
685 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
686 expired = true;
687 } else if (maj_stat != GSS_S_COMPLETE) {
688 *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
689 gssapi_error_string(cred, maj_stat, min_stat, NULL));
690 return EINVAL;
692 if (expired) {
693 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
694 } else {
695 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
696 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
698 *_gcc = cred->client_gss_creds;
699 return 0;
703 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
704 &ccache, error_string);
705 if (ret) {
706 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
707 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
708 } else {
709 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
711 return ret;
714 gcc = talloc(cred, struct gssapi_creds_container);
715 if (!gcc) {
716 (*error_string) = error_message(ENOMEM);
717 return ENOMEM;
720 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
721 ccache->ccache, NULL, NULL,
722 &gcc->creds);
723 if ((maj_stat == GSS_S_FAILURE) &&
724 (min_stat == (OM_uint32)KRB5_CC_END ||
725 min_stat == (OM_uint32)KRB5_CC_NOTFOUND ||
726 min_stat == (OM_uint32)KRB5_FCC_NOFILE))
728 /* This CCACHE is no good. Ensure we don't use it again */
729 cli_credentials_unconditionally_invalidate_ccache(cred);
731 /* Now try again to get a ccache */
732 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
733 &ccache, error_string);
734 if (ret) {
735 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
736 return ret;
739 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
740 ccache->ccache, NULL, NULL,
741 &gcc->creds);
745 if (maj_stat) {
746 talloc_free(gcc);
747 if (min_stat) {
748 ret = min_stat;
749 } else {
750 ret = EINVAL;
752 (*error_string) = talloc_asprintf(cred, "smb_gss_krb5_import_cred failed: %s", error_message(ret));
753 return ret;
758 * transfer the enctypes from the smb_krb5_context to the gssapi layer
760 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
761 * to configure the enctypes via the krb5.conf.
763 * And the gss_init_sec_context() creates it's own krb5_context and
764 * the TGS-REQ had all enctypes in it and only the ones configured
765 * and used for the AS-REQ, so it wasn't possible to disable the usage
766 * of AES keys.
768 min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
769 &etypes);
770 if (min_stat == 0) {
771 OM_uint32 num_ktypes;
773 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
775 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
776 num_ktypes,
777 (int32_t *) etypes);
778 SAFE_FREE(etypes);
779 if (maj_stat) {
780 talloc_free(gcc);
781 if (min_stat) {
782 ret = min_stat;
783 } else {
784 ret = EINVAL;
786 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
787 return ret;
791 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
793 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
795 * This allows us to disable SIGN and SEAL on a TLS connection with
796 * GSS-SPNENO. For example ldaps:// connections.
798 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
799 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
801 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
802 oid,
803 &empty_buffer);
804 if (maj_stat) {
805 talloc_free(gcc);
806 if (min_stat) {
807 ret = min_stat;
808 } else {
809 ret = EINVAL;
811 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
812 return ret;
814 #endif
815 cred->client_gss_creds_obtained = cred->ccache_obtained;
816 talloc_set_destructor(gcc, free_gssapi_creds);
817 cred->client_gss_creds = gcc;
818 *_gcc = gcc;
819 return 0;
823 Set a gssapi cred_id_t into the credentials system. (Client case)
825 This grabs the credentials both 'intact' and getting the krb5
826 ccache out of it. This routine can be generalised in future for
827 the case where we deal with GSSAPI mechs other than krb5.
829 On sucess, the caller must not free gssapi_cred, as it now belongs
830 to the credentials system.
833 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
834 struct loadparm_context *lp_ctx,
835 gss_cred_id_t gssapi_cred,
836 enum credentials_obtained obtained,
837 const char **error_string)
839 int ret;
840 OM_uint32 maj_stat, min_stat;
841 struct ccache_container *ccc = NULL;
842 struct gssapi_creds_container *gcc = NULL;
843 if (cred->client_gss_creds_obtained > obtained) {
844 return 0;
847 gcc = talloc(cred, struct gssapi_creds_container);
848 if (!gcc) {
849 (*error_string) = error_message(ENOMEM);
850 return ENOMEM;
853 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
854 if (ret != 0) {
855 return ret;
858 maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
859 gssapi_cred,
860 ccc);
861 if (maj_stat) {
862 if (min_stat) {
863 ret = min_stat;
864 } else {
865 ret = EINVAL;
867 if (ret) {
868 (*error_string) = error_message(ENOMEM);
872 if (ret == 0) {
873 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
875 cred->ccache = ccc;
876 cred->ccache_obtained = obtained;
877 if (ret == 0) {
878 gcc->creds = gssapi_cred;
879 talloc_set_destructor(gcc, free_gssapi_creds);
881 /* set the clinet_gss_creds_obtained here, as it just
882 got set to UNINITIALISED by the calls above */
883 cred->client_gss_creds_obtained = obtained;
884 cred->client_gss_creds = gcc;
886 return ret;
889 static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
891 krb5_error_code ret;
892 const struct ccache_container *old_ccc = NULL;
893 struct ccache_container *ccc = NULL;
894 char *ccache_name = NULL;
896 old_ccc = cred->ccache;
897 if (old_ccc == NULL) {
898 return 0;
901 ccc = talloc(cred, struct ccache_container);
902 if (ccc == NULL) {
903 return ENOMEM;
905 *ccc = *old_ccc;
906 ccc->ccache = NULL;
908 ccache_name = talloc_asprintf(ccc, "MEMORY:%p", ccc);
910 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
911 ccache_name, &ccc->ccache);
912 if (ret != 0) {
913 TALLOC_FREE(ccc);
914 return ret;
917 talloc_set_destructor(ccc, free_mccache);
919 TALLOC_FREE(ccache_name);
921 ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
922 old_ccc->ccache, ccc->ccache);
923 if (ret != 0) {
924 TALLOC_FREE(ccc);
925 return ret;
928 cred->ccache = ccc;
929 cred->client_gss_creds = NULL;
930 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
931 return ret;
934 _PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
935 struct cli_credentials *src)
937 struct cli_credentials *dst;
938 int ret;
940 dst = talloc(mem_ctx, struct cli_credentials);
941 if (dst == NULL) {
942 return NULL;
945 *dst = *src;
947 ret = cli_credentials_shallow_ccache(dst);
948 if (ret != 0) {
949 TALLOC_FREE(dst);
950 return NULL;
953 return dst;
956 #if 0
957 static int smb_krb5_create_salt_principal(TALLOC_CTX *mem_ctx,
958 const char *samAccountName,
959 const char *realm,
960 const char **salt_principal,
961 const char **error_string)
963 char *machine_username;
964 bool is_machine_account = false;
965 char *upper_realm;
966 TALLOC_CTX *tmp_ctx;
967 int rc = -1;
969 if (samAccountName == NULL) {
970 *error_string = "Cannot determine salt principal, no "
971 "saltPrincipal or samAccountName specified";
972 return rc;
975 if (realm == NULL) {
976 *error_string = "Cannot make principal without a realm";
977 return rc;
980 tmp_ctx = talloc_new(mem_ctx);
981 if (tmp_ctx == NULL) {
982 *error_string = "Cannot allocate talloc context";
983 return rc;
986 upper_realm = strupper_talloc(tmp_ctx, realm);
987 if (upper_realm == NULL) {
988 *error_string = "Cannot allocate to upper case realm";
989 goto out;
992 machine_username = strlower_talloc(tmp_ctx, samAccountName);
993 if (!machine_username) {
994 *error_string = "Cannot duplicate samAccountName";
995 goto out;
998 if (machine_username[strlen(machine_username) - 1] == '$') {
999 machine_username[strlen(machine_username) - 1] = '\0';
1000 is_machine_account = true;
1003 if (is_machine_account) {
1004 char *lower_realm;
1006 lower_realm = strlower_talloc(tmp_ctx, realm);
1007 if (lower_realm == NULL) {
1008 *error_string = "Cannot allocate to lower case realm";
1009 goto out;
1012 *salt_principal = talloc_asprintf(mem_ctx,
1013 "host/%s.%s@%s",
1014 machine_username,
1015 lower_realm,
1016 upper_realm);
1017 } else {
1018 *salt_principal = talloc_asprintf(mem_ctx,
1019 "%s@%s",
1020 machine_username,
1021 upper_realm);
1023 if (*salt_principal == NULL) {
1024 *error_string = "Cannot create salt principal";
1025 goto out;
1028 rc = 0;
1029 out:
1030 talloc_free(tmp_ctx);
1031 return rc;
1033 #endif
1035 /* Get the keytab (actually, a container containing the krb5_keytab)
1036 * attached to this context. If this hasn't been done or set before,
1037 * it will be generated from the password.
1039 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
1040 struct loadparm_context *lp_ctx,
1041 struct keytab_container **_ktc)
1043 krb5_error_code ret;
1044 struct keytab_container *ktc;
1045 struct smb_krb5_context *smb_krb5_context;
1046 const char *keytab_name;
1047 krb5_keytab keytab;
1048 TALLOC_CTX *mem_ctx;
1049 const char *username = cli_credentials_get_username(cred);
1050 const char *upn = NULL;
1051 const char *realm = cli_credentials_get_realm(cred);
1052 char *salt_principal = NULL;
1053 bool is_computer = false;
1055 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
1056 cred->username_obtained))) {
1057 *_ktc = cred->keytab;
1058 return 0;
1061 if (cli_credentials_is_anonymous(cred)) {
1062 return EINVAL;
1065 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1066 &smb_krb5_context);
1067 if (ret) {
1068 return ret;
1071 mem_ctx = talloc_new(cred);
1072 if (!mem_ctx) {
1073 return ENOMEM;
1076 switch (cred->secure_channel_type) {
1077 case SEC_CHAN_WKSTA:
1078 case SEC_CHAN_BDC:
1079 case SEC_CHAN_RODC:
1080 is_computer = true;
1081 break;
1082 default:
1083 upn = cli_credentials_get_principal(cred, mem_ctx);
1084 if (upn == NULL) {
1085 TALLOC_FREE(mem_ctx);
1086 return ENOMEM;
1088 break;
1091 ret = smb_krb5_salt_principal(realm,
1092 username, /* sAMAccountName */
1093 upn, /* userPrincipalName */
1094 is_computer,
1095 mem_ctx,
1096 &salt_principal);
1097 if (ret) {
1098 talloc_free(mem_ctx);
1099 return ret;
1102 ret = smb_krb5_create_memory_keytab(mem_ctx,
1103 smb_krb5_context->krb5_context,
1104 cli_credentials_get_password(cred),
1105 username,
1106 realm,
1107 salt_principal,
1108 cli_credentials_get_kvno(cred),
1109 &keytab,
1110 &keytab_name);
1111 if (ret) {
1112 talloc_free(mem_ctx);
1113 return ret;
1116 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1117 keytab, keytab_name, &ktc);
1118 if (ret) {
1119 talloc_free(mem_ctx);
1120 return ret;
1123 cred->keytab_obtained = (MAX(cred->principal_obtained,
1124 cred->username_obtained));
1126 /* We make this keytab up based on a password. Therefore
1127 * match-by-key is acceptable, we can't match on the wrong
1128 * principal */
1129 ktc->password_based = true;
1131 talloc_steal(cred, ktc);
1132 cred->keytab = ktc;
1133 *_ktc = cred->keytab;
1134 talloc_free(mem_ctx);
1135 return ret;
1138 /* Given the name of a keytab (presumably in the format
1139 * FILE:/etc/krb5.keytab), open it and attach it */
1141 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
1142 struct loadparm_context *lp_ctx,
1143 const char *keytab_name,
1144 enum credentials_obtained obtained)
1146 krb5_error_code ret;
1147 struct keytab_container *ktc;
1148 struct smb_krb5_context *smb_krb5_context;
1149 TALLOC_CTX *mem_ctx;
1151 if (cred->keytab_obtained >= obtained) {
1152 return 0;
1155 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1156 if (ret) {
1157 return ret;
1160 mem_ctx = talloc_new(cred);
1161 if (!mem_ctx) {
1162 return ENOMEM;
1165 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1166 NULL, keytab_name, &ktc);
1167 if (ret) {
1168 return ret;
1171 cred->keytab_obtained = obtained;
1173 talloc_steal(cred, ktc);
1174 cred->keytab = ktc;
1175 talloc_free(mem_ctx);
1177 return ret;
1180 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1182 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
1183 struct loadparm_context *lp_ctx,
1184 struct gssapi_creds_container **_gcc)
1186 int ret = 0;
1187 OM_uint32 maj_stat, min_stat;
1188 struct gssapi_creds_container *gcc;
1189 struct keytab_container *ktc;
1190 struct smb_krb5_context *smb_krb5_context;
1191 TALLOC_CTX *mem_ctx;
1192 krb5_principal princ;
1193 const char *error_string;
1194 enum credentials_obtained obtained;
1196 mem_ctx = talloc_new(cred);
1197 if (!mem_ctx) {
1198 return ENOMEM;
1201 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1202 if (ret) {
1203 return ret;
1206 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1207 if (ret) {
1208 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1209 error_string));
1210 talloc_free(mem_ctx);
1211 return ret;
1214 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1215 talloc_free(mem_ctx);
1216 *_gcc = cred->server_gss_creds;
1217 return 0;
1220 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1221 if (ret) {
1222 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1223 return ret;
1226 gcc = talloc(cred, struct gssapi_creds_container);
1227 if (!gcc) {
1228 talloc_free(mem_ctx);
1229 return ENOMEM;
1232 if (ktc->password_based || obtained < CRED_SPECIFIED) {
1233 /* This creates a GSSAPI cred_id_t for match-by-key with only the keytab set */
1234 maj_stat = smb_gss_krb5_import_cred(&min_stat, smb_krb5_context->krb5_context,
1235 NULL, NULL, ktc->keytab,
1236 &gcc->creds);
1237 } else {
1238 /* This creates a GSSAPI cred_id_t with the principal and keytab set, matching by name */
1239 maj_stat = smb_gss_krb5_import_cred(&min_stat, smb_krb5_context->krb5_context,
1240 NULL, princ, ktc->keytab,
1241 &gcc->creds);
1243 if (maj_stat) {
1244 if (min_stat) {
1245 ret = min_stat;
1246 } else {
1247 ret = EINVAL;
1250 if (ret == 0) {
1251 cred->server_gss_creds_obtained = cred->keytab_obtained;
1252 talloc_set_destructor(gcc, free_gssapi_creds);
1253 cred->server_gss_creds = gcc;
1254 *_gcc = gcc;
1256 talloc_free(mem_ctx);
1257 return ret;
1260 /**
1261 * Set Kerberos KVNO
1264 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1265 int kvno)
1267 cred->kvno = kvno;
1271 * Return Kerberos KVNO
1274 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1276 return cred->kvno;
1280 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
1282 return cred->salt_principal;
1285 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
1287 talloc_free(cred->salt_principal);
1288 cred->salt_principal = talloc_strdup(cred, principal);
1291 /* The 'impersonate_principal' is used to allow one Kerberos principal
1292 * (and it's associated keytab etc) to impersonate another. The
1293 * ability to do this is controlled by the KDC, but it is generally
1294 * permitted to impersonate anyone to yourself. This allows any
1295 * member of the domain to get the groups of a user. This is also
1296 * known as S4U2Self */
1298 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1300 return cred->impersonate_principal;
1304 * The 'self_service' is the service principal that
1305 * represents the same object (by its objectSid)
1306 * as the client principal (typically our machine account).
1307 * When trying to impersonate 'impersonate_principal' with
1308 * S4U2Self.
1310 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1312 return cred->self_service;
1315 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1316 const char *principal,
1317 const char *self_service)
1319 talloc_free(cred->impersonate_principal);
1320 cred->impersonate_principal = talloc_strdup(cred, principal);
1321 talloc_free(cred->self_service);
1322 cred->self_service = talloc_strdup(cred, self_service);
1323 cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
1327 * when impersonating for S4U2proxy we need to set the target principal.
1328 * Similarly, we may only be authorized to do general impersonation to
1329 * some particular services.
1331 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1333 * NULL means that tickets will be obtained for the krbtgt service.
1336 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1338 return cred->target_service;
1341 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1343 talloc_free(cred->target_service);
1344 cred->target_service = talloc_strdup(cred, target_service);