Commit 135f6940 authored by Yang Tse's avatar Yang Tse
Browse files

NTLM: move NTLM core specifics into curl_ntlm_core.[ch]

parent 260ee6b7
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -22,7 +22,8 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
  pingpong.c rtsp.c curl_threads.c warnless.c hmac.c polarssl.c		\
  curl_rtmp.c openldap.c curl_gethostname.c gopher.c axtls.c		\
  idn_win32.c http_negotiate_sspi.c cyassl.c http_proxy.c non-ascii.c	\
  asyn-ares.c asyn-thread.c curl_gssapi.c curl_ntlm.c curl_ntlm_wb.c
  asyn-ares.c asyn-thread.c curl_gssapi.c curl_ntlm.c curl_ntlm_wb.c	\
  curl_ntlm_core.c

HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h	\
  progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h	\
@@ -38,4 +39,4 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h \
  curl_memrchr.h imap.h pop3.h smtp.h pingpong.h rtsp.h curl_threads.h	\
  warnless.h curl_hmac.h polarssl.h curl_rtmp.h curl_gethostname.h	\
  gopher.h axtls.h cyassl.h http_proxy.h non-ascii.h asyn.h curl_ntlm.h \
  curl_gssapi.h curl_ntlm_wb.h
  curl_gssapi.h curl_ntlm_wb.h curl_ntlm_core.h
+1 −0
Original line number Diff line number Diff line
@@ -507,6 +507,7 @@ X_OBJS= \
	$(DIROBJ)\curl_gethostname.obj \
	$(DIROBJ)\curl_memrchr.obj \
	$(DIROBJ)\curl_ntlm.obj \
	$(DIROBJ)\curl_ntlm_core.obj \
	$(DIROBJ)\curl_ntlm_wb.obj \
	$(DIROBJ)\curl_rand.obj \
	$(DIROBJ)\curl_rtmp.obj \
+24 −328
Original line number Diff line number Diff line
@@ -35,12 +35,6 @@
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif

#if (defined(NETWARE) && !defined(__NOVELL_LIBC__))
#include <netdb.h>
@@ -49,7 +43,7 @@
#define BUILDING_CURL_NTLM_C

#include "urldata.h"
#include "non-ascii.h"  /* for Curl_convert_... prototypes */
#include "non-ascii.h"
#include "sendf.h"
#include "select.h"
#include "rawstr.h"
@@ -87,22 +81,6 @@
#error "OPENSSL_VERSION_NUMBER not defined"
#endif

#if OPENSSL_VERSION_NUMBER < 0x00907001L
#define DES_key_schedule des_key_schedule
#define DES_cblock des_cblock
#define DES_set_odd_parity des_set_odd_parity
#define DES_set_key des_set_key
#define DES_ecb_encrypt des_ecb_encrypt

/* This is how things were done in the old days */
#define DESKEY(x) x
#define DESKEYARG(x) x
#else
/* Modern version */
#define DESKEYARG(x) *x
#define DESKEY(x) &x
#endif

#ifdef OPENSSL_NO_MD4
/* This requires MD4, but OpenSSL was compiled without it */
#define USE_NTRESPONSES 0
@@ -134,15 +112,6 @@
#    error "Can't compile NTLM support without a crypto library."
#endif

/* The last #include file should be: */
#include "memdebug.h"

/* Hostname buffer size */
#define HOSTNAME_MAX 1024

/* "NTLMSSP" signature is always in ASCII regardless of the platform */
#define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50"

#ifndef USE_NTRESPONSES
/* Define this to make the type-3 message include the NT response message */
#define USE_NTRESPONSES 1
@@ -152,6 +121,17 @@
#define USE_NTLM2SESSION 1
#endif

#include "curl_ntlm_core.h"

/* The last #include file should be: */
#include "memdebug.h"

/* Hostname buffer size */
#define HOSTNAME_MAX 1024

/* "NTLMSSP" signature is always in ASCII regardless of the platform */
#define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50"

#define SHORTPAIR(x) ((x) & 0xff), (((x) >> 8) & 0xff)
#define LONGQUARTET(x) ((x) & 0xff), (((x) >> 8) & 0xff), \
  (((x) >> 16) & 0xff), (((x) >> 24) & 0xff)
