Esempio n. 1
0
static CURLcode
schannel_connect_step3(struct connectdata *conn, int sockindex)
{
  CURLcode retcode = CURLE_OK;
  struct SessionHandle *data = conn->data;
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  struct curl_schannel_cred *old_cred = NULL;
  int incache;

  DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);

  infof(data, "schannel: SSL/TLS connection with %s port %hu (step 3/3)\n",
        conn->host.name, conn->remote_port);

  /* check if the required context attributes are met */
  if(connssl->ret_flags != connssl->req_flags) {
    if(!(connssl->ret_flags & ISC_RET_SEQUENCE_DETECT))
      failf(data, "schannel: failed to setup sequence detection");
    if(!(connssl->ret_flags & ISC_RET_REPLAY_DETECT))
      failf(data, "schannel: failed to setup replay detection");
    if(!(connssl->ret_flags & ISC_RET_CONFIDENTIALITY))
      failf(data, "schannel: failed to setup confidentiality");
    if(!(connssl->ret_flags & ISC_RET_EXTENDED_ERROR))
      failf(data, "schannel: failed to setup extended errors");
    if(!(connssl->ret_flags & ISC_RET_ALLOCATED_MEMORY))
      failf(data, "schannel: failed to setup memory allocation");
    if(!(connssl->ret_flags & ISC_RET_STREAM))
      failf(data, "schannel: failed to setup stream orientation");
    return CURLE_SSL_CONNECT_ERROR;
  }

  /* save the current session data for possible re-use */
  incache = !(Curl_ssl_getsessionid(conn, (void**)&old_cred, NULL));
  if(incache) {
    if(old_cred != connssl->cred) {
      infof(data, "schannel: old credential handle is stale, removing\n");
      Curl_ssl_delsessionid(conn, (void*)old_cred);
      incache = FALSE;
    }
  }
  if(!incache) {
    retcode = Curl_ssl_addsessionid(conn, (void*)connssl->cred,
                                    sizeof(struct curl_schannel_cred));
    if(retcode) {
      failf(data, "schannel: failed to store credential handle");
      return retcode;
    }
    else {
      infof(data, "schannel: stored crendential handle\n");
    }
  }

  connssl->connecting_state = ssl_connect_done;

  return CURLE_OK;
}
Esempio n. 2
0
static CURLcode
polarssl_connect_step3(struct connectdata *conn,
                     int sockindex)
{
  CURLcode retcode = CURLE_OK;
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  struct SessionHandle *data = conn->data;
  void *old_ssl_sessionid = NULL;
  ssl_session *our_ssl_sessionid = &conn->ssl[sockindex].ssn ;
  bool incache;

  DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);

  /* Save the current session data for possible re-use */
  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;
    }
  }

  if(!incache) {
    void *new_session = malloc(sizeof(ssl_session));

    if(new_session) {
      memcpy(new_session, our_ssl_sessionid,
             sizeof(ssl_session));

      retcode = Curl_ssl_addsessionid(conn, new_session,
                                   sizeof(ssl_session));
    }
    else {
      retcode = CURLE_OUT_OF_MEMORY;
    }

    if(retcode) {
      failf(data, "failed to store ssl session");
      return retcode;
    }
  }

  connssl->connecting_state = ssl_connect_done;

  return CURLE_OK;
}
Esempio n. 3
0
static CURLcode
mbed_connect_step3(struct connectdata *conn,
                   int sockindex)
{
  CURLcode retcode = CURLE_OK;
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  struct Curl_easy *data = conn->data;

  DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);

  if(SSL_SET_OPTION(primary.sessionid)) {
    int ret;
    mbedtls_ssl_session *our_ssl_sessionid;
    void *old_ssl_sessionid = NULL;

    our_ssl_sessionid = malloc(sizeof(mbedtls_ssl_session));
    if(!our_ssl_sessionid)
      return CURLE_OUT_OF_MEMORY;

    mbedtls_ssl_session_init(our_ssl_sessionid);

    ret = mbedtls_ssl_get_session(&BACKEND->ssl, our_ssl_sessionid);
    if(ret) {
      free(our_ssl_sessionid);
      failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret);
      return CURLE_SSL_CONNECT_ERROR;
    }

    /* If there's already a matching session in the cache, delete it */
    Curl_ssl_sessionid_lock(conn);
    if(!Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL, sockindex))
      Curl_ssl_delsessionid(conn, old_ssl_sessionid);

    retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid, 0, sockindex);
    Curl_ssl_sessionid_unlock(conn);
    if(retcode) {
      free(our_ssl_sessionid);
      failf(data, "failed to store ssl session");
      return retcode;
    }
  }

  connssl->connecting_state = ssl_connect_done;

  return CURLE_OK;
}
Esempio n. 4
0
static CURLcode
cyassl_connect_step3(struct connectdata *conn,
                     int sockindex)
{
  CURLcode result = CURLE_OK;
  struct Curl_easy *data = conn->data;
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];

  DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);

  if(SSL_SET_OPTION(primary.sessionid)) {
    bool incache;
    SSL_SESSION *our_ssl_sessionid;
    void *old_ssl_sessionid = NULL;

    our_ssl_sessionid = SSL_get_session(BACKEND->handle);

    Curl_ssl_sessionid_lock(conn);
    incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL,
                                      sockindex));
    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;
      }
    }

    if(!incache) {
      result = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
                                     0 /* unknown size */, sockindex);
      if(result) {
        Curl_ssl_sessionid_unlock(conn);
        failf(data, "failed to store ssl session");
        return result;
      }
    }
    Curl_ssl_sessionid_unlock(conn);
  }

  connssl->connecting_state = ssl_connect_done;

  return result;
}
Esempio n. 5
0
static CURLcode
polarssl_connect_step3(struct connectdata *conn,
                     int sockindex)
{
  CURLcode retcode = CURLE_OK;
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  struct SessionHandle *data = conn->data;
  void *old_ssl_sessionid = NULL;
  ssl_session *our_ssl_sessionid;
  int ret;

  DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);

  our_ssl_sessionid = malloc(sizeof(ssl_session));
  if(!our_ssl_sessionid)
    return CURLE_OUT_OF_MEMORY;

  ssl_session_init(our_ssl_sessionid);

  ret = ssl_get_session(&connssl->ssl, our_ssl_sessionid);
  if(ret) {
    failf(data, "ssl_get_session returned -0x%x", -ret);
    return CURLE_SSL_CONNECT_ERROR;
  }

  /* If there's already a matching session in the cache, delete it */
  if(!Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL))
    Curl_ssl_delsessionid(conn, old_ssl_sessionid);

  retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid, 0);
  if(retcode) {
    free(our_ssl_sessionid);
    failf(data, "failed to store ssl session");
    return retcode;
  }

  connssl->connecting_state = ssl_connect_done;

  return CURLE_OK;
}
Esempio n. 6
0
static CURLcode
cyassl_connect_step3(struct connectdata *conn,
                     int sockindex)
{
  CURLcode result = CURLE_OK;
  void *old_ssl_sessionid=NULL;
  struct SessionHandle *data = conn->data;
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  bool incache;
  SSL_SESSION *our_ssl_sessionid;

  DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);

  our_ssl_sessionid = SSL_get_session(connssl->handle);

  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;
    }
  }

  if(!incache) {
    result = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
                                   0 /* unknown size */);
    if(result) {
      failf(data, "failed to store ssl session");
      return result;
    }
  }

  connssl->connecting_state = ssl_connect_done;

  return result;
}
Esempio n. 7
0
/*
 * This function is called after the TCP connect has completed. Setup the TLS
 * layer and do all necessary magic.
 */
CURLcode
Curl_axtls_connect(struct connectdata *conn,
                  int sockindex)

