docs:smbdotconf: fix type of "preferred master" parameter.
[Samba.git] / auth / credentials / credentials_krb5.c
blob6d0ef6f953e6c510f1dc7c40c17b87e62fa403a0
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_internal.h"
30 #include "auth/credentials/credentials_proto.h"
31 #include "auth/credentials/credentials_krb5.h"
32 #include "auth/kerberos/kerberos_credentials.h"
33 #include "auth/kerberos/kerberos_srv_keytab.h"
34 #include "auth/kerberos/kerberos_util.h"
35 #include "auth/kerberos/pac_utils.h"
36 #include "param/param.h"
38 static void cli_credentials_invalidate_client_gss_creds(
39 struct cli_credentials *cred,
40 enum credentials_obtained obtained);
42 _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
43 struct loadparm_context *lp_ctx,
44 struct smb_krb5_context **smb_krb5_context)
46 int ret;
47 if (cred->smb_krb5_context) {
48 *smb_krb5_context = cred->smb_krb5_context;
49 return 0;
52 ret = smb_krb5_init_context(cred, lp_ctx,
53 &cred->smb_krb5_context);
54 if (ret) {
55 cred->smb_krb5_context = NULL;
56 return ret;
58 *smb_krb5_context = cred->smb_krb5_context;
59 return 0;
62 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
63 * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
65 _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
66 struct smb_krb5_context *smb_krb5_context)
68 if (smb_krb5_context == NULL) {
69 talloc_unlink(cred, cred->smb_krb5_context);
70 cred->smb_krb5_context = NULL;
71 return NT_STATUS_OK;
74 if (!talloc_reference(cred, smb_krb5_context)) {
75 return NT_STATUS_NO_MEMORY;
77 cred->smb_krb5_context = smb_krb5_context;
78 return NT_STATUS_OK;
81 static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
82 struct ccache_container *ccache,
83 enum credentials_obtained obtained,
84 const char **error_string)
87 krb5_principal princ;
88 krb5_error_code ret;
89 char *name;
91 if (cred->ccache_obtained > obtained) {
92 return 0;
95 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
96 ccache->ccache, &princ);
98 if (ret) {
99 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
100 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
101 ret, cred));
102 return ret;
105 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
106 if (ret) {
107 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
108 smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
109 ret, cred));
110 return ret;
113 cli_credentials_set_principal(cred, name, obtained);
115 free(name);
117 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
119 /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
120 cred->ccache_obtained = obtained;
122 return 0;
125 /* Free a memory ccache */
126 static int free_mccache(struct ccache_container *ccc)
128 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
130 return 0;
133 /* Free a disk-based ccache */
134 static int free_dccache(struct ccache_container *ccc) {
135 krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
137 return 0;
140 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
141 struct loadparm_context *lp_ctx,
142 const char *name,
143 enum credentials_obtained obtained,
144 const char **error_string)
146 krb5_error_code ret;
147 krb5_principal princ;
148 struct ccache_container *ccc;
149 if (cred->ccache_obtained > obtained) {
150 return 0;
153 ccc = talloc(cred, struct ccache_container);
154 if (!ccc) {
155 (*error_string) = error_message(ENOMEM);
156 return ENOMEM;
159 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
160 &ccc->smb_krb5_context);
161 if (ret) {
162 (*error_string) = error_message(ret);
163 talloc_free(ccc);
164 return ret;
166 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
167 talloc_free(ccc);
168 (*error_string) = error_message(ENOMEM);
169 return ENOMEM;
172 if (name) {
173 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
174 if (ret) {
175 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
176 name,
177 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
178 ret, ccc));
179 talloc_free(ccc);
180 return ret;
182 } else {
183 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
184 if (ret) {
185 (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
186 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
187 ret, ccc));
188 talloc_free(ccc);
189 return ret;
193 talloc_set_destructor(ccc, free_dccache);
195 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
197 if (ret == 0) {
198 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
199 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
201 if (ret) {
202 (*error_string) = error_message(ret);
203 return ret;
206 cred->ccache = ccc;
207 cred->ccache_obtained = obtained;
208 talloc_steal(cred, ccc);
210 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
211 return 0;
213 return 0;
217 * Indicate the we failed to log in to this service/host with these
218 * credentials. The caller passes an unsigned int which they
219 * initialise to the number of times they would like to retry.
221 * This method is used to support re-trying with freshly fetched
222 * credentials in case a server is rebuilt while clients have
223 * non-expired tickets. When the client code gets a logon failure they
224 * throw away the existing credentials for the server and retry.
226 _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
227 const char *principal,
228 unsigned int *count)
230 struct ccache_container *ccc;
231 krb5_creds creds, creds2;
232 int ret;
234 if (principal == NULL) {
235 /* no way to delete if we don't know the principal */
236 return false;
239 ccc = cred->ccache;
240 if (ccc == NULL) {
241 /* not a kerberos connection */
242 return false;
245 if (*count > 0) {
246 /* We have already tried discarding the credentials */
247 return false;
249 (*count)++;
251 ZERO_STRUCT(creds);
252 ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
253 if (ret != 0) {
254 return false;
257 ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
258 if (ret != 0) {
259 /* don't retry - we didn't find these credentials to remove */
260 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
261 return false;
264 ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
265 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
266 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
267 if (ret != 0) {
268 /* don't retry - we didn't find these credentials to
269 * remove. Note that with the current backend this
270 * never happens, as it always returns 0 even if the
271 * creds don't exist, which is why we do a separate
272 * krb5_cc_retrieve_cred() above.
274 return false;
276 return true;
280 static int cli_credentials_new_ccache(struct cli_credentials *cred,
281 struct loadparm_context *lp_ctx,
282 char *ccache_name,
283 struct ccache_container **_ccc,
284 const char **error_string)
286 bool must_free_cc_name = false;
287 krb5_error_code ret;
288 struct ccache_container *ccc = talloc(cred, struct ccache_container);
289 if (!ccc) {
290 return ENOMEM;
293 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
294 &ccc->smb_krb5_context);
295 if (ret) {
296 talloc_free(ccc);
297 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
298 error_message(ret));
299 return ret;
301 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
302 talloc_free(ccc);
303 (*error_string) = strerror(ENOMEM);
304 return ENOMEM;
307 if (!ccache_name) {
308 must_free_cc_name = true;
310 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
311 ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
312 (unsigned int)getpid(), ccc);
313 } else {
314 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
315 ccc);
318 if (!ccache_name) {
319 talloc_free(ccc);
320 (*error_string) = strerror(ENOMEM);
321 return ENOMEM;
325 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
326 &ccc->ccache);
327 if (ret) {
328 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
329 ccache_name,
330 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
331 ret, ccc));
332 talloc_free(ccache_name);
333 talloc_free(ccc);
334 return ret;
337 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
338 talloc_set_destructor(ccc, free_mccache);
339 } else {
340 talloc_set_destructor(ccc, free_dccache);
343 if (must_free_cc_name) {
344 talloc_free(ccache_name);
347 *_ccc = ccc;
349 return 0;
352 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
353 struct tevent_context *event_ctx,
354 struct loadparm_context *lp_ctx,
355 char *ccache_name,
356 struct ccache_container **ccc,
357 const char **error_string)
359 krb5_error_code ret;
360 enum credentials_obtained obtained;
362 if (cred->machine_account_pending) {
363 cli_credentials_set_machine_account(cred, lp_ctx);
366 if (cred->ccache_obtained >= cred->ccache_threshold &&
367 cred->ccache_obtained > CRED_UNINITIALISED) {
368 time_t lifetime;
369 bool expired = false;
370 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
371 cred->ccache->ccache, &lifetime);
372 if (ret == KRB5_CC_END) {
373 /* If we have a particular ccache set, without
374 * an initial ticket, then assume there is a
375 * good reason */
376 } else if (ret == 0) {
377 if (lifetime == 0) {
378 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
379 cli_credentials_get_principal(cred, cred)));
380 expired = true;
381 } else if (lifetime < 300) {
382 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
383 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
384 expired = true;
386 } else {
387 (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
388 smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
389 ret, cred));
390 return ret;
393 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
394 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
396 if (!expired) {
397 *ccc = cred->ccache;
398 return 0;
401 if (cli_credentials_is_anonymous(cred)) {
402 (*error_string) = "Cannot get anonymous kerberos credentials";
403 return EINVAL;
406 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
407 if (ret) {
408 return ret;
411 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
412 if (ret) {
413 return ret;
416 ret = cli_credentials_set_from_ccache(cred, *ccc,
417 obtained, error_string);
419 cred->ccache = *ccc;
420 cred->ccache_obtained = cred->principal_obtained;
421 if (ret) {
422 return ret;
424 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
425 return 0;
428 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
429 struct tevent_context *event_ctx,
430 struct loadparm_context *lp_ctx,
431 struct ccache_container **ccc,
432 const char **error_string)
434 return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
437 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
438 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
440 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
441 talloc_unlink(cred, cred->client_gss_creds);
442 cred->client_gss_creds = NULL;
444 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
447 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
448 enum credentials_obtained obtained)
450 /* If the caller just changed the username/password etc, then
451 * any cached credentials are now invalid */
452 if (obtained >= cred->client_gss_creds_obtained) {
453 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
454 talloc_unlink(cred, cred->client_gss_creds);
455 cred->client_gss_creds = NULL;
457 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
459 /* Now that we know that the data is 'this specified', then
460 * don't allow something less 'known' to be returned as a
461 * ccache. Ie, if the username is on the command line, we
462 * don't want to later guess to use a file-based ccache */
463 if (obtained > cred->client_gss_creds_threshold) {
464 cred->client_gss_creds_threshold = obtained;
468 /* We have good reason to think this CCACHE is invalid. Blow it away */
469 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
471 if (cred->ccache_obtained > CRED_UNINITIALISED) {
472 talloc_unlink(cred, cred->ccache);
473 cred->ccache = NULL;
475 cred->ccache_obtained = CRED_UNINITIALISED;
477 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
480 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
481 enum credentials_obtained obtained)
483 /* If the caller just changed the username/password etc, then
484 * any cached credentials are now invalid */
485 if (obtained >= cred->ccache_obtained) {
486 if (cred->ccache_obtained > CRED_UNINITIALISED) {
487 talloc_unlink(cred, cred->ccache);
488 cred->ccache = NULL;
490 cred->ccache_obtained = CRED_UNINITIALISED;
492 /* Now that we know that the data is 'this specified', then
493 * don't allow something less 'known' to be returned as a
494 * ccache. i.e, if the username is on the command line, we
495 * don't want to later guess to use a file-based ccache */
496 if (obtained > cred->ccache_threshold) {
497 cred->ccache_threshold = obtained;
500 cli_credentials_invalidate_client_gss_creds(cred,
501 obtained);
504 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
506 OM_uint32 min_stat;
507 (void)gss_release_cred(&min_stat, &gcc->creds);
508 return 0;
511 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
512 struct tevent_context *event_ctx,
513 struct loadparm_context *lp_ctx,
514 struct gssapi_creds_container **_gcc,
515 const char **error_string)
517 int ret = 0;
518 OM_uint32 maj_stat, min_stat;
519 struct gssapi_creds_container *gcc;
520 struct ccache_container *ccache;
521 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
522 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
523 #endif
524 krb5_enctype *etypes = NULL;
526 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
527 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
528 bool expired = false;
529 OM_uint32 lifetime = 0;
530 gss_cred_usage_t usage = 0;
531 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
532 NULL, &lifetime, &usage, NULL);
533 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
534 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
535 expired = true;
536 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
537 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
538 expired = true;
539 } else if (maj_stat != GSS_S_COMPLETE) {
540 *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
541 gssapi_error_string(cred, maj_stat, min_stat, NULL));
542 return EINVAL;
544 if (expired) {
545 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
546 } else {
547 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
548 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
550 *_gcc = cred->client_gss_creds;
551 return 0;
555 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
556 &ccache, error_string);
557 if (ret) {
558 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
559 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
560 } else {
561 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
563 return ret;
566 gcc = talloc(cred, struct gssapi_creds_container);
567 if (!gcc) {
568 (*error_string) = error_message(ENOMEM);
569 return ENOMEM;
572 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
573 &gcc->creds);
574 if ((maj_stat == GSS_S_FAILURE) && (min_stat == (OM_uint32)KRB5_CC_END || min_stat == (OM_uint32) KRB5_CC_NOTFOUND)) {
575 /* This CCACHE is no good. Ensure we don't use it again */
576 cli_credentials_unconditionally_invalidate_ccache(cred);
578 /* Now try again to get a ccache */
579 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
580 &ccache, error_string);
581 if (ret) {
582 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
583 return ret;
586 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
587 &gcc->creds);
591 if (maj_stat) {
592 talloc_free(gcc);
593 if (min_stat) {
594 ret = min_stat;
595 } else {
596 ret = EINVAL;
598 (*error_string) = talloc_asprintf(cred, "gss_krb5_import_cred failed: %s", error_message(ret));
599 return ret;
604 * transfer the enctypes from the smb_krb5_context to the gssapi layer
606 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
607 * to configure the enctypes via the krb5.conf.
609 * And the gss_init_sec_context() creates it's own krb5_context and
610 * the TGS-REQ had all enctypes in it and only the ones configured
611 * and used for the AS-REQ, so it wasn't possible to disable the usage
612 * of AES keys.
614 min_stat = get_kerberos_allowed_etypes(ccache->smb_krb5_context->krb5_context,
615 &etypes);
616 if (min_stat == 0) {
617 OM_uint32 num_ktypes;
619 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
621 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
622 num_ktypes,
623 (int32_t *) etypes);
624 SAFE_FREE(etypes);
625 if (maj_stat) {
626 talloc_free(gcc);
627 if (min_stat) {
628 ret = min_stat;
629 } else {
630 ret = EINVAL;
632 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
633 return ret;
637 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
639 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
641 * This allows us to disable SIGN and SEAL on a TLS connection with
642 * GSS-SPNENO. For example ldaps:// connections.
644 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
645 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
647 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
648 GSS_KRB5_CRED_NO_CI_FLAGS_X,
649 &empty_buffer);
650 if (maj_stat) {
651 talloc_free(gcc);
652 if (min_stat) {
653 ret = min_stat;
654 } else {
655 ret = EINVAL;
657 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
658 return ret;
660 #endif
661 cred->client_gss_creds_obtained = cred->ccache_obtained;
662 talloc_set_destructor(gcc, free_gssapi_creds);
663 cred->client_gss_creds = gcc;
664 *_gcc = gcc;
665 return 0;
669 Set a gssapi cred_id_t into the credentials system. (Client case)
671 This grabs the credentials both 'intact' and getting the krb5
672 ccache out of it. This routine can be generalised in future for
673 the case where we deal with GSSAPI mechs other than krb5.
675 On sucess, the caller must not free gssapi_cred, as it now belongs
676 to the credentials system.
679 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
680 struct loadparm_context *lp_ctx,
681 gss_cred_id_t gssapi_cred,
682 enum credentials_obtained obtained,
683 const char **error_string)
685 int ret;
686 OM_uint32 maj_stat, min_stat;
687 struct ccache_container *ccc;
688 struct gssapi_creds_container *gcc;
689 if (cred->client_gss_creds_obtained > obtained) {
690 return 0;
693 gcc = talloc(cred, struct gssapi_creds_container);
694 if (!gcc) {
695 (*error_string) = error_message(ENOMEM);
696 return ENOMEM;
699 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
700 if (ret != 0) {
701 return ret;
704 maj_stat = gss_krb5_copy_ccache(&min_stat,
705 gssapi_cred, ccc->ccache);
706 if (maj_stat) {
707 if (min_stat) {
708 ret = min_stat;
709 } else {
710 ret = EINVAL;
712 if (ret) {
713 (*error_string) = error_message(ENOMEM);
717 if (ret == 0) {
718 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
720 cred->ccache = ccc;
721 cred->ccache_obtained = obtained;
722 if (ret == 0) {
723 gcc->creds = gssapi_cred;
724 talloc_set_destructor(gcc, free_gssapi_creds);
726 /* set the clinet_gss_creds_obtained here, as it just
727 got set to UNINITIALISED by the calls above */
728 cred->client_gss_creds_obtained = obtained;
729 cred->client_gss_creds = gcc;
731 return ret;
734 static int smb_krb5_create_salt_principal(TALLOC_CTX *mem_ctx,
735 const char *samAccountName,
736 const char *realm,
737 const char **salt_principal,
738 const char **error_string)
740 char *machine_username;
741 bool is_machine_account = false;
742 char *upper_realm;
743 TALLOC_CTX *tmp_ctx;
744 int rc = -1;
746 if (samAccountName == NULL) {
747 *error_string = "Cannot determine salt principal, no "
748 "saltPrincipal or samAccountName specified";
749 return rc;
752 if (realm == NULL) {
753 *error_string = "Cannot make principal without a realm";
754 return rc;
757 tmp_ctx = talloc_new(mem_ctx);
758 if (tmp_ctx == NULL) {
759 *error_string = "Cannot allocate talloc context";
760 return rc;
763 upper_realm = strupper_talloc(tmp_ctx, realm);
764 if (upper_realm == NULL) {
765 *error_string = "Cannot allocate to upper case realm";
766 goto out;
769 machine_username = strlower_talloc(tmp_ctx, samAccountName);
770 if (!machine_username) {
771 *error_string = "Cannot duplicate samAccountName";
772 goto out;
775 if (machine_username[strlen(machine_username) - 1] == '$') {
776 machine_username[strlen(machine_username) - 1] = '\0';
777 is_machine_account = true;
780 if (is_machine_account) {
781 char *lower_realm;
783 lower_realm = strlower_talloc(tmp_ctx, realm);
784 if (lower_realm == NULL) {
785 *error_string = "Cannot allocate to lower case realm";
786 goto out;
789 *salt_principal = talloc_asprintf(mem_ctx,
790 "host/%s.%s@%s",
791 machine_username,
792 lower_realm,
793 upper_realm);
794 } else {
795 *salt_principal = talloc_asprintf(mem_ctx,
796 "%s@%s",
797 machine_username,
798 upper_realm);
800 if (*salt_principal == NULL) {
801 *error_string = "Cannot create salt principal";
802 goto out;
805 rc = 0;
806 out:
807 talloc_free(tmp_ctx);
808 return rc;
811 /* Get the keytab (actually, a container containing the krb5_keytab)
812 * attached to this context. If this hasn't been done or set before,
813 * it will be generated from the password.
815 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
816 struct loadparm_context *lp_ctx,
817 struct keytab_container **_ktc)
819 krb5_error_code ret;
820 struct keytab_container *ktc;
821 struct smb_krb5_context *smb_krb5_context;
822 const char *keytab_name;
823 krb5_keytab keytab;
824 TALLOC_CTX *mem_ctx;
825 const char *username = cli_credentials_get_username(cred);
826 const char *realm = cli_credentials_get_realm(cred);
827 const char *error_string;
828 const char *salt_principal;
830 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
831 cred->username_obtained))) {
832 *_ktc = cred->keytab;
833 return 0;
836 if (cli_credentials_is_anonymous(cred)) {
837 return EINVAL;
840 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
841 &smb_krb5_context);
842 if (ret) {
843 return ret;
846 mem_ctx = talloc_new(cred);
847 if (!mem_ctx) {
848 return ENOMEM;
852 * FIXME: Currently there is no better way than to create the correct
853 * salt principal by checking if the username ends with a '$'. It would
854 * be better if it is part of the credentials.
856 ret = smb_krb5_create_salt_principal(mem_ctx,
857 username,
858 realm,
859 &salt_principal,
860 &error_string);
861 if (ret) {
862 talloc_free(mem_ctx);
863 return ret;
866 ret = smb_krb5_create_memory_keytab(mem_ctx,
867 smb_krb5_context->krb5_context,
868 cli_credentials_get_password(cred),
869 username,
870 realm,
871 salt_principal,
872 cli_credentials_get_kvno(cred),
873 &keytab,
874 &keytab_name);
875 if (ret) {
876 talloc_free(mem_ctx);
877 return ret;
880 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
881 keytab, keytab_name, &ktc);
882 if (ret) {
883 talloc_free(mem_ctx);
884 return ret;
887 cred->keytab_obtained = (MAX(cred->principal_obtained,
888 cred->username_obtained));
890 /* We make this keytab up based on a password. Therefore
891 * match-by-key is acceptable, we can't match on the wrong
892 * principal */
893 ktc->password_based = true;
895 talloc_steal(cred, ktc);
896 cred->keytab = ktc;
897 *_ktc = cred->keytab;
898 talloc_free(mem_ctx);
899 return ret;
902 /* Given the name of a keytab (presumably in the format
903 * FILE:/etc/krb5.keytab), open it and attach it */
905 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
906 struct loadparm_context *lp_ctx,
907 const char *keytab_name,
908 enum credentials_obtained obtained)
910 krb5_error_code ret;
911 struct keytab_container *ktc;
912 struct smb_krb5_context *smb_krb5_context;
913 TALLOC_CTX *mem_ctx;
915 if (cred->keytab_obtained >= obtained) {
916 return 0;
919 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
920 if (ret) {
921 return ret;
924 mem_ctx = talloc_new(cred);
925 if (!mem_ctx) {
926 return ENOMEM;
929 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
930 NULL, keytab_name, &ktc);
931 if (ret) {
932 return ret;
935 cred->keytab_obtained = obtained;
937 talloc_steal(cred, ktc);
938 cred->keytab = ktc;
939 talloc_free(mem_ctx);
941 return ret;
944 /* Get server gss credentials (in gsskrb5, this means the keytab) */
946 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
947 struct loadparm_context *lp_ctx,
948 struct gssapi_creds_container **_gcc)
950 int ret = 0;
951 OM_uint32 maj_stat, min_stat;
952 struct gssapi_creds_container *gcc;
953 struct keytab_container *ktc;
954 struct smb_krb5_context *smb_krb5_context;
955 TALLOC_CTX *mem_ctx;
956 krb5_principal princ;
957 const char *error_string;
958 enum credentials_obtained obtained;
960 mem_ctx = talloc_new(cred);
961 if (!mem_ctx) {
962 return ENOMEM;
965 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
966 if (ret) {
967 return ret;
970 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
971 if (ret) {
972 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
973 error_string));
974 talloc_free(mem_ctx);
975 return ret;
978 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
979 talloc_free(mem_ctx);
980 *_gcc = cred->server_gss_creds;
981 return 0;
984 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
985 if (ret) {
986 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
987 return ret;
990 gcc = talloc(cred, struct gssapi_creds_container);
991 if (!gcc) {
992 talloc_free(mem_ctx);
993 return ENOMEM;
996 if (ktc->password_based || obtained < CRED_SPECIFIED) {
997 /* This creates a GSSAPI cred_id_t for match-by-key with only the keytab set */
998 maj_stat = gss_krb5_import_cred(&min_stat, NULL, NULL, ktc->keytab,
999 &gcc->creds);
1000 } else {
1001 /* This creates a GSSAPI cred_id_t with the principal and keytab set, matching by name */
1002 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
1003 &gcc->creds);
1005 if (maj_stat) {
1006 if (min_stat) {
1007 ret = min_stat;
1008 } else {
1009 ret = EINVAL;
1012 if (ret == 0) {
1013 cred->server_gss_creds_obtained = cred->keytab_obtained;
1014 talloc_set_destructor(gcc, free_gssapi_creds);
1015 cred->server_gss_creds = gcc;
1016 *_gcc = gcc;
1018 talloc_free(mem_ctx);
1019 return ret;
1022 /**
1023 * Set Kerberos KVNO
1026 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1027 int kvno)
1029 cred->kvno = kvno;
1033 * Return Kerberos KVNO
1036 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1038 return cred->kvno;
1042 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
1044 return cred->salt_principal;
1047 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
1049 talloc_free(cred->salt_principal);
1050 cred->salt_principal = talloc_strdup(cred, principal);
1053 /* The 'impersonate_principal' is used to allow one Kerberos principal
1054 * (and it's associated keytab etc) to impersonate another. The
1055 * ability to do this is controlled by the KDC, but it is generally
1056 * permitted to impersonate anyone to yourself. This allows any
1057 * member of the domain to get the groups of a user. This is also
1058 * known as S4U2Self */
1060 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1062 return cred->impersonate_principal;
1066 * The 'self_service' is the service principal that
1067 * represents the same object (by its objectSid)
1068 * as the client principal (typically our machine account).
1069 * When trying to impersonate 'impersonate_principal' with
1070 * S4U2Self.
1072 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1074 return cred->self_service;
1077 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1078 const char *principal,
1079 const char *self_service)
1081 talloc_free(cred->impersonate_principal);
1082 cred->impersonate_principal = talloc_strdup(cred, principal);
1083 talloc_free(cred->self_service);
1084 cred->self_service = talloc_strdup(cred, self_service);
1085 cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
1089 * when impersonating for S4U2proxy we need to set the target principal.
1090 * Similarly, we may only be authorized to do general impersonation to
1091 * some particular services.
1093 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1095 * NULL means that tickets will be obtained for the krbtgt service.
1098 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1100 return cred->target_service;
1103 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1105 talloc_free(cred->target_service);
1106 cred->target_service = talloc_strdup(cred, target_service);