diff --git a/src/ChangeLog b/src/ChangeLog index 9ae9dee7..83e6c2d8 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,13 @@ +2005-04-26 Hrvoje Niksic + + * gen_sslfunc.c (ssl_init): Use default locations for loading the + certificate bundles. + (ssl_init_prng): Disable the cryptographically weak PRNG + initialization fallback. + + * init.c: Renamed SSL command-line arguments and wgetrc commands. + (defaults): Check the server certificate by default. + 2005-04-26 Hrvoje Niksic * cookies.c (cookie_handle_set_cookie): Delete the part of the diff --git a/src/gen_sslfunc.c b/src/gen_sslfunc.c index 44b5414a..2d908c32 100644 --- a/src/gen_sslfunc.c +++ b/src/gen_sslfunc.c @@ -1,6 +1,6 @@ -/* SSL support. - Copyright (C) 2000 Free Software Foundation, Inc. - Contributed by Christian Fraenkel. +/* SSL support via OpenSSL library. + Copyright (C) 2000-2005 Free Software Foundation, Inc. + Originally contributed by Christian Fraenkel. This file is part of GNU Wget. @@ -70,7 +70,6 @@ ssl_init_prng (void) #if SSLEAY_VERSION_NUMBER >= 0x00905100 char rand_file[256]; - int maxrand = 500; /* First, seed from a file specified by the user. This will be $RANDFILE, if set, or ~/.rnd. */ @@ -82,9 +81,9 @@ ssl_init_prng (void) if (RAND_status ()) return; - /* Get random data from EGD if opt.sslegdsock was set. */ - if (opt.sslegdsock && *opt.sslegdsock) - RAND_egd (opt.sslegdsock); + /* Get random data from EGD if opt.egd_file was set. */ + if (opt.egd_file && *opt.egd_file) + RAND_egd (opt.egd_file); if (RAND_status ()) return; @@ -99,20 +98,25 @@ ssl_init_prng (void) return; #endif - /* 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. */ +#if 0 /* don't do this by default */ + { + int maxrand = 500; - logprintf (LOG_VERBOSE, _("Warning: using a weak random seed.\n")); + /* Still not random enough, presumably because neither /dev/random + nor EGD were available. Try to seed OpenSSL's PRNG with libc + PRNG. This is cryptographically weak and defeats the purpose + of using OpenSSL, which is why it is highly discouraged. */ + + 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 - while (RAND_status () == 0 && maxrand-- > 0) - { - unsigned char rnd = random_number (256); - RAND_seed (&rnd, sizeof (rnd)); - } #endif /* SSLEAY_VERSION_NUMBER >= 0x00905100 */ } @@ -120,20 +124,23 @@ 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; + s = X509_NAME_oneline (X509_get_subject_name (ctx->current_cert), + buf, sizeof (buf)); + 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; + } } - } return ok; } @@ -152,8 +159,6 @@ uerr_t ssl_init () { SSL_METHOD *meth = NULL; - int verify; - int can_validate; if (ssl_ctx) return 0; @@ -172,20 +177,20 @@ ssl_init () SSL_load_error_strings (); SSLeay_add_all_algorithms (); SSLeay_add_ssl_algorithms (); - switch (opt.sslprotocol) + switch (opt.secure_protocol) { - 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; + case secure_protocol_auto: + meth = SSLv23_client_method (); + break; + case secure_protocol_sslv2: + meth = SSLv2_client_method (); + break; + case secure_protocol_sslv3: + meth = SSLv3_client_method (); + break; + case secure_protocol_tlsv1: + meth = TLSv1_client_method (); + break; } if (meth == NULL) { @@ -199,61 +204,41 @@ ssl_init () 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) - { - logputs (LOG_NOTQUIET, - _("Warning: validation of server certificate 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_default_verify_paths (ssl_ctx); + SSL_CTX_load_verify_locations (ssl_ctx, opt.ca_cert, opt.ca_directory); + SSL_CTX_set_verify (ssl_ctx, + opt.check_cert ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, + verify_callback); - SSL_CTX_set_verify (ssl_ctx, verify, verify_callback); - - if (opt.sslcertfile != NULL || opt.sslcertkey != NULL) + if (opt.cert_file != NULL || opt.cert_key != NULL) { int ssl_cert_type; - if (!opt.sslcerttype) - ssl_cert_type = SSL_FILETYPE_PEM; - else - ssl_cert_type = SSL_FILETYPE_ASN1; + switch (opt.cert_type) + { + case cert_type_pem: + ssl_cert_type = SSL_FILETYPE_PEM; + break; + case cert_type_asn1: + ssl_cert_type = SSL_FILETYPE_ASN1; + break; + } - if (opt.sslcertkey == NULL) - opt.sslcertkey = opt.sslcertfile; - if (opt.sslcertfile == NULL) - opt.sslcertfile = opt.sslcertkey; +#if 0 /* what was this supposed to achieve? */ + if (opt.cert_key == NULL) + opt.cert_key = opt.cert_file; + if (opt.cert_file == NULL) + opt.cert_file = opt.cert_key; +#endif - if (SSL_CTX_use_certificate_file (ssl_ctx, opt.sslcertfile, - ssl_cert_type) <= 0) + if (SSL_CTX_use_certificate_file (ssl_ctx, opt.cert_file, + ssl_cert_type) != 1) { ssl_print_errors (); return SSLERRCERTFILE; } - if (SSL_CTX_use_PrivateKey_file (ssl_ctx, opt.sslcertkey, - ssl_cert_type) <= 0) + if (SSL_CTX_use_PrivateKey_file (ssl_ctx, opt.cert_key, + ssl_cert_type) != 1) { ssl_print_errors (); return SSLERRCERTKEY; diff --git a/src/http.c b/src/http.c index 77861d89..ae68c6fb 100644 --- a/src/http.c +++ b/src/http.c @@ -1197,14 +1197,14 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy) /* try without certfile */ logprintf (LOG_NOTQUIET, _("Failed to load certificates from %s\n"), - opt.sslcertfile); + opt.cert_file); logprintf (LOG_NOTQUIET, _("Trying without the specified certificate\n")); break; case SSLERRCERTKEY: logprintf (LOG_NOTQUIET, _("Failed to get certificate key from %s\n"), - opt.sslcertkey); + opt.cert_key); logprintf (LOG_NOTQUIET, _("Trying without the specified certificate\n")); break; diff --git a/src/init.c b/src/init.c index 8ab1bec3..c3562cc3 100644 --- a/src/init.c +++ b/src/init.c @@ -84,6 +84,9 @@ CMD_DECLARE (cmd_directory); CMD_DECLARE (cmd_time); CMD_DECLARE (cmd_vector); +#ifdef HAVE_SSL +CMD_DECLARE (cmd_spec_cert_type); +#endif CMD_DECLARE (cmd_spec_dirstruct); CMD_DECLARE (cmd_spec_header); CMD_DECLARE (cmd_spec_htmlify); @@ -92,6 +95,9 @@ CMD_DECLARE (cmd_spec_prefer_family); CMD_DECLARE (cmd_spec_progress); CMD_DECLARE (cmd_spec_recursive); CMD_DECLARE (cmd_spec_restrict_file_names); +#ifdef HAVE_SSL +CMD_DECLARE (cmd_spec_secure_protocol); +#endif CMD_DECLARE (cmd_spec_timeout); CMD_DECLARE (cmd_spec_useragent); @@ -115,7 +121,17 @@ static struct { { "backups", &opt.backups, cmd_number }, { "base", &opt.base_href, cmd_string }, { "bindaddress", &opt.bind_address, cmd_string }, +#ifdef HAVE_SSL + { "cacertificate", &opt.ca_cert, cmd_file }, +#endif { "cache", &opt.allow_cache, cmd_boolean }, +#ifdef HAVE_SSL + { "cadirectory", &opt.ca_directory, cmd_directory }, + { "certificate", &opt.cert_file, cmd_file }, + { "certificatekey", &opt.cert_key, cmd_file }, + { "certificatetype", &opt.cert_type, cmd_spec_cert_type }, + { "checkcertificate", &opt.check_cert, cmd_boolean }, +#endif { "connecttimeout", &opt.connect_timeout, cmd_time }, { "continue", &opt.always_rest, cmd_boolean }, { "convertlinks", &opt.convert_links, cmd_boolean }, @@ -135,7 +151,7 @@ static struct { { "dotspacing", &opt.dot_spacing, cmd_number }, { "dotstyle", &opt.dot_style, cmd_string }, #ifdef HAVE_SSL - { "egdfile", &opt.sslegdsock, cmd_file }, + { "egdfile", &opt.egd_file, cmd_file }, #endif { "excludedirectories", &opt.excludes, cmd_directory_vector }, { "excludedomains", &opt.exclude_domains, cmd_vector }, @@ -200,18 +216,12 @@ static struct { { "robots", &opt.use_robots, cmd_boolean }, { "savecookies", &opt.cookies_output, cmd_file }, { "saveheaders", &opt.save_headers, cmd_boolean }, +#ifdef HAVE_SSL + { "secureprotocol", &opt.secure_protocol, cmd_spec_secure_protocol }, +#endif { "serverresponse", &opt.server_response, cmd_boolean }, { "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 */ { "strictcomments", &opt.strict_comments, cmd_boolean }, { "timeout", NULL, cmd_spec_timeout }, { "timestamping", &opt.timestamping, cmd_boolean }, @@ -289,6 +299,10 @@ defaults (void) opt.dns_cache = 1; opt.ftp_pasv = 1; +#ifdef HAVE_SSL + opt.check_cert = 1; +#endif + /* The default for file name restriction defaults to the OS type. */ #if !defined(WINDOWS) && !defined(__CYGWIN__) opt.restrict_files_os = restrict_unix; @@ -1016,6 +1030,28 @@ cmd_time (const char *com, const char *val, void *closure) options specially. */ static int check_user_specified_header PARAMS ((const char *)); +/* Forward decl */ +struct decode_item { + const char *name; + int code; +}; +static int decode_string PARAMS ((const char *, const struct decode_item *, + int, int *)); + +#ifdef HAVE_SSL +static int +cmd_spec_cert_type (const char *com, const char *val, void *closure) +{ + static const struct decode_item choices[] = { + { "pem", cert_type_pem }, + { "asn1", cert_type_asn1 }, + }; + int ok = decode_string (val, choices, countof (choices), closure); + if (!ok) + fprintf (stderr, _("%s: %s: Invalid value `%s'.\n"), exec_name, com, val); + return ok; +} +#endif static int cmd_spec_dirstruct (const char *com, const char *val, void *closure) @@ -1080,24 +1116,16 @@ cmd_spec_mirror (const char *com, const char *val, void *closure) static int cmd_spec_prefer_family (const char *com, const char *val, void *closure) { - if (0 == strcasecmp (val, "ipv4")) - { - opt.prefer_family = prefer_ipv4; - return 1; - } - else if (0 == strcasecmp (val, "ipv6")) - { - opt.prefer_family = prefer_ipv6; - return 1; - } - else if (0 == strcasecmp (val, "none")) - { - opt.prefer_family = prefer_none; - return 1; - } - fprintf (stderr, _("%s: %s: Invalid preferred family `%s'.\n"), - exec_name, com, val); - return 0; + static const struct decode_item choices[] = { + { "IPv4", prefer_ipv4 }, + { "IPv6", prefer_ipv6 }, + { "none", prefer_none }, + }; + int ok = decode_string (val, choices, countof (choices), + (int *) &opt.prefer_family); + if (!ok) + fprintf (stderr, _("%s: %s: Invalid value `%s'.\n"), exec_name, com, val); + return ok; } /* Set progress.type to VAL, but verify that it's a valid progress @@ -1179,6 +1207,23 @@ cmd_spec_restrict_file_names (const char *com, const char *val, void *closure) return 1; } +#ifdef HAVE_SSL +static int +cmd_spec_secure_protocol (const char *com, const char *val, void *closure) +{ + static const struct decode_item choices[] = { + { "auto", secure_protocol_auto }, + { "sslv2", secure_protocol_sslv2 }, + { "sslv3", secure_protocol_sslv3 }, + { "tlsv1", secure_protocol_tlsv1 }, + }; + int ok = decode_string (val, choices, countof (choices), closure); + if (!ok) + fprintf (stderr, _("%s: %s: Invalid value `%s'.\n"), exec_name, com, val); + return ok; +} +#endif + /* Set all three timeout values. */ static int @@ -1312,6 +1357,10 @@ simple_atof (const char *beg, const char *end, double *dest) return 1; } +/* Verify that the user-specified header in S is valid. It must + contain a colon preceded by non-white-space characters and must not + contain newlines. */ + static int check_user_specified_header (const char *s) { @@ -1327,6 +1376,23 @@ check_user_specified_header (const char *s) return 0; return 1; } + +/* Decode VAL into a number, according to ITEMS. */ + +static int +decode_string (const char *val, const struct decode_item *items, int itemcount, + int *place) +{ + int i; + for (i = 0; i < itemcount; i++) + if (0 == strcasecmp (val, items[i].name)) + { + *place = items[i].code; + return 1; + } + return 0; +} + void cleanup_html_url PARAMS ((void)); void http_cleanup PARAMS ((void)); diff --git a/src/main.c b/src/main.c index fcbe53a5..89c82f97 100644 --- a/src/main.c +++ b/src/main.c @@ -157,7 +157,13 @@ struct cmdline_option option_data[] = { "backups", 0, OPT_BOOLEAN, "backups", -1 }, { "base", 'B', OPT_VALUE, "base", -1 }, { "bind-address", 0, OPT_VALUE, "bindaddress", -1 }, + { IF_SSL ("ca-certificate"), 0, OPT_VALUE, "cacertificate", -1 }, + { IF_SSL ("ca-directory"), 0, OPT_VALUE, "cadirectory", -1 }, { "cache", 0, OPT_BOOLEAN, "cache", -1 }, + { IF_SSL ("certificate"), 0, OPT_VALUE, "certificate", -1 }, + { IF_SSL ("certificate-key"), 0, OPT_VALUE, "certificatekey", -1 }, + { IF_SSL ("certificate-type"), 0, OPT_VALUE, "certificatetype", -1 }, + { IF_SSL ("check-certificate"), 0, OPT_BOOLEAN, "checkcertificate", -1 }, { "clobber", 0, OPT__CLOBBER, NULL, optional_argument }, { "connect-timeout", 0, OPT_VALUE, "connecttimeout", -1 }, { "continue", 'c', OPT_BOOLEAN, "continue", -1 }, @@ -219,7 +225,7 @@ struct cmdline_option option_data[] = { "progress", 0, OPT_VALUE, "progress", -1 }, { "protocol-directories", 0, OPT_BOOLEAN, "protocoldirectories", -1 }, { "proxy", 0, OPT_BOOLEAN, "useproxy", -1 }, - { "_proxy-compat", 'Y', OPT_VALUE, "useproxy", -1 }, /* back-compatible */ + { "proxy__compat", 'Y', OPT_VALUE, "useproxy", -1 }, /* back-compatible */ { "proxy-passwd", 0, OPT_VALUE, "proxypasswd", -1 }, { "proxy-user", 0, OPT_VALUE, "proxyuser", -1 }, { "quiet", 'q', OPT_BOOLEAN, "quiet", -1 }, @@ -236,16 +242,10 @@ struct cmdline_option option_data[] = { "retry-connrefused", 0, OPT_BOOLEAN, "retryconnrefused", -1 }, { "save-cookies", 0, OPT_VALUE, "savecookies", -1 }, { "save-headers", 0, OPT_BOOLEAN, "saveheaders", -1 }, + { IF_SSL ("secure-protocol"), 0, OPT_VALUE, "secureprotocol", -1 }, { "server-response", 'S', OPT_BOOLEAN, "serverresponse", -1 }, { "span-hosts", 'H', OPT_BOOLEAN, "spanhosts", -1 }, { "spider", 0, OPT_BOOLEAN, "spider", -1 }, - { IF_SSL ("sslcadir"), 0, OPT_VALUE, "sslcadir", -1 }, - { IF_SSL ("sslcafile"), 0, OPT_VALUE, "sslcafile", -1 }, - { IF_SSL ("sslcertfile"), 0, OPT_VALUE, "sslcertfile", -1 }, - { IF_SSL ("sslcertkey"), 0, OPT_VALUE, "sslcertkey", -1 }, - { IF_SSL ("sslcerttype"), 0, OPT_VALUE, "sslcerttype", -1 }, - { IF_SSL ("sslcheckcert"), 0, OPT_VALUE, "sslcheckcert", -1 }, - { IF_SSL ("sslprotocol"), 0, OPT_VALUE, "sslprotocol", -1 }, { "strict-comments", 0, OPT_BOOLEAN, "strictcomments", -1 }, { "timeout", 'T', OPT_VALUE, "timeout", -1 }, { "timestamping", 'N', OPT_BOOLEAN, "timestamping", -1 }, @@ -532,24 +532,24 @@ HTTP options:\n"), #ifdef HAVE_SSL N_("\ -HTTPS (SSL) options:\n"), +HTTPS (SSL/TLS) options:\n"), N_("\ - --sslcertfile=FILE optional client certificate.\n"), + --secure-protocol=PR choose SSL protocol, one of auto, SSLv2, SSLv3,\n\ + and TLSv1.\n"), N_("\ - --sslcertkey=KEYFILE optional keyfile for this certificate.\n"), + --no-check-certificate don't validate the server's certificate.\n"), N_("\ - --sslcadir=DIR dir where hash list of CA's are stored.\n"), + --certificate=FILE client certificate file.\n"), N_("\ - --sslcafile=FILE file with bundle of CA's.\n"), + --certificate-key=FILE optional key file for this certificate.\n"), N_("\ - --sslcerttype=0/1 Client-Cert type 0=PEM (default) / 1=ASN1 (DER).\n"), + --certificate-type=TYPE client certificate type, PEM or ASN1.\n"), N_("\ - --sslcheckcert=0/1 Check the server cert against given CA.\n"), + --sslcadir=DIR dir where hash list of CA's are stored.\n"), N_("\ - --sslprotocol=0-3 choose SSL protocol; 0=automatic,\n\ - 1=SSLv2 2=SSLv3 3=TLSv1.\n"), + --sslcafile=FILE file with bundle of CA's.\n"), N_("\ - --egd-file=FILE file name of the EGD socket.\n"), + --egd-file=FILE file name of the EGD socket.\n"), "\n", #endif /* HAVE_SSL */ diff --git a/src/options.h b/src/options.h index d4140a0c..48c8fd00 100644 --- a/src/options.h +++ b/src/options.h @@ -157,17 +157,29 @@ struct options int page_requisites; /* Whether we need to download all files 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 + enum { + secure_protocol_auto, + secure_protocol_sslv2, + secure_protocol_sslv3, + secure_protocol_tlsv1 + } secure_protocol; /* type of secure protocol to use. */ + int check_cert; /* whether to validate the server's cert */ + char *cert_file; /* external client certificate to use. */ + char *cert_key; /* 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 */ + enum { + cert_type_pem, + cert_type_asn1 + } cert_type; /* type of client certificate */ + + char *ca_directory; /* CA directory (hash files) */ + char *ca_cert; /* CA certificate file to use */ + + + char *egd_file; /* file name of the egd daemon socket */ #endif /* HAVE_SSL */ int cookies; /* whether cookies are used. */