{
  struct SessionHandle *data = conn->data;
  SSL_CTX *ssl_ctx;
  SSL *ssl;
  int cert_types[] = {SSL_OBJ_X509_CERT, SSL_OBJ_PKCS12, 0};
  int key_types[] = {SSL_OBJ_RSA_KEY, SSL_OBJ_PKCS8, SSL_OBJ_PKCS12, 0};
  int i, ssl_fcn_return;
  const uint8_t *ssl_sessionid;
  size_t ssl_idsize;
  const char *peer_CN;
  uint32_t dns_altname_index;
  const char *dns_altname;
  int8_t found_subject_alt_names = 0;
  int8_t found_subject_alt_name_matching_conn = 0;

  /* Assuming users will not compile in custom key/cert to axTLS */
  uint32_t client_option = SSL_NO_DEFAULT_KEY|SSL_SERVER_VERIFY_LATER;

  if(conn->ssl[sockindex].state == ssl_connection_complete)
    /* to make us tolerant against being called more than once for the
       same connection */
    return CURLE_OK;

  /* axTLS only supports TLSv1 */
  /* check to see if we've been told to use an explicit SSL/TLS version */
  switch(data->set.ssl.version) {
  case CURL_SSLVERSION_DEFAULT:
  case CURL_SSLVERSION_TLSv1:
    break;
  default:
    failf(data, "axTLS only supports TLSv1");
    return CURLE_SSL_CONNECT_ERROR;
  }

#ifdef  AXTLSDEBUG
  client_option |= SSL_DISPLAY_STATES | SSL_DISPLAY_RSA | SSL_DISPLAY_CERTS;
#endif /* AXTLSDEBUG */

  /* Allocate an SSL_CTX struct */
  ssl_ctx = ssl_ctx_new(client_option, SSL_DEFAULT_CLNT_SESS);
  if(ssl_ctx == NULL) {
    failf(data, "unable to create client SSL context");
    return CURLE_SSL_CONNECT_ERROR;
  }

  /* Load the trusted CA cert bundle file */
  if(data->set.ssl.CAfile) {
    if(ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CACERT, data->set.ssl.CAfile, NULL)
       != SSL_OK) {
      infof(data, "error reading ca cert file %s \n",
            data->set.ssl.CAfile);
      if(data->set.ssl.verifypeer) {
        Curl_axtls_close(conn, sockindex);
        return CURLE_SSL_CACERT_BADFILE;
      }
    }
    else
      infof(data, "found certificates in %s\n", data->set.ssl.CAfile);
  }

  /* curl_gtls.c tasks we're skipping for now:
   * 1) certificate revocation list checking
   * 2) dns name assignment to host
   * 3) set protocol priority.  axTLS is TLSv1 only, so can probably ignore
   * 4) set certificate priority.  axTLS ignores type and sends certs in
   *  order added.  can probably ignore this.
   */

  /* Load client certificate */
  if(data->set.str[STRING_CERT]) {
    i=0;
    /* Instead of trying to analyze cert type here, let axTLS try them all. */
    while(cert_types[i] != 0) {
      ssl_fcn_return = ssl_obj_load(ssl_ctx, cert_types[i],
                                    data->set.str[STRING_CERT], NULL);
      if(ssl_fcn_return == SSL_OK) {
        infof(data, "successfully read cert file %s \n",
              data->set.str[STRING_CERT]);
        break;
      }
      i++;
    }
    /* Tried all cert types, none worked. */
    if(cert_types[i] == 0) {
      failf(data, "%s is not x509 or pkcs12 format",
            data->set.str[STRING_CERT]);
      Curl_axtls_close(conn, sockindex);
      return CURLE_SSL_CERTPROBLEM;
    }
  }

  /* Load client key.
     If a pkcs12 file successfully loaded a cert, then there's nothing to do
     because the key has already been loaded. */
  if(data->set.str[STRING_KEY] && cert_types[i] != SSL_OBJ_PKCS12) {
    i=0;
    /* Instead of trying to analyze key type here, let axTLS try them all. */
    while(key_types[i] != 0) {
      ssl_fcn_return = ssl_obj_load(ssl_ctx, key_types[i],
                                    data->set.str[STRING_KEY], NULL);
      if(ssl_fcn_return == SSL_OK) {
        infof(data, "successfully read key file %s \n",
              data->set.str[STRING_KEY]);
        break;
      }
      i++;
    }
    /* Tried all key types, none worked. */
    if(key_types[i] == 0) {
      failf(data, "Failure: %s is not a supported key file",
            data->set.str[STRING_KEY]);
      Curl_axtls_close(conn, sockindex);
      return CURLE_SSL_CONNECT_ERROR;
    }
  }

  /* curl_gtls.c does more here that is being left out for now
   * 1) set session credentials.  can probably ignore since axtls puts this
   *    info in the ssl_ctx struct
   * 2) setting up callbacks.  these seem gnutls specific
   */

  /* In axTLS, handshaking happens inside ssl_client_new. */
  if(!Curl_ssl_getsessionid(conn, (void **) &ssl_sessionid, &ssl_idsize)) {
    /* we got a session id, use it! */
    infof (data, "SSL re-using session ID\n");
    ssl = ssl_client_new(ssl_ctx, conn->sock[sockindex],
                         ssl_sessionid, (uint8_t)ssl_idsize);
  }
  else
    ssl = ssl_client_new(ssl_ctx, conn->sock[sockindex], NULL, 0);

  /* Check to make sure handshake was ok. */
  ssl_fcn_return = ssl_handshake_status(ssl);
  if(ssl_fcn_return != SSL_OK) {
    Curl_axtls_close(conn, sockindex);
    ssl_display_error(ssl_fcn_return); /* goes to stdout. */
    return map_error_to_curl(ssl_fcn_return);
  }
  infof (data, "handshake completed successfully\n");

  /* Here, curl_gtls.c gets the peer certificates and fails out depending on
   * settings in "data."  axTLS api doesn't have get cert chain fcn, so omit?
   */

  /* Verify server's certificate */
  if(data->set.ssl.verifypeer) {
    if(ssl_verify_cert(ssl) != SSL_OK) {
      Curl_axtls_close(conn, sockindex);
      failf(data, "server cert verify failed");
      return CURLE_SSL_CONNECT_ERROR;
    }
  }
  else
    infof(data, "\t server certificate verification SKIPPED\n");

  /* Here, curl_gtls.c does issuer verification. axTLS has no straightforward
   * equivalent, so omitting for now.*/

  /* Here, curl_gtls.c does the following
   * 1) x509 hostname checking per RFC2818.  axTLS doesn't support this, but
   *    it seems useful. This is now implemented, by Oscar Koeroo
   * 2) checks cert validity based on time.  axTLS does this in ssl_verify_cert
   * 3) displays a bunch of cert information.  axTLS doesn't support most of
   *    this, but a couple fields are available.
   */


  /* There is no (DNS) Altnames count in the version 1.4.8 API. There is a
     risk of an inifite loop */
  for(dns_altname_index = 0; ; dns_altname_index++) {
    dns_altname = ssl_get_cert_subject_alt_dnsname(ssl, dns_altname_index);
    if(dns_altname == NULL) {
      break;
    }
    found_subject_alt_names = 1;

    infof(data, "\tComparing subject alt name DNS with hostname: %s <-> %s\n",
          dns_altname, conn->host.name);
    if(Curl_cert_hostcheck(dns_altname, conn->host.name)) {
      found_subject_alt_name_matching_conn = 1;
      break;
    }
  }

  /* RFC2818 checks */
  if(found_subject_alt_names && !found_subject_alt_name_matching_conn) {
    /* Break connection ! */
    Curl_axtls_close(conn, sockindex);
    failf(data, "\tsubjectAltName(s) do not match %s\n", conn->host.dispname);
    return CURLE_PEER_FAILED_VERIFICATION;
  }
  else if(found_subject_alt_names == 0) {
    /* Per RFC2818, when no Subject Alt Names were available, examine the peer
       CN as a legacy fallback */
    peer_CN = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME);
    if(peer_CN == NULL) {
      /* Similar behaviour to the OpenSSL interface */
      Curl_axtls_close(conn, sockindex);
      failf(data, "unable to obtain common name from peer certificate");
      return CURLE_PEER_FAILED_VERIFICATION;
    }
    else {
      if(!Curl_cert_hostcheck((const char *)peer_CN, conn->host.name)) {
        if(data->set.ssl.verifyhost) {
          /* Break connection ! */
          Curl_axtls_close(conn, sockindex);
          failf(data, "\tcommon name \"%s\" does not match \"%s\"\n",
                peer_CN, conn->host.dispname);
          return CURLE_PEER_FAILED_VERIFICATION;
        }
        else
          infof(data, "\tcommon name \"%s\" does not match \"%s\"\n",
                peer_CN, conn->host.dispname);
      }
    }
  }

  /* General housekeeping */
  conn->ssl[sockindex].state = ssl_connection_complete;
  conn->ssl[sockindex].ssl = ssl;
  conn->ssl[sockindex].ssl_ctx = ssl_ctx;
  conn->recv[sockindex] = axtls_recv;
  conn->send[sockindex] = axtls_send;

  /* Put our freshly minted SSL session in cache */
  ssl_idsize = ssl_get_session_id_size(ssl);
  ssl_sessionid = ssl_get_session_id(ssl);
  if(Curl_ssl_addsessionid(conn, (void *) ssl_sessionid, ssl_idsize)
     != CURLE_OK)
    infof (data, "failed to add session to cache\n");

  return CURLE_OK;
}
Esempio n. 8
0
/*
 * This function is called after the TCP connect has completed. Setup the TLS
 * layer and do all necessary magic.
 */
CURLcode
Curl_gtls_connect(struct connectdata *conn,
                  int sockindex)

