summaryrefslogtreecommitdiff
path: root/source4/libcli
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2004-12-24 09:54:23 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 13:07:37 -0500
commit44113c4de1ae06a78a940782dc762b6576310d0d (patch)
tree7d6e30dc5a56b11edcd2def2304aa78071b1405e /source4/libcli
parentbebe512a57c6aabe6cd539750834297a17b5c4f5 (diff)
downloadsamba-44113c4de1ae06a78a940782dc762b6576310d0d.tar.gz
samba-44113c4de1ae06a78a940782dc762b6576310d0d.tar.bz2
samba-44113c4de1ae06a78a940782dc762b6576310d0d.zip
r4355: More work from the elves on Christmas eve:
- Update Samba4's kerberos code to match the 'salting' changes in Samba3 (and many other cleanups by jra). - Move GENSEC into the modern era of talloc destructors. This avoids many of the memory leaks in this code, as we now can't somehow 'forget' to call the end routine. - This required fixing some of the talloc hierarchies. - The new krb5 seems more sensitive to getting the service name right, so start actually setting the service name on the krb5 context. Andrew Bartlett (This used to be commit 278bf1a61a6da6ef955a12c13d7b1a0357cebf1f)
Diffstat (limited to 'source4/libcli')
-rw-r--r--source4/libcli/auth/clikrb5.c98
-rw-r--r--source4/libcli/auth/gensec.c28
-rw-r--r--source4/libcli/auth/gensec.h1
-rw-r--r--source4/libcli/auth/gensec_krb5.c29
-rw-r--r--source4/libcli/auth/gensec_ntlmssp.c52
-rw-r--r--source4/libcli/auth/kerberos.c643
-rw-r--r--source4/libcli/auth/kerberos.h15
-rw-r--r--source4/libcli/auth/kerberos_verify.c184
-rw-r--r--source4/libcli/auth/ntlmssp.c3
-rw-r--r--source4/libcli/auth/spnego.c49
10 files changed, 891 insertions, 211 deletions
diff --git a/source4/libcli/auth/clikrb5.c b/source4/libcli/auth/clikrb5.c
index 7ad8dd7b6c..122f9510e7 100644
--- a/source4/libcli/auth/clikrb5.c
+++ b/source4/libcli/auth/clikrb5.c
@@ -71,11 +71,11 @@
pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in *)paddr)->sin_addr);
}
#else
- __ERROR__XX__UNKNOWN_ADDRTYPE
+#error UNKNOWN_ADDRTYPE
#endif
-#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY)
- int create_kerberos_key_from_string(krb5_context context,
+#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY) && defined(HAVE_KRB5_ENCRYPT_BLOCK)
+ int create_kerberos_key_from_string_direct(krb5_context context,
krb5_principal host_princ,
krb5_data *password,
krb5_keyblock *key,
@@ -96,7 +96,7 @@
return ret;
}
#elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT)
- int create_kerberos_key_from_string(krb5_context context,
+ int create_kerberos_key_from_string_direct(krb5_context context,
krb5_principal host_princ,
krb5_data *password,
krb5_keyblock *key,
@@ -114,9 +114,30 @@
salt, key);
}
#else
- __ERROR_XX_UNKNOWN_CREATE_KEY_FUNCTIONS
+#error UNKNOWN_CREATE_KEY_FUNCTIONS
#endif
+ int create_kerberos_key_from_string(krb5_context context,
+ krb5_principal host_princ,
+ krb5_data *password,
+ krb5_keyblock *key,
+ krb5_enctype enctype)
+{
+ krb5_principal salt_princ = NULL;
+ int ret;
+ /*
+ * Check if we've determined that the KDC is salting keys for this
+ * principal/enctype in a non-obvious way. If it is, try to match
+ * its behavior.
+ */
+ salt_princ = kerberos_fetch_salt_princ_for_host_princ(context, host_princ, enctype);
+ ret = create_kerberos_key_from_string_direct(context, salt_princ ? salt_princ : host_princ, password, key, enctype);
+ if (salt_princ) {
+ krb5_free_principal(context, salt_princ);
+ }
+ return ret;
+}
+
#if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES)
krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
krb5_enctype **enctypes)
@@ -190,7 +211,7 @@
krb5_error_code rc;
int num_kdcs, i;
struct sockaddr *sa;
- struct addrinfo **ai;
+ struct addrinfo *ai;
*addr_pp = NULL;
*naddrs = 0;
@@ -220,16 +241,15 @@
return -1;
}
- *addr_pp = malloc_array_p(struct sockaddr, num_kdcs);
- memset(*addr_pp, '\0', sizeof(struct sockaddr) * num_kdcs );
+ memset(sa, '\0', sizeof(struct sockaddr) * num_kdcs );
for (i = 0; i < num_kdcs && (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); i++) {
#if defined(HAVE_KRB5_KRBHST_GET_ADDRINFO)
- rc = krb5_krbhst_get_addrinfo(ctx, hinfo, ai);
+ rc = krb5_krbhst_get_addrinfo(ctx, hinfo, &ai);
if (rc) {
DEBUG(0,("krb5_krbhst_get_addrinfo failed: %s\n", error_message(rc)));
- return rc;
+ continue;
}
#endif
if (hinfo->ai && hinfo->ai->ai_family == AF_INET)
@@ -251,6 +271,42 @@
}
#endif
+ void kerberos_free_data_contents(krb5_context context, krb5_data *pdata)
+{
+#if defined(HAVE_KRB5_FREE_DATA_CONTENTS)
+ if (pdata->data) {
+ krb5_free_data_contents(context, pdata);
+ }
+#else
+ SAFE_FREE(pdata->data);
+#endif
+}
+
+ void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype)
+{
+#if defined(HAVE_KRB5_KEYBLOCK_IN_CREDS)
+ KRB5_KEY_TYPE((&pcreds->keyblock)) = enctype;
+#elif defined(HAVE_KRB5_SESSION_IN_CREDS)
+ KRB5_KEY_TYPE((&pcreds->session)) = enctype;
+#else
+#error UNKNOWN_KEYBLOCK_MEMBER_IN_KRB5_CREDS_STRUCT
+#endif
+}
+
+ BOOL kerberos_compatible_enctypes(krb5_context context,
+ krb5_enctype enctype1,
+ krb5_enctype enctype2)
+{
+#if defined(HAVE_KRB5_C_ENCTYPE_COMPARE)
+ krb5_boolean similar = 0;
+
+ krb5_c_enctype_compare(context, enctype1, enctype2, &similar);
+ return similar ? True : False;
+#elif defined(HAVE_KRB5_ENCTYPES_COMPATIBLE_KEYS)
+ return krb5_enctypes_compatible_keys(context, enctype1, enctype2) ? True : False;
+#endif
+}
+
static BOOL ads_cleanup_expired_creds(krb5_context context,
krb5_ccache ccache,
krb5_creds *credsp)
@@ -279,13 +335,13 @@ static BOOL ads_cleanup_expired_creds(krb5_context context,
we're using creds obtained outside of our exectuable
*/
if (StrCaseCmp(krb5_cc_get_type(context, ccache), "FILE") == 0) {
- DEBUG(5, ("We do not remove creds from a FILE ccache\n"));
+ DEBUG(5, ("ads_cleanup_expired_creds: We do not remove creds from a FILE ccache\n"));
return False;
}
retval = krb5_cc_remove_cred(context, ccache, 0, credsp);
if (retval) {
- DEBUG(1, ("krb5_cc_remove_cred failed, err %s\n",
+ DEBUG(1, ("ads_cleanup_expired_creds: krb5_cc_remove_cred failed, err %s\n",
error_message(retval)));
/* If we have an error in this, we want to display it,
but continue as though we deleted it */
@@ -296,7 +352,7 @@ static BOOL ads_cleanup_expired_creds(krb5_context context,
/*
we can't use krb5_mk_req because w2k wants the service to be in a particular format
*/
- krb5_error_code ads_krb5_mk_req(krb5_context context,
+krb5_error_code ads_krb5_mk_req(krb5_context context,
krb5_auth_context *auth_context,
const krb5_flags ap_req_options,
const char *principal,
@@ -314,7 +370,7 @@ static BOOL ads_cleanup_expired_creds(krb5_context context,
retval = krb5_parse_name(context, principal, &server);
if (retval) {
- DEBUG(1,("Failed to parse principal %s\n", principal));
+ DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal));
return retval;
}
@@ -327,7 +383,9 @@ static BOOL ads_cleanup_expired_creds(krb5_context context,
}
if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) {
- DEBUG(10,("krb5_cc_get_principal failed (%s)\n",
+ /* This can commonly fail on smbd startup with no ticket in the cache.
+ * Report at higher level than 1. */
+ DEBUG(3,("ads_krb5_mk_req: krb5_cc_get_principal failed (%s)\n",
error_message(retval)));
goto cleanup_creds;
}
@@ -335,7 +393,7 @@ static BOOL ads_cleanup_expired_creds(krb5_context context,
while(!creds_ready) {
if ((retval = krb5_get_credentials(context, 0, ccache,
&creds, &credsp))) {
- DEBUG(1,("krb5_get_credentials failed for %s (%s)\n",
+ DEBUG(1,("ads_krb5_mk_req: krb5_get_credentials failed for %s (%s)\n",
principal, error_message(retval)));
goto cleanup_creds;
}
@@ -344,7 +402,7 @@ static BOOL ads_cleanup_expired_creds(krb5_context context,
if ((unsigned)credsp->times.starttime > time(NULL)) {
time_t t = time(NULL);
int time_offset =(unsigned)credsp->times.starttime-t;
- DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
+ DEBUG(4,("ads_krb5_mk_req: Advancing clock by %d seconds to cope with clock skew\n", time_offset));
krb5_set_real_time(context, t + time_offset + 1, 0);
}
@@ -366,7 +424,7 @@ static BOOL ads_cleanup_expired_creds(krb5_context context,
retval = krb5_mk_req_extended(context, auth_context, ap_req_options,
&in_data, credsp, outbuf);
if (retval) {
- DEBUG(1,("krb5_mk_req_extended failed (%s)\n",
+ DEBUG(1,("ads_krb5_mk_req: krb5_mk_req_extended failed (%s)\n",
error_message(retval)));
}
@@ -378,13 +436,9 @@ cleanup_creds:
cleanup_princ:
krb5_free_principal(context, server);
- if (mem_ctx) {
- talloc_destroy(mem_ctx);
- }
return retval;
}
-
#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i )
{
diff --git a/source4/libcli/auth/gensec.c b/source4/libcli/auth/gensec.c
index 8009df4e40..7e33a159f9 100644
--- a/source4/libcli/auth/gensec.c
+++ b/source4/libcli/auth/gensec.c
@@ -147,10 +147,11 @@ static NTSTATUS gensec_start(TALLOC_CTX *mem_ctx, struct gensec_security **gense
* @note Used by SPENGO in particular, for the actual implementation mechanism
*/
-NTSTATUS gensec_subcontext_start(struct gensec_security *parent,
+NTSTATUS gensec_subcontext_start(TALLOC_CTX *mem_ctx,
+ struct gensec_security *parent,
struct gensec_security **gensec_security)
{
- (*gensec_security) = talloc_p(parent, struct gensec_security);
+ (*gensec_security) = talloc_p(mem_ctx, struct gensec_security);
if (!(*gensec_security)) {
return NT_STATUS_NO_MEMORY;
}
@@ -444,10 +445,6 @@ void gensec_end(struct gensec_security **gensec_security)
if (!*gensec_security) {
return;
}
- if ((*gensec_security)->ops) {
- (*gensec_security)->ops->end(*gensec_security);
- }
- (*gensec_security)->private_data = NULL;
talloc_free(*gensec_security);
*gensec_security = NULL;
@@ -646,7 +643,7 @@ NTSTATUS gensec_set_password(struct gensec_security *gensec_security,
const char *password)
{
gensec_security->user.password = talloc_strdup(gensec_security, password);
- if (!gensec_security->user.password) {
+ if (password && !gensec_security->user.password) {
return NT_STATUS_NO_MEMORY;
}
return NT_STATUS_OK;
@@ -713,6 +710,20 @@ const char *gensec_get_target_service(struct gensec_security *gensec_security)
return "host";
}
+const char *gensec_get_target_principal(struct gensec_security *gensec_security)
+{
+ const char *mechListMIC;
+
+ if (gensec_security->target.principal) {
+ return gensec_security->target.principal;
+ }
+
+ mechListMIC = talloc_asprintf(gensec_security,"%s$@%s",
+ lp_netbios_name(),
+ lp_realm());
+ return mechListMIC;
+}
+
/**
* Set a password callback, if the gensec module we use demands a password
*/
@@ -741,7 +752,8 @@ NTSTATUS gensec_get_password(struct gensec_security *gensec_security,
}
}
if (!gensec_security->password_callback) {
- return NT_STATUS_INVALID_PARAMETER;
+ *password = NULL;
+ return NT_STATUS_OK;
}
return gensec_security->password_callback(gensec_security, mem_ctx, password);
}
diff --git a/source4/libcli/auth/gensec.h b/source4/libcli/auth/gensec.h
index 9162c935b2..2b44f0d902 100644
--- a/source4/libcli/auth/gensec.h
+++ b/source4/libcli/auth/gensec.h
@@ -84,7 +84,6 @@ struct gensec_security_ops {
NTSTATUS (*session_key)(struct gensec_security *gensec_security, DATA_BLOB *session_key);
NTSTATUS (*session_info)(struct gensec_security *gensec_security,
struct auth_session_info **session_info);
- void (*end)(struct gensec_security *gensec_security);
};
#define GENSEC_INTERFACE_VERSION 0
diff --git a/source4/libcli/auth/gensec_krb5.c b/source4/libcli/auth/gensec_krb5.c
index 9323580e92..9d4a2f6b0e 100644
--- a/source4/libcli/auth/gensec_krb5.c
+++ b/source4/libcli/auth/gensec_krb5.c
@@ -229,9 +229,9 @@ static NTSTATUS gensec_krb5_decode_pac(TALLOC_CTX *mem_ctx,
return status;
}
-static void gensec_krb5_end(struct gensec_security *gensec_security)
+static int gensec_krb5_destory(void *ptr)
{
- struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
+ struct gensec_krb5_state *gensec_krb5_state = ptr;
if (gensec_krb5_state->ticket.length) {
/* Hmm, early heimdal dooesn't have this - correct call would be krb5_data_free */
@@ -255,12 +255,9 @@ static void gensec_krb5_end(struct gensec_security *gensec_security)
if (gensec_krb5_state->krb5_context) {
krb5_free_context(gensec_krb5_state->krb5_context);
}
-
- talloc_free(gensec_krb5_state);
- gensec_security->private_data = NULL;
+ return 0;
}
-
static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security)
{
struct gensec_krb5_state *gensec_krb5_state;
@@ -282,6 +279,8 @@ static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security)
gensec_krb5_state->session_key = data_blob(NULL, 0);
gensec_krb5_state->pac = data_blob(NULL, 0);
+ talloc_set_destructor(gensec_krb5_state, gensec_krb5_destory);
+
ret = krb5_init_context(&gensec_krb5_state->krb5_context);
if (ret) {
DEBUG(1,("gensec_krb5_start: krb5_init_context failed (%s)\n", error_message(ret)));
@@ -401,8 +400,8 @@ static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security
}
ret = kerberos_kinit_password_cc(gensec_krb5_state->krb5_context, gensec_krb5_state->krb5_ccache,
- gensec_get_client_principal(gensec_security, gensec_security),
- password, NULL, &kdc_time);
+ gensec_get_client_principal(gensec_security, gensec_security),
+ password, NULL, &kdc_time);
/* cope with ticket being in the future due to clock skew */
if ((unsigned)kdc_time > time(NULL)) {
@@ -439,8 +438,9 @@ static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security
* or NT_STATUS_OK if the user is authenticated.
*/
-static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
- const DATA_BLOB in, DATA_BLOB *out)
+static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security,
+ TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB in, DATA_BLOB *out)
{
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
krb5_error_code ret = 0;
@@ -524,7 +524,8 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, TALL
nt_status = ads_verify_ticket(out_mem_ctx,
gensec_krb5_state->krb5_context,
gensec_krb5_state->krb5_auth_context,
- lp_realm(), &in,
+ lp_realm(),
+ gensec_get_target_service(gensec_security), &in,
&principal, &pac, &unwrapped_out,
&gensec_krb5_state->krb5_keyblock);
} else {
@@ -532,7 +533,9 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, TALL
nt_status = ads_verify_ticket(out_mem_ctx,
gensec_krb5_state->krb5_context,
gensec_krb5_state->krb5_auth_context,
- lp_realm(), &unwrapped_in,
+ lp_realm(),
+ gensec_get_target_service(gensec_security),
+ &unwrapped_in,
&principal, &pac, &unwrapped_out,
&gensec_krb5_state->krb5_keyblock);
}
@@ -683,7 +686,6 @@ static const struct gensec_security_ops gensec_krb5_security_ops = {
.update = gensec_krb5_update,
.session_key = gensec_krb5_session_key,
.session_info = gensec_krb5_session_info,
- .end = gensec_krb5_end
};
static const struct gensec_security_ops gensec_ms_krb5_security_ops = {
@@ -695,7 +697,6 @@ static const struct gensec_security_ops gensec_ms_krb5_security_ops = {
.update = gensec_krb5_update,
.session_key = gensec_krb5_session_key,
.session_info = gensec_krb5_session_info,
- .end = gensec_krb5_end
};
diff --git a/source4/libcli/auth/gensec_ntlmssp.c b/source4/libcli/auth/gensec_ntlmssp.c
index 07dacfb5e0..cf8019402c 100644
--- a/source4/libcli/auth/gensec_ntlmssp.c
+++ b/source4/libcli/auth/gensec_ntlmssp.c
@@ -142,6 +142,23 @@ static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state,
return nt_status;
}
+static int gensec_ntlmssp_destroy(void *ptr)
+{
+ struct gensec_ntlmssp_state *gensec_ntlmssp_state = ptr;
+
+ if (gensec_ntlmssp_state->ntlmssp_state) {
+ ntlmssp_end(&gensec_ntlmssp_state->ntlmssp_state);
+ }
+
+ if (gensec_ntlmssp_state->auth_context) {
+ free_auth_context(&gensec_ntlmssp_state->auth_context);
+ }
+ if (gensec_ntlmssp_state->server_info) {
+ free_server_info(&gensec_ntlmssp_state->server_info);
+ }
+ return 0;
+}
+
static NTSTATUS gensec_ntlmssp_start(struct gensec_security *gensec_security)
{
struct gensec_ntlmssp_state *gensec_ntlmssp_state;
@@ -155,6 +172,8 @@ static NTSTATUS gensec_ntlmssp_start(struct gensec_security *gensec_security)
gensec_ntlmssp_state->auth_context = NULL;
gensec_ntlmssp_state->server_info = NULL;
+ talloc_set_destructor(gensec_ntlmssp_state, gensec_ntlmssp_destroy);
+
gensec_security->private_data = gensec_ntlmssp_state;
return NT_STATUS_OK;
}
@@ -173,7 +192,7 @@ static NTSTATUS gensec_ntlmssp_server_start(struct gensec_security *gensec_secur
gensec_ntlmssp_state = gensec_security->private_data;
- if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_start(gensec_security,
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_start(gensec_ntlmssp_state,
&gensec_ntlmssp_state->ntlmssp_state))) {
return nt_status;
}
@@ -186,7 +205,7 @@ static NTSTATUS gensec_ntlmssp_server_start(struct gensec_security *gensec_secur
}
ntlmssp_state = gensec_ntlmssp_state->ntlmssp_state;
- nt_status = make_auth_context_subsystem(gensec_security, &gensec_ntlmssp_state->auth_context);
+ nt_status = make_auth_context_subsystem(gensec_ntlmssp_state, &gensec_ntlmssp_state->auth_context);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
@@ -213,7 +232,7 @@ static NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_secur
}
gensec_ntlmssp_state = gensec_security->private_data;
- status = ntlmssp_client_start(gensec_security,
+ status = ntlmssp_client_start(gensec_ntlmssp_state,
&gensec_ntlmssp_state->ntlmssp_state);
if (!NT_STATUS_IS_OK(status)) {
return status;
@@ -250,9 +269,11 @@ static NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_secur
return status;
}
- status = gensec_get_password(gensec_security, gensec_ntlmssp_state, &password);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
+ if (gensec_security->user.name) {
+ status = gensec_get_password(gensec_security, gensec_ntlmssp_state, &password);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
}
if (password) {
@@ -397,24 +418,6 @@ static NTSTATUS gensec_ntlmssp_session_info(struct gensec_security *gensec_secur
return NT_STATUS_OK;
}
-static void gensec_ntlmssp_end(struct gensec_security *gensec_security)
-{
- struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
-
- if (gensec_ntlmssp_state->ntlmssp_state) {
- ntlmssp_end(&gensec_ntlmssp_state->ntlmssp_state);
- }
-
- if (gensec_ntlmssp_state->auth_context) {
- free_auth_context(&gensec_ntlmssp_state->auth_context);
- }
- if (gensec_ntlmssp_state->server_info) {
- free_server_info(&gensec_ntlmssp_state->server_info);
- }
- talloc_free(gensec_ntlmssp_state);
- gensec_security->private_data = NULL;
-}
-
static const struct gensec_security_ops gensec_ntlmssp_security_ops = {
.name = "ntlmssp",
.sasl_name = "NTLM",
@@ -430,7 +433,6 @@ static const struct gensec_security_ops gensec_ntlmssp_security_ops = {
.unseal_packet = gensec_ntlmssp_unseal_packet,
.session_key = gensec_ntlmssp_session_key,
.session_info = gensec_ntlmssp_session_info,
- .end = gensec_ntlmssp_end
};
diff --git a/source4/libcli/auth/kerberos.c b/source4/libcli/auth/kerberos.c
index 9510aaa7fb..a8d9d75a1e 100644
--- a/source4/libcli/auth/kerberos.c
+++ b/source4/libcli/auth/kerberos.c
@@ -3,8 +3,9 @@
kerberos utility library
Copyright (C) Andrew Tridgell 2001
Copyright (C) Remus Koos 2001
-
-
+ Copyright (C) Nalin Dahyabhai 2004.
+ Copyright (C) Jeremy Allison 2004.
+
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 2 of the License, or
@@ -24,9 +25,12 @@
#include "system/kerberos.h"
#include "libcli/auth/kerberos.h"
#include "system/time.h"
+#include "secrets.h"
#ifdef HAVE_KRB5
+#define LIBADS_CCACHE_NAME "MEMORY:libads"
+
/*
we use a prompter to avoid a crash bug in the kerberos libs when
dealing with empty passwords
@@ -41,7 +45,7 @@ kerb_prompter(krb5_context ctx, void *data,
{
if (num_prompts == 0) return 0;
- memset(prompts[0].reply->data, 0, prompts[0].reply->length);
+ memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
if (prompts[0].reply->length > 0) {
if (data) {
strncpy(prompts[0].reply->data, data, prompts[0].reply->length-1);
@@ -54,10 +58,12 @@ kerb_prompter(krb5_context ctx, void *data,
}
/*
- simulate a kinit, putting the tgt in the default cache location
- remus@snapserver.com
+ simulate a kinit, putting the tgt in the given credentials cache.
+ Orignally by remus@snapserver.com
*/
- int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, const char *principal, const char *password, time_t *expire_time, time_t *kdc_time)
+ int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc,
+ const char *principal, const char *password,
+ time_t *expire_time, time_t *kdc_time)
{
krb5_error_code code = 0;
krb5_principal me;
@@ -103,15 +109,21 @@ kerb_prompter(krb5_context ctx, void *data,
return 0;
}
-
/*
- simulate a kinit, putting the tgt in the default cache location
- remus@snapserver.com
+ simulate a kinit, putting the tgt in the given credentials cache.
+ If cache_name == NULL place in default cache location.
+
+ Orignally by remus@snapserver.com
*/
-int kerberos_kinit_password(const char *principal, const char *password, int time_offset, time_t *expire_time, time_t *kdc_time)
+int kerberos_kinit_password(const char *principal,
+ const char *password,
+ int time_offset,
+ time_t *expire_time,
+ const char *cache_name,
+ time_t *kdc_time)
{
+ int code;
krb5_context ctx = NULL;
- krb5_error_code code = 0;
krb5_ccache cc = NULL;
if ((code = krb5_init_context(&ctx)))
@@ -121,21 +133,19 @@ int kerberos_kinit_password(const char *principal, const char *password, int tim
krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
}
- if ((code = krb5_cc_default(ctx, &cc))) {
+ if ((code = krb5_cc_resolve(ctx, cache_name ?
+ cache_name : krb5_cc_default_name(ctx), &cc))) {
krb5_free_context(ctx);
return code;
}
- if ((code = kerberos_kinit_password_cc(ctx, cc, principal, password, expire_time, kdc_time))) {
- krb5_cc_close(ctx, cc);
- krb5_free_context(ctx);
- return code;
- }
+ code = kerberos_kinit_password_cc(ctx, cc, principal, password, expire_time, kdc_time);
- return 0;
-}
-
+ krb5_cc_close(ctx, cc);
+ krb5_free_context(ctx);
+ return code;
+}
/* run kinit to setup our ccache */
int ads_kinit_password(ADS_STRUCT *ads)
@@ -151,7 +161,8 @@ int ads_kinit_password(ADS_STRUCT *ads)
return KRB5_LIBOS_CANTREADPWD;
}
- ret = kerberos_kinit_password(s, ads->auth.password, ads->auth.time_offset, &ads->auth.expire, NULL);
+ ret = kerberos_kinit_password(s, ads->auth.password, ads->auth.time_offset,
+ &ads->auth.expire, NULL, NULL);
if (ret) {
DEBUG(0,("kerberos_kinit_password %s failed: %s\n",
@@ -168,7 +179,8 @@ int ads_kdestroy(const char *cc_name)
krb5_ccache cc = NULL;
if ((code = krb5_init_context (&ctx))) {
- DEBUG(3, ("ads_kdestroy: kdb5_init_context rc=%d\n", code));
+ DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n",
+ error_message(code)));
return code;
}
@@ -179,19 +191,600 @@ int ads_kdestroy(const char *cc_name)
}
} else {
if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
- DEBUG(3, ("ads_kdestroy: krb5_cc_resolve rc=%d\n",
- code));
+ DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
+ error_message(code)));
krb5_free_context(ctx);
return code;
}
}
if ((code = krb5_cc_destroy (ctx, cc))) {
- DEBUG(3, ("ads_kdestroy: krb5_cc_destroy rc=%d\n", code));
+ DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
+ error_message(code)));
}
krb5_free_context (ctx);
return code;
}
+/************************************************************************
+ Routine to fetch the salting principal for a service. Active
+ Directory may use a non-obvious principal name to generate the salt
+ when it determines the key to use for encrypting tickets for a service,
+ and hopefully we detected that when we joined the domain.
+ ************************************************************************/
+
+static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
+{
+ char *ret = NULL;
+
+#if 0
+ asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, service, enctype);
+ if (!key) {
+ return NULL;
+ }
+ ret = (char *)secrets_fetch(key, NULL);
+ SAFE_FREE(key);
+#endif
+ return ret;
+}
+
+/************************************************************************
+ Routine to get the salting principal for this service. Active
+ Directory may use a non-obvious principal name to generate the salt
+ when it determines the key to use for encrypting tickets for a service,
+ and hopefully we detected that when we joined the domain.
+ Caller must free if return is not null.
+ ************************************************************************/
+
+krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
+ krb5_principal host_princ,
+ int enctype)
+{
+ char *unparsed_name = NULL, *salt_princ_s = NULL;
+ krb5_principal ret_princ = NULL;
+
+ if (krb5_unparse_name(context, host_princ, &unparsed_name) != 0) {
+ return (krb5_principal)NULL;
+ }
+
+ if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
+ krb5_free_unparsed_name(context, unparsed_name);
+ return (krb5_principal)NULL;
+ }
+
+ if (krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
+ krb5_free_unparsed_name(context, unparsed_name);
+ SAFE_FREE(salt_princ_s);
+ return (krb5_principal)NULL;
+ }
+ krb5_free_unparsed_name(context, unparsed_name);
+ SAFE_FREE(salt_princ_s);
+ return ret_princ;
+}
+
+/************************************************************************
+ Routine to set the salting principal for this service. Active
+ Directory may use a non-obvious principal name to generate the salt
+ when it determines the key to use for encrypting tickets for a service,
+ and hopefully we detected that when we joined the domain.
+ Setting principal to NULL deletes this entry.
+ ************************************************************************/
+
+ BOOL kerberos_secrets_store_salting_principal(const char *service,
+ int enctype,
+ const char *principal)
+{
+ char *key = NULL;
+ BOOL ret = False;
+ krb5_context context = NULL;
+ krb5_principal princ = NULL;
+ char *princ_s = NULL;
+ char *unparsed_name = NULL;
+
+ krb5_init_context(&context);
+ if (!context) {
+ return False;
+ }
+ if (strchr_m(service, '@')) {
+ asprintf(&princ_s, "%s", service);
+ } else {
+ asprintf(&princ_s, "%s@%s", service, lp_realm());
+ }
+
+ if (krb5_parse_name(context, princ_s, &princ) != 0) {
+ goto out;
+
+ }
+ if (krb5_unparse_name(context, princ, &unparsed_name) != 0) {
+ goto out;
+ }
+
+ asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype);
+ if (!key) {
+ goto out;
+ }
+
+#if 0
+ if ((principal != NULL) && (strlen(principal) > 0)) {
+ ret = secrets_store(key, principal, strlen(principal) + 1);
+ } else {
+ ret = secrets_delete(key);
+ }
+#endif
+
+ out:
+
+ SAFE_FREE(key);
+ SAFE_FREE(princ_s);
+
+ if (unparsed_name) {
+ krb5_free_unparsed_name(context, unparsed_name);
+ }
+ if (context) {
+ krb5_free_context(context);
+ }
+
+ return ret;
+}
+
+/************************************************************************
+ Routine to get initial credentials as a service ticket for the local machine.
+ Returns a buffer initialized with krb5_mk_req_extended.
+ ************************************************************************/
+
+static krb5_error_code get_service_ticket(krb5_context ctx,
+ krb5_ccache ccache,
+ const char *service_principal,
+ int enctype,
+ krb5_data *p_outbuf)
+{
+ krb5_creds creds, *new_creds = NULL;
+ char *service_s = NULL;
+ char *machine_account = NULL, *password = NULL;
+ krb5_data in_data;
+ krb5_auth_context auth_context = NULL;
+ krb5_error_code err = 0;
+
+ ZERO_STRUCT(creds);
+
+ asprintf(&machine_account, "%s$@%s", lp_netbios_name(), lp_realm());
+ if (machine_account == NULL) {
+ goto out;
+ }
+ password = secrets_fetch_machine_password(lp_workgroup());
+ if (password == NULL) {
+ goto out;
+ }
+ if ((err = kerberos_kinit_password(machine_account, password, 0, NULL, LIBADS_CCACHE_NAME, NULL)) != 0) {
+ DEBUG(0,("get_service_ticket: kerberos_kinit_password %s@%s failed: %s\n",
+ machine_account,
+ lp_realm(),
+ error_message(err)));
+ goto out;
+ }
+
+ /* Ok - the above call has gotten a TGT. Now we need to get a service
+ ticket to ourselves. */
+
+ /* Set up the enctype and client and server principal fields for krb5_get_credentials. */
+ kerberos_set_creds_enctype(&creds, enctype);
+
+ if ((err = krb5_cc_get_principal(ctx, ccache, &creds.client))) {
+ DEBUG(3, ("get_service_ticket: krb5_cc_get_principal failed: %s\n",
+ error_message(err)));
+ goto out;
+ }
+
+ if (strchr_m(service_principal, '@')) {
+ asprintf(&service_s, "%s", service_principal);
+ } else {
+ asprintf(&service_s, "%s@%s", service_principal, lp_realm());
+ }
+
+ if ((err = krb5_parse_name(ctx, service_s, &creds.server))) {
+ DEBUG(0,("get_service_ticket: krb5_parse_name %s failed: %s\n",
+ service_s, error_message(err)));
+ goto out;
+ }
+
+ if ((err = krb5_get_credentials(ctx, 0, ccache, &creds, &new_creds))) {
+ DEBUG(5,("get_service_ticket: krb5_get_credentials for %s enctype %d failed: %s\n",
+ service_s, enctype, error_message(err)));
+ goto out;
+ }
+
+ memset(&in_data, '\0', sizeof(in_data));
+ if ((err = krb5_mk_req_extended(ctx, &auth_context, 0, &in_data,
+ new_creds, p_outbuf)) != 0) {
+ DEBUG(0,("get_service_ticket: krb5_mk_req_extended failed: %s\n",
+ error_message(err)));
+ goto out;
+ }
+
+ out:
+
+ if (auth_context) {
+ krb5_auth_con_free(ctx, auth_context);
+ }
+ if (new_creds) {
+ krb5_free_creds(ctx, new_creds);
+ }
+ if (creds.server) {
+ krb5_free_principal(ctx, creds.server);
+ }
+ if (creds.client) {
+ krb5_free_principal(ctx, creds.client);
+ }
+
+ SAFE_FREE(service_s);
+ SAFE_FREE(password);
+ SAFE_FREE(machine_account);
+ return err;
+}
+
+/************************************************************************
+ Check if the machine password can be used in conjunction with the salting_principal
+ to generate a key which will successfully decrypt the AP_REQ already
+ gotten as a message to the local machine.
+ ************************************************************************/
+
+static BOOL verify_service_password(krb5_context ctx,
+ int enctype,
+ const char *salting_principal,
+ krb5_data *in_data)
+{
+ BOOL ret = False;
+ krb5_principal salting_kprinc = NULL;
+ krb5_ticket *ticket = NULL;
+ krb5_keyblock key;
+ krb5_data passdata;
+ char *salting_s = NULL;
+ char *machine_account = NULL, *password = NULL;
+ krb5_auth_context auth_context = NULL;
+ krb5_error_code err;
+
+ memset(&passdata, '\0', sizeof(passdata));
+ memset(&key, '\0', sizeof(key));
+
+ asprintf(&machine_account, "%s$@%s", lp_netbios_name(), lp_realm());
+ if (machine_account == NULL) {
+ goto out;
+ }
+ password = secrets_fetch_machine_password(lp_workgroup());
+ if (password == NULL) {
+ goto out;
+ }
+
+ if (strchr_m(salting_principal, '@')) {
+ asprintf(&salting_s, "%s", salting_principal);
+ } else {
+ asprintf(&salting_s, "%s@%s", salting_principal, lp_realm());
+ }
+
+ if ((err = krb5_parse_name(ctx, salting_s, &salting_kprinc))) {
+ DEBUG(0,("verify_service_password: krb5_parse_name %s failed: %s\n",
+ salting_s, error_message(err)));
+ goto out;
+ }
+
+ passdata.length = strlen(password);
+ passdata.data = (char*)password;
+ if ((err = create_kerberos_key_from_string_direct(ctx, salting_kprinc, &passdata, &key, enctype))) {
+ DEBUG(0,("verify_service_password: create_kerberos_key_from_string %d failed: %s\n",
+ enctype, error_message(err)));
+ goto out;
+ }
+
+ if ((err = krb5_auth_con_init(ctx, &auth_context)) != 0) {
+ DEBUG(0,("verify_service_password: krb5_auth_con_init failed %s\n", error_message(err)));
+ goto out;
+ }
+
+ if ((err = krb5_auth_con_setuseruserkey(ctx, auth_context, &key)) != 0) {
+ DEBUG(0,("verify_service_password: krb5_auth_con_setuseruserkey failed %s\n", error_message(err)));
+ goto out;
+ }
+
+ if (!(err = krb5_rd_req(ctx, &auth_context, in_data, NULL, NULL, NULL, &ticket))) {
+ DEBUG(10,("verify_service_password: decrypted message with enctype %u salt %s!\n",
+ (unsigned int)enctype, salting_s));
+ ret = True;
+ }
+
+ out:
+
+ memset(&passdata, 0, sizeof(passdata));
+ krb5_free_keyblock_contents(ctx, &key);
+ if (ticket != NULL) {
+ krb5_free_ticket(ctx, ticket);
+ }
+ if (salting_kprinc) {
+ krb5_free_principal(ctx, salting_kprinc);
+ }
+ SAFE_FREE(salting_s);
+ SAFE_FREE(password);
+ SAFE_FREE(machine_account);
+ return ret;
+}
+
+/************************************************************************
+ *
+ * From the current draft of kerberos-clarifications:
+ *
+ * It is not possible to reliably generate a user's key given a pass
+ * phrase without contacting the KDC, since it will not be known
+ * whether alternate salt or parameter values are required.
+ *
+ * And because our server has a password, we have this exact problem. We
+ * make multiple guesses as to which principal name provides the salt which
+ * the KDC is using.
+ *
+ ************************************************************************/
+
+static void kerberos_derive_salting_principal_for_enctype(const char *service_principal,
+ krb5_context ctx,
+ krb5_ccache ccache,
+ krb5_enctype enctype,
+ krb5_enctype *enctypes)
+{
+ char *salting_principals[3] = {NULL, NULL, NULL}, *second_principal = NULL;
+ krb5_error_code err = 0;
+ krb5_data outbuf;
+ int i, j;
+
+ memset(&outbuf, '\0', sizeof(outbuf));
+
+ /* Check that the service_principal is useful. */
+ if ((service_principal == NULL) || (strlen(service_principal) == 0)) {
+ return;
+ }
+
+ /* Generate our first guess -- the principal as-given. */
+ asprintf(&salting_principals[0], "%s", service_principal);
+ if ((salting_principals[0] == NULL) || (strlen(salting_principals[0]) == 0)) {
+ return;
+ }
+
+ /* Generate our second guess -- the computer's principal, as Win2k3. */
+ asprintf(&second_principal, "host/%s.%s", lp_netbios_name(), lp_realm());
+ if (second_principal != NULL) {
+ strlower_m(second_principal);
+ asprintf(&salting_principals[1], "%s@%s", second_principal, lp_realm());
+ SAFE_FREE(second_principal);
+ }
+ if ((salting_principals[1] == NULL) || (strlen(salting_principals[1]) == 0)) {
+ goto out;
+ }
+
+ /* Generate our third guess -- the computer's principal, as Win2k. */
+ asprintf(&second_principal, "HOST/%s", lp_netbios_name());
+ if (second_principal != NULL) {
+ strlower_m(second_principal + 5);
+ asprintf(&salting_principals[2], "%s@%s",
+ second_principal, lp_realm());
+ SAFE_FREE(second_principal);
+ }
+ if ((salting_principals[2] == NULL) || (strlen(salting_principals[2]) == 0)) {
+ goto out;
+ }
+
+ /* Get a service ticket for ourselves into our memory ccache. */
+ /* This will commonly fail if there is no principal by that name (and we're trying
+ many names). So don't print a debug 0 error. */
+
+ if ((err = get_service_ticket(ctx, ccache, service_principal, enctype, &outbuf)) != 0) {
+ DEBUG(3, ("verify_service_password: get_service_ticket failed: %s\n",
+ error_message(err)));
+ goto out;
+ }
+
+ /* At this point we have a message to ourselves, salted only the KDC knows how. We
+ have to work out what that salting is. */
+
+ /* Try and find the correct salting principal. */
+ for (i = 0; i < sizeof(salting_principals) / sizeof(salting_principals[i]); i++) {
+ if (verify_service_password(ctx, enctype, salting_principals[i], &outbuf)) {
+ break;
+ }
+ }
+
+ /* If we failed to get a match, return. */
+ if (i >= sizeof(salting_principals) / sizeof(salting_principals[i])) {
+ goto out;
+ }
+
+ /* If we succeeded, store the principal for use for all enctypes which
+ * share the same cipher and string-to-key function. Doing this here
+ * allows servers which just pass a keytab to krb5_rd_req() to work
+ * correctly. */
+ for (j = 0; enctypes[j] != 0; j++) {
+ if (enctype != enctypes[j]) {
+ /* If this enctype isn't compatible with the one which
+ * we used, skip it. */
+
+ if (!kerberos_compatible_enctypes(ctx, enctypes[j], enctype))
+ continue;
+ }
+ /* If the principal which gives us the proper salt is the one
+ * which we would normally guess, don't bother noting anything
+ * in the secrets tdb. */
+ if (strcmp(service_principal, salting_principals[i]) != 0) {
+ kerberos_secrets_store_salting_principal(service_principal,
+ enctypes[j],
+ salting_principals[i]);
+ }
+ }
+
+ out :
+
+ kerberos_free_data_contents(ctx, &outbuf);
+ SAFE_FREE(salting_principals[0]);
+ SAFE_FREE(salting_principals[1]);
+ SAFE_FREE(salting_principals[2]);
+ SAFE_FREE(second_principal);
+}
+
+/************************************************************************
+ Go through all the possible enctypes for this principal.
+ ************************************************************************/
+
+static void kerberos_derive_salting_principal_direct(krb5_context context,
+ krb5_ccache ccache,
+ krb5_enctype *enctypes,
+ char *service_principal)
+{
+ int i;
+
+ /* Try for each enctype separately, because the rules are
+ * different for different enctypes. */
+ for (i = 0; enctypes[i] != 0; i++) {
+ /* Delete secrets entry first. */
+ kerberos_secrets_store_salting_principal(service_principal, 0, NULL);
+#ifdef ENCTYPE_ARCFOUR_HMAC
+ if (enctypes[i] == ENCTYPE_ARCFOUR_HMAC) {
+ /* Of course this'll always work, so just save
+ * ourselves the effort. */
+ continue;
+ }
+#endif
+ /* Try to figure out what's going on with this
+ * principal. */
+ kerberos_derive_salting_principal_for_enctype(service_principal,
+ context,
+ ccache,
+ enctypes[i],
+ enctypes);
+ }
+}
+
+/************************************************************************
+ Wrapper function for the above.
+ ************************************************************************/
+
+BOOL kerberos_derive_salting_principal(char *service_principal)
+{
+ krb5_context context = NULL;
+ krb5_enctype *enctypes = NULL;
+ krb5_ccache ccache = NULL;
+ krb5_error_code ret = 0;
+
+ initialize_krb5_error_table();
+ if ((ret = krb5_init_context(&context)) != 0) {
+ DEBUG(1,("kerberos_derive_cifs_salting_principals: krb5_init_context failed. %s\n",
+ error_message(ret)));
+ return False;
+ }
+ if ((ret = get_kerberos_allowed_etypes(context, &enctypes)) != 0) {
+ DEBUG(1,("kerberos_derive_cifs_salting_principals: get_kerberos_allowed_etypes failed. %s\n",
+ error_message(ret)));
+ goto out;
+ }
+
+ if ((ret = krb5_cc_resolve(context, LIBADS_CCACHE_NAME, &ccache)) != 0) {
+ DEBUG(3, ("get_service_ticket: krb5_cc_resolve for %s failed: %s\n",
+ LIBADS_CCACHE_NAME, error_message(ret)));
+ goto out;
+ }
+
+ kerberos_derive_salting_principal_direct(context, ccache, enctypes, service_principal);
+
+ out:
+ if (enctypes) {
+ free_kerberos_etypes(context, enctypes);
+ }
+ if (ccache) {
+ krb5_cc_destroy(context, ccache);
+ }
+ if (context) {
+ krb5_free_context(context);
+ }
+
+ return ret ? False : True;
+}
+
+/************************************************************************
+ Core function to try and determine what salt is being used for any keytab
+ keys.
+ ************************************************************************/
+
+BOOL kerberos_derive_cifs_salting_principals(void)
+{
+ fstring my_fqdn;
+ char *service = NULL;
+ krb5_context context = NULL;
+ krb5_enctype *enctypes = NULL;
+ krb5_ccache ccache = NULL;
+ krb5_error_code ret = 0;
+ BOOL retval = False;
+
+ initialize_krb5_error_table();
+ if ((ret = krb5_init_context(&context)) != 0) {
+ DEBUG(1,("kerberos_derive_cifs_salting_principals: krb5_init_context failed. %s\n",
+ error_message(ret)));
+ return False;
+ }
+ if ((ret = get_kerberos_allowed_etypes(context, &enctypes)) != 0) {
+ DEBUG(1,("kerberos_derive_cifs_salting_principals: get_kerberos_allowed_etypes failed. %s\n",
+ error_message(ret)));
+ goto out;
+ }
+
+ if ((ret = krb5_cc_resolve(context, LIBADS_CCACHE_NAME, &ccache)) != 0) {
+ DEBUG(3, ("get_service_ticket: krb5_cc_resolve for %s failed: %s\n",
+ LIBADS_CCACHE_NAME, error_message(ret)));
+ goto out;
+ }
+
+ if (asprintf(&service, "%s$", lp_netbios_name()) != -1) {
+ strlower_m(service);
+ kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
+ SAFE_FREE(service);
+ }
+ if (asprintf(&service, "cifs/%s", lp_netbios_name()) != -1) {
+ strlower_m(service);
+ kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
+ SAFE_FREE(service);
+ }
+ if (asprintf(&service, "host/%s", lp_netbios_name()) != -1) {
+ strlower_m(service);
+ kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
+ SAFE_FREE(service);
+ }
+ if (asprintf(&service, "cifs/%s.%s", lp_netbios_name(), lp_realm()) != -1) {
+ strlower_m(service);
+ kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
+ SAFE_FREE(service);
+ }
+ if (asprintf(&service, "host/%s.%s", lp_netbios_name(), lp_realm()) != -1) {
+ strlower_m(service);
+ kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
+ SAFE_FREE(service);
+ }
+ name_to_fqdn(my_fqdn, lp_netbios_name());
+ if (asprintf(&service, "cifs/%s", my_fqdn) != -1) {
+ strlower_m(service);
+ kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
+ SAFE_FREE(service);
+ }
+ if (asprintf(&service, "host/%s", my_fqdn) != -1) {
+ strlower_m(service);
+ kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
+ SAFE_FREE(service);
+ }
+
+ retval = True;
+
+ out:
+ if (enctypes) {
+ free_kerberos_etypes(context, enctypes);
+ }
+ if (ccache) {
+ krb5_cc_destroy(context, ccache);
+ }
+ if (context) {
+ krb5_free_context(context);
+ }
+ return retval;
+}
#endif
diff --git a/source4/libcli/auth/kerberos.h b/source4/libcli/auth/kerberos.h
index 3ab71f7875..9bb6d22eb6 100644
--- a/source4/libcli/auth/kerberos.h
+++ b/source4/libcli/auth/kerberos.h
@@ -61,6 +61,7 @@ const krb5_data *krb5_princ_component(krb5_context context, krb5_principal princ
/* Samba wrapper function for krb5 functionality. */
void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr);
int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype);
+int create_kerberos_key_from_string_direct(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype);
krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt);
krb5_error_code krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters);
krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes);
@@ -74,13 +75,23 @@ krb5_error_code ads_krb5_mk_req(krb5_context context,
krb5_data *outbuf);
DATA_BLOB get_auth_data_from_tkt(TALLOC_CTX *mem_ctx,
krb5_ticket *tkt);
+
NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx,
krb5_context context,
krb5_auth_context auth_context,
- const char *realm, const DATA_BLOB *ticket,
+ const char *realm, const char *service,
+ const DATA_BLOB *ticket,
char **principal, DATA_BLOB *auth_data,
DATA_BLOB *ap_rep,
krb5_keyblock *keyblock);
-int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, const char *principal, const char *password, time_t *expire_time, time_t *kdc_time);
+int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc,
+ const char *principal, const char *password,
+ time_t *expire_time, time_t *kdc_time);
+krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
+ krb5_principal host_princ,
+ int enctype);
+void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype);
+BOOL kerberos_compatible_enctypes(krb5_context context, krb5_enctype enctype1, krb5_enctype enctype2);
+void kerberos_free_data_contents(krb5_context context, krb5_data *pdata);
#endif /* HAVE_KRB5 */
diff --git a/source4/libcli/auth/kerberos_verify.c b/source4/libcli/auth/kerberos_verify.c
index d00394fd79..92980f1122 100644
--- a/source4/libcli/auth/kerberos_verify.c
+++ b/source4/libcli/auth/kerberos_verify.c
@@ -29,6 +29,9 @@
#ifdef HAVE_KRB5
+#if !defined(HAVE_KRB5_PRINC_COMPONENT)
+const krb5_data *krb5_princ_component(krb5_context, krb5_principal, int );
+#endif
static DATA_BLOB unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data)
{
DATA_BLOB out;
@@ -65,21 +68,23 @@ static DATA_BLOB unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data)
ads_keytab_add_entry function for details.
***********************************************************************************/
-static krb5_error_code ads_keytab_verify_ticket(krb5_context context, krb5_auth_context auth_context,
- const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt,
- krb5_keyblock *keyblock)
+static krb5_error_code ads_keytab_verify_ticket(TALLOC_CTX *mem_ctx, krb5_context context,
+ krb5_auth_context auth_context,
+ const char *service,
+ const DATA_BLOB *ticket, krb5_data *p_packet,
+ krb5_ticket **pp_tkt,
+ krb5_keyblock *keyblock)
{
krb5_error_code ret = 0;
-
+ BOOL auth_ok = False;
krb5_keytab keytab = NULL;
- krb5_kt_cursor cursor;
- krb5_keytab_entry kt_entry;
- char *princ_name = NULL;
-
- ZERO_STRUCT(kt_entry);
- ZERO_STRUCT(cursor);
-
- ZERO_STRUCTP(keyblock);
+ char *my_fqdn;
+ fstring my_name;
+ fstring my_Fqdn, my_NAME;
+ char *p_fqdn;
+ char *host_princ_s[18];
+ krb5_principal host_princ;
+ int i;
ret = krb5_kt_default(context, &keytab);
if (ret) {
@@ -87,73 +92,74 @@ static krb5_error_code ads_keytab_verify_ticket(krb5_context context, krb5_auth_
goto out;
}
- ret = krb5_kt_start_seq_get(context, keytab, &cursor);
- if (ret) {
- DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_start_seq_get failed (%s)\n", error_message(ret)));
- goto out;
+ /* Generate the list of principal names which we expect clients might
+ * want to use for authenticating to the file service. */
+
+ fstrcpy(my_name, lp_netbios_name());
+ strlower_m(my_name);
+
+ fstrcpy(my_NAME, lp_netbios_name());
+ strupper_m(my_NAME);
+
+ my_fqdn = name_to_fqdn(mem_ctx, lp_netbios_name());
+ strlower_m(my_fqdn);
+
+ p_fqdn = strchr_m(my_fqdn, '.');
+ fstrcpy(my_Fqdn, my_NAME);
+ if (p_fqdn) {
+ fstrcat(my_Fqdn, p_fqdn);
}
- while (!(ret = krb5_kt_next_entry(context, keytab, &kt_entry, &cursor))) {
- ret = krb5_unparse_name(context, kt_entry.principal, &princ_name);
+ asprintf(&host_princ_s[0], "%s$@%s", my_name, lp_realm());
+ asprintf(&host_princ_s[1], "%s$@%s", my_NAME, lp_realm());
+ asprintf(&host_princ_s[2], "host/%s@%s", my_name, lp_realm());
+ asprintf(&host_princ_s[3], "host/%s@%s", my_NAME, lp_realm());
+ asprintf(&host_princ_s[4], "host/%s@%s", my_fqdn, lp_realm());
+ asprintf(&host_princ_s[5], "host/%s@%s", my_Fqdn, lp_realm());
+ asprintf(&host_princ_s[6], "HOST/%s@%s", my_name, lp_realm());
+ asprintf(&host_princ_s[7], "HOST/%s@%s", my_NAME, lp_realm());
+ asprintf(&host_princ_s[8], "HOST/%s@%s", my_fqdn, lp_realm());
+ asprintf(&host_princ_s[9], "HOST/%s@%s", my_Fqdn, lp_realm());
+ asprintf(&host_princ_s[10], "%s/%s@%s", service, my_name, lp_realm());
+ asprintf(&host_princ_s[11], "%s/%s@%s", service, my_NAME, lp_realm());
+ asprintf(&host_princ_s[12], "%s/%s@%s", service, my_fqdn, lp_realm());
+ asprintf(&host_princ_s[13], "%s/%s@%s", service, my_Fqdn, lp_realm());
+ asprintf(&host_princ_s[14], "%s/%s@%s", strupper_talloc(mem_ctx, service), my_name, lp_realm());
+ asprintf(&host_princ_s[15], "%s/%s@%s", strupper_talloc(mem_ctx, service), my_NAME, lp_realm());
+ asprintf(&host_princ_s[16], "%s/%s@%s", strupper_talloc(mem_ctx, service), my_fqdn, lp_realm());
+ asprintf(&host_princ_s[17], "%s/%s@%s", strupper_talloc(mem_ctx, service), my_Fqdn, lp_realm());
+
+ /* Now try to verify the ticket using the key associated with each of
+ * the principals which we think clients will expect us to be
+ * participating as. */
+ for (i = 0; i < sizeof(host_princ_s) / sizeof(host_princ_s[0]); i++) {
+ host_princ = NULL;
+ ret = krb5_parse_name(context, host_princ_s[i], &host_princ);
if (ret) {
- DEBUG(1, ("ads_keytab_verify_ticket: krb5_unparse_name failed (%s)\n", error_message(ret)));
+ DEBUG(1, ("ads_keytab_verify_ticket: krb5_parse_name(%s) failed (%s)\n",
+ host_princ_s[i], error_message(ret)));
goto out;
}
- DEBUG(10, ("Checking principal: %s\n", princ_name));
- /* Look for a CIFS ticket */
- if (!strncasecmp(princ_name, "cifs/", 5) ||
- !strncasecmp(princ_name, "host/", 5) ||
- !strncasecmp(princ_name, "ldap/", 5)) {
-#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK
- krb5_auth_con_setuseruserkey(context, auth_context, &kt_entry.keyblock);
-#else
- krb5_auth_con_setuseruserkey(context, auth_context, &kt_entry.key);
-#endif
-
- p_packet->length = ticket->length;
- p_packet->data = (krb5_pointer)ticket->data;
-
- ret = krb5_rd_req(context, &auth_context, p_packet, NULL, NULL, NULL, pp_tkt);
- if (!ret) {
- unsigned int keytype;
- krb5_free_unparsed_name(context, princ_name);
- princ_name = NULL;
-#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK
- keytype = (unsigned int) kt_entry.keyblock.keytype;
- copy_EncryptionKey(&kt_entry.keyblock, keyblock);
-#else
- keytype = (unsigned int) kt_entry.key.enctype;
- /* TODO: copy the keyblock on MIT krb5*/
-#endif
- DEBUG(10,("ads_keytab_verify_ticket: enc type [%u] decrypted message !\n",
- keytype));
-
- break;
- }
- }
- krb5_free_unparsed_name(context, princ_name);
- princ_name = NULL;
+ p_packet->length = ticket->length;
+ p_packet->data = (krb5_pointer)ticket->data;
+ *pp_tkt = NULL;
+ ret = krb5_rd_req(context, &auth_context, p_packet, host_princ, keytab, NULL, pp_tkt);
+ krb5_free_principal(context, host_princ);
+ if (ret) {
+ DEBUG(0, ("krb5_rd_req(%s) failed: %s\n", host_princ_s[i], error_message(ret)));
+ } else {
+ DEBUG(10,("krb5_rd_req succeeded for principal %s\n", host_princ_s[i]));
+ auth_ok = True;
+ break;
+ }
}
- if (ret && ret != KRB5_KT_END) {
- /* This failed because something went wrong, not because the keytab file was empty. */
- DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_next_entry failed (%s)\n", error_message(ret)));
- } else if (ret == KRB5_KT_END) {
- DEBUG(10, ("ads_keytab_verify_ticket: no keytab entry found: %s\n", error_message(ret)));
- } else {
- DEBUG(10, ("ads_keytab_verify_ticket: keytab entry found: %s\n", princ_name));
+
+ for (i = 0; i < sizeof(host_princ_s) / sizeof(host_princ_s[0]); i++) {
+ SAFE_FREE(host_princ_s[i]);
}
+
out:
- if (princ_name) {
- krb5_free_unparsed_name(context, princ_name);
- }
- {
- krb5_kt_cursor zero_csr;
- ZERO_STRUCT(zero_csr);
- if ((memcmp(&cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) {
- krb5_kt_end_seq_get(context, keytab, &cursor);
- }
- }
if (keytab) {
krb5_kt_close(context, keytab);
}
@@ -165,10 +171,12 @@ static krb5_error_code ads_keytab_verify_ticket(krb5_context context, krb5_auth_
Try to verify a ticket using the secrets.tdb.
***********************************************************************************/
-static krb5_error_code ads_secrets_verify_ticket(krb5_context context, krb5_auth_context auth_context,
- krb5_principal host_princ,
- const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt,
- krb5_keyblock *keyblock)
+static krb5_error_code ads_secrets_verify_ticket(TALLOC_CTX *mem_ctx, krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_principal host_princ,
+ const DATA_BLOB *ticket, krb5_data *p_packet,
+ krb5_ticket **pp_tkt,
+ krb5_keyblock *keyblock)
{
krb5_error_code ret = 0;
char *password_s = NULL;
@@ -250,12 +258,13 @@ static krb5_error_code ads_secrets_verify_ticket(krb5_context context, krb5_auth
***********************************************************************************/
NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx,
- krb5_context context,
- krb5_auth_context auth_context,
- const char *realm, const DATA_BLOB *ticket,
- char **principal, DATA_BLOB *auth_data,
- DATA_BLOB *ap_rep,
- krb5_keyblock *keyblock)
+ krb5_context context,
+ krb5_auth_context auth_context,
+ const char *realm, const char *service,
+ const DATA_BLOB *ticket,
+ char **principal, DATA_BLOB *auth_data,
+ DATA_BLOB *ap_rep,
+ krb5_keyblock *keyblock)
{
NTSTATUS sret = NT_STATUS_LOGON_FAILURE;
krb5_data packet;
@@ -267,8 +276,6 @@ static krb5_error_code ads_secrets_verify_ticket(krb5_context context, krb5_auth
char *host_princ_s = NULL;
BOOL got_replay_mutex = False;
- char *myname;
-
char *malloc_principal;
ZERO_STRUCT(packet);
@@ -279,9 +286,8 @@ static krb5_error_code ads_secrets_verify_ticket(krb5_context context, krb5_auth
like. We have to go through all this to allow us to store
the secret internally, instead of using /etc/krb5.keytab */
- myname = name_to_fqdn(mem_ctx, lp_netbios_name());
- strlower_m(myname);
- asprintf(&host_princ_s, "host/%s@%s", myname, lp_realm());
+ asprintf(&host_princ_s, "%s$", lp_netbios_name());
+ strlower_m(host_princ_s);
ret = krb5_parse_name(context, host_princ_s, &host_princ);
if (ret) {
DEBUG(1,("ads_verify_ticket: krb5_parse_name(%s) failed (%s)\n",
@@ -316,11 +322,13 @@ static krb5_error_code ads_secrets_verify_ticket(krb5_context context, krb5_auth
goto out;
}
- ret = ads_keytab_verify_ticket(context, auth_context, ticket, &packet, &tkt, keyblock);
+ ret = ads_keytab_verify_ticket(mem_ctx, context, auth_context,
+ service, ticket, &packet, &tkt, keyblock);
if (ret) {
DEBUG(10, ("ads_secrets_verify_ticket: using host principal: [%s]\n", host_princ_s));
- ret = ads_secrets_verify_ticket(context, auth_context, host_princ,
- ticket, &packet, &tkt, keyblock);
+ ret = ads_secrets_verify_ticket(mem_ctx, context, auth_context,
+ host_princ, ticket,
+ &packet, &tkt, keyblock);
}
release_server_mutex();
diff --git a/source4/libcli/auth/ntlmssp.c b/source4/libcli/auth/ntlmssp.c
index 5d61361d69..bee93985ac 100644
--- a/source4/libcli/auth/ntlmssp.c
+++ b/source4/libcli/auth/ntlmssp.c
@@ -146,6 +146,9 @@ static NTSTATUS set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *ch
NTSTATUS ntlmssp_set_username(struct ntlmssp_state *ntlmssp_state, const char *user)
{
ntlmssp_state->user = talloc_strdup(ntlmssp_state, user);
+ if (!user) {
+ ntlmssp_state->user = NULL;
+ }
if (!ntlmssp_state->user) {
return NT_STATUS_NO_MEMORY;
}
diff --git a/source4/libcli/auth/spnego.c b/source4/libcli/auth/spnego.c
index 01757a0487..988d0b32a5 100644
--- a/source4/libcli/auth/spnego.c
+++ b/source4/libcli/auth/spnego.c
@@ -44,6 +44,17 @@ struct spnego_state {
struct gensec_security *sub_sec_security;
};
+
+static int gensec_spnego_destroy(void *ptr)
+{
+ struct spnego_state *spnego_state = ptr;
+
+ if (spnego_state->sub_sec_security) {
+ gensec_end(&spnego_state->sub_sec_security);
+ }
+ return 0;
+}
+
static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security)
{
struct spnego_state *spnego_state;
@@ -57,6 +68,8 @@ static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_securi
spnego_state->state_position = SPNEGO_CLIENT_START;
spnego_state->sub_sec_security = NULL;
+ talloc_set_destructor(spnego_state, gensec_spnego_destroy);
+
gensec_security->private_data = spnego_state;
return NT_STATUS_OK;
}
@@ -74,6 +87,8 @@ static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_securi
spnego_state->state_position = SPNEGO_SERVER_START;
spnego_state->sub_sec_security = NULL;
+ talloc_set_destructor(spnego_state, gensec_spnego_destroy);
+
gensec_security->private_data = spnego_state;
return NT_STATUS_OK;
}
@@ -221,7 +236,8 @@ static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec
continue;
}
- nt_status = gensec_subcontext_start(gensec_security,
+ nt_status = gensec_subcontext_start(spnego_state,
+ gensec_security,
&spnego_state->sub_sec_security);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
@@ -257,7 +273,8 @@ static NTSTATUS gensec_spnego_parse_negTokenInit(struct gensec_security *gensec_
DATA_BLOB null_data_blob = data_blob(NULL,0);
for (i=0; mechType && mechType[i]; i++) {
- nt_status = gensec_subcontext_start(gensec_security,
+ nt_status = gensec_subcontext_start(spnego_state,
+ gensec_security,
&spnego_state->sub_sec_security);
if (!NT_STATUS_IS_OK(nt_status)) {
break;
@@ -317,7 +334,8 @@ static NTSTATUS gensec_spnego_client_negTokenInit(struct gensec_security *gensec
return NT_STATUS_INVALID_PARAMETER;
}
- nt_status = gensec_subcontext_start(gensec_security,
+ nt_status = gensec_subcontext_start(spnego_state,
+ gensec_security,
&spnego_state->sub_sec_security);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
@@ -467,19 +485,12 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
return nt_status;
} else {
const char **mechlist = gensec_security_oids(out_mem_ctx, GENSEC_OID_SPNEGO);
- const char *mechListMIC;
-
- mechListMIC = talloc_asprintf(out_mem_ctx,"%s$@%s",
- lp_netbios_name(),
- lp_realm());
- if (!mechListMIC) {
- return NT_STATUS_NO_MEMORY;
- }
spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
spnego_out.negTokenInit.mechTypes = mechlist;
spnego_out.negTokenInit.reqFlags = 0;
- spnego_out.negTokenInit.mechListMIC = data_blob_string_const(mechListMIC);
+ spnego_out.negTokenInit.mechListMIC
+ = data_blob_string_const(gensec_get_target_principal(gensec_security));
spnego_out.negTokenInit.mechToken = unwrapped_out;
if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
@@ -694,19 +705,6 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
return NT_STATUS_INVALID_PARAMETER;
}
-static void gensec_spnego_end(struct gensec_security *gensec_security)
-{
- struct spnego_state *spnego_state = gensec_security->private_data;
-
- if (spnego_state->sub_sec_security) {
- gensec_end(&spnego_state->sub_sec_security);
- }
-
- talloc_free(spnego_state);
-
- gensec_security->private_data = NULL;
-}
-
static const struct gensec_security_ops gensec_spnego_security_ops = {
.name = "spnego",
.sasl_name = "GSS-SPNEGO",
@@ -722,7 +720,6 @@ static const struct gensec_security_ops gensec_spnego_security_ops = {
.unseal_packet = gensec_spnego_unseal_packet,
.session_key = gensec_spnego_session_key,
.session_info = gensec_spnego_session_info,
- .end = gensec_spnego_end
};
NTSTATUS gensec_spnego_init(void)