Fix bug #9085 - NMB registration for a duplicate workstation fails with registration...
[Samba/gebeck_regimport.git] / auth / credentials / credentials_krb5.c
blob2a23688ffdbbf04f8dc51f58e642b65e826aaf0c
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 talloc_steal(cred, ktc);
721 cred->keytab = ktc;
722 *_ktc = cred->keytab;
723 talloc_free(mem_ctx);
724 return ret;
727 /* Given the name of a keytab (presumably in the format
728 * FILE:/etc/krb5.keytab), open it and attach it */
730 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
731 struct loadparm_context *lp_ctx,
732 const char *keytab_name,
733 enum credentials_obtained obtained)
735 krb5_error_code ret;
736 struct keytab_container *ktc;
737 struct smb_krb5_context *smb_krb5_context;
738 TALLOC_CTX *mem_ctx;
740 if (cred->keytab_obtained >= obtained) {
741 return 0;
744 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
745 if (ret) {
746 return ret;
749 mem_ctx = talloc_new(cred);
750 if (!mem_ctx) {
751 return ENOMEM;
754 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
755 NULL, keytab_name, &ktc);
756 if (ret) {
757 return ret;
760 cred->keytab_obtained = obtained;
762 talloc_steal(cred, ktc);
763 cred->keytab = ktc;
764 talloc_free(mem_ctx);
766 return ret;
769 /* Get server gss credentials (in gsskrb5, this means the keytab) */
771 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
772 struct loadparm_context *lp_ctx,
773 struct gssapi_creds_container **_gcc)
775 int ret = 0;
776 OM_uint32 maj_stat, min_stat;
777 struct gssapi_creds_container *gcc;
778 struct keytab_container *ktc;
779 struct smb_krb5_context *smb_krb5_context;
780 TALLOC_CTX *mem_ctx;
781 krb5_principal princ;
782 const char *error_string;
783 enum credentials_obtained obtained;
785 mem_ctx = talloc_new(cred);
786 if (!mem_ctx) {
787 return ENOMEM;
790 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
791 if (ret) {
792 return ret;
795 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
796 if (ret) {
797 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
798 error_string));
799 talloc_free(mem_ctx);
800 return ret;
803 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
804 talloc_free(mem_ctx);
805 *_gcc = cred->server_gss_creds;
806 return 0;
809 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
810 if (ret) {
811 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
812 return ret;
815 gcc = talloc(cred, struct gssapi_creds_container);
816 if (!gcc) {
817 talloc_free(mem_ctx);
818 return ENOMEM;
821 if (obtained < CRED_SPECIFIED) {
822 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
823 maj_stat = gss_krb5_import_cred(&min_stat, NULL, NULL, ktc->keytab,
824 &gcc->creds);
825 } else {
826 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
827 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
828 &gcc->creds);
830 if (maj_stat) {
831 if (min_stat) {
832 ret = min_stat;
833 } else {
834 ret = EINVAL;
837 if (ret == 0) {
838 cred->server_gss_creds_obtained = cred->keytab_obtained;
839 talloc_set_destructor(gcc, free_gssapi_creds);
840 cred->server_gss_creds = gcc;
841 *_gcc = gcc;
843 talloc_free(mem_ctx);
844 return ret;
847 /**
848 * Set Kerberos KVNO
851 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
852 int kvno)
854 cred->kvno = kvno;
858 * Return Kerberos KVNO
861 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
863 return cred->kvno;
867 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
869 return cred->salt_principal;
872 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
874 talloc_free(cred->salt_principal);
875 cred->salt_principal = talloc_strdup(cred, principal);
878 /* The 'impersonate_principal' is used to allow one Kerberos principal
879 * (and it's associated keytab etc) to impersonate another. The
880 * ability to do this is controlled by the KDC, but it is generally
881 * permitted to impersonate anyone to yourself. This allows any
882 * member of the domain to get the groups of a user. This is also
883 * known as S4U2Self */
885 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
887 return cred->impersonate_principal;
891 * The 'self_service' is the service principal that
892 * represents the same object (by its objectSid)
893 * as the client principal (typically our machine account).
894 * When trying to impersonate 'impersonate_principal' with
895 * S4U2Self.
897 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
899 return cred->self_service;
902 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
903 const char *principal,
904 const char *self_service)
906 talloc_free(cred->impersonate_principal);
907 cred->impersonate_principal = talloc_strdup(cred, principal);
908 talloc_free(cred->self_service);
909 cred->self_service = talloc_strdup(cred, self_service);
910 cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
914 * when impersonating for S4U2proxy we need to set the target principal.
915 * Similarly, we may only be authorized to do general impersonation to
916 * some particular services.
918 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
920 * NULL means that tickets will be obtained for the krbtgt service.
923 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
925 return cred->target_service;
928 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
930 talloc_free(cred->target_service);
931 cred->target_service = talloc_strdup(cred, target_service);