From: hno <> Date: Sat, 20 Oct 2001 04:34:48 +0000 (+0000) Subject: SSL update from the "ssl" branch at SourceForge X-Git-Tag: SQUID_3_0_PRE1~1339 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=79d4ccdf0a8c242f24a40d774d2c7d4dcdf28047;p=thirdparty%2Fsquid.git SSL update from the "ssl" branch at SourceForge * Several SSL tweaking options - SSL version per https_port, no longer a global setting - supported chipers, per https_port - supported protocols, per https_port - connection shutdown method * Fix the bug reported by Noel Burton-Krahn where SSL connections could get hung with data pending in the SSL internal buffers. Mostly seen on large POST/PUT requests, but could in theory appear on any request larger than 4K. --- diff --git a/src/cache_cf.cc b/src/cache_cf.cc index f31fa8358f..ab24b84f95 100644 --- a/src/cache_cf.cc +++ b/src/cache_cf.cc @@ -1,6 +1,6 @@ /* - * $Id: cache_cf.cc,v 1.393 2001/10/17 19:43:39 hno Exp $ + * $Id: cache_cf.cc,v 1.394 2001/10/19 22:34:48 hno Exp $ * * DEBUG: section 3 Configuration File Parsing * AUTHOR: Harvest Derived @@ -2317,6 +2317,14 @@ parse_https_port_list(https_port_list ** head) } else if (strncmp(token, "key=", 4) == 0) { safe_free(s->key); s->key = xstrdup(token + 4); + } else if (strncmp(token, "version=", 8) == 0) { + s->version = atoi(token + 8); + } else if (strncmp(token, "options=", 8) == 0) { + safe_free(s->options); + s->options = xstrdup(token + 8); + } else if (strncmp(token, "cipher=", 7) == 0) { + safe_free(s->cipher); + s->cipher = xstrdup(token + 7); } else { self_destruct(); } @@ -2330,12 +2338,19 @@ static void dump_https_port_list(StoreEntry * e, const char *n, const https_port_list * s) { while (s) { - storeAppendPrintf(e, "%s %s:%d cert=\"%s\" key=\"%s\"\n", + storeAppendPrintf(e, "%s %s:%d cert=\"%s\" key=\"%s\"", n, inet_ntoa(s->s.sin_addr), ntohs(s->s.sin_port), s->cert, s->key); + if (s->version) + storeAppendPrintf(e, " version=%d", s->version); + if (s->options) + storeAppendPrintf(e, " options=%s", s->options); + if (s->cipher) + storeAppendPrintf(e, " cipher=%s", s->cipher); + storeAppendPrintf(e, "\n"); s = s->next; } } diff --git a/src/cf.data.pre b/src/cf.data.pre index 0246f79eab..e8add8169d 100644 --- a/src/cf.data.pre +++ b/src/cf.data.pre @@ -1,6 +1,6 @@ # -# $Id: cf.data.pre,v 1.232 2001/10/10 15:17:40 adrian Exp $ +# $Id: cf.data.pre,v 1.233 2001/10/19 22:34:48 hno Exp $ # # # SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -90,7 +90,7 @@ TYPE: https_port_list DEFAULT: none LOC: Config.Sockaddr.https DOC_START - Usage: [ip:]port cert=certificate.pem [key=key.pem] + Usage: [ip:]port cert=certificate.pem [key=key.pem] [...] The socket address where Squid will listen for HTTPS client requests. @@ -102,24 +102,36 @@ DOC_START If key is not specified then the given certificate is assumed to be a combined certificate and key file. + Other options: + + version= The version of SSL/TLS supported + 1 automatic (default) + 2 SSLv2 only + 3 SSLv3 only + 4 TLSv1 only + + cipher= Colon separated list of supported ciphers + + options= Varions SSL engine options. The most important being: + NO_SSLv2 Disallow the use of SSLv2 + NO_SSLv3 Disallow the use of SSLv3 + NO_TLSv1 Disallow the use of TLSv1 + See src/ssl_support.c or OpenSSL documentation for + a more complete list. + You may specify multiple socket addresses on multiple lines, each with their own SSL certificate. DOC_END -NAME: ssl_version -IFDEF: USE_SSL -TYPE: int -DEFAULT: 1 -LOC: Config.SSL.version +NAME: ssl_unclean_shutdown +TYPE: onoff +DEFAULT: off +LOC: Config.SSL.unclean_shutdown DOC_START - Determines the version of SSL/TLS used. - 1: SSLv2/SSLv3 - 2: SSLv2 only - 3: SSLv3 only - 4: TLSv1 + Some browsers (especially MSIE) bugs out on SSL shutdown + messages. DOC_END - NAME: icp_port udp_port TYPE: ushort DEFAULT: 3130 diff --git a/src/client_side.cc b/src/client_side.cc index 13a38608fb..814c735332 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -1,6 +1,6 @@ /* - * $Id: client_side.cc,v 1.548 2001/10/17 20:25:01 hno Exp $ + * $Id: client_side.cc,v 1.549 2001/10/19 22:34:48 hno Exp $ * * DEBUG: section 33 Client-side Routines * AUTHOR: Duane Wessels @@ -3491,7 +3491,7 @@ clientHttpsConnectionsOpen(void) continue; CBDATA_INIT_TYPE(https_port_data); https_port = cbdataAlloc(https_port_data); - https_port->sslContext = sslLoadCert(s->cert, s->key); + https_port->sslContext = sslCreateContext(s->cert, s->key, s->version, s->cipher, s->options); comm_listen(fd); commSetSelect(fd, COMM_SELECT_READ, httpsAccept, https_port, 0); commSetDefer(fd, httpAcceptDefer, NULL); diff --git a/src/comm.cc b/src/comm.cc index 3960c40c39..7cc1d42102 100644 --- a/src/comm.cc +++ b/src/comm.cc @@ -1,6 +1,6 @@ /* - * $Id: comm.cc,v 1.322 2001/10/17 20:25:01 hno Exp $ + * $Id: comm.cc,v 1.323 2001/10/19 22:34:49 hno Exp $ * * DEBUG: section 5 Socket Functions * AUTHOR: Harvest Derived @@ -602,7 +602,7 @@ comm_lingering_close(int fd) { #if USE_SSL if (fd_table[fd].ssl) - SSL_shutdown(fd_table[fd].ssl); + ssl_shutdown_method(fd); #endif if (shutdown(fd, 1) < 0) { comm_close(fd); @@ -633,7 +633,7 @@ comm_close(int fd) F->flags.closing = 1; #if USE_SSL if (F->ssl) - SSL_shutdown(F->ssl); + ssl_shutdown_method(fd); #endif CommWriteStateCallbackAndFree(fd, COMM_ERR_CLOSING); commCallCloseHandlers(fd); diff --git a/src/comm_select.cc b/src/comm_select.cc index 50cd18e415..6bd1f9c8f9 100644 --- a/src/comm_select.cc +++ b/src/comm_select.cc @@ -1,6 +1,6 @@ /* - * $Id: comm_select.cc,v 1.50 2001/05/04 13:37:42 hno Exp $ + * $Id: comm_select.cc,v 1.51 2001/10/19 22:34:49 hno Exp $ * * DEBUG: section 5 Socket Functions * @@ -317,6 +317,7 @@ comm_poll(int msec) int i; int maxfd; unsigned long nfds; + unsigned long npending; int num; int callicp = 0, callhttp = 0; int calldns = 0; @@ -341,6 +342,7 @@ comm_poll(int msec) comm_poll_http_incoming(); callicp = calldns = callhttp = 0; nfds = 0; + npending = 0; maxfd = Biggest_FD + 1; for (i = 0; i < maxfd; i++) { int events; @@ -370,19 +372,23 @@ comm_poll(int msec) pfds[nfds].events = events; pfds[nfds].revents = 0; nfds++; + if ((events & POLLRDNORM) && fd_table[i].flags.read_pending) + npending++; } } if (nfds == 0) { assert(shutting_down); return COMM_SHUTDOWN; } + if (npending) + msec = 0; if (msec > MAX_POLL_TIME) msec = MAX_POLL_TIME; for (;;) { statCounter.syscalls.polls++; num = poll(pfds, nfds, msec); statCounter.select_loops++; - if (num >= 0) + if (num >= 0 || npending >= 0) break; if (ignoreErrno(errno)) continue; @@ -391,22 +397,27 @@ comm_poll(int msec) return COMM_ERROR; /* NOTREACHED */ } - debug(5, num ? 5 : 8) ("comm_poll: %d FDs ready\n", num); + debug(5, num ? 5 : 8) ("comm_poll: %d+%d FDs ready\n", num, npending); statHistCount(&statCounter.select_fds_hist, num); /* Check timeout handlers ONCE each second. */ if (squid_curtime > last_timeout) { last_timeout = squid_curtime; checkTimeouts(); } - if (num == 0) + if (num == 0 && npending == 0) continue; /* scan each socket but the accept socket. Poll this * more frequently to minimize losses due to the 5 connect * limit in SunOS */ for (i = 0; i < nfds; i++) { fde *F; - int revents; - if (((revents = pfds[i].revents) == 0) || ((fd = pfds[i].fd) == -1)) + int revents = pfds[i].revents; + fd = pfds[i].fd; + if (fd == -1) + continue; + if (fd_table[fd].flags.read_pending) + revents |= POLLIN; + if (revents == 0) continue; if (fdIsIcp(fd)) { callicp = 1; @@ -632,6 +643,7 @@ int comm_select(int msec) { fd_set readfds; + fd_set pendingfds; fd_set writefds; #if DELAY_POOLS fd_set slowfds; @@ -640,6 +652,7 @@ comm_select(int msec) int fd; int maxfd; int num; + int pending; int callicp = 0, callhttp = 0; int calldns = 0; int maxindex; @@ -649,6 +662,7 @@ comm_select(int msec) int i; #endif fd_mask *fdsp; + fd_mask *pfdsp; fd_mask tmask; static time_t last_timeout = 0; struct timeval poll_time; @@ -675,7 +689,8 @@ comm_select(int msec) howmany(maxfd, FD_MASK_BITS) * FD_MASK_BYTES); xmemcpy(&writefds, &global_writefds, howmany(maxfd, FD_MASK_BITS) * FD_MASK_BYTES); - /* remove stalled FDs */ + /* remove stalled FDs, and deal with pending descriptors */ + pending = 0; maxindex = howmany(maxfd, FD_MASK_BITS); fdsp = (fd_mask *) & readfds; for (j = 0; j < maxindex; j++) { @@ -700,6 +715,10 @@ comm_select(int msec) default: fatalf("bad return value from commDeferRead(FD %d)\n", fd); } + if (FD_ISSET(fd, &readfds) && fd_table[fd].flags.read_pending) { + FD_SET(fd, &pendingfds); + pending++; + } } } #if DEBUG_FDBITS @@ -727,13 +746,15 @@ comm_select(int msec) if (msec < 0) msec = MAX_POLL_TIME; #endif + if (pending) + msec = 0; for (;;) { poll_time.tv_sec = msec / 1000; poll_time.tv_usec = (msec % 1000) * 1000; statCounter.syscalls.selects++; num = select(maxfd, &readfds, &writefds, NULL, &poll_time); statCounter.select_loops++; - if (num >= 0) + if (num >= 0 || pending > 0) break; if (ignoreErrno(errno)) break; @@ -743,10 +764,10 @@ comm_select(int msec) return COMM_ERROR; /* NOTREACHED */ } - if (num < 0) + if (num < 0 && !pending) continue; - debug(5, num ? 5 : 8) ("comm_select: %d FDs ready at %d\n", - num, (int) squid_curtime); + debug(5, num ? 5 : 8) ("comm_select: %d+%d FDs ready at %d\n", + num, pending, (int) squid_curtime); statHistCount(&statCounter.select_fds_hist, num); /* Check lifetime and timeout handlers ONCE each second. * Replaces brain-dead check every time through the loop! */ @@ -754,13 +775,14 @@ comm_select(int msec) last_timeout = squid_curtime; checkTimeouts(); } - if (num == 0) + if (num == 0 && pending == 0) continue; /* Scan return fd masks for ready descriptors */ fdsp = (fd_mask *) & readfds; + pfdsp = (fd_mask *) & pendingfds; maxindex = howmany(maxfd, FD_MASK_BITS); for (j = 0; j < maxindex; j++) { - if ((tmask = fdsp[j]) == 0) + if ((tmask = (fdsp[j] | pfdsp[j])) == 0) continue; /* no bits here */ for (k = 0; k < FD_MASK_BITS; k++) { if (tmask == 0) diff --git a/src/ssl_support.cc b/src/ssl_support.cc index a109083df5..f9c9ee66ed 100644 --- a/src/ssl_support.cc +++ b/src/ssl_support.cc @@ -1,6 +1,6 @@ /* - * $Id: ssl_support.cc,v 1.3 2001/08/26 22:02:19 hno Exp $ + * $Id: ssl_support.cc,v 1.4 2001/10/19 22:34:49 hno Exp $ * * AUTHOR: Benno Rice * DEBUG: section 81 SSL accelerator support @@ -86,8 +86,147 @@ ssl_verify_cb(int ok, X509_STORE_CTX * ctx) return ok; } +static struct ssl_option { + const char *name; + long value; +} ssl_options[] = { + + { + "MICROSOFT_SESS_ID_BUG", SSL_OP_MICROSOFT_SESS_ID_BUG + }, + { + "NETSCAPE_CHALLENGE_BUG", SSL_OP_NETSCAPE_CHALLENGE_BUG + }, + { + "NETSCAPE_REUSE_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG + }, + { + "SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG + }, + { + "MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER + }, + { + "MSIE_SSLV2_RSA_PADDING", SSL_OP_MSIE_SSLV2_RSA_PADDING + }, + { + "SSLEAY_080_CLIENT_DH_BUG", SSL_OP_SSLEAY_080_CLIENT_DH_BUG + }, + { + "TLS_D5_BUG", SSL_OP_TLS_D5_BUG + }, + { + "TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG + }, + { + "TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG + }, + { + "SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE + }, + { + "EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA + }, + { + "PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1 + }, + { + "PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2 + }, + { + "NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG + }, + { + "NON_EXPORT_FIRST", SSL_OP_NON_EXPORT_FIRST + }, + { + "NETSCAPE_DEMO_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG + }, + { + "ALL", SSL_OP_ALL + }, + { + "NO_SSLv2", SSL_OP_NO_SSLv2 + }, + { + "NO_SSLv3", SSL_OP_NO_SSLv3 + }, + { + "NO_TLSv1", SSL_OP_NO_TLSv1 + }, + { + "", 0 + }, + { + NULL, 0 + } +}; + +static long +ssl_parse_options(const char *options) +{ + long op = SSL_OP_ALL; + char *tmp; + char *option; + + if (!options) + goto no_options; + + tmp = xstrdup(options); + option = strtok(tmp, ":,"); + while (option) { + struct ssl_option *opt = NULL, *opttmp; + long value = 0; + enum { + MODE_ADD, MODE_REMOVE + } mode; + switch (*option) { + case '!': + case '-': + mode = MODE_REMOVE; + option++; + break; + case '+': + mode = MODE_ADD; + option++; + break; + default: + mode = MODE_ADD; + break; + } + for (opttmp = ssl_options; opttmp->name; opttmp++) { + if (strcmp(opttmp->name, option) == 0) { + opt = opttmp; + break; + } + } + if (opt) + value = opt->value; + else if (strncmp(option, "0x", 2) == 0) { + /* Special case.. hex specification */ + value = strtol(option + 2, NULL, 16); + } else { + fatalf("Unknown SSL option '%s'", option); + value = 0; /* Keep GCC happy */ + } + switch (mode) { + case MODE_ADD: + op |= value; + break; + case MODE_REMOVE: + op &= ~value; + break; + } + option = strtok(NULL, ":,"); + } + + safe_free(tmp); + no_options: + return op; +} + SSL_CTX * -sslLoadCert(const char *certfile, const char *keyfile) +sslCreateContext(const char *certfile, const char *keyfile, int version, const char *cipher, const char *options) { int ssl_error; SSL_METHOD *method; @@ -104,7 +243,7 @@ sslLoadCert(const char *certfile, const char *keyfile) certfile = keyfile; debug(81, 1) ("Initialising SSL.\n"); - switch (Config.SSL.version) { + switch (version) { case 2: debug(81, 5) ("Using SSLv2.\n"); method = SSLv2_server_method(); @@ -130,8 +269,16 @@ sslLoadCert(const char *certfile, const char *keyfile) fatalf("Failed to allocate SSL context: %s\n", ERR_error_string(ssl_error, NULL)); } - SSL_CTX_set_options(sslContext, SSL_OP_ALL); + SSL_CTX_set_options(sslContext, ssl_parse_options(options)); + if (cipher) { + debug(81, 5) ("Using chiper suite %s.\n", cipher); + if (!SSL_CTX_set_cipher_list(sslContext, cipher)) { + ssl_error = ERR_get_error(); + fatalf("Failed to set SSL cipher suite: %s\n", + ERR_error_string(ssl_error, NULL)); + } + } debug(81, 1) ("Using certificate in %s\n", certfile); if (!SSL_CTX_use_certificate_file(sslContext, certfile, SSL_FILETYPE_PEM)) { ssl_error = ERR_get_error(); @@ -172,7 +319,17 @@ ssl_read_method(fd, buf, len) char *buf; int len; { - return (SSL_read(fd_table[fd].ssl, buf, len)); + int i; + + i = SSL_read(fd_table[fd].ssl, buf, len); + + if (i > 0 && SSL_pending(fd_table[fd].ssl) > 0) { + debug(81, 2) ("SSL fd %d is pending\n", fd); + fd_table[fd].flags.read_pending = 1; + } else + fd_table[fd].flags.read_pending = 0; + + return i; } int @@ -183,3 +340,17 @@ ssl_write_method(fd, buf, len) { return (SSL_write(fd_table[fd].ssl, buf, len)); } + +void +ssl_shutdown_method(fd) +{ + SSL *ssl = fd_table[fd].ssl; + if (!fd_table[fd].ssl_shutdown) { + fd_table[fd].ssl_shutdown = 1; + if (Config.SSL.unclean_shutdown) + SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); + else + SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); + } + SSL_shutdown(ssl); +} diff --git a/src/ssl_support.h b/src/ssl_support.h index cb9ac22ed1..0eecee36ab 100644 --- a/src/ssl_support.h +++ b/src/ssl_support.h @@ -1,6 +1,6 @@ /* - * $Id: ssl_support.h,v 1.3 2001/10/08 16:18:33 hno Exp $ + * $Id: ssl_support.h,v 1.4 2001/10/19 22:34:49 hno Exp $ * * AUTHOR: Benno Rice * @@ -43,8 +43,9 @@ #include #endif -SSL_CTX *sslLoadCert(const char *certfile, const char *keyfile); +SSL_CTX *sslCreateContext(const char *certfile, const char *keyfile, int version, const char *cipher, const char *options); int ssl_read_method(int, char *, int); int ssl_write_method(int, const char *, int); +void ssl_shutdown_method(int); #endif /* SQUID_SSL_SUPPORT_H */ diff --git a/src/structs.h b/src/structs.h index 9f59b60f00..6f2a1ee7b4 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1,6 +1,6 @@ /* - * $Id: structs.h,v 1.405 2001/10/17 20:25:03 hno Exp $ + * $Id: structs.h,v 1.406 2001/10/19 22:34:49 hno Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -330,6 +330,9 @@ struct _https_port_list { struct sockaddr_in s; char *cert; char *key; + int version; + char *cipher; + char *options; }; #endif @@ -668,9 +671,7 @@ struct _SquidConfig { #endif #if USE_SSL struct { - char *certificate; - char *key; - int version; + int unclean_shutdown; } SSL; #endif wordlist *ext_methods; @@ -762,6 +763,7 @@ struct _fde { unsigned int called_connect:1; unsigned int nodelay:1; unsigned int close_on_exec:1; + unsigned int read_pending:1; } flags; int bytes_read; int bytes_written; @@ -789,6 +791,7 @@ struct _fde { WRITE_HANDLER *write_method; #if USE_SSL SSL *ssl; + int ssl_shutdown:1; #endif }; diff --git a/src/win32.cc b/src/win32.cc index 1d3e8f2779..fa5a8f9000 100644 --- a/src/win32.cc +++ b/src/win32.cc @@ -1,5 +1,6 @@ + /* - * $Id: win32.cc,v 1.2 2001/08/16 00:16:19 hno Exp $ + * $Id: win32.cc,v 1.3 2001/10/19 22:34:49 hno Exp $ * * * * * * * * * Legal stuff * * * * * * * *