{
    const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
    struct SessionHandle *data = conn->data;
    gnutls_session session;
    int rc;
    unsigned int cert_list_size;
    const gnutls_datum *chainp;
    unsigned int verify_status;
    gnutls_x509_crt x509_cert;
    char certbuf[256]; /* big enough? */
    size_t size;
    unsigned int algo;
    unsigned int bits;
    time_t clock;
    const char *ptr;
    void *ssl_sessionid;
    size_t ssl_idsize;

    /* GnuTLS only supports TLSv1 (and SSLv3?) */
    if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) {
        failf(data, "GnuTLS does not support SSLv2");
        return CURLE_SSL_CONNECT_ERROR;
    }

    /* allocate a cred struct */
    rc = gnutls_certificate_allocate_credentials(&conn->ssl[sockindex].cred);
    if(rc < 0) {
        failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc));
        return CURLE_SSL_CONNECT_ERROR;
    }

    if(data->set.ssl.CAfile) {
        /* set the trusted CA cert bundle file */
        gnutls_certificate_set_verify_flags(conn->ssl[sockindex].cred,
                                            GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);

        rc = gnutls_certificate_set_x509_trust_file(conn->ssl[sockindex].cred,
                data->set.ssl.CAfile,
                GNUTLS_X509_FMT_PEM);
        if(rc < 0) {
            infof(data, "error reading ca cert file %s (%s)\n",
                  data->set.ssl.CAfile, gnutls_strerror(rc));
            if (data->set.ssl.verifypeer)
                return CURLE_SSL_CACERT_BADFILE;
        }
        else
            infof(data, "found %d certificates in %s\n",
                  rc, data->set.ssl.CAfile);
    }

    /* Initialize TLS session as a client */
    rc = gnutls_init(&conn->ssl[sockindex].session, GNUTLS_CLIENT);
    if(rc) {
        failf(data, "gnutls_init() failed: %d", rc);
        return CURLE_SSL_CONNECT_ERROR;
    }

    /* convenient assign */
    session = conn->ssl[sockindex].session;

    /* Use default priorities */
    rc = gnutls_set_default_priority(session);
    if(rc < 0)
        return CURLE_SSL_CONNECT_ERROR;

    /* Sets the priority on the certificate types supported by gnutls. Priority
       is higher for types specified before others. After specifying the types
       you want, you must append a 0. */
    rc = gnutls_certificate_type_set_priority(session, cert_type_priority);
    if(rc < 0)
        return CURLE_SSL_CONNECT_ERROR;

    if(data->set.cert) {
        if( gnutls_certificate_set_x509_key_file(
                    conn->ssl[sockindex].cred, data->set.cert,
                    data->set.key != 0 ? data->set.key : data->set.cert,
                    do_file_type(data->set.cert_type) ) ) {
            failf(data, "error reading X.509 key or certificate file");
            return CURLE_SSL_CONNECT_ERROR;
        }
    }

    /* put the credentials to the current session */
    rc = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
                                conn->ssl[sockindex].cred);

    /* set the connection handle (file descriptor for the socket) */
    gnutls_transport_set_ptr(session,
                             (gnutls_transport_ptr)conn->sock[sockindex]);

    /* register callback functions to send and receive data. */
    gnutls_transport_set_push_function(session, Curl_gtls_push);
    gnutls_transport_set_pull_function(session, Curl_gtls_pull);

    /* lowat must be set to zero when using custom push and pull functions. */
    gnutls_transport_set_lowat(session, 0);

    /* This might be a reconnect, so we check for a session ID in the cache
       to speed up things */

    if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, &ssl_idsize)) {
        /* we got a session id, use it! */
        gnutls_session_set_data(session, ssl_sessionid, ssl_idsize);

        /* Informational message */
        infof (data, "SSL re-using session ID\n");
    }

    rc = handshake(conn, session, sockindex, TRUE);
    if(rc)
        /* handshake() sets its own error message with failf() */
        return rc;

    /* This function will return the peer's raw certificate (chain) as sent by
       the peer. These certificates are in raw format (DER encoded for
       X.509). In case of a X.509 then a certificate list may be present. The
       first certificate in the list is the peer's certificate, following the
       issuer's certificate, then the issuer's issuer etc. */

    chainp = gnutls_certificate_get_peers(session, &cert_list_size);
    if(!chainp) {
        if(data->set.ssl.verifyhost) {
            failf(data, "failed to get server cert");
            return CURLE_SSL_PEER_CERTIFICATE;
        }
        infof(data, "\t common name: WARNING couldn't obtain\n");
    }

    /* This function will try to verify the peer's certificate and return its
       status (trusted, invalid etc.). The value of status should be one or more
       of the gnutls_certificate_status_t enumerated elements bitwise or'd. To
       avoid denial of service attacks some default upper limits regarding the
       certificate key size and chain size are set. To override them use
       gnutls_certificate_set_verify_limits(). */

    rc = gnutls_certificate_verify_peers2(session, &verify_status);
    if (rc < 0) {
        failf(data, "server cert verify failed: %d", rc);
        return CURLE_SSL_CONNECT_ERROR;
    }

    /* verify_status is a bitmask of gnutls_certificate_status bits */
    if(verify_status & GNUTLS_CERT_INVALID) {
        if (data->set.ssl.verifypeer) {
            failf(data, "server certificate verification failed. CAfile: %s",
                  data->set.ssl.CAfile?data->set.ssl.CAfile:"none");
            return CURLE_SSL_CACERT;
        }
        else
            infof(data, "\t server certificate verification FAILED\n");
    }
    else
        infof(data, "\t server certificate verification OK\n");

    /* initialize an X.509 certificate structure. */
    gnutls_x509_crt_init(&x509_cert);

    /* convert the given DER or PEM encoded Certificate to the native
       gnutls_x509_crt_t format */
    gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER);

    size=sizeof(certbuf);
    rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME,
                                       0, /* the first and only one */
                                       FALSE,
                                       certbuf,
                                       &size);
    if(rc) {
        infof(data, "error fetching CN from cert:%s\n",
              gnutls_strerror(rc));
    }

    /* This function will check if the given certificate's subject matches the
       given hostname. This is a basic implementation of the matching described
       in RFC2818 (HTTPS), which takes into account wildcards, and the subject
       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(!rc) {
        if (data->set.ssl.verifyhost > 1) {
            failf(data, "SSL: certificate subject name (%s) does not match "
                  "target host name '%s'", certbuf, conn->host.dispname);
            gnutls_x509_crt_deinit(x509_cert);
            return CURLE_SSL_PEER_CERTIFICATE;
        }
        else
            infof(data, "\t common name: %s (does not match '%s')\n",
                  certbuf, conn->host.dispname);
    }
    else
        infof(data, "\t common name: %s (matched)\n", certbuf);

    /* Show:

    - ciphers used
    - subject
    - start date
    - expire date
    - common name
    - issuer

    */

    /* public key algorithm's parameters */
    algo = gnutls_x509_crt_get_pk_algorithm(x509_cert, &bits);
    infof(data, "\t certificate public key: %s\n",
          gnutls_pk_algorithm_get_name(algo));

    /* version of the X.509 certificate. */
    infof(data, "\t certificate version: #%d\n",
          gnutls_x509_crt_get_version(x509_cert));


    size = sizeof(certbuf);
    gnutls_x509_crt_get_dn(x509_cert, certbuf, &size);
    infof(data, "\t subject: %s\n", certbuf);

    clock = gnutls_x509_crt_get_activation_time(x509_cert);
    showtime(data, "start date", clock);

    clock = gnutls_x509_crt_get_expiration_time(x509_cert);
    showtime(data, "expire date", clock);

    size = sizeof(certbuf);
    gnutls_x509_crt_get_issuer_dn(x509_cert, certbuf, &size);
    infof(data, "\t issuer: %s\n", certbuf);

    gnutls_x509_crt_deinit(x509_cert);

    /* compression algorithm (if any) */
    ptr = gnutls_compression_get_name(gnutls_compression_get(session));
    /* the *_get_name() says "NULL" if GNUTLS_COMP_NULL is returned */
    infof(data, "\t compression: %s\n", ptr);

    /* the name of the cipher used. ie 3DES. */
    ptr = gnutls_cipher_get_name(gnutls_cipher_get(session));
    infof(data, "\t cipher: %s\n", ptr);

    /* the MAC algorithms name. ie SHA1 */
    ptr = gnutls_mac_get_name(gnutls_mac_get(session));
    infof(data, "\t MAC: %s\n", ptr);

    if(!ssl_sessionid) {
        /* this session was not previously in the cache, add it now */

        /* get the session ID data size */
        gnutls_session_get_data(session, NULL, &ssl_idsize);
        ssl_sessionid = malloc(ssl_idsize); /* get a buffer for it */

        if(ssl_sessionid) {
            /* extract session ID to the allocated buffer */
            gnutls_session_get_data(session, ssl_sessionid, &ssl_idsize);

            /* store this session id */
            return Curl_ssl_addsessionid(conn, ssl_sessionid, ssl_idsize);
        }
    }

    return CURLE_OK;
}
Esempio n. 9
0
static CURLcode
polarssl_connect_step2(struct connectdata *conn,
                     int sockindex)
{
  int ret;
  struct SessionHandle *data = conn->data;
  struct ssl_connect_data* connssl = &conn->ssl[sockindex];
  char buffer[1024];

  char errorbuf[128];
  memset(errorbuf, 0, sizeof(errorbuf));

  conn->recv[sockindex] = polarssl_recv;
  conn->send[sockindex] = polarssl_send;

  for(;;) {
    if(!(ret = ssl_handshake(&connssl->ssl)))
      break;
    else if(ret != POLARSSL_ERR_NET_WANT_READ &&
            ret != POLARSSL_ERR_NET_WANT_WRITE) {
#ifdef POLARSSL_ERROR_C
     error_strerror(ret, errorbuf, sizeof(errorbuf));
#endif /* POLARSSL_ERROR_C */
     failf(data, "ssl_handshake returned - PolarSSL: (-0x%04X) %s",
                                                    -ret, errorbuf);

     return CURLE_SSL_CONNECT_ERROR;
    }
    else {
      if(ret == POLARSSL_ERR_NET_WANT_READ) {
        connssl->connecting_state = ssl_connect_2_reading;
        return CURLE_OK;
      }
      if(ret == POLARSSL_ERR_NET_WANT_WRITE) {
        connssl->connecting_state = ssl_connect_2_writing;
        return CURLE_OK;
      }
      failf(data, "SSL_connect failed with error %d.", ret);
      return CURLE_SSL_CONNECT_ERROR;

    }
  }

  infof(data, "PolarSSL: Handshake complete, cipher is %s\n",
#if POLARSSL_VERSION_NUMBER<0x01000000
        ssl_get_cipher(&conn->ssl[sockindex].ssl)
#elif POLARSSL_VERSION_NUMBER >= 0x01010000
        ssl_get_ciphersuite(&conn->ssl[sockindex].ssl)
#else
        ssl_get_ciphersuite_name(&conn->ssl[sockindex].ssl)
#endif
    );

  ret = ssl_get_verify_result(&conn->ssl[sockindex].ssl);

  if(ret && data->set.ssl.verifypeer) {
    if(ret & BADCERT_EXPIRED)
      failf(data, "Cert verify failed: BADCERT_EXPIRED");

    if(ret & BADCERT_REVOKED) {
      failf(data, "Cert verify failed: BADCERT_REVOKED");
      return CURLE_SSL_CACERT;
    }

    if(ret & BADCERT_CN_MISMATCH)
      failf(data, "Cert verify failed: BADCERT_CN_MISMATCH");

    if(ret & BADCERT_NOT_TRUSTED)
      failf(data, "Cert verify failed: BADCERT_NOT_TRUSTED");

    return CURLE_PEER_FAILED_VERIFICATION;
  }

/* PolarSSL SVN revision r1316 to r1317, matching <1.2.0 is to cover Ubuntu's
   1.1.4 version and the like */
#if POLARSSL_VERSION_NUMBER<0x01020000
  if(conn->ssl[sockindex].ssl.peer_cert) {
#else
  if(ssl_get_peer_cert(&(connssl->ssl))) {
#endif
    /* If the session was resumed, there will be no peer certs */
    memset(buffer, 0, sizeof(buffer));

/* PolarSSL SVN revision r1316 to r1317, matching <1.2.0 is to cover Ubuntu's
   1.1.4 version and the like */
#if POLARSSL_VERSION_NUMBER<0x01020000
    if(x509parse_cert_info(buffer, sizeof(buffer), (char *)"* ",
                           conn->ssl[sockindex].ssl.peer_cert) != -1)
#else
    if(x509parse_cert_info(buffer, sizeof(buffer), (char *)"* ",
                           ssl_get_peer_cert(&(connssl->ssl))) != -1)
#endif
      infof(data, "Dumping cert info:\n%s\n", buffer);
  }

  connssl->connecting_state = ssl_connect_3;
  infof(data, "SSL connected\n");

  return CURLE_OK;
}

static CURLcode
polarssl_connect_step3(struct connectdata *conn,
                     int sockindex)
{
  CURLcode retcode = CURLE_OK;
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  struct SessionHandle *data = conn->data;
  void *old_ssl_sessionid = NULL;
  ssl_session *our_ssl_sessionid = &conn->ssl[sockindex].ssn ;
  int incache;

  DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);

  /* Save the current session data for possible re-use */
  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;
    }
  }
  if(!incache) {
    void *new_session = malloc(sizeof(ssl_session));

    if(new_session) {
      memcpy(new_session, our_ssl_sessionid,
             sizeof(ssl_session));

      retcode = Curl_ssl_addsessionid(conn, new_session,
                                   sizeof(ssl_session));
    }
    else {
      retcode = CURLE_OUT_OF_MEMORY;
    }

    if(retcode) {
      failf(data, "failed to store ssl session");
      return retcode;
    }
  }

  connssl->connecting_state = ssl_connect_done;

  return CURLE_OK;
}
Esempio n. 10
0
static CURLcode
gtls_connect_step3(struct connectdata *conn,
                   int sockindex)
{
  unsigned int cert_list_size;
  const gnutls_datum *chainp;
  unsigned int verify_status;
  gnutls_x509_crt x509_cert,x509_issuer;
  gnutls_datum issuerp;
  char certbuf[256]; /* big enough? */
  size_t size;
  unsigned int algo;
  unsigned int bits;
  time_t certclock;
  const char *ptr;
  struct SessionHandle *data = conn->data;
  gnutls_session session = conn->ssl[sockindex].session;
  int rc;
  int incache;
  void *ssl_sessionid;
  CURLcode result = CURLE_OK;

  /* This function will return the peer's raw certificate (chain) as sent by
     the peer. These certificates are in raw format (DER encoded for
     X.509). In case of a X.509 then a certificate list may be present. The
     first certificate in the list is the peer's certificate, following the
     issuer's certificate, then the issuer's issuer etc. */

  chainp = gnutls_certificate_get_peers(session, &cert_list_size);
  if(!chainp) {
    if(data->set.ssl.verifypeer ||
       data->set.ssl.verifyhost ||
       data->set.ssl.issuercert) {
#ifdef USE_TLS_SRP
      if(data->set.ssl.authtype == CURL_TLSAUTH_SRP
         && data->set.ssl.username != NULL
         && !data->set.ssl.verifypeer
         && gnutls_cipher_get(session)) {
        /* no peer cert, but auth is ok if we have SRP user and cipher and no
           peer verify */
      }
      else {
#endif
        failf(data, "failed to get server cert");
        return CURLE_PEER_FAILED_VERIFICATION;
#ifdef USE_TLS_SRP
      }
#endif
    }
    infof(data, "\t common name: WARNING couldn't obtain\n");
  }

  if(data->set.ssl.verifypeer) {
    /* This function will try to verify the peer's certificate and return its
       status (trusted, invalid etc.). The value of status should be one or
       more of the gnutls_certificate_status_t enumerated elements bitwise
       or'd. To avoid denial of service attacks some default upper limits
       regarding the certificate key size and chain size are set. To override
       them use gnutls_certificate_set_verify_limits(). */

    rc = gnutls_certificate_verify_peers2(session, &verify_status);
    if(rc < 0) {
      failf(data, "server cert verify failed: %d", rc);
      return CURLE_SSL_CONNECT_ERROR;
    }

    /* verify_status is a bitmask of gnutls_certificate_status bits */
    if(verify_status & GNUTLS_CERT_INVALID) {
      if(data->set.ssl.verifypeer) {
        failf(data, "server certificate verification failed. CAfile: %s "
              "CRLfile: %s", data->set.ssl.CAfile?data->set.ssl.CAfile:"none",
              data->set.ssl.CRLfile?data->set.ssl.CRLfile:"none");
        return CURLE_SSL_CACERT;
      }
      else
        infof(data, "\t server certificate verification FAILED\n");
    }
    else
      infof(data, "\t server certificate verification OK\n");
  }
  else {
    infof(data, "\t server certificate verification SKIPPED\n");
    goto after_server_cert_verification;
  }

  /* initialize an X.509 certificate structure. */
  gnutls_x509_crt_init(&x509_cert);

  /* convert the given DER or PEM encoded Certificate to the native
     gnutls_x509_crt_t format */
  gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER);

  if(data->set.ssl.issuercert) {
    gnutls_x509_crt_init(&x509_issuer);
    issuerp = load_file(data->set.ssl.issuercert);
    gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM);
    rc = gnutls_x509_crt_check_issuer(x509_cert,x509_issuer);
    unload_file(issuerp);
    if(rc <= 0) {
      failf(data, "server certificate issuer check failed (IssuerCert: %s)",
            data->set.ssl.issuercert?data->set.ssl.issuercert:"none");
      return CURLE_SSL_ISSUER_ERROR;
    }
    infof(data,"\t server certificate issuer check OK (Issuer Cert: %s)\n",
          data->set.ssl.issuercert?data->set.ssl.issuercert:"none");
  }

  size=sizeof(certbuf);
  rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME,
                                     0, /* the first and only one */
                                     FALSE,
                                     certbuf,
                                     &size);
  if(rc) {
    infof(data, "error fetching CN from cert:%s\n",
          gnutls_strerror(rc));
  }

  /* This function will check if the given certificate's subject matches the
     given hostname. This is a basic implementation of the matching described
     in RFC2818 (HTTPS), which takes into account wildcards, and the subject
     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(!rc) {
    if(data->set.ssl.verifyhost) {
      failf(data, "SSL: certificate subject name (%s) does not match "
            "target host name '%s'", certbuf, conn->host.dispname);
      gnutls_x509_crt_deinit(x509_cert);
      return CURLE_PEER_FAILED_VERIFICATION;
    }
    else
      infof(data, "\t common name: %s (does not match '%s')\n",
            certbuf, conn->host.dispname);
  }
  else
    infof(data, "\t common name: %s (matched)\n", certbuf);

  /* Check for time-based validity */
  certclock = gnutls_x509_crt_get_expiration_time(x509_cert);

  if(certclock == (time_t)-1) {
    failf(data, "server cert expiration date verify failed");
    return CURLE_SSL_CONNECT_ERROR;
  }

  if(certclock < time(NULL)) {
    if(data->set.ssl.verifypeer) {
      failf(data, "server certificate expiration date has passed.");
      return CURLE_PEER_FAILED_VERIFICATION;
    }
    else
      infof(data, "\t server certificate expiration date FAILED\n");
  }
  else
    infof(data, "\t server certificate expiration date OK\n");

  certclock = gnutls_x509_crt_get_activation_time(x509_cert);

  if(certclock == (time_t)-1) {
    failf(data, "server cert activation date verify failed");
    return CURLE_SSL_CONNECT_ERROR;
  }

  if(certclock > time(NULL)) {
    if(data->set.ssl.verifypeer) {
      failf(data, "server certificate not activated yet.");
      return CURLE_PEER_FAILED_VERIFICATION;
    }
    else
      infof(data, "\t server certificate activation date FAILED\n");
  }
  else
    infof(data, "\t server certificate activation date OK\n");

  /* Show:

  - ciphers used
  - subject
  - start date
  - expire date
  - common name
  - issuer

  */

  /* public key algorithm's parameters */
  algo = gnutls_x509_crt_get_pk_algorithm(x509_cert, &bits);
  infof(data, "\t certificate public key: %s\n",
        gnutls_pk_algorithm_get_name(algo));

  /* version of the X.509 certificate. */
  infof(data, "\t certificate version: #%d\n",
        gnutls_x509_crt_get_version(x509_cert));


  size = sizeof(certbuf);
  gnutls_x509_crt_get_dn(x509_cert, certbuf, &size);
  infof(data, "\t subject: %s\n", certbuf);

  certclock = gnutls_x509_crt_get_activation_time(x509_cert);
  showtime(data, "start date", certclock);

  certclock = gnutls_x509_crt_get_expiration_time(x509_cert);
  showtime(data, "expire date", certclock);

  size = sizeof(certbuf);
  gnutls_x509_crt_get_issuer_dn(x509_cert, certbuf, &size);
  infof(data, "\t issuer: %s\n", certbuf);

  gnutls_x509_crt_deinit(x509_cert);

