Skip to content
ssluse.c 59 KiB
Newer Older
   Certification Authorities are encouraged to use the dNSName instead.

   Matching is performed using the matching rules specified by
   [RFC2459].  If more than one identity of a given type is present in
   the certificate (e.g., more than one dNSName name, a match in any one
   of the set is considered acceptable.) Names may contain the wildcard
   character * which is considered to match any single domain name
   component or component fragment. E.g., *.a.com matches foo.a.com but
   not bar.foo.a.com. f*.com matches foo.com but not bar.com.

   In some cases, the URI is specified as an IP address rather than a
   hostname. In this case, the iPAddress subjectAltName must be present
   in the certificate and must exactly match the IP in the URI.
static CURLcode verifyhost(struct connectdata *conn,
                           X509 *server_cert)
  bool matched = FALSE; /* no alternative match yet */
  int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */
  struct SessionHandle *data = conn->data;
  STACK_OF(GENERAL_NAME) *altnames;
#ifdef ENABLE_IPV6
  struct in6_addr addr;
#else
  struct in_addr addr;
#endif
     Curl_inet_pton(AF_INET6, conn->host.name, &addr)) {
    target = GEN_IPADD;
    addrlen = sizeof(struct in6_addr);
    if(Curl_inet_pton(AF_INET, conn->host.name, &addr)) {
      target = GEN_IPADD;
      addrlen = sizeof(struct in_addr);
  /* get a "list" of alternative names */
  altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL);
  if(altnames) {
    int numalts;
    int i;
    /* get amount of alternatives, RFC2459 claims there MUST be at least
       one, but we don't depend on it... */
    numalts = sk_GENERAL_NAME_num(altnames);

    /* loop through all alternatives while none has matched */
    for (i=0; (i<numalts) && !matched; i++) {
      /* get a handle to alternative name number i */
      const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i);

      /* only check alternatives of the same type the target is */
      if(check->type == target) {
        /* get data and length */
        const char *altptr = (char *)ASN1_STRING_data(check->d.ia5);

        switch(target) {
        case GEN_DNS: /* name/pattern comparison */
          /* The OpenSSL man page explicitly says: "In general it cannot be
             assumed that the data returned by ASN1_STRING_data() is null
             terminated or does not contain embedded nulls." But also that
             "The actual format of the data will depend on the actual string
             type itself: for example for and IA5String the data will be ASCII"

             Gisle researched the OpenSSL sources:
             "I checked the 0.9.6 and 0.9.8 sources before my patch and
             it always 0-terminates an IA5String."
          */
          if(cert_hostcheck(altptr, conn->host.name))
            matched = TRUE;
          break;
          /* compare alternative IP address if the data chunk is the same size
             our server IP address is */
          altlen = ASN1_STRING_length(check->d.ia5);
          if((altlen == addrlen) && !memcmp(altptr, &addr, altlen))
            matched = TRUE;
          break;
    GENERAL_NAMES_free(altnames);
  if(matched)
    /* an alternative name matched the server hostname */
    infof(data, "\t subjectAltName: %s matched\n", conn->host.dispname);
    /* we have to look to the last occurence of a commonName in the
       distinguished one to get the most significant one. */
    int j,i=-1 ;

/* The following is done because of a bug in 0.9.6b */
    unsigned char *nulstr = (unsigned char *)"";
    unsigned char *peer_CN = nulstr;

    X509_NAME *name = X509_get_subject_name(server_cert) ;
    if(name)
      while((j=X509_NAME_get_index_by_NID(name,NID_commonName,i))>=0)

    /* we have the name entry and we will now convert this to a string
       that we can use for comparison. Doing this we support BMPstring,
       UTF8 etc. */

      ASN1_STRING *tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name,i));

      /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input
         is already UTF-8 encoded. We check for this case and copy the raw
         string manually to avoid the problem. This code can be made
         conditional in the future when OpenSSL has been fixed. Work-around
         brought by Alexis S. L. Carvalho. */
      if(tmp && ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) {
            memcpy(peer_CN, ASN1_STRING_data(tmp), j);
            peer_CN[j] = '\0';
          }
        }
      }
      else /* not a UTF8 name */
        j = ASN1_STRING_to_UTF8(&peer_CN, tmp);
