Commit be312336 authored by Eric Hu's avatar Eric Hu Committed by Daniel Stenberg
Browse files

Initial axTLS integration. Connections can be made and some tests pass.

Failed HTTPS tests: 301, 306, 311, 312, 313, 560
311, 312 need more detailed error reporting from axTLS.
313 relates to CRL, which hasn't been implemented yet.
parent 6761cf49
Loading
Loading
Loading
Loading
+362 −14
Original line number Diff line number Diff line
@@ -50,17 +50,93 @@
/* The last #include file should be: */
#include "memdebug.h"

/* SSL_read is opied from axTLS compat layer */
static int SSL_read(SSL *ssl, void *buf, int num)
{
  uint8_t *read_buf;
  int ret;
  
  while((ret = ssl_read(ssl, &read_buf)) == SSL_OK);

  if(ret > SSL_OK){
    memcpy(buf, read_buf, ret > num ? num : ret);
  }

  return ret;
}

/* Global axTLS init, called from Curl_ssl_init() */
int Curl_axtls_init(void)
{
/* axTLS has no global init.  Everything is done through SSL and SSL_CTX
 * structs stored in connectdata structure.  Perhaps can move to axtls.h.
 */
  return 1;
}

int Curl_axtls_cleanup(void)
{
  /* axTLS has no global cleanup.  Perhaps can move this to axtls.h. */
  return 1;
}

static CURLcode map_error_to_curl(int axtls_err)
{
  switch (axtls_err)
  {
  case SSL_ERROR_NOT_SUPPORTED:
  case SSL_ERROR_INVALID_VERSION:
  case -70:                       /* protocol version alert from server */
    return CURLE_UNSUPPORTED_PROTOCOL;
    break;
  case SSL_ERROR_NO_CIPHER:
    return CURLE_SSL_CIPHER;
    break;
  case SSL_ERROR_BAD_CERTIFICATE: /* this may be bad server cert too */
  case SSL_ERROR_NO_CERT_DEFINED:
  case -42:                       /* bad certificate alert from server */
  case -43:                       /* unsupported cert alert from server */
  case -44:                       /* cert revoked alert from server */
  case -45:                       /* cert expired alert from server */
  case -46:                       /* cert unknown alert from server */
    return CURLE_SSL_CERTPROBLEM;
    break;
  case SSL_X509_ERROR(X509_NOT_OK):
  case SSL_X509_ERROR(X509_VFY_ERROR_NO_TRUSTED_CERT):
  case SSL_X509_ERROR(X509_VFY_ERROR_BAD_SIGNATURE):
  case SSL_X509_ERROR(X509_VFY_ERROR_NOT_YET_VALID):
  case SSL_X509_ERROR(X509_VFY_ERROR_EXPIRED):
  case SSL_X509_ERROR(X509_VFY_ERROR_SELF_SIGNED):
  case SSL_X509_ERROR(X509_VFY_ERROR_INVALID_CHAIN):
  case SSL_X509_ERROR(X509_VFY_ERROR_UNSUPPORTED_DIGEST):
  case SSL_X509_ERROR(X509_INVALID_PRIV_KEY):
    return CURLE_PEER_FAILED_VERIFICATION;
    break;
  case -48:                       /* unknown ca alert from server */
    return CURLE_SSL_CACERT;
    break;
  case -49:                       /* access denied alert from server */
    return CURLE_REMOTE_ACCESS_DENIED;
    break;
  case SSL_ERROR_CONN_LOST:
  case SSL_ERROR_SOCK_SETUP_FAILURE:
  case SSL_ERROR_INVALID_HANDSHAKE:
  case SSL_ERROR_INVALID_PROT_MSG:
  case SSL_ERROR_INVALID_HMAC:
  case SSL_ERROR_INVALID_SESSION:
  case SSL_ERROR_INVALID_KEY:     /* it's too bad this doesn't map better */
  case SSL_ERROR_FINISHED_INVALID:
  case SSL_ERROR_NO_CLIENT_RENOG:
  default:
    return CURLE_SSL_CONNECT_ERROR;
    break;
  }
  return CURLE_SSL_CONNECT_ERROR; /* catch-all for non-easily-mapped errors */
}

static Curl_recv axtls_recv;
static Curl_send axtls_send;

