rpcclient3: Factor out cli_rpc_pipe_open_bind_schannel()
[Samba.git] / auth / credentials / credentials_krb5.c
blobb88497dcace5084fe1d47178ae017644e71a65ab
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 /* Get the keytab (actually, a container containing the krb5_keytab)
957 * attached to this context. If this hasn't been done or set before,
958 * it will be generated from the password.
960 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
961 struct loadparm_context *lp_ctx,
962 struct keytab_container **_ktc)
964 krb5_error_code ret;
965 struct keytab_container *ktc;
966 struct smb_krb5_context *smb_krb5_context;
967 const char *keytab_name;
968 krb5_keytab keytab;
969 TALLOC_CTX *mem_ctx;
970 const char *username = cli_credentials_get_username(cred);
971 const char *upn = NULL;
972 const char *realm = cli_credentials_get_realm(cred);
973 char *salt_principal = NULL;
974 bool is_computer = false;
976 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
977 cred->username_obtained))) {
978 *_ktc = cred->keytab;
979 return 0;
982 if (cli_credentials_is_anonymous(cred)) {
983 return EINVAL;
986 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
987 &smb_krb5_context);
988 if (ret) {
989 return ret;
992 mem_ctx = talloc_new(cred);
993 if (!mem_ctx) {
994 return ENOMEM;
997 switch (cred->secure_channel_type) {
998 case SEC_CHAN_WKSTA:
999 case SEC_CHAN_BDC:
1000 case SEC_CHAN_RODC:
1001 is_computer = true;
1002 break;
1003 default:
1004 upn = cli_credentials_get_principal(cred, mem_ctx);
1005 if (upn == NULL) {
1006 TALLOC_FREE(mem_ctx);
1007 return ENOMEM;
1009 break;
1012 ret = smb_krb5_salt_principal(realm,
1013 username, /* sAMAccountName */
1014 upn, /* userPrincipalName */
1015 is_computer,
1016 mem_ctx,
1017 &salt_principal);
1018 if (ret) {
1019 talloc_free(mem_ctx);
1020 return ret;
1023 ret = smb_krb5_create_memory_keytab(mem_ctx,
1024 smb_krb5_context->krb5_context,
1025 cli_credentials_get_password(cred),
1026 username,
1027 realm,
1028 salt_principal,
1029 cli_credentials_get_kvno(cred),
1030 &keytab,
1031 &keytab_name);
1032 if (ret) {
1033 talloc_free(mem_ctx);
1034 return ret;
1037 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1038 keytab, keytab_name, &ktc);
1039 if (ret) {
1040 talloc_free(mem_ctx);
1041 return ret;
1044 cred->keytab_obtained = (MAX(cred->principal_obtained,
1045 cred->username_obtained));
1047 /* We make this keytab up based on a password. Therefore
1048 * match-by-key is acceptable, we can't match on the wrong
1049 * principal */
1050 ktc->password_based = true;
1052 talloc_steal(cred, ktc);
1053 cred->keytab = ktc;
1054 *_ktc = cred->keytab;
1055 talloc_free(mem_ctx);
1056 return ret;
1059 /* Given the name of a keytab (presumably in the format
1060 * FILE:/etc/krb5.keytab), open it and attach it */
1062 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
1063 struct loadparm_context *lp_ctx,
1064 const char *keytab_name,
1065 enum credentials_obtained obtained)
1067 krb5_error_code ret;
1068 struct keytab_container *ktc;
1069 struct smb_krb5_context *smb_krb5_context;
1070 TALLOC_CTX *mem_ctx;
1072 if (cred->keytab_obtained >= obtained) {
1073 return 0;
1076 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1077 if (ret) {
1078 return ret;
1081 mem_ctx = talloc_new(cred);
1082 if (!mem_ctx) {
1083 return ENOMEM;
1086 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1087 NULL, keytab_name, &ktc);
1088 if (ret) {
1089 return ret;
1092 cred->keytab_obtained = obtained;
1094 talloc_steal(cred, ktc);
1095 cred->keytab = ktc;
1096 talloc_free(mem_ctx);
1098 return ret;
1101 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1103 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
1104 struct loadparm_context *lp_ctx,
1105 struct gssapi_creds_container **_gcc)
1107 int ret = 0;
1108 OM_uint32 maj_stat, min_stat;
1109 struct gssapi_creds_container *gcc;
1110 struct keytab_container *ktc;
1111 struct smb_krb5_context *smb_krb5_context;
1112 TALLOC_CTX *mem_ctx;
1113 krb5_principal princ;
1114 const char *error_string;
1115 enum credentials_obtained obtained;
1117 mem_ctx = talloc_new(cred);
1118 if (!mem_ctx) {
1119 return ENOMEM;
1122 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1123 if (ret) {
1124 return ret;
1127 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1128 if (ret) {
1129 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1130 error_string));
1131 talloc_free(mem_ctx);
1132 return ret;
1135 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1136 talloc_free(mem_ctx);
1137 *_gcc = cred->server_gss_creds;
1138 return 0;
1141 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1142 if (ret) {
1143 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1144 return ret;
1147 gcc = talloc(cred, struct gssapi_creds_container);
1148 if (!gcc) {
1149 talloc_free(mem_ctx);
1150 return ENOMEM;
1153 if (ktc->password_based || obtained < CRED_SPECIFIED) {
1154 /* This creates a GSSAPI cred_id_t for match-by-key with only the keytab set */
1155 maj_stat = smb_gss_krb5_import_cred(&min_stat, smb_krb5_context->krb5_context,
1156 NULL, NULL, ktc->keytab,
1157 &gcc->creds);
1158 } else {
1159 /* This creates a GSSAPI cred_id_t with the principal and keytab set, matching by name */
1160 maj_stat = smb_gss_krb5_import_cred(&min_stat, smb_krb5_context->krb5_context,
1161 NULL, princ, ktc->keytab,
1162 &gcc->creds);
1164 if (maj_stat) {
1165 if (min_stat) {
1166 ret = min_stat;
1167 } else {
1168 ret = EINVAL;
1171 if (ret == 0) {
1172 cred->server_gss_creds_obtained = cred->keytab_obtained;
1173 talloc_set_destructor(gcc, free_gssapi_creds);
1174 cred->server_gss_creds = gcc;
1175 *_gcc = gcc;
1177 talloc_free(mem_ctx);
1178 return ret;
1181 /**
1182 * Set Kerberos KVNO
1185 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1186 int kvno)
1188 cred->kvno = kvno;
1192 * Return Kerberos KVNO
1195 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1197 return cred->kvno;
1201 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
1203 return cred->salt_principal;
1206 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
1208 talloc_free(cred->salt_principal);
1209 cred->salt_principal = talloc_strdup(cred, principal);
1212 /* The 'impersonate_principal' is used to allow one Kerberos principal
1213 * (and it's associated keytab etc) to impersonate another. The
1214 * ability to do this is controlled by the KDC, but it is generally
1215 * permitted to impersonate anyone to yourself. This allows any
1216 * member of the domain to get the groups of a user. This is also
1217 * known as S4U2Self */
1219 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1221 return cred->impersonate_principal;
1225 * The 'self_service' is the service principal that
1226 * represents the same object (by its objectSid)
1227 * as the client principal (typically our machine account).
1228 * When trying to impersonate 'impersonate_principal' with
1229 * S4U2Self.
1231 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1233 return cred->self_service;
1236 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1237 const char *principal,
1238 const char *self_service)
1240 talloc_free(cred->impersonate_principal);
1241 cred->impersonate_principal = talloc_strdup(cred, principal);
1242 talloc_free(cred->self_service);
1243 cred->self_service = talloc_strdup(cred, self_service);
1244 cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
1248 * when impersonating for S4U2proxy we need to set the target principal.
1249 * Similarly, we may only be authorized to do general impersonation to
1250 * some particular services.
1252 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1254 * NULL means that tickets will be obtained for the krbtgt service.
1257 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1259 return cred->target_service;
1262 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1264 talloc_free(cred->target_service);
1265 cred->target_service = talloc_strdup(cred, target_service);