#include "setup.h"
#ifdef USE_AXTLS
#include <axTLS/ssl.h>
#include "axtls.h"
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include "sendf.h"
#include "inet_pton.h"
#include "sslgen.h"
#include "parsedate.h"
#include "connect.h"
#include "select.h"
#define _MPRINTF_REPLACE
#include <curl/mprintf.h>
#include "curl_memory.h"
#include "memdebug.h"
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;
}
int Curl_axtls_init(void)
{
return 1;
}
int Curl_axtls_cleanup(void)
{
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:
return CURLE_UNSUPPORTED_PROTOCOL;
break;
case SSL_ERROR_NO_CIPHER:
return CURLE_SSL_CIPHER;
break;
case SSL_ERROR_BAD_CERTIFICATE:
case SSL_ERROR_NO_CERT_DEFINED:
case -42:
case -43:
case -44:
case -45:
case -46:
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:
return CURLE_SSL_CACERT;
break;
case -49:
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:
case SSL_ERROR_FINISHED_INVALID:
case SSL_ERROR_NO_CLIENT_RENOG:
default:
return CURLE_SSL_CONNECT_ERROR;
break;
}
}
static Curl_recv axtls_recv;
static Curl_send axtls_send;
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 *x509;
uint32_t client_option = SSL_NO_DEFAULT_KEY|SSL_SERVER_VERIFY_LATER;
if(conn->ssl[sockindex].state == ssl_connection_complete)
return CURLE_OK;
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
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;
}
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);
}
if(data->set.str[STRING_CERT]) {
i=0;
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++;
}
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;
}
}
if(data->set.str[STRING_KEY] && cert_types[i] != SSL_OBJ_PKCS12) {
i=0;
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++;
}
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;
}
}
if(!Curl_ssl_getsessionid(conn, (void **) &ssl_sessionid, &ssl_idsize)) {
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);
ssl_fcn_return = ssl_handshake_status(ssl);
if(ssl_fcn_return != SSL_OK) {
Curl_axtls_close(conn, sockindex);
ssl_display_error(ssl_fcn_return);
return map_error_to_curl(ssl_fcn_return);
}
infof (data, "handshake completed successfully\n");
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");
x509 = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME);
if(x509 == NULL)
infof(data, "error fetching CN from cert\n");
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;
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;
}
static ssize_t axtls_send(struct connectdata *conn,
int sockindex,
const void *mem,
size_t len,
CURLcode *err)
{
int rc = ssl_write(conn->ssl[sockindex].ssl, mem, (int)len);
infof(conn->data, " axtls_send\n");
if(rc < 0 ) {
*err = map_error_to_curl(rc);
rc = -1;
}
*err = CURLE_OK;
return rc;
}
void Curl_axtls_close_all(struct SessionHandle *data)
{
(void)data;
infof(data, " Curl_axtls_close_all\n");
}
void Curl_axtls_close(struct connectdata *conn, int sockindex)
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
infof(conn->data, " Curl_axtls_close\n");
if(connssl->ssl) {
ssl_free (connssl->ssl);
connssl->ssl = NULL;
}
if(connssl->ssl_ctx) {
ssl_ctx_free (connssl->ssl_ctx);
connssl->ssl_ctx = NULL;
}
}
int Curl_axtls_shutdown(struct connectdata *conn, int sockindex)
{
int retval = 0;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct SessionHandle *data = conn->data;
char buf[120];
ssize_t nread;
infof(conn->data, " Curl_axtls_shutdown\n");
if(connssl->ssl) {
int what = Curl_socket_ready(conn->sock[sockindex],
CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
if(what > 0) {
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) {
failf(data, "SSL shutdown timeout");
}
else {
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
retval = -1;
}
ssl_free (connssl->ssl);
connssl->ssl = NULL;
}
return retval;
}
static ssize_t axtls_recv(struct connectdata *conn,
int num,
char *buf,
size_t buffersize,
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);
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;
}
int Curl_axtls_check_cxn(struct connectdata *conn)
{
infof(conn->data, " Curl_axtls_check_cxn\n");
return 1;
}
void Curl_axtls_session_free(void *ptr)
{
(void)ptr;
}
size_t Curl_axtls_version(char *buffer, size_t size)
{
return snprintf(buffer, size, "axTLS/%s", ssl_version());
}
#endif