@@ -229,6 +209,7 @@ static void ntlm_print_flags(FILE *handle, unsigned long flags)
static void ntlm_print_hex(FILE *handle, const char *buf, size_t len)
{
  const char *p = buf;
  (void)handle;
  fprintf(stderr, "0x");
  while(len-- > 0)
    fprintf(stderr, "%02.2x", (unsigned int)*p++);
@@ -360,293 +341,6 @@ CURLcode Curl_ntlm_decode_type2_message(struct SessionHandle *data,
  return CURLE_OK;
}

#ifndef USE_WINDOWS_SSPI

#ifdef USE_SSLEAY
/*
 * Turns a 56 bit key into the 64 bit, odd parity key and sets the key.  The
 * key schedule ks is also set.
 */
static void setup_des_key(const unsigned char *key_56,
                          DES_key_schedule DESKEYARG(ks))
{
  DES_cblock key;

  key[0] = key_56[0];
  key[1] = (unsigned char)(((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1));
  key[2] = (unsigned char)(((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2));
  key[3] = (unsigned char)(((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3));
  key[4] = (unsigned char)(((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4));
  key[5] = (unsigned char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5));
  key[6] = (unsigned char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6));
  key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF);

  DES_set_odd_parity(&key);
  DES_set_key(&key, ks);
}

#else /* defined(USE_SSLEAY) */

/*
 * Turns a 56 bit key into the 64 bit, odd parity key.  Used by GnuTLS and NSS.
 */
static void extend_key_56_to_64(const unsigned char *key_56, char *key)
{
  key[0] = key_56[0];
  key[1] = (unsigned char)(((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1));
  key[2] = (unsigned char)(((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2));
  key[3] = (unsigned char)(((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3));
  key[4] = (unsigned char)(((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4));
  key[5] = (unsigned char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5));
  key[6] = (unsigned char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6));
  key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF);
}

#if defined(USE_GNUTLS)

/*
 * Turns a 56 bit key into the 64 bit, odd parity key and sets the key.
 */
static void setup_des_key(const unsigned char *key_56,
                          gcry_cipher_hd_t *des)
{
  char key[8];
  extend_key_56_to_64(key_56, key);
  gcry_cipher_setkey(*des, key, 8);
}

#elif defined(USE_NSS)

/*
 * Expands a 56 bit key KEY_56 to 64 bit and encrypts 64 bit of data, using
 * the expanded key.  The caller is responsible for giving 64 bit of valid
 * data is IN and (at least) 64 bit large buffer as OUT.
 */
static bool encrypt_des(const unsigned char *in, unsigned char *out,
                        const unsigned char *key_56)
{
  const CK_MECHANISM_TYPE mech = CKM_DES_ECB; /* DES cipher in ECB mode */
  PK11SlotInfo *slot = NULL;
  char key[8];                                /* expanded 64 bit key */
  SECItem key_item;
  PK11SymKey *symkey = NULL;
  SECItem *param = NULL;
  PK11Context *ctx = NULL;
  int out_len;                                /* not used, required by NSS */
  bool rv = FALSE;

  /* use internal slot for DES encryption (requires NSS to be initialized) */
  slot = PK11_GetInternalKeySlot();
  if(!slot)
    return FALSE;

  /* expand the 56 bit key to 64 bit and wrap by NSS */
  extend_key_56_to_64(key_56, key);
  key_item.data = (unsigned char *)key;
  key_item.len = /* hard-wired */ 8;
  symkey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, CKA_ENCRYPT,
                             &key_item, NULL);
  if(!symkey)
    goto fail;

  /* create DES encryption context */
  param = PK11_ParamFromIV(mech, /* no IV in ECB mode */ NULL);
  if(!param)
    goto fail;
  ctx = PK11_CreateContextBySymKey(mech, CKA_ENCRYPT, symkey, param);
  if(!ctx)
    goto fail;

  /* perform the encryption */
  if(SECSuccess == PK11_CipherOp(ctx, out, &out_len, /* outbuflen */ 8,
                                 (unsigned char *)in, /* inbuflen */ 8)
      && SECSuccess == PK11_Finalize(ctx))
    rv = /* all OK */ TRUE;

fail:
  /* cleanup */
  if(ctx)
    PK11_DestroyContext(ctx, PR_TRUE);
  if(symkey)
    PK11_FreeSymKey(symkey);
  if(param)
    SECITEM_FreeItem(param, PR_TRUE);
  PK11_FreeSlot(slot);
  return rv;
}

#endif /* defined(USE_NSS) */

#endif /* defined(USE_SSLEAY) */

 /*
  * takes a 21 byte array and treats it as 3 56-bit DES keys. The
  * 8 byte plaintext is encrypted with each key and the resulting 24
  * bytes are stored in the results array.
  */
static void lm_resp(const unsigned char *keys,
                    const unsigned char *plaintext,
                    unsigned char *results)
{
#ifdef USE_SSLEAY
  DES_key_schedule ks;

  setup_des_key(keys, DESKEY(ks));
  DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) results,
                  DESKEY(ks), DES_ENCRYPT);

  setup_des_key(keys + 7, DESKEY(ks));
  DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results + 8),
                  DESKEY(ks), DES_ENCRYPT);

  setup_des_key(keys + 14, DESKEY(ks));
  DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results + 16),
                  DESKEY(ks), DES_ENCRYPT);
