Hi,

On 10-11-15 20:48, Gert Doering wrote:
> On Fri, Oct 30, 2015 at 03:07:47PM +0100, Holger Kummert wrote:
>> Am 30.10.2015 um 14:58 schrieb Steffan Karger:
> [..]
>>> Seems I forgot the * before the second msg_bufpos, sorry.  msg_bufpos
>>> itself is a pointer indeed, but *msg_bufpos is (used as) an offset
>>> into msg_buf:
>>>
>>>     memcpy(&msg_buf[*msg_bufpos], data, msg_buf[sb_offset]);
>>>
>>> msg_bufpos (without the *) is not related to msg_buf.
>>
>> Ah, then your first proposal makes sense
>> (i.e. if (msg_buflen - length < *msg_bufpos) )
>>
>> We could take that.
> 
> So, how do we move onward here?  Holger, can you send a new patch (set) 
> incorporating Steffan's comments?
> 
> Since we found a proper reviewer now, merging this looks possible, after
> all the time... ;-)

Would still be nice to get this patch in.  I've had this patch (plus a
type-fix patch) in my local repo for a long time now, so to get things
started a bit, I rebased it onto the current (post-reformatting) master
branch.  The patch is attached.

Any news from the author's side?

-Steffan
From c5f5fc4278d34e8d99733672a6a324830907aa23 Mon Sep 17 00:00:00 2001
From: Holger Kummert <holger.kumm...@sophos.com>
Date: Fri, 4 Jul 2014 09:35:24 +0200
Subject: [PATCH] Get NTLMv1 and NTLMv2 up and running

  * Force conversion to UTF-16 of username and domain if server requires UTF-16.
  * Rewrite conversion function to cleanly convert UTF-8 to UTF-16.
  * Fix bug in length computation in NTLMv2-code.
  * Architecture independent access to NTLM NegotiateFlags.

Signed-off-by: Holger Kummert <holger.kumm...@sophos.com>
---
 src/openvpn/ntlm.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 166 insertions(+), 24 deletions(-)

diff --git a/src/openvpn/ntlm.c b/src/openvpn/ntlm.c
index e78af9e..062efba 100644
--- a/src/openvpn/ntlm.c
+++ b/src/openvpn/ntlm.c
@@ -56,6 +56,21 @@
 #endif
 
 
+#define UNI_MAX_UTF16               0x0010FFFF
+#define UNI_HALF_BASE               0x00010000
+#define UNI_HALF_MASK               0x000003FF
+#define UNI_HALF_SHIFT              10
+#define UNI_SUR_HIGH_START          0xD800
+#define UNI_SUR_LOW_START           0xDC00
+#define UNI_SUR_LOW_END             0xDFFF
+
+
+#define NTLM_NEGOTIATE_UNICODE      (1<<0)
+#define NTLM_NEGOTIATE_OEM          (1<<1)
+#define NTLM_NEGOTIATE_NTLM2_KEY    (1<<19)
+#define NTLM_NEGOTIATE_TARGET_INFO  (1<<23)
+
+
 
 static void
 create_des_keys(const unsigned char *hash, unsigned char *key)
@@ -78,8 +93,11 @@ gen_md4_hash(const char *data, int data_len, char *result)
     const md_kt_t *md4_kt = md_kt_get("MD4");
     char md[MD4_DIGEST_LENGTH];
 
-    md_full(md4_kt, data, data_len, md);
-    memcpy(result, md, MD4_DIGEST_LENGTH);
+    if (data_len >= 0)
+    {
+        md_full(md4_kt, data, data_len, md);
+        memcpy(result, md, MD4_DIGEST_LENGTH);
+    }
 }
 
 static void
@@ -140,23 +158,106 @@ my_strupr(unsigned char *str)
 }
 
 static int
