s4-auth-krb: Remove unneded dependency on kerberos_util.
[Samba/bjacke.git] / auth / credentials / credentials_krb5.c
blob40ae454a9d11c5d827c1be261f780bb8cbb5e347
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 "auth/kerberos/kerberos.h"
27 #include "auth/credentials/credentials.h"
28 #include "auth/credentials/credentials_proto.h"
29 #include "auth/credentials/credentials_krb5.h"
30 #include "auth/kerberos/kerberos_credentials.h"
31 #include "auth/kerberos/kerberos_srv_keytab.h"
32 #include "auth/kerberos/kerberos_util.h"
33 #include "param/param.h"
35 static void cli_credentials_invalidate_client_gss_creds(
36 struct cli_credentials *cred,
37 enum credentials_obtained obtained);
39 _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
40 struct loadparm_context *lp_ctx,
41 struct smb_krb5_context **smb_krb5_context)
43 int ret;
44 if (cred->smb_krb5_context) {
45 *smb_krb5_context = cred->smb_krb5_context;
46 return 0;
49 ret = smb_krb5_init_context(cred, NULL, lp_ctx,
50 &cred->smb_krb5_context);
51 if (ret) {
52 cred->smb_krb5_context = NULL;
53 return ret;
55 *smb_krb5_context = cred->smb_krb5_context;
56 return 0;
59 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
60 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
62 _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
63 struct smb_krb5_context *smb_krb5_context)
65 if (smb_krb5_context == NULL) {
66 talloc_unlink(cred, cred->smb_krb5_context);
67 cred->smb_krb5_context = NULL;
68 return NT_STATUS_OK;
71 if (!talloc_reference(cred, smb_krb5_context)) {
72 return NT_STATUS_NO_MEMORY;
74 cred->smb_krb5_context = smb_krb5_context;
75 return NT_STATUS_OK;
78 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
79 struct ccache_container *ccache,
80 enum credentials_obtained obtained,
81 const char **error_string)
84 krb5_principal princ;
85 krb5_error_code ret;
86 char *name;
88 if (cred->ccache_obtained > obtained) {
89 return 0;
92 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
93 ccache->ccache, &princ);
95 if (ret) {
96 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
97 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
98 ret, cred));
99 return ret;
102 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
103 if (ret) {
104 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
105 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
106 ret, cred));
107 return ret;
110 cli_credentials_set_principal(cred, name, obtained);
112 free(name);
114 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
116 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
117 cred->ccache_obtained = obtained;
119 return 0;
122 /* Free a memory ccache */
123 static int free_mccache(struct ccache_container *ccc)
125 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
127 return 0;
130 /* Free a disk-based ccache */
131 static int free_dccache(struct ccache_container *ccc) {
132 krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
134 return 0;
137 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
138 struct loadparm_context *lp_ctx,
139 const char *name,
140 enum credentials_obtained obtained,
141 const char **error_string)
143 krb5_error_code ret;
144 krb5_principal princ;
145 struct ccache_container *ccc;
146 if (cred->ccache_obtained > obtained) {
147 return 0;
150 ccc = talloc(cred, struct ccache_container);
151 if (!ccc) {
152 (*error_string) = error_message(ENOMEM);
153 return ENOMEM;
156 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
157 &ccc->smb_krb5_context);
158 if (ret) {
159 (*error_string) = error_message(ret);
160 talloc_free(ccc);
161 return ret;
163 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
164 talloc_free(ccc);
165 (*error_string) = error_message(ENOMEM);
166 return ENOMEM;
169 if (name) {
170 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
171 if (ret) {
172 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
173 name,
174 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
175 ret, ccc));
176 talloc_free(ccc);
177 return ret;
179 } else {
180 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
181 if (ret) {
182 (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
183 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
184 ret, ccc));
185 talloc_free(ccc);
186 return ret;
190 talloc_set_destructor(ccc, free_dccache);
192 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
194 if (ret == 0) {
195 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
196 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
198 if (ret) {
199 (*error_string) = error_message(ret);
200 return ret;
203 cred->ccache = ccc;
204 cred->ccache_obtained = obtained;
205 talloc_steal(cred, ccc);
207 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
208 return 0;
210 return 0;
214 static int cli_credentials_new_ccache(struct cli_credentials *cred,
215 struct loadparm_context *lp_ctx,
216 char *ccache_name,
217 struct ccache_container **_ccc,
218 const char **error_string)
220 bool must_free_cc_name = false;
221 krb5_error_code ret;
222 struct ccache_container *ccc = talloc(cred, struct ccache_container);
223 if (!ccc) {
224 return ENOMEM;
227 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
228 &ccc->smb_krb5_context);
229 if (ret) {
230 talloc_free(ccc);
231 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
232 error_message(ret));
233 return ret;
235 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
236 talloc_free(ccc);
237 (*error_string) = strerror(ENOMEM);
238 return ENOMEM;
241 if (!ccache_name) {
242 must_free_cc_name = true;
244 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
245 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
246 (unsigned int)getpid(), ccc);
247 } else {
248 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
249 ccc);
252 if (!ccache_name) {
253 talloc_free(ccc);
254 (*error_string) = strerror(ENOMEM);
255 return ENOMEM;
259 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
260 &ccc->ccache);
261 if (ret) {
262 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
263 ccache_name,
264 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
265 ret, ccc));
266 talloc_free(ccache_name);
267 talloc_free(ccc);
268 return ret;
271 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
272 talloc_set_destructor(ccc, free_mccache);
273 } else {
274 talloc_set_destructor(ccc, free_dccache);
277 if (must_free_cc_name) {
278 talloc_free(ccache_name);
281 *_ccc = ccc;
283 return 0;
286 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
287 struct tevent_context *event_ctx,
288 struct loadparm_context *lp_ctx,
289 char *ccache_name,
290 struct ccache_container **ccc,
291 const char **error_string)
293 krb5_error_code ret;
294 enum credentials_obtained obtained;
296 if (cred->machine_account_pending) {
297 cli_credentials_set_machine_account(cred, lp_ctx);
300 if (cred->ccache_obtained >= cred->ccache_threshold &&
301 cred->ccache_obtained > CRED_UNINITIALISED) {
302 time_t lifetime;
303 bool expired = false;
304 ret = krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
305 cred->ccache->ccache, &lifetime);
306 if (ret == KRB5_CC_END) {
307 /* If we have a particular ccache set, without
308 * an initial ticket, then assume there is a
309 * good reason */
310 } else if (ret == 0) {
311 if (lifetime == 0) {
312 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
313 cli_credentials_get_principal(cred, cred)));
314 expired = true;
315 } else if (lifetime < 300) {
316 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
317 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
318 expired = true;
320 } else {
321 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
322 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
323 ret, cred));
324 return ret;
327 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
328 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
330 if (!expired) {
331 *ccc = cred->ccache;
332 return 0;
335 if (cli_credentials_is_anonymous(cred)) {
336 (*error_string) = "Cannot get anonymous kerberos credentials";
337 return EINVAL;
340 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
341 if (ret) {
342 return ret;
345 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
346 if (ret) {
347 return ret;
350 ret = cli_credentials_set_from_ccache(cred, *ccc,
351 obtained, error_string);
353 cred->ccache = *ccc;
354 cred->ccache_obtained = cred->principal_obtained;
355 if (ret) {
356 return ret;
358 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
359 return 0;
362 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
363 struct tevent_context *event_ctx,
364 struct loadparm_context *lp_ctx,
365 struct ccache_container **ccc,
366 const char **error_string)
368 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
371 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
372 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
374 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
375 talloc_unlink(cred, cred->client_gss_creds);
376 cred->client_gss_creds = NULL;
378 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
381 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
382 enum credentials_obtained obtained)
384 /* If the caller just changed the username/password etc, then
385 * any cached credentials are now invalid */
386 if (obtained >= cred->client_gss_creds_obtained) {
387 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
388 talloc_unlink(cred, cred->client_gss_creds);
389 cred->client_gss_creds = NULL;
391 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
393 /* Now that we know that the data is 'this specified', then
394 * don't allow something less 'known' to be returned as a
395 * ccache. Ie, if the username is on the command line, we
396 * don't want to later guess to use a file-based ccache */
397 if (obtained > cred->client_gss_creds_threshold) {
398 cred->client_gss_creds_threshold = obtained;
402 /* We have good reason to think this CCACHE is invalid. Blow it away */
403 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
405 if (cred->ccache_obtained > CRED_UNINITIALISED) {
406 talloc_unlink(cred, cred->ccache);
407 cred->ccache = NULL;
409 cred->ccache_obtained = CRED_UNINITIALISED;
411 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
414 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
415 enum credentials_obtained obtained)
417 /* If the caller just changed the username/password etc, then
418 * any cached credentials are now invalid */
419 if (obtained >= cred->ccache_obtained) {
420 if (cred->ccache_obtained > CRED_UNINITIALISED) {
421 talloc_unlink(cred, cred->ccache);
422 cred->ccache = NULL;
424 cred->ccache_obtained = CRED_UNINITIALISED;
426 /* Now that we know that the data is 'this specified', then
427 * don't allow something less 'known' to be returned as a
428 * ccache. i.e, if the username is on the command line, we
429 * don't want to later guess to use a file-based ccache */
430 if (obtained > cred->ccache_threshold) {
431 cred->ccache_threshold = obtained;
434 cli_credentials_invalidate_client_gss_creds(cred,
435 obtained);
438 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
440 OM_uint32 min_stat;
441 (void)gss_release_cred(&min_stat, &gcc->creds);
442 return 0;
445 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
446 struct tevent_context *event_ctx,
447 struct loadparm_context *lp_ctx,
448 struct gssapi_creds_container **_gcc,
449 const char **error_string)
451 int ret = 0;
452 OM_uint32 maj_stat, min_stat;
453 struct gssapi_creds_container *gcc;
454 struct ccache_container *ccache;
455 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
456 krb5_enctype *etypes = NULL;
458 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
459 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
460 bool expired = false;
461 OM_uint32 lifetime = 0;
462 gss_cred_usage_t usage = 0;
463 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
464 NULL, &lifetime, &usage, NULL);
465 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
466 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
467 expired = true;
468 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
469 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
470 expired = true;
471 } else if (maj_stat != GSS_S_COMPLETE) {
472 *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
473 gssapi_error_string(cred, maj_stat, min_stat, NULL));
474 return EINVAL;
476 if (expired) {
477 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
478 } else {
479 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
480 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
482 *_gcc = cred->client_gss_creds;
483 return 0;
487 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
488 &ccache, error_string);
489 if (ret) {
490 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
491 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
492 } else {
493 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
495 return ret;
498 gcc = talloc(cred, struct gssapi_creds_container);
499 if (!gcc) {
500 (*error_string) = error_message(ENOMEM);
501 return ENOMEM;
504 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
505 &gcc->creds);
506 if ((maj_stat == GSS_S_FAILURE) && (min_stat == (OM_uint32)KRB5_CC_END || min_stat == (OM_uint32) KRB5_CC_NOTFOUND)) {
507 /* This CCACHE is no good. Ensure we don't use it again */
508 cli_credentials_unconditionally_invalidate_ccache(cred);
510 /* Now try again to get a ccache */
511 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
512 &ccache, error_string);
513 if (ret) {
514 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
515 return ret;
518 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
519 &gcc->creds);
523 if (maj_stat) {
524 talloc_free(gcc);
525 if (min_stat) {
526 ret = min_stat;
527 } else {
528 ret = EINVAL;
530 (*error_string) = talloc_asprintf(cred, "gss_krb5_import_cred failed: %s", error_message(ret));
531 return ret;
535 * transfer the enctypes from the smb_krb5_context to the gssapi layer
537 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
538 * to configure the enctypes via the krb5.conf.
540 * And the gss_init_sec_context() creates it's own krb5_context and
541 * the TGS-REQ had all enctypes in it and only the ones configured
542 * and used for the AS-REQ, so it wasn't possible to disable the usage
543 * of AES keys.
545 min_stat = krb5_get_default_in_tkt_etypes(ccache->smb_krb5_context->krb5_context,
546 KRB5_PDU_NONE,
547 &etypes);
548 if (min_stat == 0) {
549 OM_uint32 num_ktypes;
551 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
553 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
554 num_ktypes,
555 (int32_t *) etypes);
556 krb5_xfree (etypes);
557 if (maj_stat) {
558 talloc_free(gcc);
559 if (min_stat) {
560 ret = min_stat;
561 } else {
562 ret = EINVAL;
564 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
565 return ret;
569 /* don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG */
570 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
571 GSS_KRB5_CRED_NO_CI_FLAGS_X,
572 &empty_buffer);
573 if (maj_stat) {
574 talloc_free(gcc);
575 if (min_stat) {
576 ret = min_stat;
577 } else {
578 ret = EINVAL;
580 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
581 return ret;
584 cred->client_gss_creds_obtained = cred->ccache_obtained;
585 talloc_set_destructor(gcc, free_gssapi_creds);
586 cred->client_gss_creds = gcc;
587 *_gcc = gcc;
588 return 0;
592 Set a gssapi cred_id_t into the credentials system. (Client case)
594 This grabs the credentials both 'intact' and getting the krb5
595 ccache out of it. This routine can be generalised in future for
596 the case where we deal with GSSAPI mechs other than krb5.
598 On sucess, the caller must not free gssapi_cred, as it now belongs
599 to the credentials system.
602 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
603 struct loadparm_context *lp_ctx,
604 gss_cred_id_t gssapi_cred,
605 enum credentials_obtained obtained,
606 const char **error_string)
608 int ret;
609 OM_uint32 maj_stat, min_stat;
610 struct ccache_container *ccc;
611 struct gssapi_creds_container *gcc;
612 if (cred->client_gss_creds_obtained > obtained) {
613 return 0;
616 gcc = talloc(cred, struct gssapi_creds_container);
617 if (!gcc) {
618 (*error_string) = error_message(ENOMEM);
619 return ENOMEM;
622 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
623 if (ret != 0) {
624 return ret;
627 maj_stat = gss_krb5_copy_ccache(&min_stat,
628 gssapi_cred, ccc->ccache);
629 if (maj_stat) {
630 if (min_stat) {
631 ret = min_stat;
632 } else {
633 ret = EINVAL;
635 if (ret) {
636 (*error_string) = error_message(ENOMEM);
640 if (ret == 0) {
641 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
643 cred->ccache = ccc;
644 cred->ccache_obtained = obtained;
645 if (ret == 0) {
646 gcc->creds = gssapi_cred;
647 talloc_set_destructor(gcc, free_gssapi_creds);
649 /* set the clinet_gss_creds_obtained here, as it just
650 got set to UNINITIALISED by the calls above */
651 cred->client_gss_creds_obtained = obtained;
652 cred->client_gss_creds = gcc;
654 return ret;
657 /* Get the keytab (actually, a container containing the krb5_keytab)
658 * attached to this context. If this hasn't been done or set before,
659 * it will be generated from the password.
661 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
662 struct loadparm_context *lp_ctx,
663 struct keytab_container **_ktc)
665 krb5_error_code ret;
666 struct keytab_container *ktc;
667 struct smb_krb5_context *smb_krb5_context;
668 const char *keytab_name;
669 krb5_keytab keytab;
670 TALLOC_CTX *mem_ctx;
672 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
673 cred->username_obtained))) {
674 *_ktc = cred->keytab;
675 return 0;
678 if (cli_credentials_is_anonymous(cred)) {
679 return EINVAL;
682 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
683 &smb_krb5_context);
684 if (ret) {
685 return ret;
688 mem_ctx = talloc_new(cred);
689 if (!mem_ctx) {
690 return ENOMEM;
693 ret = smb_krb5_create_memory_keytab(mem_ctx, cred,
694 smb_krb5_context,
695 &keytab, &keytab_name);
696 if (ret) {
697 talloc_free(mem_ctx);
698 return ret;
701 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
702 keytab, keytab_name, &ktc);
703 if (ret) {
704 talloc_free(mem_ctx);
705 return ret;
708 cred->keytab_obtained = (MAX(cred->principal_obtained,
709 cred->username_obtained));
711 talloc_steal(cred, ktc);
712 cred->keytab = ktc;
713 *_ktc = cred->keytab;
714 talloc_free(mem_ctx);
715 return ret;
718 /* Given the name of a keytab (presumably in the format
719 * FILE:/etc/krb5.keytab), open it and attach it */
721 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
722 struct loadparm_context *lp_ctx,
723 const char *keytab_name,
724 enum credentials_obtained obtained)
726 krb5_error_code ret;
727 struct keytab_container *ktc;
728 struct smb_krb5_context *smb_krb5_context;
729 TALLOC_CTX *mem_ctx;
731 if (cred->keytab_obtained >= obtained) {
732 return 0;
735 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
736 if (ret) {
737 return ret;
740 mem_ctx = talloc_new(cred);
741 if (!mem_ctx) {
742 return ENOMEM;
745 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
746 NULL, keytab_name, &ktc);
747 if (ret) {
748 return ret;
751 cred->keytab_obtained = obtained;
753 talloc_steal(cred, ktc);
754 cred->keytab = ktc;
755 talloc_free(mem_ctx);
757 return ret;
760 /* Get server gss credentials (in gsskrb5, this means the keytab) */
762 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
763 struct loadparm_context *lp_ctx,
764 struct gssapi_creds_container **_gcc)
766 int ret = 0;
767 OM_uint32 maj_stat, min_stat;
768 struct gssapi_creds_container *gcc;
769 struct keytab_container *ktc;
770 struct smb_krb5_context *smb_krb5_context;
771 TALLOC_CTX *mem_ctx;
772 krb5_principal princ;
773 const char *error_string;
774 enum credentials_obtained obtained;
776 mem_ctx = talloc_new(cred);
777 if (!mem_ctx) {
778 return ENOMEM;
781 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
782 if (ret) {
783 return ret;
786 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
787 if (ret) {
788 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
789 error_string));
790 talloc_free(mem_ctx);
791 return ret;
794 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
795 talloc_free(mem_ctx);
796 *_gcc = cred->server_gss_creds;
797 return 0;
800 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
801 if (ret) {
802 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
803 return ret;
806 gcc = talloc(cred, struct gssapi_creds_container);
807 if (!gcc) {
808 talloc_free(mem_ctx);
809 return ENOMEM;
812 if (obtained < CRED_SPECIFIED) {
813 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
814 maj_stat = gss_krb5_import_cred(&min_stat, NULL, NULL, ktc->keytab,
815 &gcc->creds);
816 } else {
817 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
818 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
819 &gcc->creds);
821 if (maj_stat) {
822 if (min_stat) {
823 ret = min_stat;
824 } else {
825 ret = EINVAL;
828 if (ret == 0) {
829 cred->server_gss_creds_obtained = cred->keytab_obtained;
830 talloc_set_destructor(gcc, free_gssapi_creds);
831 cred->server_gss_creds = gcc;
832 *_gcc = gcc;
834 talloc_free(mem_ctx);
835 return ret;
838 /**
839 * Set Kerberos KVNO
842 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
843 int kvno)
845 cred->kvno = kvno;
849 * Return Kerberos KVNO
852 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
854 return cred->kvno;
858 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
860 return cred->salt_principal;
863 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
865 talloc_free(cred->salt_principal);
866 cred->salt_principal = talloc_strdup(cred, principal);
869 /* The 'impersonate_principal' is used to allow one Kerberos principal
870 * (and it's associated keytab etc) to impersonate another. The
871 * ability to do this is controlled by the KDC, but it is generally
872 * permitted to impersonate anyone to yourself. This allows any
873 * member of the domain to get the groups of a user. This is also
874 * known as S4U2Self */
876 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
878 return cred->impersonate_principal;
882 * The 'self_service' is the service principal that
883 * represents the same object (by its objectSid)
884 * as the client principal (typically our machine account).
885 * When trying to impersonate 'impersonate_principal' with
886 * S4U2Self.
888 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
890 return cred->self_service;
893 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
894 const char *principal,
895 const char *self_service)
897 talloc_free(cred->impersonate_principal);
898 cred->impersonate_principal = talloc_strdup(cred, principal);
899 talloc_free(cred->self_service);
900 cred->self_service = talloc_strdup(cred, self_service);
901 cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
905 * when impersonating for S4U2proxy we need to set the target principal.
906 * Similarly, we may only be authorized to do general impersonation to
907 * some particular services.
909 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
911 * NULL means that tickets will be obtained for the krbtgt service.
914 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
916 return cred->target_service;
919 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
921 talloc_free(cred->target_service);
922 cred->target_service = talloc_strdup(cred, target_service);