Unverified Commit 9aefbff3 authored by Robert Prag's avatar Robert Prag Committed by Daniel Stenberg
Browse files

schannel: support selecting ciphers

Given the contstraints of SChannel, I'm exposing these as the algorithms
themselves instead; while replicating the ciphersuite as specified by
OpenSSL would have been preferable, I found no way in the SChannel API
to do so.

To use this from the commandline, you need to pass the names of contants
defining the desired algorithms. For example, curl --ciphers
"CALG_SHA1:CALG_RSA_SIGN:CALG_RSA_KEYX:CALG_AES_128:CALG_DH_EPHEM"
https://github.com The specific names come from wincrypt.h

Closes #2630
parent f508d29f
Loading
Loading
Loading
Loading
+51 −0
Original line number Diff line number Diff line
@@ -434,3 +434,54 @@ but libcurl maps them to the following case-insensitive names.
`ECDHE-PSK-CHACHA20-POLY1305`,
`DHE-PSK-CHACHA20-POLY1305`,
`EDH-RSA-DES-CBC3-SHA`,

## WinSSL

WinSSL allows the enabling and disabling of encryption algorithms, but not specific ciphersuites. They are defined by Microsoft (https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx)

`CALG_MD2`,
`CALG_MD4`,
`CALG_MD5`,
`CALG_SHA`,
`CALG_SHA1`,
`CALG_MAC`,
`CALG_RSA_SIGN`,
`CALG_DSS_SIGN`,
`CALG_NO_SIGN`,
`CALG_RSA_KEYX`,
`CALG_DES`,
`CALG_3DES_112`,
`CALG_3DES`,
`CALG_DESX`,
`CALG_RC2`,
`CALG_RC4`,
`CALG_SEAL`,
`CALG_DH_SF`,
`CALG_DH_EPHEM`,
`CALG_AGREEDKEY_ANY`,
`CALG_HUGHES_MD5`,
`CALG_SKIPJACK`,
`CALG_TEK`,
`CALG_CYLINK_MEK`,
`CALG_SSL3_SHAMD5`,
`CALG_SSL3_MASTER`,
`CALG_SCHANNEL_MASTER_HASH`,
`CALG_SCHANNEL_MAC_KEY`,
`CALG_SCHANNEL_ENC_KEY`,
`CALG_PCT1_MASTER`,
`CALG_SSL2_MASTER`,
`CALG_TLS1_MASTER`,
`CALG_RC5`,
`CALG_HMAC`,
`CALG_TLS1PRF`,
`CALG_HASH_REPLACE_OWF`,
`CALG_AES_128`,
`CALG_AES_192`,
`CALG_AES_256`,
`CALG_AES`,
`CALG_SHA_256`,
`CALG_SHA_384`,
`CALG_SHA_512`,
`CALG_ECDH`,
`CALG_ECMQV`,
`CALG_ECDSA`,
+121 −0
Original line number Diff line number Diff line
@@ -205,6 +205,118 @@ set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct connectdata *conn)
  return CURLE_OK;
}