-unicodize(char *dst, const char *src)
+utf8_to_utf16LE(unsigned char *dst, int dst_len, unsigned char *src)
 {
-    /* not really unicode... */
-    int i = 0;
-    do
+    /* Convert UTF-8 to UTF-16 little endian
+     *
+     * http://clang.llvm.org/doxygen/ConvertUTF_8c_source.html
+     */
+    unsigned char *end = src + strlen(src),
+                  *dst_start = dst;
+    const char trailingBytesForUTF8[256] = {
+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+        2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+    };
+    const unsigned int offsetsUtf8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
+                                          0x03C82080UL, 0xFA082080UL, 0x82082080UL };
+
+    while (src < end)
     {
-        dst[i++] = *src;
-        dst[i++] = 0;
+        unsigned int ch = 0,
+                     extraBytes = trailingBytesForUTF8[*src];
+
+        if (extraBytes >= end - src)
+        {
+            return -1;
+        }
+
+        /* The cases all fall through. */
+        switch (extraBytes) {
+            case 5: ch += *src++; ch <<= 6;
+
+            case 4: ch += *src++; ch <<= 6;
+
+            case 3: ch += *src++; ch <<= 6;
+
+            case 2: ch += *src++; ch <<= 6;
+
+            case 1: ch += *src++; ch <<= 6;
+
+            case 0: ch += *src++;
+        }
+        ch -= offsetsUtf8[extraBytes];   /* clean up UTF-8 control bits */
+
+        /* Now 'ch' contains the codepoint of the UTF-8-character. */
+
+        if (ch <= 0xFFFF)
+        {
+            if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)
+            {
+                msg(M_INFO, "Warning: Illegal character value: %x is in reserved area of UTF-16 [%x, %x]", UNI_SUR_HIGH_START, UNI_SUR_LOW_END);
+                return -1;
+            }
+            else  /* normal case */
+            {
+                if (dst_len - (dst - dst_start) < 2)
+                {
+                    msg(M_INFO, "Warning: Not enough space in destination buffer for conversion to UTF-16");
+                    return -1;
+                }
+                *dst++ = ch & 0xff;
+                *dst++ = (ch >> 8) & 0xff;
+            }
+        }
+        else if (ch > UNI_MAX_UTF16)
+        {
+            msg(M_INFO, "Warning: Illegal character value :%x is too large for UTF-16", ch);
+            return -1;
+        }
+        else
+        {
+            /* character is in range 0xFFFF - 0x10FFFF. */
+            if (dst_len - (dst - dst_start) < 4)
+            {
+                msg(M_INFO, "Warning: Not enough space in destination buffer for conversion to UTF-16");
+                return -1;
+            }
+            ch -= UNI_HALF_BASE;
+            *dst++ = ( (ch >> UNI_HALF_SHIFT) + UNI_SUR_HIGH_START)        & 0xff;
+            *dst++ = (((ch >> UNI_HALF_SHIFT) + UNI_SUR_HIGH_START) >> 8) & 0xff;
+            *dst++ = ( (ch & UNI_HALF_MASK) + UNI_SUR_LOW_START)           & 0xff;
+            *dst++ = (((ch & UNI_HALF_MASK) + UNI_SUR_LOW_START) >> 8)    & 0xff;
+        }
     }
-    while (*src++);
 
-    return i;
+    return dst - dst_start;
 }
 
 static void
