src/openvpn/ntlm.c
6fbf66fa
 /*
  *  ntlm proxy support for OpenVPN
  *
  *  Copyright (C) 2004 William Preston
  *
1bda73a7
  *  *NTLMv2 support and domain name parsing by Miroslav Zajic, Nextsoft s.r.o.*
  *
6fbf66fa
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 2 of the License, or
  *  (at your option) any later version.
  *
  *  This program 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 General Public License for more details.
  *
caa54ac3
  *  You should have received a copy of the GNU General Public License along
  *  with this program; if not, write to the Free Software Foundation, Inc.,
  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
6fbf66fa
  */
 
c110b289
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
6fbf66fa
 #include "syshead.h"
 
 #if NTLM
 
 #include "common.h"
 #include "buffer.h"
 #include "misc.h"
 #include "socket.h"
 #include "fdmisc.h"
 #include "proxy.h"
 #include "ntlm.h"
 #include "base64.h"
 #include "crypto.h"
 
 #include "memdbg.h"
 
1bda73a7
 
 /* 64bit datatype macros */
81d882d5
 #ifdef _MSC_VER
 /* MS compilers */
 #define UINTEGER64 __int64
 #define UINT64(c) c ## Ui64
 #else
 /* Non MS compilers */
 #define UINTEGER64 unsigned long long
 #define UINT64(c) c ## LL
1bda73a7
 #endif
 
 
 
6fbf66fa
 static void
 create_des_keys(const unsigned char *hash, unsigned char *key)
 {
81d882d5
     key[0] = hash[0];
c310f1ec
     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);
81d882d5
     key_des_fixup(key, 8, 1);
6fbf66fa
 }
 
 static void
e7e4070c
 gen_md4_hash(const uint8_t *data, int data_len, uint8_t *result)
6fbf66fa
 {
81d882d5
     /* result is 16 byte md4 hash */
     const md_kt_t *md4_kt = md_kt_get("MD4");
e7e4070c
     uint8_t md[MD4_DIGEST_LENGTH];
6fbf66fa
 
81d882d5
     md_full(md4_kt, data, data_len, md);
     memcpy(result, md, MD4_DIGEST_LENGTH);
6fbf66fa
 }
 
1bda73a7
 static void
e7e4070c
 gen_hmac_md5(const uint8_t *data, int data_len, const uint8_t *key, int key_len,
              uint8_t *result)
1bda73a7
 {
81d882d5
     const md_kt_t *md5_kt = md_kt_get("MD5");
aba98e90
     hmac_ctx_t *hmac_ctx = hmac_ctx_new();
81d882d5
 
aba98e90
     hmac_ctx_init(hmac_ctx, key, key_len, md5_kt);
e7e4070c
     hmac_ctx_update(hmac_ctx, data, data_len);
     hmac_ctx_final(hmac_ctx, result);
aba98e90
     hmac_ctx_cleanup(hmac_ctx);
     hmac_ctx_free(hmac_ctx);
1bda73a7
 }
 
 static void
e7e4070c
 gen_timestamp(uint8_t *timestamp)
81d882d5
 {
     /* Copies 8 bytes long timestamp into "timestamp" buffer.
c310f1ec
      * Timestamp is Little-endian, 64-bit signed value representing the
      * number of tenths of a microsecond since January 1, 1601.
81d882d5
      */
 
     UINTEGER64 timestamp_ull;
 
     timestamp_ull = openvpn_time(NULL);
     timestamp_ull = (timestamp_ull + UINT64(11644473600)) * UINT64(10000000);
 
     /* store little endian value */
     timestamp[0] = timestamp_ull & UINT64(0xFF);
     timestamp[1] = (timestamp_ull  >> 8)  & UINT64(0xFF);
     timestamp[2] = (timestamp_ull  >> 16) & UINT64(0xFF);
     timestamp[3] = (timestamp_ull  >> 24) & UINT64(0xFF);
     timestamp[4] = (timestamp_ull  >> 32) & UINT64(0xFF);
     timestamp[5] = (timestamp_ull  >> 40) & UINT64(0xFF);
     timestamp[6] = (timestamp_ull  >> 48) & UINT64(0xFF);
     timestamp[7] = (timestamp_ull  >> 56) & UINT64(0xFF);
1bda73a7
 }
 
 static void
81d882d5
 gen_nonce(unsigned char *nonce)
 {
     /* Generates 8 random bytes to be used as client nonce */
     int i;
 
4cd4899e
     for (i = 0; i<8; i++)
     {
81d882d5
         nonce[i] = (unsigned char)get_random();
     }
1bda73a7
 }
 
72bcdfdc
 static void
1cdfc930
 my_strupr(char *str)
81d882d5
 {
     /* converts string to uppercase in place */
1bda73a7
 
69162924
     while (*str)
4cd4899e
     {
         *str = toupper(*str);
69162924
         str++;
     }
1bda73a7
 }
 
6fbf66fa
 static int
81d882d5
 unicodize(char *dst, const char *src)