#ifdef CURL_DOES_CONVERSIONS
    else {
      /* convert peer_CN from UTF8 */
      size_t rc;
      rc = Curl_convert_from_utf8(data, peer_CN, strlen(peer_CN));
      /* Curl_convert_from_utf8 calls failf if unsuccessful */
      }
    }
#endif /* CURL_DOES_CONVERSIONS */
      failf(data,
            "SSL: unable to obtain common name from peer certificate");
      return CURLE_PEER_FAILED_VERIFICATION;
    else if(!cert_hostcheck((const char *)peer_CN, conn->host.name)) {
      if(data->set.ssl.verifyhost > 1) {
        failf(data, "SSL: certificate subject name '%s' does not match "
              "target host name '%s'", peer_CN, conn->host.dispname);
        res = CURLE_PEER_FAILED_VERIFICATION;
        infof(data, "\t common name: %s (does not match '%s')\n",
              peer_CN, conn->host.dispname);
    else {
      infof(data, "\t common name: %s (matched)\n", peer_CN);
    }
#endif /* USE_SSLEAY */
/* The SSL_CTRL_SET_MSG_CALLBACK doesn't exist in ancient OpenSSL versions
   and thus this cannot be done there. */
#ifdef SSL_CTRL_SET_MSG_CALLBACK

static const char *ssl_msg_type(int ssl_ver, int msg)
{
    switch (msg) {
      case SSL2_MT_ERROR:
        return "Error";
      case SSL2_MT_CLIENT_HELLO:
        return "Client hello";
      case SSL2_MT_CLIENT_MASTER_KEY:
        return "Client key";
      case SSL2_MT_CLIENT_FINISHED:
        return "Client finished";
      case SSL2_MT_SERVER_HELLO:
        return "Server hello";
      case SSL2_MT_SERVER_VERIFY:
        return "Server verify";
      case SSL2_MT_SERVER_FINISHED:
        return "Server finished";
      case SSL2_MT_REQUEST_CERTIFICATE:
        return "Request CERT";
      case SSL2_MT_CLIENT_CERTIFICATE:
        return "Client CERT";
    }
  }
    switch (msg) {
      case SSL3_MT_HELLO_REQUEST:
        return "Hello request";
      case SSL3_MT_CLIENT_HELLO:
        return "Client hello";
      case SSL3_MT_SERVER_HELLO:
        return "Server hello";
      case SSL3_MT_CERTIFICATE:
        return "CERT";
      case SSL3_MT_SERVER_KEY_EXCHANGE:
        return "Server key exchange";
      case SSL3_MT_CLIENT_KEY_EXCHANGE:
        return "Client key exchange";
      case SSL3_MT_CERTIFICATE_REQUEST:
        return "Request CERT";
      case SSL3_MT_SERVER_DONE:
        return "Server finished";
      case SSL3_MT_CERTIFICATE_VERIFY:
        return "CERT verify";
      case SSL3_MT_FINISHED:
        return "Finished";
    }
  }
  return "Unknown";
}

static const char *tls_rt_type(int type)
{
  return (
    type == SSL3_RT_CHANGE_CIPHER_SPEC ? "TLS change cipher, " :
    type == SSL3_RT_ALERT              ? "TLS alert, "         :
    type == SSL3_RT_HANDSHAKE          ? "TLS handshake, "     :
    type == SSL3_RT_APPLICATION_DATA   ? "TLS app data, "      :
                                         "TLS Unknown, ");
}


/*
 * Our callback from the SSL/TLS layers.
 */
