diff options
author | Patrick Monnerat <pm@datasphere.ch> | 2013-07-15 19:00:36 +0200 |
---|---|---|
committer | Patrick Monnerat <pm@datasphere.ch> | 2013-07-15 19:00:36 +0200 |
commit | 0eba02fd41263218a0eaedbe65af739a9bb2da63 (patch) | |
tree | 9ed6cb8bda345c6dc5b2ceeea00e747b9d425389 /lib | |
parent | 464c8693d2f6f2b45b84951feb9414da698206be (diff) |
OS400: new SSL backend GSKit
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.inc | 5 | ||||
-rw-r--r-- | lib/config-os400.h | 3 | ||||
-rw-r--r-- | lib/curl_setup.h | 2 | ||||
-rw-r--r-- | lib/gskit.c | 906 | ||||
-rw-r--r-- | lib/gskit.h | 64 | ||||
-rw-r--r-- | lib/hostcheck.c | 7 | ||||
-rw-r--r-- | lib/setup-os400.h | 92 | ||||
-rw-r--r-- | lib/sslgen.c | 2 | ||||
-rw-r--r-- | lib/url.c | 2 | ||||
-rw-r--r-- | lib/urldata.h | 9 | ||||
-rw-r--r-- | lib/x509asn1.c | 4 | ||||
-rw-r--r-- | lib/x509asn1.h | 4 |
12 files changed, 1088 insertions, 12 deletions
diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 0ba39a25f..586d94ad3 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -25,7 +25,8 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c \ http_proxy.c non-ascii.c asyn-ares.c asyn-thread.c curl_gssapi.c \ curl_ntlm.c curl_ntlm_wb.c curl_ntlm_core.c curl_ntlm_msgs.c \ curl_sasl.c curl_schannel.c curl_multibyte.c curl_darwinssl.c \ - hostcheck.c bundles.c conncache.c pipeline.c dotdot.c x509asn1.c + hostcheck.c bundles.c conncache.c pipeline.c dotdot.c x509asn1.c \ + gskit.c HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h \ progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h \ @@ -44,4 +45,4 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h \ asyn.h curl_ntlm.h curl_gssapi.h curl_ntlm_wb.h curl_ntlm_core.h \ curl_ntlm_msgs.h curl_sasl.h curl_schannel.h curl_multibyte.h \ curl_darwinssl.h hostcheck.h bundles.h conncache.h curl_setup_once.h \ - multihandle.h setup-vms.h pipeline.h dotdot.h x509asn1.h + multihandle.h setup-vms.h pipeline.h dotdot.h x509asn1.h gskit.h diff --git a/lib/config-os400.h b/lib/config-os400.h index 4381f9a30..a290fe43d 100644 --- a/lib/config-os400.h +++ b/lib/config-os400.h @@ -531,6 +531,9 @@ /* Define to use the QsoSSL package. */ #define USE_QSOSSL +/* Define to use the GSKit package. */ +#undef USE_GSKIT + /* Use the system keyring as the default CA bundle. */ #define CURL_CA_BUNDLE "/QIBM/UserData/ICSS/Cert/Server/DEFAULT.KDB" diff --git a/lib/curl_setup.h b/lib/curl_setup.h index a18d085f6..a46a60828 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -617,7 +617,7 @@ int netware_init(void); #if defined(USE_GNUTLS) || defined(USE_SSLEAY) || defined(USE_NSS) || \ defined(USE_QSOSSL) || defined(USE_POLARSSL) || defined(USE_AXTLS) || \ defined(USE_CYASSL) || defined(USE_SCHANNEL) || \ - defined(USE_DARWINSSL) + defined(USE_DARWINSSL) || defined(USE_GSKIT) #define USE_SSL /* SSL support has been enabled */ #endif diff --git a/lib/gskit.c b/lib/gskit.c new file mode 100644 index 000000000..5cda85b9b --- /dev/null +++ b/lib/gskit.c @@ -0,0 +1,906 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * 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. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#ifdef USE_GSKIT + +#include <gskssl.h> +#include <qsoasync.h> + +/* Some symbols are undefined/unsupported on OS400 versions < V7R1. */ +#ifndef GSK_SSL_EXTN_SERVERNAME_REQUEST +#define GSK_SSL_EXTN_SERVERNAME_REQUEST 230 +#endif + +#ifdef HAVE_LIMITS_H +# include <limits.h> +#endif + +#include <curl/curl.h> +#include "urldata.h" +#include "sendf.h" +#include "gskit.h" +#include "sslgen.h" +#include "connect.h" /* for the connect timeout */ +#include "select.h" +#include "strequal.h" +#include "x509asn1.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + + +/* Supported ciphers. */ +typedef struct { + const char * name; /* Cipher name. */ + const char * gsktoken; /* Corresponding token for GSKit String. */ + int sslver; /* SSL version. */ +} gskit_cipher; + +static const gskit_cipher ciphertable[] = { + { "null-md5", "01", CURL_SSLVERSION_SSLv3 }, + { "null-sha", "02", CURL_SSLVERSION_SSLv3 }, + { "exp-rc4-md5", "03", CURL_SSLVERSION_SSLv3 }, + { "rc4-md5", "04", CURL_SSLVERSION_SSLv3 }, + { "rc4-sha", "05", CURL_SSLVERSION_SSLv3 }, + { "exp-rc2-cbc-md5", "06", CURL_SSLVERSION_SSLv3 }, + { "exp-des-cbc-sha", "09", CURL_SSLVERSION_SSLv3 }, + { "des-cbc3-sha", "0A", CURL_SSLVERSION_SSLv3 }, + { "aes128-sha", "2F", CURL_SSLVERSION_TLSv1 }, + { "aes256-sha", "35", CURL_SSLVERSION_TLSv1 }, + { "rc4-md5", "1", CURL_SSLVERSION_SSLv2 }, + { "exp-rc4-md5", "2", CURL_SSLVERSION_SSLv2 }, + { "rc2-md5", "3", CURL_SSLVERSION_SSLv2 }, + { "exp-rc2-md5", "4", CURL_SSLVERSION_SSLv2 }, + { "des-cbc-md5", "6", CURL_SSLVERSION_SSLv2 }, + { "des-cbc3-md5", "7", CURL_SSLVERSION_SSLv2 }, + { (const char *) NULL, (const char *) NULL, 0 } +}; + + +static bool is_separator(char c) +{ + /* Return whether character is a cipher list separator. */ + switch (c) { + case ' ': + case '\t': + case ':': + case ',': + case ';': + return true; + } + return false; +} + + +static CURLcode gskit_status(struct SessionHandle * data, int rc, + const char * procname, CURLcode defcode) +{ + CURLcode cc; + + /* Process GSKit status and map it to a CURLcode. */ + switch (rc) { + case GSK_OK: + case GSK_OS400_ASYNCHRONOUS_SOC_INIT: + return CURLE_OK; + case GSK_KEYRING_OPEN_ERROR: + case GSK_OS400_ERROR_NO_ACCESS: + return CURLE_SSL_CACERT_BADFILE; + case GSK_INSUFFICIENT_STORAGE: + return CURLE_OUT_OF_MEMORY; + case GSK_ERROR_BAD_V2_CIPHER: + case GSK_ERROR_BAD_V3_CIPHER: + case GSK_ERROR_NO_CIPHERS: + return CURLE_SSL_CIPHER; + case GSK_OS400_ERROR_NOT_TRUSTED_ROOT: + case GSK_ERROR_CERT_VALIDATION: + return CURLE_PEER_FAILED_VERIFICATION; + case GSK_OS400_ERROR_TIMED_OUT: + return CURLE_OPERATION_TIMEDOUT; + case GSK_WOULD_BLOCK: + return CURLE_AGAIN; + case GSK_OS400_ERROR_NOT_REGISTERED: + break; + case GSK_ERROR_IO: + switch (errno) { + case ENOMEM: + return CURLE_OUT_OF_MEMORY; + default: + failf(data, "%s I/O error: %s", procname, strerror(errno)); + break; + } + break; + default: + failf(data, "%s: %s", procname, gsk_strerror(rc)); + break; + } + return defcode; +} + + +static CURLcode set_enum(struct SessionHandle * data, + gsk_handle h, GSK_ENUM_ID id, GSK_ENUM_VALUE value) +{ + int rc = gsk_attribute_set_enum(h, id, value); + + switch (rc) { + case GSK_OK: + return CURLE_OK; + case GSK_ERROR_IO: + failf(data, "gsk_attribute_set_enum() I/O error: %s", strerror(errno)); + break; + default: + failf(data, "gsk_attribute_set_enum(): %s", gsk_strerror(rc)); + break; + } + return CURLE_SSL_CONNECT_ERROR; +} + + +static CURLcode set_buffer(struct SessionHandle * data, + gsk_handle h, GSK_BUF_ID id, const char * buffer) +{ + int rc = gsk_attribute_set_buffer(h, id, buffer, 0); + + switch (rc) { + case GSK_OK: + return CURLE_OK; + case GSK_ERROR_IO: + failf(data, "gsk_attribute_set_buffer() I/O error: %s", strerror(errno)); + break; + default: + failf(data, "gsk_attribute_set_buffer(): %s", gsk_strerror(rc)); + break; + } + return CURLE_SSL_CONNECT_ERROR; +} + + +static CURLcode set_numeric(struct SessionHandle * data, + gsk_handle h, GSK_NUM_ID id, int value) +{ + int rc = gsk_attribute_set_numeric_value(h, id, value); + + switch (rc) { + case GSK_OK: + return CURLE_OK; + case GSK_ERROR_IO: + failf(data, "gsk_attribute_set_numeric_value() I/O error: %s", + strerror(errno)); + break; + default: + failf(data, "gsk_attribute_set_numeric_value(): %s", gsk_strerror(rc)); + break; + } + return CURLE_SSL_CONNECT_ERROR; +} + + +static CURLcode set_callback(struct SessionHandle * data, + gsk_handle h, GSK_CALLBACK_ID id, void * info) +{ + int rc = gsk_attribute_set_callback(h, id, info); + + switch (rc) { + case GSK_OK: + return CURLE_OK; + case GSK_ERROR_IO: + failf(data, "gsk_attribute_set_callback() I/O error: %s", strerror(errno)); + break; + default: + failf(data, "gsk_attribute_set_callback(): %s", gsk_strerror(rc)); + break; + } + return CURLE_SSL_CONNECT_ERROR; +} + + +static CURLcode set_ciphers(struct SessionHandle * data, gsk_handle h) +{ + const char * cipherlist = data->set.str[STRING_SSL_CIPHER_LIST]; + char * sslv2ciphers; + char * sslv3ciphers; + const char * clp; + const gskit_cipher * ctp; + char * v2p; + char * v3p; + int i; + CURLcode cc; + + /* Compile cipher list into GSKit-compatible cipher lists. */ + + if(!cipherlist) + return CURLE_OK; + while(is_separator(*cipherlist)) /* Skip initial separators. */ + cipherlist++; + if(!*cipherlist) + return CURLE_OK; + + /* We allocate GSKit buffers of the same size as the input string: since + GSKit tokens are always shorter than their cipher names, allocated buffers + will always be large enough to accomodate the result. */ + i = strlen(cipherlist) + 1; + v2p = malloc(i); + if(!v2p) + return CURLE_OUT_OF_MEMORY; + v3p = malloc(i); + if(!v3p) { + free(v2p); + return CURLE_OUT_OF_MEMORY; + } + sslv2ciphers = v2p; + sslv3ciphers = v3p; + + /* Process each cipher in input string. */ + for(;;) { + for(clp = cipherlist; *cipherlist && !is_separator(*cipherlist);) + cipherlist++; + i = cipherlist - clp; + if(!i) + break; + /* Search the cipher in our table. */ + for(ctp = ciphertable; ctp->name; ctp++) + if(strnequal(ctp->name, clp, i) && !ctp->name[i]) + break; + if(!ctp->name) + failf(data, "Unknown cipher %.*s: ignored", i, clp); + else { + switch (ctp->sslver) { + case CURL_SSLVERSION_SSLv2: + strcpy(v2p, ctp->gsktoken); + v2p += strlen(v2p); + break; + default: + /* GSKit wants TLSv1 ciphers with SSLv3 ciphers. */ + strcpy(v3p, ctp->gsktoken); + v3p += strlen(v3p); + break; + } + } + + /* Advance to next cipher name or end of string. */ + while(is_separator(*cipherlist)) + cipherlist++; + } + *v2p = '\0'; + *v3p = '\0'; + cc = set_buffer(data, h, GSK_V2_CIPHER_SPECS, sslv2ciphers); + if(cc == CURLE_OK) + cc = set_buffer(data, h, GSK_V3_CIPHER_SPECS, sslv3ciphers); + free(sslv2ciphers); + free(sslv3ciphers); + return cc; +} + + +int Curl_gskit_init(void) +{ + /* No initialisation needed. */ + + return 1; +} + + +void Curl_gskit_cleanup(void) +{ + /* Nothing to do. */ +} + + +static CURLcode init_environment(struct SessionHandle * data, + gsk_handle * envir, const char * appid, + const char * file, const char * label, + const char * password) +{ + int rc; + CURLcode c; + gsk_handle h; + + /* Creates the GSKit environment. */ + + rc = gsk_environment_open(&h); + switch (rc) { + case GSK_OK: + break; + case GSK_INSUFFICIENT_STORAGE: + return CURLE_OUT_OF_MEMORY; + default: + failf(data, "gsk_environment_open(): %s", gsk_strerror(rc)); + return CURLE_SSL_CONNECT_ERROR; + } + + c = set_enum(data, h, GSK_SESSION_TYPE, GSK_CLIENT_SESSION); + if(c == CURLE_OK && appid) + c = set_buffer(data, h, GSK_OS400_APPLICATION_ID, appid); + if(c == CURLE_OK && file) + c = set_buffer(data, h, GSK_KEYRING_FILE, file); + if(c == CURLE_OK && label) + c = set_buffer(data, h, GSK_KEYRING_LABEL, label); + if(c == CURLE_OK && password) + c = set_buffer(data, h, GSK_KEYRING_PW, password); + + if(c == CURLE_OK) { + /* Locate CAs, Client certificate and key according to our settings. + Note: this call may be blocking for some tenths of seconds. */ + c = gskit_status(data, gsk_environment_init(h), + "gsk_environment_init()", CURLE_SSL_CERTPROBLEM); + if(c == CURLE_OK) { + *envir = h; + return c; + } + } + /* Error: rollback. */ + gsk_environment_close(&h); + return c; +} + + +static void cancel_async_handshake(struct connectdata * conn, int sockindex) +{ + struct ssl_connect_data * connssl = &conn->ssl[sockindex]; + Qso_OverlappedIO_t cstat; + + if(QsoCancelOperation(conn->sock[sockindex], 0) > 0) + QsoWaitForIOCompletion(connssl->iocport, &cstat, (struct timeval *) NULL); +} + + +static void close_async_handshake(struct ssl_connect_data * connssl) +{ + QsoDestroyIOCompletionPort(connssl->iocport); + connssl->iocport = -1; +} + + +static void close_one(struct ssl_connect_data * conn, + struct SessionHandle * data) +{ + if(conn->handle) { + gskit_status(data, gsk_secure_soc_close(&conn->handle), + "gsk_secure_soc_close()", 0); + conn->handle = (gsk_handle) NULL; + } + if(conn->iocport >= 0) + close_async_handshake(conn); +} + + +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; + 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(cc != CURLE_OK) { + *curlcode = cc; + written = -1; + } + return (ssize_t) written; /* number of bytes */ +} + + +static ssize_t gskit_recv(struct connectdata * conn, int num, char * buf, + size_t buffersize, CURLcode * curlcode) +{ + struct SessionHandle * data = conn->data; + int buffsize; + int nread; + CURLcode cc; + + 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; + } + return (ssize_t) nread; +} + + +static CURLcode gskit_connect_step1(struct connectdata * conn, int sockindex) +{ + struct SessionHandle * data = conn->data; + struct ssl_connect_data * connssl = &conn->ssl[sockindex]; + gsk_handle envir; + CURLcode cc; + int rc; + char * keyringfile; + char * keyringpwd; + char * keyringlabel; + char * v2ciphers; + char * v3ciphers; + char * sni; + bool sslv2enable, sslv3enable, tlsv1enable; + long timeout; + Qso_OverlappedIO_t commarea; + + /* Create SSL environment, start (preferably asynchronous) handshake. */ + + connssl->handle = (gsk_handle) NULL; + connssl->iocport = -1; + + /* GSKit supports two ways of specifying an SSL context: either by + * application identifier (that should have been defined at the system + * level) or by keyring file, password and certificate label. + * Local certificate name (CURLOPT_SSLCERT) is used to hold either the + * application identifier of the certificate label. + * Key password (CURLOPT_KEYPASSWD) holds the keyring password. + * It is not possible to have different keyrings for the CAs and the + * local certificate. We thus use the CA file (CURLOPT_CAINFO) to identify + * the keyring file. + * If no key password is given and the keyring is the system keyring, + * application identifier mode is tried first, as recommended in IBM doc. + */ + + keyringfile = data->set.str[STRING_SSL_CAFILE]; + keyringpwd = data->set.str[STRING_KEY_PASSWD]; + keyringlabel = data->set.str[STRING_CERT]; + envir = (gsk_handle) NULL; + + if(keyringlabel && *keyringlabel && !keyringpwd && + !strcmp(keyringfile, CURL_CA_BUNDLE)) { + /* Try application identifier mode. */ + init_environment(data, &envir, keyringlabel, (const char *) NULL, + (const char *) NULL, (const char *) NULL); + } + + if(!envir) { + /* Use keyring mode. */ + cc = init_environment(data, &envir, (const char *) NULL, + keyringfile, keyringlabel, keyringpwd); + if(cc != CURLE_OK) + return cc; + } + + /* Create secure session. */ + cc = gskit_status(data, gsk_secure_soc_open(envir, &connssl->handle), + "gsk_secure_soc_open()", CURLE_SSL_CONNECT_ERROR); + gsk_environment_close(&envir); + if(cc != CURLE_OK) + return cc; + + /* Determine which SSL/TLS version should be enabled. */ + sslv2enable = sslv3enable = tlsv1enable = false; + sni = conn->host.name; + switch (data->set.ssl.version) { + case CURL_SSLVERSION_SSLv2: + sslv2enable = true; + sni = (char *) NULL; + break; + case CURL_SSLVERSION_SSLv3: + sslv3enable = true; + sni = (char *) NULL; + break; + case CURL_SSLVERSION_TLSv1: + tlsv1enable = true; + break; + default: /* CURL_SSLVERSION_DEFAULT. */ + sslv3enable = true; + tlsv1enable = true; + break; + } + + /* Process SNI. Ignore if not supported (on OS400 < V7R1). */ + if(sni) { + rc = gsk_attribute_set_buffer(connssl->handle, + GSK_SSL_EXTN_SERVERNAME_REQUEST, sni, 0); + switch (rc) { + case GSK_OK: + case GSK_ATTRIBUTE_INVALID_ID: + break; + case GSK_ERROR_IO: + failf(data, "gsk_attribute_set_buffer() I/O error: %s", strerror(errno)); + cc = CURLE_SSL_CONNECT_ERROR; + break; + default: + failf(data, "gsk_attribute_set_buffer(): %s", gsk_strerror(rc)); + cc = CURLE_SSL_CONNECT_ERROR; + break; + } + } + + /* Set session parameters. */ + if(cc == CURLE_OK) { + /* Compute the handshake timeout. Since GSKit granularity is 1 second, + we round up the required value. */ + timeout = Curl_timeleft(data, NULL, TRUE); + if(timeout < 0) + cc = CURLE_OPERATION_TIMEDOUT; + else + cc = set_numeric(data, connssl->handle, GSK_HANDSHAKE_TIMEOUT, + (timeout + 999) / 1000); + } + if(cc == CURLE_OK) + cc = set_numeric(data, connssl->handle, GSK_FD, conn->sock[sockindex]); + if(cc == CURLE_OK) + cc = set_ciphers(data, connssl->handle); + if(cc == CURLE_OK) + cc = set_enum(data, connssl->handle, GSK_PROTOCOL_SSLV2, + sslv2enable? GSK_PROTOCOL_SSLV2_ON: + GSK_PROTOCOL_SSLV2_OFF); + if(cc == CURLE_OK) + cc = set_enum(data, connssl->handle, GSK_PROTOCOL_SSLV3, + sslv3enable? GSK_PROTOCOL_SSLV3_ON: + GSK_PROTOCOL_SSLV3_OFF); + if(cc == CURLE_OK) + cc = set_enum(data, connssl->handle, GSK_PROTOCOL_TLSV1, + sslv3enable? GSK_PROTOCOL_TLSV1_ON: + GSK_PROTOCOL_TLSV1_OFF); + if(cc == CURLE_OK) + cc = set_enum(data, connssl->handle, GSK_SERVER_AUTH_TYPE, + data->set.ssl.verifypeer? GSK_SERVER_AUTH_FULL: + GSK_SERVER_AUTH_PASSTHRU); + + if(cc == CURLE_OK) { + /* Start handshake. Try asynchronous first. */ + memset(&commarea, 0, sizeof commarea); + connssl->iocport = QsoCreateIOCompletionPort(); + if(connssl->iocport != -1) { + cc = gskit_status(data, gsk_secure_soc_startInit(connssl->handle, + connssl->iocport, &commarea), + "gsk_secure_soc_startInit()", CURLE_SSL_CONNECT_ERROR); + if(cc == CURLE_OK) { + connssl->connecting_state = ssl_connect_2; + return CURLE_OK; + } + else + close_async_handshake(connssl); + } + else if(errno != ENOBUFS) + cc = gskit_status(data, GSK_ERROR_IO, "QsoCreateIOCompletionPort()", 0); + else { + /* No more completion port available. Use synchronous IO. */ + cc = gskit_status(data, gsk_secure_soc_init(connssl->handle), + "gsk_secure_soc_init()", CURLE_SSL_CONNECT_ERROR); + if(cc == CURLE_OK) { + connssl->connecting_state = ssl_connect_3; + return CURLE_OK; + } + } + } + + /* Error: rollback. */ + close_one(connssl, data); + return cc; +} + + +static CURLcode gskit_connect_step2(struct connectdata * conn, int sockindex, + bool nonblocking) +{ + struct SessionHandle * data = conn->data; + struct ssl_connect_data * connssl = &conn->ssl[sockindex]; + Qso_OverlappedIO_t cstat; + long timeout_ms; + struct timeval stmv; + CURLcode cc; + + /* Poll or wait for end of SSL asynchronous handshake. */ + + for(;;) { + timeout_ms = nonblocking? 0: Curl_timeleft(data, NULL, TRUE); + if(timeout_ms < 0) + timeout_ms = 0; + stmv.tv_sec = timeout_ms / 1000; + stmv.tv_usec = (timeout_ms - stmv.tv_sec * 1000) * 1000; + switch (QsoWaitForIOCompletion(connssl->iocport, &cstat, &stmv)) { + case 1: /* Operation complete. */ + break; + case -1: /* An error occurred: handshake still in progress. */ + if(errno == EINTR) { + if(nonblocking) + return CURLE_OK; + continue; /* Retry. */ + } + if(errno != ETIME) { + failf(data, "QsoWaitForIOCompletion() I/O error: %s", strerror(errno)); + cancel_async_handshake(conn, sockindex); + close_async_handshake(connssl); + return CURLE_SSL_CONNECT_ERROR; + } + /* FALL INTO... */ + case 0: /* Handshake in progress, timeout occurred. */ + if(nonblocking) + return CURLE_OK; + cancel_async_handshake(conn, sockindex); + close_async_handshake(connssl); + return CURLE_OPERATION_TIMEDOUT; + } + break; + } + cc = gskit_status(data, cstat.returnValue, "SSL handshake", + CURLE_SSL_CONNECT_ERROR); + if(cc == CURLE_OK) + connssl->connecting_state = ssl_connect_3; + close_async_handshake(connssl); + return cc; +} + + +static CURLcode gskit_connect_step3(struct connectdata * conn, int sockindex) +{ + struct SessionHandle * data = conn->data; + struct ssl_connect_data * connssl = &conn->ssl[sockindex]; + const gsk_cert_data_elem * cdev; + int cdec; + const gsk_cert_data_elem * p; + const char * cert = (const char *) NULL; + const char * certend; + int i; + CURLcode cc; + + /* SSL handshake done: gather certificate info and verify host. */ + + if(gskit_status(data, gsk_attribute_get_cert_info(connssl->handle, + GSK_PARTNER_CERT_INFO, + &cdev, &cdec), + "gsk_attribute_get_cert_info()", CURLE_SSL_CONNECT_ERROR) == + CURLE_OK) { + infof(data, "Server certificate:\n"); + p = cdev; + for(i = 0; i++ < cdec; p++) + switch (p->cert_data_id) { + case CERT_BODY_DER: + cert = p->cert_data_p; + certend = cert + cdev->cert_data_l; + break; + case CERT_DN_PRINTABLE: + infof(data, "\t subject: %.*s\n", p->cert_data_l, p->cert_data_p); + break; + case CERT_ISSUER_DN_PRINTABLE: + infof(data, "\t issuer: %.*s\n", p->cert_data_l, p->cert_data_p); + break; + case CERT_VALID_FROM: + infof(data, "\t start date: %.*s\n", p->cert_data_l, p->cert_data_p); + break; + case CERT_VALID_TO: + infof(data, "\t expire date: %.*s\n", p->cert_data_l, p->cert_data_p); + break; + } + } + + /* Verify host. */ + cc = Curl_verifyhost(conn, cert, certend); + if(cc != CURLE_OK) + return cc; + + /* The only place GSKit can get the whole CA chain is a validation + callback where no user data pointer is available. Therefore it's not + possible to copy this chain into our structures for CAINFO. + However the server certificate may be available, thus we can return + info about it. */ + if(data->set.ssl.certinfo) { + if(Curl_ssl_init_certinfo(data, 1)) + return CURLE_OUT_OF_MEMORY; + if(cert) { + cc = Curl_extract_certinfo(conn, 0, cert, certend); + if(cc != CURLE_OK) + return cc; + } + } + + connssl->connecting_state = ssl_connect_done; + return CURLE_OK; +} + + +static CURLcode gskit_connect_common(struct connectdata * conn, int sockindex, + bool nonblocking, bool * done) +{ + struct SessionHandle * data = conn->data; + struct ssl_connect_data * connssl = &conn->ssl[sockindex]; + long timeout_ms; + Qso_OverlappedIO_t cstat; + CURLcode cc = CURLE_OK; + + *done = connssl->state == ssl_connection_complete; + if(*done) + return CURLE_OK; + + /* Step 1: create session, start handshake. */ + if(connssl->connecting_state == ssl_connect_1) { + /* check allowed time left */ + timeout_ms = Curl_timeleft(data, NULL, TRUE); + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + cc = CURLE_OPERATION_TIMEDOUT; + } + else + cc = gskit_connect_step1(conn, sockindex); + } + + /* Step 2: check if handshake is over. */ + if(cc == CURLE_OK && connssl->connecting_state == ssl_connect_2) { + /* check allowed time left */ + timeout_ms = Curl_timeleft(data, NULL, TRUE); + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + cc = CURLE_OPERATION_TIMEDOUT; + } + else + cc = gskit_connect_step2(conn, sockindex, nonblocking); + } + + /* Step 3: gather certificate info, verify host. */ + if(cc == CURLE_OK && connssl->connecting_state == ssl_connect_3) + cc = gskit_connect_step3(conn, sockindex); + + if(cc != CURLE_OK) + close_one(connssl, data); + else if(connssl->connecting_state == ssl_connect_done) { + connssl->state = ssl_connection_complete; + connssl->connecting_state = ssl_connect_1; + conn->recv[sockindex] = gskit_recv; + conn->send[sockindex] = gskit_send; + *done = TRUE; + } + + return cc; +} + + +CURLcode Curl_gskit_connect_nonblocking(struct connectdata * conn, + int sockindex, + bool * done) +{ + CURLcode cc; + + cc = gskit_connect_common(conn, sockindex, TRUE, done); + if(*done || cc != CURLE_OK) + conn->ssl[sockindex].connecting_state = ssl_connect_1; + return cc; +} + + +CURLcode Curl_gskit_connect(struct connectdata * conn, int sockindex) +{ + CURLcode retcode; + bool done; + + conn->ssl[sockindex].connecting_state = ssl_connect_1; + retcode = gskit_connect_common(conn, sockindex, FALSE, &done); + if(retcode) + return retcode; + + DEBUGASSERT(done); + + return CURLE_OK; +} + + +void Curl_gskit_close(struct connectdata * conn, int sockindex) +{ + struct SessionHandle * data = conn->data; + struct ssl_connect_data * connssl = &conn->ssl[sockindex]; + + if(connssl->use) + close_one(connssl, data); +} + + +int Curl_gskit_close_all(struct SessionHandle * data) +{ + /* Unimplemented. */ + (void) data; + return 0; +} + + +int Curl_gskit_shutdown(struct connectdata * conn, int sockindex) +{ + struct ssl_connect_data * connssl = &conn->ssl[sockindex]; + struct SessionHandle * data = conn->data; + ssize_t nread; + int what; + int rc; + char buf[120]; + + if(!connssl->handle) + return 0; + + if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE) + return 0; + + close_one(connssl, data); + rc = 0; + what = Curl_socket_ready(conn->sock[sockindex], + CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT); + + for(;;) { + if(what < 0) { + /* anything that gets here is fatally bad */ + failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); + rc = -1; + break; + } + + if(!what) { /* timeout */ + failf(data, "SSL shutdown timeout"); + break; + } + + /* Something to read, let's do it and hope that it is the close + notify alert from the server. No way to gsk_secure_soc_read() now, so + use read(). */ + + nread = read(conn->sock[sockindex], buf, sizeof(buf)); + + if(nread < 0) { + failf(data, "read: %s", strerror(errno)); + rc = -1; + } + + if(nread <= 0) + break; + + what = Curl_socket_ready(conn->sock[sockindex], CURL_SOCKET_BAD, 0); + } + + return rc; +} + + +size_t Curl_gskit_version(char * buffer, size_t size) +{ + strncpy(buffer, "GSKit", size); + return strlen(buffer); +} + + +int Curl_gskit_check_cxn(struct connectdata * cxn) +{ + int err; + int errlen; + + /* The only thing that can be tested here is at the socket level. */ + + if(!cxn->ssl[FIRSTSOCKET].handle) + return 0; /* connection has been closed */ + + err = 0; + errlen = sizeof err; + + if(getsockopt(cxn->sock[FIRSTSOCKET], SOL_SOCKET, SO_ERROR, + (unsigned char *) &err, &errlen) || + errlen != sizeof err || err) + return 0; /* connection has been closed */ + + return -1; /* connection status unknown */ +} + +#endif /* USE_GSKIT */ diff --git a/lib/gskit.h b/lib/gskit.h new file mode 100644 index 000000000..0d59aa709 --- /dev/null +++ b/lib/gskit.h @@ -0,0 +1,64 @@ +#ifndef HEADER_CURL_GSKIT_H +#define HEADER_CURL_GSKIT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * 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. + * + ***************************************************************************/ +#include "curl_setup.h" + +/* + * This header should only be needed to get included by sslgen.c and gskit.c + */ + +#include "urldata.h" + +#ifdef USE_GSKIT +int Curl_gskit_init(void); +void Curl_gskit_cleanup(void); +CURLcode Curl_gskit_connect(struct connectdata * conn, int sockindex); +CURLcode Curl_gskit_connect_nonblocking(struct connectdata * conn, + int sockindex, bool * done); +void Curl_gskit_close(struct connectdata *conn, int sockindex); +int Curl_gskit_close_all(struct SessionHandle * data); +int Curl_gskit_shutdown(struct connectdata * conn, int sockindex); + +size_t Curl_gskit_version(char * buffer, size_t size); +int Curl_gskit_check_cxn(struct connectdata * cxn); + +/* API setup for GSKit */ +#define curlssl_init Curl_gskit_init +#define curlssl_cleanup Curl_gskit_cleanup +#define curlssl_connect Curl_gskit_connect +#define curlssl_connect_nonblocking Curl_gskit_connect_nonblocking + +/* No session handling for GSKit */ +#define curlssl_session_free(x) Curl_nop_stmt +#define curlssl_close_all Curl_gskit_close_all +#define curlssl_close Curl_gskit_close +#define curlssl_shutdown(x,y) Curl_gskit_shutdown(x,y) +#define curlssl_set_engine(x,y) CURLE_NOT_BUILT_IN +#define curlssl_set_engine_default(x) CURLE_NOT_BUILT_IN +#define curlssl_engines_list(x) NULL +#define curlssl_version Curl_gskit_version +#define curlssl_check_cxn(x) Curl_gskit_check_cxn(x) +#define curlssl_data_pending(x,y) 0 +#endif /* USE_GSKIT */ + +#endif /* HEADER_CURL_GSKIT_H */ diff --git a/lib/hostcheck.c b/lib/hostcheck.c index ef32d8cff..abd1fa0c3 100644 --- a/lib/hostcheck.c +++ b/lib/hostcheck.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,7 +22,8 @@ #include "curl_setup.h" -#if defined(USE_SSLEAY) || defined(USE_AXTLS) || defined(USE_QSOSSL) +#if defined(USE_SSLEAY) || defined(USE_AXTLS) || defined(USE_QSOSSL) || \ + defined(USE_GSKIT) /* these backends use functions from this file */ #include "hostcheck.h" @@ -93,4 +94,4 @@ int Curl_cert_hostcheck(const char *match_pattern, const char *hostname) return 0; } -#endif /* SSLEAY or AXTLS or QSOSSL */ +#endif /* SSLEAY or AXTLS or QSOSSL or GSKIT */ diff --git a/lib/setup-os400.h b/lib/setup-os400.h index cdeefe3dd..319efec92 100644 --- a/lib/setup-os400.h +++ b/lib/setup-os400.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -38,6 +38,8 @@ typedef unsigned long u_int32_t; #include <sys/socket.h> #include <netdb.h> #include <qsossl.h> +#include <gskssl.h> +#include <qsoasync.h> #include <gssapi.h> extern int Curl_getaddrinfo_a(const char * nodename, const char * servname, @@ -68,6 +70,93 @@ extern char * Curl_SSL_Strerror_a(int sslreturnvalue, SSLErrorMsg * serrmsgp); #define SSL_Strerror Curl_SSL_Strerror_a +/* GSKit wrappers. */ + +extern int Curl_gsk_environment_open(gsk_handle * my_env_handle); +#define gsk_environment_open Curl_gsk_environment_open + +extern int Curl_gsk_secure_soc_open(gsk_handle my_env_handle, + gsk_handle * my_session_handle); +#define gsk_secure_soc_open Curl_gsk_secure_soc_open + +extern int Curl_gsk_environment_close(gsk_handle * my_env_handle); +#define gsk_environment_close Curl_gsk_environment_close + +extern int Curl_gsk_secure_soc_close(gsk_handle * my_session_handle); +#define gsk_secure_soc_close Curl_gsk_secure_soc_close + +extern int Curl_gsk_environment_init(gsk_handle my_env_handle); +#define gsk_environment_init Curl_gsk_environment_init + +extern int Curl_gsk_secure_soc_init(gsk_handle my_session_handle); +#define gsk_secure_soc_init Curl_gsk_secure_soc_init + +extern int Curl_gsk_attribute_set_buffer_a(gsk_handle my_gsk_handle, + GSK_BUF_ID bufID, + const char * buffer, + int bufSize); +#define gsk_attribute_set_buffer Curl_gsk_attribute_set_buffer_a + +extern int Curl_gsk_attribute_set_enum(gsk_handle my_gsk_handle, + GSK_ENUM_ID enumID, + GSK_ENUM_VALUE enumValue); +#define gsk_attribute_set_enum Curl_gsk_attribute_set_enum + +extern int Curl_gsk_attribute_set_numeric_value(gsk_handle my_gsk_handle, + GSK_NUM_ID numID, + int numValue); +#define gsk_attribute_set_numeric_value Curl_gsk_attribute_set_numeric_value + +extern int Curl_gsk_attribute_set_callback(gsk_handle my_gsk_handle, + GSK_CALLBACK_ID callBackID, + void * callBackAreaPtr); +#define gsk_attribute_set_callback Curl_gsk_attribute_set_callback + +extern int Curl_gsk_attribute_get_buffer_a(gsk_handle my_gsk_handle, + GSK_BUF_ID bufID, + const char * * buffer, + int * bufSize); +#define gsk_attribute_get_buffer Curl_gsk_attribute_get_buffer_a + +extern int Curl_gsk_attribute_get_enum(gsk_handle my_gsk_handle, + GSK_ENUM_ID enumID, + GSK_ENUM_VALUE * enumValue); +#define gsk_attribute_get_enum Curl_gsk_attribute_get_enum + +extern int Curl_gsk_attribute_get_numeric_value(gsk_handle my_gsk_handle, + GSK_NUM_ID numID, + int * numValue); +#define gsk_attribute_get_numeric_value Curl_gsk_attribute_get_numeric_value + +extern int Curl_gsk_attribute_get_cert_info(gsk_handle my_gsk_handle, + GSK_CERT_ID certID, + const gsk_cert_data_elem * * certDataElem, + int * certDataElementCount); +#define gsk_attribute_get_cert_info Curl_gsk_attribute_get_cert_info + +extern int Curl_gsk_secure_soc_misc(gsk_handle my_session_handle, + GSK_MISC_ID miscID); +#define gsk_secure_soc_misc Curl_gsk_secure_soc_misc + +extern int Curl_gsk_secure_soc_read(gsk_handle my_session_handle, + char * readBuffer, + int readBufSize, int * amtRead); +#define gsk_secure_soc_read Curl_gsk_secure_soc_read + +extern int Curl_gsk_secure_soc_write(gsk_handle my_session_handle, + char * writeBuffer, + int writeBufSize, int * amtWritten); +#define gsk_secure_soc_write Curl_gsk_secure_soc_write + +extern const char * Curl_gsk_strerror_a(int gsk_return_value); +#define gsk_strerror Curl_gsk_strerror_a + +extern int Curl_gsk_secure_soc_startInit(gsk_handle my_session_handle, + int IOCompletionPort, + Qso_OverlappedIO_t * communicationsArea); +#define gsk_secure_soc_startInit Curl_gsk_secure_soc_startInit + + /* GSSAPI wrappers. */ extern OM_uint32 Curl_gss_import_name_a(OM_uint32 * minor_status, @@ -107,6 +196,7 @@ extern OM_uint32 Curl_gss_delete_sec_context_a(OM_uint32 * minor_status, gss_buffer_t output_token); #define gss_delete_sec_context Curl_gss_delete_sec_context_a + /* LDAP wrappers. */ #define BerValue struct berval diff --git a/lib/sslgen.c b/lib/sslgen.c index d9b5c4715..d2d0e303e 100644 --- a/lib/sslgen.c +++ b/lib/sslgen.c @@ -32,6 +32,7 @@ Curl_gtls_ - prefix for GnuTLS ones Curl_nss_ - prefix for NSS ones Curl_qssl_ - prefix for QsoSSL ones + Curl_gskit_ - prefix for GSKit ones Curl_polarssl_ - prefix for PolarSSL ones Curl_cyassl_ - prefix for CyaSSL ones Curl_schannel_ - prefix for Schannel SSPI ones @@ -63,6 +64,7 @@ #include "gtls.h" /* GnuTLS versions */ #include "nssg.h" /* NSS versions */ #include "qssl.h" /* QSOSSL versions */ +#include "gskit.h" /* Global Secure ToolKit versions */ #include "polarssl.h" /* PolarSSL versions */ #include "axtls.h" /* axTLS versions */ #include "cyassl.h" /* CyaSSL versions */ @@ -1901,7 +1901,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, data->set.ssl.fsslctxp = va_arg(param, void *); break; #endif -#if defined(USE_SSLEAY) || defined(USE_QSOSSL) +#if defined(USE_SSLEAY) || defined(USE_QSOSSL) || defined(USE_GSKIT) case CURLOPT_CERTINFO: data->set.ssl.certinfo = (0 != va_arg(param, long))?TRUE:FALSE; break; diff --git a/lib/urldata.h b/lib/urldata.h index 0c07cab70..47841e1f4 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -134,6 +134,10 @@ #include <qsossl.h> #endif +#ifdef USE_GSKIT +#include <gskssl.h> +#endif + #ifdef USE_AXTLS #include <axTLS/ssl.h> #undef malloc @@ -322,6 +326,11 @@ struct ssl_connect_data { #ifdef USE_QSOSSL SSLHandle *handle; #endif /* USE_QSOSSL */ +#ifdef USE_GSKIT + gsk_handle handle; + int iocport; + ssl_connect_state connecting_state; +#endif #ifdef USE_AXTLS SSL_CTX* ssl_ctx; SSL* ssl; diff --git a/lib/x509asn1.c b/lib/x509asn1.c index 060697997..94b89b2be 100644 --- a/lib/x509asn1.c +++ b/lib/x509asn1.c @@ -22,7 +22,7 @@ #include "curl_setup.h" -#if defined(USE_QSOSSL) +#if defined(USE_QSOSSL) || defined(USE_GSKIT) #include <curl/curl.h> #include "urldata.h" @@ -1148,4 +1148,4 @@ CURLcode Curl_verifyhost(struct connectdata * conn, return CURLE_PEER_FAILED_VERIFICATION; } -#endif /* USE_QSOSSL */ +#endif /* USE_QSOSSL or USE_GSKIT */ diff --git a/lib/x509asn1.h b/lib/x509asn1.h index 8eabf8b0b..2276b5b64 100644 --- a/lib/x509asn1.h +++ b/lib/x509asn1.h @@ -25,7 +25,7 @@ #include "curl_setup.h" -#if defined(USE_QSOSSL) +#if defined(USE_QSOSSL) || defined(USE_GSKIT) #include "urldata.h" @@ -125,5 +125,5 @@ CURLcode Curl_extract_certinfo(struct connectdata * conn, int certnum, CURLcode Curl_verifyhost(struct connectdata * conn, const char * beg, const char * end); -#endif /* USE_QSOSSL */ +#endif /* USE_QSOSSL or USE_GSKIT */ #endif /* HEADER_CURL_X509ASN1_H */ |