OS400: new SSL backend GSKit
authorPatrick Monnerat <pm@datasphere.ch>
Mon, 15 Jul 2013 17:00:36 +0000 (19:00 +0200)
committerPatrick Monnerat <pm@datasphere.ch>
Mon, 15 Jul 2013 17:00:36 +0000 (19:00 +0200)
15 files changed:
lib/Makefile.inc
lib/config-os400.h
lib/curl_setup.h
lib/gskit.c [new file with mode: 0644]
lib/gskit.h [new file with mode: 0644]
lib/hostcheck.c
lib/setup-os400.h
lib/sslgen.c
lib/url.c
lib/urldata.h
lib/x509asn1.c
lib/x509asn1.h
packages/OS400/README.OS400
packages/OS400/os400sys.c
packages/OS400/os400sys.h

index 0ba39a2..586d94a 100644 (file)
@@ -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
index 4381f9a..a290fe4 100644 (file)
 /* 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"
 
index a18d085..a46a608 100644 (file)
@@ -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 (file)
index 0000000..5cda85b
--- /dev/null
@@ -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 (file)
index 0000000..0d59aa7
--- /dev/null
@@ -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 */
index ef32d8c..abd1fa0 100644 (file)
@@ -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 */
index cdeefe3..319efec 100644 (file)
@@ -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
index d9b5c47..d2d0e30 100644 (file)
@@ -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 */
index b6e9860..c2bbfb3 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -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;
index 0c07cab..47841e1 100644 (file)
 #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;
index 0606979..94b89b2 100644 (file)
@@ -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 */
index 8eabf8b..2276b5b 100644 (file)
@@ -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 */
index 9890d06..73e81f8 100644 (file)
@@ -39,22 +39,24 @@ header files are thus altered during build process to use this pragma, in
 order to force libcurl enums of being type int (the pragma disposition in use
 before inclusion is restored before resuming the including unit compilation).
 
-  Three SSL implementations were present in libcurl. Nevertheless, none of them
-is available on OS/400. To support SSL on OS/400, a fourth implementation has
-been added (qssl.[ch]). There is no way to have different certificate stores
-for CAs and for personal/application certificates/key. More, the SSL context
-may be defined as an application identifier in the main certificate store,
-or as a keyring file. As a consequence, the meaning of some fields have been
-slightly altered:
-_ The "certificate identifier" is taken from CURLOPT_SSLCERT if defined, else
-from CURLOPT_CAINFO.
-_ The certificate identifier is then used as an application identifier in the
-main certificate store. If successful, this context is used.
-_ If the previous step failed, the certificate identifier is used as the file
-name of a keyring. CURLOPT_KEYPASSWD is used here as the keyring password.
-_ The default ca-bundle (CURLOPT_CAINFO) is set to the main certificate store's
-keyring file name: this allows to use the system global CAs by default. (In that
-case, the keyring password is safely recovered from the system... IBM dixit!)
+  Two SSL implementations are available to libcurl on OS/400: QsoSSL which is
+obsolescent, does not support asynchronous I/O and only allows a single SSL
+context within a job, and GSKit that does not suffer from these limitations
+and is able to provide some information about the server certificate.
+  Both implementations of SSL are working on "certificate stores" or keyrings,
+rather than individual certificate/key files. Certificate stores, as weel as
+"certificate labels" are managed by external IBM-defined applications.
+  There are two ways to specify an SSL context:
+- By an application identifier.
+- By a keyring file pathname and (optionally) certificate label.
+  To identify an SSL context by application identifier, use option
+SETOPT_SSLCERT to specify the application identifier.
+  To address an SSL context by keyring and certificate label, use CURLOPT_CAINFO
+to set-up the keyring pathname, CURLOPT_SSLCERT to define the certificate label
+(omitting it will cause the default certificate in keyring to be used) and
+CURLOPT_KEYPASSWD to give the keyring password. If SSL is used without
+defining any of these options, the default (i.e.: system) keyring is used for
+server certificate validation.
 
   Non-standard EBCDIC wrapper prototypes are defined in an additional header
 file: ccsidcurl.h. These should be self-explanatory to an OS/400-aware
