Skip to content
ssluse.c 73 KiB
Newer Older
        print_pubkey_BN(dsa, p, i);
        print_pubkey_BN(dsa, q, i);
        print_pubkey_BN(dsa, g, i);
        print_pubkey_BN(dsa, priv_key, i);
        print_pubkey_BN(dsa, pub_key, i);
        break;
      case EVP_PKEY_DH:
        print_pubkey_BN(dh, p, i);
        print_pubkey_BN(dh, g, i);
        print_pubkey_BN(dh, priv_key, i);
        print_pubkey_BN(dh, pub_key, i);
        break;
#if 0
      case EVP_PKEY_EC: /* symbol not present in OpenSSL 0.9.6 */
        /* left TODO */
        break;
#endif
      }
    }

    X509V3_ext(data, i, cinf->extensions);

    X509_signature(data, i, x->signature);

    dumpcert(data, x, i);
  }

  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;
  char buffer[256];

  if(data->set.ssl.certinfo)
    /* we've been asked to gather certificate info! */
    (void)get_cert_chain(conn, connssl);
  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");
  rc = x509_name_oneline(X509_get_subject_name(connssl->server_cert),
                          buffer, sizeof(buffer));
  if(rc) {
    if(strict)
      failf(data, "SSL: couldn't get X509-subject!");
    connssl->server_cert = NULL;
  infof(data, "\t subject: %s\n", buffer);
  certdate = X509_get_notBefore(connssl->server_cert);
  asn1_output(certdate, buffer, sizeof(buffer));
  infof(data, "\t start date: %s\n", buffer);
  certdate = X509_get_notAfter(connssl->server_cert);
  asn1_output(certdate, buffer, sizeof(buffer));
  infof(data, "\t expire date: %s\n", buffer);
    retcode = verifyhost(conn, connssl->server_cert);
      connssl->server_cert = NULL;
  rc = x509_name_oneline(X509_get_issuer_name(connssl->server_cert),
                         buffer, sizeof(buffer));
  if(rc) {
    if(strict)
      failf(data, "SSL: couldn't get X509-issuer name!");
    infof(data, "\t issuer: %s\n", buffer);
Daniel Stenberg's avatar
Daniel Stenberg committed

    /* We could do all sorts of certificate verification stuff here before
       deallocating the certificate. */
    /* e.g. match issuer name with provided issuer certificate */
    if (data->set.str[STRING_SSL_ISSUERCERT]) {
      if (! (fp=fopen(data->set.str[STRING_SSL_ISSUERCERT],"r"))) {
        if (strict)
          failf(data, "SSL: Unable to open issuer cert (%s)\n",
                data->set.str[STRING_SSL_ISSUERCERT]);
        X509_free(connssl->server_cert);
        connssl->server_cert = NULL;
        return CURLE_SSL_ISSUER_ERROR;
      issuer = PEM_read_X509(fp,NULL,ZERO_NULL,NULL);
          failf(data, "SSL: Unable to read issuer cert (%s)\n",
                data->set.str[STRING_SSL_ISSUERCERT]);
        X509_free(connssl->server_cert);
        X509_free(issuer);
        fclose(fp);
        return CURLE_SSL_ISSUER_ERROR;
      }
      fclose(fp);
      if (X509_check_issued(issuer,connssl->server_cert) != X509_V_OK) {
        if (strict)
          failf(data, "SSL: Certificate issuer check failed (%s)\n",
                data->set.str[STRING_SSL_ISSUERCERT]);
        X509_free(connssl->server_cert);
        X509_free(issuer);
        connssl->server_cert = NULL;
        return CURLE_SSL_ISSUER_ERROR;
      }
      infof(data, "\t SSL certificate issuer check ok (%s)\n",
    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 earlier 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, "\t SSL certificate verify result: %s (%ld),"
              X509_verify_cert_error_string(lerr), lerr);
      infof(data, "\t 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) {

Yang Tse's avatar
Yang Tse committed
      curl_socket_t writefd = ssl_connect_2_writing==
        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
Yang Tse's avatar
Yang Tse committed
      curl_socket_t 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);
bool Curl_ossl_data_pending(const struct connectdata *conn,
                            int connindex)
{
  if(conn->ssl[connindex].handle)
    /* SSL is in use */
    return (bool)(0 != SSL_pending(conn->ssl[connindex].handle));
  else
    return FALSE;
}

/* return number of sent (non-SSL) bytes */
ssize_t Curl_ossl_send(struct connectdata *conn,
                       int sockindex,
{
  /* 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];
    sub[1]='\0';
    if(SSLEAY_VERSION_NUMBER&0x0f) {
      sub[0]=(SSLEAY_VERSION_NUMBER&0x0f) + 'a' -1;
    }
    else
      sub[0]='\0';

    return snprintf(buffer, size, "SSL/%x.%x.%x%s",
                    (SSLEAY_VERSION_NUMBER>>12)&0xff,
                    (SSLEAY_VERSION_NUMBER>>8)&0xf,
                    (SSLEAY_VERSION_NUMBER>>4)&0xf, sub);
  }
#endif /* (SSLEAY_VERSION_NUMBER >= 0x900000) */
#endif /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */