Commit b3461bab authored by Daniel Stenberg's avatar Daniel Stenberg
Browse files

Implemented the parts of Patrick Monnerat's OS/400 patch that introduces

support for the OS/400 Secure Sockets Layer library
parent 5ecd56d9
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -6,6 +6,10 @@

                                  Changelog

Daniel S (23 July 2007)
- Implemented the parts of Patrick Monnerat's OS/400 patch that introduces
  support for the OS/400 Secure Sockets Layer library.

Dan F (23 July 2007)
- Implemented only the parts of Patrick Monnerat's OS/400 patch that renamed
  some few internal identifiers to avoid conflicts, which could be useful on
+1 −1
Original line number Diff line number Diff line
@@ -11,7 +11,7 @@ Curl and libcurl 7.16.5

This release includes the following changes:
 
 o 
 o support for OS/400 Secure Sockets Layer library

This release includes the following bugfixes:

+14 −14
Original line number Diff line number Diff line
@@ -4,20 +4,20 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
  cookie.c http.c sendf.c ftp.c url.c dict.c if2ip.c speedcheck.c	\
  ldap.c ssluse.c version.c getenv.c escape.c mprintf.c telnet.c	\
  netrc.c getinfo.c transfer.c strequal.c easy.c security.c krb4.c	\
  krb5.c \
  memdebug.c http_chunks.c strtok.c connect.c llist.c hash.c multi.c	\
  content_encoding.c share.c http_digest.c md5.c http_negotiate.c	\
  http_ntlm.c inet_pton.c strtoofft.c strerror.c hostares.c hostasyn.c	\
  hostip4.c hostip6.c hostsyn.c hostthre.c inet_ntop.c parsedate.c	\
  select.c gtls.c sslgen.c tftp.c splay.c strdup.c socks.c ssh.c nss.c
  krb5.c memdebug.c http_chunks.c strtok.c connect.c llist.c hash.c	\
  multi.c content_encoding.c share.c http_digest.c md5.c		\
  http_negotiate.c http_ntlm.c inet_pton.c strtoofft.c strerror.c	\
  hostares.c hostasyn.c hostip4.c hostip6.c hostsyn.c hostthre.c	\
  inet_ntop.c parsedate.c select.c gtls.c sslgen.c tftp.c splay.c	\
  strdup.c socks.c ssh.c nss.c qssl.c

HHEADERS = arpa_telnet.h netrc.h file.h timeval.h base64.h hostip.h	\
  progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h	\
  if2ip.h speedcheck.h urldata.h ldap.h ssluse.h escape.h telnet.h	\
  getinfo.h strequal.h krb4.h memdebug.h inet_ntoa_r.h \
  http_chunks.h strtok.h connect.h llist.h hash.h content_encoding.h	\
  share.h md5.h http_digest.h http_negotiate.h http_ntlm.h ca-bundle.h	\
  inet_pton.h strtoofft.h strerror.h inet_ntop.h curlx.h memory.h	\
  setup.h transfer.h select.h easyif.h multiif.h parsedate.h sslgen.h   \
  gtls.h tftp.h sockaddr.h splay.h strdup.h setup_once.h socks.h ssh.h  \
  nssg.h
  getinfo.h strequal.h krb4.h memdebug.h inet_ntoa_r.h http_chunks.h	\
  strtok.h connect.h llist.h hash.h content_encoding.h share.h md5.h	\
  http_digest.h http_negotiate.h http_ntlm.h ca-bundle.h inet_pton.h	\
  strtoofft.h strerror.h inet_ntop.h curlx.h memory.h setup.h		\
  transfer.h select.h easyif.h multiif.h parsedate.h sslgen.h gtls.h	\
  tftp.h sockaddr.h splay.h strdup.h setup_once.h socks.h ssh.h nssg.h	\
  qssl.h

lib/qssl.c

0 → 100644
+496 −0
Original line number Diff line number Diff line
/***************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2007, 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.
 *
 * $Id$
 ***************************************************************************/

#include "setup.h"

#ifdef USE_QSOSSL
#include <qsossl.h>
#include <errno.h>
#include <string.h>

