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/>.
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)
47 if (cred->smb_krb5_context) {
48 *smb_krb5_context = cred->smb_krb5_context;
52 ret = smb_krb5_init_context(cred, lp_ctx,
53 &cred->smb_krb5_context);
55 cred->smb_krb5_context = NULL;
58 *smb_krb5_context = cred->smb_krb5_context;
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;
74 if (!talloc_reference(cred, smb_krb5_context)) {
75 return NT_STATUS_NO_MEMORY;
77 cred->smb_krb5_context = smb_krb5_context;
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)
91 if (cred->ccache_obtained > obtained) {
95 ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
96 ccache->ccache, &princ);
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,
105 ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
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,
113 cli_credentials_set_principal(cred, name, obtained);
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;
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);
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);
140 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
141 struct loadparm_context *lp_ctx,
143 enum credentials_obtained obtained,
144 const char **error_string)
147 krb5_principal princ;
148 struct ccache_container *ccc;
149 if (cred->ccache_obtained > obtained) {
153 ccc = talloc(cred, struct ccache_container);
155 (*error_string) = error_message(ENOMEM);
159 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
160 &ccc->smb_krb5_context);
162 (*error_string) = error_message(ret);
166 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
168 (*error_string) = error_message(ENOMEM);
173 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
175 (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
177 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
183 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
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,
193 talloc_set_destructor(ccc, free_dccache);
195 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
198 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
199 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
202 (*error_string) = error_message(ret);
207 cred->ccache_obtained = obtained;
208 talloc_steal(cred, ccc);
210 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
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,
230 struct ccache_container *ccc;
231 krb5_creds creds, creds2;
234 if (principal == NULL) {
235 /* no way to delete if we don't know the principal */
241 /* not a kerberos connection */
246 /* We have already tried discarding the credentials */
252 ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
257 ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
259 /* don't retry - we didn't find these credentials to remove */
260 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
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);
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.
280 static int cli_credentials_new_ccache(struct cli_credentials *cred,
281 struct loadparm_context *lp_ctx,
283 struct ccache_container **_ccc,
284 const char **error_string)
286 bool must_free_cc_name = false;
288 struct ccache_container *ccc = talloc(cred, struct ccache_container);
293 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
294 &ccc->smb_krb5_context);
297 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
301 if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
303 (*error_string) = strerror(ENOMEM);
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);
314 ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
320 (*error_string) = strerror(ENOMEM);
325 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
328 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
330 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
332 talloc_free(ccache_name);
337 if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
338 talloc_set_destructor(ccc, free_mccache);
340 talloc_set_destructor(ccc, free_dccache);
343 if (must_free_cc_name) {
344 talloc_free(ccache_name);
352 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
353 struct tevent_context *event_ctx,
354 struct loadparm_context *lp_ctx,
356 struct ccache_container **ccc,
357 const char **error_string)
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) {
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
376 } else if (ret == 0) {
378 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
379 cli_credentials_get_principal(cred, cred)));
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));
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,
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));
401 if (cli_credentials_is_anonymous(cred)) {
402 (*error_string) = "Cannot get anonymous kerberos credentials";
406 ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
411 ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
416 ret = cli_credentials_set_from_ccache(cred, *ccc,
417 obtained, error_string);
420 cred->ccache_obtained = cred->principal_obtained;
424 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
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);
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);
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,
504 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
507 (void)gss_release_cred(&min_stat, &gcc->creds);
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)
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 gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
525 krb5_enctype *etypes = NULL;
527 if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
528 cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
529 bool expired = false;
530 OM_uint32 lifetime = 0;
531 gss_cred_usage_t usage = 0;
532 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
533 NULL, &lifetime, &usage, NULL);
534 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
535 DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
537 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
538 DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
540 } else if (maj_stat != GSS_S_COMPLETE) {
541 *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
542 gssapi_error_string(cred, maj_stat, min_stat, NULL));
546 cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
548 DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
549 cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
551 *_gcc = cred->client_gss_creds;
556 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
557 &ccache, error_string);
559 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
560 DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
562 DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
567 gcc = talloc(cred, struct gssapi_creds_container);
569 (*error_string) = error_message(ENOMEM);
573 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
575 if ((maj_stat == GSS_S_FAILURE) && (min_stat == (OM_uint32)KRB5_CC_END || min_stat == (OM_uint32) KRB5_CC_NOTFOUND)) {
576 /* This CCACHE is no good. Ensure we don't use it again */
577 cli_credentials_unconditionally_invalidate_ccache(cred);
579 /* Now try again to get a ccache */
580 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
581 &ccache, error_string);
583 DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
587 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
599 (*error_string) = talloc_asprintf(cred, "gss_krb5_import_cred failed: %s", error_message(ret));
605 * transfer the enctypes from the smb_krb5_context to the gssapi layer
607 * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
608 * to configure the enctypes via the krb5.conf.
610 * And the gss_init_sec_context() creates it's own krb5_context and
611 * the TGS-REQ had all enctypes in it and only the ones configured
612 * and used for the AS-REQ, so it wasn't possible to disable the usage
615 min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
618 OM_uint32 num_ktypes;
620 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
622 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
633 (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
638 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
640 * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
642 * This allows us to disable SIGN and SEAL on a TLS connection with
643 * GSS-SPNENO. For example ldaps:// connections.
645 * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
646 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
648 maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
658 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
662 cred->client_gss_creds_obtained = cred->ccache_obtained;
663 talloc_set_destructor(gcc, free_gssapi_creds);
664 cred->client_gss_creds = gcc;
670 Set a gssapi cred_id_t into the credentials system. (Client case)
672 This grabs the credentials both 'intact' and getting the krb5
673 ccache out of it. This routine can be generalised in future for
674 the case where we deal with GSSAPI mechs other than krb5.
676 On sucess, the caller must not free gssapi_cred, as it now belongs
677 to the credentials system.
680 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
681 struct loadparm_context *lp_ctx,
682 gss_cred_id_t gssapi_cred,
683 enum credentials_obtained obtained,
684 const char **error_string)
687 OM_uint32 maj_stat, min_stat;
688 struct ccache_container *ccc;
689 struct gssapi_creds_container *gcc;
690 if (cred->client_gss_creds_obtained > obtained) {
694 gcc = talloc(cred, struct gssapi_creds_container);
696 (*error_string) = error_message(ENOMEM);
700 ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
705 maj_stat = gss_krb5_copy_ccache(&min_stat,
706 gssapi_cred, ccc->ccache);
714 (*error_string) = error_message(ENOMEM);
719 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
722 cred->ccache_obtained = obtained;
724 gcc->creds = gssapi_cred;
725 talloc_set_destructor(gcc, free_gssapi_creds);
727 /* set the clinet_gss_creds_obtained here, as it just
728 got set to UNINITIALISED by the calls above */
729 cred->client_gss_creds_obtained = obtained;
730 cred->client_gss_creds = gcc;
735 static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
738 const struct ccache_container *old_ccc = NULL;
739 struct ccache_container *ccc = NULL;
740 char *ccache_name = NULL;
742 old_ccc = cred->ccache;
743 if (old_ccc == NULL) {
747 ccc = talloc(cred, struct ccache_container);
754 ccache_name = talloc_asprintf(ccc, "MEMORY:%p", ccc);
756 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
757 ccache_name, &ccc->ccache);
763 talloc_set_destructor(ccc, free_mccache);
765 TALLOC_FREE(ccache_name);
767 ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
768 old_ccc->ccache, ccc->ccache);
775 cred->client_gss_creds = NULL;
776 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
780 _PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
781 struct cli_credentials *src)
783 struct cli_credentials *dst;
786 dst = talloc(mem_ctx, struct cli_credentials);
793 ret = cli_credentials_shallow_ccache(dst);
802 static int smb_krb5_create_salt_principal(TALLOC_CTX *mem_ctx,
803 const char *samAccountName,
805 const char **salt_principal,
806 const char **error_string)
808 char *machine_username;
809 bool is_machine_account = false;
814 if (samAccountName == NULL) {
815 *error_string = "Cannot determine salt principal, no "
816 "saltPrincipal or samAccountName specified";
821 *error_string = "Cannot make principal without a realm";
825 tmp_ctx = talloc_new(mem_ctx);
826 if (tmp_ctx == NULL) {
827 *error_string = "Cannot allocate talloc context";
831 upper_realm = strupper_talloc(tmp_ctx, realm);
832 if (upper_realm == NULL) {
833 *error_string = "Cannot allocate to upper case realm";
837 machine_username = strlower_talloc(tmp_ctx, samAccountName);
838 if (!machine_username) {
839 *error_string = "Cannot duplicate samAccountName";
843 if (machine_username[strlen(machine_username) - 1] == '$') {
844 machine_username[strlen(machine_username) - 1] = '\0';
845 is_machine_account = true;
848 if (is_machine_account) {
851 lower_realm = strlower_talloc(tmp_ctx, realm);
852 if (lower_realm == NULL) {
853 *error_string = "Cannot allocate to lower case realm";
857 *salt_principal = talloc_asprintf(mem_ctx,
863 *salt_principal = talloc_asprintf(mem_ctx,
868 if (*salt_principal == NULL) {
869 *error_string = "Cannot create salt principal";
875 talloc_free(tmp_ctx);
879 /* Get the keytab (actually, a container containing the krb5_keytab)
880 * attached to this context. If this hasn't been done or set before,
881 * it will be generated from the password.
883 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
884 struct loadparm_context *lp_ctx,
885 struct keytab_container **_ktc)
888 struct keytab_container *ktc;
889 struct smb_krb5_context *smb_krb5_context;
890 const char *keytab_name;
893 const char *username = cli_credentials_get_username(cred);
894 const char *realm = cli_credentials_get_realm(cred);
895 const char *error_string;
896 const char *salt_principal;
898 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
899 cred->username_obtained))) {
900 *_ktc = cred->keytab;
904 if (cli_credentials_is_anonymous(cred)) {
908 ret = cli_credentials_get_krb5_context(cred, lp_ctx,
914 mem_ctx = talloc_new(cred);
920 * FIXME: Currently there is no better way than to create the correct
921 * salt principal by checking if the username ends with a '$'. It would
922 * be better if it is part of the credentials.
924 ret = smb_krb5_create_salt_principal(mem_ctx,
930 talloc_free(mem_ctx);
934 ret = smb_krb5_create_memory_keytab(mem_ctx,
935 smb_krb5_context->krb5_context,
936 cli_credentials_get_password(cred),
940 cli_credentials_get_kvno(cred),
944 talloc_free(mem_ctx);
948 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
949 keytab, keytab_name, &ktc);
951 talloc_free(mem_ctx);
955 cred->keytab_obtained = (MAX(cred->principal_obtained,
956 cred->username_obtained));
958 /* We make this keytab up based on a password. Therefore
959 * match-by-key is acceptable, we can't match on the wrong
961 ktc->password_based = true;
963 talloc_steal(cred, ktc);
965 *_ktc = cred->keytab;
966 talloc_free(mem_ctx);
970 /* Given the name of a keytab (presumably in the format
971 * FILE:/etc/krb5.keytab), open it and attach it */
973 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
974 struct loadparm_context *lp_ctx,
975 const char *keytab_name,
976 enum credentials_obtained obtained)
979 struct keytab_container *ktc;
980 struct smb_krb5_context *smb_krb5_context;
983 if (cred->keytab_obtained >= obtained) {
987 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
992 mem_ctx = talloc_new(cred);
997 ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
998 NULL, keytab_name, &ktc);
1003 cred->keytab_obtained = obtained;
1005 talloc_steal(cred, ktc);
1007 talloc_free(mem_ctx);
1012 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1014 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
1015 struct loadparm_context *lp_ctx,
1016 struct gssapi_creds_container **_gcc)
1019 OM_uint32 maj_stat, min_stat;
1020 struct gssapi_creds_container *gcc;
1021 struct keytab_container *ktc;
1022 struct smb_krb5_context *smb_krb5_context;
1023 TALLOC_CTX *mem_ctx;
1024 krb5_principal princ;
1025 const char *error_string;
1026 enum credentials_obtained obtained;
1028 mem_ctx = talloc_new(cred);
1033 ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1038 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1040 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1042 talloc_free(mem_ctx);
1046 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1047 talloc_free(mem_ctx);
1048 *_gcc = cred->server_gss_creds;
1052 ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1054 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1058 gcc = talloc(cred, struct gssapi_creds_container);
1060 talloc_free(mem_ctx);
1064 if (ktc->password_based || obtained < CRED_SPECIFIED) {
1065 /* This creates a GSSAPI cred_id_t for match-by-key with only the keytab set */
1066 maj_stat = gss_krb5_import_cred(&min_stat, NULL, NULL, ktc->keytab,
1069 /* This creates a GSSAPI cred_id_t with the principal and keytab set, matching by name */
1070 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
1081 cred->server_gss_creds_obtained = cred->keytab_obtained;
1082 talloc_set_destructor(gcc, free_gssapi_creds);
1083 cred->server_gss_creds = gcc;
1086 talloc_free(mem_ctx);
1094 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1101 * Return Kerberos KVNO
1104 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1110 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
1112 return cred->salt_principal;
1115 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
1117 talloc_free(cred->salt_principal);
1118 cred->salt_principal = talloc_strdup(cred, principal);
1121 /* The 'impersonate_principal' is used to allow one Kerberos principal
1122 * (and it's associated keytab etc) to impersonate another. The
1123 * ability to do this is controlled by the KDC, but it is generally
1124 * permitted to impersonate anyone to yourself. This allows any
1125 * member of the domain to get the groups of a user. This is also
1126 * known as S4U2Self */
1128 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1130 return cred->impersonate_principal;
1134 * The 'self_service' is the service principal that
1135 * represents the same object (by its objectSid)
1136 * as the client principal (typically our machine account).
1137 * When trying to impersonate 'impersonate_principal' with
1140 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1142 return cred->self_service;
1145 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1146 const char *principal,
1147 const char *self_service)
1149 talloc_free(cred->impersonate_principal);
1150 cred->impersonate_principal = talloc_strdup(cred, principal);
1151 talloc_free(cred->self_service);
1152 cred->self_service = talloc_strdup(cred, self_service);
1153 cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
1157 * when impersonating for S4U2proxy we need to set the target principal.
1158 * Similarly, we may only be authorized to do general impersonation to
1159 * some particular services.
1161 * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1163 * NULL means that tickets will be obtained for the krbtgt service.
1166 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1168 return cred->target_service;
1171 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1173 talloc_free(cred->target_service);
1174 cred->target_service = talloc_strdup(cred, target_service);