Skip to content

Commit

Permalink
Remove support for NTLM v1 proxy authentication
Browse files Browse the repository at this point in the history
Due to the limitation of the protocol it is not
considered secure. Better to use basic auth instead
of a false sense of security. NTLM v2 remains
supported for now.

Change-Id: I0dcb2dac4136f194da7050a8ea8495e9faba9dd9
Signed-off-by: Frank Lichtenheld <[email protected]>
Acked-by: Arne Schwabe <[email protected]>
Message-Id: <[email protected]>
URL: https://www.mail-archive.com/[email protected]/msg27862.html
Signed-off-by: Gert Doering <[email protected]>
  • Loading branch information
flichtenheld authored and cron2 committed Dec 30, 2023
1 parent 3075163 commit 21910eb
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 221 deletions.
6 changes: 6 additions & 0 deletions Changes.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
Overview of changes in 2.7
==========================
Deprecated features
-------------------
``secret`` support has been removed by default.
static key mode (non-TLS) is no longer considered "good and secure enough"
for today's requirements. Use TLS mode instead. If deploying a PKI CA
Expand All @@ -10,6 +12,10 @@ Overview of changes in 2.7
``--allow-deprecated-insecure-static-crypto`` but will be removed in
OpenVPN 2.8.

NTLMv1 support has been removed because it is completely insecure.
NTLMv2 support is still available, but will removed in a future release.


Overview of changes in 2.6
==========================

Expand Down
6 changes: 4 additions & 2 deletions doc/man-sections/proxy-options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
``--http-proxy-user-pass`` option. (See section on inline files)

The last optional argument is an ``auth-method`` which should be one
of :code:`none`, :code:`basic`, or :code:`ntlm`.
of :code:`none`, :code:`basic`, or :code:`ntlm2`.

HTTP Digest authentication is supported as well, but only via the
:code:`auto` or :code:`auto-nct` flags (below). This must replace
Expand All @@ -29,7 +29,9 @@
http-proxy proxy.example.net 3128 authfile.txt
http-proxy proxy.example.net 3128 stdin
http-proxy proxy.example.net 3128 auto basic
http-proxy proxy.example.net 3128 auto-nct ntlm
http-proxy proxy.example.net 3128 auto-nct ntlm2

Note that support for NTLMv1 proxies was removed with OpenVPN 2.7.