#include <curl/curl.h>
#include "urldata.h"
#include "sendf.h"
#include "qssl.h"
#include "sslgen.h"
#include "connect.h" /* for the connect timeout */
#include "select.h"
#include "memory.h"
/* The last #include file should be: */
#include "memdebug.h"


static bool qsossl_inited = FALSE;


int Curl_qsossl_init(void)

{
  /* Nothing to do here. We must have connection data to initialize ssl, so
   * defer.
   */

  return 1;
}


void Curl_qsossl_cleanup(void)

{
  /* Nothing to do. */
}


static CURLcode Curl_qsossl_init_session(struct SessionHandle * data)

{
  int rc;
  char * certname;
  SSLInit initstr;
  SSLInitApp initappstr;

  /* Initialize the job for SSL according to the current parameters.
   * QsoSSL offers two ways to do it: SSL_Init_Application() that uses an
   *  application identifier to select certificates in the main certificate
   *  store, and SSL_Init() that uses named keyring files and a password.
   * It is not possible to have different keyrings for the CAs and the
   *  local certificate. We thus use the certificate name to identify the
   *  keyring if given, else the CA file name.
   * If the key file name is given, it is taken as the password for the
   *  keyring in certificate file.
   * We first try to SSL_Init_Application(), then SSL_Init() if it failed.
   */

  certname = data->set.cert;

  if (!certname) {
    certname = data->set.ssl.CAfile;

    if (!certname)
      return CURLE_OK;          /* Use previous setup. */
    }

  memset((char *) &initappstr, 0, sizeof initappstr);
  initappstr.applicationID = certname;
  initappstr.applicationIDLen = strlen(certname);
  initappstr.protocol = SSL_VERSION_CURRENT;
  initappstr.sessionType = SSL_REGISTERED_AS_CLIENT;
  rc = SSL_Init_Application(&initappstr);

  if (rc == SSL_ERROR_NOT_REGISTERED) {
    initstr.keyringFileName = certname;
    initstr.keyringPassword = data->set.key;
    initstr.cipherSuiteList = NULL;    /* Use default. */
    initstr.cipherSuiteListLen = 0;
    rc = SSL_Init(&initstr);
    }

  switch (rc) {

  case 0:                             /* No error. */
    break;

  case SSL_ERROR_IO:
    failf(data, "SSL_Init() I/O error: %s\n", strerror(errno));
    return CURLE_SSL_CONNECT_ERROR;

  case SSL_ERROR_BAD_CIPHER_SUITE:
    return CURLE_SSL_CIPHER;

  case SSL_ERROR_KEYPASSWORD_EXPIRED:
  case SSL_ERROR_NOT_REGISTERED:
    return CURLE_SSL_CONNECT_ERROR;

  case SSL_ERROR_NO_KEYRING:
    return CURLE_SSL_CACERT;

  case SSL_ERROR_CERT_EXPIRED:
    return CURLE_SSL_CERTPROBLEM;

  default:
    failf(data, "SSL_Init(): %s\n", SSL_Strerror(rc, NULL));
    return CURLE_SSL_CONNECT_ERROR;
  }

  return CURLE_OK;
}


static CURLcode Curl_qsossl_create(struct connectdata * conn, int sockindex)

{
  SSLHandle * h;
  struct ssl_connect_data * connssl = &conn->ssl[sockindex];

  h = SSL_Create(conn->sock[sockindex], SSL_ENCRYPT);

  if (!h) {
    failf(conn->data, "SSL_Create() I/O error: %s\n", strerror(errno));
    return CURLE_SSL_CONNECT_ERROR;
    }

  connssl->handle = h;
  return CURLE_OK;
}


static int Curl_qsossl_trap_cert(SSLHandle * h)

{
	return 1;       /* Accept certificate. */
}


static CURLcode Curl_qsossl_handshake(struct connectdata * conn, int sockindex)

