X-Git-Url: http://sjero.net/git/?a=blobdiff_plain;f=src%2Fgen_sslfunc.c;h=98e75a1dba6258d77b840a1b05b093aae79b3010;hb=d9fea91a0a319e348adb504bd3edff148ff3d8a0;hp=1f97edcc38b315c083bb8fde959b7e450dee302f;hpb=75699d62131af1f72895bfa7c8858ccaf1cb100c;p=wget diff --git a/src/gen_sslfunc.c b/src/gen_sslfunc.c index 1f97edcc..98e75a1d 100644 --- a/src/gen_sslfunc.c +++ b/src/gen_sslfunc.c @@ -16,12 +16,20 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Wget; if not, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +In addition, as a special exception, the Free Software Foundation +gives permission to link the code of its release of Wget with the +OpenSSL project's "OpenSSL" library (or with modified versions of it +that use the same license as the "OpenSSL" library), and distribute +the linked executables. You must obey the GNU General Public License +in all respects for all of the code used other than "OpenSSL". If you +modify this file, you may extend this exception to your version of the +file, but you are not obligated to do so. If you do not wish to do +so, delete this exception statement from your version. */ #include -#ifdef HAVE_SSL - #include #include #ifdef HAVE_UNISTD_H @@ -50,9 +58,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ extern int errno; #endif -static int verify_callback PARAMS ((int, X509_STORE_CTX *)); +SSL_CTX *ssl_ctx; -void +static void ssl_init_prng (void) { /* It is likely that older versions of OpenSSL will fail on @@ -90,208 +98,266 @@ ssl_init_prng (void) return; #endif - /* Still not enough randomness, presumably because neither random - file nor EGD have been available. Use the stupidest possible - method -- seed OpenSSL's PRNG with the system's PRNG. This is - insecure in the cryptographic sense, but people who care about - security will use /dev/random or their own source of randomness - anyway. */ + /* Still not enough randomness, most likely because neither + /dev/random nor EGD were available. Resort to a simple and + stupid method -- seed OpenSSL's PRNG with libc PRNG. This is + cryptographically weak, but people who care about strong + cryptography should install /dev/random (default on Linux) or + specify their own source of randomness anyway. */ + + logprintf (LOG_VERBOSE, _("Warning: using a weak random seed.\n")); while (RAND_status () == 0 && maxrand-- > 0) { unsigned char rnd = random_number (256); RAND_seed (&rnd, sizeof (rnd)); } +#endif /* SSLEAY_VERSION_NUMBER >= 0x00905100 */ +} - if (RAND_status () == 0) - { - logprintf (LOG_NOTQUIET, - _("Could not seed OpenSSL PRNG; disabling SSL.\n")); - scheme_disable (SCHEME_HTTPS); +static int +verify_callback (int ok, X509_STORE_CTX *ctx) +{ + char *s, buf[256]; + s = X509_NAME_oneline (X509_get_subject_name (ctx->current_cert), buf, 256); + if (ok == 0) { + switch (ctx->error) { + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_CERT_HAS_EXPIRED: + /* This mean the CERT is not valid !!! */ + ok = 0; + break; + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + /* Unsure if we should handle that this way */ + ok = 1; + break; } -#endif /* SSLEAY_VERSION_NUMBER >= 0x00905100 */ + } + return ok; +} + +/* Print SSL errors. */ + +void +ssl_print_errors (void) +{ + unsigned long curerr = 0; + char errbuff[1024]; + xzero (errbuff); + while ((curerr = ERR_get_error ()) != 0) + logprintf (LOG_NOTQUIET, "OpenSSL: %s\n", + ERR_error_string (curerr, errbuff)); } /* Creates a SSL Context and sets some defaults for it */ uerr_t -init_ssl (SSL_CTX **ctx) +ssl_init () { SSL_METHOD *meth = NULL; - int verify = SSL_VERIFY_NONE; + int verify; + int can_validate; + + if (ssl_ctx) + return 0; + + /* Init the PRNG. If that fails, bail out. */ + ssl_init_prng (); + if (RAND_status () == 0) + { + logprintf (LOG_NOTQUIET, + _("Could not seed OpenSSL PRNG; disabling SSL.\n")); + scheme_disable (SCHEME_HTTPS); + return SSLERRCTXCREATE; + } + SSL_library_init (); SSL_load_error_strings (); SSLeay_add_all_algorithms (); SSLeay_add_ssl_algorithms (); - meth = SSLv23_client_method (); - *ctx = SSL_CTX_new (meth); - SSL_CTX_set_verify (*ctx, verify, verify_callback); - if (*ctx == NULL) return SSLERRCTXCREATE; - if (opt.sslcertfile) + switch (opt.sslprotocol) { - if (SSL_CTX_use_certificate_file (*ctx, opt.sslcertfile, - SSL_FILETYPE_PEM) <= 0) - return SSLERRCERTFILE; + default: + meth = SSLv23_client_method (); + break; + case 1 : + meth = SSLv2_client_method (); + break; + case 2 : + meth = SSLv3_client_method (); + break; + case 3 : + meth = TLSv1_client_method (); + break; + } + if (meth == NULL) + { + ssl_print_errors (); + return SSLERRCTXCREATE; + } + + ssl_ctx = SSL_CTX_new (meth); + if (meth == NULL) + { + ssl_print_errors (); + return SSLERRCTXCREATE; + } + /* Can we validate the server Cert ? */ + if (opt.sslcadir != NULL || opt.sslcafile != NULL) + { + SSL_CTX_load_verify_locations (ssl_ctx, opt.sslcafile, opt.sslcadir); + can_validate = 1; + } + else + { + can_validate = 0; + } + + if (!opt.sslcheckcert) + { + /* check cert but ignore error, do not break handshake on error */ + verify = SSL_VERIFY_NONE; + } + else + { + if (!can_validate) + { + logprintf (LOG_NOTQUIET, "Warrining validation of Server Cert not possible!\n"); + verify = SSL_VERIFY_NONE; + } + else + { + /* break handshake if server cert is not valid but allow NO-Cert mode */ + verify = SSL_VERIFY_PEER; + } + } + + SSL_CTX_set_verify (ssl_ctx, verify, verify_callback); + + if (opt.sslcertfile != NULL || opt.sslcertkey != NULL) + { + int ssl_cert_type; + if (!opt.sslcerttype) + ssl_cert_type = SSL_FILETYPE_PEM; + else + ssl_cert_type = SSL_FILETYPE_ASN1; + if (opt.sslcertkey == NULL) - opt.sslcertkey=opt.sslcertfile; - if (SSL_CTX_use_PrivateKey_file (*ctx, opt.sslcertkey, - SSL_FILETYPE_PEM) <= 0) - return SSLERRCERTKEY; - } - return 0; /* Succeded */ -} + opt.sslcertkey = opt.sslcertfile; + if (opt.sslcertfile == NULL) + opt.sslcertfile = opt.sslcertkey; -/* Sets up a SSL structure and performs the handshake on fd - Returns 0 if everything went right - Returns 1 if something went wrong ----- TODO: More exit codes -*/ -int -connect_ssl (SSL **con, SSL_CTX *ctx, int fd) -{ - *con = (SSL *)SSL_new (ctx); - SSL_set_fd (*con, fd); - SSL_set_connect_state (*con); - SSL_connect (*con); - if ((*con)->state != SSL_ST_OK) - return 1; - /*while((SSLerror=ERR_get_error())!=0) - printf("%s\n", ERR_error_string(SSLerror,NULL));*/ + if (SSL_CTX_use_certificate_file (ssl_ctx, opt.sslcertfile, + ssl_cert_type) <= 0) + { + ssl_print_errors (); + return SSLERRCERTFILE; + } + if (SSL_CTX_use_PrivateKey_file (ssl_ctx, opt.sslcertkey, + ssl_cert_type) <= 0) + { + ssl_print_errors (); + return SSLERRCERTKEY; + } + } - return 0; + return 0; /* Succeded */ } -void -shutdown_ssl (SSL* con) +static int +ssl_read (int fd, char *buf, int bufsize, void *ctx) { - SSL_shutdown (con); - if (con != NULL) - SSL_free (con); + int ret; + SSL *ssl = (SSL *) ctx; + do + ret = SSL_read (ssl, buf, bufsize); + while (ret == -1 + && SSL_get_error (ssl, ret) == SSL_ERROR_SYSCALL + && errno == EINTR); + return ret; } -void -free_ssl_ctx (SSL_CTX * ctx) +static int +ssl_write (int fd, char *buf, int bufsize, void *ctx) { - SSL_CTX_free (ctx); + int ret = 0; + SSL *ssl = (SSL *) ctx; + do + ret = SSL_write (ssl, buf, bufsize); + while (ret == -1 + && SSL_get_error (ssl, ret) == SSL_ERROR_SYSCALL + && errno == EINTR); + return ret; } -int -verify_callback (int ok, X509_STORE_CTX *ctx) +static int +ssl_poll (int fd, double timeout, int wait_for, void *ctx) { - char *s, buf[256]; - s = X509_NAME_oneline (X509_get_subject_name (ctx->current_cert), buf, 256); - if (ok == 0) { - switch (ctx->error) { - case X509_V_ERR_CERT_NOT_YET_VALID: - case X509_V_ERR_CERT_HAS_EXPIRED: - case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: - ok = 1; - } - } - return ok; + SSL *ssl = (SSL *) ctx; + if (timeout == 0) + return 1; + if (SSL_pending (ssl)) + return 1; + return select_fd (fd, timeout, wait_for); } -/* pass all ssl errors to DEBUGP - returns the number of printed errors */ -int -ssl_printerrors (void) +static int +ssl_peek (int fd, char *buf, int bufsize, void *ctx) { - int ocerr = 0; - unsigned long curerr = 0; - char errbuff[1024]; - memset(errbuff, 0, sizeof(errbuff)); - for (curerr = ERR_get_error (); curerr; curerr = ERR_get_error ()) - { - DEBUGP (("OpenSSL: %s\n", ERR_error_string (curerr, errbuff))); - ++ocerr; - } - return ocerr; + int ret; + SSL *ssl = (SSL *) ctx; + do + ret = SSL_peek (ssl, buf, bufsize); + while (ret == -1 + && SSL_get_error (ssl, ret) == SSL_ERROR_SYSCALL + && errno == EINTR); + return ret; } -/* SSL version of iread. Only exchanged read for SSL_read - Read at most LEN bytes from FD, storing them to BUF. This is - virtually the same as read(), but takes care of EINTR braindamage - and uses select() to timeout the stale connections (a connection is - stale if more than OPT.TIMEOUT time is spent in select() or - read()). */ -int -ssl_iread (SSL *con, char *buf, int len) +static void +ssl_close (int fd, void *ctx) { - int res; - int fd; - BIO_get_fd (con->rbio, &fd); - do - { -#ifdef HAVE_SELECT - if (opt.timeout) - { - - do - { - res = select_fd (fd, opt.timeout, 0); - } - while (res == -1 && errno == EINTR); - if (res <= 0) - { - /* Set errno to ETIMEDOUT on timeout. */ - if (res == 0) - /* #### Potentially evil! */ - errno = ETIMEDOUT; - return -1; - } - } + SSL *ssl = (SSL *) ctx; + SSL_shutdown (ssl); + SSL_free (ssl); + +#ifdef WINDOWS + closesocket (fd); +#else + close (fd); #endif - res = SSL_read (con, buf, len); - } - while (res == -1 && errno == EINTR); - return res; + DEBUGP (("Closed %d/SSL 0x%0lx\n", fd, (unsigned long) ssl)); } -/* SSL version of iwrite. Only exchanged write for SSL_write - Write LEN bytes from BUF to FD. This is similar to iread(), but - doesn't bother with select(). Unlike iread(), it makes sure that - all of BUF is actually written to FD, so callers needn't bother - with checking that the return value equals to LEN. Instead, you - should simply check for -1. */ -int -ssl_iwrite (SSL *con, char *buf, int len) +/* Sets up a SSL structure and performs the handshake on fd. */ + +SSL * +ssl_connect (int fd) { - int res = 0; - int fd; - BIO_get_fd (con->rbio, &fd); - /* `write' may write less than LEN bytes, thus the outward loop - keeps trying it until all was written, or an error occurred. The - inner loop is reserved for the usual EINTR f*kage, and the - innermost loop deals with the same during select(). */ - while (len > 0) - { - do - { -#ifdef HAVE_SELECT - if (opt.timeout) - { - do - { - res = select_fd (fd, opt.timeout, 1); - } - while (res == -1 && errno == EINTR); - if (res <= 0) - { - /* Set errno to ETIMEDOUT on timeout. */ - if (res == 0) - /* #### Potentially evil! */ - errno = ETIMEDOUT; - return -1; - } - } -#endif - res = SSL_write (con, buf, len); - } - while (res == -1 && errno == EINTR); - if (res <= 0) - break; - buf += res; - len -= res; - } - return res; + SSL *ssl; + + assert (ssl_ctx != NULL); + ssl = SSL_new (ssl_ctx); + if (!ssl) + goto err; + if (!SSL_set_fd (ssl, fd)) + goto err; + SSL_set_connect_state (ssl); + if (SSL_connect (ssl) <= 0 || ssl->state != SSL_ST_OK) + goto err; + + /* Register FD with Wget's transport layer, i.e. arrange that + SSL-enabled functions are used for reading, writing, and polling. + That way the rest of Wget can keep using xread, xwrite, and + friends and not care what happens underneath. */ + fd_register_transport (fd, ssl_read, ssl_write, ssl_poll, ssl_peek, + ssl_close, ssl); + DEBUGP (("Connected %d to SSL 0x%0lx\n", fd, (unsigned long) ssl)); + return ssl; + + err: + ssl_print_errors (); + if (ssl) + SSL_free (ssl); + return NULL; } -#endif /* HAVE_SSL */