/*longest is 26, buffer is slightly bigger*/
#define LONGEST_ALG_ID 32
#define CIPHEROPTION(X) \
if(strcmp(#X, tmp) == 0) \
  return X

static int
get_alg_id_by_name(char *name)
{
  char tmp[LONGEST_ALG_ID] = { 0 };
  char *nameEnd = strchr(name, ':');
  size_t n = nameEnd ? min(nameEnd - name, LONGEST_ALG_ID - 1) : \
    min(strlen(name), LONGEST_ALG_ID - 1);
  strncpy(tmp, name, n);
  tmp[n] = 0;
  CIPHEROPTION(CALG_MD2);
  CIPHEROPTION(CALG_MD4);
  CIPHEROPTION(CALG_MD5);
  CIPHEROPTION(CALG_SHA);
  CIPHEROPTION(CALG_SHA1);
  CIPHEROPTION(CALG_MAC);
  CIPHEROPTION(CALG_RSA_SIGN);
  CIPHEROPTION(CALG_DSS_SIGN);
/*ifdefs for the options that are defined conditionally in wincrypt.h*/
#ifdef CALG_NO_SIGN
  CIPHEROPTION(CALG_NO_SIGN);
#endif
  CIPHEROPTION(CALG_RSA_KEYX);
  CIPHEROPTION(CALG_DES);
  CIPHEROPTION(CALG_3DES_112);
  CIPHEROPTION(CALG_3DES);
  CIPHEROPTION(CALG_DESX);
  CIPHEROPTION(CALG_RC2);
  CIPHEROPTION(CALG_RC4);
  CIPHEROPTION(CALG_SEAL);
  CIPHEROPTION(CALG_DH_SF);
  CIPHEROPTION(CALG_DH_EPHEM);
  CIPHEROPTION(CALG_AGREEDKEY_ANY);
  CIPHEROPTION(CALG_HUGHES_MD5);
  CIPHEROPTION(CALG_SKIPJACK);
  CIPHEROPTION(CALG_TEK);
  CIPHEROPTION(CALG_CYLINK_MEK);
  CIPHEROPTION(CALG_SSL3_SHAMD5);
  CIPHEROPTION(CALG_SSL3_MASTER);
  CIPHEROPTION(CALG_SCHANNEL_MASTER_HASH);
  CIPHEROPTION(CALG_SCHANNEL_MAC_KEY);
  CIPHEROPTION(CALG_SCHANNEL_ENC_KEY);
  CIPHEROPTION(CALG_PCT1_MASTER);
  CIPHEROPTION(CALG_SSL2_MASTER);
  CIPHEROPTION(CALG_TLS1_MASTER);
  CIPHEROPTION(CALG_RC5);
  CIPHEROPTION(CALG_HMAC);
  CIPHEROPTION(CALG_TLS1PRF);
#ifdef CALG_HASH_REPLACE_OWF
  CIPHEROPTION(CALG_HASH_REPLACE_OWF);
#endif
#ifdef CALG_AES_128
  CIPHEROPTION(CALG_AES_128);
#endif
#ifdef CALG_AES_192
  CIPHEROPTION(CALG_AES_192);
#endif
#ifdef CALG_AES_256
  CIPHEROPTION(CALG_AES_256);
#endif
#ifdef CALG_AES
  CIPHEROPTION(CALG_AES);
#endif
#ifdef CALG_SHA_256
  CIPHEROPTION(CALG_SHA_256);
#endif
#ifdef CALG_SHA_384
  CIPHEROPTION(CALG_SHA_384);
#endif
#ifdef CALG_SHA_512
  CIPHEROPTION(CALG_SHA_512);
#endif
#ifdef CALG_ECDH
  CIPHEROPTION(CALG_ECDH);
#endif
#ifdef CALG_ECMQV
  CIPHEROPTION(CALG_ECMQV);
#endif
#ifdef CALG_ECDSA
  CIPHEROPTION(CALG_ECDSA);
#endif
  return 0;
}

static CURLcode
set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers)
{
  char *startCur = ciphers;
  int algCount = 0;
  static ALG_ID algIds[45]; /*There are 45 listed in the MS headers*/
  while(startCur && (0 != *startCur) && (algCount < 45)) {
    long alg = strtol(startCur, 0, 0);
    if(!alg)
      alg = get_alg_id_by_name(startCur);
    if(alg)
      algIds[algCount++] = alg;
    else
      return CURLE_SSL_CIPHER;
    startCur = strchr(startCur, ':');
    if(startCur)
      startCur++;
  }
    schannel_cred->palgSupportedAlgs = algIds;
  schannel_cred->cSupportedAlgs = algCount;
  return CURLE_OK;
}

#ifdef HAS_CLIENT_CERT_PATH
static CURLcode
get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path,
@@ -422,6 +534,15 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
      return CURLE_SSL_CONNECT_ERROR;
    }

    if(SSL_CONN_CONFIG(cipher_list)) {
      result = set_ssl_ciphers(&schannel_cred, SSL_CONN_CONFIG(cipher_list));
      if(CURLE_OK != result) {
        failf(data, "Unable to set ciphers to passed via SSL_CONN_CONFIG");
        return result;
      }
    }


#ifdef HAS_CLIENT_CERT_PATH
    /* client certificate */
    if(data->set.ssl.cert) {