diff options
author | Simo Sorce <simo@redhat.com> | 2013-08-18 01:04:30 -0400 |
---|---|---|
committer | Simo Sorce <simo@redhat.com> | 2013-08-19 00:09:56 -0400 |
commit | 885d7e6bb8a08b739fd3a5eac528445a2524500e (patch) | |
tree | dc06495a8b959578af76b700eced88b3495808cd | |
parent | def4d4a35a007e1c442006e6f9744fa7a8e1da69 (diff) | |
download | gss-ntlmssp-885d7e6bb8a08b739fd3a5eac528445a2524500e.tar.gz gss-ntlmssp-885d7e6bb8a08b739fd3a5eac528445a2524500e.tar.xz gss-ntlmssp-885d7e6bb8a08b739fd3a5eac528445a2524500e.zip |
Add integrity and confidentiality functions
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | src/crypto.c | 26 | ||||
-rw-r--r-- | src/crypto.h | 13 | ||||
-rw-r--r-- | src/gss_ntlmssp.c | 1 | ||||
-rw-r--r-- | src/gss_ntlmssp.h | 26 | ||||
-rw-r--r-- | src/gss_signseal.c | 246 | ||||
-rw-r--r-- | src/gss_spi.c | 58 | ||||
-rw-r--r-- | src/ntlm.h | 54 | ||||
-rw-r--r-- | src/ntlm_common.h | 5 | ||||
-rw-r--r-- | src/ntlm_crypto.c | 125 |
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); +} @@ -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); +} |