Base code merged to SPIN 2.4
[platform/upstream/curl.git] / lib / vtls / vtls.c
index 6c2295a..cf1df24 100644 (file)
@@ -31,7 +31,6 @@
    Curl_ossl_ - prefix for OpenSSL ones
    Curl_gtls_ - prefix for GnuTLS ones
    Curl_nss_ - prefix for NSS ones
-   Curl_qssl_ - prefix for QsoSSL ones
    Curl_gskit_ - prefix for GSKit ones
    Curl_polarssl_ - prefix for PolarSSL ones
    Curl_cyassl_ - prefix for CyaSSL ones
 #include "urldata.h"
 
 #include "vtls.h" /* generic SSL protos etc */
-#include "openssl.h" /* OpenSSL versions */
-#include "gtls.h"   /* GnuTLS versions */
-#include "nssg.h"   /* NSS versions */
-#include "qssl.h"   /* QSOSSL versions */
-#include "gskit.h"  /* Global Secure ToolKit versions */
-#include "polarssl.h" /* PolarSSL versions */
-#include "axtls.h"  /* axTLS versions */
-#include "cyassl.h"  /* CyaSSL versions */
-#include "curl_schannel.h" /* Schannel SSPI version */
-#include "curl_darwinssl.h" /* SecureTransport (Darwin) version */
 #include "slist.h"
 #include "sendf.h"
 #include "rawstr.h"
@@ -78,6 +67,9 @@
 #include "progress.h"
 #include "share.h"
 #include "timeval.h"
+#include "curl_md5.h"
+#include "warnless.h"
+#include "curl_base64.h"
 
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
@@ -193,7 +185,7 @@ void Curl_free_ssl_config(struct ssl_config_data* sslc)
 
 unsigned int Curl_rand(struct SessionHandle *data)
 {
-  unsigned int r;
+  unsigned int r = 0;
   static unsigned int randseed;
   static bool seeded = FALSE;
 
@@ -213,14 +205,12 @@ unsigned int Curl_rand(struct SessionHandle *data)
   }
 #endif
 