6fbf66fa
 {
81d882d5
     /* not really unicode... */
     int i = 0;
     do
6fbf66fa
     {
81d882d5
         dst[i++] = *src;
         dst[i++] = 0;
c310f1ec
     } while (*src++);
6fbf66fa
 
81d882d5
     return i;
6fbf66fa
 }
 
1bda73a7
 static void
c310f1ec
 add_security_buffer(int sb_offset, void *data, int length,
                     unsigned char *msg_buf, int *msg_bufpos)
1bda73a7
 {
c310f1ec
     /* Adds security buffer data to a message and sets security buffer's
      * offset and length */
81d882d5
     msg_buf[sb_offset] = (unsigned char)length;
     msg_buf[sb_offset + 2] = msg_buf[sb_offset];
     msg_buf[sb_offset + 4] = (unsigned char)(*msg_bufpos & 0xff);
     msg_buf[sb_offset + 5] = (unsigned char)((*msg_bufpos >> 8) & 0xff);
     memcpy(&msg_buf[*msg_bufpos], data, msg_buf[sb_offset]);
     *msg_bufpos += length;
1bda73a7
 }
 
6fbf66fa
 const char *
81d882d5
 ntlm_phase_1(const struct http_proxy_info *p, struct gc_arena *gc)
6fbf66fa
 {
81d882d5
     struct buffer out = alloc_buf_gc(96, gc);
     /* try a minimal NTLM handshake
      *
      * http://davenport.sourceforge.net/ntlm.html
      *
      * This message contains only the NTLMSSP signature,
      * the NTLM message type,
      * and the minimal set of flags (Negotiate NTLM and Negotiate OEM).
      *
      */
     buf_printf(&out, "%s", "TlRMTVNTUAABAAAAAgIAAA==");
     return (BSTR(&out));
6fbf66fa
 }
 
 const char *
c310f1ec
 ntlm_phase_3(const struct http_proxy_info *p, const char *phase_2,
              struct gc_arena *gc)
