123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501 |
- /***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2010, 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 "setup.h"
- #ifdef USE_QSOSSL
- #include <qsossl.h>
- #include <errno.h>
- #include <string.h>
- #ifdef HAVE_LIMITS_H
- # include <limits.h>
- #endif
- #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 "curl_memory.h"
- /* The last #include file should be: */
- #include "memdebug.h"
- 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.str[STRING_CERT];
- if(!certname) {
- certname = data->set.str[STRING_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; /* TLSV1 compat. SSLV[23]. */
- initappstr.sessionType = SSL_REGISTERED_AS_CLIENT;
- rc = SSL_Init_Application(&initappstr);
- if(rc == SSL_ERROR_NOT_REGISTERED) {
- initstr.keyringFileName = certname;
- initstr.keyringPassword = data->set.str[STRING_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", 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", 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", 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;
- /* figure out how long time we should wait at maximum */
- timeout_ms = Curl_timeleft(conn, NULL, TRUE);
- if(timeout_ms < 0) {
- /* time-out, bail out, go home */
- failf(data, "Connection time-out");
- return CURLE_OPERATION_TIMEDOUT;
- }
- /* 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; /* TLSV1 compat. SSLV[23]. */
- 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_PEER_FAILED_VERIFICATION;
- 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() I/O error: %s", strerror(errno));
- return CURLE_SSL_CONNECT_ERROR;
- default:
- failf(data, "SSL_Handshake(): %s", SSL_Strerror(rc, NULL));
- return CURLE_SSL_CONNECT_ERROR;
- }
- return CURLE_OK;
- }
- static Curl_recv qsossl_recv;
- static Curl_send qsossl_send;
- 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;
- connssl->state = ssl_connection_none;
- }
- }
- if (rc == CURLE_OK) {
- connssl->state = ssl_connection_complete;
- conn->recv[sockindex] = qsossl_recv;
- conn->send[sockindex] = qsossl_send;
- }
- 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", strerror(errno));
- return -1;
- }
- /* An SSL error. */
- failf(data, "SSL_Destroy() returned error %s", SSL_Strerror(rc, NULL));
- return -1;
- }
- conn->handle = NULL;
- return 0;
- }
- void Curl_qsossl_close(struct connectdata *conn, int sockindex)
- {
- struct SessionHandle *data = conn->data;
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- 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", strerror(errno));
- rc = -1;
- }
- if(nread <= 0)
- break;
- what = Curl_socket_ready(conn->sock[sockindex], CURL_SOCKET_BAD, 0);
- }
- return rc;
- }
- static ssize_t qsossl_send(struct connectdata * conn, int sockindex,
- const void * mem, size_t len, CURLcode * curlcode)
- {
- /* SSL_Write() is said to return 'int' while write() and send() returns
- 'size_t' */
- int rc;
- rc = SSL_Write(conn->ssl[sockindex].handle, (void *) 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. */
- *curlcode = CURLE_AGAIN;
- return -1;
- case SSL_ERROR_IO:
- switch (errno) {
- case EWOULDBLOCK:
- case EINTR:
- *curlcode = CURLE_AGAIN;
- return -1;
- }
- failf(conn->data, "SSL_Write() I/O error: %s", strerror(errno));
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
- /* An SSL error. */
- failf(conn->data, "SSL_Write() returned error %s",
- SSL_Strerror(rc, NULL));
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
- return (ssize_t) rc; /* number of bytes */
- }
- static ssize_t qsossl_recv(struct connectdata * conn, int num, char * buf,
- size_t buffersize, CURLcode * curlcode)
- {
- char error_buffer[120]; /* OpenSSL documents that this must be at
- least 120 bytes long. */
- unsigned long sslerror;
- int buffsize;
- int nread;
- buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
- nread = SSL_Read(conn->ssl[num].handle, buf, buffsize);
- if(nread < 0) {
- /* failed SSL_read */
- switch (nread) {
- case SSL_ERROR_BAD_STATE:
- /* there's data pending, re-invoke SSL_Read(). */
- *curlcode = CURLE_AGAIN;
- return -1;
- case SSL_ERROR_IO:
- switch (errno) {
- case EWOULDBLOCK:
- *curlcode = CURLE_AGAIN;
- return -1;
- }
- failf(conn->data, "SSL_Read() I/O error: %s", strerror(errno));
- *curlcode = CURLE_RECV_ERROR;
- return -1;
- default:
- failf(conn->data, "SSL read error: %s", SSL_Strerror(nread, NULL));
- *curlcode = CURLE_RECV_ERROR;
- 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 */
|