--http-proxy-option args
Set extended HTTP proxy options. Requires an option ``type`` as argument
Expand Down
11 changes: 0 additions & 11 deletions src/openvpn/crypto_backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,17 +157,6 @@ bool crypto_pem_decode(const char *name, struct buffer *dst,
*/
int rand_bytes(uint8_t *output, int len);

/**
* Encrypt the given block, using DES ECB mode
*
* @param key DES key to use.
* @param src Buffer containing the 8-byte source.
* @param dst Buffer containing the 8-byte destination
*/
void cipher_des_encrypt_ecb(const unsigned char key[DES_KEY_LENGTH],
unsigned char src[DES_KEY_LENGTH],
unsigned char dst[DES_KEY_LENGTH]);

/*
*
* Generic cipher key type functions
Expand Down
11 changes: 0 additions & 11 deletions src/openvpn/crypto_mbedtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -758,17 +758,6 @@ cipher_ctx_final_check_tag(mbedtls_cipher_context_t *ctx, uint8_t *dst,
return 1;
}

void
cipher_des_encrypt_ecb(const unsigned char key[DES_KEY_LENGTH],
unsigned char src[DES_KEY_LENGTH],
unsigned char dst[DES_KEY_LENGTH])
{
mbedtls_des_context ctx;

ASSERT(mbed_ok(mbedtls_des_setkey_enc(&ctx, key)));
ASSERT(mbed_ok(mbedtls_des_crypt_ecb(&ctx, src, dst)));
}



/*
Expand Down
44 changes: 0 additions & 44 deletions src/openvpn/crypto_openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -988,50 +988,6 @@ cipher_ctx_final_check_tag(EVP_CIPHER_CTX *ctx, uint8_t *dst, int *dst_len,
return cipher_ctx_final(ctx, dst, dst_len);
}

void
cipher_des_encrypt_ecb(const unsigned char key[DES_KEY_LENGTH],
unsigned char src[DES_KEY_LENGTH],
unsigned char dst[DES_KEY_LENGTH])
{
/* We are using 3DES here with three times the same key to cheat
* and emulate DES as 3DES is better supported than DES */
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
if (!ctx)
{
crypto_msg(M_FATAL, "%s: EVP_CIPHER_CTX_new() failed", __func__);
}

unsigned char key3[DES_KEY_LENGTH*3];
for (int i = 0; i < 3; i++)
{
memcpy(key3 + (i * DES_KEY_LENGTH), key, DES_KEY_LENGTH);
}

if (!EVP_EncryptInit_ex(ctx, EVP_des_ede3_ecb(), NULL, key3, NULL))
{
crypto_msg(M_FATAL, "%s: EVP_EncryptInit_ex() failed", __func__);
}

int len;

/* The EVP_EncryptFinal method will write to the dst+len pointer even
* though there is nothing to encrypt anymore, provide space for that to
* not overflow the stack */
unsigned char dst2[DES_KEY_LENGTH * 2];
if (!EVP_EncryptUpdate(ctx, dst2, &len, src, DES_KEY_LENGTH))
{
crypto_msg(M_FATAL, "%s: EVP_EncryptUpdate() failed", __func__);
}

if (!EVP_EncryptFinal(ctx, dst2 + len, &len))
{
crypto_msg(M_FATAL, "%s: EVP_EncryptFinal() failed", __func__);
}

memcpy(dst, dst2, DES_KEY_LENGTH);

EVP_CIPHER_CTX_free(ctx);
}

