From 44e7ea692708c1c956fc9bd20ed9a6d5de9479a4 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Sat, 1 May 2010 10:33:08 +1000 Subject: s4:credentials Make the CCACHE in credentials depend on the things that built it This means that we consider the ccache only as reliable as the least specified of the inputs we used. This means that we will regenerate the ccache if any of the inputs change. Andrew Bartlett --- source4/auth/credentials/credentials.c | 20 +++++++- source4/auth/credentials/credentials_krb5.c | 75 +++++++++++++++++++++------- source4/auth/credentials/credentials_krb5.h | 6 +++ source4/auth/gensec/gensec_gssapi.c | 4 ++ source4/auth/gensec/gensec_krb5.c | 9 +++- source4/auth/kerberos/kerberos.h | 15 ------ source4/auth/kerberos/kerberos_credentials.h | 28 +++++++++++ source4/auth/kerberos/kerberos_util.c | 16 ++++-- 8 files changed, 132 insertions(+), 41 deletions(-) create mode 100644 source4/auth/kerberos/kerberos_credentials.h (limited to 'source4/auth') diff --git a/source4/auth/credentials/credentials.c b/source4/auth/credentials/credentials.c index 5f2658d5bd..6f7630a206 100644 --- a/source4/auth/credentials/credentials.c +++ b/source4/auth/credentials/credentials.c @@ -222,7 +222,7 @@ _PUBLIC_ const char *cli_credentials_get_bind_dn(struct cli_credentials *cred) * @retval The username set on this context. * @note Return value will never be NULL except by programmer error. */ -_PUBLIC_ const char *cli_credentials_get_principal(struct cli_credentials *cred, TALLOC_CTX *mem_ctx) +const char *cli_credentials_get_principal_and_obtained(struct cli_credentials *cred, TALLOC_CTX *mem_ctx, enum credentials_obtained *obtained) { if (cred->machine_account_pending) { cli_credentials_set_machine_account(cred, @@ -238,20 +238,36 @@ _PUBLIC_ const char *cli_credentials_get_principal(struct cli_credentials *cred, cli_credentials_invalidate_ccache(cred, cred->principal_obtained); } - if (cred->principal_obtained < cred->username_obtained) { + if (cred->principal_obtained < cred->username_obtained + || cred->principal_obtained < MAX(cred->domain_obtained, cred->realm_obtained)) { if (cred->domain_obtained > cred->realm_obtained) { + *obtained = MIN(cred->domain_obtained, cred->username_obtained); return talloc_asprintf(mem_ctx, "%s@%s", cli_credentials_get_username(cred), cli_credentials_get_domain(cred)); } else { + *obtained = MIN(cred->domain_obtained, cred->username_obtained); return talloc_asprintf(mem_ctx, "%s@%s", cli_credentials_get_username(cred), cli_credentials_get_realm(cred)); } } + *obtained = cred->principal_obtained; return talloc_reference(mem_ctx, cred->principal); } +/** + * Obtain the client principal for this credentials context. + * @param cred credentials context + * @retval The username set on this context. + * @note Return value will never be NULL except by programmer error. + */ +_PUBLIC_ const char *cli_credentials_get_principal(struct cli_credentials *cred, TALLOC_CTX *mem_ctx) +{ + enum credentials_obtained obtained; + return cli_credentials_get_principal_and_obtained(cred, mem_ctx, &obtained); +} + bool cli_credentials_set_principal(struct cli_credentials *cred, const char *val, enum credentials_obtained obtained) diff --git a/source4/auth/credentials/credentials_krb5.c b/source4/auth/credentials/credentials_krb5.c index d76073093b..12bf610bf8 100644 --- a/source4/auth/credentials/credentials_krb5.c +++ b/source4/auth/credentials/credentials_krb5.c @@ -27,6 +27,7 @@ #include "auth/credentials/credentials.h" #include "auth/credentials/credentials_proto.h" #include "auth/credentials/credentials_krb5.h" +#include "auth/kerberos/kerberos_credentials.h" #include "param/param.h" _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred, @@ -282,6 +283,7 @@ _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred, const char **error_string) { krb5_error_code ret; + enum credentials_obtained obtained; if (cred->machine_account_pending) { cli_credentials_set_machine_account(cred, lp_ctx); @@ -302,15 +304,13 @@ _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred, return ret; } - ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, (*ccc)->ccache, error_string); + ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, (*ccc)->ccache, &obtained, error_string); if (ret) { return ret; } ret = cli_credentials_set_from_ccache(cred, *ccc, - (MAX(MAX(cred->principal_obtained, - cred->username_obtained), - cred->password_obtained)), error_string); + obtained, error_string); cred->ccache = *ccc; cred->ccache_obtained = cred->principal_obtained; @@ -330,6 +330,16 @@ _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred, return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string); } +/* We have good reason to think the ccache in these credentials is invalid - blow it away */ +static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred) +{ + if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) { + talloc_unlink(cred, cred->client_gss_creds); + cred->client_gss_creds = NULL; + } + cred->client_gss_creds_obtained = CRED_UNINITIALISED; +} + void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred, enum credentials_obtained obtained) { @@ -351,6 +361,18 @@ void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred, } } +/* We have good reason to think this CCACHE is invalid. Blow it away */ +static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred) +{ + if (cred->ccache_obtained > CRED_UNINITIALISED) { + talloc_unlink(cred, cred->ccache); + cred->ccache = NULL; + } + cred->ccache_obtained = CRED_UNINITIALISED; + + cli_credentials_unconditionally_invalidate_client_gss_creds(cred); +} + _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred, enum credentials_obtained obtained) { @@ -416,6 +438,23 @@ _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred, maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL, &gcc->creds); + if ((maj_stat == GSS_S_FAILURE) && (min_stat == (OM_uint32)KRB5_CC_END || min_stat == (OM_uint32) KRB5_CC_NOTFOUND)) { + /* This CCACHE is no good. Ensure we don't use it again */ + cli_credentials_unconditionally_invalidate_ccache(cred); + + /* Now try again to get a ccache */ + ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx, + &ccache, error_string); + if (ret) { + DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret))); + return ret; + } + + maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL, + &gcc->creds); + + } + if (maj_stat) { talloc_free(gcc); if (min_stat) { @@ -698,12 +737,11 @@ _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred, TALLOC_CTX *mem_ctx; krb5_principal princ; const char *error_string; + enum credentials_obtained obtained; - if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, - MAX(cred->principal_obtained, - cred->username_obtained)))) { - *_gcc = cred->server_gss_creds; - return 0; + mem_ctx = talloc_new(cred); + if (!mem_ctx) { + return ENOMEM; } ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, &smb_krb5_context); @@ -711,22 +749,23 @@ _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred, return ret; } - ret = cli_credentials_get_keytab(cred, event_ctx, lp_ctx, &ktc); + ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string); if (ret) { - DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret))); + DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n", + error_string)); + talloc_free(mem_ctx); return ret; } - mem_ctx = talloc_new(cred); - if (!mem_ctx) { - return ENOMEM; + if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) { + talloc_free(mem_ctx); + *_gcc = cred->server_gss_creds; + return 0; } - ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &error_string); + ret = cli_credentials_get_keytab(cred, event_ctx, lp_ctx, &ktc); if (ret) { - DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n", - error_string)); - talloc_free(mem_ctx); + DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret))); return ret; } diff --git a/source4/auth/credentials/credentials_krb5.h b/source4/auth/credentials/credentials_krb5.h index 72a4373609..3a614ff30e 100644 --- a/source4/auth/credentials/credentials_krb5.h +++ b/source4/auth/credentials/credentials_krb5.h @@ -44,6 +44,12 @@ krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx, struct cli_credentials *credentials, struct smb_krb5_context *smb_krb5_context, krb5_principal *princ, + enum credentials_obtained *obtained, const char **error_string); +krb5_error_code impersonate_principal_from_credentials(TALLOC_CTX *parent_ctx, + struct cli_credentials *credentials, + struct smb_krb5_context *smb_krb5_context, + krb5_principal *princ, + const char **error_string); #endif /* __CREDENTIALS_KRB5_H__ */ diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c index 9e974cb941..c6901a7b5e 100644 --- a/source4/auth/gensec/gensec_gssapi.c +++ b/source4/auth/gensec/gensec_gssapi.c @@ -379,6 +379,10 @@ static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_securi case KRB5_KDC_UNREACH: DEBUG(3, ("Cannot reach a KDC we require to contact %s : %s\n", principal, error_string)); return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ + case KRB5_CC_NOTFOUND: + case KRB5_CC_END: + DEBUG(3, ("Error preparing credentials we require to contact %s : %s\n", principal, error_string)); + return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ default: DEBUG(1, ("Aquiring initiator credentials failed: %s\n", error_string)); return NT_STATUS_UNSUCCESSFUL; diff --git a/source4/auth/gensec/gensec_krb5.c b/source4/auth/gensec/gensec_krb5.c index bb9ace70b1..c2f96d7b7f 100644 --- a/source4/auth/gensec/gensec_krb5.c +++ b/source4/auth/gensec/gensec_krb5.c @@ -31,6 +31,8 @@ #include "lib/tsocket/tsocket.h" #include "librpc/rpc/dcerpc.h" #include "auth/credentials/credentials.h" +#include "auth/credentials/credentials_krb5.h" +#include "auth/kerberos/kerberos_credentials.h" #include "auth/gensec/gensec.h" #include "auth/gensec/gensec_proto.h" #include "param/param.h" @@ -287,6 +289,10 @@ static NTSTATUS gensec_krb5_common_client_start(struct gensec_security *gensec_s case KRB5_KDC_UNREACH: DEBUG(3, ("Cannot reach a KDC we require to contact %s: %s\n", principal, error_string)); return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ + case KRB5_CC_NOTFOUND: + case KRB5_CC_END: + DEBUG(3, ("Error preparing credentials we require to contact %s : %s\n", principal, error_string)); + return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ default: DEBUG(1, ("gensec_krb5_start: Aquiring initiator credentials failed: %s\n", error_string)); return NT_STATUS_UNSUCCESSFUL; @@ -474,6 +480,7 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, struct keytab_container *keytab; krb5_principal server_in_keytab; const char *error_string; + enum credentials_obtained obtained; if (!in.data) { return NT_STATUS_INVALID_PARAMETER; @@ -490,7 +497,7 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, /* This ensures we lookup the correct entry in that keytab */ ret = principal_from_credentials(out_mem_ctx, gensec_get_credentials(gensec_security), gensec_krb5_state->smb_krb5_context, - &server_in_keytab, &error_string); + &server_in_keytab, &obtained, &error_string); if (ret) { DEBUG(2,("Failed to make credentials from principal: %s\n", error_string)); diff --git a/source4/auth/kerberos/kerberos.h b/source4/auth/kerberos/kerberos.h index 992b509dbf..1990343808 100644 --- a/source4/auth/kerberos/kerberos.h +++ b/source4/auth/kerberos/kerberos.h @@ -104,21 +104,6 @@ bool kerberos_compatible_enctypes(krb5_context context, krb5_enctype enctype1, k void kerberos_free_data_contents(krb5_context context, krb5_data *pdata); krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry); char *smb_get_krb5_error_message(krb5_context context, krb5_error_code code, TALLOC_CTX *mem_ctx); - krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx, - struct cli_credentials *credentials, - struct smb_krb5_context *smb_krb5_context, - krb5_ccache ccache, - const char **error_string); -krb5_error_code impersonate_principal_from_credentials(TALLOC_CTX *parent_ctx, - struct cli_credentials *credentials, - struct smb_krb5_context *smb_krb5_context, - krb5_principal *princ, - const char **error_string); -krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx, - struct cli_credentials *credentials, - struct smb_krb5_context *smb_krb5_context, - krb5_principal *princ, - const char **error_string); NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience, struct PAC_DATA **pac_data_out, diff --git a/source4/auth/kerberos/kerberos_credentials.h b/source4/auth/kerberos/kerberos_credentials.h new file mode 100644 index 0000000000..55227752e3 --- /dev/null +++ b/source4/auth/kerberos/kerberos_credentials.h @@ -0,0 +1,28 @@ +/* + Unix SMB/CIFS implementation. + + Kerberos utility functions for GENSEC + + Copyright (C) Andrew Bartlett 2004-2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx, + struct cli_credentials *credentials, + struct smb_krb5_context *smb_krb5_context, + krb5_ccache ccache, + enum credentials_obtained *obtained, + const char **error_string); diff --git a/source4/auth/kerberos/kerberos_util.c b/source4/auth/kerberos/kerberos_util.c index 44d97b7f08..2b358515f8 100644 --- a/source4/auth/kerberos/kerberos_util.c +++ b/source4/auth/kerberos/kerberos_util.c @@ -26,6 +26,7 @@ #include "auth/credentials/credentials.h" #include "auth/credentials/credentials_proto.h" #include "auth/credentials/credentials_krb5.h" +#include "auth/kerberos/kerberos_credentials.h" struct principal_container { struct smb_krb5_context *smb_krb5_context; @@ -143,6 +144,7 @@ static krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx, struct cli_credentials *credentials, struct smb_krb5_context *smb_krb5_context, krb5_principal *princ, + enum credentials_obtained *obtained, const char **error_string) { krb5_error_code ret; @@ -152,7 +154,7 @@ static krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx, (*error_string) = error_message(ENOMEM); return ENOMEM; } - princ_string = cli_credentials_get_principal(credentials, mem_ctx); + princ_string = cli_credentials_get_principal_and_obtained(credentials, mem_ctx, obtained); if (!princ_string) { (*error_string) = error_message(ENOMEM); return ENOMEM; @@ -188,6 +190,7 @@ static krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx, struct cli_credentials *credentials, struct smb_krb5_context *smb_krb5_context, krb5_ccache ccache, + enum credentials_obtained *obtained, const char **error_string) { krb5_error_code ret; @@ -203,7 +206,7 @@ static krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx, return ENOMEM; } - ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ, error_string); + ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ, obtained, error_string); if (ret) { talloc_free(mem_ctx); return ret; @@ -285,7 +288,8 @@ static krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx, ret = kinit_to_ccache(parent_ctx, credentials, smb_krb5_context, - ccache, error_string); + ccache, obtained, + error_string); } if (ret) { (*error_string) = talloc_asprintf(credentials, "kinit for %s failed (%s)\n", @@ -410,6 +414,7 @@ static krb5_error_code create_keytab(TALLOC_CTX *parent_ctx, krb5_principal princ; const char *princ_string; const char *error_string; + enum credentials_obtained obtained; TALLOC_CTX *mem_ctx = talloc_new(parent_ctx); if (!mem_ctx) { @@ -418,7 +423,7 @@ static krb5_error_code create_keytab(TALLOC_CTX *parent_ctx, princ_string = cli_credentials_get_principal(machine_account, mem_ctx); /* Get the principal we will store the new keytab entries under */ - ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ, &error_string); + ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ, &obtained, &error_string); if (ret) { DEBUG(1,("create_keytab: makeing krb5 principal failed (%s)\n", error_string)); talloc_free(mem_ctx); @@ -549,6 +554,7 @@ static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx, TALLOC_CTX *mem_ctx = talloc_new(parent_ctx); const char *princ_string; const char *error_string; + enum credentials_obtained obtained; if (!mem_ctx) { return ENOMEM; @@ -558,7 +564,7 @@ static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx, princ_string = cli_credentials_get_principal(machine_account, mem_ctx); /* Get the principal we will store the new keytab entries under */ - ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ, &error_string); + ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ, &obtained, &error_string); if (ret) { DEBUG(1,("update_keytab: makeing krb5 principal failed (%s)\n", error_string)); talloc_free(mem_ctx); -- cgit