/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2009, Daniel Stenberg, , 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: qssl.c,v 1.17 2009-04-21 11:46:17 yangtse Exp $ ***************************************************************************/ #include "setup.h" #ifdef USE_QSOSSL #include #include #include #ifdef HAVE_LIMITS_H # include #endif #include #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; } 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; 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 %d", 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; } ssize_t Curl_qsossl_send(struct connectdata * conn, int sockindex, const 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, (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. */ return 0; case SSL_ERROR_IO: switch (errno) { case EWOULDBLOCK: case EINTR: return 0; } failf(conn->data, "SSL_Write() I/O error: %s", strerror(errno)); return -1; } /* An SSL error. */ failf(conn->data, "SSL_Write() returned error %d", 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 buffsize; int nread; buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; nread = SSL_Read(conn->ssl[num].handle, buf, buffsize); *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", strerror(errno)); return -1; default: failf(conn->data, "SSL read error: %s", 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 */