librpc: Shorten dcerpc_binding_handle_call a bit
[Samba/bjacke.git] / auth / credentials / credentials_krb5.c
blobcc51f56d79b2a0f0d7027491bc766ecbca258792
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_proto.h"
30 #include "auth/credentials/credentials_krb5.h"
31 #include "auth/kerberos/kerberos_credentials.h"
32 #include "auth/kerberos/kerberos_srv_keytab.h"
33 #include "auth/kerberos/kerberos_util.h"
34 #include "auth/kerberos/pac_utils.h"
35 #include "param/param.h"
37 static void cli_credentials_invalidate_client_gss_creds(
38 struct cli_credentials *cred,
39 enum credentials_obtained obtained);
41 _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
42 struct loadparm_context *lp_ctx,
43 struct smb_krb5_context **smb_krb5_context)
45 int ret;
46 if (cred->smb_krb5_context) {
47 *smb_krb5_context = cred->smb_krb5_context;
48 return 0;
51 ret = smb_krb5_init_context(cred, NULL, lp_ctx,
52 &cred->smb_krb5_context);
53 if (ret) {
54 cred->smb_krb5_context = NULL;
55 return ret;
57 *smb_krb5_context = cred->smb_krb5_context;
58 return 0;
61 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
62 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
64 _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
65 struct smb_krb5_context *smb_krb5_context)
67 if (smb_krb5_context == NULL) {
68 talloc_unlink(cred, cred->smb_krb5_context);
69 cred->smb_krb5_context = NULL;
70 return NT_STATUS_OK;
73 if (!talloc_reference(cred, smb_krb5_context)) {
74 return NT_STATUS_NO_MEMORY;
76 cred->smb_krb5_context = smb_krb5_context;
77 return NT_STATUS_OK;
80 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
81 struct ccache_container *ccache,
82 enum credentials_obtained obtained,
83 const char **error_string)
86 krb5_principal princ;
87 krb5_error_code ret;
88 char *name;
90 if (cred->ccache_obtained > obtained) {
91 return 0;
94 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
95 ccache->ccache, &princ);
97 if (ret) {
98 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
99 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
100 ret, cred));
101 return ret;
104 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
105 if (ret) {
106 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
107 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
108 ret, cred));
109 return ret;
112 cli_credentials_set_principal(cred, name, obtained);
114 free(name);
116 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
118 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
119 cred->ccache_obtained = obtained;
121 return 0;
124 /* Free a memory ccache */
125 static int free_mccache(struct ccache_container *ccc)
127 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
129 return 0;
132 /* Free a disk-based ccache */
133 static int free_dccache(struct ccache_container *ccc) {
134 krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
136 return 0;
139 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
140 struct loadparm_context *lp_ctx,
141 const char *name,
142 enum credentials_obtained obtained,
143 const char **error_string)
145 krb5_error_code ret;
146 krb5_principal princ;
147 struct ccache_container *ccc;
148 if (cred->ccache_obtained > obtained) {
149 return 0;
152 ccc = talloc(cred, struct ccache_container);
153 if (!ccc) {
154 (*error_string) = error_message(ENOMEM);
155 return ENOMEM;
158 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
159 &ccc->smb_krb5_context);
160 if (ret) {
161 (*error_string) = error_message(ret);
162 talloc_free(ccc);
163 return ret;
165 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
166 talloc_free(ccc);
167 (*error_string) = error_message(ENOMEM);
168 return ENOMEM;
171 if (name) {
172 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
173 if (ret) {
174 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
175 name,
176 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
177 ret, ccc));
178 talloc_free(ccc);
179 return ret;
181 } else {
182 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
183 if (ret) {
184 (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
185 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
186 ret, ccc));
187 talloc_free(ccc);
188 return ret;
192 talloc_set_destructor(ccc, free_dccache);
194 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
196 if (ret == 0) {
197 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
198 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
200 if (ret) {
201 (*error_string) = error_message(ret);
202 return ret;
205 cred->ccache = ccc;
206 cred->ccache_obtained = obtained;
207 talloc_steal(cred, ccc);
209 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
210 return 0;
212 return 0;
216 * Indicate the we failed to log in to this service/host with these
217 * credentials. The caller passes an unsigned int which they
218 * initialise to the number of times they would like to retry.
220 * This method is used to support re-trying with freshly fetched
221 * credentials in case a server is rebuilt while clients have
222 * non-expired tickets. When the client code gets a logon failure they
223 * throw away the existing credentials for the server and retry.
225 _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
226 const char *principal,
227 unsigned int *count)
229 struct ccache_container *ccc;
230 krb5_creds creds, creds2;
231 int ret;
233 if (principal == NULL) {
234 /* no way to delete if we don't know the principal */
235 return false;
238 ccc = cred->ccache;
239 if (ccc == NULL) {
240 /* not a kerberos connection */
241 return false;
244 if (*count > 0) {
245 /* We have already tried discarding the credentials */
246 return false;
248 (*count)++;
250 ZERO_STRUCT(creds);
251 ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
252 if (ret != 0) {
253 return false;
256 ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
257 if (ret != 0) {
258 /* don't retry - we didn't find these credentials to remove */
259 return false;
262 ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
263 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
264 if (ret != 0) {
265 /* don't retry - we didn't find these credentials to
266 * remove. Note that with the current backend this
267 * never happens, as it always returns 0 even if the
268 * creds don't exist, which is why we do a separate
269 * krb5_cc_retrieve_cred() above.
271 return false;
273 return true;
277 static int cli_credentials_new_ccache(struct cli_credentials *cred,
278 struct loadparm_context *lp_ctx,
279 char *ccache_name,
280 struct ccache_container **_ccc,
281 const char **error_string)
283 bool must_free_cc_name = false;
284 krb5_error_code ret;
285 struct ccache_container *ccc = talloc(cred, struct ccache_container);
286 if (!ccc) {
287 return ENOMEM;
290 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
291 &ccc->smb_krb5_context);
292 if (ret) {
293 talloc_free(ccc);
294 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
295 error_message(ret));
296 return ret;
298 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
299 talloc_free(ccc);
300 (*error_string) = strerror(ENOMEM);
301 return ENOMEM;
304 if (!ccache_name) {
305 must_free_cc_name = true;
307 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
308 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
309 (unsigned int)getpid(), ccc);
310 } else {
311 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
312 ccc);
315 if (!ccache_name) {
316 talloc_free(ccc);
317 (*error_string) = strerror(ENOMEM);
318 return ENOMEM;
322 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
323 &ccc->ccache);
324 if (ret) {
325 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
326 ccache_name,
327 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
328 ret, ccc));
329 talloc_free(ccache_name);
330 talloc_free(ccc);
331 return ret;
334 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
335 talloc_set_destructor(ccc, free_mccache);
336 } else {
337 talloc_set_destructor(ccc, free_dccache);
340 if (must_free_cc_name) {
341 talloc_free(ccache_name);
344 *_ccc = ccc;
346 return 0;
349 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
350 struct tevent_context *event_ctx,
351 struct loadparm_context *lp_ctx,
352 char *ccache_name,
353 struct ccache_container **ccc,
354 const char **error_string)
356 krb5_error_code ret;
357 enum credentials_obtained obtained;
359 if (cred->machine_account_pending) {
360 cli_credentials_set_machine_account(cred, lp_ctx);
363 if (cred->ccache_obtained >= cred->ccache_threshold &&
364 cred->ccache_obtained > CRED_UNINITIALISED) {
365 time_t lifetime;
366 bool expired = false;
367 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
368 cred->ccache->ccache, &lifetime);
369 if (ret == KRB5_CC_END) {
370 /* If we have a particular ccache set, without
371 * an initial ticket, then assume there is a
372 * good reason */
373 } else if (ret == 0) {
374 if (lifetime == 0) {
375 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
376 cli_credentials_get_principal(cred, cred)));
377 expired = true;
378 } else if (lifetime < 300) {
379 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
380 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
381 expired = true;
383 } else {
384 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
385 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
386 ret, cred));
387 return ret;
390 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
391 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
393 if (!expired) {
394 *ccc = cred->ccache;
395 return 0;
398 if (cli_credentials_is_anonymous(cred)) {
399 (*error_string) = "Cannot get anonymous kerberos credentials";
400 return EINVAL;
403 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
404 if (ret) {
405 return ret;
408 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
409 if (ret) {
410 return ret;
413 ret = cli_credentials_set_from_ccache(cred, *ccc,
414 obtained, error_string);
416 cred->ccache = *ccc;
417 cred->ccache_obtained = cred->principal_obtained;
418 if (ret) {
419 return ret;
421 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
422 return 0;
425 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
426 struct tevent_context *event_ctx,
427 struct loadparm_context *lp_ctx,
428 struct ccache_container **ccc,
429 const char **error_string)
431 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
434 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
435 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
437 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
438 talloc_unlink(cred, cred->client_gss_creds);
439 cred->client_gss_creds = NULL;
441 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
444 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
445 enum credentials_obtained obtained)
447 /* If the caller just changed the username/password etc, then
448 * any cached credentials are now invalid */
449 if (obtained >= cred->client_gss_creds_obtained) {
450 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
451 talloc_unlink(cred, cred->client_gss_creds);
452 cred->client_gss_creds = NULL;
454 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
456 /* Now that we know that the data is 'this specified', then
457 * don't allow something less 'known' to be returned as a
458 * ccache. Ie, if the username is on the command line, we
459 * don't want to later guess to use a file-based ccache */
460 if (obtained > cred->client_gss_creds_threshold) {
461 cred->client_gss_creds_threshold = obtained;
465 /* We have good reason to think this CCACHE is invalid. Blow it away */
466 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
468 if (cred->ccache_obtained > CRED_UNINITIALISED) {
469 talloc_unlink(cred, cred->ccache);
470 cred->ccache = NULL;
472 cred->ccache_obtained = CRED_UNINITIALISED;
474 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
477 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
478 enum credentials_obtained obtained)
480 /* If the caller just changed the username/password etc, then
481 * any cached credentials are now invalid */
482 if (obtained >= cred->ccache_obtained) {
483 if (cred->ccache_obtained > CRED_UNINITIALISED) {
484 talloc_unlink(cred, cred->ccache);
485 cred->ccache = NULL;
487 cred->ccache_obtained = CRED_UNINITIALISED;
489 /* Now that we know that the data is 'this specified', then
490 * don't allow something less 'known' to be returned as a
491 * ccache. i.e, if the username is on the command line, we
492 * don't want to later guess to use a file-based ccache */
493 if (obtained > cred->ccache_threshold) {
494 cred->ccache_threshold = obtained;
497 cli_credentials_invalidate_client_gss_creds(cred,
498 obtained);
501 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
503 OM_uint32 min_stat;
504 (void)gss_release_cred(&min_stat, &gcc->creds);
505 return 0;
508 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
509 struct tevent_context *event_ctx,
510 struct loadparm_context *lp_ctx,
511 struct gssapi_creds_container **_gcc,
512 const char **error_string)
514 int ret = 0;
515 OM_uint32 maj_stat, min_stat;
516 struct gssapi_creds_container *gcc;
517 struct ccache_container *ccache;
518 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
519 krb5_enctype *etypes = NULL;
521 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
522 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
523 bool expired = false;
524 OM_uint32 lifetime = 0;
525 gss_cred_usage_t usage = 0;
526 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
527 NULL, &lifetime, &usage, NULL);
528 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
529 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
530 expired = true;
531 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
532 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
533 expired = true;
534 } else if (maj_stat != GSS_S_COMPLETE) {
535 *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
536 gssapi_error_string(cred, maj_stat, min_stat, NULL));
537 return EINVAL;
539 if (expired) {
540 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
541 } else {
542 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
543 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
545 *_gcc = cred->client_gss_creds;
546 return 0;
550 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
551 &ccache, error_string);
552 if (ret) {
553 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
554 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
555 } else {
556 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
558 return ret;
561 gcc = talloc(cred, struct gssapi_creds_container);
562 if (!gcc) {
563 (*error_string) = error_message(ENOMEM);
564 return ENOMEM;
567 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
568 &gcc->creds);
569 if ((maj_stat == GSS_S_FAILURE) && (min_stat == (OM_uint32)KRB5_CC_END || min_stat == (OM_uint32) KRB5_CC_NOTFOUND)) {
570 /* This CCACHE is no good. Ensure we don't use it again */
571 cli_credentials_unconditionally_invalidate_ccache(cred);
573 /* Now try again to get a ccache */
574 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
575 &ccache, error_string);
576 if (ret) {
577 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
578 return ret;
581 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
582 &gcc->creds);
586 if (maj_stat) {
587 talloc_free(gcc);
588 if (min_stat) {
589 ret = min_stat;
590 } else {
591 ret = EINVAL;
593 (*error_string) = talloc_asprintf(cred, "gss_krb5_import_cred failed: %s", error_message(ret));
594 return ret;
597 #ifdef SAMBA4_USES_HEIMDAL /* MIT lacks krb5_get_default_in_tkt_etypes */
599 * transfer the enctypes from the smb_krb5_context to the gssapi layer
601 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
602 * to configure the enctypes via the krb5.conf.
604 * And the gss_init_sec_context() creates it's own krb5_context and
605 * the TGS-REQ had all enctypes in it and only the ones configured
606 * and used for the AS-REQ, so it wasn't possible to disable the usage
607 * of AES keys.
609 min_stat = krb5_get_default_in_tkt_etypes(ccache->smb_krb5_context->krb5_context,
610 KRB5_PDU_NONE,
611 &etypes);
612 if (min_stat == 0) {
613 OM_uint32 num_ktypes;
615 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
617 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
618 num_ktypes,
619 (int32_t *) etypes);
620 krb5_xfree (etypes);
621 if (maj_stat) {
622 talloc_free(gcc);
623 if (min_stat) {
624 ret = min_stat;
625 } else {
626 ret = EINVAL;
628 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
629 return ret;
632 #endif
633 #ifdef SAMBA4_USES_HEIMDAL /* MIT lacks GSS_KRB5_CRED_NO_CI_FLAGS_X */
635 /* don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG */
636 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
637 GSS_KRB5_CRED_NO_CI_FLAGS_X,
638 &empty_buffer);
639 if (maj_stat) {
640 talloc_free(gcc);
641 if (min_stat) {
642 ret = min_stat;
643 } else {
644 ret = EINVAL;
646 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
647 return ret;
649 #endif
650 cred->client_gss_creds_obtained = cred->ccache_obtained;
651 talloc_set_destructor(gcc, free_gssapi_creds);
652 cred->client_gss_creds = gcc;
653 *_gcc = gcc;
654 return 0;
658 Set a gssapi cred_id_t into the credentials system. (Client case)
660 This grabs the credentials both 'intact' and getting the krb5
661 ccache out of it. This routine can be generalised in future for
662 the case where we deal with GSSAPI mechs other than krb5.
664 On sucess, the caller must not free gssapi_cred, as it now belongs
665 to the credentials system.
668 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
669 struct loadparm_context *lp_ctx,
670 gss_cred_id_t gssapi_cred,
671 enum credentials_obtained obtained,
672 const char **error_string)
674 int ret;
675 OM_uint32 maj_stat, min_stat;
676 struct ccache_container *ccc;
677 struct gssapi_creds_container *gcc;
678 if (cred->client_gss_creds_obtained > obtained) {
679 return 0;
682 gcc = talloc(cred, struct gssapi_creds_container);
683 if (!gcc) {
684 (*error_string) = error_message(ENOMEM);
685 return ENOMEM;
688 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
689 if (ret != 0) {
690 return ret;
693 maj_stat = gss_krb5_copy_ccache(&min_stat,
694 gssapi_cred, ccc->ccache);
695 if (maj_stat) {
696 if (min_stat) {
697 ret = min_stat;
698 } else {
699 ret = EINVAL;
701 if (ret) {
702 (*error_string) = error_message(ENOMEM);
706 if (ret == 0) {
707 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
709 cred->ccache = ccc;
710 cred->ccache_obtained = obtained;
711 if (ret == 0) {
712 gcc->creds = gssapi_cred;
713 talloc_set_destructor(gcc, free_gssapi_creds);
715 /* set the clinet_gss_creds_obtained here, as it just
716 got set to UNINITIALISED by the calls above */
717 cred->client_gss_creds_obtained = obtained;
718 cred->client_gss_creds = gcc;
720 return ret;
723 /* Get the keytab (actually, a container containing the krb5_keytab)
724 * attached to this context. If this hasn't been done or set before,
725 * it will be generated from the password.
727 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
728 struct loadparm_context *lp_ctx,
729 struct keytab_container **_ktc)
731 krb5_error_code ret;
732 struct keytab_container *ktc;
733 struct smb_krb5_context *smb_krb5_context;
734 const char *keytab_name;
735 krb5_keytab keytab;
736 TALLOC_CTX *mem_ctx;
738 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
739 cred->username_obtained))) {
740 *_ktc = cred->keytab;
741 return 0;
744 if (cli_credentials_is_anonymous(cred)) {
745 return EINVAL;
748 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
749 &smb_krb5_context);
750 if (ret) {
751 return ret;
754 mem_ctx = talloc_new(cred);
755 if (!mem_ctx) {
756 return ENOMEM;
759 ret = smb_krb5_create_memory_keytab(mem_ctx,
760 smb_krb5_context->krb5_context,
761 cli_credentials_get_password(cred),
762 cli_credentials_get_username(cred),
763 cli_credentials_get_realm(cred),
764 cli_credentials_get_kvno(cred),
765 &keytab, &keytab_name);
766 if (ret) {
767 talloc_free(mem_ctx);
768 return ret;
771 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
772 keytab, keytab_name, &ktc);
773 if (ret) {
774 talloc_free(mem_ctx);
775 return ret;
778 cred->keytab_obtained = (MAX(cred->principal_obtained,
779 cred->username_obtained));
781 /* We make this keytab up based on a password. Therefore
782 * match-by-key is acceptable, we can't match on the wrong
783 * principal */
784 ktc->password_based = true;
786 talloc_steal(cred, ktc);
787 cred->keytab = ktc;
788 *_ktc = cred->keytab;
789 talloc_free(mem_ctx);
790 return ret;
793 /* Given the name of a keytab (presumably in the format
794 * FILE:/etc/krb5.keytab), open it and attach it */
796 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
797 struct loadparm_context *lp_ctx,
798 const char *keytab_name,
799 enum credentials_obtained obtained)
801 krb5_error_code ret;
802 struct keytab_container *ktc;
803 struct smb_krb5_context *smb_krb5_context;
804 TALLOC_CTX *mem_ctx;
806 if (cred->keytab_obtained >= obtained) {
807 return 0;
810 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
811 if (ret) {
812 return ret;
815 mem_ctx = talloc_new(cred);
816 if (!mem_ctx) {
817 return ENOMEM;
820 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
821 NULL, keytab_name, &ktc);
822 if (ret) {
823 return ret;
826 cred->keytab_obtained = obtained;
828 talloc_steal(cred, ktc);
829 cred->keytab = ktc;
830 talloc_free(mem_ctx);
832 return ret;
835 /* Get server gss credentials (in gsskrb5, this means the keytab) */
837 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
838 struct loadparm_context *lp_ctx,
839 struct gssapi_creds_container **_gcc)
841 int ret = 0;
842 OM_uint32 maj_stat, min_stat;
843 struct gssapi_creds_container *gcc;
844 struct keytab_container *ktc;
845 struct smb_krb5_context *smb_krb5_context;
846 TALLOC_CTX *mem_ctx;
847 krb5_principal princ;
848 const char *error_string;
849 enum credentials_obtained obtained;
851 mem_ctx = talloc_new(cred);
852 if (!mem_ctx) {
853 return ENOMEM;
856 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
857 if (ret) {
858 return ret;
861 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
862 if (ret) {
863 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
864 error_string));
865 talloc_free(mem_ctx);
866 return ret;
869 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
870 talloc_free(mem_ctx);
871 *_gcc = cred->server_gss_creds;
872 return 0;
875 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
876 if (ret) {
877 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
878 return ret;
881 gcc = talloc(cred, struct gssapi_creds_container);
882 if (!gcc) {
883 talloc_free(mem_ctx);
884 return ENOMEM;
887 if (ktc->password_based || obtained < CRED_SPECIFIED) {
888 /* This creates a GSSAPI cred_id_t for match-by-key with only the keytab set */
889 maj_stat = gss_krb5_import_cred(&min_stat, NULL, NULL, ktc->keytab,
890 &gcc->creds);
891 } else {
892 /* This creates a GSSAPI cred_id_t with the principal and keytab set, matching by name */
893 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
894 &gcc->creds);
896 if (maj_stat) {
897 if (min_stat) {
898 ret = min_stat;
899 } else {
900 ret = EINVAL;
903 if (ret == 0) {
904 cred->server_gss_creds_obtained = cred->keytab_obtained;
905 talloc_set_destructor(gcc, free_gssapi_creds);
906 cred->server_gss_creds = gcc;
907 *_gcc = gcc;
909 talloc_free(mem_ctx);
910 return ret;
913 /**
914 * Set Kerberos KVNO
917 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
918 int kvno)
920 cred->kvno = kvno;
924 * Return Kerberos KVNO
927 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
929 return cred->kvno;
933 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
935 return cred->salt_principal;
938 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
940 talloc_free(cred->salt_principal);
941 cred->salt_principal = talloc_strdup(cred, principal);
944 /* The 'impersonate_principal' is used to allow one Kerberos principal
945 * (and it's associated keytab etc) to impersonate another. The
946 * ability to do this is controlled by the KDC, but it is generally
947 * permitted to impersonate anyone to yourself. This allows any
948 * member of the domain to get the groups of a user. This is also
949 * known as S4U2Self */
951 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
953 return cred->impersonate_principal;
957 * The 'self_service' is the service principal that
958 * represents the same object (by its objectSid)
959 * as the client principal (typically our machine account).
960 * When trying to impersonate 'impersonate_principal' with
961 * S4U2Self.
963 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
965 return cred->self_service;
968 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
969 const char *principal,
970 const char *self_service)
972 talloc_free(cred->impersonate_principal);
973 cred->impersonate_principal = talloc_strdup(cred, principal);
974 talloc_free(cred->self_service);
975 cred->self_service = talloc_strdup(cred, self_service);
976 cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
980 * when impersonating for S4U2proxy we need to set the target principal.
981 * Similarly, we may only be authorized to do general impersonation to
982 * some particular services.
984 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
986 * NULL means that tickets will be obtained for the krbtgt service.
989 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
991 return cred->target_service;
994 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
996 talloc_free(cred->target_service);
997 cred->target_service = talloc_strdup(cred, target_service);