summaryrefslogtreecommitdiff
path: root/source4/libcli/auth
diff options
context:
space:
mode:
Diffstat (limited to 'source4/libcli/auth')
-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)