after_server_cert_verification:

  /* compression algorithm (if any) */
  ptr = gnutls_compression_get_name(gnutls_compression_get(session));
  /* the *_get_name() says "NULL" if GNUTLS_COMP_NULL is returned */
  infof(data, "\t compression: %s\n", ptr);

  /* the name of the cipher used. ie 3DES. */
  ptr = gnutls_cipher_get_name(gnutls_cipher_get(session));
  infof(data, "\t cipher: %s\n", ptr);

  /* the MAC algorithms name. ie SHA1 */
  ptr = gnutls_mac_get_name(gnutls_mac_get(session));
  infof(data, "\t MAC: %s\n", ptr);

  conn->ssl[sockindex].state = ssl_connection_complete;
  conn->recv[sockindex] = gtls_recv;
  conn->send[sockindex] = gtls_send;

  {
    /* we always unconditionally get the session id here, as even if we
       already got it from the cache and asked to use it in the connection, it
       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;

    /* get the session ID data size */
    gnutls_session_get_data(session, NULL, &connect_idsize);
    connect_sessionid = malloc(connect_idsize); /* get a buffer for it */

    if(connect_sessionid) {
      /* extract session ID to the allocated buffer */
      gnutls_session_get_data(session, connect_sessionid, &connect_idsize);

      incache = !(Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL));
      if(incache) {
        /* there was one before in the cache, so instead of risking that the
           previous one was rejected, we just kill that and store the new */
        Curl_ssl_delsessionid(conn, ssl_sessionid);
      }

      /* store this session id */
      result = Curl_ssl_addsessionid(conn, connect_sessionid, connect_idsize);
      if(result) {
        free(connect_sessionid);
        result = CURLE_OUT_OF_MEMORY;
      }
    }
    else
      result = CURLE_OUT_OF_MEMORY;
  }

  return result;
}
Esempio n. 11
0
/*
 * For both blocking and non-blocking connects, this function finalizes the
 * SSL connection.
 */
