Beispiel #1
0
/*
 * This function is called after the TCP connect has completed. Setup the TLS
 * layer and do all necessary magic for a blocking connect.
 */
CURLcode
Curl_axtls_connect(struct connectdata *conn,
                  int sockindex)

{
  CURLcode conn_step = connect_prep(conn, sockindex);
  int ssl_fcn_return;
  SSL *ssl = conn->ssl[sockindex].ssl;

  if(conn_step != CURLE_OK) {
    Curl_axtls_close(conn, sockindex);
    return conn_step;
  }

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

  conn_step = connect_finish(conn, sockindex);
  if(conn_step != CURLE_OK) {
    Curl_axtls_close(conn, sockindex);
    return conn_step;
  }

  return CURLE_OK;
}
Beispiel #2
0
/*
 * Use axTLS's non-blocking connection feature to open an SSL connection.
 * This is called after a TCP connection is already established.
*/
CURLcode Curl_axtls_connect_nonblocking(
    struct connectdata *conn,
    int sockindex,
    bool *done)
{
  CURLcode conn_step;
  int ssl_fcn_return;

 *done = FALSE;
  /* connectdata is calloc'd and connecting_state is only changed in this
     function, so this is safe, as the state is effectively initialized. */
  if(conn->ssl[sockindex].connecting_state == ssl_connect_1) {
    conn_step = connect_prep(conn, sockindex);
    if(conn_step != CURLE_OK) {
      Curl_axtls_close(conn, sockindex);
      return conn_step;
    }
    conn->ssl[sockindex].connecting_state = ssl_connect_2;
  }

  if(conn->ssl[sockindex].connecting_state == ssl_connect_2) {
    /* Check to make sure handshake was ok. */
    if(ssl_handshake_status(conn->ssl[sockindex].ssl) != SSL_OK) {
      ssl_fcn_return = ssl_read(conn->ssl[sockindex].ssl, NULL);
      if(ssl_fcn_return < 0) {
        Curl_axtls_close(conn, sockindex);
        ssl_display_error(ssl_fcn_return); /* goes to stdout. */
        return map_error_to_curl(ssl_fcn_return);
      }
      else {
        return CURLE_OK; /* Return control to caller for retries */
      }
    }
    infof (conn->data, "handshake completed successfully\n");
    conn->ssl[sockindex].connecting_state = ssl_connect_3;
  }

  if(conn->ssl[sockindex].connecting_state == ssl_connect_3) {
    conn_step = connect_finish(conn, sockindex);
    if(conn_step != CURLE_OK) {
      Curl_axtls_close(conn, sockindex);
      return conn_step;
    }

    /* Reset connect state */
    conn->ssl[sockindex].connecting_state = ssl_connect_1;

    *done = TRUE;
    return CURLE_OK;
  }

  /* Unrecognized state.  Things are very bad. */
  conn->ssl[sockindex].state  = ssl_connection_none;
  conn->ssl[sockindex].connecting_state = ssl_connect_1;
  /* Return value perhaps not strictly correct, but distinguishes the issue.*/
  return CURLE_BAD_FUNCTION_ARGUMENT;
}
Beispiel #3
0
static ssize_t axtls_recv(struct connectdata *conn, /* connection data */
                          int num,                  /* socketindex */
                          char *buf,                /* store read data here */
                          size_t buffersize,        /* max amount to read */
                          CURLcode *err)
{
  struct ssl_connect_data *connssl = &conn->ssl[num];
  ssize_t ret = 0;

  infof(conn->data, "  axtls_recv\n");

  if(connssl) {
    ret = (ssize_t)SSL_read(conn->ssl[num].ssl, buf, (int)buffersize);

    /* axTLS isn't terribly generous about error reporting */
    /* With patched axTLS, SSL_CLOSE_NOTIFY=-3.  Hard-coding until axTLS
       team approves proposed fix. */
    if(ret == -3 ) {
      Curl_axtls_close(conn, num);
    }
    else if(ret < 0) {
      failf(conn->data, "axTLS recv error (%d)", (int)ret);
      *err = map_error_to_curl(ret);
      return -1;
    }
  }

  *err = CURLE_OK;
  return ret;
}
Beispiel #4
0
/*
 * This function is called after the TCP connect has completed. Setup the TLS
 * layer and do all necessary magic for a blocking connect.
 */
CURLcode
Curl_axtls_connect(struct connectdata *conn,
                  int sockindex)