#elif defined(USE_GNUTLS)
  gcry_cipher_hd_t des;

  gcry_cipher_open(&des, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
  setup_des_key(keys, &des);
  gcry_cipher_encrypt(des, results, 8, plaintext, 8);
  gcry_cipher_close(des);

  gcry_cipher_open(&des, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
  setup_des_key(keys + 7, &des);
  gcry_cipher_encrypt(des, results + 8, 8, plaintext, 8);
  gcry_cipher_close(des);

  gcry_cipher_open(&des, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
  setup_des_key(keys + 14, &des);
  gcry_cipher_encrypt(des, results + 16, 8, plaintext, 8);
  gcry_cipher_close(des);
#elif defined(USE_NSS)
  encrypt_des(plaintext, results, keys);
  encrypt_des(plaintext, results + 8, keys + 7);
  encrypt_des(plaintext, results + 16, keys + 14);
#endif
}

/*
 * Set up lanmanager hashed password
 */
static void mk_lm_hash(struct SessionHandle *data,
                       const char *password,
                       unsigned char *lmbuffer /* 21 bytes */)
{
  CURLcode res;
  unsigned char pw[14];
  static const unsigned char magic[] = {
    0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 /* i.e. KGS!@#$% */
  };
  size_t len = CURLMIN(strlen(password), 14);

  Curl_strntoupper((char *)pw, password, len);
  memset(&pw[len], 0, 14 - len);

  /*
   * The LanManager hashed password needs to be created using the
   * password in the network encoding not the host encoding.
   */
  res = Curl_convert_to_network(data, (char *)pw, 14);
  if(res)
    return;

  {
    /* Create LanManager hashed password. */

#ifdef USE_SSLEAY
    DES_key_schedule ks;

    setup_des_key(pw, DESKEY(ks));
    DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)lmbuffer,
                    DESKEY(ks), DES_ENCRYPT);

    setup_des_key(pw + 7, DESKEY(ks));
    DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)(lmbuffer + 8),
                    DESKEY(ks), DES_ENCRYPT);
#elif defined(USE_GNUTLS)
    gcry_cipher_hd_t des;

    gcry_cipher_open(&des, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
    setup_des_key(pw, &des);
    gcry_cipher_encrypt(des, lmbuffer, 8, magic, 8);
    gcry_cipher_close(des);

    gcry_cipher_open(&des, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
    setup_des_key(pw + 7, &des);
    gcry_cipher_encrypt(des, lmbuffer + 8, 8, magic, 8);
    gcry_cipher_close(des);
#elif defined(USE_NSS)
    encrypt_des(magic, lmbuffer, pw);
    encrypt_des(magic, lmbuffer + 8, pw + 7);
#endif

    memset(lmbuffer + 16, 0, 21 - 16);
  }
}

#if USE_NTRESPONSES
static void ascii_to_unicode_le(unsigned char *dest, const char *src,
                                size_t srclen)
{
  size_t i;
  for(i = 0; i < srclen; i++) {
    dest[2 * i] = (unsigned char)src[i];
    dest[2 * i + 1] = '\0';
  }
}

/*
 * Set up nt hashed passwords
 */
static CURLcode mk_nt_hash(struct SessionHandle *data,
                           const char *password,
                           unsigned char *ntbuffer /* 21 bytes */)
{
  size_t len = strlen(password);
  unsigned char *pw = malloc(len * 2);
  CURLcode result;
  if(!pw)
    return CURLE_OUT_OF_MEMORY;

  ascii_to_unicode_le(pw, password, len);

  /*
   * The NT hashed password needs to be created using the password in the
   * network encoding not the host encoding.
   */
  result = Curl_convert_to_network(data, (char *)pw, len * 2);
  if(result)
    return result;

  {
    /* Create NT hashed password. */
#ifdef USE_SSLEAY
    MD4_CTX MD4pw;
    MD4_Init(&MD4pw);
    MD4_Update(&MD4pw, pw, 2 * len);
    MD4_Final(ntbuffer, &MD4pw);
#elif defined(USE_GNUTLS)
    gcry_md_hd_t MD4pw;
    gcry_md_open(&MD4pw, GCRY_MD_MD4, 0);
    gcry_md_write(MD4pw, pw, 2 * len);
    memcpy (ntbuffer, gcry_md_read (MD4pw, 0), MD4_DIGEST_LENGTH);
    gcry_md_close(MD4pw);
#elif defined(USE_NSS)
    Curl_md4it(ntbuffer, pw, 2 * len);
#endif

    memset(ntbuffer + 16, 0, 21 - 16);
  }

  free(pw);

  return CURLE_OK;
}
#endif