{
  int rc;
  struct SessionHandle * data = conn->data;
  struct ssl_connect_data * connssl = &conn->ssl[sockindex];
  SSLHandle * h = connssl->handle;
  long timeout_ms;

  h->exitPgm = NULL;

  if (!data->set.ssl.verifyhost)
    h->exitPgm = Curl_qsossl_trap_cert;

  if (data->set.connecttimeout) {
    timeout_ms = data->set.connecttimeout;

    if (data->set.timeout)
      if (timeout_ms > data->set.timeout)
        timeout_ms = data->set.timeout;
    }
  else if (data->set.timeout)
    timeout_ms = data->set.timeout;
  else
    timeout_ms = DEFAULT_CONNECT_TIMEOUT;

  /* SSL_Handshake() timeout resolution is second, so round up. */

  h->timeout = (timeout_ms + 1000 - 1) / 1000;

  /* Set-up protocol. */

  switch(data->set.ssl.version) {

  default:
  case CURL_SSLVERSION_DEFAULT:
    h->protocol = SSL_VERSION_CURRENT;
    break;

  case CURL_SSLVERSION_TLSv1:
    h->protocol = TLS_VERSION_1;
    break;

  case CURL_SSLVERSION_SSLv2:
    h->protocol = SSL_VERSION_2;
    break;

  case CURL_SSLVERSION_SSLv3:
    h->protocol = SSL_VERSION_3;
    break;
  }

  rc = SSL_Handshake(h, SSL_HANDSHAKE_AS_CLIENT);

  switch (rc) {

  case 0:                             /* No error. */
    break;

  case SSL_ERROR_BAD_CERTIFICATE:
  case SSL_ERROR_BAD_CERT_SIG:
  case SSL_ERROR_NOT_TRUSTED_ROOT:
    return CURLE_SSL_PEER_CERTIFICATE;

  case SSL_ERROR_BAD_CIPHER_SUITE:
  case SSL_ERROR_NO_CIPHERS:
    return CURLE_SSL_CIPHER;

  case SSL_ERROR_CERTIFICATE_REJECTED:
  case SSL_ERROR_CERT_EXPIRED:
  case SSL_ERROR_NO_CERTIFICATE:
    return CURLE_SSL_CERTPROBLEM;

  case SSL_ERROR_IO:
    failf(data, "SSL_Handshake(): %s\n", SSL_Strerror(rc, NULL));
    return CURLE_SSL_CONNECT_ERROR;

  default:
    failf(data, "SSL_Init(): %s\n", SSL_Strerror(rc, NULL));
    return CURLE_SSL_CONNECT_ERROR;
  }

  return CURLE_OK;
}


CURLcode Curl_qsossl_connect(struct connectdata * conn, int sockindex)

{
  struct SessionHandle * data = conn->data;
  struct ssl_connect_data * connssl = &conn->ssl[sockindex];
  int rc;

  rc = Curl_qsossl_init_session(data);

  if (rc == CURLE_OK) {
    rc = Curl_qsossl_create(conn, sockindex);

    if (rc == CURLE_OK)
      rc = Curl_qsossl_handshake(conn, sockindex);
    else {
      SSL_Destroy(connssl->handle);
      connssl->handle = NULL;
      connssl->use = FALSE;
    }
  }

  return rc;
}


static int Curl_qsossl_close_one(struct ssl_connect_data * conn,
                                 struct SessionHandle * data)

{
  int rc;

  if (!conn->handle)
    return 0;

  rc = SSL_Destroy(conn->handle);

  if(rc) {
    if (rc == SSL_ERROR_IO) {
      failf(data, "SSL_Destroy() I/O error: %s\n", strerror(errno));
      return -1;
    }

    /* An SSL error. */
    failf(data, "SSL_Destroy() returned error %d\n", SSL_Strerror(rc, NULL));
    return -1;
  }

  conn->use = FALSE;    /* get back to ordinary socket usage */
  conn->handle = NULL;
  return 0;
}


void Curl_qsossl_close(struct connectdata * conn)

{
  struct SessionHandle * data = conn->data;
  struct ssl_connect_data * connssl = conn->ssl;

  if(connssl->use)
    (void) Curl_qsossl_close_one(connssl, data);

  connssl++;

  if(connssl->use)
    (void) Curl_qsossl_close_one(connssl, data);
}


int Curl_qsossl_close_all(struct SessionHandle * data)

{
  /* Unimplemented. */
  (void) data;
  return 0;
}


