Skip to content
openssl.c 85.4 KiB
Newer Older
  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 / %s\n",
           get_ssl_version_txt(SSL_get_session(connssl->handle)),
Fabian Frank's avatar
Fabian Frank committed
#ifdef HAS_ALPN
    /* Sets data and len to negotiated protocol, len is 0 if no protocol was
     * negotiated
     */
    if(data->set.ssl_enable_alpn) {
      const unsigned char* neg_protocol;
      unsigned int len;
      SSL_get0_alpn_selected(connssl->handle, &neg_protocol, &len);
      if(len != 0) {
        infof(data, "ALPN, server accepted to use %.*s\n", len, neg_protocol);

        if(len == NGHTTP2_PROTO_VERSION_ID_LEN &&
           memcmp(NGHTTP2_PROTO_VERSION_ID, neg_protocol, len) == 0) {
             conn->negnpn = NPN_HTTP2;
        }
        else if(len == ALPN_HTTP_1_1_LENGTH && memcmp(ALPN_HTTP_1_1,
            neg_protocol, ALPN_HTTP_1_1_LENGTH) == 0) {
          conn->negnpn = NPN_HTTP1_1;
        }
      else {
        infof(data, "ALPN, server did not agree to a protocol\n");
static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len)
{
Yang Tse's avatar
Yang Tse committed
  int i, ilen;

  if((ilen = (int)len) < 0)
    return 1; /* buffer too big */

  i = i2t_ASN1_OBJECT(buf, ilen, a);

  if(i >= ilen)
    return 1; /* buffer too small */

  return 0;
}

static void pubkey_show(struct SessionHandle *data,
                        int num,
                        const char *type,
                        const char *name,
                        unsigned char *raw,
                        int len)
{
  left = len*3 + 1;
  buffer = malloc(left);
  if(buffer) {
    char *ptr=buffer;
    snprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name);
    for(i=0; i< len; i++) {
      snprintf(ptr, left, "%02x:", raw[i]);
      ptr += 3;
      left -= 3;
    }
    infof(data, "   %s: %s\n", namebuf, buffer);
    Curl_ssl_push_certinfo(data, num, namebuf, buffer);
  if(pubkey->pkey._type->_name != NULL) { \
    int len = BN_num_bytes(pubkey->pkey._type->_name);  \
    if(len < CERTBUFFERSIZE) {                                    \
      BN_bn2bin(pubkey->pkey._type->_name, (unsigned char*)bufp); \
      pubkey_show(data, _num, #_type, #_name, (unsigned char*)bufp, len); \
} WHILE_FALSE

static int X509V3_ext(struct SessionHandle *data,
                      int certnum,
                      STACK_OF(X509_EXTENSION) *exts)
{

  if(sk_X509_EXTENSION_num(exts) <= 0)
    /* no extensions, bail out */
    return 1;

  for(i=0; i<sk_X509_EXTENSION_num(exts); i++) {
    ASN1_OBJECT *obj;
    X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
    BUF_MEM *biomem;
    char buf[512];
    char *ptr=buf;
    char namebuf[128];
    BIO *bio_out = BIO_new(BIO_s_mem());

    if(!bio_out)
      return 1;

    obj = X509_EXTENSION_get_object(ext);

    asn1_object_dump(obj, namebuf, sizeof(namebuf));

    infof(data, "%s: %s\n", namebuf,
          X509_EXTENSION_get_critical(ext)?"(critical)":"");

    if(!X509V3_EXT_print(bio_out, ext, 0, 0))
      M_ASN1_OCTET_STRING_print(bio_out, ext->value);

    BIO_get_mem_ptr(bio_out, &biomem);

    /* biomem->length bytes at biomem->data, this little loop here is only
       done for the infof() call, we send the "raw" data to the certinfo
       function */
    for(j=0; j<(size_t)biomem->length; j++) {
      const char *sep="";
      if(biomem->data[j] == '\n') {
        sep=", ";
        j++; /* skip the newline */
      };
      while((j<(size_t)biomem->length) && (biomem->data[j] == ' '))
        ptr+=snprintf(ptr, sizeof(buf)-(ptr-buf), "%s%c", sep,
                      biomem->data[j]);
    Curl_ssl_push_certinfo(data, certnum, namebuf, buf);

    BIO_free(bio_out);

  }
  return 0; /* all is fine */
}


static void X509_signature(struct SessionHandle *data,
                           int numcert,
                           ASN1_STRING *sig)
{
  char buf[1024];
  char *ptr = buf;
  int i;
  for(i=0; i<sig->length; i++)
    ptr+=snprintf(ptr, sizeof(buf)-(ptr-buf), "%02x:", sig->data[i]);

  infof(data, " Signature: %s\n", buf);
  Curl_ssl_push_certinfo(data, numcert, "Signature", buf);
}

static void dumpcert(struct SessionHandle *data, X509 *x, int numcert)
{
  BIO *bio_out = BIO_new(BIO_s_mem());
  BUF_MEM *biomem;

  /* this outputs the cert in this 64 column wide style with newlines and
     -----BEGIN CERTIFICATE----- texts and more */
  PEM_write_bio_X509(bio_out, x);

  BIO_get_mem_ptr(bio_out, &biomem);

  Curl_ssl_push_certinfo_len(data, numcert,
                             "Cert", biomem->data, biomem->length);
/*
 * This size was previously 512 which has been reported "too small" without
 * any specifics, so it was enlarged to allow more data to get shown uncut.
 * The "perfect" size is yet to figure out.
 */
#define CERTBUFFERSIZE 8192

static CURLcode get_cert_chain(struct connectdata *conn,
                               struct ssl_connect_data *connssl)

{
  STACK_OF(X509) *sk;
  int i;
  struct SessionHandle *data = conn->data;
  int numcerts;

  bufp = malloc(CERTBUFFERSIZE);
  if(!bufp)
    return CURLE_OUT_OF_MEMORY;
  sk = SSL_get_peer_cert_chain(connssl->handle);
  if(!sk) {
    free(bufp);
  if(Curl_ssl_init_certinfo(data, numcerts)) {
  for(i=0; i<numcerts; i++) {
    long value;
    ASN1_INTEGER *num;
    ASN1_TIME *certdate;

    /* get the certs in "importance order" */
#if 0
    X509 *x = sk_X509_value(sk, numcerts - i - 1);
#else
    X509 *x = sk_X509_value(sk, i);
#endif

    X509_CINF *cinf;
    EVP_PKEY *pubkey=NULL;
    int j;
    char *ptr;

    (void)x509_name_oneline(X509_get_subject_name(x), bufp, CERTBUFFERSIZE);
    infof(data, "%2d Subject: %s\n", i, bufp);
    Curl_ssl_push_certinfo(data, i, "Subject", bufp);
    (void)x509_name_oneline(X509_get_issuer_name(x), bufp, CERTBUFFERSIZE);
    infof(data, "   Issuer: %s\n", bufp);
    Curl_ssl_push_certinfo(data, i, "Issuer", bufp);

    value = X509_get_version(x);
    infof(data, "   Version: %lu (0x%lx)\n", value+1, value);
    snprintf(bufp, CERTBUFFERSIZE, "%lx", value);
    Curl_ssl_push_certinfo(data, i, "Version", bufp); /* hex */
      value = ASN1_INTEGER_get(num);
      infof(data,"   Serial Number: %ld (0x%lx)\n", value, value);
      snprintf(bufp, CERTBUFFERSIZE, "%lx", value);
      int left = CERTBUFFERSIZE;
      *ptr++ = 0;
      if(num->type == V_ASN1_NEG_INTEGER)
        *ptr++='-';

      for(j=0; (j<num->length) && (left>=4); j++) {
        /* TODO: length restrictions */
        snprintf(ptr, 3, "%02x%c",num->data[j],
                 ((j+1 == num->length)?'\n':':'));
        ptr += 3;
        infof(data,"   Serial Number: %s\n", bufp);
      Curl_ssl_push_certinfo(data, i, "Serial Number", bufp); /* hex */
    j = asn1_object_dump(cinf->signature->algorithm, bufp, CERTBUFFERSIZE);
      infof(data, "   Signature Algorithm: %s\n", bufp);
      Curl_ssl_push_certinfo(data, i, "Signature Algorithm", bufp);
    asn1_output(certdate, bufp, CERTBUFFERSIZE);
    infof(data, "   Start date: %s\n", bufp);
    Curl_ssl_push_certinfo(data, i, "Start date", bufp);
    asn1_output(certdate, bufp, CERTBUFFERSIZE);
    infof(data, "   Expire date: %s\n", bufp);
    Curl_ssl_push_certinfo(data, i, "Expire date", bufp);
    j = asn1_object_dump(cinf->key->algor->algorithm, bufp, CERTBUFFERSIZE);
      infof(data, "   Public Key Algorithm: %s\n", bufp);
      Curl_ssl_push_certinfo(data, i, "Public Key Algorithm", bufp);
    }

    pubkey = X509_get_pubkey(x);
    if(!pubkey)
      infof(data, "   Unable to load public key\n");
    else {
      switch(pubkey->type) {
      case EVP_PKEY_RSA:
        infof(data,  "   RSA Public Key (%d bits)\n",
              BN_num_bits(pubkey->pkey.rsa->n));
        snprintf(bufp, CERTBUFFERSIZE, "%d", BN_num_bits(pubkey->pkey.rsa->n));
        Curl_ssl_push_certinfo(data, i, "RSA Public Key", bufp);

        print_pubkey_BN(rsa, n, i);
        print_pubkey_BN(rsa, e, i);
        print_pubkey_BN(rsa, d, i);
        print_pubkey_BN(rsa, p, i);
        print_pubkey_BN(rsa, q, i);
        print_pubkey_BN(rsa, dmp1, i);
        print_pubkey_BN(rsa, dmq1, i);
        print_pubkey_BN(rsa, iqmp, i);
        break;
      case EVP_PKEY_DSA:
        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);
  }

/*
 * 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 = data->state.buffer;

  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),
  infof(data, "\t subject: %s\n", rc?"[NONE]":buffer);
  certdate = X509_get_notBefore(connssl->server_cert);
  asn1_output(certdate, buffer, BUFSIZE);
  infof(data, "\t start date: %s\n", buffer);
  certdate = X509_get_notAfter(connssl->server_cert);
  asn1_output(certdate, buffer, BUFSIZE);
  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),
    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]) {
      fp=fopen(data->set.str[STRING_SSL_ISSUERCERT],"r");
      if(!fp) {
        if(strict)
          failf(data, "SSL: Unable to open issuer cert (%s)",
                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)",
                data->set.str[STRING_SSL_ISSUERCERT]);
        X509_free(connssl->server_cert);
        X509_free(issuer);
        fclose(fp);
        return CURLE_SSL_ISSUER_ERROR;
      if(X509_check_issued(issuer,connssl->server_cert) != X509_V_OK) {
        if(strict)
          failf(data, "SSL: Certificate issuer check failed (%s)",
                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)
  void *old_ssl_sessionid=NULL;
  struct SessionHandle *data = conn->data;
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  int incache;
  SSL_SESSION *our_ssl_sessionid;

  DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);

#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. */
  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.
  */

  incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL));
  if(incache) {
    if(old_ssl_sessionid != our_ssl_sessionid) {
      infof(data, "old SSL session ID is stale, removing\n");
      Curl_ssl_delsessionid(conn, old_ssl_sessionid);
      incache = FALSE;
    }
  }
    retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
                                    0 /* unknown size */);
    if(retcode) {
      failf(data, "failed to store ssl session");
      return retcode;
    }
  }
#ifdef HAVE_SSL_GET1_SESSION
  else {
    /* Session was incache, so refcount already incremented earlier.
     * Avoid further increments with each SSL_get1_session() call.
     * This does not free the session as refcount remains > 0
     */
    SSL_SESSION_free(our_ssl_sessionid);
  }
#endif

  /*
   * 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 && !data->set.ssl.verifyhost)
    (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
}
static Curl_recv ossl_recv;
static Curl_send ossl_send;

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;
  /* check if the connection has already been established */
  if(ssl_connection_complete == connssl->state) {
    *done = TRUE;
    return CURLE_OK;
  }

  if(ssl_connect_1==connssl->connecting_state) {
    /* Find out how much more time we're allowed */
    timeout_ms = Curl_timeleft(data, 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) {
    timeout_ms = Curl_timeleft(data, 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;

      what = Curl_socket_ready(readfd, writefd, nonblocking?0:timeout_ms);
      if(what < 0) {
        /* fatal error */
        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
        return CURLE_SSL_CONNECT_ERROR;
      }
      else if(0 == what) {
        if(nonblocking) {
          *done = FALSE;
          return CURLE_OK;
          /* timeout */
          failf(data, "SSL connection timeout");
          return CURLE_OPERATION_TIMEDOUT;
      }
      /* socket is readable or writable */
    /* Run transaction, and return to the caller if it failed or if this
     * connection is done nonblocking and this loop would execute again. This
     * permits the owner of a multi handle to abort a connection attempt
     * before step2 has completed while ensuring that a client using select()
     * or epoll() will always have a valid fdset to wait on.
    retcode = ossl_connect_step2(conn, sockindex);
    if(retcode || (nonblocking &&
                   (ssl_connect_2 == connssl->connecting_state ||
                    ssl_connect_2_reading == connssl->connecting_state ||
                    ssl_connect_2_writing == connssl->connecting_state)))
      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;
    conn->recv[sockindex] = ossl_recv;
    conn->send[sockindex] = ossl_send;
  /* 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 (0 != SSL_pending(conn->ssl[connindex].handle)) ? TRUE : FALSE;
static ssize_t ossl_send(struct connectdata *conn,
                         int sockindex,
                         const void *mem,
                         size_t len,
                         CURLcode *curlcode)
{
  /* 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;
  memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
  rc = SSL_write(conn->ssl[sockindex].handle, mem, memlen);
    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 basically an EWOULDBLOCK
      *curlcode = CURLE_AGAIN;
      failf(conn->data, "SSL_write() returned SYSCALL, errno = %d",
      *curlcode = CURLE_SEND_ERROR;
      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));
      *curlcode = CURLE_SEND_ERROR;
    failf(conn->data, "SSL_write() return error %d", err);
    *curlcode = CURLE_SEND_ERROR;
  *curlcode = CURLE_OK;
  return (ssize_t)rc; /* number of bytes */
static ssize_t ossl_recv(struct connectdata *conn, /* connection data */
                         int num,                  /* socketindex */
                         char *buf,                /* store read data here */
                         size_t buffersize,        /* max amount to read */
                         CURLcode *curlcode)
{
  char error_buffer[120]; /* OpenSSL documents that this must be at
                             least 120 bytes long. */
  unsigned long sslerror;
  buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
  nread = (ssize_t)SSL_read(conn->ssl[num].handle, buf, buffsize);
    /* 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() */
      *curlcode = CURLE_AGAIN;
      /* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return
         value/errno" */
      /* http://www.openssl.org/docs/crypto/ERR_get_error.html */
      if((nread < 0) || sslerror) {
        /* If the return code was negative or there actually is an error in the
           queue */
        failf(conn->data, "SSL read: %s, errno %d",
              ERR_error_string(sslerror, error_buffer),
              SOCKERRNO);
        *curlcode = CURLE_RECV_ERROR;
        return -1;
      }
    }
  }
  return nread;
}

size_t Curl_ossl_version(char *buffer, size_t size)
{
  /* yassl provides an OpenSSL API compatibility layer so it looks identical
  return snprintf(buffer, size, "yassl/%s", YASSL_VERSION);
    sub[1]='\0';
    ssleay_value=SSLeay();
    if(ssleay_value < 0x906000) {
      ssleay_value=SSLEAY_VERSION_NUMBER;
      sub[0]='\0';
    }
    else {
      if(ssleay_value&0xff0) {
        int minor = (ssleay_value >> 4) & 0xff;
        if(minor > 26) { /* handle extended version introduced for 0.9.8za */
          sub[1] = (char) ((minor - 1) % 26 + 'a' + 1);
          sub[0] = 'z';
        }
        else {
          sub[0]=(char)(((ssleay_value>>4)&0xff) + 'a' -1);
        }
    return snprintf(buffer, size, "%s/%lx.%lx.%lx%s",
#ifdef OPENSSL_IS_BORINGSSL
                    "BoringSSL"
#else
#ifdef LIBRESSL_VERSION_NUMBER
                    "LibreSSL"
#else
                    "OpenSSL"
#endif
                    , (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 */
/* can be called with data == NULL */
int Curl_ossl_random(struct SessionHandle *data, unsigned char *entropy,
                     size_t length)
  if(data)
    Curl_ossl_seed(data); /* Initiate the seed if not already done */
  RAND_bytes(entropy, curlx_uztosi(length));
  return 0; /* 0 as in no problem */
}

void Curl_ossl_md5sum(unsigned char *tmp, /* input */
                      size_t tmplen,
                      unsigned char *md5sum /* output */,
                      size_t unused)
{
  MD5_CTX MD5pw;
  (void)unused;
  MD5_Init(&MD5pw);
  MD5_Update(&MD5pw, tmp, tmplen);
  MD5_Final(md5sum, &MD5pw);
}