This patch is generated from the ssl-20011020 branch of HEAD-20011020 in squid Tue Sep 9 11:52:41 2003 GMT See http://devel.squid-cache.org/ Index: squid/src/cache_cf.c diff -u squid/src/cache_cf.c:1.36 squid/src/cache_cf.c:1.33.2.5 --- squid/src/cache_cf.c:1.36 Thu Oct 18 13:52:11 2001 +++ squid/src/cache_cf.c Fri Oct 19 15:38:50 2001 @@ -2317,6 +2317,14 @@ } 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 @@ 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; } } Index: squid/src/cf.data.pre diff -u squid/src/cf.data.pre:1.37 squid/src/cf.data.pre:1.32.2.5 --- squid/src/cf.data.pre:1.37 Wed Oct 10 11:07:43 2001 +++ squid/src/cf.data.pre Fri Oct 19 05:29:18 2001 @@ -90,7 +90,7 @@ 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,23 +102,35 @@ 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 Index: squid/src/client_side.c diff -u squid/src/client_side.c:1.39 squid/src/client_side.c:1.34.2.3 --- squid/src/client_side.c:1.39 Thu Oct 18 13:52:11 2001 +++ squid/src/client_side.c Fri Oct 19 05:29:18 2001 @@ -3491,7 +3491,7 @@ 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); Index: squid/src/comm.c diff -u squid/src/comm.c:1.16 squid/src/comm.c:1.13.2.3 --- squid/src/comm.c:1.16 Thu Oct 18 13:52:11 2001 +++ squid/src/comm.c Fri Oct 19 05:29:18 2001 @@ -602,7 +602,7 @@ { #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 @@ 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); Index: squid/src/comm_select.c diff -u squid/src/comm_select.c:1.6 squid/src/comm_select.c:1.6.14.2 --- squid/src/comm_select.c:1.6 Fri May 4 06:39:12 2001 +++ squid/src/comm_select.c Fri Oct 19 07:13:10 2001 @@ -317,6 +317,7 @@ 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_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 @@ 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 @@ 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 @@ comm_select(int msec) { fd_set readfds; + fd_set pendingfds; fd_set writefds; #if DELAY_POOLS fd_set slowfds; @@ -640,6 +652,7 @@ int fd; int maxfd; int num; + int pending; int callicp = 0, callhttp = 0; int calldns = 0; int maxindex; @@ -649,6 +662,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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) Index: squid/src/ssl_support.c diff -u squid/src/ssl_support.c:1.4 squid/src/ssl_support.c:1.3.14.10 --- squid/src/ssl_support.c:1.4 Sun Aug 26 15:24:05 2001 +++ squid/src/ssl_support.c Fri Oct 19 15:38:51 2001 @@ -86,8 +86,147 @@ 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 @@ 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 @@ 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 @@ 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 @@ -182,4 +339,18 @@ int 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); } Index: squid/src/ssl_support.h diff -u squid/src/ssl_support.h:1.4 squid/src/ssl_support.h:1.3.14.3 --- squid/src/ssl_support.h:1.4 Tue Oct 9 14:18:00 2001 +++ squid/src/ssl_support.h Fri Oct 19 05:29:19 2001 @@ -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 */ Index: squid/src/structs.h diff -u squid/src/structs.h:1.45 squid/src/structs.h:1.38.4.5 --- squid/src/structs.h:1.45 Thu Oct 18 13:52:11 2001 +++ squid/src/structs.h Fri Oct 19 05:52:08 2001 @@ -330,6 +330,9 @@ struct sockaddr_in s; char *cert; char *key; + int version; + char *cipher; + char *options; }; #endif @@ -668,9 +671,7 @@ #endif #if USE_SSL struct { - char *certificate; - char *key; - int version; + int unclean_shutdown; } SSL; #endif wordlist *ext_methods; @@ -762,6 +763,7 @@ 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 @@ WRITE_HANDLER *write_method; #if USE_SSL SSL *ssl; + int ssl_shutdown:1; #endif }; squid-ssl-20011020-HEAD-20011020.new squid-ssl-20011020-HEAD-20011020 differ: char 80, line 2