gskit: Implement SSL over SSL

This is done via a TCP socket pair monitored at each negotiate/send/receive
attempt for explicit pipelining.
See gskit.c comments for more information.
This commit is contained in:
Patrick Monnerat 2015-12-22 15:58:28 +01:00
parent 3e7a1ff9a6
commit cc9b053d84
3 changed files with 209 additions and 22 deletions

View File

@ -331,6 +331,8 @@ struct ssl_connect_data {
gsk_handle handle;
int iocport;
ssl_connect_state connecting_state;
int localfd;
int remotefd;
#endif
#ifdef USE_AXTLS
SSL_CTX* ssl_ctx;

View File

@ -81,6 +81,10 @@
#include "memdebug.h"
/* Directions. */
#define SOS_READ 0x01
#define SOS_WRITE 0x02
/* SSL version flags. */
#define CURL_GSKPROTO_SSLV2 0
#define CURL_GSKPROTO_SSLV2_MASK (1 << CURL_GSKPROTO_SSLV2)
@ -501,6 +505,148 @@ static void close_async_handshake(struct ssl_connect_data *connssl)
connssl->iocport = -1;
}
/* SSL over SSL
* Problems:
* 1) GSKit can only perform SSL on an AF_INET or AF_INET6 stream socket. To
* pipe an SSL stream into another, it is therefore needed to have a pair
* of such communicating sockets and handle the pipelining explicitly.
* 2) OS/400 socketpair() is only implemented for domain AF_UNIX, thus cannot
* be used to produce the pipeline.
* The solution is to simulate socketpair() for AF_INET with low-level API
* listen(), bind() and connect().
*/
static int
inetsocketpair(int sv[2])
{
int lfd; /* Listening socket. */
int sfd; /* Server socket. */
int cfd; /* Client socket. */
int len;
struct sockaddr_in laddr;
/* Create listening socket on a local dynamic port. */
lfd = socket(AF_INET, SOCK_STREAM, 0);
if(lfd < 0)
return -1;
memset((char *) &laddr, 0, sizeof laddr);
laddr.sin_family = AF_INET;
laddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
laddr.sin_port = 0;
if(bind(lfd, (struct sockaddr *) &laddr, sizeof laddr) ||
listen(lfd, 0) < 0) {
close(lfd);
return -1;
}
/* Get the allocated port. */
len = sizeof laddr;
if(getsockname(lfd, (struct sockaddr *) &laddr, &len) < 0) {
close(lfd);
return -1;
}
/* Create the client socket. */
cfd = socket(AF_INET, SOCK_STREAM, 0);
if(cfd < 0) {
close(lfd);
return -1;
}
/* Request unblocking connection to the listening socket. */
curlx_nonblock(cfd, TRUE);
if(connect(cfd, (struct sockaddr *) &laddr, sizeof laddr) < 0 &&
errno != EINPROGRESS) {
close(lfd);
close(cfd);
return -1;
}
/* Accept the incoming connection and get the server socket. */
sfd = accept(lfd, NULL, NULL);
close(lfd);
if(sfd < 0) {
close(cfd);
return -1;
}
/* Done, return sockets and succeed. */
curlx_nonblock(cfd, FALSE);
sv[0] = cfd;
sv[1] = sfd;
return 0;
}
static int pipe_ssloverssl(struct connectdata *conn, int sockindex,
int directions)
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct ssl_connect_data *connproxyssl = &conn->proxy_ssl[sockindex];
fd_set fds_read;
fd_set fds_write;
int n;
int m;
int i;
struct timeval tv = {0, 0};
char buf[CURL_MAX_WRITE_SIZE];
if(!connssl->use || !connproxyssl->use)
return 0; /* No SSL over SSL: OK. */
FD_ZERO(&fds_read);
FD_ZERO(&fds_write);
n = -1;
if(directions & SOS_READ) {
FD_SET(connssl->remotefd, &fds_write);
n = connssl->remotefd;
}
if(directions & SOS_WRITE) {
FD_SET(connssl->remotefd, &fds_read);
n = connssl->remotefd;
FD_SET(conn->sock[sockindex], &fds_write);
if(n < conn->sock[sockindex])
n = conn->sock[sockindex];
}
i = select(n + 1, &fds_read, &fds_write, NULL, &tv);
if(i < 0)
return -1; /* Select error. */
if(FD_ISSET(connssl->remotefd, &fds_write)) {
/* Try getting data from HTTPS proxy and pipe it upstream. */
n = 0;
i = gsk_secure_soc_read(connproxyssl->handle, buf, sizeof buf, &n);
switch(i) {
case GSK_OK:
if(n) {
i = write(connssl->remotefd, buf, n);
if(i < 0)
return -1;
}
break;
case GSK_INVALID_STATE:
case GSK_WOULD_BLOCK:
break;
default:
return -1;
}
}
if(FD_ISSET(connssl->remotefd, &fds_read) &&
FD_ISSET(conn->sock[sockindex], &fds_write)) {
/* Pipe data to HTTPS proxy. */
n = read(connssl->remotefd, buf, sizeof buf);
if(n < 0)
return -1;
if(n) {
i = gsk_secure_soc_write(connproxyssl->handle, buf, n, &m);
if(i != GSK_OK || n != m)
return -1;
}
}
return 0; /* OK */
}
static void close_one(struct ssl_connect_data *connssl,
struct SessionHandle *data)
@ -509,6 +655,14 @@ static void close_one(struct ssl_connect_data *connssl,
gskit_status(data, gsk_secure_soc_close(&connssl->handle),
"gsk_secure_soc_close()", 0);
connssl->handle = (gsk_handle) NULL;
if(connssl->localfd >= 0) {
close(connssl->localfd);
connssl->localfd = -1;
}
if(connssl->remotefd >= 0) {
close(connssl->remotefd);
connssl->remotefd = -1;
}
}
if(connssl->iocport >= 0)
close_async_handshake(connssl);
@ -519,13 +673,18 @@ static ssize_t gskit_send(struct connectdata *conn, int sockindex,
const void *mem, size_t len, CURLcode *curlcode)
{
struct SessionHandle *data = conn->data;
CURLcode cc;
CURLcode cc = CURLE_SEND_ERROR;
int written;
cc = gskit_status(data,
gsk_secure_soc_write(conn->ssl[sockindex].handle,
(char *) mem, (int) len, &written),
"gsk_secure_soc_write()", CURLE_SEND_ERROR);
if(!pipe_ssloverssl(conn, sockindex, SOS_WRITE)) {
cc = gskit_status(data,
gsk_secure_soc_write(conn->ssl[sockindex].handle,
(char *) mem, (int) len, &written),
"gsk_secure_soc_write()", CURLE_SEND_ERROR);
if(cc == CURLE_OK)
if(pipe_ssloverssl(conn, sockindex, SOS_WRITE))
cc = CURLE_SEND_ERROR;
}
if(cc != CURLE_OK) {
*curlcode = cc;
written = -1;
@ -540,12 +699,14 @@ static ssize_t gskit_recv(struct connectdata *conn, int num, char *buf,
struct SessionHandle *data = conn->data;
int buffsize;
int nread;
CURLcode cc;
CURLcode cc = CURLE_RECV_ERROR;
buffsize = buffersize > (size_t) INT_MAX? INT_MAX: (int) buffersize;
cc = gskit_status(data, gsk_secure_soc_read(conn->ssl[num].handle,
buf, buffsize, &nread),
"gsk_secure_soc_read()", CURLE_RECV_ERROR);
if(!pipe_ssloverssl(conn, num, SOS_READ)) {
buffsize = buffersize > (size_t) INT_MAX? INT_MAX: (int) buffersize;
cc = gskit_status(data, gsk_secure_soc_read(conn->ssl[num].handle,
buf, buffsize, &nread),
"gsk_secure_soc_read()", CURLE_RECV_ERROR);
}
if(cc != CURLE_OK) {
*curlcode = cc;
nread = -1;
@ -572,21 +733,15 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex)
unsigned int protoflags;
long timeout;
Qso_OverlappedIO_t commarea;
/* GSKit does not feature an easy way to manipulate the encrypted data,
thus SSL stacking is not yet implemented.
GSKit always reads/writes the encrypted data from/to a file descriptor:
the stacking could therefore be implemented via a socketpair() and
and by periodically calling an interface procedure (no threads, no fork on
OS/400 in interactive mode). */
if(conn->proxy_ssl[sockindex].use)
return CURLE_UNSUPPORTED_PROTOCOL;
int sockpair[2];
static const int sobufsize = CURL_MAX_WRITE_SIZE;
/* Create SSL environment, start (preferably asynchronous) handshake. */
connssl->handle = (gsk_handle) NULL;
connssl->iocport = -1;
connssl->localfd = -1;
connssl->remotefd = -1;
/* GSKit supports two ways of specifying an SSL context: either by
* application identifier (that should have been defined at the system
@ -625,6 +780,24 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex)
if(result)
return result;
/* Establish a pipelining socket pair for SSL over SSL. */
if(conn->proxy_ssl[sockindex].use) {
if(inetsocketpair(sockpair))
return CURLE_SSL_CONNECT_ERROR;
connssl->localfd = sockpair[0];
connssl->remotefd = sockpair[1];
setsockopt(connssl->localfd, SOL_SOCKET, SO_RCVBUF,
(void *) sobufsize, sizeof sobufsize);
setsockopt(connssl->remotefd, SOL_SOCKET, SO_RCVBUF,
(void *) sobufsize, sizeof sobufsize);
setsockopt(connssl->localfd, SOL_SOCKET, SO_SNDBUF,
(void *) sobufsize, sizeof sobufsize);
setsockopt(connssl->remotefd, SOL_SOCKET, SO_SNDBUF,
(void *) sobufsize, sizeof sobufsize);
curlx_nonblock(connssl->localfd, TRUE);
curlx_nonblock(connssl->remotefd, TRUE);
}
/* Determine which SSL/TLS version should be enabled. */
protoflags = CURL_GSKPROTO_TLSV10_MASK | CURL_GSKPROTO_TLSV11_MASK |
CURL_GSKPROTO_TLSV12_MASK;
@ -673,7 +846,8 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex)
(timeout + 999) / 1000);
}
if(!result)
result = set_numeric(data, connssl->handle, GSK_FD, conn->sock[sockindex]);
result = set_numeric(data, connssl->handle, GSK_FD, connssl->localfd >= 0?
connssl->localfd: conn->sock[sockindex]);
if(!result)
result = set_ciphers(conn, connssl->handle, &protoflags);
if(!protoflags) {
@ -925,6 +1099,11 @@ static CURLcode gskit_connect_common(struct connectdata *conn, int sockindex,
result = gskit_connect_step1(conn, sockindex);
}
/* Handle handshake pipelining. */
if(!result)
if(pipe_ssloverssl(conn, sockindex, SOS_READ | SOS_WRITE))
result = CURLE_SSL_CONNECT_ERROR;
/* Step 2: check if handshake is over. */
if(!result && connssl->connecting_state == ssl_connect_2) {
/* check allowed time left */
@ -939,6 +1118,11 @@ static CURLcode gskit_connect_common(struct connectdata *conn, int sockindex,
result = gskit_connect_step2(conn, sockindex, nonblocking);
}
/* Handle handshake pipelining. */
if(!result)
if(pipe_ssloverssl(conn, sockindex, SOS_READ | SOS_WRITE))
result = CURLE_SSL_CONNECT_ERROR;
/* Step 3: gather certificate info, verify host. */
if(!result && connssl->connecting_state == ssl_connect_3)
result = gskit_connect_step3(conn, sockindex);

View File

@ -308,7 +308,8 @@ ssl_connect_init_proxy(struct connectdata *conn, int sockindex) {
DEBUGASSERT(conn->bits.proxy_ssl_connected[sockindex]);
if(ssl_connection_complete == conn->ssl[sockindex].state &&
!conn->proxy_ssl[sockindex].use) {
#if defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_NSS)
#if defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_NSS) || \
defined(USE_GSKIT)
conn->proxy_ssl[sockindex] = conn->ssl[sockindex];
memset(&conn->ssl[sockindex], 0, sizeof(conn->ssl[sockindex]));
#else