static CURLcode connect_finish(struct connectdata *conn, int sockindex)
{
  struct SessionHandle *data = conn->data;
  SSL *ssl = conn->ssl[sockindex].ssl;
  const uint8_t *ssl_sessionid;
  size_t ssl_idsize;
  const char *peer_CN;
  uint32_t dns_altname_index;
  const char *dns_altname;
  int8_t found_subject_alt_names = 0;
  int8_t found_subject_alt_name_matching_conn = 0;

  /* Here, gtls.c gets the peer certificates and fails out depending on
   * settings in "data."  axTLS api doesn't have get cert chain fcn, so omit?
   */

  /* Verify server's certificate */
  if(data->set.ssl.verifypeer) {
    if(ssl_verify_cert(ssl) != SSL_OK) {
      Curl_axtls_close(conn, sockindex);
      failf(data, "server cert verify failed");
      return CURLE_PEER_FAILED_VERIFICATION;
    }
  }
  else
    infof(data, "\t server certificate verification SKIPPED\n");

  /* Here, gtls.c does issuer verification. axTLS has no straightforward
   * equivalent, so omitting for now.*/

  /* Here, gtls.c does the following
   * 1) x509 hostname checking per RFC2818.  axTLS doesn't support this, but
   *    it seems useful. This is now implemented, by Oscar Koeroo
   * 2) checks cert validity based on time.  axTLS does this in ssl_verify_cert
   * 3) displays a bunch of cert information.  axTLS doesn't support most of
   *    this, but a couple fields are available.
   */

  /* There is no (DNS) Altnames count in the version 1.4.8 API. There is a
     risk of an inifite loop */
  for(dns_altname_index = 0; ; dns_altname_index++) {
    dns_altname = ssl_get_cert_subject_alt_dnsname(ssl, dns_altname_index);
    if(dns_altname == NULL) {
      break;
    }
    found_subject_alt_names = 1;

    infof(data, "\tComparing subject alt name DNS with hostname: %s <-> %s\n",
          dns_altname, conn->host.name);
    if(Curl_cert_hostcheck(dns_altname, conn->host.name)) {
      found_subject_alt_name_matching_conn = 1;
      break;
    }
  }

  /* RFC2818 checks */
  if(found_subject_alt_names && !found_subject_alt_name_matching_conn) {
    if(data->set.ssl.verifyhost) {
      /* Break connection ! */
      Curl_axtls_close(conn, sockindex);
      failf(data, "\tsubjectAltName(s) do not match %s\n",
            conn->host.dispname);
      return CURLE_PEER_FAILED_VERIFICATION;
    }
    else
      infof(data, "\tsubjectAltName(s) do not match %s\n",
            conn->host.dispname);
  }
  else if(found_subject_alt_names == 0) {
    /* Per RFC2818, when no Subject Alt Names were available, examine the peer
       CN as a legacy fallback */
    peer_CN = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME);
    if(peer_CN == NULL) {
      if(data->set.ssl.verifyhost) {
        Curl_axtls_close(conn, sockindex);
        failf(data, "unable to obtain common name from peer certificate");
        return CURLE_PEER_FAILED_VERIFICATION;
      }
      else
        infof(data, "unable to obtain common name from peer certificate");
    }
    else {
      if(!Curl_cert_hostcheck((const char *)peer_CN, conn->host.name)) {
        if(data->set.ssl.verifyhost) {
          /* Break connection ! */
          Curl_axtls_close(conn, sockindex);
          failf(data, "\tcommon name \"%s\" does not match \"%s\"\n",
                peer_CN, conn->host.dispname);
          return CURLE_PEER_FAILED_VERIFICATION;
        }
        else
          infof(data, "\tcommon name \"%s\" does not match \"%s\"\n",
                peer_CN, conn->host.dispname);
      }
    }
  }

  /* General housekeeping */
  conn->ssl[sockindex].state = ssl_connection_complete;
  conn->recv[sockindex] = axtls_recv;
  conn->send[sockindex] = axtls_send;

  /* Put our freshly minted SSL session in cache */
  ssl_idsize = ssl_get_session_id_size(ssl);
  ssl_sessionid = ssl_get_session_id(ssl);
  if(Curl_ssl_addsessionid(conn, (void *) ssl_sessionid, ssl_idsize)
     != CURLE_OK)
    infof (data, "failed to add session to cache\n");

  return CURLE_OK;
}
Esempio n. 12
0
/* ====================================================== */
CURLcode
Curl_ossl_connect(struct connectdata *conn,
                  int sockindex)
{
  CURLcode retcode = CURLE_OK;

  struct SessionHandle *data = conn->data;
  int err;
  long lerr;
  int what;
  char * str;
  SSL_METHOD_QUAL SSL_METHOD *req_method=NULL;
  void *ssl_sessionid=NULL;
  ASN1_TIME *certdate;
  curl_socket_t sockfd = conn->sock[sockindex];
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];

  if(!ssl_seeded || data->set.ssl.random_file || data->set.ssl.egdsocket) {
    /* Make funny stuff to get random input */
    random_the_seed(data);

    ssl_seeded = TRUE;
  }

  /* check to see if we've been told to use an explicit SSL/TLS version */
  switch(data->set.ssl.version) {
  default:
  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;
  case CURL_SSLVERSION_SSLv3:
    req_method = SSLv3_client_method();
    break;
  }

  connssl->ctx = SSL_CTX_new(req_method);

  if(!connssl->ctx) {
    failf(data, "SSL: couldn't create a context!");
    return CURLE_OUT_OF_MEMORY;
  }

