test:dnsserver: Add zone creation and deletion test
[Samba.git] / auth / credentials / credentials_krb5.c
blob1e5600c2b1549307544d5263ab8d07a7a66da75b
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_util.h"
32 #include "param/param.h"
34 _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
35 struct loadparm_context *lp_ctx,
36 struct smb_krb5_context **smb_krb5_context)
38 int ret;
39 if (cred->smb_krb5_context) {
40 *smb_krb5_context = cred->smb_krb5_context;
41 return 0;
44 ret = smb_krb5_init_context(cred, NULL, lp_ctx,
45 &cred->smb_krb5_context);
46 if (ret) {
47 cred->smb_krb5_context = NULL;
48 return ret;
50 *smb_krb5_context = cred->smb_krb5_context;
51 return 0;
54 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
55 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
57 _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
58 struct smb_krb5_context *smb_krb5_context)
60 if (smb_krb5_context == NULL) {
61 talloc_unlink(cred, cred->smb_krb5_context);
62 cred->smb_krb5_context = NULL;
63 return NT_STATUS_OK;
66 if (!talloc_reference(cred, smb_krb5_context)) {
67 return NT_STATUS_NO_MEMORY;
69 cred->smb_krb5_context = smb_krb5_context;
70 return NT_STATUS_OK;
73 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
74 struct ccache_container *ccache,
75 enum credentials_obtained obtained,
76 const char **error_string)
79 krb5_principal princ;
80 krb5_error_code ret;
81 char *name;
83 if (cred->ccache_obtained > obtained) {
84 return 0;
87 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
88 ccache->ccache, &princ);
90 if (ret) {
91 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
92 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
93 ret, cred));
94 return ret;
97 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
98 if (ret) {
99 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
100 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
101 ret, cred));
102 return ret;
105 cli_credentials_set_principal(cred, name, obtained);
107 free(name);
109 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
111 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
112 cred->ccache_obtained = obtained;
114 return 0;
117 /* Free a memory ccache */
118 static int free_mccache(struct ccache_container *ccc)
120 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
122 return 0;
125 /* Free a disk-based ccache */
126 static int free_dccache(struct ccache_container *ccc) {
127 krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
129 return 0;
132 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
133 struct loadparm_context *lp_ctx,
134 const char *name,
135 enum credentials_obtained obtained,
136 const char **error_string)
138 krb5_error_code ret;
139 krb5_principal princ;
140 struct ccache_container *ccc;
141 if (cred->ccache_obtained > obtained) {
142 return 0;
145 ccc = talloc(cred, struct ccache_container);
146 if (!ccc) {
147 (*error_string) = error_message(ENOMEM);
148 return ENOMEM;
151 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
152 &ccc->smb_krb5_context);
153 if (ret) {
154 (*error_string) = error_message(ret);
155 talloc_free(ccc);
156 return ret;
158 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
159 talloc_free(ccc);
160 (*error_string) = error_message(ENOMEM);
161 return ENOMEM;
164 if (name) {
165 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
166 if (ret) {
167 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
168 name,
169 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
170 ret, ccc));
171 talloc_free(ccc);
172 return ret;
174 } else {
175 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
176 if (ret) {
177 (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
178 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
179 ret, ccc));
180 talloc_free(ccc);
181 return ret;
185 talloc_set_destructor(ccc, free_dccache);
187 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
189 if (ret == 0) {
190 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
191 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
193 if (ret) {
194 (*error_string) = error_message(ret);
195 return ret;
198 cred->ccache = ccc;
199 cred->ccache_obtained = obtained;
200 talloc_steal(cred, ccc);
202 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
203 return 0;
205 return 0;
209 static int cli_credentials_new_ccache(struct cli_credentials *cred,
210 struct loadparm_context *lp_ctx,
211 char *ccache_name,
212 struct ccache_container **_ccc,
213 const char **error_string)
215 bool must_free_cc_name = false;
216 krb5_error_code ret;
217 struct ccache_container *ccc = talloc(cred, struct ccache_container);
218 if (!ccc) {
219 return ENOMEM;
222 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
223 &ccc->smb_krb5_context);
224 if (ret) {
225 talloc_free(ccc);
226 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
227 error_message(ret));
228 return ret;
230 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
231 talloc_free(ccc);
232 (*error_string) = strerror(ENOMEM);
233 return ENOMEM;
236 if (!ccache_name) {
237 must_free_cc_name = true;
239 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
240 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
241 (unsigned int)getpid(), ccc);
242 } else {
243 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
244 ccc);
247 if (!ccache_name) {
248 talloc_free(ccc);
249 (*error_string) = strerror(ENOMEM);
250 return ENOMEM;
254 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
255 &ccc->ccache);
256 if (ret) {
257 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
258 ccache_name,
259 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
260 ret, ccc));
261 talloc_free(ccache_name);
262 talloc_free(ccc);
263 return ret;
266 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
267 talloc_set_destructor(ccc, free_mccache);
268 } else {
269 talloc_set_destructor(ccc, free_dccache);
272 if (must_free_cc_name) {
273 talloc_free(ccache_name);
276 *_ccc = ccc;
278 return 0;
281 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
282 struct tevent_context *event_ctx,
283 struct loadparm_context *lp_ctx,
284 char *ccache_name,
285 struct ccache_container **ccc,
286 const char **error_string)
288 krb5_error_code ret;
289 enum credentials_obtained obtained;
291 if (cred->machine_account_pending) {
292 cli_credentials_set_machine_account(cred, lp_ctx);
295 if (cred->ccache_obtained >= cred->ccache_threshold &&
296 cred->ccache_obtained > CRED_UNINITIALISED) {
297 time_t lifetime;
298 bool expired = false;
299 ret = krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
300 cred->ccache->ccache, &lifetime);
301 if (ret == KRB5_CC_END) {
302 /* If we have a particular ccache set, without
303 * an initial ticket, then assume there is a
304 * good reason */
305 } else if (ret == 0) {
306 if (lifetime == 0) {
307 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
308 cli_credentials_get_principal(cred, cred)));
309 expired = true;
310 } else if (lifetime < 300) {
311 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
312 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
313 expired = true;
315 } else {
316 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
317 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
318 ret, cred));
319 return ret;
322 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
323 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
325 if (!expired) {
326 *ccc = cred->ccache;
327 return 0;
330 if (cli_credentials_is_anonymous(cred)) {
331 (*error_string) = "Cannot get anonymous kerberos credentials";
332 return EINVAL;
335 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
336 if (ret) {
337 return ret;
340 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
341 if (ret) {
342 return ret;
345 ret = cli_credentials_set_from_ccache(cred, *ccc,
346 obtained, error_string);
348 cred->ccache = *ccc;
349 cred->ccache_obtained = cred->principal_obtained;
350 if (ret) {
351 return ret;
353 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
354 return 0;
357 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
358 struct tevent_context *event_ctx,
359 struct loadparm_context *lp_ctx,
360 struct ccache_container **ccc,
361 const char **error_string)
363 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
366 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
367 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
369 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
370 talloc_unlink(cred, cred->client_gss_creds);
371 cred->client_gss_creds = NULL;
373 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
376 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
377 enum credentials_obtained obtained)
379 /* If the caller just changed the username/password etc, then
380 * any cached credentials are now invalid */
381 if (obtained >= cred->client_gss_creds_obtained) {
382 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
383 talloc_unlink(cred, cred->client_gss_creds);
384 cred->client_gss_creds = NULL;
386 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
388 /* Now that we know that the data is 'this specified', then
389 * don't allow something less 'known' to be returned as a
390 * ccache. Ie, if the username is on the command line, we
391 * don't want to later guess to use a file-based ccache */
392 if (obtained > cred->client_gss_creds_threshold) {
393 cred->client_gss_creds_threshold = obtained;
397 /* We have good reason to think this CCACHE is invalid. Blow it away */
398 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
400 if (cred->ccache_obtained > CRED_UNINITIALISED) {
401 talloc_unlink(cred, cred->ccache);
402 cred->ccache = NULL;
404 cred->ccache_obtained = CRED_UNINITIALISED;
406 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
409 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
410 enum credentials_obtained obtained)
412 /* If the caller just changed the username/password etc, then
413 * any cached credentials are now invalid */
414 if (obtained >= cred->ccache_obtained) {
415 if (cred->ccache_obtained > CRED_UNINITIALISED) {
416 talloc_unlink(cred, cred->ccache);
417 cred->ccache = NULL;
419 cred->ccache_obtained = CRED_UNINITIALISED;
421 /* Now that we know that the data is 'this specified', then
422 * don't allow something less 'known' to be returned as a
423 * ccache. i.e, if the username is on the command line, we
424 * don't want to later guess to use a file-based ccache */
425 if (obtained > cred->ccache_threshold) {
426 cred->ccache_threshold = obtained;
429 cli_credentials_invalidate_client_gss_creds(cred,
430 obtained);
433 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
435 OM_uint32 min_stat;
436 (void)gss_release_cred(&min_stat, &gcc->creds);
437 return 0;
440 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
441 struct tevent_context *event_ctx,
442 struct loadparm_context *lp_ctx,
443 struct gssapi_creds_container **_gcc,
444 const char **error_string)
446 int ret = 0;
447 OM_uint32 maj_stat, min_stat;
448 struct gssapi_creds_container *gcc;
449 struct ccache_container *ccache;
450 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
451 krb5_enctype *etypes = NULL;
453 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
454 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
455 bool expired = false;
456 OM_uint32 lifetime = 0;
457 gss_cred_usage_t usage = 0;
458 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
459 NULL, &lifetime, &usage, NULL);
460 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
461 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
462 expired = true;
463 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
464 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
465 expired = true;
466 } else if (maj_stat != GSS_S_COMPLETE) {
467 *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
468 gssapi_error_string(cred, maj_stat, min_stat, NULL));
469 return EINVAL;
471 if (expired) {
472 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
473 } else {
474 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
475 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
477 *_gcc = cred->client_gss_creds;
478 return 0;
482 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
483 &ccache, error_string);
484 if (ret) {
485 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
486 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", error_message(ret)));
487 } else {
488 DEBUG(4, ("Failed to get kerberos credentials: %s\n", error_message(ret)));
490 return ret;
493 gcc = talloc(cred, struct gssapi_creds_container);
494 if (!gcc) {
495 (*error_string) = error_message(ENOMEM);
496 return ENOMEM;
499 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
500 &gcc->creds);
501 if ((maj_stat == GSS_S_FAILURE) && (min_stat == (OM_uint32)KRB5_CC_END || min_stat == (OM_uint32) KRB5_CC_NOTFOUND)) {
502 /* This CCACHE is no good. Ensure we don't use it again */
503 cli_credentials_unconditionally_invalidate_ccache(cred);
505 /* Now try again to get a ccache */
506 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
507 &ccache, error_string);
508 if (ret) {
509 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
510 return ret;
513 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
514 &gcc->creds);
518 if (maj_stat) {
519 talloc_free(gcc);
520 if (min_stat) {
521 ret = min_stat;
522 } else {
523 ret = EINVAL;
525 (*error_string) = talloc_asprintf(cred, "gss_krb5_import_cred failed: %s", error_message(ret));
526 return ret;
530 * transfer the enctypes from the smb_krb5_context to the gssapi layer
532 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
533 * to configure the enctypes via the krb5.conf.
535 * And the gss_init_sec_context() creates it's own krb5_context and
536 * the TGS-REQ had all enctypes in it and only the ones configured
537 * and used for the AS-REQ, so it wasn't possible to disable the usage
538 * of AES keys.
540 min_stat = krb5_get_default_in_tkt_etypes(ccache->smb_krb5_context->krb5_context,
541 KRB5_PDU_NONE,
542 &etypes);
543 if (min_stat == 0) {
544 OM_uint32 num_ktypes;
546 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
548 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
549 num_ktypes,
550 (int32_t *) etypes);
551 krb5_xfree (etypes);
552 if (maj_stat) {
553 talloc_free(gcc);
554 if (min_stat) {
555 ret = min_stat;
556 } else {
557 ret = EINVAL;
559 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
560 return ret;
564 /* don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG */
565 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
566 GSS_KRB5_CRED_NO_CI_FLAGS_X,
567 &empty_buffer);
568 if (maj_stat) {
569 talloc_free(gcc);
570 if (min_stat) {
571 ret = min_stat;
572 } else {
573 ret = EINVAL;
575 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
576 return ret;
579 cred->client_gss_creds_obtained = cred->ccache_obtained;
580 talloc_set_destructor(gcc, free_gssapi_creds);
581 cred->client_gss_creds = gcc;
582 *_gcc = gcc;
583 return 0;
587 Set a gssapi cred_id_t into the credentials system. (Client case)
589 This grabs the credentials both 'intact' and getting the krb5
590 ccache out of it. This routine can be generalised in future for
591 the case where we deal with GSSAPI mechs other than krb5.
593 On sucess, the caller must not free gssapi_cred, as it now belongs
594 to the credentials system.
597 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
598 struct loadparm_context *lp_ctx,
599 gss_cred_id_t gssapi_cred,
600 enum credentials_obtained obtained,
601 const char **error_string)
603 int ret;
604 OM_uint32 maj_stat, min_stat;
605 struct ccache_container *ccc;
606 struct gssapi_creds_container *gcc;
607 if (cred->client_gss_creds_obtained > obtained) {
608 return 0;
611 gcc = talloc(cred, struct gssapi_creds_container);
612 if (!gcc) {
613 (*error_string) = error_message(ENOMEM);
614 return ENOMEM;
617 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
618 if (ret != 0) {
619 return ret;
622 maj_stat = gss_krb5_copy_ccache(&min_stat,
623 gssapi_cred, ccc->ccache);
624 if (maj_stat) {
625 if (min_stat) {
626 ret = min_stat;
627 } else {
628 ret = EINVAL;
630 if (ret) {
631 (*error_string) = error_message(ENOMEM);
635 if (ret == 0) {
636 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
638 cred->ccache = ccc;
639 cred->ccache_obtained = obtained;
640 if (ret == 0) {
641 gcc->creds = gssapi_cred;
642 talloc_set_destructor(gcc, free_gssapi_creds);
644 /* set the clinet_gss_creds_obtained here, as it just
645 got set to UNINITIALISED by the calls above */
646 cred->client_gss_creds_obtained = obtained;
647 cred->client_gss_creds = gcc;
649 return ret;
652 /* Get the keytab (actually, a container containing the krb5_keytab)
653 * attached to this context. If this hasn't been done or set before,
654 * it will be generated from the password.
656 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
657 struct loadparm_context *lp_ctx,
658 struct keytab_container **_ktc)
660 krb5_error_code ret;
661 struct keytab_container *ktc;
662 struct smb_krb5_context *smb_krb5_context;
663 TALLOC_CTX *mem_ctx;
665 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
666 cred->username_obtained))) {
667 *_ktc = cred->keytab;
668 return 0;
671 if (cli_credentials_is_anonymous(cred)) {
672 return EINVAL;
675 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
676 &smb_krb5_context);
677 if (ret) {
678 return ret;
681 mem_ctx = talloc_new(cred);
682 if (!mem_ctx) {
683 return ENOMEM;
686 ret = smb_krb5_create_memory_keytab(mem_ctx, cred,
687 smb_krb5_context, &ktc);
688 if (ret) {
689 talloc_free(mem_ctx);
690 return ret;
693 cred->keytab_obtained = (MAX(cred->principal_obtained,
694 cred->username_obtained));
696 talloc_steal(cred, ktc);
697 cred->keytab = ktc;
698 *_ktc = cred->keytab;
699 talloc_free(mem_ctx);
700 return ret;
703 /* Given the name of a keytab (presumably in the format
704 * FILE:/etc/krb5.keytab), open it and attach it */
706 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
707 struct loadparm_context *lp_ctx,
708 const char *keytab_name,
709 enum credentials_obtained obtained)
711 krb5_error_code ret;
712 struct keytab_container *ktc;
713 struct smb_krb5_context *smb_krb5_context;
714 TALLOC_CTX *mem_ctx;
716 if (cred->keytab_obtained >= obtained) {
717 return 0;
720 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
721 if (ret) {
722 return ret;
725 mem_ctx = talloc_new(cred);
726 if (!mem_ctx) {
727 return ENOMEM;
730 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
731 keytab_name, &ktc);
732 if (ret) {
733 return ret;
736 cred->keytab_obtained = obtained;
738 talloc_steal(cred, ktc);
739 cred->keytab = ktc;
740 talloc_free(mem_ctx);
742 return ret;
745 /* Get server gss credentials (in gsskrb5, this means the keytab) */
747 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
748 struct loadparm_context *lp_ctx,
749 struct gssapi_creds_container **_gcc)
751 int ret = 0;
752 OM_uint32 maj_stat, min_stat;
753 struct gssapi_creds_container *gcc;
754 struct keytab_container *ktc;
755 struct smb_krb5_context *smb_krb5_context;
756 TALLOC_CTX *mem_ctx;
757 krb5_principal princ;
758 const char *error_string;
759 enum credentials_obtained obtained;
761 mem_ctx = talloc_new(cred);
762 if (!mem_ctx) {
763 return ENOMEM;
766 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
767 if (ret) {
768 return ret;
771 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
772 if (ret) {
773 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
774 error_string));
775 talloc_free(mem_ctx);
776 return ret;
779 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
780 talloc_free(mem_ctx);
781 *_gcc = cred->server_gss_creds;
782 return 0;
785 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
786 if (ret) {
787 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
788 return ret;
791 gcc = talloc(cred, struct gssapi_creds_container);
792 if (!gcc) {
793 talloc_free(mem_ctx);
794 return ENOMEM;
797 if (obtained < CRED_SPECIFIED) {
798 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
799 maj_stat = gss_krb5_import_cred(&min_stat, NULL, NULL, ktc->keytab,
800 &gcc->creds);
801 } else {
802 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
803 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
804 &gcc->creds);
806 if (maj_stat) {
807 if (min_stat) {
808 ret = min_stat;
809 } else {
810 ret = EINVAL;
813 if (ret == 0) {
814 cred->server_gss_creds_obtained = cred->keytab_obtained;
815 talloc_set_destructor(gcc, free_gssapi_creds);
816 cred->server_gss_creds = gcc;
817 *_gcc = gcc;
819 talloc_free(mem_ctx);
820 return ret;
823 /**
824 * Set Kerberos KVNO
827 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
828 int kvno)
830 cred->kvno = kvno;
834 * Return Kerberos KVNO
837 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
839 return cred->kvno;
843 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
845 return cred->salt_principal;
848 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
850 talloc_free(cred->salt_principal);
851 cred->salt_principal = talloc_strdup(cred, principal);
854 /* The 'impersonate_principal' is used to allow one Kerberos principal
855 * (and it's associated keytab etc) to impersonate another. The
856 * ability to do this is controlled by the KDC, but it is generally
857 * permitted to impersonate anyone to yourself. This allows any
858 * member of the domain to get the groups of a user. This is also
859 * known as S4U2Self */
861 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
863 return cred->impersonate_principal;
867 * The 'self_service' is the service principal that
868 * represents the same object (by its objectSid)
869 * as the client principal (typically our machine account).
870 * When trying to impersonate 'impersonate_principal' with
871 * S4U2Self.
873 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
875 return cred->self_service;
878 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
879 const char *principal,
880 const char *self_service)
882 talloc_free(cred->impersonate_principal);
883 cred->impersonate_principal = talloc_strdup(cred, principal);
884 talloc_free(cred->self_service);
885 cred->self_service = talloc_strdup(cred, self_service);
886 cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
890 * when impersonating for S4U2proxy we need to set the target principal.
891 * Similarly, we may only be authorized to do general impersonation to
892 * some particular services.
894 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
896 * NULL means that tickets will be obtained for the krbtgt service.
899 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
901 return cred->target_service;
904 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
906 talloc_free(cred->target_service);
907 cred->target_service = talloc_strdup(cred, target_service);