/*
 * This function is called after the TCP connect has completed. Setup the TLS
 * layer and do all necessary magic.
@@ -70,25 +146,232 @@ 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 *x509;

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

  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 */
  if(data->set.ssl.version != CURL_SSLVERSION_TLSv1) {
    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);
  }

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

  /* 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, 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, 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, gtls.c does issuer verfication. axTLS has no straightforward
   * equivalent, so omitting for now.*/

  /* See if common name was set in server certificate */
  x509 = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME);
  if(x509 == NULL) 
    infof(data, "error fetching CN from cert\n");

  /* Here, gtls.c does the following
   * 1) x509 hostname checking per RFC2818.  axTLS doesn't support this, but 
   *    it seems useful.  Omitting for now.
   * 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.
   */

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


/* return number of sent (non-SSL) bytes */
ssize_t Curl_axtls_send(struct connectdata *conn,
static ssize_t axtls_send(struct connectdata *conn,
			  int sockindex,
			  const void *mem,
                       size_t len)
			  size_t len,   
			  CURLcode *err)
{
  return 0;
  /* ssl_write() returns 'int' while write() and send() returns
     'size_t' */
  char error_buffer[120]; /* Comply with OpenSSL, which documents that this 
			     must be at least 120 bytes long. */
  int rc = ssl_write(conn->ssl[sockindex].ssl, mem, (int)len);

  if(rc < 0 ) {
    *err = map_error_to_curl(rc);
    rc = -1; /* generic error code for send failure */
  }

  *err = CURLE_OK;
  return rc;
}

void Curl_axtls_close_all(struct SessionHandle *data)
{
  (void)data;
}

void Curl_axtls_close(struct connectdata *conn, int sockindex)
{
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];

  if(connssl->ssl) {
    /* line from ssluse.c: (void)SSL_shutdown(connssl->ssl);
       axTLS compat layer does nothing for SSL_shutdown */

    /* The following line is from ssluse.c.  There seems to be no axTLS
       equivalent.  ssl_free and ssl_ctx_free close things.
       SSL_set_connect_state(connssl->handle); */

    ssl_free (connssl->ssl);
    connssl->ssl = NULL;
  }
  if(connssl->ssl_ctx) {
    ssl_ctx_free (connssl->ssl_ctx);
    connssl->ssl_ctx = NULL;
  }
}

/*
@@ -97,7 +380,54 @@ void Curl_axtls_close(struct connectdata *conn, int sockindex)
 */
int Curl_axtls_shutdown(struct connectdata *conn, int sockindex)
{
  return 0;
  /* Outline taken from ssluse.c since functions are in axTLS compat layer.
     axTLS's error set is much smaller, so a lot of error-handling was removed.
   */
  int retval = 0;
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  struct SessionHandle *data = conn->data;
  char buf[120]; /* We will use this for the OpenSSL error buffer, so it has
                    to be at least 120 bytes long. */
  ssize_t nread;

  /* This has only been tested on the proftpd server, and the mod_tls code
     sends a close notify alert without waiting for a close notify alert in
     response. Thus we wait for a close notify alert from the server, but
     we do not send one. Let's hope other servers do the same... */

  /* axTLS compat layer does nothing for SSL_shutdown, so we do nothing too
  if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)
      (void)SSL_shutdown(connssl->ssl);
  */

  if(connssl->ssl) {
    int what = Curl_socket_ready(conn->sock[sockindex],
				 CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
    if(what > 0) {
      /* Something to read, let's do it and hope that it is the close
	 notify alert from the server */
      nread = (ssize_t)SSL_read(conn->ssl[sockindex].ssl, buf,
				sizeof(buf));

      if (nread < SSL_OK){
	failf(data, "close notify alert not received during shutdown");
	retval = -1;
      }
    }
    else if(0 == what) {
      /* timeout */
      failf(data, "SSL shutdown timeout");
    }
    else {
      /* anything that gets here is fatally bad */
      failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
      retval = -1;
    }

    ssl_free (connssl->ssl);
    connssl->ssl = NULL;
  }
  return retval;
}

/*
@@ -105,13 +435,24 @@ int Curl_axtls_shutdown(struct connectdata *conn, int sockindex)
 * Otherwise we return the amount of data read. Other errors should return -1
 * and set 'wouldblock' to FALSE.
 */
ssize_t Curl_axtls_recv(struct connectdata *conn, /* connection data */
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 */
                       bool *wouldblock)
			  CURLcode *err)
{
  return 0;
  ssize_t ret = (ssize_t)SSL_read(conn->ssl[num].ssl, buf,
                                    (int)buffersize);

  /* axTLS isn't terribly generous about error reporting */
  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;
}