{
  struct SessionHandle *data = conn->data;
  CURLcode conn_step = connect_prep(conn, sockindex);
  int ssl_fcn_return;
  SSL *ssl = conn->ssl[sockindex].ssl;
  long timeout_ms;

  if(conn_step != CURLE_OK) {
    Curl_axtls_close(conn, sockindex);
    return conn_step;
  }

  /* Check to make sure handshake was ok. */
  while(ssl_handshake_status(ssl) != SSL_OK) {
    /* check allowed time left */
    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;
    }

    ssl_fcn_return = ssl_read(ssl, NULL);
    if(ssl_fcn_return < 0) {
      Curl_axtls_close(conn, sockindex);
      ssl_display_error(ssl_fcn_return); /* goes to stdout. */
      return map_error_to_curl(ssl_fcn_return);
    }
    /* TODO: avoid polling */
    usleep(10000);
  }
  infof (conn->data, "handshake completed successfully\n");

  conn_step = connect_finish(conn, sockindex);
  if(conn_step != CURLE_OK) {
    Curl_axtls_close(conn, sockindex);
    return conn_step;
  }

  return CURLE_OK;
}
Beispiel #5
0
static ssize_t axtls_recv(struct connectdata *conn, /* connection data */
                          int num,                  /* socketindex */
                          char *buf,                /* store read data here */
                          size_t buffersize,        /* max amount to read */
                          CURLcode *err)
{
  struct ssl_connect_data *connssl = &conn->ssl[num];
  ssize_t ret = 0;
  uint8_t *read_buf;

  infof(conn->data, "  axtls_recv\n");

  *err = CURLE_OK;
  if(connssl) {
    ret = ssl_read(connssl->ssl, &read_buf);
    if(ret > SSL_OK) {
      /* ssl_read returns SSL_OK if there is more data to read, so if it is
         larger, then all data has been read already.  */
      memcpy(buf, read_buf,
             (size_t)ret > buffersize ? buffersize : (size_t)ret);
    }
    else if(ret == SSL_OK) {
      /* more data to be read, signal caller to call again */
      *err = CURLE_AGAIN;
      ret = -1;
    }
    else if(ret == -3) {
      /* With patched axTLS, SSL_CLOSE_NOTIFY=-3.  Hard-coding until axTLS
         team approves proposed fix. */
      Curl_axtls_close(conn, num);
    }
    else {
      failf(conn->data, "axTLS recv error (%d)", ret);
      *err = map_error_to_curl(ret);
      ret = -1;
    }
  }

  return ret;
}
Beispiel #6
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;
}
Beispiel #7
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;
}
Beispiel #8
0
/*
 * Use axTLS's non-blocking connection feature to open an SSL connection.
 * This is called after a TCP connection is already established.
 */
CURLcode Curl_axtls_connect_nonblocking(
    struct connectdata *conn,
    int sockindex,
    bool *done)
{
  CURLcode conn_step;
  int ssl_fcn_return;
  int i;

 *done = FALSE;
  /* connectdata is calloc'd and connecting_state is only changed in this
     function, so this is safe, as the state is effectively initialized. */
  if(conn->ssl[sockindex].connecting_state == ssl_connect_1) {
    conn_step = connect_prep(conn, sockindex);
    if(conn_step != CURLE_OK) {
      Curl_axtls_close(conn, sockindex);
      return conn_step;
    }
    conn->ssl[sockindex].connecting_state = ssl_connect_2;
  }

  if(conn->ssl[sockindex].connecting_state == ssl_connect_2) {
    /* Check to make sure handshake was ok. */
    if(ssl_handshake_status(conn->ssl[sockindex].ssl) != SSL_OK) {
      /* Loop to perform more work in between sleeps. This is work around the
         fact that axtls does not expose any knowledge about when work needs
         to be performed. This can save ~25% of time on SSL handshakes. */
      for(i=0; i<5; i++) {
        ssl_fcn_return = ssl_read(conn->ssl[sockindex].ssl, NULL);
        if(ssl_fcn_return < 0) {
          Curl_axtls_close(conn, sockindex);
          ssl_display_error(ssl_fcn_return); /* goes to stdout. */
          return map_error_to_curl(ssl_fcn_return);
        }
        return CURLE_OK;
      }
    }
    infof (conn->data, "handshake completed successfully\n");
    conn->ssl[sockindex].connecting_state = ssl_connect_3;
  }

  if(conn->ssl[sockindex].connecting_state == ssl_connect_3) {
    conn_step = connect_finish(conn, sockindex);
    if(conn_step != CURLE_OK) {
      Curl_axtls_close(conn, sockindex);
      return conn_step;
    }

    /* Reset connect state */
    conn->ssl[sockindex].connecting_state = ssl_connect_1;

    *done = TRUE;
    return CURLE_OK;
  }

  /* Unrecognized state.  Things are very bad. */
  conn->ssl[sockindex].state  = ssl_connection_none;
  conn->ssl[sockindex].connecting_state = ssl_connect_1;
  /* Return value perhaps not strictly correct, but distinguishes the issue.*/
  return CURLE_BAD_FUNCTION_ARGUMENT;
}