@@ -154,6 +156,14 @@ use:
         CURLINFO_PRIMARY_IP
         CURLINFO_RTSP_SESSION_ID
         CURLINFO_LOCAL_IP
+  Likewise, the following options are followed by a struct curl_slist * * and a
+CCSID.
+        CURLINFO_SSL_ENGINES
+        CURLINFO_COOKIELIST
+Lists returned should be released with curl_slist_free_all() after use.
+  Option CURLINFO_CERTINFO is followed by a struct curl_certinfo * * and a
+CCSID. Returned structures sould be free'ed using curl_certinfo_free_all() after
+use.
   Other options are processed like in curl_easy_getinfo().
 
   Standard compilation environment does support neither autotools nor make;
@@ -200,6 +210,8 @@ _ As a prerequisite, QADRT development environment must be installed.
 _ Install the curl source directory in IFS.
 _ Enter shell (QSH)
 _ Change current directory to the curl installation directory
+- If the SSL backend has to be changed, edit file lib/config-os400.h
+  accordingly.
 _ Change current directory to ./packages/OS400
 _ Edit file iniscript.sh. You may want to change tunable configuration
   parameters, like debug info generation, optimisation level, listing option,
index 1dd3a79..ab0c4fb 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2011, 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
 #include <qsossl.h>
 #endif
 
+#ifdef USE_GSKIT
+#include <gskssl.h>
+#include <qsoasync.h>
+#endif
+
 #ifdef HAVE_GSSAPI
 #include <gssapi.h>
 #endif
@@ -451,6 +456,412 @@ Curl_SSL_Strerror_a(int sslreturnvalue, SSLErrorMsg * serrmsgp)
 #endif /* USE_QSOSSL */
 
 