#ifdef SSL_CTRL_SET_MSG_CALLBACK
  if (data->set.fdebug) {
    /* 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)) {
      infof(data, "SSL: couldn't set callback!");
    }
    else if (!SSL_CTX_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK_ARG, 0,
                           conn)) {
      infof(data, "SSL: couldn't set callback argument!");
    }
  }
#endif

  /* 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
     ll 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);

#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

  if(data->set.cert) {
    if(!cert_stuff(conn,
                   connssl->ctx,
                   data->set.cert,
                   data->set.cert_type,
                   data->set.key,
                   data->set.key_type)) {
      /* failf() is already done in cert_stuff() */
      return CURLE_SSL_CERTPROBLEM;
    }
  }

  if(data->set.ssl.cipher_list) {
    if(!SSL_CTX_set_cipher_list(connssl->ctx,
                                data->set.ssl.cipher_list)) {
      failf(data, "failed setting cipher list");
      return CURLE_SSL_CIPHER;
    }
  }

  if (data->set.ssl.CAfile || data->set.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.ssl.CAfile,
                                       data->set.ssl.CApath)) {
      if (data->set.ssl.verifypeer) {
        /* 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.ssl.CAfile ? data->set.ssl.CAfile : "none",
              data->set.ssl.CApath ? data->set.ssl.CApath : "none");
        return CURLE_SSL_CACERT;
      }
      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");
    }
    infof(data,
          "  CAfile: %s\n"
          "  CApath: %s\n",
          data->set.ssl.CAfile ? data->set.ssl.CAfile : "none",
          data->set.ssl.CApath ? data->set.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. */
  SSL_CTX_set_verify(connssl->ctx,
                     data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
                     cert_verify_callback);

  /* give application a chance to interfere with SSL set up. */
  if(data->set.ssl.fsslctx) {
    retcode = (*data->set.ssl.fsslctx)(data, connssl->ctx,
                                       data->set.ssl.fsslctxp);
    if(retcode) {
      failf(data,"error signaled by ssl ctx callback");
      return retcode;
    }
  }

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

  connssl->server_cert = 0x0;

  /* 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;
  }

  while(1) {
    int writefd;
    int readfd;
    long timeout_ms;
    long has_passed;

    /* Find out if any timeout is set. If not, use 300 seconds.
       Otherwise, figure out the most strict timeout of the two possible one
       and then how much time that has elapsed to know how much time we
       allow for the connect call */
    if(data->set.timeout || data->set.connecttimeout) {

      /* get the most strict timeout of the ones converted to milliseconds */
      if(data->set.timeout &&
         (data->set.timeout>data->set.connecttimeout))
        timeout_ms = data->set.timeout*1000;
      else
        timeout_ms = data->set.connecttimeout*1000;
    }
    else
      /* no particular time-out has been set */
      timeout_ms= DEFAULT_CONNECT_TIMEOUT;

    /* Evaluate in milliseconds how much time that has passed */
    has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);

    /* subtract the passed time */
    timeout_ms -= has_passed;

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

    readfd = CURL_SOCKET_BAD;
    writefd = CURL_SOCKET_BAD;

    err = SSL_connect(connssl->handle);

    /* 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)
        readfd = sockfd;
      else if(SSL_ERROR_WANT_WRITE == detail)
        writefd = sockfd;
      else {
        /* untreated error */
        unsigned long errdetail;
        char error_buffer[120]; /* OpenSSL documents that this must be at least
                                   120 bytes long. */
        CURLcode rc;
        const char *cert_problem = NULL;

        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;
          break;
        }

          /* 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, get out of the connect loop */
      break;

    while(1) {
      what = Curl_select(readfd, writefd, (int)timeout_ms);
      if(what > 0)
        /* reabable or writable, go loop in the outer loop */
        break;
      else if(0 == what) {
        /* timeout */
        failf(data, "SSL connection timeout");
        return CURLE_OPERATION_TIMEDOUT;
      }
      else {
        /* anything that gets here is fatally bad */
        failf(data, "select on SSL socket, errno: %d", Curl_ourerrno());
        return CURLE_SSL_CONNECT_ERROR;
      }
    } /* while()-loop for the select() */
  } /* while()-loop for the SSL_connect() */

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

  if(!ssl_sessionid) {
    /* Since this is not a cached session ID, then we want to stach this one
       in the cache! */
    SSL_SESSION *ssl_sessionid;
#ifdef HAVE_SSL_GET1_SESSION
    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
    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, ssl_sessionid,
                                    0 /* unknown size */);
    if(retcode) {
      failf(data, "failed to store ssl session");
      return retcode;
    }
  }


  /* Get server's certificate (note: beware of dynamic allocation) - opt */
  /* major serious hack alert -- we should check certificates
   * to authenticate the server; otherwise we risk man-in-the-middle
   * attack
   */

  connssl->server_cert = SSL_get_peer_certificate(connssl->handle);
  if(!connssl->server_cert) {
    failf(data, "SSL: couldn't get peer certificate!");
    return CURLE_SSL_PEER_CERTIFICATE;
  }
  infof (data, "Server certificate:\n");

  str = X509_NAME_oneline(X509_get_subject_name(connssl->server_cert),
                          NULL, 0);
  if(!str) {
    failf(data, "SSL: couldn't get X509-subject!");
    X509_free(connssl->server_cert);
    connssl->server_cert = NULL;
    return CURLE_SSL_CONNECT_ERROR;
  }
  infof(data, "\t subject: %s\n", str);
  CRYPTO_free(str);

  certdate = X509_get_notBefore(connssl->server_cert);
  Curl_ASN1_UTCTIME_output(conn, "\t start date: ", certdate);

  certdate = X509_get_notAfter(connssl->server_cert);
  Curl_ASN1_UTCTIME_output(conn, "\t expire date: ", certdate);

  if(data->set.ssl.verifyhost) {
    retcode = verifyhost(conn, connssl->server_cert);
    if(retcode) {
      X509_free(connssl->server_cert);
      connssl->server_cert = NULL;
      return retcode;
    }
  }

  str = X509_NAME_oneline(X509_get_issuer_name(connssl->server_cert),
                          NULL, 0);
  if(!str) {
    failf(data, "SSL: couldn't get X509-issuer name!");
    retcode = CURLE_SSL_CONNECT_ERROR;
  }
  else {
    infof(data, "\t issuer: %s\n", str);
    CRYPTO_free(str);

    /* 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? */
        failf(data, "SSL certificate verify result: %s (%ld)",
              X509_verify_cert_error_string(lerr), lerr);
        retcode = CURLE_SSL_PEER_CERTIFICATE;
      }
      else
        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");
  }

  X509_free(connssl->server_cert);
  connssl->server_cert = NULL;
  return retcode;
}
Esempio n. 13
0
static CURLcode
gtls_connect_step3(struct connectdata *conn,
                   int sockindex)
{
  unsigned int cert_list_size;
  const gnutls_datum_t *chainp;
  unsigned int verify_status = 0;
  gnutls_x509_crt_t x509_cert, x509_issuer;
  gnutls_datum_t issuerp;
  char certbuf[256] = ""; /* big enough? */
  size_t size;
  unsigned int algo;
  unsigned int bits;
  time_t certclock;
  const char *ptr;
  struct SessionHandle *data = conn->data;
  gnutls_session_t session = conn->ssl[sockindex].session;
  int rc;
  bool incache;
  void *ssl_sessionid;
#ifdef HAS_ALPN
  gnutls_datum_t proto;
#endif
  CURLcode result = CURLE_OK;

  gnutls_protocol_t version = gnutls_protocol_get_version(session);

  /* the name of the cipher suite used, e.g. ECDHE_RSA_AES_256_GCM_SHA384. */
  ptr = gnutls_cipher_suite_get_name(gnutls_kx_get(session),
                                     gnutls_cipher_get(session),
                                     gnutls_mac_get(session));

  infof(data, "SSL connection using %s / %s\n",
        gnutls_protocol_get_name(version), ptr);

  /* This function will return the peer's raw certificate (chain) as sent by
     the peer. These certificates are in raw format (DER encoded for
     X.509). In case of a X.509 then a certificate list may be present. The
     first certificate in the list is the peer's certificate, following the
     issuer's certificate, then the issuer's issuer etc. */

  chainp = gnutls_certificate_get_peers(session, &cert_list_size);
  if(!chainp) {
    if(data->set.ssl.verifypeer ||
       data->set.ssl.verifyhost ||
       data->set.ssl.issuercert) {
#ifdef USE_TLS_SRP
      if(data->set.ssl.authtype == CURL_TLSAUTH_SRP
         && data->set.ssl.username != NULL
         && !data->set.ssl.verifypeer
         && gnutls_cipher_get(session)) {
        /* no peer cert, but auth is ok if we have SRP user and cipher and no
           peer verify */
      }
      else {
#endif
        failf(data, "failed to get server cert");
        return CURLE_PEER_FAILED_VERIFICATION;
#ifdef USE_TLS_SRP
      }
#endif
    }
    infof(data, "\t common name: WARNING couldn't obtain\n");
  }

  if(data->set.ssl.certinfo && chainp) {
    unsigned int i;

    result = Curl_ssl_init_certinfo(data, cert_list_size);
    if(result)
      return result;

    for(i = 0; i < cert_list_size; i++) {
      const char *beg = (const char *) chainp[i].data;
      const char *end = beg + chainp[i].size;

      result = Curl_extract_certinfo(conn, i, beg, end);
      if(result)
        return result;
    }
  }

  if(data->set.ssl.verifypeer) {
    /* This function will try to verify the peer's certificate and return its
       status (trusted, invalid etc.). The value of status should be one or
       more of the gnutls_certificate_status_t enumerated elements bitwise
       or'd. To avoid denial of service attacks some default upper limits
       regarding the certificate key size and chain size are set. To override
       them use gnutls_certificate_set_verify_limits(). */

    rc = gnutls_certificate_verify_peers2(session, &verify_status);
    if(rc < 0) {
      failf(data, "server cert verify failed: %d", rc);
      return CURLE_SSL_CONNECT_ERROR;
    }

    /* verify_status is a bitmask of gnutls_certificate_status bits */
    if(verify_status & GNUTLS_CERT_INVALID) {
      if(data->set.ssl.verifypeer) {
        failf(data, "server certificate verification failed. CAfile: %s "
              "CRLfile: %s", data->set.ssl.CAfile?data->set.ssl.CAfile:"none",
              data->set.ssl.CRLfile?data->set.ssl.CRLfile:"none");
        return CURLE_SSL_CACERT;
      }
      else
        infof(data, "\t server certificate verification FAILED\n");
    }
    else
      infof(data, "\t server certificate verification OK\n");
  }
  else
    infof(data, "\t server certificate verification SKIPPED\n");

#ifdef HAS_OCSP
  if(data->set.ssl.verifystatus) {
    if(gnutls_ocsp_status_request_is_checked(session, 0) == 0) {
      gnutls_datum_t status_request;
      gnutls_ocsp_resp_t ocsp_resp;

      gnutls_ocsp_cert_status_t status;
      gnutls_x509_crl_reason_t reason;

      rc = gnutls_ocsp_status_request_get(session, &status_request);

      infof(data, "\t server certificate status verification FAILED\n");

      if(rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
        failf(data, "No OCSP response received");
        return CURLE_SSL_INVALIDCERTSTATUS;
      }

      if(rc < 0) {
        failf(data, "Invalid OCSP response received");
        return CURLE_SSL_INVALIDCERTSTATUS;
      }

      gnutls_ocsp_resp_init(&ocsp_resp);

      rc = gnutls_ocsp_resp_import(ocsp_resp, &status_request);
      if(rc < 0) {
        failf(data, "Invalid OCSP response received");
        return CURLE_SSL_INVALIDCERTSTATUS;
      }

      rc = gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL,
                                       &status, NULL, NULL, NULL, &reason);

      switch(status) {
      case GNUTLS_OCSP_CERT_GOOD:
        break;

      case GNUTLS_OCSP_CERT_REVOKED: {
        const char *crl_reason;

        switch(reason) {
          default:
          case GNUTLS_X509_CRLREASON_UNSPECIFIED:
            crl_reason = "unspecified reason";
            break;

          case GNUTLS_X509_CRLREASON_KEYCOMPROMISE:
            crl_reason = "private key compromised";
            break;

          case GNUTLS_X509_CRLREASON_CACOMPROMISE:
            crl_reason = "CA compromised";
            break;

          case GNUTLS_X509_CRLREASON_AFFILIATIONCHANGED:
            crl_reason = "affiliation has changed";
            break;

          case GNUTLS_X509_CRLREASON_SUPERSEDED:
            crl_reason = "certificate superseded";
            break;

          case GNUTLS_X509_CRLREASON_CESSATIONOFOPERATION:
            crl_reason = "operation has ceased";
            break;

          case GNUTLS_X509_CRLREASON_CERTIFICATEHOLD:
            crl_reason = "certificate is on hold";
            break;

          case GNUTLS_X509_CRLREASON_REMOVEFROMCRL:
            crl_reason = "will be removed from delta CRL";
            break;

          case GNUTLS_X509_CRLREASON_PRIVILEGEWITHDRAWN:
            crl_reason = "privilege withdrawn";
            break;

          case GNUTLS_X509_CRLREASON_AACOMPROMISE:
            crl_reason = "AA compromised";
            break;
        }

        failf(data, "Server certificate was revoked: %s", crl_reason);
        break;
      }

      default:
      case GNUTLS_OCSP_CERT_UNKNOWN:
        failf(data, "Server certificate status is unknown");
        break;
      }

      gnutls_ocsp_resp_deinit(ocsp_resp);

      return CURLE_SSL_INVALIDCERTSTATUS;
    }
    else
      infof(data, "\t server certificate status verification OK\n");
  }
  else
    infof(data, "\t server certificate status verification SKIPPED\n");
