/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 2010, DirecTV * contact: Eric Hu * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ***************************************************************************/ /* * Source file for all axTLS-specific code for the TLS/SSL layer. No code * but sslgen.c should ever call or use these functions. */ #include "setup.h" #ifdef USE_AXTLS #include #include "axtls.h" #ifdef HAVE_SYS_SOCKET_H #include #endif #include "sendf.h" #include "inet_pton.h" #include "sslgen.h" #include "parsedate.h" #include "connect.h" /* for the connect timeout */ #include "select.h" #define _MPRINTF_REPLACE /* use our functions only */ #include #include "curl_memory.h" /* 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; } } 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. */ 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; /* 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); } /* 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, (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, 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 verification. 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 */ static ssize_t axtls_send(struct connectdata *conn, int sockindex, const void *mem, size_t len, CURLcode *err) { /* ssl_write() returns 'int' while write() and send() returns 'size_t' */ 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; /* generic error code for send failure */ } *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) { /* 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; } } /* * This function is called to shut down the SSL layer but keep the * socket open (CCC - Clear Command Channel) */ int Curl_axtls_shutdown(struct connectdata *conn, int sockindex) { /* 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; infof(conn->data, " Curl_axtls_shutdown\n"); /* 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; } 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; } /* * Return codes: * 1 means the connection is still in place * 0 means the connection has been closed * -1 means the connection status is unknown */ int Curl_axtls_check_cxn(struct connectdata *conn) { /* 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? */ infof(conn->data, " Curl_axtls_check_cxn\n"); return 1; /* connection still in place */ } void Curl_axtls_session_free(void *ptr) { (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/%s", ssl_version()); } #endif /* USE_AXTLS */