gnutls: fix compiler warning
[platform/upstream/curl.git] / lib / vtls / gtls.c
index 7f920b2..e9f60b5 100644 (file)
@@ -51,6 +51,7 @@
 #include "connect.h" /* for the connect timeout */
 #include "select.h"
 #include "rawstr.h"
+#include "warnless.h"
 
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
@@ -369,17 +370,28 @@ gtls_connect_step1(struct connectdata *conn,
   struct in_addr addr;
 #endif
 #ifndef USE_GNUTLS_PRIORITY_SET_DIRECT
-  static int cipher_priority[] = { GNUTLS_CIPHER_AES_128_GCM,
-    GNUTLS_CIPHER_AES_256_GCM, GNUTLS_CIPHER_AES_128_CBC,
-    GNUTLS_CIPHER_AES_256_CBC, GNUTLS_CIPHER_CAMELLIA_128_CBC,
-    GNUTLS_CIPHER_CAMELLIA_256_CBC, GNUTLS_CIPHER_3DES_CBC,
+  static const int cipher_priority[] = {
+  /* These two ciphers were added to GnuTLS as late as ver. 3.0.1,
+     but this code path is only ever used for ver. < 2.12.0.
+     GNUTLS_CIPHER_AES_128_GCM,
+     GNUTLS_CIPHER_AES_256_GCM,
+  */
+    GNUTLS_CIPHER_AES_128_CBC,
+    GNUTLS_CIPHER_AES_256_CBC,
+    GNUTLS_CIPHER_CAMELLIA_128_CBC,
+    GNUTLS_CIPHER_CAMELLIA_256_CBC,
+    GNUTLS_CIPHER_3DES_CBC,
   };
   static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
   static int protocol_priority[] = { 0, 0, 0, 0 };
 #else
 #define GNUTLS_CIPHERS "NORMAL:-ARCFOUR-128:-CTYPE-ALL:+CTYPE-X509"
+/* If GnuTLS was compiled without support for SRP it will error out if SRP is
+   requested in the priority string, so treat it specially
+ */
+#define GNUTLS_SRP "+SRP"
   const char* prioritylist;
-  const char *err;
+  const char *err = NULL;
 #endif
 #ifdef HAS_ALPN
   int protocols_size = 2;
@@ -536,7 +548,15 @@ gtls_connect_step1(struct connectdata *conn,
       break;
   }
   rc = gnutls_protocol_set_priority(session, protocol_priority);
+  if(rc != GNUTLS_E_SUCCESS) {
+    failf(data, "Did you pass a valid GnuTLS cipher list?");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
 #else
+  /* Ensure +SRP comes at the *end* of all relevant strings so that it can be
+   * removed if a run-time error indicates that SRP is not supported by this
+   * GnuTLS version */
   switch (data->set.ssl.version) {
     case CURL_SSLVERSION_SSLv3:
       prioritylist = GNUTLS_CIPHERS ":-VERS-TLS-ALL:+VERS-SSL3.0";
@@ -544,19 +564,19 @@ gtls_connect_step1(struct connectdata *conn,
       break;
     case CURL_SSLVERSION_DEFAULT:
     case CURL_SSLVERSION_TLSv1:
-      prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0";
+      prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:" GNUTLS_SRP;
       break;
     case CURL_SSLVERSION_TLSv1_0:
       prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
-                     "+VERS-TLS1.0";
+                     "+VERS-TLS1.0:" GNUTLS_SRP;
       break;
     case CURL_SSLVERSION_TLSv1_1:
       prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
-                     "+VERS-TLS1.1";
+                     "+VERS-TLS1.1:" GNUTLS_SRP;
       break;
     case CURL_SSLVERSION_TLSv1_2:
       prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
-                     "+VERS-TLS1.2";
+                     "+VERS-TLS1.2:" GNUTLS_SRP;
       break;
     case CURL_SSLVERSION_SSLv2:
     default:
@@ -565,6 +585,28 @@ gtls_connect_step1(struct connectdata *conn,
       break;
   }
   rc = gnutls_priority_set_direct(session, prioritylist, &err);
+  if((rc == GNUTLS_E_INVALID_REQUEST) && err) {
+    if(!strcmp(err, GNUTLS_SRP)) {
+      /* This GnuTLS was probably compiled without support for SRP.
+       * Note that fact and try again without it. */
+      int validprioritylen = curlx_uztosi(err - prioritylist);
+      char *prioritycopy = strdup(prioritylist);
+      if(!prioritycopy)
+        return CURLE_OUT_OF_MEMORY;
+
+      infof(data, "This GnuTLS does not support SRP\n");
+      if(validprioritylen)
+        /* Remove the :+SRP */
+        prioritycopy[validprioritylen - 1] = 0;
+      rc = gnutls_priority_set_direct(session, prioritycopy, &err);
+      free(prioritycopy);
+    }
+  }
+  if(rc != GNUTLS_E_SUCCESS) {
+    failf(data, "Error %d setting GnuTLS cipher list starting with %s",
+          rc, err);
+    return CURLE_SSL_CONNECT_ERROR;
+  }
 #endif
 
 #ifdef HAS_ALPN
@@ -584,12 +626,6 @@ gtls_connect_step1(struct connectdata *conn,
   }
 #endif
 
-  if(rc != GNUTLS_E_SUCCESS) {
-    failf(data, "Did you pass a valid GnuTLS cipher list?");
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-
   if(data->set.str[STRING_CERT]) {
     if(gnutls_certificate_set_x509_key_file(
          conn->ssl[sockindex].cred,
@@ -653,7 +689,7 @@ gtls_connect_step3(struct connectdata *conn,
   unsigned int verify_status;
   gnutls_x509_crt_t x509_cert,x509_issuer;
   gnutls_datum_t issuerp;
-  char certbuf[256]; /* big enough? */
+  char certbuf[256] = ""; /* big enough? */
   size_t size;
   unsigned int algo;
   unsigned int bits;
@@ -770,7 +806,48 @@ gtls_connect_step3(struct connectdata *conn,
      alternative name PKIX extension. Returns non zero on success, and zero on
      failure. */
   rc = gnutls_x509_crt_check_hostname(x509_cert, conn->host.name);
+#if GNUTLS_VERSION_NUMBER < 0x030306
+  /* Before 3.3.6, gnutls_x509_crt_check_hostname() didn't check IP
+     addresses. */
+  if(!rc) {
+#ifdef ENABLE_IPV6
+    #define use_addr in6_addr
+#else
+    #define use_addr in_addr
+#endif
+    unsigned char addrbuf[sizeof(struct use_addr)];
+    unsigned char certaddr[sizeof(struct use_addr)];
+    size_t addrlen = 0, certaddrlen;
+    int i;
+    int ret = 0;
+
+    if(Curl_inet_pton(AF_INET, conn->host.name, addrbuf) > 0)
+      addrlen = 4;
+#ifdef ENABLE_IPV6
+    else if(Curl_inet_pton(AF_INET6, conn->host.name, addrbuf) > 0)
+      addrlen = 16;
+#endif
 
+    if(addrlen) {
+      for(i=0; ; i++) {
+        certaddrlen = sizeof(certaddr);
+        ret = gnutls_x509_crt_get_subject_alt_name(x509_cert, i, certaddr,
+                                                   &certaddrlen, NULL);
+        /* If this happens, it wasn't an IP address. */
+        if(ret == GNUTLS_E_SHORT_MEMORY_BUFFER)
+          continue;
+        if(ret < 0)
+          break;
+        if(ret != GNUTLS_SAN_IPADDRESS)
+          continue;
+        if(certaddrlen == addrlen && !memcmp(addrbuf, certaddr, addrlen)) {
+          rc = 1;
+          break;
+        }
+      }
+    }
+  }
+#endif
   if(!rc) {
     if(data->set.ssl.verifyhost) {
       failf(data, "SSL: certificate subject name (%s) does not match "
@@ -915,7 +992,7 @@ gtls_connect_step3(struct connectdata *conn,
        might've been rejected and then a new one is in use now and we need to
        detect that. */
     void *connect_sessionid;
-    size_t connect_idsize;
+    size_t connect_idsize = 0;
 
     /* get the session ID data size */
     gnutls_session_get_data(session, NULL, &connect_idsize);