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:
parent
3e7a1ff9a6
commit
cc9b053d84
@ -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;
|
||||
|
226
lib/vtls/gskit.c
226
lib/vtls/gskit.c
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user