s4:dsdb/schema_load: make error message more verbose
[Samba.git] / auth / credentials / credentials_krb5.c
blob489a959daf8107a4a0379635a075b2f1cb13d19c
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 _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
43 struct loadparm_context *lp_ctx,
44 struct smb_krb5_context **smb_krb5_context)
46 int ret;
47 if (cred->smb_krb5_context) {
48 *smb_krb5_context = cred->smb_krb5_context;
49 return 0;
52 ret = smb_krb5_init_context(cred, lp_ctx,
53 &cred->smb_krb5_context);
54 if (ret) {
55 cred->smb_krb5_context = NULL;
56 return ret;
58 *smb_krb5_context = cred->smb_krb5_context;
59 return 0;
62 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
63 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
65 _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
66 struct smb_krb5_context *smb_krb5_context)
68 if (smb_krb5_context == NULL) {
69 talloc_unlink(cred, cred->smb_krb5_context);
70 cred->smb_krb5_context = NULL;
71 return NT_STATUS_OK;
74 if (!talloc_reference(cred, smb_krb5_context)) {
75 return NT_STATUS_NO_MEMORY;
77 cred->smb_krb5_context = smb_krb5_context;
78 return NT_STATUS_OK;
81 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
82 struct ccache_container *ccache,
83 enum credentials_obtained obtained,
84 const char **error_string)
87 krb5_principal princ;
88 krb5_error_code ret;
89 char *name;
91 if (cred->ccache_obtained > obtained) {
92 return 0;
95 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
96 ccache->ccache, &princ);
98 if (ret) {
99 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
100 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
101 ret, cred));
102 return ret;
105 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
106 if (ret) {
107 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
108 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
109 ret, cred));
110 return ret;
113 cli_credentials_set_principal(cred, name, obtained);
115 free(name);
117 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
119 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
120 cred->ccache_obtained = obtained;
122 return 0;
125 /* Free a memory ccache */
126 static int free_mccache(struct ccache_container *ccc)
128 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
130 return 0;
133 /* Free a disk-based ccache */
134 static int free_dccache(struct ccache_container *ccc) {
135 krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
137 return 0;
140 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
141 struct loadparm_context *lp_ctx,
142 const char *name,
143 enum credentials_obtained obtained,
144 const char **error_string)
146 krb5_error_code ret;
147 krb5_principal princ;
148 struct ccache_container *ccc;
149 if (cred->ccache_obtained > obtained) {
150 return 0;
153 ccc = talloc(cred, struct ccache_container);
154 if (!ccc) {
155 (*error_string) = error_message(ENOMEM);
156 return ENOMEM;
159 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
160 &ccc->smb_krb5_context);
161 if (ret) {
162 (*error_string) = error_message(ret);
163 talloc_free(ccc);
164 return ret;
166 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
167 talloc_free(ccc);
168 (*error_string) = error_message(ENOMEM);
169 return ENOMEM;
172 if (name) {
173 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
174 if (ret) {
175 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
176 name,
177 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
178 ret, ccc));
179 talloc_free(ccc);
180 return ret;
182 } else {
183 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
184 if (ret) {
185 (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
186 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
187 ret, ccc));
188 talloc_free(ccc);
189 return ret;
193 talloc_set_destructor(ccc, free_dccache);
195 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
197 if (ret == 0) {
198 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
199 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
201 if (ret) {
202 (*error_string) = error_message(ret);
203 return ret;
206 cred->ccache = ccc;
207 cred->ccache_obtained = obtained;
208 talloc_steal(cred, ccc);
210 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
211 return 0;
213 return 0;
217 * Indicate the we failed to log in to this service/host with these
218 * credentials. The caller passes an unsigned int which they
219 * initialise to the number of times they would like to retry.
221 * This method is used to support re-trying with freshly fetched
222 * credentials in case a server is rebuilt while clients have
223 * non-expired tickets. When the client code gets a logon failure they
224 * throw away the existing credentials for the server and retry.
226 _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
227 const char *principal,
228 unsigned int *count)
230 struct ccache_container *ccc;
231 krb5_creds creds, creds2;
232 int ret;
234 if (principal == NULL) {
235 /* no way to delete if we don't know the principal */
236 return false;
239 ccc = cred->ccache;
240 if (ccc == NULL) {
241 /* not a kerberos connection */
242 return false;
245 if (*count > 0) {
246 /* We have already tried discarding the credentials */
247 return false;
249 (*count)++;
251 ZERO_STRUCT(creds);
252 ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
253 if (ret != 0) {
254 return false;
257 ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
258 if (ret != 0) {
259 /* don't retry - we didn't find these credentials to remove */
260 return false;
263 ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
264 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
265 if (ret != 0) {
266 /* don't retry - we didn't find these credentials to
267 * remove. Note that with the current backend this
268 * never happens, as it always returns 0 even if the
269 * creds don't exist, which is why we do a separate
270 * krb5_cc_retrieve_cred() above.
272 return false;
274 return true;
278 static int cli_credentials_new_ccache(struct cli_credentials *cred,
279 struct loadparm_context *lp_ctx,
280 char *ccache_name,
281 struct ccache_container **_ccc,
282 const char **error_string)
284 bool must_free_cc_name = false;
285 krb5_error_code ret;
286 struct ccache_container *ccc = talloc(cred, struct ccache_container);
287 if (!ccc) {
288 return ENOMEM;
291 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
292 &ccc->smb_krb5_context);
293 if (ret) {
294 talloc_free(ccc);
295 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
296 error_message(ret));
297 return ret;
299 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
300 talloc_free(ccc);
301 (*error_string) = strerror(ENOMEM);
302 return ENOMEM;
305 if (!ccache_name) {
306 must_free_cc_name = true;
308 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
309 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
310 (unsigned int)getpid(), ccc);
311 } else {
312 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
313 ccc);
316 if (!ccache_name) {
317 talloc_free(ccc);
318 (*error_string) = strerror(ENOMEM);
319 return ENOMEM;
323 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
324 &ccc->ccache);
325 if (ret) {
326 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
327 ccache_name,
328 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
329 ret, ccc));
330 talloc_free(ccache_name);
331 talloc_free(ccc);
332 return ret;
335 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
336 talloc_set_destructor(ccc, free_mccache);
337 } else {
338 talloc_set_destructor(ccc, free_dccache);
341 if (must_free_cc_name) {
342 talloc_free(ccache_name);
345 *_ccc = ccc;
347 return 0;
350 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
351 struct tevent_context *event_ctx,
352 struct loadparm_context *lp_ctx,
353 char *ccache_name,
354 struct ccache_container **ccc,
355 const char **error_string)
357 krb5_error_code ret;
358 enum credentials_obtained obtained;
360 if (cred->machine_account_pending) {
361 cli_credentials_set_machine_account(cred, lp_ctx);
364 if (cred->ccache_obtained >= cred->ccache_threshold &&
365 cred->ccache_obtained > CRED_UNINITIALISED) {
366 time_t lifetime;
367 bool expired = false;
368 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
369 cred->ccache->ccache, &lifetime);
370 if (ret == KRB5_CC_END) {
371 /* If we have a particular ccache set, without
372 * an initial ticket, then assume there is a
373 * good reason */
374 } else if (ret == 0) {
375 if (lifetime == 0) {
376 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
377 cli_credentials_get_principal(cred, cred)));
378 expired = true;
379 } else if (lifetime < 300) {
380 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
381 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
382 expired = true;
384 } else {
385 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
386 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
387 ret, cred));
388 return ret;
391 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
392 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
394 if (!expired) {
395 *ccc = cred->ccache;
396 return 0;
399 if (cli_credentials_is_anonymous(cred)) {
400 (*error_string) = "Cannot get anonymous kerberos credentials";
401 return EINVAL;
404 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
405 if (ret) {
406 return ret;
409 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
410 if (ret) {
411 return ret;
414 ret = cli_credentials_set_from_ccache(cred, *ccc,
415 obtained, error_string);
417 cred->ccache = *ccc;
418 cred->ccache_obtained = cred->principal_obtained;
419 if (ret) {
420 return ret;
422 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
423 return 0;
426 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
427 struct tevent_context *event_ctx,
428 struct loadparm_context *lp_ctx,
429 struct ccache_container **ccc,
430 const char **error_string)
432 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
435 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
436 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
438 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
439 talloc_unlink(cred, cred->client_gss_creds);
440 cred->client_gss_creds = NULL;
442 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
445 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
446 enum credentials_obtained obtained)
448 /* If the caller just changed the username/password etc, then
449 * any cached credentials are now invalid */
450 if (obtained >= cred->client_gss_creds_obtained) {
451 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
452 talloc_unlink(cred, cred->client_gss_creds);
453 cred->client_gss_creds = NULL;
455 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
457 /* Now that we know that the data is 'this specified', then
458 * don't allow something less 'known' to be returned as a
459 * ccache. Ie, if the username is on the command line, we
460 * don't want to later guess to use a file-based ccache */
461 if (obtained > cred->client_gss_creds_threshold) {
462 cred->client_gss_creds_threshold = obtained;
466 /* We have good reason to think this CCACHE is invalid. Blow it away */
467 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
469 if (cred->ccache_obtained > CRED_UNINITIALISED) {
470 talloc_unlink(cred, cred->ccache);
471 cred->ccache = NULL;
473 cred->ccache_obtained = CRED_UNINITIALISED;
475 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
478 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
479 enum credentials_obtained obtained)
481 /* If the caller just changed the username/password etc, then
482 * any cached credentials are now invalid */
483 if (obtained >= cred->ccache_obtained) {
484 if (cred->ccache_obtained > CRED_UNINITIALISED) {
485 talloc_unlink(cred, cred->ccache);
486 cred->ccache = NULL;
488 cred->ccache_obtained = CRED_UNINITIALISED;
490 /* Now that we know that the data is 'this specified', then
491 * don't allow something less 'known' to be returned as a
492 * ccache. i.e, if the username is on the command line, we
493 * don't want to later guess to use a file-based ccache */
494 if (obtained > cred->ccache_threshold) {
495 cred->ccache_threshold = obtained;
498 cli_credentials_invalidate_client_gss_creds(cred,
499 obtained);
502 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
504 OM_uint32 min_stat;
505 (void)gss_release_cred(&min_stat, &gcc->creds);
506 return 0;
509 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
510 struct tevent_context *event_ctx,
511 struct loadparm_context *lp_ctx,
512 struct gssapi_creds_container **_gcc,
513 const char **error_string)
515 int ret = 0;
516 OM_uint32 maj_stat, min_stat;
517 struct gssapi_creds_container *gcc;
518 struct ccache_container *ccache;
519 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
520 krb5_enctype *etypes = NULL;
522 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
523 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
524 bool expired = false;
525 OM_uint32 lifetime = 0;
526 gss_cred_usage_t usage = 0;
527 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
528 NULL, &lifetime, &usage, NULL);
529 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
530 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
531 expired = true;
532 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
533 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
534 expired = true;
535 } else if (maj_stat != GSS_S_COMPLETE) {
536 *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
537 gssapi_error_string(cred, maj_stat, min_stat, NULL));
538 return EINVAL;
540 if (expired) {
541 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
542 } else {
543 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
544 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
546 *_gcc = cred->client_gss_creds;
547 return 0;
551 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
552 &ccache, error_string);
553 if (ret) {
554 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
555 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
556 } else {
557 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
559 return ret;
562 gcc = talloc(cred, struct gssapi_creds_container);
563 if (!gcc) {
564 (*error_string) = error_message(ENOMEM);
565 return ENOMEM;
568 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
569 &gcc->creds);
570 if ((maj_stat == GSS_S_FAILURE) && (min_stat == (OM_uint32)KRB5_CC_END || min_stat == (OM_uint32) KRB5_CC_NOTFOUND)) {
571 /* This CCACHE is no good. Ensure we don't use it again */
572 cli_credentials_unconditionally_invalidate_ccache(cred);
574 /* Now try again to get a ccache */
575 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
576 &ccache, error_string);
577 if (ret) {
578 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
579 return ret;
582 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
583 &gcc->creds);
587 if (maj_stat) {
588 talloc_free(gcc);
589 if (min_stat) {
590 ret = min_stat;
591 } else {
592 ret = EINVAL;
594 (*error_string) = talloc_asprintf(cred, "gss_krb5_import_cred failed: %s", error_message(ret));
595 return ret;
598 #ifdef SAMBA4_USES_HEIMDAL /* MIT lacks krb5_get_default_in_tkt_etypes */
600 * transfer the enctypes from the smb_krb5_context to the gssapi layer
602 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
603 * to configure the enctypes via the krb5.conf.
605 * And the gss_init_sec_context() creates it's own krb5_context and
606 * the TGS-REQ had all enctypes in it and only the ones configured
607 * and used for the AS-REQ, so it wasn't possible to disable the usage
608 * of AES keys.
610 min_stat = krb5_get_default_in_tkt_etypes(ccache->smb_krb5_context->krb5_context,
611 KRB5_PDU_NONE,
612 &etypes);
613 if (min_stat == 0) {
614 OM_uint32 num_ktypes;
616 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
618 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
619 num_ktypes,
620 (int32_t *) etypes);
621 krb5_xfree (etypes);
622 if (maj_stat) {
623 talloc_free(gcc);
624 if (min_stat) {
625 ret = min_stat;
626 } else {
627 ret = EINVAL;
629 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
630 return ret;
633 #endif
634 #ifdef SAMBA4_USES_HEIMDAL /* MIT lacks GSS_KRB5_CRED_NO_CI_FLAGS_X */
636 /* don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG */
637 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
638 GSS_KRB5_CRED_NO_CI_FLAGS_X,
639 &empty_buffer);
640 if (maj_stat) {
641 talloc_free(gcc);
642 if (min_stat) {
643 ret = min_stat;
644 } else {
645 ret = EINVAL;
647 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
648 return ret;
650 #endif
651 cred->client_gss_creds_obtained = cred->ccache_obtained;
652 talloc_set_destructor(gcc, free_gssapi_creds);
653 cred->client_gss_creds = gcc;
654 *_gcc = gcc;
655 return 0;
659 Set a gssapi cred_id_t into the credentials system. (Client case)
661 This grabs the credentials both 'intact' and getting the krb5
662 ccache out of it. This routine can be generalised in future for
663 the case where we deal with GSSAPI mechs other than krb5.
665 On sucess, the caller must not free gssapi_cred, as it now belongs
666 to the credentials system.
669 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
670 struct loadparm_context *lp_ctx,
671 gss_cred_id_t gssapi_cred,
672 enum credentials_obtained obtained,
673 const char **error_string)
675 int ret;
676 OM_uint32 maj_stat, min_stat;
677 struct ccache_container *ccc;
678 struct gssapi_creds_container *gcc;
679 if (cred->client_gss_creds_obtained > obtained) {
680 return 0;
683 gcc = talloc(cred, struct gssapi_creds_container);
684 if (!gcc) {
685 (*error_string) = error_message(ENOMEM);
686 return ENOMEM;
689 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
690 if (ret != 0) {
691 return ret;
694 maj_stat = gss_krb5_copy_ccache(&min_stat,
695 gssapi_cred, ccc->ccache);
696 if (maj_stat) {
697 if (min_stat) {
698 ret = min_stat;
699 } else {
700 ret = EINVAL;
702 if (ret) {
703 (*error_string) = error_message(ENOMEM);
707 if (ret == 0) {
708 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
710 cred->ccache = ccc;
711 cred->ccache_obtained = obtained;
712 if (ret == 0) {
713 gcc->creds = gssapi_cred;
714 talloc_set_destructor(gcc, free_gssapi_creds);
716 /* set the clinet_gss_creds_obtained here, as it just
717 got set to UNINITIALISED by the calls above */
718 cred->client_gss_creds_obtained = obtained;
719 cred->client_gss_creds = gcc;
721 return ret;
724 /* Get the keytab (actually, a container containing the krb5_keytab)
725 * attached to this context. If this hasn't been done or set before,
726 * it will be generated from the password.
728 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
729 struct loadparm_context *lp_ctx,
730 struct keytab_container **_ktc)
732 krb5_error_code ret;
733 struct keytab_container *ktc;
734 struct smb_krb5_context *smb_krb5_context;
735 const char *keytab_name;
736 krb5_keytab keytab;
737 TALLOC_CTX *mem_ctx;
739 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
740 cred->username_obtained))) {
741 *_ktc = cred->keytab;
742 return 0;
745 if (cli_credentials_is_anonymous(cred)) {
746 return EINVAL;
749 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
750 &smb_krb5_context);
751 if (ret) {
752 return ret;
755 mem_ctx = talloc_new(cred);
756 if (!mem_ctx) {
757 return ENOMEM;
760 ret = smb_krb5_create_memory_keytab(mem_ctx,
761 smb_krb5_context->krb5_context,
762 cli_credentials_get_password(cred),
763 cli_credentials_get_username(cred),
764 cli_credentials_get_realm(cred),
765 cli_credentials_get_kvno(cred),
766 &keytab, &keytab_name);
767 if (ret) {
768 talloc_free(mem_ctx);
769 return ret;
772 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
773 keytab, keytab_name, &ktc);
774 if (ret) {
775 talloc_free(mem_ctx);
776 return ret;
779 cred->keytab_obtained = (MAX(cred->principal_obtained,
780 cred->username_obtained));
782 /* We make this keytab up based on a password. Therefore
783 * match-by-key is acceptable, we can't match on the wrong
784 * principal */
785 ktc->password_based = true;
787 talloc_steal(cred, ktc);
788 cred->keytab = ktc;
789 *_ktc = cred->keytab;
790 talloc_free(mem_ctx);
791 return ret;
794 /* Given the name of a keytab (presumably in the format
795 * FILE:/etc/krb5.keytab), open it and attach it */
797 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
798 struct loadparm_context *lp_ctx,
799 const char *keytab_name,
800 enum credentials_obtained obtained)
802 krb5_error_code ret;
803 struct keytab_container *ktc;
804 struct smb_krb5_context *smb_krb5_context;
805 TALLOC_CTX *mem_ctx;
807 if (cred->keytab_obtained >= obtained) {
808 return 0;
811 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
812 if (ret) {
813 return ret;
816 mem_ctx = talloc_new(cred);
817 if (!mem_ctx) {
818 return ENOMEM;
821 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
822 NULL, keytab_name, &ktc);
823 if (ret) {
824 return ret;
827 cred->keytab_obtained = obtained;
829 talloc_steal(cred, ktc);
830 cred->keytab = ktc;
831 talloc_free(mem_ctx);
833 return ret;
836 /* Get server gss credentials (in gsskrb5, this means the keytab) */
838 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
839 struct loadparm_context *lp_ctx,
840 struct gssapi_creds_container **_gcc)
842 int ret = 0;
843 OM_uint32 maj_stat, min_stat;
844 struct gssapi_creds_container *gcc;
845 struct keytab_container *ktc;
846 struct smb_krb5_context *smb_krb5_context;
847 TALLOC_CTX *mem_ctx;
848 krb5_principal princ;
849 const char *error_string;
850 enum credentials_obtained obtained;
852 mem_ctx = talloc_new(cred);
853 if (!mem_ctx) {
854 return ENOMEM;
857 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
858 if (ret) {
859 return ret;
862 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
863 if (ret) {
864 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
865 error_string));
866 talloc_free(mem_ctx);
867 return ret;
870 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
871 talloc_free(mem_ctx);
872 *_gcc = cred->server_gss_creds;
873 return 0;
876 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
877 if (ret) {
878 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
879 return ret;
882 gcc = talloc(cred, struct gssapi_creds_container);
883 if (!gcc) {
884 talloc_free(mem_ctx);
885 return ENOMEM;
888 if (ktc->password_based || obtained < CRED_SPECIFIED) {
889 /* This creates a GSSAPI cred_id_t for match-by-key with only the keytab set */
890 maj_stat = gss_krb5_import_cred(&min_stat, NULL, NULL, ktc->keytab,
891 &gcc->creds);
892 } else {
893 /* This creates a GSSAPI cred_id_t with the principal and keytab set, matching by name */
894 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
895 &gcc->creds);
897 if (maj_stat) {
898 if (min_stat) {
899 ret = min_stat;
900 } else {
901 ret = EINVAL;
904 if (ret == 0) {
905 cred->server_gss_creds_obtained = cred->keytab_obtained;
906 talloc_set_destructor(gcc, free_gssapi_creds);
907 cred->server_gss_creds = gcc;
908 *_gcc = gcc;
910 talloc_free(mem_ctx);
911 return ret;
914 /**
915 * Set Kerberos KVNO
918 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
919 int kvno)
921 cred->kvno = kvno;
925 * Return Kerberos KVNO
928 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
930 return cred->kvno;
934 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
936 return cred->salt_principal;
939 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
941 talloc_free(cred->salt_principal);
942 cred->salt_principal = talloc_strdup(cred, principal);
945 /* The 'impersonate_principal' is used to allow one Kerberos principal
946 * (and it's associated keytab etc) to impersonate another. The
947 * ability to do this is controlled by the KDC, but it is generally
948 * permitted to impersonate anyone to yourself. This allows any
949 * member of the domain to get the groups of a user. This is also
950 * known as S4U2Self */
952 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
954 return cred->impersonate_principal;
958 * The 'self_service' is the service principal that
959 * represents the same object (by its objectSid)
960 * as the client principal (typically our machine account).
961 * When trying to impersonate 'impersonate_principal' with
962 * S4U2Self.
964 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
966 return cred->self_service;
969 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
970 const char *principal,
971 const char *self_service)
973 talloc_free(cred->impersonate_principal);
974 cred->impersonate_principal = talloc_strdup(cred, principal);
975 talloc_free(cred->self_service);
976 cred->self_service = talloc_strdup(cred, self_service);
977 cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
981 * when impersonating for S4U2proxy we need to set the target principal.
982 * Similarly, we may only be authorized to do general impersonation to
983 * some particular services.
985 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
987 * NULL means that tickets will be obtained for the krbtgt service.
990 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
992 return cred->target_service;
995 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
997 talloc_free(cred->target_service);
998 cred->target_service = talloc_strdup(cred, target_service);