s4-torture: Complete test for winbindd PAC parsing
[Samba.git] / auth / credentials / credentials_krb5.c
blob459e9487f4822cf8f37d52ab98e7a6d4acbf23e8
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 static int cli_credentials_new_ccache(struct cli_credentials *cred,
217 struct loadparm_context *lp_ctx,
218 char *ccache_name,
219 struct ccache_container **_ccc,
220 const char **error_string)
222 bool must_free_cc_name = false;
223 krb5_error_code ret;
224 struct ccache_container *ccc = talloc(cred, struct ccache_container);
225 if (!ccc) {
226 return ENOMEM;
229 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
230 &ccc->smb_krb5_context);
231 if (ret) {
232 talloc_free(ccc);
233 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
234 error_message(ret));
235 return ret;
237 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
238 talloc_free(ccc);
239 (*error_string) = strerror(ENOMEM);
240 return ENOMEM;
243 if (!ccache_name) {
244 must_free_cc_name = true;
246 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
247 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
248 (unsigned int)getpid(), ccc);
249 } else {
250 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
251 ccc);
254 if (!ccache_name) {
255 talloc_free(ccc);
256 (*error_string) = strerror(ENOMEM);
257 return ENOMEM;
261 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
262 &ccc->ccache);
263 if (ret) {
264 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
265 ccache_name,
266 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
267 ret, ccc));
268 talloc_free(ccache_name);
269 talloc_free(ccc);
270 return ret;
273 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
274 talloc_set_destructor(ccc, free_mccache);
275 } else {
276 talloc_set_destructor(ccc, free_dccache);
279 if (must_free_cc_name) {
280 talloc_free(ccache_name);
283 *_ccc = ccc;
285 return 0;
288 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
289 struct tevent_context *event_ctx,
290 struct loadparm_context *lp_ctx,
291 char *ccache_name,
292 struct ccache_container **ccc,
293 const char **error_string)
295 krb5_error_code ret;
296 enum credentials_obtained obtained;
298 if (cred->machine_account_pending) {
299 cli_credentials_set_machine_account(cred, lp_ctx);
302 if (cred->ccache_obtained >= cred->ccache_threshold &&
303 cred->ccache_obtained > CRED_UNINITIALISED) {
304 time_t lifetime;
305 bool expired = false;
306 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
307 cred->ccache->ccache, &lifetime);
308 if (ret == KRB5_CC_END) {
309 /* If we have a particular ccache set, without
310 * an initial ticket, then assume there is a
311 * good reason */
312 } else if (ret == 0) {
313 if (lifetime == 0) {
314 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
315 cli_credentials_get_principal(cred, cred)));
316 expired = true;
317 } else if (lifetime < 300) {
318 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
319 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
320 expired = true;
322 } else {
323 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
324 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
325 ret, cred));
326 return ret;
329 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
330 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
332 if (!expired) {
333 *ccc = cred->ccache;
334 return 0;
337 if (cli_credentials_is_anonymous(cred)) {
338 (*error_string) = "Cannot get anonymous kerberos credentials";
339 return EINVAL;
342 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
343 if (ret) {
344 return ret;
347 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
348 if (ret) {
349 return ret;
352 ret = cli_credentials_set_from_ccache(cred, *ccc,
353 obtained, error_string);
355 cred->ccache = *ccc;
356 cred->ccache_obtained = cred->principal_obtained;
357 if (ret) {
358 return ret;
360 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
361 return 0;
364 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
365 struct tevent_context *event_ctx,
366 struct loadparm_context *lp_ctx,
367 struct ccache_container **ccc,
368 const char **error_string)
370 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
373 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
374 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
376 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
377 talloc_unlink(cred, cred->client_gss_creds);
378 cred->client_gss_creds = NULL;
380 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
383 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
384 enum credentials_obtained obtained)
386 /* If the caller just changed the username/password etc, then
387 * any cached credentials are now invalid */
388 if (obtained >= cred->client_gss_creds_obtained) {
389 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
390 talloc_unlink(cred, cred->client_gss_creds);
391 cred->client_gss_creds = NULL;
393 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
395 /* Now that we know that the data is 'this specified', then
396 * don't allow something less 'known' to be returned as a
397 * ccache. Ie, if the username is on the command line, we
398 * don't want to later guess to use a file-based ccache */
399 if (obtained > cred->client_gss_creds_threshold) {
400 cred->client_gss_creds_threshold = obtained;
404 /* We have good reason to think this CCACHE is invalid. Blow it away */
405 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
407 if (cred->ccache_obtained > CRED_UNINITIALISED) {
408 talloc_unlink(cred, cred->ccache);
409 cred->ccache = NULL;
411 cred->ccache_obtained = CRED_UNINITIALISED;
413 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
416 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
417 enum credentials_obtained obtained)
419 /* If the caller just changed the username/password etc, then
420 * any cached credentials are now invalid */
421 if (obtained >= cred->ccache_obtained) {
422 if (cred->ccache_obtained > CRED_UNINITIALISED) {
423 talloc_unlink(cred, cred->ccache);
424 cred->ccache = NULL;
426 cred->ccache_obtained = CRED_UNINITIALISED;
428 /* Now that we know that the data is 'this specified', then
429 * don't allow something less 'known' to be returned as a
430 * ccache. i.e, if the username is on the command line, we
431 * don't want to later guess to use a file-based ccache */
432 if (obtained > cred->ccache_threshold) {
433 cred->ccache_threshold = obtained;
436 cli_credentials_invalidate_client_gss_creds(cred,
437 obtained);
440 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
442 OM_uint32 min_stat;
443 (void)gss_release_cred(&min_stat, &gcc->creds);
444 return 0;
447 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
448 struct tevent_context *event_ctx,
449 struct loadparm_context *lp_ctx,
450 struct gssapi_creds_container **_gcc,
451 const char **error_string)
453 int ret = 0;
454 OM_uint32 maj_stat, min_stat;
455 struct gssapi_creds_container *gcc;
456 struct ccache_container *ccache;
457 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
458 krb5_enctype *etypes = NULL;
460 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
461 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
462 bool expired = false;
463 OM_uint32 lifetime = 0;
464 gss_cred_usage_t usage = 0;
465 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
466 NULL, &lifetime, &usage, NULL);
467 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
468 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
469 expired = true;
470 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
471 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
472 expired = true;
473 } else if (maj_stat != GSS_S_COMPLETE) {
474 *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
475 gssapi_error_string(cred, maj_stat, min_stat, NULL));
476 return EINVAL;
478 if (expired) {
479 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
480 } else {
481 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
482 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
484 *_gcc = cred->client_gss_creds;
485 return 0;
489 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
490 &ccache, error_string);
491 if (ret) {
492 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
493 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
494 } else {
495 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
497 return ret;
500 gcc = talloc(cred, struct gssapi_creds_container);
501 if (!gcc) {
502 (*error_string) = error_message(ENOMEM);
503 return ENOMEM;
506 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
507 &gcc->creds);
508 if ((maj_stat == GSS_S_FAILURE) && (min_stat == (OM_uint32)KRB5_CC_END || min_stat == (OM_uint32) KRB5_CC_NOTFOUND)) {
509 /* This CCACHE is no good. Ensure we don't use it again */
510 cli_credentials_unconditionally_invalidate_ccache(cred);
512 /* Now try again to get a ccache */
513 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
514 &ccache, error_string);
515 if (ret) {
516 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
517 return ret;
520 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
521 &gcc->creds);
525 if (maj_stat) {
526 talloc_free(gcc);
527 if (min_stat) {
528 ret = min_stat;
529 } else {
530 ret = EINVAL;
532 (*error_string) = talloc_asprintf(cred, "gss_krb5_import_cred failed: %s", error_message(ret));
533 return ret;
536 #ifdef SAMBA4_USES_HEIMDAL /* MIT lacks krb5_get_default_in_tkt_etypes */
538 * transfer the enctypes from the smb_krb5_context to the gssapi layer
540 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
541 * to configure the enctypes via the krb5.conf.
543 * And the gss_init_sec_context() creates it's own krb5_context and
544 * the TGS-REQ had all enctypes in it and only the ones configured
545 * and used for the AS-REQ, so it wasn't possible to disable the usage
546 * of AES keys.
548 min_stat = krb5_get_default_in_tkt_etypes(ccache->smb_krb5_context->krb5_context,
549 KRB5_PDU_NONE,
550 &etypes);
551 if (min_stat == 0) {
552 OM_uint32 num_ktypes;
554 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
556 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
557 num_ktypes,
558 (int32_t *) etypes);
559 krb5_xfree (etypes);
560 if (maj_stat) {
561 talloc_free(gcc);
562 if (min_stat) {
563 ret = min_stat;
564 } else {
565 ret = EINVAL;
567 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
568 return ret;
571 #endif
572 #ifdef SAMBA4_USES_HEIMDAL /* MIT lacks GSS_KRB5_CRED_NO_CI_FLAGS_X */
574 /* don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG */
575 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
576 GSS_KRB5_CRED_NO_CI_FLAGS_X,
577 &empty_buffer);
578 if (maj_stat) {
579 talloc_free(gcc);
580 if (min_stat) {
581 ret = min_stat;
582 } else {
583 ret = EINVAL;
585 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
586 return ret;
588 #endif
589 cred->client_gss_creds_obtained = cred->ccache_obtained;
590 talloc_set_destructor(gcc, free_gssapi_creds);
591 cred->client_gss_creds = gcc;
592 *_gcc = gcc;
593 return 0;
597 Set a gssapi cred_id_t into the credentials system. (Client case)
599 This grabs the credentials both 'intact' and getting the krb5
600 ccache out of it. This routine can be generalised in future for
601 the case where we deal with GSSAPI mechs other than krb5.
603 On sucess, the caller must not free gssapi_cred, as it now belongs
604 to the credentials system.
607 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
608 struct loadparm_context *lp_ctx,
609 gss_cred_id_t gssapi_cred,
610 enum credentials_obtained obtained,
611 const char **error_string)
613 int ret;
614 OM_uint32 maj_stat, min_stat;
615 struct ccache_container *ccc;
616 struct gssapi_creds_container *gcc;
617 if (cred->client_gss_creds_obtained > obtained) {
618 return 0;
621 gcc = talloc(cred, struct gssapi_creds_container);
622 if (!gcc) {
623 (*error_string) = error_message(ENOMEM);
624 return ENOMEM;
627 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
628 if (ret != 0) {
629 return ret;
632 maj_stat = gss_krb5_copy_ccache(&min_stat,
633 gssapi_cred, ccc->ccache);
634 if (maj_stat) {
635 if (min_stat) {
636 ret = min_stat;
637 } else {
638 ret = EINVAL;
640 if (ret) {
641 (*error_string) = error_message(ENOMEM);
645 if (ret == 0) {
646 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
648 cred->ccache = ccc;
649 cred->ccache_obtained = obtained;
650 if (ret == 0) {
651 gcc->creds = gssapi_cred;
652 talloc_set_destructor(gcc, free_gssapi_creds);
654 /* set the clinet_gss_creds_obtained here, as it just
655 got set to UNINITIALISED by the calls above */
656 cred->client_gss_creds_obtained = obtained;
657 cred->client_gss_creds = gcc;
659 return ret;
662 /* Get the keytab (actually, a container containing the krb5_keytab)
663 * attached to this context. If this hasn't been done or set before,
664 * it will be generated from the password.
666 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
667 struct loadparm_context *lp_ctx,
668 struct keytab_container **_ktc)
670 krb5_error_code ret;
671 struct keytab_container *ktc;
672 struct smb_krb5_context *smb_krb5_context;
673 const char *keytab_name;
674 krb5_keytab keytab;
675 TALLOC_CTX *mem_ctx;
677 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
678 cred->username_obtained))) {
679 *_ktc = cred->keytab;
680 return 0;
683 if (cli_credentials_is_anonymous(cred)) {
684 return EINVAL;
687 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
688 &smb_krb5_context);
689 if (ret) {
690 return ret;
693 mem_ctx = talloc_new(cred);
694 if (!mem_ctx) {
695 return ENOMEM;
698 ret = smb_krb5_create_memory_keytab(mem_ctx,
699 smb_krb5_context->krb5_context,
700 cli_credentials_get_password(cred),
701 cli_credentials_get_username(cred),
702 cli_credentials_get_realm(cred),
703 cli_credentials_get_kvno(cred),
704 &keytab, &keytab_name);
705 if (ret) {
706 talloc_free(mem_ctx);
707 return ret;
710 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
711 keytab, keytab_name, &ktc);
712 if (ret) {
713 talloc_free(mem_ctx);
714 return ret;
717 cred->keytab_obtained = (MAX(cred->principal_obtained,
718 cred->username_obtained));
720 /* We make this keytab up based on a password. Therefore
721 * match-by-key is acceptable, we can't match on the wrong
722 * principal */
723 ktc->password_based = true;
725 talloc_steal(cred, ktc);
726 cred->keytab = ktc;
727 *_ktc = cred->keytab;
728 talloc_free(mem_ctx);
729 return ret;
732 /* Given the name of a keytab (presumably in the format
733 * FILE:/etc/krb5.keytab), open it and attach it */
735 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
736 struct loadparm_context *lp_ctx,
737 const char *keytab_name,
738 enum credentials_obtained obtained)
740 krb5_error_code ret;
741 struct keytab_container *ktc;
742 struct smb_krb5_context *smb_krb5_context;
743 TALLOC_CTX *mem_ctx;
745 if (cred->keytab_obtained >= obtained) {
746 return 0;
749 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &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_get_keytab_container(mem_ctx, smb_krb5_context,
760 NULL, keytab_name, &ktc);
761 if (ret) {
762 return ret;
765 cred->keytab_obtained = obtained;
767 talloc_steal(cred, ktc);
768 cred->keytab = ktc;
769 talloc_free(mem_ctx);
771 return ret;
774 /* Get server gss credentials (in gsskrb5, this means the keytab) */
776 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
777 struct loadparm_context *lp_ctx,
778 struct gssapi_creds_container **_gcc)
780 int ret = 0;
781 OM_uint32 maj_stat, min_stat;
782 struct gssapi_creds_container *gcc;
783 struct keytab_container *ktc;
784 struct smb_krb5_context *smb_krb5_context;
785 TALLOC_CTX *mem_ctx;
786 krb5_principal princ;
787 const char *error_string;
788 enum credentials_obtained obtained;
790 mem_ctx = talloc_new(cred);
791 if (!mem_ctx) {
792 return ENOMEM;
795 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
796 if (ret) {
797 return ret;
800 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
801 if (ret) {
802 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
803 error_string));
804 talloc_free(mem_ctx);
805 return ret;
808 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
809 talloc_free(mem_ctx);
810 *_gcc = cred->server_gss_creds;
811 return 0;
814 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
815 if (ret) {
816 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
817 return ret;
820 gcc = talloc(cred, struct gssapi_creds_container);
821 if (!gcc) {
822 talloc_free(mem_ctx);
823 return ENOMEM;
826 if (ktc->password_based || obtained < CRED_SPECIFIED) {
827 /* This creates a GSSAPI cred_id_t for match-by-key with only the keytab set */
828 maj_stat = gss_krb5_import_cred(&min_stat, NULL, NULL, ktc->keytab,
829 &gcc->creds);
830 } else {
831 /* This creates a GSSAPI cred_id_t with the principal and keytab set, matching by name */
832 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
833 &gcc->creds);
835 if (maj_stat) {
836 if (min_stat) {
837 ret = min_stat;
838 } else {
839 ret = EINVAL;
842 if (ret == 0) {
843 cred->server_gss_creds_obtained = cred->keytab_obtained;
844 talloc_set_destructor(gcc, free_gssapi_creds);
845 cred->server_gss_creds = gcc;
846 *_gcc = gcc;
848 talloc_free(mem_ctx);
849 return ret;
852 /**
853 * Set Kerberos KVNO
856 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
857 int kvno)
859 cred->kvno = kvno;
863 * Return Kerberos KVNO
866 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
868 return cred->kvno;
872 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
874 return cred->salt_principal;
877 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
879 talloc_free(cred->salt_principal);
880 cred->salt_principal = talloc_strdup(cred, principal);
883 /* The 'impersonate_principal' is used to allow one Kerberos principal
884 * (and it's associated keytab etc) to impersonate another. The
885 * ability to do this is controlled by the KDC, but it is generally
886 * permitted to impersonate anyone to yourself. This allows any
887 * member of the domain to get the groups of a user. This is also
888 * known as S4U2Self */
890 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
892 return cred->impersonate_principal;
896 * The 'self_service' is the service principal that
897 * represents the same object (by its objectSid)
898 * as the client principal (typically our machine account).
899 * When trying to impersonate 'impersonate_principal' with
900 * S4U2Self.
902 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
904 return cred->self_service;
907 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
908 const char *principal,
909 const char *self_service)
911 talloc_free(cred->impersonate_principal);
912 cred->impersonate_principal = talloc_strdup(cred, principal);
913 talloc_free(cred->self_service);
914 cred->self_service = talloc_strdup(cred, self_service);
915 cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
919 * when impersonating for S4U2proxy we need to set the target principal.
920 * Similarly, we may only be authorized to do general impersonation to
921 * some particular services.
923 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
925 * NULL means that tickets will be obtained for the krbtgt service.
928 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
930 return cred->target_service;
933 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
935 talloc_free(cred->target_service);
936 cred->target_service = talloc_strdup(cred, target_service);