static void ssl_tls_trace(int direction, int ssl_ver, int content_type,
                          const void *buf, size_t len, const SSL *ssl,
                          struct connectdata *conn)
{
Gisle Vanem's avatar
Gisle Vanem committed
  struct SessionHandle *data;
  const char *msg_name, *tls_rt_name;
  char ssl_buf[1024];
  int  ver, msg_type, txt_len;

  if(!conn || !conn->data || !conn->data->set.fdebug ||
      (direction != 0 && direction != 1))
    return;

  data = conn->data;
  ssl_ver >>= 8;
  ver = (ssl_ver == SSL2_VERSION_MAJOR ? '2' :
         ssl_ver == SSL3_VERSION_MAJOR ? '3' : '?');

  /* SSLv2 doesn't seem to have TLS record-type headers, so OpenSSL
   * always pass-up content-type as 0. But the interesting message-type
  if(ssl_ver == SSL3_VERSION_MAJOR && content_type != 0)
    tls_rt_name = tls_rt_type(content_type);
  else
    tls_rt_name = "";

  msg_type = *(char*)buf;
  msg_name = ssl_msg_type(ssl_ver, msg_type);

  txt_len = snprintf(ssl_buf, sizeof(ssl_buf), "SSLv%c, %s%s (%d):\n",
                     ver, tls_rt_name, msg_name, msg_type);
  Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len, NULL);

  Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT :
             CURLINFO_SSL_DATA_IN, (char *)buf, len, NULL);
Daniel Stenberg's avatar
Daniel Stenberg committed
/* ====================================================== */
ossl_connect_step1(struct connectdata *conn,
                   int sockindex)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  struct SessionHandle *data = conn->data;
  SSL_METHOD_QUAL SSL_METHOD *req_method=NULL;
  curl_socket_t sockfd = conn->sock[sockindex];
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
#ifdef ENABLE_IPV6
  struct in6_addr addr;
#else
  struct in_addr addr;
#endif
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed

  DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
  /* Make funny stuff to get random input */
  Curl_ossl_seed(data);

  /* check to see if we've been told to use an explicit SSL/TLS version */
  case CURL_SSLVERSION_DEFAULT:
    /* we try to figure out version */
    req_method = SSLv23_client_method();
    break;
  case CURL_SSLVERSION_TLSv1:
    req_method = TLSv1_client_method();
    break;
  case CURL_SSLVERSION_SSLv2:
    req_method = SSLv2_client_method();
    break;
    req_method = SSLv3_client_method();
    break;
  }
  connssl->ctx = SSL_CTX_new(req_method);
Daniel Stenberg's avatar
Daniel Stenberg committed

    failf(data, "SSL: couldn't create a context!");
  if(data->set.fdebug && data->set.verbose) {
    /* the SSL trace callback is only used for verbose logging so we only
       inform about failures of setting it */
    if(!SSL_CTX_callback_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK,
                               (void (*)(void))ssl_tls_trace)) {
Daniel Stenberg's avatar
Daniel Stenberg committed
      infof(data, "SSL: couldn't set callback!\n");
    else if(!SSL_CTX_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK_ARG, 0,
Daniel Stenberg's avatar
Daniel Stenberg committed
      infof(data, "SSL: couldn't set callback argument!\n");
  /* OpenSSL contains code to work-around lots of bugs and flaws in various
     SSL-implementations. SSL_CTX_set_options() is used to enabled those
     work-arounds. The man page for this option states that SSL_OP_ALL enables
     all the work-arounds and that "It is usually safe to use SSL_OP_ALL to
     enable the bug workaround options if compatibility with somewhat broken
     implementations is desired."

  */
  SSL_CTX_set_options(connssl->ctx, SSL_OP_ALL);
  /* disable SSLv2 in the default case (i.e. allow SSLv3 and TLSv1) */
  if(data->set.ssl.version == CURL_SSLVERSION_DEFAULT)
    SSL_CTX_set_options(connssl->ctx, SSL_OP_NO_SSLv2);

#if 0
  /*
   * Not sure it's needed to tell SSL_connect() that socket is
   * non-blocking. It doesn't seem to care, but just return with
   * SSL_ERROR_WANT_x.
   */
  if(data->state.used_interface == Curl_if_multi)
    SSL_CTX_ctrl(connssl->ctx, BIO_C_SET_NBIO, 1, NULL);
#endif

                   data->set.str[STRING_CERT],
                   data->set.str[STRING_CERT_TYPE],
                   data->set.str[STRING_KEY],
                   data->set.str[STRING_KEY_TYPE])) {
      /* failf() is already done in cert_stuff() */
Daniel Stenberg's avatar
Daniel Stenberg committed
    }
