*/
#include "includes.h"
+#include <tevent.h>
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/util/time_basic.h"
#include "auth/ntlmssp/ntlmssp.h"
#include "auth/ntlmssp/ntlmssp_private.h"
#include "../librpc/gen_ndr/ndr_ntlmssp.h"
#include "auth/ntlmssp/ntlmssp_ndr.h"
#include "../libcli/auth/libcli_auth.h"
#include "../lib/crypto/crypto.h"
+#include "auth/gensec/gensec.h"
+#include "auth/gensec/gensec_internal.h"
+#include "auth/common_auth.h"
+#include "param/param.h"
+#include "param/loadparm.h"
+#include "libcli/security/session.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
/**
* Determine correct target name flags for reply, given server role
}
/**
- * Next state function for the Negotiate packet
+ * Next state function for the NTLMSSP Negotiate packet
*
- * @param ntlmssp_state NTLMSSP state
+ * @param gensec_security GENSEC state
* @param out_mem_ctx Memory context for *out
* @param in The request, as a DATA_BLOB. reply.data must be NULL
* @param out The reply, as an allocated DATA_BLOB, caller to free.
* @return Errors or MORE_PROCESSING_REQUIRED if (normal) a reply is required.
*/
-NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state,
- TALLOC_CTX *out_mem_ctx,
- const DATA_BLOB request, DATA_BLOB *reply)
+NTSTATUS gensec_ntlmssp_server_negotiate(struct gensec_security *gensec_security,
+ TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB request, DATA_BLOB *reply)
{
+ struct gensec_ntlmssp_context *gensec_ntlmssp =
+ talloc_get_type_abort(gensec_security->private_data,
+ struct gensec_ntlmssp_context);
+ struct ntlmssp_state *ntlmssp_state = gensec_ntlmssp->ntlmssp_state;
+ struct auth4_context *auth_context = gensec_security->auth_context;
DATA_BLOB struct_blob;
uint32_t neg_flags = 0;
uint32_t ntlmssp_command, chal_flags;
uint8_t cryptkey[8];
const char *target_name;
NTSTATUS status;
+ struct timeval tv_now = timeval_current();
+ /*
+ * See [MS-NLMP]
+ *
+ * Windows NT 4.0, windows_2000: use 30 minutes,
+ * Windows XP, Windows Server 2003, Windows Vista,
+ * Windows Server 2008, Windows 7, and Windows Server 2008 R2
+ * use 36 hours.
+ *
+ * Newer systems doesn't check this, likely because the
+ * connectionless NTLMSSP is no longer supported.
+ *
+ * As we expect the AUTHENTICATION_MESSAGE to arrive
+ * directly after the NEGOTIATE_MESSAGE (typically less than
+ * as 1 second later). We use a hard timeout of 30 Minutes.
+ *
+ * We don't look at AUTHENTICATE_MESSAGE.NtChallengeResponse.TimeStamp
+ * instead we just remember our own time.
+ */
+ uint32_t max_lifetime = 30 * 60;
+ struct timeval tv_end = timeval_add(&tv_now, max_lifetime, 0);
/* parse the NTLMSSP packet */
#if 0
#endif
if (request.length) {
+ if (request.length > UINT16_MAX) {
+ DEBUG(1, ("ntlmssp_server_negotiate: reject large request of length %u\n",
+ (unsigned int)request.length));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
if ((request.length < 16) || !msrpc_parse(ntlmssp_state, &request, "Cdd",
"NTLMSSP",
&ntlmssp_command,
}
}
- ntlmssp_handle_neg_flags(ntlmssp_state, neg_flags, ntlmssp_state->allow_lm_key);
-
- /* Ask our caller what challenge they would like in the packet */
- status = ntlmssp_state->get_challenge(ntlmssp_state, cryptkey);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(1, ("ntlmssp_server_negotiate: backend doesn't give a challenge: %s\n",
- nt_errstr(status)));
+ status = ntlmssp_handle_neg_flags(ntlmssp_state, neg_flags, "negotiate");
+ if (!NT_STATUS_IS_OK(status)){
return status;
}
- /* Check if we may set the challenge */
- if (!ntlmssp_state->may_set_challenge(ntlmssp_state)) {
- ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
+ /* Ask our caller what challenge they would like in the packet */
+ if (auth_context->get_ntlm_challenge) {
+ status = auth_context->get_ntlm_challenge(auth_context, cryptkey);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("gensec_ntlmssp_server_negotiate: failed to get challenge: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+ } else {
+ DEBUG(1, ("gensec_ntlmssp_server_negotiate: backend doesn't give a challenge\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
}
/* The flags we send back are not just the negotiated flags,
*/
chal_flags = ntlmssp_state->neg_flags;
+ ntlmssp_state->server.challenge_endtime = timeval_to_nttime(&tv_end);
/* get the right name to fill in as 'target' */
target_name = ntlmssp_target_name(ntlmssp_state,
cryptkey, 8);
/* This creates the 'blob' of names that appears at the end of the packet */
- if (chal_flags & NTLMSSP_NEGOTIATE_TARGET_INFO)
- {
- status = msrpc_gen(ntlmssp_state, &struct_blob, "aaaaa",
- MsvAvNbDomainName, target_name,
- MsvAvNbComputerName, ntlmssp_state->server.netbios_name,
- MsvAvDnsDomainName, ntlmssp_state->server.dns_domain,
- MsvAvDnsComputerName, ntlmssp_state->server.dns_name,
- MsvAvEOL, "");
- if (!NT_STATUS_IS_OK(status)) {
- return status;
+ if (chal_flags & NTLMSSP_NEGOTIATE_TARGET_INFO) {
+ enum ndr_err_code err;
+ struct AV_PAIR *pairs = NULL;
+ uint32_t count = 5;
+
+ pairs = talloc_zero_array(ntlmssp_state, struct AV_PAIR, count + 1);
+ if (pairs == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pairs[0].AvId = MsvAvNbDomainName;
+ pairs[0].Value.AvNbDomainName = target_name;
+
+ pairs[1].AvId = MsvAvNbComputerName;
+ pairs[1].Value.AvNbComputerName = ntlmssp_state->server.netbios_name;
+
+ pairs[2].AvId = MsvAvDnsDomainName;
+ pairs[2].Value.AvDnsDomainName = ntlmssp_state->server.dns_domain;
+
+ pairs[3].AvId = MsvAvDnsComputerName;
+ pairs[3].Value.AvDnsComputerName= ntlmssp_state->server.dns_name;
+
+ if (!ntlmssp_state->force_old_spnego) {
+ pairs[4].AvId = MsvAvTimestamp;
+ pairs[4].Value.AvTimestamp =
+ timeval_to_nttime(&tv_now);
+ count += 1;
+
+ pairs[5].AvId = MsvAvEOL;
+ } else {
+ pairs[4].AvId = MsvAvEOL;
+ }
+
+ ntlmssp_state->server.av_pair_list.count = count;
+ ntlmssp_state->server.av_pair_list.pair = pairs;
+
+ err = ndr_push_struct_blob(&struct_blob,
+ ntlmssp_state,
+ &ntlmssp_state->server.av_pair_list,
+ (ndr_push_flags_fn_t)ndr_push_AV_PAIR_LIST);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ return NT_STATUS_NO_MEMORY;
}
} else {
struct_blob = data_blob_null;
{
/* Marshal the packet in the right format, be it unicode or ASCII */
const char *gen_string;
- DATA_BLOB version_blob = data_blob_null;
-
- if (chal_flags & NTLMSSP_NEGOTIATE_VERSION) {
- enum ndr_err_code err;
- struct ntlmssp_VERSION vers;
-
- /* "What Windows returns" as a version number. */
- ZERO_STRUCT(vers);
- vers.ProductMajorVersion = NTLMSSP_WINDOWS_MAJOR_VERSION_6;
- vers.ProductMinorVersion = NTLMSSP_WINDOWS_MINOR_VERSION_1;
- vers.ProductBuild = 0;
- vers.NTLMRevisionCurrent = NTLMSSP_REVISION_W2K3;
-
- err = ndr_push_struct_blob(&version_blob,
- ntlmssp_state,
- &vers,
- (ndr_push_flags_fn_t)ndr_push_ntlmssp_VERSION);
-
- if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
- data_blob_free(&struct_blob);
- return NT_STATUS_NO_MEMORY;
- }
- }
+ const DATA_BLOB version_blob = ntlmssp_version_blob();
if (ntlmssp_state->unicode) {
gen_string = "CdUdbddBb";
version_blob.data, version_blob.length);
if (!NT_STATUS_IS_OK(status)) {
- data_blob_free(&version_blob);
data_blob_free(&struct_blob);
return status;
}
- data_blob_free(&version_blob);
-
if (DEBUGLEVEL >= 10) {
struct CHALLENGE_MESSAGE *challenge = talloc(
ntlmssp_state, struct CHALLENGE_MESSAGE);
data_blob_free(&struct_blob);
+ ntlmssp_state->negotiate_blob = data_blob_dup_talloc(ntlmssp_state,
+ request);
+ if (ntlmssp_state->negotiate_blob.length != request.length) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ntlmssp_state->challenge_blob = data_blob_dup_talloc(ntlmssp_state,
+ *reply);
+ if (ntlmssp_state->challenge_blob.length != reply->length) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
ntlmssp_state->expected_state = NTLMSSP_AUTH;
return NT_STATUS_MORE_PROCESSING_REQUIRED;
}
struct ntlmssp_server_auth_state {
+ struct gensec_security *gensec_security;
+ struct gensec_ntlmssp_context *gensec_ntlmssp;
+ DATA_BLOB in;
+ struct auth_usersupplied_info *user_info;
DATA_BLOB user_session_key;
DATA_BLOB lm_session_key;
/* internal variables used by KEY_EXCH (client-supplied user session key */
uint8_t session_nonce[16];
};
+static NTSTATUS ntlmssp_server_preauth(struct gensec_security *gensec_security,
+ struct gensec_ntlmssp_context *gensec_ntlmssp,
+ struct ntlmssp_server_auth_state *state,
+ const DATA_BLOB request);
+static void ntlmssp_server_auth_done(struct tevent_req *subreq);
+static NTSTATUS ntlmssp_server_postauth(struct gensec_security *gensec_security,
+ struct gensec_ntlmssp_context *gensec_ntlmssp,
+ struct ntlmssp_server_auth_state *state,
+ DATA_BLOB request);
+
+struct tevent_req *ntlmssp_server_auth_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct gensec_security *gensec_security,
+ const DATA_BLOB in)
+{
+ struct gensec_ntlmssp_context *gensec_ntlmssp =
+ talloc_get_type_abort(gensec_security->private_data,
+ struct gensec_ntlmssp_context);
+ struct auth4_context *auth_context = gensec_security->auth_context;
+ struct tevent_req *req = NULL;
+ struct ntlmssp_server_auth_state *state = NULL;
+ uint8_t authoritative = 0;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct ntlmssp_server_auth_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->gensec_security = gensec_security;
+ state->gensec_ntlmssp = gensec_ntlmssp;
+ state->in = in;
+
+ status = ntlmssp_server_preauth(gensec_security,
+ gensec_ntlmssp,
+ state, in);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (auth_context->check_ntlm_password_send != NULL) {
+ struct tevent_req *subreq = NULL;
+
+ subreq = auth_context->check_ntlm_password_send(state, ev,
+ auth_context,
+ state->user_info);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq,
+ ntlmssp_server_auth_done,
+ req);
+ return req;
+ }
+
+ if (auth_context->check_ntlm_password == NULL) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return tevent_req_post(req, ev);
+ }
+
+ status = auth_context->check_ntlm_password(auth_context,
+ gensec_ntlmssp,
+ state->user_info,
+ &authoritative,
+ &gensec_ntlmssp->server_returned_info,
+ &state->user_session_key,
+ &state->lm_session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_INFO("Checking NTLMSSP password for %s\\%s failed: %s\n",
+ state->user_info->client.domain_name,
+ state->user_info->client.account_name,
+ nt_errstr(status));
+ }
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ talloc_steal(state, state->user_session_key.data);
+ talloc_steal(state, state->lm_session_key.data);
+
+ status = ntlmssp_server_postauth(gensec_security,
+ gensec_ntlmssp,
+ state, in);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
/**
* Next state function for the Authenticate packet
*
* @return Errors or NT_STATUS_OK.
*/
-static NTSTATUS ntlmssp_server_preauth(struct ntlmssp_state *ntlmssp_state,
+static NTSTATUS ntlmssp_server_preauth(struct gensec_security *gensec_security,
+ struct gensec_ntlmssp_context *gensec_ntlmssp,
struct ntlmssp_server_auth_state *state,
const DATA_BLOB request)
{
+ struct ntlmssp_state *ntlmssp_state = gensec_ntlmssp->ntlmssp_state;
+ struct auth4_context *auth_context = gensec_security->auth_context;
+ struct auth_usersupplied_info *user_info = NULL;
uint32_t ntlmssp_command, auth_flags;
NTSTATUS nt_status;
-
+ const unsigned int version_len = 8;
+ DATA_BLOB version_blob = data_blob_null;
+ const unsigned int mic_len = NTLMSSP_MIC_SIZE;
+ DATA_BLOB mic_blob = data_blob_null;
uint8_t session_nonce_hash[16];
-
const char *parse_string;
+ bool ok;
+ struct timeval endtime;
+ bool expired = false;
#if 0
file_save("ntlmssp_auth.dat", request.data, request.length);
#endif
if (ntlmssp_state->unicode) {
- parse_string = "CdBBUUUBd";
+ parse_string = "CdBBUUUBdbb";
} else {
- parse_string = "CdBBAAABd";
+ parse_string = "CdBBAAABdbb";
}
/* zero these out */
ntlmssp_state->client.netbios_name = NULL;
/* now the NTLMSSP encoded auth hashes */
- if (!msrpc_parse(ntlmssp_state, &request, parse_string,
+ ok = msrpc_parse(ntlmssp_state, &request, parse_string,
"NTLMSSP",
&ntlmssp_command,
&ntlmssp_state->lm_resp,
&ntlmssp_state->user,
&ntlmssp_state->client.netbios_name,
&state->encrypted_session_key,
- &auth_flags)) {
+ &auth_flags,
+ &version_blob, version_len,
+ &mic_blob, mic_len);
+ if (!ok) {
+ DEBUG(10, ("ntlmssp_server_auth: failed to parse NTLMSSP (nonfatal):\n"));
+ dump_data(10, request.data, request.length);
+
+ data_blob_free(&version_blob);
+ data_blob_free(&mic_blob);
+
+ if (ntlmssp_state->unicode) {
+ parse_string = "CdBBUUUBd";
+ } else {
+ parse_string = "CdBBAAABd";
+ }
+
+ ok = msrpc_parse(ntlmssp_state, &request, parse_string,
+ "NTLMSSP",
+ &ntlmssp_command,
+ &ntlmssp_state->lm_resp,
+ &ntlmssp_state->nt_resp,
+ &ntlmssp_state->domain,
+ &ntlmssp_state->user,
+ &ntlmssp_state->client.netbios_name,
+ &state->encrypted_session_key,
+ &auth_flags);
+ }
+
+ if (!ok) {
DEBUG(10, ("ntlmssp_server_auth: failed to parse NTLMSSP (nonfatal):\n"));
dump_data(10, request.data, request.length);
talloc_steal(state, state->encrypted_session_key.data);
- if (auth_flags)
- ntlmssp_handle_neg_flags(ntlmssp_state, auth_flags, ntlmssp_state->allow_lm_key);
+ if (auth_flags != 0) {
+ nt_status = ntlmssp_handle_neg_flags(ntlmssp_state,
+ auth_flags,
+ "authenticate");
+ if (!NT_STATUS_IS_OK(nt_status)){
+ return nt_status;
+ }
+ }
if (DEBUGLEVEL >= 10) {
struct AUTHENTICATE_MESSAGE *authenticate = talloc(
file_save("lmhash1.dat", &ntlmssp_state->lm_resp.data, &ntlmssp_state->lm_resp.length);
#endif
+ if (ntlmssp_state->nt_resp.length > 24) {
+ struct NTLMv2_RESPONSE v2_resp;
+ enum ndr_err_code err;
+ uint32_t i = 0;
+ uint32_t count = 0;
+ const struct AV_PAIR *flags = NULL;
+ const struct AV_PAIR *eol = NULL;
+ uint32_t av_flags = 0;
+
+ err = ndr_pull_struct_blob(&ntlmssp_state->nt_resp,
+ ntlmssp_state,
+ &v2_resp,
+ (ndr_pull_flags_fn_t)ndr_pull_NTLMv2_RESPONSE);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ nt_status = ndr_map_error2ntstatus(err);
+ DEBUG(1,("%s: failed to parse NTLMv2_RESPONSE of length %zu for "
+ "user=[%s] domain=[%s] workstation=[%s] - %s %s\n",
+ __func__, ntlmssp_state->nt_resp.length,
+ ntlmssp_state->user, ntlmssp_state->domain,
+ ntlmssp_state->client.netbios_name,
+ ndr_errstr(err), nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ if (DEBUGLVL(10)) {
+ NDR_PRINT_DEBUG(NTLMv2_RESPONSE, &v2_resp);
+ }
+
+ eol = ndr_ntlmssp_find_av(&v2_resp.Challenge.AvPairs,
+ MsvAvEOL);
+ if (eol == NULL) {
+ DEBUG(1,("%s: missing MsvAvEOL for "
+ "user=[%s] domain=[%s] workstation=[%s]\n",
+ __func__, ntlmssp_state->user, ntlmssp_state->domain,
+ ntlmssp_state->client.netbios_name));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ flags = ndr_ntlmssp_find_av(&v2_resp.Challenge.AvPairs,
+ MsvAvFlags);
+ if (flags != NULL) {
+ av_flags = flags->Value.AvFlags;
+ }
+
+ if (av_flags & NTLMSSP_AVFLAG_MIC_IN_AUTHENTICATE_MESSAGE) {
+ if (mic_blob.length != NTLMSSP_MIC_SIZE) {
+ DEBUG(1,("%s: mic_blob.length[%u] for "
+ "user=[%s] domain=[%s] workstation=[%s]\n",
+ __func__,
+ (unsigned)mic_blob.length,
+ ntlmssp_state->user,
+ ntlmssp_state->domain,
+ ntlmssp_state->client.netbios_name));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (request.length <
+ (NTLMSSP_MIC_OFFSET + NTLMSSP_MIC_SIZE))
+ {
+ DEBUG(1,("%s: missing MIC "
+ "request.length[%u] for "
+ "user=[%s] domain=[%s] workstation=[%s]\n",
+ __func__,
+ (unsigned)request.length,
+ ntlmssp_state->user,
+ ntlmssp_state->domain,
+ ntlmssp_state->client.netbios_name));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ntlmssp_state->new_spnego = true;
+ }
+
+ count = ntlmssp_state->server.av_pair_list.count;
+ if (v2_resp.Challenge.AvPairs.count < count) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ for (i = 0; i < count; i++) {
+ const struct AV_PAIR *sp =
+ &ntlmssp_state->server.av_pair_list.pair[i];
+ const struct AV_PAIR *cp = NULL;
+
+ if (sp->AvId == MsvAvEOL) {
+ continue;
+ }
+
+ cp = ndr_ntlmssp_find_av(&v2_resp.Challenge.AvPairs,
+ sp->AvId);
+ if (cp == NULL) {
+ DEBUG(1,("%s: AvId 0x%x missing for"
+ "user=[%s] domain=[%s] "
+ "workstation=[%s]\n",
+ __func__,
+ (unsigned)sp->AvId,
+ ntlmssp_state->user,
+ ntlmssp_state->domain,
+ ntlmssp_state->client.netbios_name));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (cp->AvId) {
+#define CASE_STRING(v) case Msv ## v: do { \
+ int cmp; \
+ if (sp->Value.v == NULL) { \
+ return NT_STATUS_INTERNAL_ERROR; \
+ } \
+ if (cp->Value.v == NULL) { \
+ DEBUG(1,("%s: invalid %s " \
+ "got[%s] expect[%s] for " \
+ "user=[%s] domain=[%s] workstation=[%s]\n", \
+ __func__, #v, \
+ cp->Value.v, \
+ sp->Value.v, \
+ ntlmssp_state->user, \
+ ntlmssp_state->domain, \
+ ntlmssp_state->client.netbios_name)); \
+ return NT_STATUS_INVALID_PARAMETER; \
+ } \
+ cmp = strcmp(cp->Value.v, sp->Value.v); \
+ if (cmp != 0) { \
+ DEBUG(1,("%s: invalid %s " \
+ "got[%s] expect[%s] for " \
+ "user=[%s] domain=[%s] workstation=[%s]\n", \
+ __func__, #v, \
+ cp->Value.v, \
+ sp->Value.v, \
+ ntlmssp_state->user, \
+ ntlmssp_state->domain, \
+ ntlmssp_state->client.netbios_name)); \
+ return NT_STATUS_INVALID_PARAMETER; \
+ } \
+} while(0); break
+ CASE_STRING(AvNbComputerName);
+ CASE_STRING(AvNbDomainName);
+ CASE_STRING(AvDnsComputerName);
+ CASE_STRING(AvDnsDomainName);
+ CASE_STRING(AvDnsTreeName);
+ case MsvAvTimestamp:
+ if (cp->Value.AvTimestamp != sp->Value.AvTimestamp) {
+ struct timeval ct;
+ struct timeval st;
+ struct timeval_buf tmp1;
+ struct timeval_buf tmp2;
+
+ nttime_to_timeval(&ct,
+ cp->Value.AvTimestamp);
+ nttime_to_timeval(&st,
+ sp->Value.AvTimestamp);
+
+ DEBUG(1,("%s: invalid AvTimestamp "
+ "got[%s] expect[%s] for "
+ "user=[%s] domain=[%s] "
+ "workstation=[%s]\n",
+ __func__,
+ timeval_str_buf(&ct, false,
+ true, &tmp1),
+ timeval_str_buf(&st, false,
+ true, &tmp2),
+ ntlmssp_state->user,
+ ntlmssp_state->domain,
+ ntlmssp_state->client.netbios_name));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ break;
+ default:
+ /*
+ * This can't happen as we control
+ * ntlmssp_state->server.av_pair_list
+ */
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ }
+ }
+
+ nttime_to_timeval(&endtime, ntlmssp_state->server.challenge_endtime);
+ expired = timeval_expired(&endtime);
+ if (expired) {
+ struct timeval_buf tmp;
+ DEBUG(1,("%s: challenge invalid (expired %s) for "
+ "user=[%s] domain=[%s] workstation=[%s]\n",
+ __func__,
+ timeval_str_buf(&endtime, false, true, &tmp),
+ ntlmssp_state->user, ntlmssp_state->domain,
+ ntlmssp_state->client.netbios_name));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
/* NTLM2 uses a 'challenge' that is made of up both the server challenge, and a
client challenge
*/
if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
if (ntlmssp_state->nt_resp.length == 24 && ntlmssp_state->lm_resp.length == 24) {
- struct MD5Context md5_session_nonce_ctx;
+ MD5_CTX md5_session_nonce_ctx;
state->doing_ntlm2 = true;
memcpy(state->session_nonce, ntlmssp_state->internal_chal.data, 8);
MD5Update(&md5_session_nonce_ctx, state->session_nonce, 16);
MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
- ntlmssp_state->chal = data_blob_talloc(
- ntlmssp_state, session_nonce_hash, 8);
-
/* LM response is no longer useful */
data_blob_free(&ntlmssp_state->lm_resp);
/* We changed the effective challenge - set it */
- if (!NT_STATUS_IS_OK(nt_status = ntlmssp_state->set_challenge(ntlmssp_state, &ntlmssp_state->chal))) {
- return nt_status;
+ if (auth_context->set_ntlm_challenge) {
+ nt_status = auth_context->set_ntlm_challenge(auth_context,
+ session_nonce_hash,
+ "NTLMSSP callback (NTLM2)");
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("gensec_ntlmssp_server_negotiate: failed to get challenge: %s\n",
+ nt_errstr(nt_status)));
+ return nt_status;
+ }
+ } else {
+ DEBUG(1, ("gensec_ntlmssp_server_negotiate: backend doesn't have facility for challenge to be set\n"));
+
+ return NT_STATUS_NOT_IMPLEMENTED;
}
/* LM Key is incompatible. */
ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
}
}
+
+ user_info = talloc_zero(state, struct auth_usersupplied_info);
+ if (!user_info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ user_info->logon_parameters = MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
+ user_info->flags = 0;
+ user_info->mapped_state = false;
+ user_info->client.account_name = ntlmssp_state->user;
+ user_info->client.domain_name = ntlmssp_state->domain;
+ user_info->workstation_name = ntlmssp_state->client.netbios_name;
+ user_info->remote_host = gensec_get_remote_address(gensec_security);
+ user_info->local_host = gensec_get_local_address(gensec_security);
+ user_info->service_description
+ = gensec_get_target_service_description(gensec_security);
+
+ /*
+ * This will just be the string "NTLMSSP" from
+ * gensec_ntlmssp_final_auth_type, but ensures it stays in sync
+ * with the same use in the authorization logging triggered by
+ * gensec_session_info() later
+ */
+ user_info->auth_description = gensec_final_auth_type(gensec_security);
+
+ user_info->password_state = AUTH_PASSWORD_RESPONSE;
+ user_info->password.response.lanman = ntlmssp_state->lm_resp;
+ user_info->password.response.nt = ntlmssp_state->nt_resp;
+
+ state->user_info = user_info;
return NT_STATUS_OK;
}
+static void ntlmssp_server_auth_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ntlmssp_server_auth_state *state =
+ tevent_req_data(req,
+ struct ntlmssp_server_auth_state);
+ struct gensec_security *gensec_security = state->gensec_security;
+ struct gensec_ntlmssp_context *gensec_ntlmssp = state->gensec_ntlmssp;
+ struct auth4_context *auth_context = gensec_security->auth_context;
+ uint8_t authoritative = 0;
+ NTSTATUS status;
+
+ status = auth_context->check_ntlm_password_recv(subreq,
+ gensec_ntlmssp,
+ &authoritative,
+ &gensec_ntlmssp->server_returned_info,
+ &state->user_session_key,
+ &state->lm_session_key);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_INFO("Checking NTLMSSP password for %s\\%s failed: %s\n",
+ state->user_info->client.domain_name,
+ state->user_info->client.account_name,
+ nt_errstr(status));
+ }
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ talloc_steal(state, state->user_session_key.data);
+ talloc_steal(state, state->lm_session_key.data);
+
+ status = ntlmssp_server_postauth(state->gensec_security,
+ state->gensec_ntlmssp,
+ state, state->in);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
/**
* Next state function for the Authenticate packet
* (after authentication - figures out the session keys etc)
* @return Errors or NT_STATUS_OK.
*/
-static NTSTATUS ntlmssp_server_postauth(struct ntlmssp_state *ntlmssp_state,
- struct ntlmssp_server_auth_state *state)
+static NTSTATUS ntlmssp_server_postauth(struct gensec_security *gensec_security,
+ struct gensec_ntlmssp_context *gensec_ntlmssp,
+ struct ntlmssp_server_auth_state *state,
+ DATA_BLOB request)
{
+ struct ntlmssp_state *ntlmssp_state = gensec_ntlmssp->ntlmssp_state;
+ struct auth4_context *auth_context = gensec_security->auth_context;
DATA_BLOB user_session_key = state->user_session_key;
DATA_BLOB lm_session_key = state->lm_session_key;
NTSTATUS nt_status = NT_STATUS_OK;
DATA_BLOB session_key = data_blob(NULL, 0);
+ struct auth_session_info *session_info = NULL;
+
+ TALLOC_FREE(state->user_info);
+
+ if (lpcfg_map_to_guest(gensec_security->settings->lp_ctx) != NEVER_MAP_TO_GUEST
+ && auth_context->generate_session_info != NULL)
+ {
+ NTSTATUS tmp_status;
+
+ /*
+ * We need to check if the auth is anonymous or mapped to guest
+ */
+ tmp_status = auth_context->generate_session_info(auth_context, state,
+ gensec_ntlmssp->server_returned_info,
+ gensec_ntlmssp->ntlmssp_state->user,
+ AUTH_SESSION_INFO_SIMPLE_PRIVILEGES,
+ &session_info);
+ if (!NT_STATUS_IS_OK(tmp_status)) {
+ /*
+ * We don't care about failures,
+ * the worst result is that we try MIC checking
+ * for a map to guest authentication.
+ */
+ TALLOC_FREE(session_info);
+ }
+ }
+
+ if (session_info != NULL) {
+ if (security_session_user_level(session_info, NULL) < SECURITY_USER) {
+ /*
+ * Anonymous and GUEST are not secure anyway.
+ * avoid new_spnego and MIC checking.
+ */
+ ntlmssp_state->new_spnego = false;
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SEAL;
+ }
+ TALLOC_FREE(session_info);
+ }
dump_data_pw("NT session key:\n", user_session_key.data, user_session_key.length);
dump_data_pw("LM first-8:\n", lm_session_key.data, lm_session_key.length);
talloc_steal(ntlmssp_state, session_key.data);
}
- if (ntlmssp_state->session_key.length) {
+ if (ntlmssp_state->new_spnego) {
+ HMACMD5Context ctx;
+ uint8_t mic_buffer[NTLMSSP_MIC_SIZE] = { 0, };
+ int cmp;
+
+ hmac_md5_init_limK_to_64(ntlmssp_state->session_key.data,
+ ntlmssp_state->session_key.length,
+ &ctx);
+
+ hmac_md5_update(ntlmssp_state->negotiate_blob.data,
+ ntlmssp_state->negotiate_blob.length,
+ &ctx);
+ hmac_md5_update(ntlmssp_state->challenge_blob.data,
+ ntlmssp_state->challenge_blob.length,
+ &ctx);
+
+ /* checked were we set ntlmssp_state->new_spnego */
+ SMB_ASSERT(request.length >
+ (NTLMSSP_MIC_OFFSET + NTLMSSP_MIC_SIZE));
+
+ hmac_md5_update(request.data, NTLMSSP_MIC_OFFSET, &ctx);
+ hmac_md5_update(mic_buffer, NTLMSSP_MIC_SIZE, &ctx);
+ hmac_md5_update(request.data +
+ (NTLMSSP_MIC_OFFSET + NTLMSSP_MIC_SIZE),
+ request.length -
+ (NTLMSSP_MIC_OFFSET + NTLMSSP_MIC_SIZE),
+ &ctx);
+ hmac_md5_final(mic_buffer, &ctx);
+
+ cmp = memcmp(request.data + NTLMSSP_MIC_OFFSET,
+ mic_buffer, NTLMSSP_MIC_SIZE);
+ if (cmp != 0) {
+ DEBUG(1,("%s: invalid NTLMSSP_MIC for "
+ "user=[%s] domain=[%s] workstation=[%s]\n",
+ __func__,
+ ntlmssp_state->user,
+ ntlmssp_state->domain,
+ ntlmssp_state->client.netbios_name));
+ dump_data(1, request.data + NTLMSSP_MIC_OFFSET,
+ NTLMSSP_MIC_SIZE);
+ dump_data(1, mic_buffer,
+ NTLMSSP_MIC_SIZE);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ data_blob_free(&ntlmssp_state->negotiate_blob);
+ data_blob_free(&ntlmssp_state->challenge_blob);
+
+ if (gensec_ntlmssp_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
nt_status = ntlmssp_sign_init(ntlmssp_state);
}
+ data_blob_clear_free(&ntlmssp_state->internal_chal);
+ data_blob_clear_free(&ntlmssp_state->chal);
+ data_blob_clear_free(&ntlmssp_state->lm_resp);
+ data_blob_clear_free(&ntlmssp_state->nt_resp);
+
ntlmssp_state->expected_state = NTLMSSP_DONE;
return nt_status;
}
-
-/**
- * Next state function for the Authenticate packet
- *
- * @param gensec_security GENSEC state
- * @param out_mem_ctx Memory context for *out
- * @param in The request, as a DATA_BLOB. reply.data must be NULL
- * @param out The reply, as an allocated DATA_BLOB, caller to free.
- * @return Errors or NT_STATUS_OK if authentication sucessful
- */
-
-NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state,
- TALLOC_CTX *out_mem_ctx,
- const DATA_BLOB in, DATA_BLOB *out)
+NTSTATUS ntlmssp_server_auth_recv(struct tevent_req *req,
+ TALLOC_CTX *out_mem_ctx,
+ DATA_BLOB *out)
{
- struct ntlmssp_server_auth_state *state;
- NTSTATUS nt_status;
+ NTSTATUS status;
- /* zero the outbound NTLMSSP packet */
*out = data_blob_null;
- state = talloc_zero(ntlmssp_state, struct ntlmssp_server_auth_state);
- if (state == NULL) {
- return NT_STATUS_NO_MEMORY;
- }
-
- nt_status = ntlmssp_server_preauth(ntlmssp_state, state, in);
- if (!NT_STATUS_IS_OK(nt_status)) {
- TALLOC_FREE(state);
- return nt_status;
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
}
- /*
- * Note we don't check here for NTLMv2 auth settings. If NTLMv2 auth
- * is required (by "ntlm auth = no" and "lm auth = no" being set in the
- * smb.conf file) and no NTLMv2 response was sent then the password check
- * will fail here. JRA.
- */
-
- /* Finally, actually ask if the password is OK */
- nt_status = ntlmssp_state->check_password(ntlmssp_state,
- state,
- &state->user_session_key,
- &state->lm_session_key);
- if (!NT_STATUS_IS_OK(nt_status)) {
- DEBUG(5,("%s: Checking NTLMSSP password for %s\\%s failed: %s\n",
- __location__,
- ntlmssp_state->domain, ntlmssp_state->user,
- nt_errstr(nt_status)));
- TALLOC_FREE(state);
- return nt_status;
- }
-
- /* When we get more async in the auth code behind
- ntlmssp_state->check_password, the ntlmssp_server_postpath
- can be done in a callback */
-
- nt_status = ntlmssp_server_postauth(ntlmssp_state, state);
- TALLOC_FREE(state);
- return nt_status;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
}