diff options
-rw-r--r-- | source4/auth/credentials/credentials.c | 20 | ||||
-rw-r--r-- | source4/auth/credentials/credentials_krb5.c | 75 | ||||
-rw-r--r-- | source4/auth/credentials/credentials_krb5.h | 6 | ||||
-rw-r--r-- | source4/auth/gensec/gensec_gssapi.c | 4 | ||||
-rw-r--r-- | source4/auth/gensec/gensec_krb5.c | 9 | ||||
-rw-r--r-- | source4/auth/kerberos/kerberos.h | 15 | ||||
-rw-r--r-- | source4/auth/kerberos/kerberos_credentials.h | 28 | ||||
-rw-r--r-- | source4/auth/kerberos/kerberos_util.c | 16 |
8 files changed, 132 insertions, 41 deletions
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 <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>. +*/ + +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); |