Daniel Stenberg's avatar
Daniel Stenberg committed

  if(data->set.str[STRING_SSL_CIPHER_LIST]) {
    if(!SSL_CTX_set_cipher_list(connssl->ctx,
                                data->set.str[STRING_SSL_CIPHER_LIST])) {
      failf(data, "failed setting cipher list");
  if(data->set.str[STRING_SSL_CAFILE] || data->set.str[STRING_SSL_CAPATH]) {
    /* tell SSL where to find CA certificates that are used to verify
       the servers certificate. */
    if(!SSL_CTX_load_verify_locations(connssl->ctx,
                                       data->set.str[STRING_SSL_CAFILE],
                                       data->set.str[STRING_SSL_CAPATH])) {
        /* Fail if we insist on successfully verifying the server. */
        failf(data,"error setting certificate verify locations:\n"
              "  CAfile: %s\n  CApath: %s\n",
              data->set.str[STRING_SSL_CAFILE]?
              data->set.str[STRING_SSL_CAFILE]: "none",
              data->set.str[STRING_SSL_CAPATH]?
              data->set.str[STRING_SSL_CAPATH] : "none");
      }
      else {
        /* Just continue with a warning if no strict  certificate verification
           is required. */
        infof(data, "error setting certificate verify locations,"
              " continuing anyway:\n");
      }
    }
    else {
      /* Everything is fine. */
      infof(data, "successfully set certificate verify locations:\n");
          data->set.str[STRING_SSL_CAFILE] ? data->set.str[STRING_SSL_CAFILE]:
          "none",
          data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]:
          "none");
  /* SSL always tries to verify the peer, this only says whether it should
   * fail to connect if the verification fails, or if it should continue
   * anyway. In the latter case the result of the verification is checked with
   * SSL_get_verify_result() below. */
                     data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
                     cert_verify_callback);
  /* give application a chance to interfere with SSL set up. */
    retcode = (*data->set.ssl.fsslctx)(data, connssl->ctx,
      failf(data,"error signaled by ssl ctx callback");
Daniel Stenberg's avatar
Daniel Stenberg committed

  /* Lets make an SSL structure */
  connssl->handle = SSL_new(connssl->ctx);
    failf(data, "SSL: couldn't create a context (handle)!");
    return CURLE_OUT_OF_MEMORY;
  }
  SSL_set_connect_state(connssl->handle);
Daniel Stenberg's avatar
Daniel Stenberg committed

Daniel Stenberg's avatar
Daniel Stenberg committed

#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
  if ((0 == Curl_inet_pton(AF_INET, conn->host.name, &addr)) &&
#ifdef ENABLE_IPV6
      (0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr)) &&
#endif
      !SSL_set_tlsext_host_name(connssl->handle, conn->host.name))
    infof(data, "WARNING: failed to configure server name indication (SNI) "
          "TLS extension\n");