6fbf66fa
 {
81d882d5
     /* NTLM handshake
      *
      * http://davenport.sourceforge.net/ntlm.html
      *
      */
 
     char pwbuf[sizeof(p->up.password) * 2]; /* for unicode password */
e7e4070c
     uint8_t buf2[128]; /* decoded reply from proxy */
     uint8_t phase3[464];
81d882d5
 
e7e4070c
     uint8_t md4_hash[MD4_DIGEST_LENGTH + 5];
     uint8_t challenge[8], ntlm_response[24];
81d882d5
     int i, ret_val;
 
e7e4070c
     uint8_t ntlmv2_response[144];
81d882d5
     char userdomain_u[256];     /* for uppercase unicode username and domain */
     char userdomain[128];       /* the same as previous but ascii */
e7e4070c
     uint8_t ntlmv2_hash[MD5_DIGEST_LENGTH];
     uint8_t ntlmv2_hmacmd5[16];
     uint8_t *ntlmv2_blob = ntlmv2_response + 16;     /* inside ntlmv2_response, length: 128 */
81d882d5
     int ntlmv2_blob_size = 0;
     int phase3_bufpos = 0x40;     /* offset to next security buffer data to be added */
     size_t len;
 
     char domain[128];
     char username[128];
     char *separator;
 
     bool ntlmv2_enabled = (p->auth_method == HTTP_AUTH_NTLM2);
 
     CLEAR(buf2);
 
     ASSERT(strlen(p->up.username) > 0);
     ASSERT(strlen(p->up.password) > 0);
 
     /* username parsing */
     separator = strchr(p->up.username, '\\');
     if (separator == NULL)
     {
         strncpy(username, p->up.username, sizeof(username)-1);
         username[sizeof(username)-1] = 0;
         domain[0] = 0;
     }
     else
     {
         strncpy(username, separator+1, sizeof(username)-1);
         username[sizeof(username)-1] = 0;
         len = separator - p->up.username;
         if (len > sizeof(domain) - 1)
         {
             len = sizeof(domain) - 1;
         }
         strncpy(domain, p->up.username,  len);
         domain[len] = 0;
     }
 
 
     /* fill 1st 16 bytes with md4 hash, disregard terminating null */
c2d08916
     int unicode_len = unicodize(pwbuf, p->up.password) - 2;
     gen_md4_hash((uint8_t *)pwbuf, unicode_len, md4_hash);
81d882d5
 
     /* pad to 21 bytes */
     memset(md4_hash + MD4_DIGEST_LENGTH, 0, 5);
 
e7e4070c
     ret_val = openvpn_base64_decode(phase_2, buf2, -1);
81d882d5
     if (ret_val < 0)
     {
         return NULL;
     }
 
     /* we can be sure that phase_2 is less than 128
      * therefore buf2 needs to be (3/4 * 128) */
 
     /* extract the challenge from bytes 24-31 */
     for (i = 0; i<8; i++)
     {
         challenge[i] = buf2[i+24];
     }
 
     if (ntlmv2_enabled)      /* Generate NTLMv2 response */
     {
         int tib_len;
 
         /* NTLMv2 hash */
ad7f7e56
         strcpy(userdomain, username);
         my_strupr(userdomain);
81d882d5
         if (strlen(username) + strlen(domain) < sizeof(userdomain))
         {
             strcat(userdomain, domain);
         }
         else
         {
             msg(M_INFO, "Warning: Username or domain too long");
         }
         unicodize(userdomain_u, userdomain);
e7e4070c
         gen_hmac_md5((uint8_t *)userdomain_u, 2 * strlen(userdomain), md4_hash,
                      MD5_DIGEST_LENGTH, ntlmv2_hash);
81d882d5
 
         /* 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 */
e7e4070c
         gen_timestamp(&ntlmv2_blob[0x08]);                  /* 64-bit Timestamp */
         gen_nonce(&ntlmv2_blob[0x10]);                      /* 64-bit Client Nonce */
81d882d5
         ntlmv2_blob[0x18] = 0;                              /* Unknown, zero should work */
 
         /* Add target information block to the blob */
c310f1ec
 
         /* Check for Target Information block */
e84b6994
         /* 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)
81d882d5
         {
             tib_len = buf2[0x28];            /* Get Target Information block size */
             if (tib_len > 96)
             {
                 tib_len = 96;
             }
c310f1ec
 
81d882d5
             {
e7e4070c
                 uint8_t *tib_ptr;
                 uint8_t tib_pos = buf2[0x2c];
7718c898
                 if (tib_pos + tib_len > sizeof(buf2))
                 {
                     return NULL;
                 }
c310f1ec
                 /* 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);
81d882d5
             }
         }
         else
         {
             tib_len = 0;
         }
 
c310f1ec
         /* Unknown, zero works */
         ntlmv2_blob[0x1c + tib_len] = 0;
81d882d5
 
         /* Get blob length */
         ntlmv2_blob_size = 0x20 + tib_len;
 
         /* Add challenge from message 2 */
         memcpy(&ntlmv2_response[8], challenge, 8);
 
         /* hmac-md5 */
c310f1ec
         gen_hmac_md5(&ntlmv2_response[8], ntlmv2_blob_size + 8, ntlmv2_hash,
                      MD5_DIGEST_LENGTH, ntlmv2_hmacmd5);
81d882d5
 
c310f1ec
         /* 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);
81d882d5
     }
c310f1ec
     else /* Generate NTLM response */
81d882d5
     {
c310f1ec
         unsigned char key1[DES_KEY_LENGTH], key2[DES_KEY_LENGTH];
         unsigned char key3[DES_KEY_LENGTH];
81d882d5
 
e7e4070c
         create_des_keys(md4_hash, key1);
81d882d5
         cipher_des_encrypt_ecb(key1, challenge, ntlm_response);
 
e7e4070c
         create_des_keys(&md4_hash[DES_KEY_LENGTH - 1], key2);
81d882d5
         cipher_des_encrypt_ecb(key2, challenge, &ntlm_response[DES_KEY_LENGTH]);
 
e7e4070c
         create_des_keys(&md4_hash[2 * (DES_KEY_LENGTH - 1)], key3);
c310f1ec
         cipher_des_encrypt_ecb(key3, challenge,
                                &ntlm_response[DES_KEY_LENGTH * 2]);
81d882d5
     }
 
 
     memset(phase3, 0, sizeof(phase3));       /* clear reply */
 
     strcpy((char *)phase3, "NTLMSSP\0");      /* signature */
     phase3[8] = 3;     /* type 3 */
 
     if (ntlmv2_enabled)      /* NTLMv2 response */
     {
c310f1ec
         add_security_buffer(0x14, ntlmv2_response, ntlmv2_blob_size + 16,
                             phase3, &phase3_bufpos);
81d882d5
     }
     else       /* NTLM response */
     {
         add_security_buffer(0x14, ntlm_response, 24, phase3, &phase3_bufpos);
     }
 
     /* username in ascii */
c310f1ec
     add_security_buffer(0x24, username, strlen(username), phase3,
                         &phase3_bufpos);
81d882d5
 
c310f1ec
     /* Set domain. If <domain> is empty, default domain will be used
      * (i.e. proxy's domain) */
81d882d5
     add_security_buffer(0x1c, domain, strlen(domain), phase3, &phase3_bufpos);
 
     /* other security buffers will be empty */
     phase3[0x10] = phase3_bufpos;     /* lm not used */
     phase3[0x30] = phase3_bufpos;     /* no workstation name supplied */
     phase3[0x38] = phase3_bufpos;     /* no session key */
 
     /* flags */
     phase3[0x3c] = 0x02; /* negotiate oem */
     phase3[0x3d] = 0x02; /* negotiate ntlm */
 
c310f1ec
     return ((const char *)make_base64_string2((unsigned char *)phase3,
                                               phase3_bufpos, gc));
6fbf66fa
 }
 
81d882d5
 #else  /* if NTLM */
 static void
4cd4899e
 dummy(void)
 {
81d882d5
 }
 #endif /* if NTLM */