#endif

#ifdef USE_WINDOWS_SSPI
void Curl_ntlm_sspi_cleanup(struct ntlmdata *ntlm)
{
@@ -1096,11 +790,12 @@ CURLcode Curl_ntlm_create_type3_message(struct SessionHandle *data,
    PK11_DestroyContext(MD5pw, PR_TRUE);
#endif

    /* We shall only use the first 8 bytes of md5sum,
       but the des code in lm_resp only encrypt the first 8 bytes */
    if(mk_nt_hash(data, passwdp, ntbuffer) == CURLE_OUT_OF_MEMORY)
    /* We shall only use the first 8 bytes of md5sum, but the des
       code in Curl_ntlm_core_lm_resp only encrypt the first 8 bytes */
    if(CURLE_OUT_OF_MEMORY ==
       Curl_ntlm_core_mk_nt_hash(data, passwdp, ntbuffer))
      return CURLE_OUT_OF_MEMORY;
    lm_resp(ntbuffer, md5sum, ntresp);
    Curl_ntlm_core_lm_resp(ntbuffer, md5sum, ntresp);

    /* End of NTLM2 Session code */
  }
@@ -1114,15 +809,16 @@ CURLcode Curl_ntlm_create_type3_message(struct SessionHandle *data,
    unsigned char lmbuffer[0x18];

#if USE_NTRESPONSES
    if(mk_nt_hash(data, passwdp, ntbuffer) == CURLE_OUT_OF_MEMORY)
    if(CURLE_OUT_OF_MEMORY ==
       Curl_ntlm_core_mk_nt_hash(data, passwdp, ntbuffer))
      return CURLE_OUT_OF_MEMORY;
    lm_resp(ntbuffer, &ntlm->nonce[0], ntresp);
    Curl_ntlm_core_lm_resp(ntbuffer, &ntlm->nonce[0], ntresp);
#endif

    mk_lm_hash(data, passwdp, lmbuffer);
    lm_resp(lmbuffer, &ntlm->nonce[0], lmresp);
    Curl_ntlm_core_mk_lm_hash(data, passwdp, lmbuffer);
    Curl_ntlm_core_lm_resp(lmbuffer, &ntlm->nonce[0], lmresp);
    /* A safer but less compatible alternative is:
     *   lm_resp(ntbuffer, &ntlm->nonce[0], lmresp);
     *   Curl_ntlm_core_lm_resp(ntbuffer, &ntlm->nonce[0], lmresp);
     * See http://davenport.sourceforge.net/ntlm.html#ntlmVersion2 */
  }

lib/curl_ntlm_core.c

0 → 100644
+412 −0

File added.

Preview size limit exceeded, changes collapsed.

lib/curl_ntlm_core.h

0 → 100644
+43 −0
Original line number Diff line number Diff line
#ifndef HEADER_CURL_NTLM_CORE_H
#define HEADER_CURL_NTLM_CORE_H
/***************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
 * are also available at http://curl.haxx.se/docs/copyright.html.
 *
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 * copies of the Software, and permit persons to whom the Software is
 * furnished to do so, under the terms of the COPYING file.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ***************************************************************************/

#ifdef USE_NTLM

void Curl_ntlm_core_lm_resp(const unsigned char *keys,
                            const unsigned char *plaintext,
                            unsigned char *results);

void Curl_ntlm_core_mk_lm_hash(struct SessionHandle *data,
                               const char *password,
                               unsigned char *lmbuffer /* 21 bytes */);

#if !defined(USE_WINDOWS_SSPI) && (USE_NTRESPONSES != 0)
CURLcode Curl_ntlm_core_mk_nt_hash(struct SessionHandle *data,
                                   const char *password,
                                   unsigned char *ntbuffer /* 21 bytes */);
#endif

#endif /* USE_NTLM */

#endif /* HEADER_CURL_NTLM_CORE_H */
Loading