From 91a190a770c417549dddd64ce762140184aabf0c Mon Sep 17 00:00:00 2001 From: hniksic Date: Sat, 20 Apr 2002 19:26:48 -0700 Subject: [PATCH] [svn] Implement several SSL features. - allow checking of server cert - allow defining client cert type - allow limit of ssl protocol - check more return values - added debug message on break Published by Thomas Lussnig in <3CC09969.5000607@bewegungsmelder.de>. --- src/ChangeLog | 9 ++ src/gen_sslfunc.c | 221 ++++++++++++++++++++++++++++++++-------------- src/init.c | 5 ++ src/main.c | 45 ++++++++-- src/options.h | 5 ++ 5 files changed, 213 insertions(+), 72 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index 68c2ae59..cba701d9 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,12 @@ +2002-04-21 Thomas Lussnig + + * gen_ssl.c: + - allow checking of server cert + - allow defining client cert type + - allow limit of ssl protocol + - check more return values + - added debug message on break + 2002-04-21 Hrvoje Niksic * recur.c (download_child_p): Revert order of items in check diff --git a/src/gen_sslfunc.c b/src/gen_sslfunc.c index f03f9c56..d9515130 100644 --- a/src/gen_sslfunc.c +++ b/src/gen_sslfunc.c @@ -50,8 +50,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ extern int errno; #endif -static int verify_callback PARAMS ((int, X509_STORE_CTX *)); - void ssl_init_prng (void) { @@ -112,68 +110,6 @@ ssl_init_prng (void) #endif /* SSLEAY_VERSION_NUMBER >= 0x00905100 */ } -/* Creates a SSL Context and sets some defaults for it */ -uerr_t -init_ssl (SSL_CTX **ctx) -{ - SSL_METHOD *meth = NULL; - int verify = SSL_VERIFY_NONE; - 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) - { - if (SSL_CTX_use_certificate_file (*ctx, opt.sslcertfile, - SSL_FILETYPE_PEM) <= 0) - return SSLERRCERTFILE; - 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 */ -} - -/* 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));*/ - - return 0; -} - -void -shutdown_ssl (SSL* con) -{ - SSL_shutdown (con); - if (con != NULL) - SSL_free (con); -} - -void -free_ssl_ctx (SSL_CTX * ctx) -{ - SSL_CTX_free (ctx); -} - int verify_callback (int ok, X509_STORE_CTX *ctx) { @@ -183,8 +119,13 @@ verify_callback (int ok, X509_STORE_CTX *ctx) 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; } } return ok; @@ -199,7 +140,7 @@ ssl_printerrors (void) unsigned long curerr = 0; char errbuff[1024]; memset(errbuff, 0, sizeof(errbuff)); - for (curerr = ERR_get_error (); curerr; curerr = ERR_get_error ()) + while ( 0 != (curerr = ERR_get_error ())) { DEBUGP (("OpenSSL: %s\n", ERR_error_string (curerr, errbuff))); ++ocerr; @@ -207,6 +148,156 @@ ssl_printerrors (void) return ocerr; } +/* Creates a SSL Context and sets some defaults for it */ +uerr_t +init_ssl (SSL_CTX **ctx) +{ + SSL_METHOD *meth = NULL; + int verify; + int can_validate; + SSL_library_init (); + SSL_load_error_strings (); + SSLeay_add_all_algorithms (); + SSLeay_add_ssl_algorithms (); + switch (opt.sslprotocol) + { + 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_printerrors (); + return SSLERRCTXCREATE; + } + + *ctx = SSL_CTX_new (meth); + if (meth == NULL) + { + ssl_printerrors (); + return SSLERRCTXCREATE; + } + /* Can we validate the server Cert ? */ + if (opt.sslcadir != NULL || opt.sslcafile != NULL) + { + SSL_CTX_load_verify_locations (*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 (*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 (opt.sslcertfile == NULL) + opt.sslcertfile = opt.sslcertkey; + + if (SSL_CTX_use_certificate_file (*ctx, opt.sslcertfile, ssl_cert_type) <= 0) + { + ssl_printerrors (); + return SSLERRCERTFILE; + } + if (SSL_CTX_use_PrivateKey_file (*ctx, opt.sslcertkey , ssl_cert_type) <= 0) + { + ssl_printerrors (); + return SSLERRCERTKEY; + } + } + + return 0; /* Succeded */ +} + +void +shutdown_ssl (SSL* con) +{ + if (con == NULL) + return; + if (0==SSL_shutdown (con)) + SSL_shutdown (con); + SSL_free (con); +} + +/* 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) +{ + if (NULL == (*con = SSL_new (ctx))) + { + ssl_printerrors (); + return 1; + } + if (!SSL_set_fd (*con, fd)) + { + ssl_printerrors (); + return 1; + } + SSL_set_connect_state (*con); + switch (SSL_connect (*con)) + { + case 1 : + return (*con)->state != SSL_ST_OK; + default: + ssl_printerrors (); + shutdown_ssl (*con); + *con = NULL; + return 1; + case 0 : + ssl_printerrors (); + SSL_free (*con); + *con = NULL; + return 1; + } + return 0; +} + +void +free_ssl_ctx (SSL_CTX * ctx) +{ + SSL_CTX_free (ctx); +} + /* SSL version of iread. Only exchanged read for SSL_read Read at most LEN bytes from FD, storing them to BUF. */ diff --git a/src/init.c b/src/init.c index d54c1613..2255b29d 100644 --- a/src/init.c +++ b/src/init.c @@ -185,8 +185,13 @@ static struct { { "spanhosts", &opt.spanhost, cmd_boolean }, { "spider", &opt.spider, cmd_boolean }, #ifdef HAVE_SSL + { "sslcadir", &opt.sslcadir, cmd_directory }, + { "sslcafile", &opt.sslcafile, cmd_file }, { "sslcertfile", &opt.sslcertfile, cmd_file }, { "sslcertkey", &opt.sslcertkey, cmd_file }, + { "sslcerttype", &opt.sslcerttype, cmd_number }, + { "sslcheckcert", &opt.sslcheckcert, cmd_number }, + { "sslprotocol", &opt.sslprotocol, cmd_number }, #endif /* HAVE_SSL */ { "timeout", &opt.timeout, cmd_time }, { "timestamping", &opt.timestamping, cmd_boolean }, diff --git a/src/main.c b/src/main.c index bbf4a37d..4370f3b8 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,5 @@ /* Command line parsing. - Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001 + Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001, 2002 Free Software Foundation, Inc. This file is part of GNU Wget. @@ -148,13 +148,9 @@ Logging and input file:\n\ -i, --input-file=FILE download URLs found in FILE.\n\ -F, --force-html treat input file as HTML.\n\ -B, --base=URL prepends URL to relative links in -F -i file.\n\ - --sslcertfile=FILE optional client certificate.\n\ - --sslcertkey=KEYFILE optional keyfile for this certificate.\n\ - --egd-file=FILE file name of the EGD socket.\n\ -\n"), stdout); +\n"),stdout); fputs (_("\ Download:\n\ - --bind-address=ADDRESS bind to ADDRESS (hostname or IP) on local host.\n\ -t, --tries=NUMBER set number of retries to NUMBER (0 unlimits).\n\ -O --output-document=FILE write documents to FILE.\n\ -nc, --no-clobber don\'t clobber existing files or use .# suffixes.\n\ @@ -169,6 +165,7 @@ Download:\n\ --random-wait wait from 0...2*WAIT secs between retrievals.\n\ -Y, --proxy=on/off turn proxy on or off.\n\ -Q, --quota=NUMBER set retrieval quota to NUMBER.\n\ + --bind-address=ADDRESS bind to ADDRESS (hostname or IP) on local host.\n\ --limit-rate=RATE limit download rate to RATE.\n\ \n"), stdout); fputs (_("\ @@ -199,6 +196,20 @@ HTTP options:\n\ --post-data=STRING use the POST method; send STRING as the data.\n\ --post-file=FILE use the POST method; send contents of FILE.\n\ \n"), stdout); +#ifdef HAVE_SSL + fputs (_("\ +HTTPS (SSL) options:\n\ + --sslcertfile=FILE optional client certificate.\n\ + --sslcertkey=KEYFILE optional keyfile for this certificate.\n\ + --egd-file=FILE file name of the EGD socket.\n\ + --sslcadir=DIR dir where hash list of CA's are stured.\n\ + --sslcafile=FILE file with bundle of CA's\n\ + --sslcerttype=0/1 Client-Cert type 0=PEM (default) / 1=ASN1 (DER)\n\ + --sslcheckcert=0/1 Check the server cert agenst given CA\n\ + --sslprotocol=0-3 choose SSL protocol; 0=automatic,\n\ + 1=SSLv2 2=SSLv3 3=TLSv1\n\ +\n"), stdout); +#endif fputs (_("\ FTP options:\n\ -nr, --dont-remove-listing don\'t remove `.listing\' files.\n\ @@ -208,7 +219,7 @@ FTP options:\n\ \n"), stdout); fputs (_("\ Recursive retrieval:\n\ - -r, --recursive recursive web-suck -- use with care!\n\ + -r, --recursive recursive download.\n\ -l, --level=NUMBER maximum recursion depth (inf or 0 for infinite).\n\ --delete-after delete files locally after downloading them.\n\ -k, --convert-links convert non-relative links to relative.\n\ @@ -329,6 +340,11 @@ main (int argc, char *const *argv) { "sslcertfile", required_argument, NULL, 158 }, { "sslcertkey", required_argument, NULL, 159 }, { "egd-file", required_argument, NULL, 166 }, + { "sslcadir", required_argument, NULL, 169}, + { "sslcafile", required_argument, NULL, 170}, + { "sslcerttype", required_argument, NULL, 171}, + { "sslcheckcert", required_argument, NULL, 172}, + { "sslprotocol", required_argument, NULL, 173}, #endif /* HAVE_SSL */ { "wait", required_argument, NULL, 'w' }, { "waitretry", required_argument, NULL, 152 }, @@ -552,6 +568,21 @@ GNU General Public License for more details.\n")); case 166: setval ("egdfile", optarg); break; + case 169: + setval ("sslcadir", optarg); + break; + case 170: + setval ("sslcafile", optarg); + break; + case 171: + setval ("sslcerttype", optarg); + break; + case 172: + setval ("sslcheckcert", optarg); + break; + case 173: + setval ("sslprotocol", optarg); + break; #endif /* HAVE_SSL */ case 167: setval ("postdata", optarg); diff --git a/src/options.h b/src/options.h index c6438602..2ecfecad 100644 --- a/src/options.h +++ b/src/options.h @@ -154,11 +154,16 @@ struct options necessary to display a page properly. */ char *bind_address; /* What local IP address to bind to. */ #ifdef HAVE_SSL + char *sslcadir; /* CA directory (hash files) */ + char *sslcafile; /* CA File to use */ char *sslcertfile; /* external client cert to use. */ char *sslcertkey; /* the keyfile for this certificate (if not internal) included in the certfile. */ + int sslcerttype; /* 0 = PEM / 1=ASN1 (DER) */ + int sslcheckcert; /* 0 do not check / 1 check server cert */ char *sslegdsock; /* optional socket of the egd daemon */ + int sslprotocol; /* 0 = auto / 1 = v2 / 2 = v3 / 3 = TLSv1 */ #endif /* HAVE_SSL */ int cookies; -- 2.39.2