#endif

  /* initialize an X.509 certificate structure. */
  gnutls_x509_crt_init(&x509_cert);

  if(chainp)
    /* convert the given DER or PEM encoded Certificate to the native
       gnutls_x509_crt_t format */
    gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER);

  if(data->set.ssl.issuercert) {
    gnutls_x509_crt_init(&x509_issuer);
    issuerp = load_file(data->set.ssl.issuercert);
    gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM);
    rc = gnutls_x509_crt_check_issuer(x509_cert, x509_issuer);
    gnutls_x509_crt_deinit(x509_issuer);
    unload_file(issuerp);
    if(rc <= 0) {
      failf(data, "server certificate issuer check failed (IssuerCert: %s)",
            data->set.ssl.issuercert?data->set.ssl.issuercert:"none");
      gnutls_x509_crt_deinit(x509_cert);
      return CURLE_SSL_ISSUER_ERROR;
    }
    infof(data, "\t server certificate issuer check OK (Issuer Cert: %s)\n",
          data->set.ssl.issuercert?data->set.ssl.issuercert:"none");
  }

  size=sizeof(certbuf);
  rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME,
                                     0, /* the first and only one */
                                     FALSE,
                                     certbuf,
                                     &size);
  if(rc) {
    infof(data, "error fetching CN from cert:%s\n",
          gnutls_strerror(rc));
  }

  /* This function will check if the given certificate's subject matches the
     given hostname. This is a basic implementation of the matching described
     in RFC2818 (HTTPS), which takes into account wildcards, and the subject
     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 "
            "target host name '%s'", certbuf, conn->host.dispname);
      gnutls_x509_crt_deinit(x509_cert);
      return CURLE_PEER_FAILED_VERIFICATION;
    }
    else
      infof(data, "\t common name: %s (does not match '%s')\n",
            certbuf, conn->host.dispname);
  }
  else
    infof(data, "\t common name: %s (matched)\n", certbuf);

  /* Check for time-based validity */
  certclock = gnutls_x509_crt_get_expiration_time(x509_cert);

  if(certclock == (time_t)-1) {
    if(data->set.ssl.verifypeer) {
      failf(data, "server cert expiration date verify failed");
      gnutls_x509_crt_deinit(x509_cert);
      return CURLE_SSL_CONNECT_ERROR;
    }
    else
      infof(data, "\t server certificate expiration date verify FAILED\n");
  }
  else {
    if(certclock < time(NULL)) {
      if(data->set.ssl.verifypeer) {
        failf(data, "server certificate expiration date has passed.");
        gnutls_x509_crt_deinit(x509_cert);
        return CURLE_PEER_FAILED_VERIFICATION;
      }
      else
        infof(data, "\t server certificate expiration date FAILED\n");
    }
    else
      infof(data, "\t server certificate expiration date OK\n");
  }

  certclock = gnutls_x509_crt_get_activation_time(x509_cert);

  if(certclock == (time_t)-1) {
    if(data->set.ssl.verifypeer) {
      failf(data, "server cert activation date verify failed");
      gnutls_x509_crt_deinit(x509_cert);
      return CURLE_SSL_CONNECT_ERROR;
    }
    else
      infof(data, "\t server certificate activation date verify FAILED\n");
  }
  else {
    if(certclock > time(NULL)) {
      if(data->set.ssl.verifypeer) {
        failf(data, "server certificate not activated yet.");
        gnutls_x509_crt_deinit(x509_cert);
        return CURLE_PEER_FAILED_VERIFICATION;
      }
      else
        infof(data, "\t server certificate activation date FAILED\n");
    }
    else
      infof(data, "\t server certificate activation date OK\n");
  }

  ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY];
  if(ptr) {
    result = pkp_pin_peer_pubkey(data, x509_cert, ptr);
    if(result != CURLE_OK) {
      failf(data, "SSL: public key does not match pinned public key!");
      gnutls_x509_crt_deinit(x509_cert);
      return result;
    }
  }

  /* Show:

  - subject
  - start date
  - expire date
  - common name
  - issuer

  */

  /* public key algorithm's parameters */
  algo = gnutls_x509_crt_get_pk_algorithm(x509_cert, &bits);
  infof(data, "\t certificate public key: %s\n",
        gnutls_pk_algorithm_get_name(algo));

  /* version of the X.509 certificate. */
  infof(data, "\t certificate version: #%d\n",
        gnutls_x509_crt_get_version(x509_cert));


  size = sizeof(certbuf);
  gnutls_x509_crt_get_dn(x509_cert, certbuf, &size);
  infof(data, "\t subject: %s\n", certbuf);

  certclock = gnutls_x509_crt_get_activation_time(x509_cert);
  showtime(data, "start date", certclock);

  certclock = gnutls_x509_crt_get_expiration_time(x509_cert);
  showtime(data, "expire date", certclock);

  size = sizeof(certbuf);
  gnutls_x509_crt_get_issuer_dn(x509_cert, certbuf, &size);
  infof(data, "\t issuer: %s\n", certbuf);

  gnutls_x509_crt_deinit(x509_cert);

  /* compression algorithm (if any) */
  ptr = gnutls_compression_get_name(gnutls_compression_get(session));
  /* the *_get_name() says "NULL" if GNUTLS_COMP_NULL is returned */
  infof(data, "\t compression: %s\n", ptr);

#ifdef HAS_ALPN
  if(data->set.ssl_enable_alpn) {
    rc = gnutls_alpn_get_selected_protocol(session, &proto);
    if(rc == 0) {
      infof(data, "ALPN, server accepted to use %.*s\n", proto.size,
          proto.data);

#ifdef USE_NGHTTP2
      if(proto.size == NGHTTP2_PROTO_VERSION_ID_LEN &&
         !memcmp(NGHTTP2_PROTO_VERSION_ID, proto.data,
                 NGHTTP2_PROTO_VERSION_ID_LEN)) {
        conn->negnpn = CURL_HTTP_VERSION_2;
      }
      else
#endif
      if(proto.size == ALPN_HTTP_1_1_LENGTH &&
         !memcmp(ALPN_HTTP_1_1, proto.data, ALPN_HTTP_1_1_LENGTH)) {
        conn->negnpn = CURL_HTTP_VERSION_1_1;
      }
    }
    else
      infof(data, "ALPN, server did not agree to a protocol\n");
  }