int Curl_qsossl_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;

  if (Curl_qsossl_close_one(connssl, data))
    return -1;

  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 SSL_Read now, so use read(). */

    nread = read(conn->sock[sockindex], buf, sizeof(buf));

    if (nread < 0) {
      failf(data, "read: %s\n", strerror(errno));
      rc = -1;
    }

    if (nread <= 0)
      break;

    what = Curl_socket_ready(conn->sock[sockindex], CURL_SOCKET_BAD, 0);
  }

  return rc;
}


ssize_t Curl_qsossl_send(struct connectdata * conn, int sockindex, void * mem,
                         size_t len)

{
  /* SSL_Write() is said to return 'int' while write() and send() returns
     'size_t' */
  int rc;

  rc = SSL_Write(conn->ssl[sockindex].handle, mem, (int) len);

  if(rc < 0) {
    switch(rc) {

    case SSL_ERROR_BAD_STATE:
      /* The operation did not complete; the same SSL I/O function
         should be called again later. This is basicly an EWOULDBLOCK
         equivalent. */
      return 0;

    case SSL_ERROR_IO:
      switch (errno) {
      case EWOULDBLOCK:
      case EINTR:
        return 0;
        }

      failf(conn->data, "SSL_Write() I/O error: %s\n", strerror(errno));
      return -1;
    }

    /* An SSL error. */
    failf(conn->data, "SSL_Write() returned error %d\n",
          SSL_Strerror(rc, NULL));
    return -1;
  }

  return (ssize_t) rc; /* number of bytes */
}


ssize_t Curl_qsossl_recv(struct connectdata * conn, int num, char * buf,
                         size_t buffersize, bool * wouldblock)

{
  char error_buffer[120]; /* OpenSSL documents that this must be at
                             least 120 bytes long. */
  unsigned long sslerror;
  int nread;

  nread = SSL_Read(conn->ssl[num].handle, buf, (int) buffersize);
  *wouldblock = FALSE;

  if(nread < 0) {
    /* failed SSL_read */

    switch (nread) {

    case SSL_ERROR_BAD_STATE:
      /* there's data pending, re-invoke SSL_Read(). */
      *wouldblock = TRUE;
      return -1; /* basically EWOULDBLOCK */

    case SSL_ERROR_IO:
      switch (errno) {
      case EWOULDBLOCK:
	*wouldblock = TRUE;
        return -1;
        }

      failf(conn->data, "SSL_Read() I/O error: %s\n", strerror(errno));
      return -1;

    default:
      failf(conn->data, "SSL read error: %s\n", SSL_Strerror(nread, NULL));
      return -1;
    }
  }
  return (ssize_t) nread;
}


size_t Curl_qsossl_version(char * buffer, size_t size)

{
  strncpy(buffer, "IBM OS/400 SSL", size);
  return strlen(buffer);
}


int Curl_qsossl_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_QSOSSL */

lib/qssl.h

0 → 100644
+52 −0
Original line number Diff line number Diff line
#ifndef __QSSL_H
#define __QSSL_H
/***************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2007, 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.
 *
 * $Id$
 ***************************************************************************/

/*
 * This header should only be needed to get included by sslgen.c and qssl.c
 */

#include "urldata.h"

int Curl_qsossl_init(void);
void Curl_qsossl_cleanup(void);
CURLcode Curl_qsossl_connect(struct connectdata * conn, int sockindex);
void Curl_qsossl_close(struct connectdata * conn); /* close a SSL connection */
int Curl_qsossl_close_all(struct SessionHandle * data);
int Curl_qsossl_shutdown(struct connectdata * conn, int sockindex);

ssize_t Curl_qsossl_send(struct connectdata * conn,
                         int sockindex,
                         void * mem,
                         size_t len);
ssize_t Curl_qsossl_recv(struct connectdata * conn, /* connection data */
                         int num,                   /* socketindex */
                         char * buf,                /* store read data here */
                         size_t buffersize,         /* max amount to read */
                         bool * wouldblock);

size_t Curl_qsossl_version(char * buffer, size_t size);
int Curl_qsossl_check_cxn(struct connectdata * cxn);

#endif
Loading