summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2013-08-18 01:04:30 -0400
committerSimo Sorce <simo@redhat.com>2013-08-19 00:09:56 -0400
commit885d7e6bb8a08b739fd3a5eac528445a2524500e (patch)
treedc06495a8b959578af76b700eced88b3495808cd
parentdef4d4a35a007e1c442006e6f9744fa7a8e1da69 (diff)
downloadgss-ntlmssp-885d7e6bb8a08b739fd3a5eac528445a2524500e.tar.gz
gss-ntlmssp-885d7e6bb8a08b739fd3a5eac528445a2524500e.tar.xz
gss-ntlmssp-885d7e6bb8a08b739fd3a5eac528445a2524500e.zip
Add integrity and confidentiality functions
-rw-r--r--Makefile.am1
-rw-r--r--src/crypto.c26
-rw-r--r--src/crypto.h13
-rw-r--r--src/gss_ntlmssp.c1
-rw-r--r--src/gss_ntlmssp.h26
-rw-r--r--src/gss_signseal.c246
-rw-r--r--src/gss_spi.c58
-rw-r--r--src/ntlm.h54
-rw-r--r--src/ntlm_common.h5
-rw-r--r--src/ntlm_crypto.c125
10 files changed, 548 insertions, 7 deletions
diff --git a/Makefile.am b/Makefile.am
index 268c219..728a85f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -76,6 +76,7 @@ GN_MECHGLUE_OBJ = \
src/gss_names.c \
src/gss_creds.c \
src/gss_sec_ctx.c \
+ src/gss_signseal.c \
src/gss_ntlmssp.c
dist_noinst_HEADERS = \
diff --git a/src/crypto.c b/src/crypto.c
index ab1efbf..dd33b34 100644
--- a/src/crypto.c
+++ b/src/crypto.c
@@ -36,12 +36,13 @@ int RAND_BUFFER(struct ntlm_buffer *random)
return 0;
}
-int HMAC_MD5(struct ntlm_buffer *key,
- struct ntlm_buffer *payload,
- struct ntlm_buffer *result)
+int HMAC_MD5_IOV(struct ntlm_buffer *key,
+ struct ntlm_iov *iov,
+ struct ntlm_buffer *result)
{
HMAC_CTX hmac_ctx;
unsigned int len;
+ size_t i;
int ret = 0;
if (result->length != 16) return EINVAL;
@@ -54,10 +55,12 @@ int HMAC_MD5(struct ntlm_buffer *key,
goto done;
}
- ret = HMAC_Update(&hmac_ctx, payload->data, payload->length);
- if (ret == 0) {
- ret = ERR_CRYPTO;
- goto done;
+ for (i = 0; i < iov->num; i++) {
+ ret = HMAC_Update(&hmac_ctx, iov->data[i]->data, iov->data[i]->length);
+ if (ret == 0) {
+ ret = ERR_CRYPTO;
+ goto done;
+ }
}
ret = HMAC_Final(&hmac_ctx, result->data, &len);
@@ -73,7 +76,16 @@ done:
return ret;
}
+int HMAC_MD5(struct ntlm_buffer *key,
+ struct ntlm_buffer *payload,
+ struct ntlm_buffer *result)
+{
+ struct ntlm_iov iov;
+ iov.num = 1;
+ iov.data = &payload;
+ return HMAC_MD5_IOV(key, &iov, result);
+}
static int mdx_hash(const EVP_MD *type,
struct ntlm_buffer *payload,
diff --git a/src/crypto.h b/src/crypto.h
index efce1fc..9f2448a 100644
--- a/src/crypto.h
+++ b/src/crypto.h
@@ -45,6 +45,19 @@ int HMAC_MD5(struct ntlm_buffer *key,
struct ntlm_buffer *result);
/**
+ * @brief HMAC-MD5 function that operats on multiple buffers
+ *
+ * @param key The authentication key
+ * @param iov The IOVec of the payloads to authenticate
+ * @param result A preallocated 16 byte buffer
+ *
+ * @return 0 on success or ERR_CRYPTO
+ */
+int HMAC_MD5_IOV(struct ntlm_buffer *key,
+ struct ntlm_iov *iov,
+ struct ntlm_buffer *result);
+
+/**
* @brief MD4 Hash Function
*
* @param payload The payoad to hash
diff --git a/src/gss_ntlmssp.c b/src/gss_ntlmssp.c
index 36a623e..6dba56b 100644
--- a/src/gss_ntlmssp.c
+++ b/src/gss_ntlmssp.c
@@ -72,6 +72,7 @@ uint32_t gssntlm_context_is_valid(struct gssntlm_ctx *ctx, time_t *time_now)
{
time_t now;
+ if (!ctx) return GSS_S_NO_CONTEXT;
if (!ctx->established) return GSS_S_NO_CONTEXT;
now = time(NULL);
diff --git a/src/gss_ntlmssp.h b/src/gss_ntlmssp.h
index 670f182..308ecfd 100644
--- a/src/gss_ntlmssp.h
+++ b/src/gss_ntlmssp.h
@@ -243,4 +243,30 @@ uint32_t gssntlm_accept_sec_context(uint32_t *minor_status,
uint32_t *time_rec,
gss_cred_id_t *delegated_cred_handle);
+uint32_t gssntlm_get_mic(uint32_t *minor_status,
+ gss_ctx_id_t context_handle,
+ gss_qop_t qop_req,
+ gss_buffer_t message_buffer,
+ gss_buffer_t message_token);
+
+uint32_t gssntlm_verify_mic(uint32_t *minor_status,
+ gss_ctx_id_t context_handle,
+ gss_buffer_t message_buffer,
+ gss_buffer_t message_token,
+ gss_qop_t *qop_state);
+
+uint32_t gssntlm_wrap(uint32_t *minor_status,
+ gss_ctx_id_t context_handle,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ gss_buffer_t input_message_buffer,
+ int *conf_state,
+ gss_buffer_t output_message_buffer);
+
+uint32_t gssntlm_unwrap(uint32_t *minor_status,
+ gss_ctx_id_t context_handle,
+ gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,
+ int *conf_state,
+ gss_qop_t *qop_state);
#endif /* _GSS_NTLMSSP_H_ */
diff --git a/src/gss_signseal.c b/src/gss_signseal.c
new file mode 100644
index 0000000..5828c2a
--- /dev/null
+++ b/src/gss_signseal.c
@@ -0,0 +1,246 @@
+/*
+ Copyright (C) 2013 Simo Sorce <simo@samba.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
+
+#include "gss_ntlmssp.h"
+
+uint32_t gssntlm_get_mic(uint32_t *minor_status,
+ gss_ctx_id_t context_handle,
+ gss_qop_t qop_req,
+ gss_buffer_t message_buffer,
+ gss_buffer_t message_token)
+{
+ struct gssntlm_ctx *ctx;
+ struct ntlm_buffer message;
+ struct ntlm_buffer signature;
+ uint32_t retmaj, retmin;
+
+ *minor_status = 0;
+
+ ctx = (struct gssntlm_ctx *)context_handle;
+ retmaj = gssntlm_context_is_valid(ctx, NULL);
+ if (retmaj != GSS_S_COMPLETE) {
+ return retmaj;
+ }
+ if (qop_req != GSS_C_QOP_DEFAULT) {
+ return GSS_S_BAD_QOP;
+ }
+ if (!message_buffer->value || message_buffer->length == 0) {
+ return GSS_S_CALL_INACCESSIBLE_READ;
+ }
+
+ message_token->value = malloc(16);
+ if (!message_token->value) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ message_token->length = 16;
+
+ message.data = message_buffer->value;
+ message.length = message_buffer->length;
+ signature.data = message_token->value;
+ signature.length = message_token->length;
+ retmin = ntlm_sign(&ctx->send.sign_key, ctx->send.seq_num,
+ ctx->send.seal_handle, ctx->neg_flags,
+ &message, &signature);
+ if (retmin) {
+ *minor_status = retmin;
+ safefree(message_token->value);
+ return GSS_S_FAILURE;
+ }
+
+ /* increment seq_num upon succesful signature */
+ ctx->send.seq_num++;
+
+ return GSS_S_COMPLETE;
+}
+
+uint32_t gssntlm_verify_mic(uint32_t *minor_status,
+ gss_ctx_id_t context_handle,
+ gss_buffer_t message_buffer,
+ gss_buffer_t message_token,
+ gss_qop_t *qop_state)
+{
+ struct gssntlm_ctx *ctx;
+ struct ntlm_buffer message;
+ uint8_t token[16];
+ struct ntlm_buffer signature = { token, 16 };
+ uint32_t retmaj, retmin;
+
+ *minor_status = 0;
+
+ ctx = (struct gssntlm_ctx *)context_handle;
+ retmaj = gssntlm_context_is_valid(ctx, NULL);
+ if (retmaj != GSS_S_COMPLETE) {
+ return retmaj;
+ }
+ if (!message_buffer->value || message_buffer->length == 0) {
+ return GSS_S_CALL_INACCESSIBLE_READ;
+ }
+ if (qop_state) {
+ *qop_state = GSS_C_QOP_DEFAULT;
+ }
+
+ message.data = message_buffer->value;
+ message.length = message_buffer->length;
+ retmin = ntlm_sign(&ctx->recv.sign_key, ctx->recv.seq_num,
+ ctx->recv.seal_handle, ctx->neg_flags,
+ &message, &signature);
+ if (retmin) {
+ *minor_status = retmin;
+ return GSS_S_FAILURE;
+ }
+
+ if (memcmp(signature.data, message_token->value, 16) != 0) {
+ return GSS_S_BAD_SIG;
+ }
+
+ /* increment seq_num upon succesful signature */
+ ctx->recv.seq_num++;
+
+ return GSS_S_COMPLETE;
+}
+
+uint32_t gssntlm_wrap(uint32_t *minor_status,
+ gss_ctx_id_t context_handle,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ gss_buffer_t input_message_buffer,
+ int *conf_state,
+ gss_buffer_t output_message_buffer)
+{
+ struct gssntlm_ctx *ctx;
+ struct ntlm_buffer message;
+ struct ntlm_buffer output;
+ struct ntlm_buffer signature;
+ uint32_t retmaj, retmin;
+
+ *minor_status = 0;
+
+ ctx = (struct gssntlm_ctx *)context_handle;
+ retmaj = gssntlm_context_is_valid(ctx, NULL);
+ if (retmaj != GSS_S_COMPLETE) {
+ return retmaj;
+ }
+ if (qop_req != GSS_C_QOP_DEFAULT) {
+ return GSS_S_BAD_QOP;
+ }
+ if (!input_message_buffer->value || input_message_buffer->length == 0) {
+ return GSS_S_CALL_INACCESSIBLE_READ;
+ }
+ if (conf_state) {
+ *conf_state = 0;
+ }
+
+ if (conf_req_flag == 0) {
+ /* ignore, always seal */
+ }
+
+ output_message_buffer->value = malloc(input_message_buffer->length + 16);
+ if (!output_message_buffer->value) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ output_message_buffer->length = input_message_buffer->length + 16;
+
+ message.data = input_message_buffer->value;
+ message.length = input_message_buffer->length;
+ output.data = output_message_buffer->value;
+ output.length = input_message_buffer->length;
+ signature.data = &output.data[input_message_buffer->length];
+ signature.length = 16;
+ retmin = ntlm_seal(ctx->send.seal_handle, ctx->neg_flags,
+ &ctx->send.sign_key, ctx->send.seq_num,
+ &message, &output, &signature);
+ if (retmin) {
+ *minor_status = retmin;
+ safefree(output_message_buffer->value);
+ return GSS_S_FAILURE;
+ }
+
+ /* increment seq_num upon succesful signature */
+ ctx->send.seq_num++;
+ return GSS_S_COMPLETE;
+}
+
+uint32_t gssntlm_unwrap(uint32_t *minor_status,
+ gss_ctx_id_t context_handle,
+ gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,
+ int *conf_state,
+ gss_qop_t *qop_state)
+{
+ struct gssntlm_ctx *ctx;
+ struct ntlm_buffer message;
+ struct ntlm_buffer output;
+ uint8_t sig[16];
+ struct ntlm_buffer signature = { sig, 16 };
+ uint32_t retmaj, retmin;
+
+ *minor_status = 0;
+
+ ctx = (struct gssntlm_ctx *)context_handle;
+ retmaj = gssntlm_context_is_valid(ctx, NULL);
+ if (retmaj != GSS_S_COMPLETE) {
+ return retmaj;
+ }
+ if (!input_message_buffer->value || input_message_buffer->length == 0) {
+ return GSS_S_CALL_INACCESSIBLE_READ;
+ }
+ if (conf_state) {
+ *conf_state = 0;
+ }
+ if (qop_state) {
+ *qop_state = GSS_C_QOP_DEFAULT;
+ }
+
+ output_message_buffer->value = malloc(input_message_buffer->length - 16);
+ if (!output_message_buffer->value) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ output_message_buffer->length = input_message_buffer->length - 16;
+
+ message.data = input_message_buffer->value;
+ message.length = input_message_buffer->length;
+ output.data = output_message_buffer->value;
+ output.length = output_message_buffer->length;
+ retmin = ntlm_unseal(ctx->recv.seal_handle, ctx->neg_flags,
+ &ctx->recv.sign_key, ctx->recv.seq_num,
+ &message, &output, &signature);
+ if (retmin) {
+ *minor_status = retmin;
+ safefree(output_message_buffer->value);
+ return GSS_S_FAILURE;
+ }
+
+ if (memcmp(&message.data[output.length], signature.data, 16) != 0) {
+ safefree(output_message_buffer->value);
+ return GSS_S_BAD_SIG;
+ }
+
+ /* increment seq_num upon succesful signature */
+ ctx->send.seq_num++;
+ return GSS_S_COMPLETE;
+}
diff --git a/src/gss_spi.c b/src/gss_spi.c
index 557e23c..8139d3c 100644
--- a/src/gss_spi.c
+++ b/src/gss_spi.c
@@ -174,3 +174,61 @@ OM_uint32 gss_accept_sec_context(OM_uint32 *minor_status,
delegated_cred_handle);
}
+OM_uint32 gss_get_mic(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ gss_qop_t qop_req,
+ gss_buffer_t message_buffer,
+ gss_buffer_t message_token)
+{
+ return gssntlm_get_mic(minor_status,
+ context_handle,
+ qop_req,
+ message_buffer,
+ message_token);
+}
+
+
+OM_uint32 gss_verify_mic(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ gss_buffer_t message_buffer,
+ gss_buffer_t message_token,
+ gss_qop_t *qop_state)
+{
+ return gssntlm_verify_mic(minor_status,
+ context_handle,
+ message_buffer,
+ message_token,
+ qop_state);
+}
+
+OM_uint32 gss_wrap(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ gss_buffer_t input_message_buffer,
+ int *conf_state,
+ gss_buffer_t output_message_buffer)
+{
+ return gssntlm_wrap(minor_status,
+ context_handle,
+ conf_req_flag,
+ qop_req,
+ input_message_buffer,
+ conf_state,
+ output_message_buffer);
+}
+
+OM_uint32 gss_unwrap(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,
+ int *conf_state,
+ gss_qop_t *qop_state)
+{
+ return gssntlm_unwrap(minor_status,
+ context_handle,
+ input_message_buffer,
+ output_message_buffer,
+ conf_state,
+ qop_state);
+}
diff --git a/src/ntlm.h b/src/ntlm.h
index 748b58d..bfbfb47 100644
--- a/src/ntlm.h
+++ b/src/ntlm.h
@@ -69,6 +69,8 @@
#define NTLMSSP_VERSION_BUILD 0
#define NTLMSSP_VERSION_REV NTLMSSP_REVISION_W2K3
+#define NTLMSSP_MESSAGE_SIGNATURE_VERSION 0x00000001
+
#define NEGOTIATE_MESSAGE 0x00000001
#define CHALLENGE_MESSAGE 0x00000002
#define AUTHENTICATE_MESSAGE 0x00000003
@@ -329,6 +331,58 @@ int ntlmv2_verify_lm_response(struct ntlm_buffer *nt_response,
struct ntlm_key *ntlmv2_key,
uint8_t server_chal[8]);
+/**
+ * @brief Create NTLM signature for the provided message
+ *
+ * @param sign_key Signing key
+ * @param seq_num Sequence number
+ * @param handle Encryption handle
+ * @param flags Negotiated flags
+ * @param message Message buffer
+ * @param signature Preallocated byffer of 16 bytes for signature
+ *
+ * @return 0 on success, or an error
+ */
+int ntlm_sign(struct ntlm_key *sign_key, uint32_t seq_num,
+ struct ntlm_rc4_handle *handle, uint32_t flags,
+ struct ntlm_buffer *message, struct ntlm_buffer *signature);
+
+/**
+ * @brief NTLM seal the provided message
+ *
+ * @param handle Encryption handle
+ * @param flags Negotiated flags
+ * @param sign_key Signing key
+ * @param seq_num Sequence number
+ * @param message Message buffer
+ * @param output Output buffer
+ * @param signature Signature
+ *
+ * @return 0 on success, or an error
+ */
+int ntlm_seal(struct ntlm_rc4_handle *handle, uint32_t flags,
+ struct ntlm_key *sign_key, uint32_t seq_num,
+ struct ntlm_buffer *message, struct ntlm_buffer *output,
+ struct ntlm_buffer *signature);
+
+/**
+ * @brief NTLM unseal the provided message
+ *
+ * @param handle Encryption handle
+ * @param flags Negotiated flags
+ * @param sign_key Signing key
+ * @param seq_num Sequence number
+ * @param message Message buffer
+ * @param output Output buffer
+ * @param signature Signature
+ *
+ * @return 0 on success, or an error
+ */
+int ntlm_unseal(struct ntlm_rc4_handle *handle, uint32_t flags,
+ struct ntlm_key *sign_key, uint32_t seq_num,
+ struct ntlm_buffer *message, struct ntlm_buffer *output,
+ struct ntlm_buffer *signature);
+
/* ############## ENCODING / DECODING ############## */
/**
diff --git a/src/ntlm_common.h b/src/ntlm_common.h
index c70fe8d..264afe1 100644
--- a/src/ntlm_common.h
+++ b/src/ntlm_common.h
@@ -44,6 +44,11 @@ struct ntlm_buffer {
size_t length;
};
+struct ntlm_iov {
+ struct ntlm_buffer **data;
+ size_t num;
+};
+
struct ntlm_rc4_handle;
enum ntlm_cipher_mode {
diff --git a/src/ntlm_crypto.c b/src/ntlm_crypto.c
index 1993148..ba5c5b0 100644
--- a/src/ntlm_crypto.c
+++ b/src/ntlm_crypto.c
@@ -619,3 +619,128 @@ int ntlmv2_verify_lm_response(struct ntlm_buffer *lm_response,
return EINVAL;
}
+
+static int ntlmv2_sign(struct ntlm_key *sign_key, uint32_t seq_num,
+ struct ntlm_rc4_handle *handle, bool keyex,
+ struct ntlm_buffer *message,
+ struct ntlm_buffer *signature)
+{
+ uint32_t ver;
+ struct ntlm_buffer key = { sign_key->data, sign_key->length };
+ uint32_t le_seq;
+ uint8_t le8seq[8];
+ struct ntlm_buffer seq = { le8seq, 4 };
+ struct ntlm_buffer *data[2];
+ struct ntlm_iov iov;
+ uint8_t hmac_sig[16];
+ struct ntlm_buffer hmac = { hmac_sig, 16 };
+ struct ntlm_buffer rc4buf;
+ struct ntlm_buffer rc4res;
+ int ret;
+
+ if (signature->length != 16) {
+ return EINVAL;
+ }
+
+ le_seq = htole32(seq_num);
+ memcpy(seq.data, &le_seq, 4);
+ data[0] = &seq;
+ data[1] = message;
+ iov.data = data;
+ iov.num = 2;
+
+ ret = HMAC_MD5_IOV(&key, &iov, &hmac);
+ if (ret) return ret;
+
+ /* put version */
+ ver = htole32(NTLMSSP_MESSAGE_SIGNATURE_VERSION);
+ memcpy(signature->data, &ver, 4);
+
+ /* put actual MAC */
+ if (keyex) {
+ /* encrypt truncated hmac */
+ rc4buf.data = hmac.data;
+ rc4buf.length = 8;
+ /* and put it in the middle of the output signature */
+ rc4res.data = &signature->data[4];
+ rc4res.length = 8;
+ ret = RC4_UPDATE(handle, &rc4buf, &rc4res);
+ if (ret) return ret;
+ } else {
+ memcpy(&signature->data[4], hmac.data, 8);
+ }
+
+ /* put used seq_num */
+ memcpy(&signature->data[12], seq.data, 4);
+
+ return 0;
+}
+
+int ntlm_sign(struct ntlm_key *sign_key, uint32_t seq_num,
+ struct ntlm_rc4_handle *handle, uint32_t flags,
+ struct ntlm_buffer *message, struct ntlm_buffer *signature)
+{
+ if ((flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY)
+ && (flags & NTLMSSP_NEGOTIATE_SIGN)) {
+ return ntlmv2_sign(sign_key, seq_num, handle,
+ (flags & NTLMSSP_NEGOTIATE_KEY_EXCH),
+ message, signature);
+ } else if (flags & NTLMSSP_NEGOTIATE_SIGN) {
+ /* FIXME: needs an implementation of CRC32 maybe use zlib ? */
+ } else if (flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) {
+ uint32_t le_seq = htole32(seq_num);
+ memcpy(signature->data, &le_seq, 4);
+ memset(&signature->data[4], 0, 12);
+ return 0;
+ }
+
+ return ENOTSUP;
+}
+
+int ntlm_seal(struct ntlm_rc4_handle *handle, uint32_t flags,
+ struct ntlm_key *sign_key, uint32_t seq_num,
+ struct ntlm_buffer *message, struct ntlm_buffer *output,
+ struct ntlm_buffer *signature)
+{
+ int ret;
+
+ if (!((flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY)
+ && (flags & NTLMSSP_NEGOTIATE_SEAL))) {
+ /* we only support v2 for now as we can't sign w/o session security
+ * anyway */
+ return ENOTSUP;
+ }
+
+ ret = RC4_UPDATE(handle, message, output);
+ if (ret) return ret;
+
+ return ntlmv2_sign(sign_key, seq_num, handle,
+ (flags & NTLMSSP_NEGOTIATE_KEY_EXCH),
+ message, signature);
+}
+
+int ntlm_unseal(struct ntlm_rc4_handle *handle, uint32_t flags,
+ struct ntlm_key *sign_key, uint32_t seq_num,
+ struct ntlm_buffer *message, struct ntlm_buffer *output,
+ struct ntlm_buffer *signature)
+{
+ struct ntlm_buffer msg_buffer;
+ int ret;
+
+ if (!((flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY)
+ && (flags & NTLMSSP_NEGOTIATE_SEAL))) {
+ /* we only support v2 for now as we can't sign w/o session security
+ * anyway */
+ return ENOTSUP;
+ }
+
+ msg_buffer = *message;
+ msg_buffer.length -= 16;
+
+ ret = RC4_UPDATE(handle, &msg_buffer, output);
+ if (ret) return ret;
+
+ return ntlmv2_sign(sign_key, seq_num, handle,
+ (flags & NTLMSSP_NEGOTIATE_KEY_EXCH),
+ output, signature);
+}