-#ifndef have_curlssl_random
-  (void)data;
-#else
-  if(data) {
-    curlssl_random(data, (unsigned char *)&r, sizeof(r));
+  /* data may be NULL! */
+  if(!Curl_ssl_random(data, (unsigned char *)&r, sizeof(r)))
     return r;
-  }
-#endif
+
+  /* If Curl_ssl_random() returns non-zero it couldn't offer randomness and we
+     instead perform a "best effort" */
 
 #ifdef RANDOM_FILE
   if(!seeded) {
@@ -238,6 +228,7 @@ unsigned int Curl_rand(struct SessionHandle *data)
 
   if(!seeded) {
     struct timeval now = curlx_tvnow();
+    infof(data, "WARNING: Using weak random seed\n");
     randseed += (unsigned int)now.tv_usec + (unsigned int)now.tv_sec;
     randseed = randseed * 1103515245 + 12345;
     randseed = randseed * 1103515245 + 12345;
@@ -250,6 +241,11 @@ unsigned int Curl_rand(struct SessionHandle *data)
   return (r << 16) | ((r >> 16) & 0xFFFF);
 }
 
+int Curl_ssl_backend(void)
+{
+  return (int)CURL_SSL_BACKEND;
+}
+
 #ifdef USE_SSL
 
 /* "global" init done? */
@@ -285,44 +281,44 @@ void Curl_ssl_cleanup(void)
 CURLcode
 Curl_ssl_connect(struct connectdata *conn, int sockindex)
 {
-  CURLcode res;
+  CURLcode result;
   /* mark this is being ssl-enabled from here on. */
   conn->ssl[sockindex].use = TRUE;
   conn->ssl[sockindex].state = ssl_connection_negotiating;
 
-  res = curlssl_connect(conn, sockindex);
+  result = curlssl_connect(conn, sockindex);
 
-  if(!res)
+  if(!result)
     Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */
 
-  return res;
+  return result;
 }
 
 CURLcode
 Curl_ssl_connect_nonblocking(struct connectdata *conn, int sockindex,
                              bool *done)
 {
-  CURLcode res;
+  CURLcode result;
   /* mark this is being ssl requested from here on. */
   conn->ssl[sockindex].use = TRUE;
 #ifdef curlssl_connect_nonblocking
-  res = curlssl_connect_nonblocking(conn, sockindex, done);
+  result = curlssl_connect_nonblocking(conn, sockindex, done);
 #else
   *done = TRUE; /* fallback to BLOCKING */
-  res = curlssl_connect(conn, sockindex);
+  result = curlssl_connect(conn, sockindex);
 #endif /* non-blocking connect support */
-  if(!res && *done)
+  if(!result && *done)
     Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */
-  return res;
+  return result;
 }
 
 /*
  * Check if there's a session ID for the given connection in the cache, and if
  * there's one suitable, it is provided. Returns TRUE when no entry matched.
  */
-int Curl_ssl_getsessionid(struct connectdata *conn,
-                          void **ssl_sessionid,
-                          size_t *idsize) /* set 0 if unknown */
+bool Curl_ssl_getsessionid(struct connectdata *conn,
+                           void **ssl_sessionid,
+                           size_t *idsize) /* set 0 if unknown */
 {
   struct curl_ssl_session *check;
   struct SessionHandle *data = conn->data;
@@ -597,34 +593,37 @@ void Curl_ssl_free_certinfo(struct SessionHandle *data)
 {
   int i;
   struct curl_certinfo *ci = &data->info.certs;
+
   if(ci->num_of_certs) {
     /* free all individual lists used */
     for(i=0; i<ci->num_of_certs; i++) {
       curl_slist_free_all(ci->certinfo[i]);
       ci->certinfo[i] = NULL;
     }
+
     free(ci->certinfo); /* free the actual array too */
     ci->certinfo = NULL;
     ci->num_of_certs = 0;
   }
 }
 
-int Curl_ssl_init_certinfo(struct SessionHandle * data,
-                           int num)
+CURLcode Curl_ssl_init_certinfo(struct SessionHandle *data, int num)
 {
-  struct curl_certinfo * ci = &data->info.certs;
-  struct curl_slist * * table;
+  struct curl_certinfo *ci = &data->info.certs;
+  struct curl_slist **table;
 
-  /* Initialize the certificate information structures. Return 0 if OK, else 1.
-   */
+  /* Free any previous certificate information structures */
   Curl_ssl_free_certinfo(data);
-  ci->num_of_certs = num;
+
+  /* Allocate the required certificate information structures */
   table = calloc((size_t) num, sizeof(struct curl_slist *));
   if(!table)
-    return 1;
+    return CURLE_OUT_OF_MEMORY;
 
+  ci->num_of_certs = num;
   ci->certinfo = table;
-  return 0;
+
+  return CURLE_OK;
 }
 
 /*
@@ -639,7 +638,7 @@ CURLcode Curl_ssl_push_certinfo_len(struct SessionHandle *data,
   struct curl_certinfo * ci = &data->info.certs;
   char * output;
   struct curl_slist * nl;
-  CURLcode res = CURLE_OK;
+  CURLcode result = CURLE_OK;
   size_t labellen = strlen(label);
   size_t outlen = labellen + 1 + valuelen + 1; /* label:value\0 */
 
@@ -660,11 +659,11 @@ CURLcode Curl_ssl_push_certinfo_len(struct SessionHandle *data,
   if(!nl) {
     free(output);
     curl_slist_free_all(ci->certinfo[certnum]);
-    res = CURLE_OUT_OF_MEMORY;
+    result = CURLE_OUT_OF_MEMORY;
   }
 
   ci->certinfo[certnum] = nl;
-  return res;
+  return result;
 }
 
 /*
@@ -681,14 +680,184 @@ CURLcode Curl_ssl_push_certinfo(struct SessionHandle *data,
   return Curl_ssl_push_certinfo_len(data, certnum, label, value, valuelen);
 }
 
-#ifdef have_curlssl_md5sum
+int Curl_ssl_random(struct SessionHandle *data,
+                     unsigned char *entropy,
+                     size_t length)
+{
+  return curlssl_random(data, entropy, length);
+}
+
+/*
+ * Public key pem to der conversion
+ */
+
+static CURLcode pubkey_pem_to_der(const char *pem,
+                                  unsigned char **der, size_t *der_len)
+{
+  char *stripped_pem, *begin_pos, *end_pos;
+  size_t pem_count, stripped_pem_count = 0, pem_len;
+  CURLcode result;
+
+  /* if no pem, exit. */
+  if(!pem)
+    return CURLE_BAD_CONTENT_ENCODING;
+
+  begin_pos = strstr(pem, "-----BEGIN PUBLIC KEY-----");
+  if(!begin_pos)
+    return CURLE_BAD_CONTENT_ENCODING;
+
+  pem_count = begin_pos - pem;
+  /* Invalid if not at beginning AND not directly following \n */
+  if(0 != pem_count && '\n' != pem[pem_count - 1])
+    return CURLE_BAD_CONTENT_ENCODING;
+
+  /* 26 is length of "-----BEGIN PUBLIC KEY-----" */
+  pem_count += 26;
+
+  /* Invalid if not directly following \n */
+  end_pos = strstr(pem + pem_count, "\n-----END PUBLIC KEY-----");
+  if(!end_pos)
+    return CURLE_BAD_CONTENT_ENCODING;
+
+  pem_len = end_pos - pem;
+
+  stripped_pem = malloc(pem_len - pem_count + 1);
+  if(!stripped_pem)
+    return CURLE_OUT_OF_MEMORY;
+
+  /*
+   * Here we loop through the pem array one character at a time between the
+   * correct indices, and place each character that is not '\n' or '\r'
+   * into the stripped_pem array, which should represent the raw base64 string
+   */
+  while(pem_count < pem_len) {
+    if('\n' != pem[pem_count] && '\r' != pem[pem_count])
+      stripped_pem[stripped_pem_count++] = pem[pem_count];
+    ++pem_count;
+  }
+  /* Place the null terminator in the correct place */
+  stripped_pem[stripped_pem_count] = '\0';
+
+  result = Curl_base64_decode(stripped_pem, der, der_len);
+
+  Curl_safefree(stripped_pem);
+
+  return result;
+}
+
+/*
+ * Generic pinned public key check.
+ */
+
+CURLcode Curl_pin_peer_pubkey(const char *pinnedpubkey,
+                              const unsigned char *pubkey, size_t pubkeylen)
+{
+  FILE *fp;
+  unsigned char *buf = NULL, *pem_ptr = NULL;
+  long filesize;
+  size_t size, pem_len;
+  CURLcode pem_read;
+  CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+
+  /* if a path wasn't specified, don't pin */
+  if(!pinnedpubkey)
+    return CURLE_OK;
+  if(!pubkey || !pubkeylen)
+    return result;
+  fp = fopen(pinnedpubkey, "rb");
+  if(!fp)
+    return result;
+
+  do {
+    /* Determine the file's size */
+    if(fseek(fp, 0, SEEK_END))
+      break;
+    filesize = ftell(fp);
+    if(fseek(fp, 0, SEEK_SET))
+      break;
+    if(filesize < 0 || filesize > MAX_PINNED_PUBKEY_SIZE)
+      break;
+
+    /*
+     * if the size of our certificate is bigger than the file
+     * size then it can't match
+     */
+    size = curlx_sotouz((curl_off_t) filesize);
+    if(pubkeylen > size)
+      break;
+
+    /*
+     * Allocate buffer for the pinned key
+     * With 1 additional byte for null terminator in case of PEM key
+     */
+    buf = malloc(size + 1);
+    if(!buf)
+      break;
+
+    /* Returns number of elements read, which should be 1 */
+    if((int) fread(buf, size, 1, fp) != 1)
+      break;
+
+    /* If the sizes are the same, it can't be base64 encoded, must be der */
+    if(pubkeylen == size) {
+      if(!memcmp(pubkey, buf, pubkeylen))
+        result = CURLE_OK;
+      break;
+    }
+
+    /*
+     * Otherwise we will assume it's PEM and try to decode it
+     * after placing null terminator
+     */
+    buf[size] = '\0';
+    pem_read = pubkey_pem_to_der((const char *)buf, &pem_ptr, &pem_len);
+    /* if it wasn't read successfully, exit */
+    if(pem_read)
+      break;
+
+    /*
+     * if the size of our certificate doesn't match the size of
+     * the decoded file, they can't be the same, otherwise compare
+     */
+    if(pubkeylen == pem_len && !memcmp(pubkey, pem_ptr, pubkeylen))
+      result = CURLE_OK;
+  } while(0);
+
+  Curl_safefree(buf);
+  Curl_safefree(pem_ptr);
+  fclose(fp);
+
+  return result;
+}
+
 void Curl_ssl_md5sum(unsigned char *tmp, /* input */
                      size_t tmplen,
                      unsigned char *md5sum, /* output */
                      size_t md5len)
 {
+#ifdef curlssl_md5sum
   curlssl_md5sum(tmp, tmplen, md5sum, md5len);
+#else
+  MD5_context *MD5pw;
+
+  (void) md5len;
+
+  MD5pw = Curl_MD5_init(Curl_DIGEST_MD5);
+  Curl_MD5_update(MD5pw, tmp, curlx_uztoui(tmplen));
+  Curl_MD5_final(MD5pw, md5sum);
+#endif
 }
+
+/*
+ * Check whether the SSL backend supports the status_request extension.
+ */
+bool Curl_ssl_cert_status_request(void)
+{
+#ifdef curlssl_cert_status_request
+  return curlssl_cert_status_request();
+#else
+  return FALSE;
 #endif
+}
 
 #endif /* USE_SSL */