/*
*
Expand Down
195 changes: 78 additions & 117 deletions src/openvpn/ntlm.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,6 @@



static void
create_des_keys(const unsigned char *hash, unsigned char *key)
{
key[0] = hash[0];
key[1] = ((hash[0] & 1) << 7) | (hash[1] >> 1);
key[2] = ((hash[1] & 3) << 6) | (hash[2] >> 2);
key[3] = ((hash[2] & 7) << 5) | (hash[3] >> 3);
key[4] = ((hash[3] & 15) << 4) | (hash[4] >> 4);
key[5] = ((hash[4] & 31) << 3) | (hash[5] >> 5);
key[6] = ((hash[5] & 63) << 2) | (hash[6] >> 6);
key[7] = ((hash[6] & 127) << 1);
}

static void
gen_md4_hash(const uint8_t *data, int data_len, uint8_t *result)
{
Expand Down Expand Up @@ -210,7 +197,7 @@ ntlm_phase_3(const struct http_proxy_info *p, const char *phase_2,
uint8_t phase3[464];

uint8_t md4_hash[MD4_DIGEST_LENGTH + 5];
uint8_t challenge[8], ntlm_response[24];
uint8_t challenge[8];
int i, ret_val;

uint8_t ntlmv2_response[144];
Expand All @@ -227,8 +214,6 @@ ntlm_phase_3(const struct http_proxy_info *p, const char *phase_2,
char username[128];
char *separator;

bool ntlmv2_enabled = (p->auth_method == HTTP_AUTH_NTLM2);

ASSERT(strlen(p->up.username) > 0);
ASSERT(strlen(p->up.password) > 0);

Expand Down Expand Up @@ -282,126 +267,102 @@ ntlm_phase_3(const struct http_proxy_info *p, const char *phase_2,
challenge[i] = buf2[i+24];
}

if (ntlmv2_enabled) /* Generate NTLMv2 response */
{
int tib_len;
/* Generate NTLMv2 response */
int tib_len;

/* NTLMv2 hash */
strcpy(userdomain, username);
my_strupr(userdomain);
if (strlen(username) + strlen(domain) < sizeof(userdomain))
{
strcat(userdomain, domain);
}
else
/* NTLMv2 hash */
strcpy(userdomain, username);
my_strupr(userdomain);
if (strlen(username) + strlen(domain) < sizeof(userdomain))
{
strcat(userdomain, domain);
}
else
{
msg(M_INFO, "Warning: Username or domain too long");
}
unicodize(userdomain_u, userdomain);
gen_hmac_md5((uint8_t *)userdomain_u, 2 * strlen(userdomain), md4_hash,
ntlmv2_hash);

/* NTLMv2 Blob */
memset(ntlmv2_blob, 0, 128); /* Clear blob buffer */
ntlmv2_blob[0x00] = 1; /* Signature */
ntlmv2_blob[0x01] = 1; /* Signature */
ntlmv2_blob[0x04] = 0; /* Reserved */
gen_timestamp(&ntlmv2_blob[0x08]); /* 64-bit Timestamp */
gen_nonce(&ntlmv2_blob[0x10]); /* 64-bit Client Nonce */
ntlmv2_blob[0x18] = 0; /* Unknown, zero should work */

/* Add target information block to the blob */

/* Check for Target Information block */
/* The NTLM spec instructs to interpret these 4 consecutive bytes as a
* 32bit long integer. However, no endianness is specified.
* The code here and that found in other NTLM implementations point
* towards the assumption that the byte order on the wire has to
* match the order on the sending and receiving hosts. Probably NTLM has
* been thought to be always running on x86_64/i386 machine thus
* implying Little-Endian everywhere.
*
* This said, in case of future changes, we should keep in mind that the
* byte order on the wire for the NTLM header is LE.
*/
const size_t hoff = 0x14;
unsigned long flags = buf2[hoff] | (buf2[hoff + 1] << 8)
|(buf2[hoff + 2] << 16) | (buf2[hoff + 3] << 24);
if ((flags & 0x00800000) == 0x00800000)
{
tib_len = buf2[0x28]; /* Get Target Information block size */
if (tib_len > 96)
{
msg(M_INFO, "Warning: Username or domain too long");
tib_len = 96;
}
unicodize(userdomain_u, userdomain);
gen_hmac_md5((uint8_t *)userdomain_u, 2 * strlen(userdomain), md4_hash,
ntlmv2_hash);

/* NTLMv2 Blob */
memset(ntlmv2_blob, 0, 128); /* Clear blob buffer */
ntlmv2_blob[0x00] = 1; /* Signature */
ntlmv2_blob[0x01] = 1; /* Signature */
ntlmv2_blob[0x04] = 0; /* Reserved */
gen_timestamp(&ntlmv2_blob[0x08]); /* 64-bit Timestamp */
gen_nonce(&ntlmv2_blob[0x10]); /* 64-bit Client Nonce */
ntlmv2_blob[0x18] = 0; /* Unknown, zero should work */

/* Add target information block to the blob */

/* Check for Target Information block */
/* The NTLM spec instructs to interpret these 4 consecutive bytes as a
* 32bit long integer. However, no endianness is specified.
* The code here and that found in other NTLM implementations point
* towards the assumption that the byte order on the wire has to
* match the order on the sending and receiving hosts. Probably NTLM has
* been thought to be always running on x86_64/i386 machine thus
* implying Little-Endian everywhere.
*
* This said, in case of future changes, we should keep in mind that the
* byte order on the wire for the NTLM header is LE.
*/
const size_t hoff = 0x14;
unsigned long flags = buf2[hoff] | (buf2[hoff + 1] << 8)
|(buf2[hoff + 2] << 16) | (buf2[hoff + 3] << 24);
if ((flags & 0x00800000) == 0x00800000)
{
tib_len = buf2[0x28]; /* Get Target Information block size */
if (tib_len > 96)
{
tib_len = 96;
}

{
uint8_t *tib_ptr;
uint8_t tib_pos = buf2[0x2c];
if (tib_pos + tib_len > sizeof(buf2))
{
uint8_t *tib_ptr;
uint8_t tib_pos = buf2[0x2c];
if (tib_pos + tib_len > sizeof(buf2))
{
return NULL;
}
/* Get Target Information block pointer */
tib_ptr = buf2 + tib_pos;
/* Copy Target Information block into the blob */
memcpy(&ntlmv2_blob[0x1c], tib_ptr, tib_len);
return NULL;
}
/* Get Target Information block pointer */
tib_ptr = buf2 + tib_pos;
/* Copy Target Information block into the blob */
memcpy(&ntlmv2_blob[0x1c], tib_ptr, tib_len);
}
else
{
tib_len = 0;
}

/* Unknown, zero works */
ntlmv2_blob[0x1c + tib_len] = 0;

/* Get blob length */
ntlmv2_blob_size = 0x20 + tib_len;

/* Add challenge from message 2 */
memcpy(&ntlmv2_response[8], challenge, 8);

/* hmac-md5 */
gen_hmac_md5(&ntlmv2_response[8], ntlmv2_blob_size + 8, ntlmv2_hash,
ntlmv2_hmacmd5);

/* Add hmac-md5 result to the blob.
* Note: This overwrites challenge previously written at
* ntlmv2_response[8..15] */
memcpy(ntlmv2_response, ntlmv2_hmacmd5, MD5_DIGEST_LENGTH);
}
else /* Generate NTLM response */
else
{
unsigned char key1[DES_KEY_LENGTH], key2[DES_KEY_LENGTH];
unsigned char key3[DES_KEY_LENGTH];
tib_len = 0;
}

create_des_keys(md4_hash, key1);
cipher_des_encrypt_ecb(key1, challenge, ntlm_response);
/* Unknown, zero works */
ntlmv2_blob[0x1c + tib_len] = 0;

create_des_keys(&md4_hash[DES_KEY_LENGTH - 1], key2);
cipher_des_encrypt_ecb(key2, challenge, &ntlm_response[DES_KEY_LENGTH]);
/* Get blob length */
ntlmv2_blob_size = 0x20 + tib_len;

create_des_keys(&md4_hash[2 * (DES_KEY_LENGTH - 1)], key3);
cipher_des_encrypt_ecb(key3, challenge,
&ntlm_response[DES_KEY_LENGTH * 2]);
}
/* Add challenge from message 2 */
memcpy(&ntlmv2_response[8], challenge, 8);

/* hmac-md5 */
gen_hmac_md5(&ntlmv2_response[8], ntlmv2_blob_size + 8, ntlmv2_hash,
ntlmv2_hmacmd5);

/* Add hmac-md5 result to the blob.
* Note: This overwrites challenge previously written at
* ntlmv2_response[8..15] */
memcpy(ntlmv2_response, ntlmv2_hmacmd5, MD5_DIGEST_LENGTH);

memset(phase3, 0, sizeof(phase3)); /* clear reply */

strcpy((char *)phase3, "NTLMSSP\0"); /* signature */
phase3[8] = 3; /* type 3 */

if (ntlmv2_enabled) /* NTLMv2 response */
{
add_security_buffer(0x14, ntlmv2_response, ntlmv2_blob_size + 16,
phase3, &phase3_bufpos);
}
else /* NTLM response */
{
add_security_buffer(0x14, ntlm_response, 24, phase3, &phase3_bufpos);
}
/* NTLMv2 response */
add_security_buffer(0x14, ntlmv2_response, ntlmv2_blob_size + 16,
phase3, &phase3_bufpos);

/* username in ascii */
add_security_buffer(0x24, username, strlen(username), phase3,
Expand Down
2 changes: 1 addition & 1 deletion src/openvpn/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ static const char usage_message[] =
" through an HTTP proxy at address s and port p.\n"
" If proxy authentication is required,\n"
" up is a file containing username/password on 2 lines, or\n"
" 'stdin' to prompt from console. Add auth='ntlm' if\n"
" 'stdin' to prompt from console. Add auth='ntlm2' if\n"
" the proxy requires NTLM authentication.\n"
"--http-proxy s p 'auto[-nct]' : Like the above directive, but automatically\n"
" determine auth method and query for username/password\n"
Expand Down
Loading

0 comments on commit 21910eb

Please sign in to comment.