Make krb5 wrapper library common so they can be used all over
[Samba/bjacke.git] / auth / credentials / credentials_krb5.c
blob480d7c5951c6f5946b4f942d741d5b6cff54606e
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 "auth/kerberos/pac_utils.h"
34 #include "param/param.h"
36 static void cli_credentials_invalidate_client_gss_creds(
37 struct cli_credentials *cred,
38 enum credentials_obtained obtained);
40 _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
41 struct loadparm_context *lp_ctx,
42 struct smb_krb5_context **smb_krb5_context)
44 int ret;
45 if (cred->smb_krb5_context) {
46 *smb_krb5_context = cred->smb_krb5_context;
47 return 0;
50 ret = smb_krb5_init_context(cred, NULL, lp_ctx,
51 &cred->smb_krb5_context);
52 if (ret) {
53 cred->smb_krb5_context = NULL;
54 return ret;
56 *smb_krb5_context = cred->smb_krb5_context;
57 return 0;
60 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
61 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
63 _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
64 struct smb_krb5_context *smb_krb5_context)
66 if (smb_krb5_context == NULL) {
67 talloc_unlink(cred, cred->smb_krb5_context);
68 cred->smb_krb5_context = NULL;
69 return NT_STATUS_OK;
72 if (!talloc_reference(cred, smb_krb5_context)) {
73 return NT_STATUS_NO_MEMORY;
75 cred->smb_krb5_context = smb_krb5_context;
76 return NT_STATUS_OK;
79 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
80 struct ccache_container *ccache,
81 enum credentials_obtained obtained,
82 const char **error_string)
85 krb5_principal princ;
86 krb5_error_code ret;
87 char *name;
89 if (cred->ccache_obtained > obtained) {
90 return 0;
93 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
94 ccache->ccache, &princ);
96 if (ret) {
97 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
98 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
99 ret, cred));
100 return ret;
103 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
104 if (ret) {
105 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
106 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
107 ret, cred));
108 return ret;
111 cli_credentials_set_principal(cred, name, obtained);
113 free(name);
115 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
117 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
118 cred->ccache_obtained = obtained;
120 return 0;
123 /* Free a memory ccache */
124 static int free_mccache(struct ccache_container *ccc)
126 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
128 return 0;
131 /* Free a disk-based ccache */
132 static int free_dccache(struct ccache_container *ccc) {
133 krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
135 return 0;
138 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
139 struct loadparm_context *lp_ctx,
140 const char *name,
141 enum credentials_obtained obtained,
142 const char **error_string)
144 krb5_error_code ret;
145 krb5_principal princ;
146 struct ccache_container *ccc;
147 if (cred->ccache_obtained > obtained) {
148 return 0;
151 ccc = talloc(cred, struct ccache_container);
152 if (!ccc) {
153 (*error_string) = error_message(ENOMEM);
154 return ENOMEM;
157 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
158 &ccc->smb_krb5_context);
159 if (ret) {
160 (*error_string) = error_message(ret);
161 talloc_free(ccc);
162 return ret;
164 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
165 talloc_free(ccc);
166 (*error_string) = error_message(ENOMEM);
167 return ENOMEM;
170 if (name) {
171 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
172 if (ret) {
173 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
174 name,
175 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
176 ret, ccc));
177 talloc_free(ccc);
178 return ret;
180 } else {
181 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
182 if (ret) {
183 (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
184 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
185 ret, ccc));
186 talloc_free(ccc);
187 return ret;
191 talloc_set_destructor(ccc, free_dccache);
193 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
195 if (ret == 0) {
196 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
197 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
199 if (ret) {
200 (*error_string) = error_message(ret);
201 return ret;
204 cred->ccache = ccc;
205 cred->ccache_obtained = obtained;
206 talloc_steal(cred, ccc);
208 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
209 return 0;
211 return 0;
215 static int cli_credentials_new_ccache(struct cli_credentials *cred,
216 struct loadparm_context *lp_ctx,
217 char *ccache_name,
218 struct ccache_container **_ccc,
219 const char **error_string)
221 bool must_free_cc_name = false;
222 krb5_error_code ret;
223 struct ccache_container *ccc = talloc(cred, struct ccache_container);
224 if (!ccc) {
225 return ENOMEM;
228 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
229 &ccc->smb_krb5_context);
230 if (ret) {
231 talloc_free(ccc);
232 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
233 error_message(ret));
234 return ret;
236 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
237 talloc_free(ccc);
238 (*error_string) = strerror(ENOMEM);
239 return ENOMEM;
242 if (!ccache_name) {
243 must_free_cc_name = true;
245 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
246 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
247 (unsigned int)getpid(), ccc);
248 } else {
249 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
250 ccc);
253 if (!ccache_name) {
254 talloc_free(ccc);
255 (*error_string) = strerror(ENOMEM);
256 return ENOMEM;
260 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
261 &ccc->ccache);
262 if (ret) {
263 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
264 ccache_name,
265 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
266 ret, ccc));
267 talloc_free(ccache_name);
268 talloc_free(ccc);
269 return ret;
272 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
273 talloc_set_destructor(ccc, free_mccache);
274 } else {
275 talloc_set_destructor(ccc, free_dccache);
278 if (must_free_cc_name) {
279 talloc_free(ccache_name);
282 *_ccc = ccc;
284 return 0;
287 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
288 struct tevent_context *event_ctx,
289 struct loadparm_context *lp_ctx,
290 char *ccache_name,
291 struct ccache_container **ccc,
292 const char **error_string)
294 krb5_error_code ret;
295 enum credentials_obtained obtained;
297 if (cred->machine_account_pending) {
298 cli_credentials_set_machine_account(cred, lp_ctx);
301 if (cred->ccache_obtained >= cred->ccache_threshold &&
302 cred->ccache_obtained > CRED_UNINITIALISED) {
303 time_t lifetime;
304 bool expired = false;
305 ret = krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
306 cred->ccache->ccache, &lifetime);
307 if (ret == KRB5_CC_END) {
308 /* If we have a particular ccache set, without
309 * an initial ticket, then assume there is a
310 * good reason */
311 } else if (ret == 0) {
312 if (lifetime == 0) {
313 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
314 cli_credentials_get_principal(cred, cred)));
315 expired = true;
316 } else if (lifetime < 300) {
317 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
318 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
319 expired = true;
321 } else {
322 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
323 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
324 ret, cred));
325 return ret;
328 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
329 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
331 if (!expired) {
332 *ccc = cred->ccache;
333 return 0;
336 if (cli_credentials_is_anonymous(cred)) {
337 (*error_string) = "Cannot get anonymous kerberos credentials";
338 return EINVAL;
341 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
342 if (ret) {
343 return ret;
346 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
347 if (ret) {
348 return ret;
351 ret = cli_credentials_set_from_ccache(cred, *ccc,
352 obtained, error_string);
354 cred->ccache = *ccc;
355 cred->ccache_obtained = cred->principal_obtained;
356 if (ret) {
357 return ret;
359 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
360 return 0;
363 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
364 struct tevent_context *event_ctx,
365 struct loadparm_context *lp_ctx,
366 struct ccache_container **ccc,
367 const char **error_string)
369 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
372 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
373 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
375 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
376 talloc_unlink(cred, cred->client_gss_creds);
377 cred->client_gss_creds = NULL;
379 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
382 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
383 enum credentials_obtained obtained)
385 /* If the caller just changed the username/password etc, then
386 * any cached credentials are now invalid */
387 if (obtained >= cred->client_gss_creds_obtained) {
388 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
389 talloc_unlink(cred, cred->client_gss_creds);
390 cred->client_gss_creds = NULL;
392 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
394 /* Now that we know that the data is 'this specified', then
395 * don't allow something less 'known' to be returned as a
396 * ccache. Ie, if the username is on the command line, we
397 * don't want to later guess to use a file-based ccache */
398 if (obtained > cred->client_gss_creds_threshold) {
399 cred->client_gss_creds_threshold = obtained;
403 /* We have good reason to think this CCACHE is invalid. Blow it away */
404 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
406 if (cred->ccache_obtained > CRED_UNINITIALISED) {
407 talloc_unlink(cred, cred->ccache);
408 cred->ccache = NULL;
410 cred->ccache_obtained = CRED_UNINITIALISED;
412 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
415 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
416 enum credentials_obtained obtained)
418 /* If the caller just changed the username/password etc, then
419 * any cached credentials are now invalid */
420 if (obtained >= cred->ccache_obtained) {
421 if (cred->ccache_obtained > CRED_UNINITIALISED) {
422 talloc_unlink(cred, cred->ccache);
423 cred->ccache = NULL;
425 cred->ccache_obtained = CRED_UNINITIALISED;
427 /* Now that we know that the data is 'this specified', then
428 * don't allow something less 'known' to be returned as a
429 * ccache. i.e, if the username is on the command line, we
430 * don't want to later guess to use a file-based ccache */
431 if (obtained > cred->ccache_threshold) {
432 cred->ccache_threshold = obtained;
435 cli_credentials_invalidate_client_gss_creds(cred,
436 obtained);
439 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
441 OM_uint32 min_stat;
442 (void)gss_release_cred(&min_stat, &gcc->creds);
443 return 0;
446 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
447 struct tevent_context *event_ctx,
448 struct loadparm_context *lp_ctx,
449 struct gssapi_creds_container **_gcc,
450 const char **error_string)
452 int ret = 0;
453 OM_uint32 maj_stat, min_stat;
454 struct gssapi_creds_container *gcc;
455 struct ccache_container *ccache;
456 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
457 krb5_enctype *etypes = NULL;
459 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
460 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
461 bool expired = false;
462 OM_uint32 lifetime = 0;
463 gss_cred_usage_t usage = 0;
464 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
465 NULL, &lifetime, &usage, NULL);
466 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
467 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
468 expired = true;
469 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
470 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
471 expired = true;
472 } else if (maj_stat != GSS_S_COMPLETE) {
473 *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
474 gssapi_error_string(cred, maj_stat, min_stat, NULL));
475 return EINVAL;
477 if (expired) {
478 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
479 } else {
480 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
481 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
483 *_gcc = cred->client_gss_creds;
484 return 0;
488 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
489 &ccache, error_string);
490 if (ret) {
491 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
492 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
493 } else {
494 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
496 return ret;
499 gcc = talloc(cred, struct gssapi_creds_container);
500 if (!gcc) {
501 (*error_string) = error_message(ENOMEM);
502 return ENOMEM;
505 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
506 &gcc->creds);
507 if ((maj_stat == GSS_S_FAILURE) && (min_stat == (OM_uint32)KRB5_CC_END || min_stat == (OM_uint32) KRB5_CC_NOTFOUND)) {
508 /* This CCACHE is no good. Ensure we don't use it again */
509 cli_credentials_unconditionally_invalidate_ccache(cred);
511 /* Now try again to get a ccache */
512 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
513 &ccache, error_string);
514 if (ret) {
515 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
516 return ret;
519 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
520 &gcc->creds);
524 if (maj_stat) {
525 talloc_free(gcc);
526 if (min_stat) {
527 ret = min_stat;
528 } else {
529 ret = EINVAL;
531 (*error_string) = talloc_asprintf(cred, "gss_krb5_import_cred failed: %s", error_message(ret));
532 return ret;
536 * transfer the enctypes from the smb_krb5_context to the gssapi layer
538 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
539 * to configure the enctypes via the krb5.conf.
541 * And the gss_init_sec_context() creates it's own krb5_context and
542 * the TGS-REQ had all enctypes in it and only the ones configured
543 * and used for the AS-REQ, so it wasn't possible to disable the usage
544 * of AES keys.
546 min_stat = krb5_get_default_in_tkt_etypes(ccache->smb_krb5_context->krb5_context,
547 KRB5_PDU_NONE,
548 &etypes);
549 if (min_stat == 0) {
550 OM_uint32 num_ktypes;
552 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
554 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
555 num_ktypes,
556 (int32_t *) etypes);
557 krb5_xfree (etypes);
558 if (maj_stat) {
559 talloc_free(gcc);
560 if (min_stat) {
561 ret = min_stat;
562 } else {
563 ret = EINVAL;
565 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
566 return ret;
570 /* don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG */
571 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
572 GSS_KRB5_CRED_NO_CI_FLAGS_X,
573 &empty_buffer);
574 if (maj_stat) {
575 talloc_free(gcc);
576 if (min_stat) {
577 ret = min_stat;
578 } else {
579 ret = EINVAL;
581 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
582 return ret;
585 cred->client_gss_creds_obtained = cred->ccache_obtained;
586 talloc_set_destructor(gcc, free_gssapi_creds);
587 cred->client_gss_creds = gcc;
588 *_gcc = gcc;
589 return 0;
593 Set a gssapi cred_id_t into the credentials system. (Client case)
595 This grabs the credentials both 'intact' and getting the krb5
596 ccache out of it. This routine can be generalised in future for
597 the case where we deal with GSSAPI mechs other than krb5.
599 On sucess, the caller must not free gssapi_cred, as it now belongs
600 to the credentials system.
603 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
604 struct loadparm_context *lp_ctx,
605 gss_cred_id_t gssapi_cred,
606 enum credentials_obtained obtained,
607 const char **error_string)
609 int ret;
610 OM_uint32 maj_stat, min_stat;
611 struct ccache_container *ccc;
612 struct gssapi_creds_container *gcc;
613 if (cred->client_gss_creds_obtained > obtained) {
614 return 0;
617 gcc = talloc(cred, struct gssapi_creds_container);
618 if (!gcc) {
619 (*error_string) = error_message(ENOMEM);
620 return ENOMEM;
623 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
624 if (ret != 0) {
625 return ret;
628 maj_stat = gss_krb5_copy_ccache(&min_stat,
629 gssapi_cred, ccc->ccache);
630 if (maj_stat) {
631 if (min_stat) {
632 ret = min_stat;
633 } else {
634 ret = EINVAL;
636 if (ret) {
637 (*error_string) = error_message(ENOMEM);
641 if (ret == 0) {
642 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
644 cred->ccache = ccc;
645 cred->ccache_obtained = obtained;
646 if (ret == 0) {
647 gcc->creds = gssapi_cred;
648 talloc_set_destructor(gcc, free_gssapi_creds);
650 /* set the clinet_gss_creds_obtained here, as it just
651 got set to UNINITIALISED by the calls above */
652 cred->client_gss_creds_obtained = obtained;
653 cred->client_gss_creds = gcc;
655 return ret;
658 /* Get the keytab (actually, a container containing the krb5_keytab)
659 * attached to this context. If this hasn't been done or set before,
660 * it will be generated from the password.
662 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
663 struct loadparm_context *lp_ctx,
664 struct keytab_container **_ktc)
666 krb5_error_code ret;
667 struct keytab_container *ktc;
668 struct smb_krb5_context *smb_krb5_context;
669 const char *keytab_name;
670 krb5_keytab keytab;
671 TALLOC_CTX *mem_ctx;
673 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
674 cred->username_obtained))) {
675 *_ktc = cred->keytab;
676 return 0;
679 if (cli_credentials_is_anonymous(cred)) {
680 return EINVAL;
683 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
684 &smb_krb5_context);
685 if (ret) {
686 return ret;
689 mem_ctx = talloc_new(cred);
690 if (!mem_ctx) {
691 return ENOMEM;
694 ret = smb_krb5_create_memory_keytab(mem_ctx,
695 smb_krb5_context->krb5_context,
696 cli_credentials_get_password(cred),
697 cli_credentials_get_username(cred),
698 cli_credentials_get_realm(cred),
699 cli_credentials_get_kvno(cred),
700 &keytab, &keytab_name);
701 if (ret) {
702 talloc_free(mem_ctx);
703 return ret;
706 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
707 keytab, keytab_name, &ktc);
708 if (ret) {
709 talloc_free(mem_ctx);
710 return ret;
713 cred->keytab_obtained = (MAX(cred->principal_obtained,
714 cred->username_obtained));
716 talloc_steal(cred, ktc);
717 cred->keytab = ktc;
718 *_ktc = cred->keytab;
719 talloc_free(mem_ctx);
720 return ret;
723 /* Given the name of a keytab (presumably in the format
724 * FILE:/etc/krb5.keytab), open it and attach it */
726 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
727 struct loadparm_context *lp_ctx,
728 const char *keytab_name,
729 enum credentials_obtained obtained)
731 krb5_error_code ret;
732 struct keytab_container *ktc;
733 struct smb_krb5_context *smb_krb5_context;
734 TALLOC_CTX *mem_ctx;
736 if (cred->keytab_obtained >= obtained) {
737 return 0;
740 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
741 if (ret) {
742 return ret;
745 mem_ctx = talloc_new(cred);
746 if (!mem_ctx) {
747 return ENOMEM;
750 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
751 NULL, keytab_name, &ktc);
752 if (ret) {
753 return ret;
756 cred->keytab_obtained = obtained;
758 talloc_steal(cred, ktc);
759 cred->keytab = ktc;
760 talloc_free(mem_ctx);
762 return ret;
765 /* Get server gss credentials (in gsskrb5, this means the keytab) */
767 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
768 struct loadparm_context *lp_ctx,
769 struct gssapi_creds_container **_gcc)
771 int ret = 0;
772 OM_uint32 maj_stat, min_stat;
773 struct gssapi_creds_container *gcc;
774 struct keytab_container *ktc;
775 struct smb_krb5_context *smb_krb5_context;
776 TALLOC_CTX *mem_ctx;
777 krb5_principal princ;
778 const char *error_string;
779 enum credentials_obtained obtained;
781 mem_ctx = talloc_new(cred);
782 if (!mem_ctx) {
783 return ENOMEM;
786 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
787 if (ret) {
788 return ret;
791 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
792 if (ret) {
793 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
794 error_string));
795 talloc_free(mem_ctx);
796 return ret;
799 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
800 talloc_free(mem_ctx);
801 *_gcc = cred->server_gss_creds;
802 return 0;
805 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
806 if (ret) {
807 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
808 return ret;
811 gcc = talloc(cred, struct gssapi_creds_container);
812 if (!gcc) {
813 talloc_free(mem_ctx);
814 return ENOMEM;
817 if (obtained < CRED_SPECIFIED) {
818 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
819 maj_stat = gss_krb5_import_cred(&min_stat, NULL, NULL, ktc->keytab,
820 &gcc->creds);
821 } else {
822 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
823 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
824 &gcc->creds);
826 if (maj_stat) {
827 if (min_stat) {
828 ret = min_stat;
829 } else {
830 ret = EINVAL;
833 if (ret == 0) {
834 cred->server_gss_creds_obtained = cred->keytab_obtained;
835 talloc_set_destructor(gcc, free_gssapi_creds);
836 cred->server_gss_creds = gcc;
837 *_gcc = gcc;
839 talloc_free(mem_ctx);
840 return ret;
843 /**
844 * Set Kerberos KVNO
847 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
848 int kvno)
850 cred->kvno = kvno;
854 * Return Kerberos KVNO
857 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
859 return cred->kvno;
863 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
865 return cred->salt_principal;
868 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
870 talloc_free(cred->salt_principal);
871 cred->salt_principal = talloc_strdup(cred, principal);
874 /* The 'impersonate_principal' is used to allow one Kerberos principal
875 * (and it's associated keytab etc) to impersonate another. The
876 * ability to do this is controlled by the KDC, but it is generally
877 * permitted to impersonate anyone to yourself. This allows any
878 * member of the domain to get the groups of a user. This is also
879 * known as S4U2Self */
881 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
883 return cred->impersonate_principal;
887 * The 'self_service' is the service principal that
888 * represents the same object (by its objectSid)
889 * as the client principal (typically our machine account).
890 * When trying to impersonate 'impersonate_principal' with
891 * S4U2Self.
893 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
895 return cred->self_service;
898 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
899 const char *principal,
900 const char *self_service)
902 talloc_free(cred->impersonate_principal);
903 cred->impersonate_principal = talloc_strdup(cred, principal);
904 talloc_free(cred->self_service);
905 cred->self_service = talloc_strdup(cred, self_service);
906 cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
910 * when impersonating for S4U2proxy we need to set the target principal.
911 * Similarly, we may only be authorized to do general impersonation to
912 * some particular services.
914 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
916 * NULL means that tickets will be obtained for the krbtgt service.
919 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
921 return cred->target_service;
924 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
926 talloc_free(cred->target_service);
927 cred->target_service = talloc_strdup(cred, target_service);