diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/axtls.c | 376 | ||||
| -rw-r--r-- | lib/axtls.h | 10 | 
2 files changed, 362 insertions, 24 deletions
| diff --git a/lib/axtls.c b/lib/axtls.c index 9a253bc1e..ff4ff2b15 100644 --- a/lib/axtls.c +++ b/lib/axtls.c @@ -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, -                       int sockindex, -                       const void *mem, -                       size_t len) +static ssize_t axtls_send(struct connectdata *conn, +			  int sockindex, +			  const void *mem, +			  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 */ -                       int num,                  /* socketindex */ -                       char *buf,                /* store read data here */ -                       size_t buffersize,        /* max amount to read */ -                       bool *wouldblock) +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)  { -  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 */ diff --git a/lib/axtls.h b/lib/axtls.h index ba62eecbf..f1e94b65c 100644 --- a/lib/axtls.h +++ b/lib/axtls.h @@ -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) | 