#endif

  conn->ssl[sockindex].state = ssl_connection_complete;
  conn->recv[sockindex] = gtls_recv;
  conn->send[sockindex] = gtls_send;

  {
    /* we always unconditionally get the session id here, as even if we
       already got it from the cache and asked to use it in the connection, it
       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 = 0;

    /* get the session ID data size */
    gnutls_session_get_data(session, NULL, &connect_idsize);
    connect_sessionid = malloc(connect_idsize); /* get a buffer for it */

    if(connect_sessionid) {
      /* extract session ID to the allocated buffer */
      gnutls_session_get_data(session, connect_sessionid, &connect_idsize);

      incache = !(Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL));
      if(incache) {
        /* there was one before in the cache, so instead of risking that the
           previous one was rejected, we just kill that and store the new */
        Curl_ssl_delsessionid(conn, ssl_sessionid);
      }

      /* store this session id */
      result = Curl_ssl_addsessionid(conn, connect_sessionid, connect_idsize);
      if(result) {
        free(connect_sessionid);
        result = CURLE_OUT_OF_MEMORY;
      }
    }
    else
      result = CURLE_OUT_OF_MEMORY;
  }

  return result;
}
Esempio n. 14
0
/*
 * This function loads all the client/CA certificates and CRLs. Setup the TLS
 * layer and do all necessary magic.
 */
CURLcode
Curl_polarssl_connect(struct connectdata *conn,
                      int sockindex)
{
  struct SessionHandle *data = conn->data;
  bool sni = TRUE; /* default is SNI enabled */
  int ret = -1;
#ifdef ENABLE_IPV6
  struct in6_addr addr;
#else
  struct in_addr addr;
#endif
  void *old_session = NULL;
  size_t old_session_size = 0;
  char buffer[1024];

  if(conn->ssl[sockindex].state == ssl_connection_complete)
    return CURLE_OK;

  /* PolarSSL only supports SSLv3 and TLSv1 */
  if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) {
    failf(data, "PolarSSL does not support SSLv2");
    return CURLE_SSL_CONNECT_ERROR;
  }
  else if(data->set.ssl.version == CURL_SSLVERSION_SSLv3)
    sni = FALSE; /* SSLv3 has no SNI */

  havege_init(&conn->ssl[sockindex].hs);

  /* Load the trusted CA */
  memset(&conn->ssl[sockindex].cacert, 0, sizeof(x509_cert));

  if(data->set.str[STRING_SSL_CAFILE]) {
    ret = x509parse_crtfile(&conn->ssl[sockindex].cacert,
                            data->set.str[STRING_SSL_CAFILE]);

    if(ret) {
      failf(data, "Error reading ca cert file %s: -0x%04X",
            data->set.str[STRING_SSL_CAFILE], -ret);

      if(data->set.ssl.verifypeer)
        return CURLE_SSL_CACERT_BADFILE;
    }
  }

  /* Load the client certificate */
  memset(&conn->ssl[sockindex].clicert, 0, sizeof(x509_cert));

  if(data->set.str[STRING_CERT]) {
    ret = x509parse_crtfile(&conn->ssl[sockindex].clicert,
                            data->set.str[STRING_CERT]);

    if(ret) {
      failf(data, "Error reading client cert file %s: -0x%04X",
            data->set.str[STRING_CERT], -ret);
      return CURLE_SSL_CERTPROBLEM;
    }
  }

  /* Load the client private key */
  if(data->set.str[STRING_KEY]) {
    ret = x509parse_keyfile(&conn->ssl[sockindex].rsa,
                            data->set.str[STRING_KEY],
                            data->set.str[STRING_KEY_PASSWD]);

    if(ret) {
      failf(data, "Error reading private key %s: -0x%04X",
            data->set.str[STRING_KEY], -ret);
      return CURLE_SSL_CERTPROBLEM;
    }
  }

  /* Load the CRL */
  memset(&conn->ssl[sockindex].crl, 0, sizeof(x509_crl));

  if(data->set.str[STRING_SSL_CRLFILE]) {
    ret = x509parse_crlfile(&conn->ssl[sockindex].crl,
                            data->set.str[STRING_SSL_CRLFILE]);

    if(ret) {
      failf(data, "Error reading CRL file %s: -0x%04X",
            data->set.str[STRING_SSL_CRLFILE], -ret);
      return CURLE_SSL_CRL_BADFILE;
    }
  }

  infof(data, "PolarSSL: Connected to %s:%d\n",
        conn->host.name, conn->remote_port);

  havege_init(&conn->ssl[sockindex].hs);

  if(ssl_init(&conn->ssl[sockindex].ssl)) {
    failf(data, "PolarSSL: ssl_init failed");
    return CURLE_SSL_CONNECT_ERROR;
  }

  ssl_set_endpoint(&conn->ssl[sockindex].ssl, SSL_IS_CLIENT);
  ssl_set_authmode(&conn->ssl[sockindex].ssl, SSL_VERIFY_OPTIONAL);

  ssl_set_rng(&conn->ssl[sockindex].ssl, havege_rand,
              &conn->ssl[sockindex].hs);
  ssl_set_bio(&conn->ssl[sockindex].ssl,
              net_recv, &conn->sock[sockindex],
              net_send, &conn->sock[sockindex]);

  ssl_set_ciphers(&conn->ssl[sockindex].ssl, ssl_default_ciphers);

  if(!Curl_ssl_getsessionid(conn, &old_session, &old_session_size)) {
    memcpy(&conn->ssl[sockindex].ssn, old_session, old_session_size);
    infof(data, "PolarSSL re-using session\n");
  }

  ssl_set_session(&conn->ssl[sockindex].ssl, 1, 600,
                  &conn->ssl[sockindex].ssn);

  ssl_set_ca_chain(&conn->ssl[sockindex].ssl,
                   &conn->ssl[sockindex].cacert,
                   &conn->ssl[sockindex].crl,
                   conn->host.name);

  ssl_set_own_cert(&conn->ssl[sockindex].ssl,
                   &conn->ssl[sockindex].clicert, &conn->ssl[sockindex].rsa);

  if(!Curl_inet_pton(AF_INET, conn->host.name, &addr) &&
#ifdef ENABLE_IPV6
     !Curl_inet_pton(AF_INET6, conn->host.name, &addr) &&
#endif
     sni && ssl_set_hostname(&conn->ssl[sockindex].ssl, conn->host.name)) {
     infof(data, "WARNING: failed to configure "
                 "server name indication (SNI) TLS extension\n");
  }

  infof(data, "PolarSSL: performing SSL/TLS handshake...\n");

#ifdef POLARSSL_DEBUG
  ssl_set_dbg(&conn->ssl[sockindex].ssl, polarssl_debug, data);
#endif

  for(;;) {
    if(!(ret = ssl_handshake(&conn->ssl[sockindex].ssl)))
      break;
    else if(ret != POLARSSL_ERR_NET_TRY_AGAIN) {
      failf(data, "ssl_handshake returned -0x%04X", -ret);
      return CURLE_SSL_CONNECT_ERROR;
    }
    else {
      /* wait for data from server... */
      long timeout_ms = Curl_timeleft(data, NULL, TRUE);

      if(timeout_ms < 0) {
        failf(data, "SSL connection timeout");
        return CURLE_OPERATION_TIMEDOUT;
      }

      switch(Curl_socket_ready(conn->sock[sockindex],
                        CURL_SOCKET_BAD, timeout_ms)) {
      case 0:
        failf(data, "SSL handshake timeout");
        return CURLE_OPERATION_TIMEDOUT;
        break;
      case CURL_CSELECT_IN:
        continue;
        break;
      default:
        return CURLE_SSL_CONNECT_ERROR;
        break;
      }
    }
  }

  infof(data, "PolarSSL: Handshake complete, cipher is %s\n",
        ssl_get_cipher(&conn->ssl[sockindex].ssl));

  ret = ssl_get_verify_result(&conn->ssl[sockindex].ssl);

  if(ret && data->set.ssl.verifypeer) {
    if(ret & BADCERT_EXPIRED)
      failf(data, "Cert verify failed: BADCERT_EXPIRED\n");

    if(ret & BADCERT_REVOKED)
      failf(data, "Cert verify failed: BADCERT_REVOKED");

    if(ret & BADCERT_CN_MISMATCH)
      failf(data, "Cert verify failed: BADCERT_CN_MISMATCH");

    if(ret & BADCERT_NOT_TRUSTED)
      failf(data, "Cert verify failed: BADCERT_NOT_TRUSTED");

    return CURLE_SSL_CACERT;
  }

  if(conn->ssl[sockindex].ssl.peer_cert) {
    /* If the session was resumed, there will be no peer certs */
    memset(buffer, 0, sizeof(buffer));

    if(x509parse_cert_info(buffer, sizeof(buffer), (char *)"* ",
                           conn->ssl[sockindex].ssl.peer_cert) != -1)
      infof(data, "Dumping cert info:\n%s\n", buffer);
  }

  conn->ssl[sockindex].state = ssl_connection_complete;
  conn->recv[sockindex] = polarssl_recv;
  conn->send[sockindex] = polarssl_send;

  /* Save the current session data for possible re-use */
  {
    void *new_session = malloc(sizeof(conn->ssl[sockindex].ssn));

    if(new_session) {
      memcpy(new_session, &conn->ssl[sockindex].ssn,
             sizeof(conn->ssl[sockindex].ssn));

      if(old_session)
        Curl_ssl_delsessionid(conn, old_session);

      return Curl_ssl_addsessionid(conn, new_session,
                                   sizeof(conn->ssl[sockindex].ssn));
    }
  }

  return CURLE_OK;
}