summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2013-07-27 13:57:56 -0400
committerSimo Sorce <simo@redhat.com>2013-08-18 00:35:24 -0400
commitae725b89c771f7b0625433815302997aa3b8f839 (patch)
tree1241ca175d34c2d423f11aaaa03a2bdb820c1524
parent1d94f105588f961aba1bfd783cd18f0acf05e686 (diff)
downloadgss-ntlmssp-ae725b89c771f7b0625433815302997aa3b8f839.tar.gz
gss-ntlmssp-ae725b89c771f7b0625433815302997aa3b8f839.tar.xz
gss-ntlmssp-ae725b89c771f7b0625433815302997aa3b8f839.zip
Basic implementation of accept_sec_context
For now works only for satndalone server with access to a password file.
-rw-r--r--src/gss_creds.c28
-rw-r--r--src/gss_names.c2
-rw-r--r--src/gss_ntlmssp.h36
-rw-r--r--src/gss_sec_ctx.c449
-rw-r--r--src/gss_spi.c25
-rw-r--r--src/ntlm.c10
-rw-r--r--src/ntlm.h32
-rw-r--r--src/ntlm_crypto.c84
8 files changed, 625 insertions, 41 deletions
diff --git a/src/gss_creds.c b/src/gss_creds.c
index 37343b6..5ccc344 100644
--- a/src/gss_creds.c
+++ b/src/gss_creds.c
@@ -25,7 +25,6 @@
#include "gss_ntlmssp.h"
-
static int get_user_file_creds(struct gssntlm_name *name,
struct gssntlm_cred *cred)
{
@@ -196,7 +195,7 @@ static void gssntlm_copy_key(struct ntlm_key *dest, struct ntlm_key *src)
int gssntlm_copy_creds(struct gssntlm_cred *in, struct gssntlm_cred *out)
{
- char *dom = NULL, *usr = NULL;
+ char *dom = NULL, *usr = NULL, *srv = NULL;
int ret = 0;
out->type = GSSNTLM_CRED_NONE;
@@ -208,25 +207,18 @@ int gssntlm_copy_creds(struct gssntlm_cred *in, struct gssntlm_cred *out)
out->cred.anon.dummy = 1;
break;
case GSSNTLM_CRED_USER:
- dom = strdup(in->cred.user.user.data.user.domain);
- if (!dom) {
- ret = ENOMEM;
- goto done;
- }
- usr = strdup(in->cred.user.user.data.user.name);
- if (!usr) {
- ret = ENOMEM;
- goto done;
- }
- out->cred.user.user.data.user.domain = dom;
- out->cred.user.user.data.user.name = usr;
+ ret = gssntlm_copy_name(&in->cred.user.user,
+ &out->cred.user.user);
+ if (ret) goto done;
gssntlm_copy_key(&out->cred.user.nt_hash,
&in->cred.user.nt_hash);
gssntlm_copy_key(&out->cred.user.lm_hash,
&in->cred.user.lm_hash);
break;
case GSSNTLM_CRED_SERVER:
- out->cred.server.dummy = 1;
+ ret = gssntlm_copy_name(&in->cred.server.name,
+ &out->cred.server.name);
+ if (ret) goto done;
break;
}
out->type = in->type;
@@ -235,6 +227,7 @@ done:
if (ret) {
safefree(dom);
safefree(usr);
+ safefree(srv);
}
return ret;
}
@@ -250,15 +243,14 @@ void gssntlm_int_release_cred(struct gssntlm_cred *cred)
cred->cred.anon.dummy = 0;
break;
case GSSNTLM_CRED_USER:
- safefree(cred->cred.user.user.data.user.domain);
- safefree(cred->cred.user.user.data.user.name);
+ gssntlm_int_release_name(&cred->cred.user.user);
safezero(cred->cred.user.nt_hash.data, 16);
cred->cred.user.nt_hash.length = 0;
safezero(cred->cred.user.lm_hash.data, 16);
cred->cred.user.lm_hash.length = 0;
break;
case GSSNTLM_CRED_SERVER:
- cred->cred.server.dummy = 0;
+ gssntlm_int_release_name(&cred->cred.server.name);
break;
}
}
diff --git a/src/gss_names.c b/src/gss_names.c
index 9ab29c6..fe95aeb 100644
--- a/src/gss_names.c
+++ b/src/gss_names.c
@@ -93,7 +93,7 @@ static uint32_t uid_to_name(uint32_t *retmin, uid_t uid, char **name)
}
uint32_t gssntlm_import_name_by_mech(uint32_t *minor_status,
- gss_OID mech_type,
+ gss_const_OID mech_type,
gss_buffer_t input_name_buffer,
gss_OID input_name_type,
gss_name_t *output_name)
diff --git a/src/gss_ntlmssp.h b/src/gss_ntlmssp.h
index 8ea8851..670f182 100644
--- a/src/gss_ntlmssp.h
+++ b/src/gss_ntlmssp.h
@@ -42,6 +42,24 @@
NTLMSSP_REQUEST_TARGET | \
NTLMSSP_NEGOTIATE_UNICODE)
+#define NTLMSSP_DEFAULT_ALLOWED_SERVER_FLAGS ( \
+ NTLMSSP_NEGOTIATE_ALWAYS_SIGN | \
+ NTLMSSP_NEGOTIATE_56 | \
+ NTLMSSP_NEGOTIATE_KEY_EXCH | \
+ NTLMSSP_NEGOTIATE_128 | \
+ NTLMSSP_NEGOTIATE_VERSION | \
+ NTLMSSP_TARGET_TYPE_SERVER | \
+ NTLMSSP_TARGET_TYPE_DOMAIN | \
+ NTLMSSP_NEGOTIATE_ALWAYS_SIGN | \
+ NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED | \
+ NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED | \
+ NTLMSSP_NEGOTIATE_NTLM | \
+ NTLMSSP_NEGOTIATE_SEAL | \
+ NTLMSSP_NEGOTIATE_SIGN | \
+ NTLMSSP_REQUEST_TARGET | \
+ NTLMSSP_NEGOTIATE_OEM | \
+ NTLMSSP_NEGOTIATE_UNICODE)
+
struct gssntlm_name {
enum ntlm_name_type {
GSSNTLM_NAME_ANON,
@@ -78,7 +96,7 @@ struct gssntlm_cred {
struct ntlm_key lm_hash;
} user;
struct {
- int dummy;
+ struct gssntlm_name name;
} server;
} cred;
};
@@ -114,6 +132,8 @@ struct gssntlm_ctx {
struct ntlm_buffer chal_msg;
struct ntlm_buffer auth_msg;
+ uint8_t server_chal[8];
+
/* requested gss fags */
uint32_t gss_flags;
@@ -176,7 +196,7 @@ uint32_t gssntlm_import_name(uint32_t *minor_status,
gss_name_t *output_name);
uint32_t gssntlm_import_name_by_mech(uint32_t *minor_status,
- gss_OID mech_type,
+ gss_const_OID mech_type,
gss_buffer_t input_name_buffer,
gss_OID input_name_type,
gss_name_t *output_name);
@@ -211,4 +231,16 @@ uint32_t gssntlm_context_time(uint32_t *minor_status,
gss_ctx_id_t context_handle,
uint32_t *time_rec);
+uint32_t gssntlm_accept_sec_context(uint32_t *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_cred_id_t acceptor_cred_handle,
+ gss_buffer_t input_token_buffer,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_name_t *src_name,
+ gss_OID *mech_type,
+ gss_buffer_t output_token,
+ uint32_t *ret_flags,
+ uint32_t *time_rec,
+ gss_cred_id_t *delegated_cred_handle);
+
#endif /* _GSS_NTLMSSP_H_ */
diff --git a/src/gss_sec_ctx.c b/src/gss_sec_ctx.c
index 5ee4964..fb574f4 100644
--- a/src/gss_sec_ctx.c
+++ b/src/gss_sec_ctx.c
@@ -71,6 +71,10 @@ uint32_t gssntlm_init_sec_context(uint32_t *minor_status,
if (ret_flags) *ret_flags = 0;
if (time_rec) *time_rec = 0;
+ if (output_token == GSS_C_NO_BUFFER) {
+ return GSS_S_CALL_INACCESSIBLE_WRITE;
+ }
+
if (target_name) {
server = (struct gssntlm_name *)target_name;
if (server->type != GSSNTLM_NAME_SERVER) {
@@ -475,8 +479,7 @@ uint32_t gssntlm_init_sec_context(uint32_t *minor_status,
}
if (in_flags & (NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL)) {
- retmin = ntlm_signseal_keys(in_flags,
- (ctx->role == GSSNTLM_CLIENT),
+ retmin = ntlm_signseal_keys(in_flags, true,
&ctx->exported_session_key,
&ctx->send.sign_key,
&ctx->recv.sign_key,
@@ -508,7 +511,7 @@ uint32_t gssntlm_init_sec_context(uint32_t *minor_status,
goto done;
}
- ctx->stage = NTLMSSP_STAGE_AUTHENTICATE;
+ ctx->stage = NTLMSSP_STAGE_DONE;
output_token->value = malloc(ctx->auth_msg.length);
if (!output_token->value) {
@@ -599,3 +602,443 @@ uint32_t gssntlm_context_time(uint32_t *minor_status,
*time_rec = ctx->expiration_time - now;
return GSS_S_COMPLETE;
}
+
+uint32_t gssntlm_accept_sec_context(uint32_t *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_cred_id_t acceptor_cred_handle,
+ gss_buffer_t input_token,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_name_t *src_name,
+ gss_OID *mech_type,
+ gss_buffer_t output_token,
+ uint32_t *ret_flags,
+ uint32_t *time_rec,
+ gss_cred_id_t *delegated_cred_handle)
+{
+ struct gssntlm_ctx *ctx;
+ struct gssntlm_cred *cred;
+ int lm_compat_lvl = -1;
+ char *workstation = NULL;
+ char *domain = NULL;
+ struct ntlm_buffer challenge = { 0 };
+ struct gssntlm_name *server_name = NULL;
+ char *computer_name = NULL;
+ gss_buffer_desc tmpbuf;
+ uint64_t timestamp;
+ struct ntlm_buffer target_info = { 0 };
+ struct ntlm_buffer nt_chal_resp = { 0 };
+ struct ntlm_buffer lm_chal_resp = { 0 };
+ struct ntlm_buffer enc_sess_key = { 0 };
+ struct ntlm_key encrypted_random_session_key = { .length = 16 };
+ struct ntlm_key key_exchange_key = { .length = 16 };
+ uint8_t mic_data[16];
+ struct ntlm_buffer mic = { mic_data, 16 };
+ char *dom_name = NULL;
+ char *usr_name = NULL;
+ char *wks_name = NULL;
+ struct gssntlm_name *gss_usrname = NULL;
+ struct gssntlm_cred *usr_cred = NULL;
+ uint32_t retmin = 0;
+ uint32_t retmaj = 0;
+ uint32_t tmpmin;
+ uint32_t in_flags;
+ uint32_t msg_type;
+ uint8_t sec_req;
+ char *p;
+
+ if (context_handle == NULL) return GSS_S_CALL_INACCESSIBLE_READ;
+ if (input_token == GSS_C_NO_BUFFER) {
+ return GSS_S_CALL_INACCESSIBLE_READ;
+ }
+ if (output_token == GSS_C_NO_BUFFER) {
+ return GSS_S_CALL_INACCESSIBLE_WRITE;
+ }
+
+ /* reset return values */
+ *minor_status = 0;
+ if (src_name) *src_name = GSS_C_NO_NAME;
+ if (mech_type) *mech_type = GSS_C_NO_OID;
+ if (ret_flags) *ret_flags = 0;
+ if (time_rec) *time_rec = 0;
+ if (delegated_cred_handle) *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
+
+ if (*context_handle == GSS_C_NO_CONTEXT) {
+
+ /* first call */
+ ctx = calloc(1, sizeof(struct gssntlm_ctx));
+ if (!ctx) {
+ retmin = ENOMEM;
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+
+ /* FIXME: add call to determine if we are any other type of
+ * server, including setting up callbacks to perform validation
+ * against a remote DC */
+ ctx->role = GSSNTLM_SERVER;
+
+ if (acceptor_cred_handle) {
+ cred = (struct gssntlm_cred *)acceptor_cred_handle;
+ if (cred->type != GSSNTLM_CRED_SERVER) {
+ retmaj = GSS_S_DEFECTIVE_CREDENTIAL;
+ goto done;
+ }
+ if (cred->cred.server.name.type != GSSNTLM_NAME_SERVER) {
+ retmaj = GSS_S_DEFECTIVE_CREDENTIAL;
+ goto done;
+ }
+ /* FIXME: duplicate */
+ retmaj = gssntlm_duplicate_name(&retmin,
+ (const gss_name_t)&cred->cred.server.name,
+ (gss_name_t *)&server_name);
+ if (retmaj) goto done;
+ }
+
+ lm_compat_lvl = gssntlm_get_lm_compatibility_level();
+ sec_req = gssntlm_required_security(lm_compat_lvl, ctx->role);
+ if (sec_req == 0xff) {
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+
+ ctx->neg_flags = NTLMSSP_DEFAULT_ALLOWED_SERVER_FLAGS;
+ /* Fixme: How do we allow anonymous negotition ? */
+
+ if ((sec_req & SEC_LM_OK) || (sec_req & SEC_DC_LM_OK)) {
+ ctx->neg_flags |= NTLMSSP_REQUEST_NON_NT_SESSION_KEY;
+ ctx->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY;
+ }
+ if (sec_req & SEC_EXT_SEC_OK) {
+ ctx->neg_flags |= NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY;
+ }
+
+ retmin = ntlm_init_ctx(&ctx->ntlm);
+ if (retmin) {
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+
+ ctx->nego_msg.data = malloc(input_token->length);
+ if (!ctx->nego_msg.data) {
+ retmin = ENOMEM;
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+ memcpy(ctx->nego_msg.data, input_token->value, input_token->length);
+ ctx->nego_msg.length = input_token->length;
+
+ retmin = ntlm_decode_msg_type(ctx->ntlm, &ctx->nego_msg, &msg_type);
+ if (retmin || (msg_type != NEGOTIATE_MESSAGE)) {
+ retmaj = GSS_S_DEFECTIVE_TOKEN;
+ goto done;
+ }
+
+ retmin = ntlm_decode_neg_msg(ctx->ntlm, &ctx->nego_msg, &in_flags,
+ &domain, &workstation);
+ if (retmin) {
+ retmaj = GSS_S_DEFECTIVE_TOKEN;
+ goto done;
+ }
+
+ /* TODO: Support MS-NLMP ServerBlock ? */
+
+ /* leave only the crossing between requested and allowed flags */
+ ctx->neg_flags &= in_flags;
+
+ /* TODO: Check some minimum required flags ? */
+ /* TODO: Check MS-NLMP ServerRequire128bitEncryption */
+
+ if (ctx->neg_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+ /* Choose unicode in preferemce if both are set */
+ ctx->neg_flags &= ~NTLMSSP_NEGOTIATE_OEM;
+ } else if (!(ctx->neg_flags & NTLMSSP_NEGOTIATE_OEM)) {
+ /* no agreement */
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+
+ if (ctx->neg_flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) {
+ ctx->neg_flags &= ~NTLMSSP_REQUEST_NON_NT_SESSION_KEY;
+ ctx->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
+ }
+
+ /* TODO: support Domain type */
+ if (true) {
+ ctx->neg_flags |= NTLMSSP_TARGET_TYPE_SERVER;
+ ctx->neg_flags &= ~NTLMSSP_TARGET_TYPE_DOMAIN;
+ }
+
+ if (ctx->neg_flags & NTLMSSP_REQUEST_TARGET) {
+ ctx->neg_flags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
+ }
+
+ if (ctx->neg_flags & NTLMSSP_NEGOTIATE_SIGN) {
+ ctx->gss_flags |= GSS_C_INTEG_FLAG;
+ }
+ if (ctx->neg_flags & NTLMSSP_NEGOTIATE_SEAL) {
+ ctx->gss_flags |= GSS_C_CONF_FLAG;
+ }
+
+ /* Random server challenge */
+ challenge.data = ctx->server_chal;
+ challenge.length = 8;
+ retmin = RAND_BUFFER(&challenge);
+ if (retmin) {
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+
+ /* acquire our own name */
+ if (!server_name) {
+ tmpbuf.value = "";
+ tmpbuf.length = 0;
+ retmaj = gssntlm_import_name_by_mech(&retmin,
+ &gssntlm_oid,
+ &tmpbuf,
+ GSS_C_NT_HOSTBASED_SERVICE,
+ (gss_name_t *)&server_name);
+ if (retmaj) goto done;
+ }
+
+ computer_name = strdup(server_name->data.server.name);
+ if (!computer_name) {
+ retmin = ENOMEM;
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+ if ((p = strchr(server_name->data.server.name, '.')) != NULL) {
+ /* we want only the non qualified computer name for now */
+ *p = '\0';
+ }
+
+ timestamp = ntlm_timestamp_now();
+
+ retmin = ntlm_encode_target_info(ctx->ntlm, computer_name, NULL,
+ NULL, NULL, NULL,
+ NULL, &timestamp,
+ NULL, computer_name, NULL,
+ &target_info);
+ if (retmin) {
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+
+ retmin = ntlm_encode_chal_msg(ctx->ntlm, ctx->neg_flags,
+ computer_name, &challenge,
+ &target_info, &ctx->chal_msg);
+ if (retmin) {
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+
+ ctx->stage = NTLMSSP_STAGE_CHALLENGE;
+
+ output_token->value = malloc(ctx->chal_msg.length);
+ if (!output_token->value) {
+ retmin = ENOMEM;
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+ memcpy(output_token->value, ctx->chal_msg.data, ctx->chal_msg.length);
+ output_token->length = ctx->chal_msg.length;
+
+ retmaj = GSS_S_CONTINUE_NEEDED;
+
+ } else {
+ ctx = (struct gssntlm_ctx *)(*context_handle);
+
+ if (ctx->role != GSSNTLM_SERVER) {
+ retmaj = GSS_S_NO_CONTEXT;
+ goto done;
+ }
+
+ ctx->auth_msg.data = malloc(input_token->length);
+ if (!ctx->auth_msg.data) {
+ retmin = ENOMEM;
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+ memcpy(ctx->auth_msg.data, input_token->value, input_token->length);
+ ctx->auth_msg.length = input_token->length;
+
+ retmin = ntlm_decode_msg_type(ctx->ntlm, &ctx->auth_msg, &msg_type);
+ if (retmin) {
+ retmaj = GSS_S_DEFECTIVE_TOKEN;
+ goto done;
+ }
+
+ if (msg_type != AUTHENTICATE_MESSAGE ||
+ ctx->stage != NTLMSSP_STAGE_CHALLENGE) {
+ retmaj = GSS_S_NO_CONTEXT;
+ goto done;
+ }
+
+ retmin = ntlm_decode_auth_msg(ctx->ntlm, &ctx->auth_msg,
+ ctx->neg_flags,
+ &lm_chal_resp, &nt_chal_resp,
+ &dom_name, &usr_name, &wks_name,
+ &enc_sess_key, &mic);
+ if (retmin) {
+ retmaj = GSS_S_DEFECTIVE_TOKEN;
+ goto done;
+ }
+
+ lm_compat_lvl = gssntlm_get_lm_compatibility_level();
+ sec_req = gssntlm_required_security(lm_compat_lvl, ctx->role);
+ if (sec_req == 0xff) {
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+
+ if (((usr_name == NULL) || (usr_name[0] == '\0')) &&
+ (nt_chal_resp.length == 0) &&
+ (((lm_chal_resp.length == 1) && (lm_chal_resp.data[0] == '\0')) ||
+ (lm_chal_resp.length == 0))) {
+ /* Anonymous auth */
+ /* FIXME: not supported for now */
+ retmin = EINVAL;
+ retmaj = GSS_S_FAILURE;
+
+ } else if (sec_req & SEC_V2_ONLY) {
+
+ /* ### NTLMv2 ### */
+ struct ntlm_key ntlmv2_key = { .length = 16 };
+ struct ntlm_buffer nt_proof = { 0 };
+ char useratdom[1024];
+ size_t ulen, dlen, uadlen;
+ gss_buffer_desc usrname;
+
+ ulen = strlen(usr_name);
+ dlen = strlen(dom_name);
+ if (ulen + dlen + 2 > 1024) {
+ retmin = EINVAL;
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+ strncpy(useratdom, usr_name, ulen);
+ uadlen = ulen;
+ if (dlen) {
+ useratdom[uadlen] = '@';
+ uadlen++;
+ strncpy(&useratdom[uadlen], dom_name, dlen);
+ uadlen += dlen;
+ }
+ useratdom[uadlen] = '\0';
+
+ usrname.value = useratdom;
+ usrname.length = uadlen;
+ retmaj = gssntlm_import_name(&retmin, &usrname,
+ GSS_C_NT_USER_NAME,
+ (gss_name_t *)&gss_usrname);
+ if (retmaj) goto done;
+
+ retmaj = gssntlm_acquire_cred(&retmin,
+ (gss_name_t)gss_usrname,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET,
+ GSS_C_INITIATE,
+ (gss_cred_id_t *)&usr_cred,
+ NULL, NULL);
+ if (retmaj) goto done;
+
+ /* NTLMv2 Key */
+ retmin = NTOWFv2(ctx->ntlm, &usr_cred->cred.user.nt_hash,
+ usr_cred->cred.user.user.data.user.name,
+ usr_cred->cred.user.user.data.user.domain,
+ &ntlmv2_key);
+ if (retmin) {
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+
+ /* NTLMv2 Response */
+ retmin = ntlmv2_verify_nt_response(&nt_chal_resp, &ntlmv2_key,
+ ctx->server_chal);
+ if (retmin) {
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+
+ /* FIXME: retries using NULL as domain name in case of failure */
+
+ /* LMv2 Response */
+ retmin = ntlmv2_verify_lm_response(&lm_chal_resp, &ntlmv2_key,
+ ctx->server_chal);
+ if (retmin) {
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+
+ /* The NT proof is the first 16 bytes */
+ nt_proof.data = nt_chal_resp.data;
+ nt_proof.length = 16;
+
+ /* The Session Base Key */
+ /* In NTLMv2 the Key Exchange Key is the Session Base Key */
+ retmin = ntlmv2_session_base_key(&ntlmv2_key, &nt_proof,
+ &key_exchange_key);
+ if (retmin) {
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+
+ /* FIXME: Verify MIC if client sent it */
+
+ } else {
+ /* ### NTLMv1 ### */
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+
+ if (ctx->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
+ memcpy(encrypted_random_session_key.data, enc_sess_key.data, 16);
+ ctx->exported_session_key.length = 16;
+
+ retmin = ntlm_encrypted_session_key(&key_exchange_key,
+ &encrypted_random_session_key,
+ &ctx->exported_session_key);
+ if (retmin) {
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+ } else {
+ ctx->exported_session_key = key_exchange_key;
+ }
+
+ if (ctx->neg_flags & (NTLMSSP_NEGOTIATE_SIGN |
+ NTLMSSP_NEGOTIATE_SEAL)) {
+ retmin = ntlm_signseal_keys(ctx->neg_flags, false,
+ &ctx->exported_session_key,
+ &ctx->send.sign_key,
+ &ctx->recv.sign_key,
+ &ctx->send.seal_key,
+ &ctx->recv.seal_key,
+ &ctx->send.seal_handle,
+ &ctx->recv.seal_handle);
+ if (retmin) {
+ retmaj = GSS_S_FAILURE;
+ goto done;
+ }
+ }
+
+ ctx->stage = NTLMSSP_STAGE_DONE;
+ ctx->expiration_time = time(NULL) + MAX_CHALRESP_LIFETIME;
+ ctx->established = true;
+ retmaj = GSS_S_COMPLETE;
+ }
+
+done:
+
+ if ((retmaj != GSS_S_COMPLETE) &&
+ (retmaj != GSS_S_CONTINUE_NEEDED)) {
+ gssntlm_delete_sec_context(&tmpmin, (gss_ctx_id_t *)&ctx, NULL);
+ *minor_status = retmin;
+ }
+ *context_handle = (gss_ctx_id_t)ctx;
+ gssntlm_release_name(&tmpmin, (gss_name_t *)&server_name);
+ safefree(computer_name);
+ safefree(workstation);
+ safefree(domain);
+ ntlm_free_buffer_data(&target_info);
+ return retmaj;
+}
diff --git a/src/gss_spi.c b/src/gss_spi.c
index 44db903..557e23c 100644
--- a/src/gss_spi.c
+++ b/src/gss_spi.c
@@ -149,3 +149,28 @@ OM_uint32 gss_context_time(OM_uint32 *minor_status,
return gssntlm_context_time(minor_status, context_handle, time_rec);
}
+OM_uint32 gss_accept_sec_context(OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_cred_id_t acceptor_cred_handle,
+ gss_buffer_t input_token_buffer,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_name_t *src_name,
+ gss_OID *mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec,
+ gss_cred_id_t *delegated_cred_handle)
+{
+ return gssntlm_accept_sec_context(minor_status,
+ context_handle,
+ acceptor_cred_handle,
+ input_token_buffer,
+ input_chan_bindings,
+ src_name,
+ mech_type,
+ output_token,
+ ret_flags,
+ time_rec,
+ delegated_cred_handle);
+}
+
diff --git a/src/ntlm.c b/src/ntlm.c
index f42f105..3e00ceb 100644
--- a/src/ntlm.c
+++ b/src/ntlm.c
@@ -1302,22 +1302,28 @@ int ntlm_decode_auth_msg(struct ntlm_ctx *ctx,
payload_offs += sizeof(struct wire_version);
}
- /* this must be second as it pushes the payload further down */
+ /* Unconditionally copy 16 bytes for the MIC, if it was really
+ * added by the client it will be flagged in the AV_PAIR contained
+ * in the NT Response, that will be fully decoded later by the caller
+ * and the MIC checked otherwise these 16 bytes will just be ignored */
if (mic) {
if (mic->length < 16) return ERR_DECODE;
/* mic is at payload_offs right now */
if (buffer->length - payload_offs < 16) return ERR_DECODE;
memcpy(mic->data, &buffer->data[payload_offs], 16);
- payload_offs += 16;
+ /* NOTE: we do not push down the payload because we do not know that
+ * the MIC is actually present yet for real */
}
if (msg->lm_chalresp.len != 0 && lm_chalresp) {
ret = ntlm_decode_field(&msg->lm_chalresp, buffer,
payload_offs, lm_chalresp);
+ if (ret) goto done;
}
if (msg->nt_chalresp.len != 0 && nt_chalresp) {
ret = ntlm_decode_field(&msg->nt_chalresp, buffer,
payload_offs, nt_chalresp);
+ if (ret) goto done;
}
if (msg->domain_name.len != 0 && domain_name) {
if (flags & NTLMSSP_NEGOTIATE_UNICODE) {
diff --git a/src/ntlm.h b/src/ntlm.h
index 2f1429a..748b58d 100644
--- a/src/ntlm.h
+++ b/src/ntlm.h
@@ -268,7 +268,7 @@ int ntlm_exported_session_key(struct ntlm_key *key_exchange_key,
struct ntlm_key *exported_session_key);
/**
- * @brief Comutes the NTLM encrypted session key
+ * @brief Encrypts or Decrypts the NTLM session key using RC4K
*
* @param key_exchange_key[16] The Key Exchange Key
* @param exported_session_key[16] Resulting exported session key
@@ -276,9 +276,8 @@ int ntlm_exported_session_key(struct ntlm_key *key_exchange_key,
*
* @return 0 on success or error.
*/
-int ntlm_encrypted_session_key(struct ntlm_key *key_exchange_key,
- struct ntlm_key *exported_session_key,
- struct ntlm_key *encrypted_random_session_key);
+int ntlm_encrypted_session_key(struct ntlm_key *key,
+ struct ntlm_key *in, struct ntlm_key *out);
/**
* @brief Computes all the sign and seal keys from the session key
@@ -304,6 +303,31 @@ int ntlm_signseal_keys(uint32_t flags, bool client,
struct ntlm_rc4_handle **seal_send_handle,
struct ntlm_rc4_handle **seal_recv_handle);
+/**
+ * @brief Verifies a 16 bit NT Response
+ *
+ * @param nt_response The NT Response buffer including client challenge
+ * @param ntlmv2_key The NTLMv2 key
+ * @param server_chal[8] The server challenge used to compute the response
+ *
+ * @return 0 on success, or an error
+ */
+int ntlmv2_verify_nt_response(struct ntlm_buffer *nt_response,
+ struct ntlm_key *ntlmv2_key,
+ uint8_t server_chal[8]);
+
+/**
+ * @brief Verifies a 16 bit LM Response
+ *
+ * @param nt_response The LM Response buffer including client challenge
+ * @param ntlmv2_key The NTLMv2 key
+ * @param server_chal[8] The server challenge used to compute the response
+ *
+ * @return 0 on success, or an error
+ */
+int ntlmv2_verify_lm_response(struct ntlm_buffer *nt_response,
+ struct ntlm_key *ntlmv2_key,
+ uint8_t server_chal[8]);
/* ############## ENCODING / DECODING ############## */
diff --git a/src/ntlm_crypto.c b/src/ntlm_crypto.c
index d999d0f..1993148 100644
--- a/src/ntlm_crypto.c
+++ b/src/ntlm_crypto.c
@@ -409,18 +409,14 @@ int ntlm_exported_session_key(struct ntlm_key *key_exchange_key,
return RAND_BUFFER(&nonce);
}
-int ntlm_encrypted_session_key(struct ntlm_key *key_exchange_key,
- struct ntlm_key *exported_session_key,
- struct ntlm_key *encrypted_random_session_key)
+int ntlm_encrypted_session_key(struct ntlm_key *key,
+ struct ntlm_key *in, struct ntlm_key *out)
{
- struct ntlm_buffer key = { key_exchange_key->data,
- key_exchange_key->length };
- struct ntlm_buffer nonce = { exported_session_key->data,
- exported_session_key->length };
- struct ntlm_buffer cipher = { encrypted_random_session_key->data,
- encrypted_random_session_key->length };
-
- return RC4K(&key, NTLM_CIPHER_ENCRYPT, &nonce, &cipher);
+ struct ntlm_buffer _key = { key->data, key->length };
+ struct ntlm_buffer data = { in->data, in->length };
+ struct ntlm_buffer result = { out->data, out->length };
+
+ return RC4K(&_key, NTLM_CIPHER_ENCRYPT, &data, &result);
}
static int ntlm_key_derivation_function(struct ntlm_key *key,
@@ -557,3 +553,69 @@ int ntlm_signseal_keys(uint32_t flags, bool client,
return 0;
}
+
+int ntlmv2_verify_nt_response(struct ntlm_buffer *nt_response,
+ struct ntlm_key *ntlmv2_key,
+ uint8_t server_chal[8])
+{
+ union wire_ntlm_response *nt_resp = NULL;
+ struct ntlm_buffer key = { ntlmv2_key->data, ntlmv2_key->length };
+ uint8_t proof[16];
+ struct ntlm_buffer nt_proof = { proof, 16 };
+ struct ntlm_buffer payload;
+ int ret;
+
+ if (nt_response->length < 24) return EINVAL;
+
+ nt_resp = (union wire_ntlm_response *)nt_response->data;
+
+ payload.length = 8;
+ payload.data = server_chal;
+
+ payload.length = nt_response->length - 8;
+ payload.data = malloc(payload.length);
+ if (!payload.data) return ENOMEM;
+ memcpy(payload.data, server_chal, 8);
+ memcpy(&payload.data[8], nt_resp->v2.cli_chal, payload.length - 8);
+
+ ret = HMAC_MD5(&key, &payload, &nt_proof);
+
+ if (ret) goto done;
+
+ ret = EINVAL;
+ if (memcmp(nt_resp->v2.resp, proof, 16) == 0) {
+ ret = 0;
+ }
+
+done:
+ safefree(payload.data);
+ return ret;
+}
+
+int ntlmv2_verify_lm_response(struct ntlm_buffer *lm_response,
+ struct ntlm_key *ntlmv2_key,
+ uint8_t server_chal[8])
+{
+ struct ntlm_buffer key = { ntlmv2_key->data, ntlmv2_key->length };
+ union wire_lm_response *lm_resp = NULL;
+ uint8_t payload_buf[16];
+ struct ntlm_buffer payload = { payload_buf, 16 };
+ uint8_t proof[16];
+ struct ntlm_buffer lm_proof = { proof, 16 };
+ int ret;
+
+ if (lm_response->length != 24) return EINVAL;
+
+ /* now caluclate the LM Proof */
+ lm_resp = (union wire_lm_response *)lm_response->data;
+
+ memcpy(payload.data, server_chal, 8);
+ memcpy(&payload.data[8], lm_resp->v2.cli_chal, 8);
+ ret = HMAC_MD5(&key, &payload, &lm_proof);
+
+ if (ret) return ret;
+
+ if (memcmp(lm_resp->v2.resp, proof, 16) == 0) return 0;
+
+ return EINVAL;
+}