+#ifdef USE_GSKIT
+
+/* ASCII wrappers for the GSKit procedures. */
+
+/*
+ * EBCDIC --> ASCII string mapping table.
+ * Some strings returned by GSKit are dynamically allocated and automatically
+ * released when closing the handle.
+ * To provide the same functionality, we use a "private" handle that
+ * holds the GSKit handle and a list of string mappings. This will allow
+ * avoid conversion of already converted strings and releasing them upon
+ * close time.
+ */
+
+struct gskstrlist {
+  struct gskstrlist * next;
+  const char * ebcdicstr;
+  const char * asciistr;
+};
+
+struct Curl_gsk_descriptor {
+  gsk_handle h;
+  struct gskstrlist * strlist;
+};
+
+
+int
+Curl_gsk_environment_open(gsk_handle * my_env_handle)
+
+{
+  struct Curl_gsk_descriptor * p;
+  gsk_handle h;
+  int rc;
+
+  if(!my_env_handle)
+    return GSK_OS400_ERROR_INVALID_POINTER;
+  if(!(p = (struct Curl_gsk_descriptor *) malloc(sizeof *p)))
+    return GSK_INSUFFICIENT_STORAGE;
+  p->strlist = (struct gskstrlist *) NULL;
+  if((rc = gsk_environment_open(&p->h)) != GSK_OK)
+    free(p);
+  else
+    *my_env_handle = (gsk_handle) p;
+  return rc;
+}
+
+
+int
+Curl_gsk_secure_soc_open(gsk_handle my_env_handle,
+                         gsk_handle * my_session_handle)
+
+{
+  struct Curl_gsk_descriptor * p;
+  gsk_handle h;
+  int rc;
+
+  if(!my_env_handle)
+    return GSK_INVALID_HANDLE;
+  if(!my_session_handle)
+    return GSK_OS400_ERROR_INVALID_POINTER;
+  h = ((struct Curl_gsk_descriptor *) my_env_handle)->h;
+  if(!(p = (struct Curl_gsk_descriptor *) malloc(sizeof *p)))
+    return GSK_INSUFFICIENT_STORAGE;
+  p->strlist = (struct gskstrlist *) NULL;
+  if((rc = gsk_secure_soc_open(h, &p->h)) != GSK_OK)
+    free(p);
+  else
+    *my_session_handle = (gsk_handle) p;
+  return rc;
+}
+
+
+static void
+gsk_free_handle(struct Curl_gsk_descriptor * p)
+
+{
+  struct gskstrlist * q;
+
+  while ((q = p->strlist)) {
+    p->strlist = q;
+    free((void *) q->asciistr);
+    free(q);
+  }
+  free(p);
+}
+
+
+int
+Curl_gsk_environment_close(gsk_handle * my_env_handle)
+
+{
+  struct Curl_gsk_descriptor * p;
+  int rc;
+
+  if(!my_env_handle)
+    return GSK_OS400_ERROR_INVALID_POINTER;
+  if(!*my_env_handle)
+    return GSK_INVALID_HANDLE;
+  p = (struct Curl_gsk_descriptor *) *my_env_handle;
+  if ((rc = gsk_environment_close(&p->h)) == GSK_OK) {
+    gsk_free_handle(p);
+    *my_env_handle = (gsk_handle) NULL;
+  }
+  return rc;
+}
+
+
+int
+Curl_gsk_secure_soc_close(gsk_handle * my_session_handle)
+
+{
+  struct Curl_gsk_descriptor * p;
+  int rc;
+
+  if(!my_session_handle)
+    return GSK_OS400_ERROR_INVALID_POINTER;
+  if(!*my_session_handle)
+    return GSK_INVALID_HANDLE;
+  p = (struct Curl_gsk_descriptor *) *my_session_handle;
+  if ((rc = gsk_secure_soc_close(&p->h)) == GSK_OK) {
+    gsk_free_handle(p);
+    *my_session_handle = (gsk_handle) NULL;
+  }
+  return rc;
+}
+
+
+int
+Curl_gsk_environment_init(gsk_handle my_env_handle)
+
+{
+  struct Curl_gsk_descriptor * p;
+
+  if(!my_env_handle)
+    return GSK_INVALID_HANDLE;
+  p = (struct Curl_gsk_descriptor *) my_env_handle;
+  return gsk_environment_init(p->h);
+}
+
+
+int
+Curl_gsk_secure_soc_init(gsk_handle my_session_handle)
+
+{
+  struct Curl_gsk_descriptor * p;
+
+  if(!my_session_handle)
+    return GSK_INVALID_HANDLE;
+  p = (struct Curl_gsk_descriptor *) my_session_handle;
+  return gsk_secure_soc_init(p->h);
+}
+
+
+int
+Curl_gsk_attribute_set_buffer_a(gsk_handle my_gsk_handle, GSK_BUF_ID bufID,
+                                const char * buffer, int bufSize)
+
+{
+  struct Curl_gsk_descriptor * p;
+  char * ebcdicbuf;
+  int rc;
+
+  if(!my_gsk_handle)
+    return GSK_INVALID_HANDLE;
+  if(!buffer)
+    return GSK_OS400_ERROR_INVALID_POINTER;
+  if(bufSize < 0)
+    return GSK_ATTRIBUTE_INVALID_LENGTH;
+  p = (struct Curl_gsk_descriptor *) my_gsk_handle;
+  if(!bufSize)
+    bufSize = strlen(buffer);
+  if (!(ebcdicbuf = malloc(bufSize + 1)))
+      return GSK_INSUFFICIENT_STORAGE;
+  QadrtConvertA2E(ebcdicbuf, buffer, bufSize, bufSize);
+  ebcdicbuf[bufSize] = '\0';
+  rc = gsk_attribute_set_buffer(p->h, bufID, ebcdicbuf, bufSize);
+  free(ebcdicbuf);
+  return rc;
+}
+
+
+int
+Curl_gsk_attribute_set_enum(gsk_handle my_gsk_handle, GSK_ENUM_ID enumID,
+                            GSK_ENUM_VALUE enumValue)
+
+{
+  struct Curl_gsk_descriptor * p;
+
+  if(!my_gsk_handle)
+    return GSK_INVALID_HANDLE;
+  p = (struct Curl_gsk_descriptor *) my_gsk_handle;
+  return gsk_attribute_set_enum(p->h, enumID, enumValue);
+}
+
+
+int
+Curl_gsk_attribute_set_numeric_value(gsk_handle my_gsk_handle,
+                                     GSK_NUM_ID numID, int numValue)
+
+{
+  struct Curl_gsk_descriptor * p;
+
+  if(!my_gsk_handle)
+    return GSK_INVALID_HANDLE;
+  p = (struct Curl_gsk_descriptor *) my_gsk_handle;
+  return gsk_attribute_set_numeric_value(p->h, numID, numValue);
+}
+
+
+int
+Curl_gsk_attribute_set_callback(gsk_handle my_gsk_handle,
+                                GSK_CALLBACK_ID callBackID,
+                                void * callBackAreaPtr)
+
+{
+  struct Curl_gsk_descriptor * p;
+
+  if(!my_gsk_handle)
+    return GSK_INVALID_HANDLE;
+  p = (struct Curl_gsk_descriptor *) my_gsk_handle;
+  return gsk_attribute_set_callback(p->h, callBackID, callBackAreaPtr);
+}
+
+
+static int
+cachestring(struct Curl_gsk_descriptor * p,
+            const char * ebcdicbuf, int bufsize, const char * * buffer)
+
+{
+  int rc;
+  char * asciibuf;
+  struct gskstrlist * sp;
+
+  for (sp = p->strlist; sp; sp = sp->next)
+    if(sp->ebcdicstr == ebcdicbuf)
+      break;
+  if(!sp) {
+    if(!(sp = (struct gskstrlist *) malloc(sizeof *sp)))
+      return GSK_INSUFFICIENT_STORAGE;
+    if(!(asciibuf = malloc(bufsize + 1))) {
+      free(sp);
+      return GSK_INSUFFICIENT_STORAGE;
+    }
+    QadrtConvertE2A(asciibuf, ebcdicbuf, bufsize, bufsize);
+    asciibuf[bufsize] = '\0';
+    sp->ebcdicstr = ebcdicbuf;
+    sp->asciistr = asciibuf;
+    sp->next = p->strlist;
+    p->strlist = sp;
+  }
+  *buffer = sp->asciistr;
+  return GSK_OK;
+}
+
+
+int
+Curl_gsk_attribute_get_buffer_a(gsk_handle my_gsk_handle, GSK_BUF_ID bufID,
+                                const char * * buffer, int * bufSize)
+
+{
+  struct Curl_gsk_descriptor * p;
+  int rc;
+  const char * mybuf;
+  int mylen;
+
+  if(!my_gsk_handle)
+    return GSK_INVALID_HANDLE;
+  if(!buffer || !bufSize)
+    return GSK_OS400_ERROR_INVALID_POINTER;
+  p = (struct Curl_gsk_descriptor *) my_gsk_handle;
+  if ((rc = gsk_attribute_get_buffer(p->h, bufID, &mybuf, &mylen)) != GSK_OK)
+    return rc;
+  if((rc = cachestring(p, mybuf, mylen, buffer)) == GSK_OK)
+    *bufSize = mylen;
+  return rc;
+}
+
+
+int
+Curl_gsk_attribute_get_enum(gsk_handle my_gsk_handle, GSK_ENUM_ID enumID,
+                            GSK_ENUM_VALUE * enumValue)
+
+{
+  struct Curl_gsk_descriptor * p;
+
+  if(!my_gsk_handle)
+    return GSK_INVALID_HANDLE;
+  p = (struct Curl_gsk_descriptor *) my_gsk_handle;
+  return gsk_attribute_get_enum(p->h, enumID, enumValue);
+}
+
+
+int
+Curl_gsk_attribute_get_numeric_value(gsk_handle my_gsk_handle,
+                                     GSK_NUM_ID numID, int * numValue)
+
+{
+  struct Curl_gsk_descriptor * p;
+
+  if(!my_gsk_handle)
+    return GSK_INVALID_HANDLE;
+  p = (struct Curl_gsk_descriptor *) my_gsk_handle;
+  return gsk_attribute_get_numeric_value(p->h, numID, numValue);
+}
+
+
+int
+Curl_gsk_attribute_get_cert_info(gsk_handle my_gsk_handle,
+                                 GSK_CERT_ID certID,
+                                 const gsk_cert_data_elem * * certDataElem,
+                                 int * certDataElementCount)
+
+{
+  struct Curl_gsk_descriptor * p;
+
+  if(!my_gsk_handle)
+    return GSK_INVALID_HANDLE;
+  p = (struct Curl_gsk_descriptor *) my_gsk_handle;
+  /* No need to convert code: text results are already in ASCII. */
+  return gsk_attribute_get_cert_info(p->h, certID,
+                                     certDataElem, certDataElementCount);
+}
+
+
+int
+Curl_gsk_secure_soc_misc(gsk_handle my_session_handle, GSK_MISC_ID miscID)
+
+{
+  struct Curl_gsk_descriptor * p;
+
+  if(!my_session_handle)
+    return GSK_INVALID_HANDLE;
+  p = (struct Curl_gsk_descriptor *) my_session_handle;
+  return gsk_secure_soc_misc(p->h, miscID);
+}
+
+
+int
+Curl_gsk_secure_soc_read(gsk_handle my_session_handle, char * readBuffer,
+                         int readBufSize, int * amtRead)
+
+{
+  struct Curl_gsk_descriptor * p;
+
+  if(!my_session_handle)
+    return GSK_INVALID_HANDLE;
+  p = (struct Curl_gsk_descriptor *) my_session_handle;
+  return gsk_secure_soc_read(p->h, readBuffer, readBufSize, amtRead);
+}
+
+
+int
+Curl_gsk_secure_soc_write(gsk_handle my_session_handle, char * writeBuffer,
+                          int writeBufSize, int * amtWritten)
+
+{
+  struct Curl_gsk_descriptor * p;
+
+  if(!my_session_handle)
+    return GSK_INVALID_HANDLE;
+  p = (struct Curl_gsk_descriptor *) my_session_handle;
+  return gsk_secure_soc_write(p->h, writeBuffer, writeBufSize, amtWritten);
+}
+
+
+const char *
+Curl_gsk_strerror_a(int gsk_return_value)
+
+{
+  int i;
+  const char * cp;
+  char * cp2;
+
+  cp = gsk_strerror(gsk_return_value);
+
+  if (!cp)
+    return cp;
+
+  i = strlen(cp);
+
+  if (!(cp2 = Curl_thread_buffer(LK_GSK_ERROR, MAX_CONV_EXPANSION * i + 1)))
+    return cp2;
+
+  i = QadrtConvertE2A(cp2, cp, MAX_CONV_EXPANSION * i, i);
+  cp2[i] = '\0';
+  return cp2;
+}
+
+int
+Curl_gsk_secure_soc_startInit(gsk_handle my_session_handle,
+                              int IOCompletionPort,
+                              Qso_OverlappedIO_t * communicationsArea)
+
+{
+  struct Curl_gsk_descriptor * p;
+
+  if(!my_session_handle)
+    return GSK_INVALID_HANDLE;
+  p = (struct Curl_gsk_descriptor *) my_session_handle;
+  return gsk_secure_soc_startInit(p->h, IOCompletionPort, communicationsArea);
+}
+
+#endif /* USE_GSKIT */
+
+
+
 #ifdef HAVE_GSSAPI
 
 /* ASCII wrappers for the GSSAPI procedures. */
index e708365..234bf5e 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2008, 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
@@ -31,6 +31,7 @@
 
 typedef enum {
         LK_SSL_ERROR,
+        LK_GSK_ERROR,
         LK_LDAP_ERROR,
         LK_CURL_VERSION,
         LK_VERSION_INFO,