/*
@@ -124,16 +465,23 @@ ssize_t Curl_axtls_recv(struct connectdata *conn, /* connection data */
 */
int Curl_axtls_check_cxn(struct connectdata *conn)
{
   return 0;
  /* ssluse.c line: rc = SSL_peek(conn->ssl[FIRSTSOCKET].ssl, (void*)&buf, 1);
     axTLS compat layer always returns the last argument, so connection is 
     always alive? */

   return 1; /* connection still in place */
}

void Curl_axtls_session_free(void *ptr)
{
  /* free the ID */
  /* both ssluse.c and gtls.c do something here, but axTLS's OpenSSL
     compatibility layer does nothing, so we do nothing too. */
}

size_t Curl_axtls_version(char *buffer, size_t size)
{
  return snprintf(buffer, size, "axTLS/1.2.7");
  return snprintf(buffer, size, "axTLS/%s", ssl_version());
}

#endif /* USE_AXTLS */
+0 −10
Original line number Diff line number Diff line
@@ -38,14 +38,6 @@ void Curl_axtls_close_all(struct SessionHandle *data);
 /* close a SSL connection */
void Curl_axtls_close(struct connectdata *conn, int sockindex);

/* return number of sent (non-SSL) bytes */
ssize_t Curl_axtls_send(struct connectdata *conn, int sockindex,
                       const void *mem, size_t len);
ssize_t Curl_axtls_recv(struct connectdata *conn, /* connection data */
                       int num,                  /* socketindex */
                       char *buf,                /* store read data here */
                       size_t buffersize,        /* max amount to read */
                       bool *wouldblock);
void Curl_axtls_session_free(void *ptr);
size_t Curl_axtls_version(char *buffer, size_t size);
int Curl_axtls_shutdown(struct connectdata *conn, int sockindex);
@@ -62,8 +54,6 @@ int Curl_axtls_check_cxn(struct connectdata *conn);
#define curlssl_set_engine(x,y) (x=x, y=y, CURLE_FAILED_INIT)
#define curlssl_set_engine_default(x) (x=x, CURLE_FAILED_INIT)
#define curlssl_engines_list(x) (x=x, (struct curl_slist *)NULL)
#define curlssl_send Curl_axtls_send
#define curlssl_recv Curl_axtls_recv
#define curlssl_version Curl_axtls_version
#define curlssl_check_cxn(x) Curl_axtls_check_cxn(x)
#define curlssl_data_pending(x,y) (x=x, y=y, 0)
+12 −0
Original line number Diff line number Diff line
@@ -207,6 +207,7 @@ my $has_gnutls; # built with GnuTLS
my $has_nss;     # built with NSS
my $has_yassl;   # built with yassl
my $has_polarssl;# built with polarssl
my $has_axtls;   # built with axTLS

my $has_shared;  # built shared

@@ -696,6 +697,7 @@ sub verifyhttp {
    $flags .= "--silent ";
    $flags .= "--verbose ";
    $flags .= "--globoff ";
    $flags .= "-1 "         if($has_axtls == 1);
    $flags .= "--insecure " if($proto eq 'https');
    $flags .= "\"$proto://$ip:$port/${bonus}verifiedserver\"";

@@ -1950,6 +1952,10 @@ sub checksystem {
               $has_openssl=1;
               $ssllib="polarssl";
           } 
	   elsif ($libcurl =~ /axtls/i) {
	       $has_axtls=1;
	       $ssllib="axTLS";
	   }
        }
        elsif($_ =~ /^Protocols: (.*)/i) {
            # these are the protocols compiled in to this libcurl
@@ -2297,6 +2303,11 @@ sub singletest {
                next;
            }
        }
	elsif($f eq "axTLS") {
	    if($has_axtls) {
		next;
	    }
	}
        elsif($f eq "netrc_debug") {
            if($debug_build) {
                next;
@@ -2548,6 +2559,7 @@ sub singletest {
    if($curl_debug) {
        unlink($memdump);
    }
    $cmd = "-1 ".$cmd if(exists $feature{"SSL"} && $has_axtls == 1);

    # create a (possibly-empty) file before starting the test
    my @inputfile=getpart("client", "file");