-add_security_buffer(int sb_offset, void *data, int length, unsigned char *msg_buf, int *msg_bufpos)
+add_security_buffer(int sb_offset, void *data, int length, unsigned char *msg_buf, int *msg_bufpos, int msg_buflen)
 {
+    if ((*msg_bufpos - *msg_buf) + length > msg_buflen)
+    {
+        msg(M_INFO, "Warning: Not enough space in destination buffer");
+        return;
+    }
+
     /* Adds security buffer data to a message and sets security buffer's offset and length */
     msg_buf[sb_offset] = (unsigned char)length;
     msg_buf[sb_offset + 2] = msg_buf[sb_offset];
@@ -195,10 +296,12 @@ ntlm_phase_3(const struct http_proxy_info *p, const char *phase_2, struct gc_are
     char pwbuf[sizeof(p->up.password) * 2]; /* for unicode password */
     char buf2[128]; /* decoded reply from proxy */
     unsigned char phase3[464];
+    unsigned long ntlm_negotiate_flags;
 
     char md4_hash[MD4_DIGEST_LENGTH+5];
     char challenge[8], ntlm_response[24];
     int i, ret_val;
+    int ntlm_unicode;
 
     char ntlmv2_response[144];
     char userdomain_u[256];     /* for uppercase unicode username and domain */
@@ -211,7 +314,9 @@ ntlm_phase_3(const struct http_proxy_info *p, const char *phase_2, struct gc_are
     size_t len;
 
     char domain[128];
-    char username[128];
+    char domain_u[256];
+    char username[USER_PASS_LEN];
+    char username_u[USER_PASS_LEN*2];
     char *separator;
 
     bool ntlmv2_enabled = (p->auth_method == HTTP_AUTH_NTLM2);
@@ -244,7 +349,7 @@ ntlm_phase_3(const struct http_proxy_info *p, const char *phase_2, struct gc_are
 
 
     /* fill 1st 16 bytes with md4 hash, disregard terminating null */
-    gen_md4_hash(pwbuf, unicodize(pwbuf, p->up.password) - 2, md4_hash);
+    gen_md4_hash(pwbuf, utf8_to_utf16LE(pwbuf, sizeof(p->up.password) * 2, (unsigned char *)p->up.password), md4_hash);
 
     /* pad to 21 bytes */
     memset(md4_hash + MD4_DIGEST_LENGTH, 0, 5);
@@ -258,12 +363,23 @@ ntlm_phase_3(const struct http_proxy_info *p, const char *phase_2, struct gc_are
     /* we can be sure that phase_2 is less than 128
      * therefore buf2 needs to be (3/4 * 128) */
 
+    /* extract NegotiateFlags from bytes 20-23 */
+    ntlm_negotiate_flags = (buf2[0x14] & 0xff) | (buf2[0x15] & 0xff) << 8 | (buf2[0x16] & 0xff) << 16 | (buf2[0x17] & 0xff) << 24;
+
+    ntlm_unicode = ntlm_negotiate_flags & NTLM_NEGOTIATE_UNICODE ? 1 : 0;
+
     /* extract the challenge from bytes 24-31 */
     for (i = 0; i<8; i++)
     {
         challenge[i] = buf2[i+24];
     }
 
+    msg(M_DEBUG, "Received NTLM negotiate flags 0x%08x: %s encoding, %sNTLM2sec, %sTargetInfo - doing NTLMv%s",
+        ntlm_negotiate_flags, ntlm_unicode ? "Unicode" : "OEM",
+        ntlm_negotiate_flags & NTLM_NEGOTIATE_NTLM2_KEY ? "" : "no ",
+        ntlm_negotiate_flags & NTLM_NEGOTIATE_TARGET_INFO ? "" : "no ",
+        ntlmv2_enabled ? "2" : "1");
+
     if (ntlmv2_enabled)      /* Generate NTLMv2 response */
     {
         int tib_len;
@@ -278,8 +394,11 @@ ntlm_phase_3(const struct http_proxy_info *p, const char *phase_2, struct gc_are
         {
             msg(M_INFO, "Warning: Username or domain too long");
         }
-        unicodize(userdomain_u, userdomain);
-        gen_hmac_md5(userdomain_u, 2 * strlen(userdomain), md4_hash, MD5_DIGEST_LENGTH, ntlmv2_hash);
+        len = utf8_to_utf16LE(userdomain_u, sizeof(userdomain_u), userdomain);
+        if (len >= 0)
+        {
+            gen_hmac_md5(userdomain_u, len, md4_hash, MD5_DIGEST_LENGTH, ntlmv2_hash);
+        }
 
         /* NTLMv2 Blob */
         memset(ntlmv2_blob, 0, 128);                        /* Clear blob buffer */
@@ -291,9 +410,9 @@ ntlm_phase_3(const struct http_proxy_info *p, const char *phase_2, struct gc_are
         ntlmv2_blob[0x18] = 0;                              /* Unknown, zero should work */
 
         /* Add target information block to the blob */
-        if (( *((long *)&buf2[0x14]) & 0x00800000) == 0x00800000)          /* Check for Target Information block */
+        if (ntlm_negotiate_flags & NTLM_NEGOTIATE_TARGET_INFO)             /* Check for Target Information block */
         {
-            tib_len = buf2[0x28];            /* Get Target Information block size */
+            tib_len = buf2[0x28] & 0xff | (buf2[0x29] & 0xff) << 8;              /* Get Target Information block size */
             if (tib_len > 96)
             {
                 tib_len = 96;
@@ -311,7 +430,7 @@ ntlm_phase_3(const struct http_proxy_info *p, const char *phase_2, struct gc_are
         ntlmv2_blob[0x1c + tib_len] = 0;                    /* Unknown, zero works */
 
         /* Get blob length */
-        ntlmv2_blob_size = 0x20 + tib_len;
+        ntlmv2_blob_size = 0x1c + tib_len;
 
         /* Add challenge from message 2 */
         memcpy(&ntlmv2_response[8], challenge, 8);
@@ -345,18 +464,41 @@ ntlm_phase_3(const struct http_proxy_info *p, const char *phase_2, struct gc_are
 
     if (ntlmv2_enabled)      /* NTLMv2 response */
     {
-        add_security_buffer(0x14, ntlmv2_response, ntlmv2_blob_size + 16, phase3, &phase3_bufpos);
+        add_security_buffer(0x14, ntlmv2_response, ntlmv2_blob_size + 16, phase3, &phase3_bufpos, sizeof(phase3));
     }
     else       /* NTLM response */
     {
-        add_security_buffer(0x14, ntlm_response, 24, phase3, &phase3_bufpos);
+        add_security_buffer(0x14, ntlm_response, 24, phase3, &phase3_bufpos, sizeof(phase3));
+    }
+
+    /* Set username. */
+    if (ntlm_unicode)
+    {
+        len = utf8_to_utf16LE(username_u, sizeof(username_u), username);
+        if (len>=0)
+        {
+            add_security_buffer(0x24, username_u, len, phase3, &phase3_bufpos, sizeof(phase3));
+        }
+    }
+    else
+    {
+        add_security_buffer(0x24, username, strlen(username), phase3, &phase3_bufpos, sizeof(phase3));
     }
 
-    /* username in ascii */
-    add_security_buffer(0x24, username, strlen(username), phase3, &phase3_bufpos);
 
     /* Set domain. If <domain> is empty, default domain will be used (i.e. proxy's domain) */
-    add_security_buffer(0x1c, domain, strlen(domain), phase3, &phase3_bufpos);
+    if (ntlm_unicode)
+    {
+        len = utf8_to_utf16LE(domain_u, sizeof(domain_u), domain);
+        if (len>=0)
+        {
+            add_security_buffer(0x1c, domain_u, len, phase3, &phase3_bufpos, sizeof(phase3));
+        }
+    }
+    else
+    {
+        add_security_buffer(0x1c, domain, strlen(domain), phase3, &phase3_bufpos, sizeof(phase3));
+    }
 
 
     /* other security buffers will be empty */
@@ -365,7 +507,7 @@ ntlm_phase_3(const struct http_proxy_info *p, const char *phase_2, struct gc_are
     phase3[0x38] = phase3_bufpos;     /* no session key */
 
     /* flags */
-    phase3[0x3c] = 0x02; /* negotiate oem */
+    phase3[0x3c] = ntlm_unicode ? 0x01 : 0x02; /* negotiate unicode/oem */
     phase3[0x3d] = 0x02; /* negotiate ntlm */
 
     return ((const char *)make_base64_string2((unsigned char *)phase3, phase3_bufpos, gc));
-- 
2.7.4

Attachment: signature.asc
Description: OpenPGP digital signature

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most 
engaging tech sites, SlashDot.org! http://sdm.link/slashdot
_______________________________________________
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to