#endif

  /* Check if there's a cached ID we can/should use here! */
  if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
    /* we got a session id, use it! */
    if(!SSL_set_session(connssl->handle, ssl_sessionid)) {
      failf(data, "SSL: SSL_set_session failed: %s",
            ERR_error_string(ERR_get_error(),NULL));
      return CURLE_SSL_CONNECT_ERROR;
    /* Informational message */
    infof (data, "SSL re-using session ID\n");
  /* pass the raw socket into the SSL layers */
  if(!SSL_set_fd(connssl->handle, sockfd)) {
     failf(data, "SSL: SSL_set_fd failed: %s",
           ERR_error_string(ERR_get_error(),NULL));
     return CURLE_SSL_CONNECT_ERROR;
  }
  connssl->connecting_state = ssl_connect_2;
  return CURLE_OK;
}
ossl_connect_step2(struct connectdata *conn, int sockindex)
{
  struct SessionHandle *data = conn->data;
  int err;
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
             || ssl_connect_2_reading == connssl->connecting_state
             || ssl_connect_2_writing == connssl->connecting_state);
  /* 1  is fine
     0  is "not successful but was shut down controlled"
     <0 is "handshake was not successful, because a fatal error occurred" */
  if(1 != err) {
    int detail = SSL_get_error(connssl->handle, err);
    if(SSL_ERROR_WANT_READ == detail) {
      connssl->connecting_state = ssl_connect_2_reading;
      return CURLE_OK;
    else if(SSL_ERROR_WANT_WRITE == detail) {
      connssl->connecting_state = ssl_connect_2_writing;
      return CURLE_OK;
    }
    else {
      /* untreated error */
      unsigned long errdetail;
      char error_buffer[256]; /* OpenSSL documents that this must be at least
                                 256 bytes long. */
      CURLcode rc;
      const char *cert_problem = NULL;

      connssl->connecting_state = ssl_connect_2; /* the connection failed,
                                                    we're not waiting for
                                                    anything else. */

      errdetail = ERR_get_error(); /* Gets the earliest error code from the
                                      thread's error queue and removes the
                                      entry. */

      switch(errdetail) {
      case 0x1407E086:
        /* 1407E086:
           SSL routines:
           SSL2_SET_CERTIFICATE:
           certificate verify failed */
        /* fall-through */
      case 0x14090086:
        /* 14090086:
           SSL routines:
           SSL3_GET_SERVER_CERTIFICATE:
           certificate verify failed */
        cert_problem = "SSL certificate problem, verify that the CA cert is"
          " OK. Details:\n";
        rc = CURLE_SSL_CACERT;
        break;
      default:
        rc = CURLE_SSL_CONNECT_ERROR;

      /* detail is already set to the SSL error above */

      /* If we e.g. use SSLv2 request-method and the server doesn't like us
       * (RST connection etc.), OpenSSL gives no explanation whatsoever and
       * the SO_ERROR is also lost.
       */
      if(CURLE_SSL_CONNECT_ERROR == rc && errdetail == 0) {
        failf(data, "Unknown SSL protocol error in connection to %s:%d ",
              conn->host.name, conn->port);
        return rc;
      /* Could be a CERT problem */

      SSL_strerror(errdetail, error_buffer, sizeof(error_buffer));
      failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer);
      return rc;
    }
  }
  else {
    /* we have been connected fine, we're not waiting for anything else. */
    connssl->connecting_state = ssl_connect_3;

    /* Informational message */
    infof (data, "SSL connection using %s\n",
           SSL_get_cipher(connssl->handle));

    return CURLE_OK;
  }
}

/*
 * Get the server cert, verify it and show it etc, only call failf() if the
 * 'strict' argument is TRUE as otherwise all this is for informational
 * purposes only!
 *
 * We check certificates to authenticate the server; otherwise we risk
 * man-in-the-middle attack.
 */
static CURLcode servercert(struct connectdata *conn,
                           struct ssl_connect_data *connssl,
                           bool strict)
  CURLcode retcode = CURLE_OK;
  long lerr;
  ASN1_TIME *certdate;
  struct SessionHandle *data = conn->data;
  connssl->server_cert = SSL_get_peer_certificate(connssl->handle);
  if(!connssl->server_cert) {
    if(strict)
      failf(data, "SSL: couldn't get peer certificate!");
    return CURLE_PEER_FAILED_VERIFICATION;
  }
  infof (data, "Server certificate:\n");
  str = X509_NAME_oneline(X509_get_subject_name(connssl->server_cert),
    if(strict)
      failf(data, "SSL: couldn't get X509-subject!");
    connssl->server_cert = NULL;
  }
  infof(data, "\t subject: %s\n", str);
  CRYPTO_free(str);

  certdate = X509_get_notBefore(connssl->server_cert);
  asn1_output(conn, "\t start date: ", certdate);
  certdate = X509_get_notAfter(connssl->server_cert);
  asn1_output(conn, "\t expire date: ", certdate);
    retcode = verifyhost(conn, connssl->server_cert);
      connssl->server_cert = NULL;
  str = X509_NAME_oneline(X509_get_issuer_name(connssl->server_cert),
    if(strict)
      failf(data, "SSL: couldn't get X509-issuer name!");
  else {
    infof(data, "\t issuer: %s\n", str);
    CRYPTO_free(str);
Daniel Stenberg's avatar
Daniel Stenberg committed

    /* We could do all sorts of certificate verification stuff here before
       deallocating the certificate. */
    lerr = data->set.ssl.certverifyresult=
      SSL_get_verify_result(connssl->handle);
    if(data->set.ssl.certverifyresult != X509_V_OK) {
      if(data->set.ssl.verifypeer) {
        /* We probably never reach this, because SSL_connect() will fail
           and we return earlyer if verifypeer is set? */
        if(strict)
          failf(data, "SSL certificate verify result: %s (%ld)",
                X509_verify_cert_error_string(lerr), lerr);
        retcode = CURLE_PEER_FAILED_VERIFICATION;
        infof(data, "SSL certificate verify result: %s (%ld),"
              " continuing anyway.\n",
              X509_verify_cert_error_string(lerr), lerr);
    else
      infof(data, "SSL certificate verify ok.\n");
Daniel Stenberg's avatar
Daniel Stenberg committed

  connssl->server_cert = NULL;
  connssl->connecting_state = ssl_connect_done;
ossl_connect_step3(struct connectdata *conn,
                   int sockindex)
{
  CURLcode retcode = CURLE_OK;
  void *ssl_sessionid=NULL;
  struct SessionHandle *data = conn->data;
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];

  DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);

  if(Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
    /* Since this is not a cached session ID, then we want to stach this one
       in the cache! */
    SSL_SESSION *our_ssl_sessionid;
#ifdef HAVE_SSL_GET1_SESSION
    our_ssl_sessionid = SSL_get1_session(connssl->handle);

    /* SSL_get1_session() will increment the reference
       count and the session will stay in memory until explicitly freed with
       SSL_SESSION_free(3), regardless of its state.
       This function was introduced in openssl 0.9.5a. */
#else
    our_ssl_sessionid = SSL_get_session(connssl->handle);

    /* if SSL_get1_session() is unavailable, use SSL_get_session().
       This is an inferior option because the session can be flushed
       at any time by openssl. It is included only so curl compiles
       under versions of openssl < 0.9.5a.

       WARNING: How curl behaves if it's session is flushed is
       untested.
    */
#endif
    retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
                                    0 /* unknown size */);
    if(retcode) {
      failf(data, "failed to store ssl session");
      return retcode;
    }
  }


  /*
   * We check certificates to authenticate the server; otherwise we risk
   * man-in-the-middle attack; NEVERTHELESS, if we're told explicitly not to
   * verify the peer ignore faults and failures from the server cert
   * operations.
   */

  if(!data->set.ssl.verifypeer)
    (void)servercert(conn, connssl, FALSE);
  else
    retcode = servercert(conn, connssl, TRUE);

  if(CURLE_OK == retcode)
    connssl->connecting_state = ssl_connect_done;
Daniel Stenberg's avatar
Daniel Stenberg committed
}
ossl_connect_common(struct connectdata *conn,
                    int sockindex,
                    bool nonblocking,
                    bool *done)
{
  CURLcode retcode;
  struct SessionHandle *data = conn->data;
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  curl_socket_t sockfd = conn->sock[sockindex];
  long timeout_ms;

  if(ssl_connect_1==connssl->connecting_state) {
    /* Find out how much more time we're allowed */
    timeout_ms = Curl_timeleft(conn, NULL, TRUE);

    if(timeout_ms < 0) {
      /* no need to continue if time already is up */
      failf(data, "SSL connection timeout");
      return CURLE_OPERATION_TIMEDOUT;
    }
    retcode = ossl_connect_step1(conn, sockindex);
  while(ssl_connect_2 == connssl->connecting_state ||
        ssl_connect_2_reading == connssl->connecting_state ||
        ssl_connect_2_writing == connssl->connecting_state) {
    /* check allowed time left */
    timeout_ms = Curl_timeleft(conn, NULL, TRUE);

    if(timeout_ms < 0) {
      /* no need to continue if time already is up */
      failf(data, "SSL connection timeout");
      return CURLE_OPERATION_TIMEDOUT;
    }

    /* if ssl is expecting something, check if it's available. */
    if(connssl->connecting_state == ssl_connect_2_reading
        || connssl->connecting_state == ssl_connect_2_writing) {

      int writefd = ssl_connect_2_writing==
        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
      int readfd = ssl_connect_2_reading==
        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;

      while(1) {
        int what = Curl_socket_ready(readfd, writefd,
                                     nonblocking?0:(int)timeout_ms);
          /* readable or writable, go loop in the outer loop */
            *done = FALSE;
            return CURLE_OK;
          }
          else {
            /* timeout */
            failf(data, "SSL connection timeout");
            return CURLE_OPERATION_TIMEDOUT;
          }
        }
        else {
          /* anything that gets here is fatally bad */
Yang Tse's avatar
Yang Tse committed
          failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
          return CURLE_SSL_CONNECT_ERROR;
        }
      } /* while()-loop for the select() */
    }

    /* get the timeout from step2 to avoid computing it twice. */
    retcode = ossl_connect_step2(conn, sockindex);
      return retcode;

  } /* repeat step2 until all transactions are done. */


  if(ssl_connect_3==connssl->connecting_state) {
    retcode = ossl_connect_step3(conn, sockindex);
  if(ssl_connect_done==connssl->connecting_state) {
    connssl->state = ssl_connection_complete;
  /* Reset our connect state machine */
  connssl->connecting_state = ssl_connect_1;

  return CURLE_OK;
}

CURLcode
Curl_ossl_connect_nonblocking(struct connectdata *conn,
                              int sockindex,
                              bool *done)
{
  return ossl_connect_common(conn, sockindex, TRUE, done);
}

CURLcode
Curl_ossl_connect(struct connectdata *conn,
                  int sockindex)
{
  CURLcode retcode;
  bool done = FALSE;

  retcode = ossl_connect_common(conn, sockindex, FALSE, &done);
/* return number of sent (non-SSL) bytes */
ssize_t Curl_ossl_send(struct connectdata *conn,
                       int sockindex,
                       void *mem,
                       size_t len)
{
  /* SSL_write() is said to return 'int' while write() and send() returns
     'size_t' */
  int err;
  char error_buffer[120]; /* OpenSSL documents that this must be at least 120
                             bytes long. */
  unsigned long sslerror;
  int rc = SSL_write(conn->ssl[sockindex].handle, mem, (int)len);

  if(rc < 0) {
    err = SSL_get_error(conn->ssl[sockindex].handle, rc);

    switch(err) {
    case SSL_ERROR_WANT_READ:
    case SSL_ERROR_WANT_WRITE:
      /* The operation did not complete; the same TLS/SSL I/O function
         should be called again later. This is basicly an EWOULDBLOCK
         equivalent. */
      return 0;
    case SSL_ERROR_SYSCALL:
      failf(conn->data, "SSL_write() returned SYSCALL, errno = %d",
      return -1;
    case SSL_ERROR_SSL:
      /*  A failure in the SSL library occurred, usually a protocol error.
          The OpenSSL error queue contains more information on the error. */
      sslerror = ERR_get_error();
      failf(conn->data, "SSL_write() error: %s",
            ERR_error_string(sslerror, error_buffer));
      return -1;
    }
    /* a true error */
    failf(conn->data, "SSL_write() return error %d", err);
  return (ssize_t)rc; /* number of bytes */
}

/*
 * If the read would block we return -1 and set 'wouldblock' to TRUE.
 * Otherwise we return the amount of data read. Other errors should return -1
 * and set 'wouldblock' to FALSE.
 */
ssize_t Curl_ossl_recv(struct connectdata *conn, /* connection data */
                       int num,                  /* socketindex */
                       char *buf,                /* store read data here */
                       size_t buffersize,        /* max amount to read */
                       bool *wouldblock)
{
  char error_buffer[120]; /* OpenSSL documents that this must be at
                             least 120 bytes long. */
  unsigned long sslerror;
  ssize_t nread = (ssize_t)SSL_read(conn->ssl[num].handle, buf,
                                    (int)buffersize);
  *wouldblock = FALSE;
  if(nread < 0) {
    /* failed SSL_read */
    int err = SSL_get_error(conn->ssl[num].handle, (int)nread);

    switch(err) {
    case SSL_ERROR_NONE: /* this is not an error */
    case SSL_ERROR_ZERO_RETURN: /* no more data */
      break;
    case SSL_ERROR_WANT_READ:
    case SSL_ERROR_WANT_WRITE:
      /* there's data pending, re-invoke SSL_read() */
      *wouldblock = TRUE;
      return -1; /* basically EWOULDBLOCK */
    default:
      /* openssl/ssl.h says "look at error stack/return value/errno" */
      sslerror = ERR_get_error();
      failf(conn->data, "SSL read: %s, errno %d",
            ERR_error_string(sslerror, error_buffer),
      return -1;
    }
  }
  return nread;
}

size_t Curl_ossl_version(char *buffer, size_t size)
{
#ifdef YASSL_VERSION
  /* yassl provides an OpenSSL API compatiblity layer so it looks identical
     to OpenSSL in all other aspects */
  return snprintf(buffer, size, "yassl/%s", YASSL_VERSION);
  {
    char sub[2];
    unsigned long ssleay_value;
    sub[1]='\0';
    ssleay_value=SSLeay();
    if(ssleay_value < 0x906000) {
      ssleay_value=SSLEAY_VERSION_NUMBER;
      sub[0]='\0';
    }
    else {
      if(ssleay_value&0xff0) {
Yang Tse's avatar
Yang Tse committed
        sub[0]=(char)(((ssleay_value>>4)&0xff) + 'a' -1);
    return snprintf(buffer, size, "OpenSSL/%lx.%lx.%lx%s",
                    (ssleay_value>>28)&0xf,
                    (ssleay_value>>20)&0xff,
                    (ssleay_value>>12)&0xff,
                    sub);
  }

#else /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */

  return snprintf(buffer, size, "OpenSSL/%lx.%lx.%lx",
                  (SSLEAY_VERSION_NUMBER>>28)&0xff,
                  (SSLEAY_VERSION_NUMBER>>20)&0xff,
                  (SSLEAY_VERSION_NUMBER>>12)&0xf);

#else /* (SSLEAY_VERSION_NUMBER >= 0x900000) */
  {
    char sub[2];