From a9faabd203906c615d9dbb5d8b90f5779ce61f4a Mon Sep 17 00:00:00 2001
From: Thomas Reim
Date: Thu, 1 Apr 2021 05:19:56 +0200
Subject: [PATCH 01/14] OpenSSL/libssl does only allow to disable TLS protocols
Cherokee Admin suggests system administrators that they have to enable wanted
SSL/TLS protocols for traffic encryption. In general, OpenSSL accepts all
SSL/TLS protocols requested by clients that are supported if the provided cipher
set and certificates fit.
- Add a new checkbox widget type CheckCfgTextInv with following behaviour:
------------------------------------------
|Checkbox Status|Configuration File Value|
------------------------------------------
| Checked | 0 |
------------------------------------------
| Not Checked | 1 |
------------------------------------------
- Update Cherokee Admin Advanced page to use the new checkbox
- Add a new style for Notice widgets:
Slim boxes with 50% width compared to normal boxes
Fixes: https://github.com/cherokee/webserver/issues/1254
Signed-off-by: Thomas Reim
---
admin/CTK/CTK/Checkbox.py | 46 +++++++++++++++++++++++++++++---
admin/CTK/static/css/CTK.css | 24 +++++++++++++++++
admin/CTK/static/js/Submitter.js | 6 ++++-
admin/PageAdvanced.py | 22 ++++++++-------
4 files changed, 84 insertions(+), 14 deletions(-)
diff --git a/admin/CTK/CTK/Checkbox.py b/admin/CTK/CTK/Checkbox.py
index 058b97504..ea7438946 100644
--- a/admin/CTK/CTK/Checkbox.py
+++ b/admin/CTK/CTK/Checkbox.py
@@ -88,6 +88,8 @@ class CheckCfg (Checkbox):
checkbox with the value of the configuration tree given by key
argument if it exists. It accepts properties to pass to the base
Checkbox object.
+ As an option the checkbox setting can be inverted to show checked
+ for False instead of True.
Arguments:
key: key in the configuration tree.
@@ -103,12 +105,16 @@ def __init__ (self, key, default, props=None):
if not props:
props = {}
+ self.invert = False
+ if props.has_key('mode') and props['mode'] == 'inverse':
+ self.invert = True
+
# Read the key value
val = cfg.get_val(key)
- if not val:
- props['checked'] = "01"[bool(int(default))]
+ if not val or (props.has_key('disabled') and int(props['disabled'])):
+ props['checked'] = '01'[bool(int(default))]
elif val.isdigit():
- props['checked'] = "01"[bool(int(val))]
+ props['checked'] = ['01', '10'][self.invert][bool(int(val))]
else:
assert False, "Could not handle value: %s"%(val)
@@ -159,6 +165,40 @@ def __init__ (self, key, default, text='Enabled', props=None):
assert type(props) in (dict, type(None))
CheckCfg.__init__ (self, key, default, props)
+ if not props:
+ props = {}
+ self.text = text
+ self.disabled = False
+ if props.has_key('disabled') and int(props['disabled']):
+ self.disabled = True
+
+ def Render (self):
+ render = CheckCfg.Render (self)
+ render.html = '' %(self.id, render.html, self.text)
+ if not self.disabled:
+ render.js += CLICK_CHANGE_JS %(self.id)
+ return render
+
+
+class CheckCfgTextInv (CheckCfg):
+ """
+ Configuration-Tree based CheckboxText widget. Populates the input
+ checkbox with the inverted value of the configuration tree given by key
+ argument if it exists. It accepts properties to pass to the base
+ CheckboxText object.
+
+ Arguments:
+ key: key in the configuration tree.
+ default: default value to give to the checkbox.
+ text: text to show beside the checkbox (by default, 'Enabled')
+ props: additional properties for base CheckboxText.
+ """
+ def __init__ (self, key, default, text='Enabled', props=None):
+ assert type(default) == bool
+ assert type(text) == str
+ assert type(props) in (dict, type(None))
+
+ CheckCfg.__init__ (self, key, default, props, True)
self.text = text
def Render (self):
diff --git a/admin/CTK/static/css/CTK.css b/admin/CTK/static/css/CTK.css
index 27dfd2380..5e79686d1 100644
--- a/admin/CTK/static/css/CTK.css
+++ b/admin/CTK/static/css/CTK.css
@@ -202,6 +202,14 @@ button:hover { cursor: pointer; }
padding: 1em 1em 1em 40px;
margin: 1em 0;
}
+.dialog-information-slim {
+ background: #f9fcff url(/CTK/images/dialog-information.png) 4px 4px no-repeat;
+ -moz-border-radius: 4px; -webkit-border-radius: 4px;
+ border: 1px solid #d4e1f1;
+ padding: 1em 1em 1em 40px;
+ margin: 1em 0;
+ width: 50%
+}
.dialog-important-information {
background: #e9ffd4 url(/CTK/images/dialog-information.png) 4px 4px no-repeat;
-moz-border-radius: 4px; -webkit-border-radius: 4px;
@@ -216,6 +224,14 @@ button:hover { cursor: pointer; }
padding: 1em 1em 1em 40px;
margin: 1em 0;
}
+.dialog-warning-slim {
+ background: #fff2b3 url(/CTK/images/dialog-warning.png) 4px 4px no-repeat;
+ -moz-border-radius: 4px; -webkit-border-radius: 4px;
+ border: 1px solid #b90;
+ padding: 1em 1em 1em 40px;
+ margin: 1em 0;
+ width: 50%
+}
.dialog-error {
background: #ffe7e7 url(/CTK/images/dialog-error.png) 4px 4px no-repeat;
-moz-border-radius: 4px; -webkit-border-radius: 4px;
@@ -223,6 +239,14 @@ button:hover { cursor: pointer; }
padding: 1em 1em 1em 40px;
margin: 1em 0;
}
+.dialog-error-slim {
+ background: #ffe7e7 url(/CTK/images/dialog-error.png) 4px 4px no-repeat;
+ -moz-border-radius: 4px; -webkit-border-radius: 4px;
+ border: 1px solid #c00;
+ padding: 1em 1em 1em 40px;
+ margin: 1em 0;
+ width: 50%
+}
.dialog-information h2,
.dialog-warning h2,
diff --git a/admin/CTK/static/js/Submitter.js b/admin/CTK/static/js/Submitter.js
index 71e1c668b..9bd59fd68 100644
--- a/admin/CTK/static/js/Submitter.js
+++ b/admin/CTK/static/js/Submitter.js
@@ -125,7 +125,11 @@ if ((typeof submitter_loaded) == 'undefined') {
}
});
self.find ("input:checkbox").each(function(){
- info[this.name] = this.checked ? "1" : "0";
+ if (this.hasAttribute('mode') && this.getAttribute('mode') == 'inverse') {
+ info[this.name] = this.checked ? "0" : "1";
+ } else {
+ info[this.name] = this.checked ? "1" : "0";
+ }
});
self.find ("select").each(function(){
info[this.name] = $(this).val();
diff --git a/admin/PageAdvanced.py b/admin/PageAdvanced.py
index 21a9d0c91..b1d2dea34 100644
--- a/admin/PageAdvanced.py
+++ b/admin/PageAdvanced.py
@@ -95,11 +95,11 @@
NOTE_DH2048 = N_('Path to a Diffie Hellman (DH) parameters PEM file: 2048 bits.')
NOTE_DH4096 = N_('Path to a Diffie Hellman (DH) parameters PEM file: 4096 bits.')
NOTE_TLS_TIMEOUT = N_('Timeout for the TLS/SSL handshake. Default: 15 seconds.')
-NOTE_TLS_SSLv2 = N_('Allow clients to use SSL version 2 - Beware: it is vulnerable. (Default: No)')
-NOTE_TLS_SSLv3 = N_('Allow clients to use SSL version 3 - Beware: it is vulnerable. (Default: No)')
-NOTE_TLS_TLSv1 = N_('Allow clients to use TLS version 1 (Default: Yes)')
-NOTE_TLS_TLSv1_1 = N_('Allow clients to use TLS version 1.1 (Default: Yes)')
-NOTE_TLS_TLSv1_2 = N_('Allow clients to use TLS version 1.2 (Default: Yes)')
+NOTE_TLS_SSLv2 = N_('Beware: it is vulnerable. You should disable SSLv2.')
+NOTE_TLS_SSLv3 = N_('Beware: it is vulnerable. You should disable SSLv3.')
+NOTE_TLS_TLSv1 = N_('TLSv1 is deprecated')
+NOTE_TLS_TLSv1_1 = N_('TLSv1.1 is deprecated')
+NOTE_TLS_TLSv1_2 = N_(' ')
HELPS = [('config_advanced', N_('Advanced'))]
@@ -179,12 +179,14 @@ class TLSWidget (CTK.Container):
def __init__ (self):
CTK.Container.__init__ (self)
+ props = {}
+ props = {'mode': 'inverse'}
table = CTK.PropsAuto(URL_APPLY)
- table.Add (_('SSL version 2'), CTK.CheckCfgText('server!tls!protocol!SSLv2', False, _("Allow")), _(NOTE_TLS_SSLv2))
- table.Add (_('SSL version 3'), CTK.CheckCfgText('server!tls!protocol!SSLv3', False, _("Allow")), _(NOTE_TLS_SSLv3))
- table.Add (_('TLS version 1'), CTK.CheckCfgText('server!tls!protocol!TLSv1', True, _("Allow")), _(NOTE_TLS_TLSv1))
- table.Add (_('TLS version 1.1'), CTK.CheckCfgText('server!tls!protocol!TLSv1_1', True, _("Allow")), _(NOTE_TLS_TLSv1_1))
- table.Add (_('TLS version 1.2'), CTK.CheckCfgText('server!tls!protocol!TLSv1_2', True, _("Allow")), _(NOTE_TLS_TLSv1_2))
+ table.Add (_('SSL version 2'), CTK.CheckCfgText('server!tls!protocol!SSLv2', True, _("Disable"), props), _(NOTE_TLS_SSLv2))
+ table.Add (_('SSL version 3'), CTK.CheckCfgText('server!tls!protocol!SSLv3', True, _("Disable"), props), _(NOTE_TLS_SSLv3))
+ table.Add (_('TLS version 1'), CTK.CheckCfgText('server!tls!protocol!TLSv1', False, _("Disable"), props), _(NOTE_TLS_TLSv1))
+ table.Add (_('TLS version 1.1'), CTK.CheckCfgText('server!tls!protocol!TLSv1_1', False, _("Disable"), props), _(NOTE_TLS_TLSv1_1))
+ table.Add (_('TLS version 1.2'), CTK.CheckCfgText('server!tls!protocol!TLSv1_2', False, _("Disable"), props), _(NOTE_TLS_TLSv1_2))
table.Add (_('Handshake Timeout'), CTK.TextCfg('server!tls!timeout_handshake', True), _(NOTE_TLS_TIMEOUT))
table.Add (_('DH parameters: 512 bits'), CTK.TextCfg('server!tls!dh_param512', True), _(NOTE_DH512))
table.Add (_('DH parameters: 1024 bits'), CTK.TextCfg('server!tls!dh_param1024', True), _(NOTE_DH1024))
From a85ca09d09e325c4c3ce134e4220c96041d4637a Mon Sep 17 00:00:00 2001
From: Thomas Reim
Date: Thu, 1 Apr 2021 03:48:05 +0200
Subject: [PATCH 02/14] Cherokee ignores SSL/TLS protocols supported by OpenSSL
SSL/TLS protocols are hardcoded in Cherokee. Neither at build time nor at run-
time SSL/TLS protocols that are supported by the OpenSSL back-end are being
checked. This may lead to the dangerous situation that OpenSSL encrypts HTTPS
traffic using an SSL/TLS encryption, which is not explicitly supported by
Cherokee. Current Cherokee for example does not support TLS protocol version
1.3, which requires ciphersuites for encryption that cannot be configured by
Cherokee.
More and more OS distribution maintainers now control security of their OpenSSL
packages by deactivating unsafe SSL/TLS protocols at build time. For system
administrators it is very difficult to identify the root cause for rejected
HTTPS communication requests due to suddenly unavailable SSL/TLS protocols.
OpenSSL provides only pretty cryptic notifications.
This patch implements following improvements:
- Check SSL/TLS protocols supported by OpenSSL at build time
- configure Displays and logs supported protocols
- Abort build with error message if unsupported protocols are detected
- Check SSL/TLS protocols supported by the actual OpenSSL back-end at runtime
- Log an error message if unsupported protocols are detected
- Command-line option -i provides more detailed information about OpenSSL
+ Build version and actually used version
+ Supported SSL/TLS protocols
+ Maintainer deactivated protocols
- Make SSL/TLS protocol information available to Cherokee Admin scripts
- Fix Cherokee Admin Advanced page to outline support of SSL/TLS protocols:
+ Mark deactivated protocols
+ Warn users if SSL/TLS protocols are detected that are not supported by
Cherokee
+ Inform users if OpenSSL/libssl is not supported at all
Fixes: https://github.com/cherokee/webserver/issues/1255
Signed-off-by: Thomas Reim
---
admin/Makefile.am | 8 +-
admin/PageAdvanced.py | 78 +++++++++--
admin/configured.py.pre | 1 +
admin/server.py | 42 ++++--
cherokee/Makefile.am | 6 +-
cherokee/cryptor.c | 16 +++
cherokee/cryptor.h | 7 +
cherokee/cryptor_libssl.c | 134 ++++++++++++++++++
cherokee/error_list.py | 6 +
cherokee/info.c | 41 +++++-
cherokee/main_admin.c | 115 +++++++++++++++-
cherokee/main_worker.c | 283 ++++++++++++++++++++++++++++++++++++++
configure.ac | 49 ++++++-
13 files changed, 748 insertions(+), 38 deletions(-)
diff --git a/admin/Makefile.am b/admin/Makefile.am
index 0269073cb..3109e7287 100644
--- a/admin/Makefile.am
+++ b/admin/Makefile.am
@@ -8,8 +8,14 @@ endif
SUFFIXES = .py.pre .py
+if USE_OPENSSL
+tlsprotocols = @OPENSSL_TLS_PROTOCOLS@
+else
+tlsprotocols =
+endif
+
.py.pre.py:
- sed -e "s|%sysconfdir%|${sysconfdir}|g; s|%sbindir%|${sbindir}|g; s|%docdir%|${docdir}|g; s|%prefix%|${prefix}|g; s|%localstatedir%|${localstatedir}|g; s|%libdir%|${libdir}|g; s|%wwwroot%|${WWW_ROOT}|g; s|%version%|${PACKAGE_VERSION}|g; s|%phpcgi%|${PHPCGI}|g; s|%datadir%|${datadir}|g; s|%localedir%|${localedir}|g" $< > $@
+ sed -e "s|%sysconfdir%|${sysconfdir}|g; s|%sbindir%|${sbindir}|g; s|%docdir%|${docdir}|g; s|%prefix%|${prefix}|g; s|%localstatedir%|${localstatedir}|g; s|%libdir%|${libdir}|g; s|%wwwroot%|${WWW_ROOT}|g; s|%version%|${PACKAGE_VERSION}|g; s|%phpcgi%|${PHPCGI}|g; s|%datadir%|${datadir}|g; s|%localedir%|${localedir}|g; s|%tlsprotocols%|$(tlsprotocols)|g" $< > $@
PY_PRE = \
configured.py.pre
diff --git a/admin/PageAdvanced.py b/admin/PageAdvanced.py
index b1d2dea34..7b977e833 100644
--- a/admin/PageAdvanced.py
+++ b/admin/PageAdvanced.py
@@ -34,6 +34,29 @@
URL_BASE = '/advanced'
URL_APPLY = '/advanced/apply'
+KNOWN_TLS_PROTOCOLS = [
+ ('SSLv2', 'SSL version 2'),
+ ('SSLv3', 'SSL version 3'),
+ ('TLSv1', 'TLS version 1'),
+ ('TLSv1.1', 'TLS version 1.1'),
+ ('TLSv1.2', 'TLS version 1.2')
+]
+
+# Keep aligned with cherokee_cryptor_init_base()
+TLS_PROTOCOL_CHECKBOX_DEFAULT = {
+ 'SSLv2': True,
+ 'SSLv3': True,
+ 'TLSv1': False,
+ 'TLSv1.1': False,
+ 'TLSv1.2': False
+}
+
+TLS_WARNING = N_("""WARNING: The SSL/TLS back-end supports more recent
+versions of TLS protocols, which are not recognized by Cherokee. Please check
+for latest Cherokee updates or report this issue. As a temporary workaround please
+set 'Max. TLS protocol version' to the maximum TLS protocol version supported by
+Cherokee.
Following unknown TLS protocols have been found:""")
+
VALIDATIONS = [
("server!fdlimit", validations.is_positive_int),
("server!pid_file", validations.can_create_file),
@@ -94,6 +117,7 @@
NOTE_DH1024 = N_('Path to a Diffie Hellman (DH) parameters PEM file: 1024 bits.')
NOTE_DH2048 = N_('Path to a Diffie Hellman (DH) parameters PEM file: 2048 bits.')
NOTE_DH4096 = N_('Path to a Diffie Hellman (DH) parameters PEM file: 4096 bits.')
+NOTE_TLS_NA = N_('Your Cherokee Web Server does not support SSL/TLS encryption.')
NOTE_TLS_TIMEOUT = N_('Timeout for the TLS/SSL handshake. Default: 15 seconds.')
NOTE_TLS_SSLv2 = N_('Beware: it is vulnerable. You should disable SSLv2.')
NOTE_TLS_SSLv3 = N_('Beware: it is vulnerable. You should disable SSLv3.')
@@ -101,6 +125,14 @@
NOTE_TLS_TLSv1_1 = N_('TLSv1.1 is deprecated')
NOTE_TLS_TLSv1_2 = N_(' ')
+TLS_PROTOCOL_NOTES = {
+ 'SSLv2': NOTE_TLS_SSLv2,
+ 'SSLv3': NOTE_TLS_SSLv3,
+ 'TLSv1': NOTE_TLS_TLSv1,
+ 'TLSv1.1': NOTE_TLS_TLSv1_1,
+ 'TLSv1.2': NOTE_TLS_TLSv1_2
+}
+
HELPS = [('config_advanced', N_('Advanced'))]
JS_SCROLL = """
@@ -176,25 +208,53 @@ def __init__ (self):
self += CTK.Indenter(table)
class TLSWidget (CTK.Container):
+ @staticmethod
+ def check_for_tls_protocol_versions (self):
+ available_tls_protocols = {}
+ unknown_tls_protocols = []
+ tls_option_list = TLSPROTOCOLS.split(',')
+ if len(tls_option_list) > 1:
+ for protocol in tls_option_list:
+ for known_protocol in KNOWN_TLS_PROTOCOLS:
+ if known_protocol[0] == protocol:
+ available_tls_protocols[protocol] = known_protocol[1]
+ break
+ else:
+ unknown_tls_protocols.append(protocol)
+ return available_tls_protocols, unknown_tls_protocols
+
+ @staticmethod
+ def add_tls_version_config (self, table):
+ props = {}
+ props = {'mode': 'inverse'}
+ props_disable = props.copy()
+ props_disable["disabled"] = 1
+ for protocol in KNOWN_TLS_PROTOCOLS:
+ if self.available_tls_protocols.has_key(protocol[0]):
+ table.Add ('%s%s' % (_('Disable '), self.available_tls_protocols[protocol[0]]), CTK.CheckCfgText('server!tls!protocol!' + protocol[0].replace('.','_'), TLS_PROTOCOL_CHECKBOX_DEFAULT[protocol[0]], _("Disable"), props), TLS_PROTOCOL_NOTES[protocol[0]])
+ else:
+ table.Add ('%s%s' % (_('Disable '), protocol[1]), CTK.CheckCfgText('server!tls!protocol!' + protocol[0].replace('.','_'), True, _("Protocol deacrivated") if protocol[0].startswith('SSLv') else _("Protocol not supported"), props_disable), TLS_PROTOCOL_NOTES[protocol[0]])
+
def __init__ (self):
CTK.Container.__init__ (self)
+ self.available_tls_protocols, self.unknown_tls_protocols = TLSWidget.check_for_tls_protocol_versions (self)
- props = {}
- props = {'mode': 'inverse'}
table = CTK.PropsAuto(URL_APPLY)
- table.Add (_('SSL version 2'), CTK.CheckCfgText('server!tls!protocol!SSLv2', True, _("Disable"), props), _(NOTE_TLS_SSLv2))
- table.Add (_('SSL version 3'), CTK.CheckCfgText('server!tls!protocol!SSLv3', True, _("Disable"), props), _(NOTE_TLS_SSLv3))
- table.Add (_('TLS version 1'), CTK.CheckCfgText('server!tls!protocol!TLSv1', False, _("Disable"), props), _(NOTE_TLS_TLSv1))
- table.Add (_('TLS version 1.1'), CTK.CheckCfgText('server!tls!protocol!TLSv1_1', False, _("Disable"), props), _(NOTE_TLS_TLSv1_1))
- table.Add (_('TLS version 1.2'), CTK.CheckCfgText('server!tls!protocol!TLSv1_2', False, _("Disable"), props), _(NOTE_TLS_TLSv1_2))
+ TLSWidget.add_tls_version_config (self, table)
table.Add (_('Handshake Timeout'), CTK.TextCfg('server!tls!timeout_handshake', True), _(NOTE_TLS_TIMEOUT))
table.Add (_('DH parameters: 512 bits'), CTK.TextCfg('server!tls!dh_param512', True), _(NOTE_DH512))
table.Add (_('DH parameters: 1024 bits'), CTK.TextCfg('server!tls!dh_param1024', True), _(NOTE_DH1024))
table.Add (_('DH parameters: 2048 bits'), CTK.TextCfg('server!tls!dh_param2048', True), _(NOTE_DH2048))
table.Add (_('DH parameters: 4096 bits'), CTK.TextCfg('server!tls!dh_param4096', True), _(NOTE_DH4096))
-
self += CTK.RawHTML ("
%s
" %(_('TLS')))
- self += CTK.Indenter(table)
+ if len(self.unknown_tls_protocols) > 0:
+ tip = '%s %s
' % (_(TLS_WARNING), join(self.unknown_tls_protocols))
+ props = {'class': 'dialog-error-slim'}
+ self += CTK.Notice ('error', CTK.RawHTML(tip), props)
+ if len(self.available_tls_protocols) == 0:
+ self += CTK.RawHTML ("%s
" %(_(NOTE_TLS_NA)))
+ else:
+ self += CTK.Indenter(table)
class Render:
def __call__ (self):
diff --git a/admin/configured.py.pre b/admin/configured.py.pre
index 009f24a56..364c9031f 100644
--- a/admin/configured.py.pre
+++ b/admin/configured.py.pre
@@ -9,6 +9,7 @@ WWWROOT = "%wwwroot%"
SYSCONFDIR = "%sysconfdir%"
LOCALSTATE = "%localstatedir%"
VERSION = "%version%"
+TLSPROTOCOLS = "%tlsprotocols%"
CHEROKEE_SERVER = join (PREFIX, "sbin/cherokee")
CHEROKEE_WORKER = join (PREFIX, "sbin/cherokee-worker")
diff --git a/admin/server.py b/admin/server.py
index 26c09964f..596f572d3 100755
--- a/admin/server.py
+++ b/admin/server.py
@@ -36,6 +36,8 @@
import socket
import signal
import thread
+import re
+import argparse
# Import CTK
sys.path.append (os.path.abspath (os.path.realpath(__file__) + '/../CTK'))
@@ -43,13 +45,13 @@
# Cherokee imports
import config_version
-from configured import *
+import configured
import PageError
def init (scgi_port, cfg_file):
# Translation support
- CTK.i18n.install ('cherokee', LOCALEDIR, unicode=True)
+ CTK.i18n.install ('cherokee', configured.LOCALEDIR, unicode=True)
# Ensure SIGCHLD is set. It needs to receive the signal in order
# to detect when its child processes finish.
@@ -61,7 +63,7 @@ def init (scgi_port, cfg_file):
os.chdir(os.path.abspath(pathname))
# Let the user know what is going on
- version = VERSION
+ version = configured.VERSION
pid = os.getpid()
if scgi_port.isdigit():
@@ -137,17 +139,39 @@ def trace_callback (sig, stack):
if __name__ == "__main__":
# Read the arguments
+ parser = argparse.ArgumentParser()
+ parser.add_argument("SCGI_SOCKET", help="Cherokee admin server SCGI socket (TCP port or socket path)")
+ parser.add_argument("CONFIG_FILE", help="path to Cherokee webserver configuration file to modify")
+ parser.add_argument("-x", "--debug", help="print server backend errors to the terminal", action="store_true")
+ parser.add_argument("-P", "--tls-protocols",
+ help="tells server which SSL/TLS protocol versions are supported by the TLS back-end (comma-separated list) "
+ "TLS_PROTOCOLS have to be specified in OpenSSL options format, e. g. tls1_3,tls1,ssl3")
try:
- scgi_port = sys.argv[1]
- cfg_file = sys.argv[2]
+ args = parser.parse_args()
except:
- print _("Incorrect parameters: PORT CONFIG_FILE")
+ parser.print_help()
raise SystemExit
+ scgi_port = args.SCGI_SOCKET
+ cfg_file = args.CONFIG_FILE
# Debugging mode
- if '-x' in sys.argv:
+ if args.debug == True:
debug_set_up()
+ # SSL/TLS protocol version list from TLS back-end
+ if not args.tls_protocols == None:
+ configured.TLSPROTOCOLS = args.tls_protocols
+
+ # Check and reformat SSL/TSL protocol list for module PageAdvanced
+ p = re.compile('([0-9]+)[_]([0-9]+)')
+ pp = re.compile('([0-9]+)')
+ tls_option_list = []
+ for protocol in args.tls_protocols.split(','):
+ tls_option_list.append(pp.sub("v\\1", p.sub("\\1.\\2", protocol.strip()).upper(), 1))
+ if len(tls_option_list) > 1:
+ configured.TLSPROTOCOLS = ','
+ configured.TLSPROTOCOLS = configured.TLSPROTOCOLS.join(tls_option_list)
+
# Init
init (scgi_port, cfg_file)
@@ -192,9 +216,9 @@ def are_vsrvs_num():
CTK.unpublish (r'')
- for path in (CHEROKEE_WORKER, CHEROKEE_SERVER, CHEROKEE_ICONSDIR):
+ for path in (configured.CHEROKEE_WORKER, configured.CHEROKEE_SERVER, configured.CHEROKEE_ICONSDIR):
if not os.path.exists (path):
- CTK.publish (r'', PageError.ResourceMissing, path=CHEROKEE_WORKER)
+ CTK.publish (r'', PageError.ResourceMissing, path=configured.CHEROKEE_WORKER)
while not os.path.exists (path):
CTK.step()
diff --git a/cherokee/Makefile.am b/cherokee/Makefile.am
index b4537216d..0cf0df0a9 100644
--- a/cherokee/Makefile.am
+++ b/cherokee/Makefile.am
@@ -1264,7 +1264,8 @@ $(static_validator_htpasswd_flags) \
$(static_validator_mysql_flags) \
$(static_cryptor_libssl_flags) \
$(static_handler_dbslayer_flags) \
-$(static_handler_streaming_flags)
+$(static_handler_streaming_flags) \
+$(LIBSSL_CFLAGS)
libcherokee_server_la_LIBADD = \
$(PTHREAD_LIBS) \
@@ -1731,7 +1732,8 @@ cherokee_worker_LDFLAGS = -export-dynamic
cherokee_worker_LDADD = \
$(libcherokee_server_la_LIBADD) \
libcherokee-base.la \
-libcherokee-server.la
+libcherokee-server.la \
+$(external_pcre_lib)
#
# Cherokee
diff --git a/cherokee/cryptor.c b/cherokee/cryptor.c
index 4ae92fddf..843589956 100644
--- a/cherokee/cryptor.c
+++ b/cherokee/cryptor.c
@@ -44,9 +44,11 @@ cherokee_cryptor_init_base (cherokee_cryptor_t *cryp,
cryp->vserver_new = NULL;
cryp->socket_new = NULL;
cryp->configure = NULL;
+ cryp->tls_info = NULL;
/* Properties
*/
+ cryp->hardcoded_ssl_options = 0;
cryp->timeout_handshake = TIMEOUT_DEFAULT;
cryp->allow_SSLv2 = false;
cryp->allow_SSLv3 = false;
@@ -277,3 +279,17 @@ cherokee_cryptor_client_init (cherokee_cryptor_client_t *cryp,
return CRYPTOR_SOCKET(cryp)->init_tls (cryp, host, socket, NULL, &foo);
}
+
+ret_t
+cherokee_cryptor_tls_backend_info (cherokee_cryptor_t *cryp,
+ cherokee_buffer_t *backend_info_buf,
+ cherokee_buffer_t *tls_protocols_buf,
+ cherokee_buffer_t *deactivated_protocols_buf)
+{
+ if (unlikely (cryp->tls_info == NULL))
+ return ret_error;
+
+ return cryp->tls_info (cryp, backend_info_buf,
+ tls_protocols_buf,
+ deactivated_protocols_buf);
+}
diff --git a/cherokee/cryptor.h b/cherokee/cryptor.h
index 1adfa97e4..ca5c590f7 100644
--- a/cherokee/cryptor.h
+++ b/cherokee/cryptor.h
@@ -45,6 +45,7 @@ typedef ret_t (* cryptor_func_configure_t) (void *cryp, cherokee_config_node_
typedef ret_t (* cryptor_func_vserver_new_t) (void *cryp, void *vsrv, void **vserver_crypt);
typedef ret_t (* cryptor_func_socket_new_t) (void *cryp, void **socket_crypt);
typedef ret_t (* cryptor_func_client_new_t) (void *cryp, void **client_crypt);
+typedef ret_t (* cryptor_func_tls_info_t) (void *cryp, cherokee_buffer_t *backend_info_buf, cherokee_buffer_t *tls_protocols_buf, cherokee_buffer_t *deactivated_protocols_buf);
/* Cryptor: Virtual server */
typedef ret_t (* cryptor_vsrv_func_free_t) (void *cryp);
@@ -65,6 +66,7 @@ typedef ret_t (* cryptor_client_func_init_t) (void *cryp, void *host, void *
*/
typedef struct {
cherokee_module_t module;
+ long hardcoded_ssl_options;
cint_t timeout_handshake;
cherokee_boolean_t allow_SSLv2;
cherokee_boolean_t allow_SSLv3;
@@ -77,6 +79,7 @@ typedef struct {
cryptor_func_vserver_new_t vserver_new;
cryptor_func_socket_new_t socket_new;
cryptor_func_client_new_t client_new;
+ cryptor_func_tls_info_t tls_info;
} cherokee_cryptor_t;
typedef struct {
@@ -148,6 +151,10 @@ ret_t cherokee_cryptor_socket_new (cherokee_cryptor_t *cryp,
ret_t cherokee_cryptor_client_new (cherokee_cryptor_t *cryp,
cherokee_cryptor_client_t **cryp_client);
+ret_t cherokee_cryptor_tls_backend_info (cherokee_cryptor_t *cryp,
+ cherokee_buffer_t *backend_info_buf,
+ cherokee_buffer_t *tls_protocols_buf,
+ cherokee_buffer_t *deactivated_protocols_buf);
/* Cryptor: Virtual Server
*/
diff --git a/cherokee/cryptor_libssl.c b/cherokee/cryptor_libssl.c
index 80a5c2ebc..16b03b468 100644
--- a/cherokee/cryptor_libssl.c
+++ b/cherokee/cryptor_libssl.c
@@ -68,6 +68,10 @@ static DH *dh_param_4096 = NULL;
ERR_error_string(openssl_error, NULL)); \
} \
} while(0)
+#define CRYPTOR_VSRV_SSL_OPTIONS(v) \
+ VSERVER_SRV(v)->cryptor->hardcoded_ssl_options
+#define PROTOCOL_SWITCHED_OFF(prot, opt, vsrv) \
+ ((opt & prot) | (CRYPTOR_VSRV_SSL_OPTIONS(vsrv) & prot)) > 0
static ret_t
@@ -1258,6 +1262,135 @@ _client_new (cherokee_cryptor_t *cryp,
return ret_ok;
}
+static ret_t
+_tls_backend_info (cherokee_cryptor_t *cryp,
+ cherokee_buffer_t *backend_info_buf,
+ cherokee_buffer_t *tls_protocols_buf,
+ cherokee_buffer_t *deactivated_protocols_buf)
+{
+ SSL_CTX *ctx;
+ SSL *session;
+ long options;
+ int version;
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ cherokee_buffer_add_va (backend_info_buf, "%s",
+ SSLeay_version (SSLEAY_VERSION));
+
+ ctx = SSL_CTX_new (SSLv23_server_method());
+#else
+ cherokee_buffer_add_va (backend_info_buf, "%s",
+ OpenSSL_version (OPENSSL_VERSION));
+
+ ctx = SSL_CTX_new (TLS_server_method());
+#endif
+ if (ctx == NULL) {
+ goto error;
+ }
+ session = SSL_new (ctx);
+ if (session == NULL) {
+ goto error;
+ }
+
+ SSL_set_accept_state (session);
+
+ options = SSL_CTX_get_options (ctx);
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+# ifdef SSL_TXT_SSLV3
+# ifdef SSL_OP_NO_SSLv3
+ if ((options & SSL_OP_NO_SSLv3) == 0) {
+ cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_SSLV3);
+ }
+# else
+ cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_SSLV3);
+# endif
+# ifdef SSL_OP_NO_SSLv3
+ if ((cryp->hardcoded_ssl_options & SSL_OP_NO_SSLv3) > 0) {
+ cherokee_buffer_add_str (deactivated_protocols_buf, SSL_TXT_SSLV3);
+ }
+# endif
+# endif
+# ifdef SSL_TXT_TLSV1
+# ifdef SSL_OP_NO_TLSv1
+ if ((options & SSL_OP_NO_TLSv1) == 0) {
+ if (! cherokee_buffer_is_empty(tls_protocols_buf)) {
+ cherokee_buffer_add_str (tls_protocols_buf, ", ");
+ }
+ cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_TLSV1);
+ }
+# else
+ if (! cherokee_buffer_is_empty(tls_protocols_buf)) {
+ cherokee_buffer_add_str (tls_protocols_buf, ", ");
+ }
+ cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_TLSV1);
+# endif
+# ifdef SSL_OP_NO_TLSv1
+ if ((cryp->hardcoded_ssl_options & SSL_OP_NO_TLSv1) > 0) {
+ if (! cherokee_buffer_is_empty(deactivated_protocols_buf)) {
+ cherokee_buffer_add_str (deactivated_protocols_buf, ", ");
+ }
+ cherokee_buffer_add_str (deactivated_protocols_buf, SSL_TXT_TLSV1);
+ }
+# endif
+# endif
+# ifdef SSL_OP_NO_TLSv1_1
+ if ((options & SSL_OP_NO_TLSv1_1) == 0) {
+ if (! cherokee_buffer_is_empty(tls_protocols_buf)) {
+ cherokee_buffer_add_str (tls_protocols_buf, ", ");
+ }
+ cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_TLSV1_1);
+ }
+# else
+ if (! cherokee_buffer_is_empty(tls_protocols_buf)) {
+ cherokee_buffer_add_str (tls_protocols_buf, ", ");
+ }
+ cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_TLSV1_1);
+# endif
+# ifdef SSL_OP_NO_TLSv1_1
+ if ((cryp->hardcoded_ssl_options & SSL_OP_NO_TLSv1_1) > 0) {
+ if (! cherokee_buffer_is_empty(deactivated_protocols_buf)) {
+ cherokee_buffer_add_str (deactivated_protocols_buf, ", ");
+ }
+ cherokee_buffer_add_str (deactivated_protocols_buf, SSL_TXT_TLSV1_1);
+ }
+# endif
+# ifdef SSL_OP_NO_TLSv1_2
+ if ((options & SSL_OP_NO_TLSv1_2) == 0) {
+ if (! cherokee_buffer_is_empty(tls_protocols_buf)) {
+ cherokee_buffer_add_str (tls_protocols_buf, ", ");
+ }
+ cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_TLSV1_2);
+ }
+# else
+ if (! cherokee_buffer_is_empty(tls_protocols_buf)) {
+ cherokee_buffer_add_str (tls_protocols_buf, ", ");
+ }
+ cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_TLSV1_2);
+# endif
+# ifdef SSL_OP_NO_TLSv1_2
+ if ((cryp->hardcoded_ssl_options & SSL_OP_NO_TLSv1_2) > 0) {
+ if (! cherokee_buffer_is_empty(deactivated_protocols_buf)) {
+ cherokee_buffer_add_str (deactivated_protocols_buf, ", ");
+ }
+ cherokee_buffer_add_str (deactivated_protocols_buf, SSL_TXT_TLSV1_2);
+ }
+# endif
+#endif
+
+ if (ctx != NULL) {
+ SSL_CTX_free (ctx);
+ }
+ return ret_ok;
+
+error:
+
+ if (ctx != NULL) {
+ SSL_CTX_free (ctx);
+ }
+ return ret_error;
+}
+
PLUGIN_INFO_INIT (libssl,
cherokee_cryptor,
@@ -1282,6 +1415,7 @@ cherokee_cryptor_libssl_new (cherokee_cryptor_libssl_t **cryp)
CRYPTOR(n)->vserver_new = (cryptor_func_vserver_new_t) _vserver_new;
CRYPTOR(n)->socket_new = (cryptor_func_socket_new_t) _socket_new;
CRYPTOR(n)->client_new = (cryptor_func_client_new_t) _client_new;
+ CRYPTOR(n)->tls_info = (cryptor_func_tls_info_t) _tls_backend_info;
*cryp = n;
return ret_ok;
diff --git a/cherokee/error_list.py b/cherokee/error_list.py
index 146c6f90c..dd86cef6a 100644
--- a/cherokee/error_list.py
+++ b/cherokee/error_list.py
@@ -612,6 +612,12 @@
admin = "/general#Network-1",
show_bt = False)
+e('SERVER_UNKNOWN_TLS_PROTOCOL',
+ title = "Ignoring unknown SSL/TLS protocol %s",
+ desc = "OpenSSL/libssl supports an SSL/TLS protocol version that Cherokee is not capable of. This might impact security and accessability of your webserver. Please upgrade Cherokee webserver.",
+ admin = SYSTEM_ISSUE,
+ show_bt = False)
+
e('SERVER_TLS_DEFAULT',
title = "TLS/SSL support required for 'default' Virtual Server.",
desc = "TLS/SSL support must be set up in the 'default' Virtual Server. Its certificate will be used by the server in case TLS SNI information is not provided by the client.")
diff --git a/cherokee/info.c b/cherokee/info.c
index 8fff47e6d..02196feb3 100644
--- a/cherokee/info.c
+++ b/cherokee/info.c
@@ -26,11 +26,17 @@
#include "info.h"
#include "plugin_loader.h"
#include "server-protected.h"
+#ifdef HAVE_OPENSSL
+# include "cryptor_libssl.h"
+#endif
void
cherokee_info_build_print (cherokee_server_t *srv)
{
- cherokee_buffer_t builtin = CHEROKEE_BUF_INIT;
+ cherokee_buffer_t tmp = CHEROKEE_BUF_INIT;
+ cherokee_buffer_t prot_buf = CHEROKEE_BUF_INIT;
+ cherokee_buffer_t deact_buf = CHEROKEE_BUF_INIT;
+ ret_t ret;
/* Basic info
*/
@@ -38,6 +44,9 @@ cherokee_info_build_print (cherokee_server_t *srv)
printf (" Version: " PACKAGE_VERSION "\n");
printf (" Compiled on: " __DATE__ " " __TIME__ "\n");
printf (" Arguments to configure: " CHEROKEE_CONFIG_ARGS "\n");
+#ifdef HAVE_OPENSSL
+ printf (" OpenSSL support: libssl (" OPENSSL_VERSION_TEXT ")\n");
+#endif
printf ("\n");
/* Paths
@@ -54,10 +63,10 @@ cherokee_info_build_print (cherokee_server_t *srv)
/* Print plug-ins information
*/
printf ("Plug-ins\n");
- cherokee_plugin_loader_get_mods_info (&srv->loader, &builtin);
- printf (" Built-in: %s\n", builtin.buf ? builtin.buf : "");
+ cherokee_plugin_loader_get_mods_info (&srv->loader, &tmp);
+ printf (" Built-in: %s\n", tmp.buf ? tmp.buf : "");
printf ("\n");
- cherokee_buffer_mrproper (&builtin);
+ cherokee_buffer_clean(&tmp);
/* Support
*/
@@ -107,7 +116,25 @@ cherokee_info_build_print (cherokee_server_t *srv)
printf ("\n");
#ifdef HAVE_OPENSSL
- printf (" SSL/TLS: libssl\n");
+ cherokee_buffer_clean (&prot_buf);
+ cherokee_buffer_clean (&deact_buf);
+ if (srv->cryptor != NULL && srv->cryptor->tls_info != NULL) {
+ ret = srv->cryptor->tls_info (srv->cryptor,
+ &tmp,
+ &prot_buf,
+ &deact_buf);
+ if (ret == ret_ok) {
+ printf (" SSL/TLS: libssl (%s)\n", tmp.buf);
+ if (! cherokee_buffer_is_empty(&prot_buf)) {
+ printf (" supported protocols: %s\n",
+ prot_buf.buf);
+ }
+ if (! cherokee_buffer_is_empty(&deact_buf)) {
+ printf (" protocols deactivated by maintainer: %s\n",
+ deact_buf.buf);
+ }
+ }
+ }
# ifndef OPENSSL_NO_TLSEXT
printf (" TLS SNI: yes\n");
# else
@@ -119,4 +146,8 @@ cherokee_info_build_print (cherokee_server_t *srv)
#endif
printf ("\n");
+
+ cherokee_buffer_mrproper (&tmp);
+ cherokee_buffer_mrproper (&prot_buf);
+ cherokee_buffer_mrproper (&deact_buf);
}
diff --git a/cherokee/main_admin.c b/cherokee/main_admin.c
index eda501fd5..f2629b798 100644
--- a/cherokee/main_admin.c
+++ b/cherokee/main_admin.c
@@ -34,6 +34,11 @@
#include "server-protected.h"
#include "util.h"
+#ifdef HAVE_OPENSSL
+# include
+# include "pcre/pcre.h"
+#endif
+
#ifdef HAVE_SYS_WAIT_H
# include
#endif
@@ -64,6 +69,8 @@
#define DEFAULT_BIND "127.0.0.1"
#define RULE_PRE "vserver!1!rule!"
+#define ENTRIES "main"
+
static int port = DEFAULT_PORT;
static char *document_root = NULL;
static char *config_file = NULL;
@@ -75,6 +82,7 @@ static int scgi_port = 4000;
static int thread_num = -1;
static cherokee_server_t *srv = NULL;
static cherokee_buffer_t password = CHEROKEE_BUF_INIT;
+static cherokee_buffer_t tls_protocols = CHEROKEE_BUF_INIT;
static ret_t
@@ -233,29 +241,34 @@ config_server (cherokee_server_t *srv)
cherokee_buffer_add_str (&buf, "vserver!1!nick = default\n");
cherokee_buffer_add_va (&buf, "vserver!1!document_root = %s\n", document_root);
+ if (!cherokee_buffer_is_empty(&tls_protocols)) {
+ cherokee_buffer_prepend_str(&tls_protocols, "-P ");
+ }
+
if (scgi_port <= 0) {
cherokee_buffer_add_va (&buf,
"source!1!nick = app-logic\n"
"source!1!type = interpreter\n"
"source!1!timeout = " TIMEOUT "\n"
"source!1!host = %s\n"
- "source!1!interpreter = %s/server.py %s %s %s\n"
+ "source!1!interpreter = %s/server.py %s %s %s %s\n"
"source!1!env_inherited = 1\n",
DEFAULT_UNIX_SOCKET, document_root,
DEFAULT_UNIX_SOCKET, config_file,
- (debug) ? "-x" : "");
-
+ (debug) ? "-x" : "",
+ tls_protocols.buf);
} else {
cherokee_buffer_add_va (&buf,
"source!1!nick = app-logic\n"
"source!1!type = interpreter\n"
"source!1!timeout = " TIMEOUT "\n"
"source!1!host = localhost:%d\n"
- "source!1!interpreter = %s/server.py %d %s %s\n"
+ "source!1!interpreter = %s/server.py %d %s %s %s\n"
"source!1!env_inherited = 1\n",
scgi_port, document_root,
scgi_port, config_file,
- (debug) ? "-x" : "");
+ (debug) ? "-x" : "",
+ tls_protocols.buf);
}
if (debug) {
@@ -550,6 +563,90 @@ process_parameters (int argc, char **argv)
}
}
+#ifdef HAVE_OPENSSL
+static int
+check_for_tls_protocols() {
+ const char *regex = "-((ssl|tls)[[:digit:]]+_*[[:digit:]]*)";
+ pcre *reCompiled;
+ const char *pcreErrorStr;
+ const char *psubStrMatchStr;
+ FILE *f;
+ char tmp[256];
+ char *line;
+ int pcreErrorOffset;
+ int subStrVec[30];
+ int match_count = 0;
+ int start_offset = 0;
+ int ret = 0;
+
+ cherokee_buffer_t buf = CHEROKEE_BUF_INIT;;
+
+# ifdef PATH_OPENSSL_BIN
+ snprintf(tmp, sizeof (tmp), "%s s_server -help 2>&1",
+ PATH_OPENSSL_BIN);
+# else
+ snprintf(tmp, sizeof (tmp), "openssl s_server -help 2>&1");
+# endif
+ f = popen(tmp, "r");
+ if (f == NULL) {
+ PRINT_MSG("(critical) Cannot execute '%s'\n", tmp);
+ ret = ret_error;
+ goto out;
+ }
+ while (!feof(f)) {
+ line = fgets(tmp, sizeof (tmp), f);
+ if (line == NULL)
+ continue;
+ cherokee_buffer_add(&buf, line, strlen(line));
+ }
+ pclose(f);
+
+ reCompiled = pcre_compile(regex, 0, &pcreErrorStr,
+ &pcreErrorOffset, NULL);
+ if (reCompiled == NULL) {
+ TRACE(ENTRIES, "Regex error compiling '%s': %s\n", regex, pcreErrorStr);
+ ret = PCRE_ERROR_NULL;
+ goto out;
+ }
+ while (1) {
+ ret = pcre_exec(reCompiled, NULL, buf.buf, buf.len,
+ start_offset, 0, subStrVec, 30);
+ if (ret < 0) {
+ switch (ret) {
+ case PCRE_ERROR_NOMATCH:
+ ret = match_count;
+ break;
+ default:
+ TRACE(ENTRIES, "Regex matching failed with error code %d\n", ret);
+ }
+ goto out;
+ }
+ if (ret == 0) {
+ // Too many substrings were found to fit in subStrVec!");
+ // Set rc to the max number of substring matches possible.
+ ret = 30 / 3;
+ }
+ pcre_get_substring(buf.buf, subStrVec, ret, 1,
+ &(psubStrMatchStr));
+ if (!cherokee_buffer_is_empty(&tls_protocols)) {
+ cherokee_buffer_add_str(&tls_protocols, ",");
+ }
+ cherokee_buffer_add(&tls_protocols,
+ psubStrMatchStr, strlen(psubStrMatchStr));
+ match_count++;
+ pcre_free_substring(psubStrMatchStr);
+ start_offset = subStrVec[1];
+ }
+
+out:
+ if (reCompiled == NULL) {
+ pcre_free(reCompiled);
+ }
+ cherokee_buffer_mrproper(&buf);
+ return ret;
+}
+#endif
+
static ret_t
check_for_python (void)
{
@@ -631,6 +728,14 @@ main (int argc, char **argv)
exit (EXIT_ERROR);
}
+#ifdef HAVE_OPENSSL
+ /* OpenSSL supported TLS protocols */
+ if (check_for_tls_protocols() <= 0) {
+ PRINT_MSG("ERROR: No TLS protocol options in 'openssl s_server -help' output. "
+ "Please issue a bug report.\n");
+ }
+#endif
+
/* Signal handling */
act.sa_handler = SIG_IGN;
sigaction (SIGPIPE, &act, NULL);
diff --git a/cherokee/main_worker.c b/cherokee/main_worker.c
index 8af979781..f8610d73f 100644
--- a/cherokee/main_worker.c
+++ b/cherokee/main_worker.c
@@ -45,6 +45,11 @@
# include "getopt/getopt.h"
#endif
+#ifdef HAVE_OPENSSL
+# include
+# include "pcre/pcre.h"
+#endif
+
/* Notices
*/
#define APP_NAME \
@@ -94,6 +99,215 @@ static int services_fd = -1;
static ret_t common_server_initialization (cherokee_server_t *srv);
+#ifdef HAVE_OPENSSL
+static int
+check_for_tls_protocols(cherokee_buffer_t *buf, const char *regex, int options,
+ cherokee_buffer_t *protocol_option_list) {
+ pcre *reCompiled;
+ const char *pcreErrorStr;
+ const char *psubStrMatchStr;
+ int pcreErrorOffset;
+ int subStrVec[30];
+ int match_count = 0;
+ int start_offset = 0;
+ int j;
+ int ret = 0;
+
+ if (!buf)
+ goto out;
+ reCompiled = pcre_compile(regex, options, &pcreErrorStr,
+ &pcreErrorOffset, NULL);
+ if (reCompiled == NULL) {
+ TRACE(ENTRIES, "Regex error compiling '%s': %s\n", regex, pcreErrorStr);
+ ret = PCRE_ERROR_NULL;
+ goto out;
+ }
+ while (1) {
+ ret = pcre_exec(reCompiled, NULL, buf->buf, buf->len,
+ start_offset, 0, subStrVec, 30);
+ if (ret < 0) {
+ switch (ret) {
+ case PCRE_ERROR_NOMATCH:
+ ret = match_count;
+ break;
+ default:
+ TRACE(ENTRIES, "Regex matching failed with error code %d\n", ret);
+ }
+ goto out;
+ }
+ if (ret == 0) {
+ // Too many substrings were found to fit in subStrVec!");
+ // Set rc to the max number of substring matches possible.
+ ret = 30 / 3;
+ }
+ pcre_get_substring(buf->buf, subStrVec, ret, 1,
+ &(psubStrMatchStr));
+ if (!cherokee_buffer_is_empty(protocol_option_list)) {
+ cherokee_buffer_add_str(protocol_option_list, ",");
+ }
+ cherokee_buffer_add(protocol_option_list,
+ psubStrMatchStr, strlen(psubStrMatchStr));
+ match_count++;
+ pcre_free_substring(psubStrMatchStr);
+ start_offset = subStrVec[1];
+ }
+
+out:
+ if (reCompiled == NULL) {
+ pcre_free(reCompiled);
+ }
+ return ret;
+}
+
+/*
+ * More and more Linux distributions deactivate insecure SSL/TLS protocols
+ * at build time using configure options. OpenSSL 1.1.1 silently blocks the
+ * deactivated protocols, but they can still be configured using the libssl
+ * API. Build-time deactivation is documented in opensslconf.h, which in
+ * general requires libssl-dev package installation. Currently, the only
+ * feasible way to identify deactivated SSL/TLS protocols is to try to start
+ * an OpenSSL SSL/TLS server with the protocol that is subject to test.
+ * OpenSSL will terminate with an error if the protocol has been deactivated
+ * at build time,
+ */
+static ret_t
+add_hardcoded_ssl_options (cherokee_server_t *srv)
+{
+ unsigned int tls_protocols[] = {
+#ifdef SSL_OP_NO_SSLv2
+ SSL_OP_NO_SSLv2,
+#endif
+#ifdef SSL_OP_NO_SSLv3
+ SSL_OP_NO_SSLv3,
+#endif
+#ifdef SSL_OP_NO_TLSv1
+ SSL_OP_NO_TLSv1,
+#endif
+#ifdef SSL_OP_NO_TLSv1_1
+ SSL_OP_NO_TLSv1_1,
+#endif
+#ifdef SSL_OP_NO_TLSv1_2
+ SSL_OP_NO_TLSv1_2,
+#endif
+ 0,
+ };
+ const char *openssl_tls_options[] = {
+#ifdef SSL_OP_NO_SSLv2
+ "-ssl2",
+#endif
+#ifdef SSL_OP_NO_SSLv3
+ "-ssl3",
+#endif
+#ifdef SSL_OP_NO_TLSv1
+ "-tls1",
+#endif
+#ifdef SSL_OP_NO_TLSv1_1
+ "-tls1_1",
+#endif
+#ifdef SSL_OP_NO_TLSv1_2
+ "-tls1_2",
+#endif
+ NULL
+ };
+ const char *pattern = "(-(ssl|tls)[[:digit:]]+_*[[:digit:]]*)";
+ FILE *f;
+ char tmp[256];
+ char *line;
+ int retVal, i;
+ long options = 0;
+ cherokee_boolean_t known_protocol;
+ cherokee_buffer_t protocol_option_list = CHEROKEE_BUF_INIT;
+ cherokee_buffer_t buf = CHEROKEE_BUF_INIT;
+ ret_t ret;
+
+ for (i = 0; openssl_tls_options[i]; i++) {
+#ifdef PATH_OPENSSL_BIN
+ snprintf(tmp, sizeof (tmp), "%s s_server %s 2>&1",
+ PATH_OPENSSL_BIN,
+ openssl_tls_options[i]);
+#else
+ snprintf(tmp, sizeof (tmp), "openssl s_server -%s 2>&1",
+ openssl_tls_options[i]);
+#endif
+ f = popen(tmp, "r");
+ if (f == NULL) {
+ PRINT_MSG("(critical) Cannot execute '%s'\n", tmp);
+ ret = ret_error;
+ goto out;
+ }
+ while (!feof(f)) {
+ /* Skip line until it found the version entry
+ */
+ line = fgets(tmp, sizeof (tmp), f);
+ if (line == NULL)
+ continue;
+ line = strstr(line, openssl_tls_options[i]);
+ if (line == NULL)
+ continue;
+ options |= tls_protocols[i];
+ break;
+ }
+ pclose(f);
+ }
+ if (srv->cryptor != NULL) {
+ srv->cryptor->hardcoded_ssl_options = options;
+ }
+
+#ifdef PATH_OPENSSL_BIN
+ snprintf(tmp, sizeof (tmp), "%s s_server -help 2>&1",
+ PATH_OPENSSL_BIN);
+#else
+ snprintf(tmp, sizeof (tmp), "openssl s_server -help 2>&1");
+#endif
+ snprintf(tmp, sizeof (tmp), "openssl s_server -help 2>&1");
+ f = popen(tmp, "r");
+ if (f == NULL) {
+ PRINT_MSG("(critical) Cannot execute '%s'\n", tmp);
+ ret = ret_error;
+ goto out;
+ }
+ while (!feof(f)) {
+ line = fgets(tmp, sizeof (tmp), f);
+ if (line == NULL)
+ continue;
+ cherokee_buffer_add(&buf, line, strlen(line));
+ }
+ pclose(f);
+ retVal = check_for_tls_protocols(&buf, pattern, 0, &protocol_option_list);
+ if (retVal > 0) {
+ char *begin;
+ cherokee_buffer_clean(&buf);
+ while ((begin = (char *) strsep(&protocol_option_list.buf, ",")) != NULL) {
+ known_protocol = false;
+ for (i = 0; openssl_tls_options[i]; i++) {
+ if (strstr(openssl_tls_options[i], begin)) {
+ known_protocol = true;
+ break;
+ }
+ }
+ if (known_protocol == false) {
+ if (!cherokee_buffer_is_empty(&buf)) {
+ cherokee_buffer_add_str(&buf, ", ");
+ }
+ cherokee_buffer_add(&buf, (begin + 1), strlen(begin) - 1);
+ }
+ }
+ if (!cherokee_buffer_is_empty(&buf)) {
+ LOG_WARNING(CHEROKEE_ERROR_SERVER_UNKNOWN_TLS_PROTOCOL, buf.buf);
+ }
+ } else {
+ PRINT_MSG("ERROR: No TLS protocol options in 'openssl s_server -help' output. "
+ "Please issue a bug report.\n");
+ }
+ ret = ret_ok;
+
+out:
+ cherokee_buffer_mrproper(&buf);
+ cherokee_buffer_mrproper(&protocol_option_list);
+ return ret;
+}
+#endif
+
static void
wait_process (pid_t pid)
{
@@ -172,6 +386,55 @@ test_configuration_file (void)
}
+static ret_t
+fake_tls_server_initialization (cherokee_server_t *srv)
+{
+ ret_t ret;
+
+ cherokee_buffer_t tmp = CHEROKEE_BUF_INIT;
+ cherokee_buffer_t droot = CHEROKEE_BUF_INIT;
+
+ /* Sanity checka
+ */
+ if (port > 0xFFFF) {
+ PRINT_ERROR ("Port %d is out of limits\n", port);
+ return ret_error;
+ }
+ if (document_root == NULL) {
+ PRINT_ERROR ("Document root is not specified\n");
+ return ret_error;
+ }
+
+ /* Build the configuration string
+ */
+ cherokee_buffer_add (&droot, document_root, strlen(document_root));
+ cherokee_path_arg_eval (&droot);
+
+ cherokee_buffer_add_va (&tmp,
+ "server!bind!1!port = %d\n"
+#ifdef HAVE_OPENSSL
+ "server!tls = libssl\n"
+#endif
+ "vserver!1!document_root = %s\n"
+ BASIC_CONFIG, port, droot.buf);
+
+ /* Apply it
+ */
+ ret = cherokee_server_read_config_string (srv, &tmp);
+
+ cherokee_buffer_mrproper (&tmp);
+ cherokee_buffer_mrproper (&droot);
+
+ if (ret != ret_ok) {
+ PRINT_MSG ("Couldn't initialize HTTPS test server\n");
+ return ret_error;
+ }
+ srv->tls_enabled = true;
+
+ return ret_ok;
+}
+
+
static ret_t
common_server_initialization (cherokee_server_t *srv)
{
@@ -249,6 +512,11 @@ common_server_initialization (cherokee_server_t *srv)
}
}
+#ifdef HAVE_OPENSSL
+ if (add_hardcoded_ssl_options (srv) != ret_ok) {
+ PRINT_MSG ("Couldn't identify deactivated SSL/TLS protocols\n");
+ }
+#endif
if (daemon_mode)
cherokee_server_daemonize (srv);
@@ -378,6 +646,21 @@ main (int argc, char **argv)
}
if (print_modules) {
+ if (document_root == NULL) {
+ document_root = WWW_ROOT;
+ }
+ if (!port_set) {
+ port = 443;
+ }
+ ret = fake_tls_server_initialization (srv);
+ if (ret != ret_ok) {
+ exit (EXIT_ERROR);
+ }
+#ifdef HAVE_OPENSSL
+ if (add_hardcoded_ssl_options (srv) != ret_ok) {
+ PRINT_MSG ("Couldn't identify deactivated SSL/TLS protocols\n");
+ }
+#endif
cherokee_info_build_print (srv);
exit (EXIT_OK_ONCE);
}
diff --git a/configure.ac b/configure.ac
index d6298f27a..9501adb70 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1095,14 +1095,48 @@ if test "$WITH_OPENSSL" != "no"; then
AC_CHECK_LIB(ssl, SSL_accept, [have_openssl=yes], [have_openssl=no], $LIBSSL_LIBS)
if test "$have_openssl" = "yes"; then
- LIBSSL_LIBS="$LIBSSL_LIBS -lssl"
-
- AC_SUBST(LIBSSL_LIBS)
- AC_SUBST(LIBSSL_CFLAGS)
+ TLS_BACK_END="OpenSSL/libssl"
+ OPENSSL_PATH="$OPENSSL_PATH$PATH_SEPARATOR$PATH"
+ LIBSSL_LIBS="$LIBSSL_LIBS -lssl -lcrypto"
+
+ AC_SUBST(LIBSSL_LIBS)
+ AC_SUBST(LIBSSL_CFLAGS)
+
+ AC_DEFINE(HAVE_OPENSSL, 1, [Have OpenSSL library])
+ AC_CACHE_CHECK([for a suitable test server to verify $TLS_BACK_END TLS protocol support], [ac_cv_path_OPENSSL_BIN],
+ [AC_PATH_PROGS_FEATURE_CHECK([OPENSSL_BIN], [openssl],
+ [v=$($ac_path_OPENSSL_BIN s_server -help 2>&1)
+ if test $? -eq 0 || [[[ $v == unknown* ]]]; then
+ TLS_PROTOCOLS=$(echo $v | grep -Eoe '-(ssl|tls)[[0-9]]+[[_]]*[[0-9]]*' | tr '\n' ',' | sed 's/.$//' | sed 's/-//g')
+ ac_cv_path_OPENSSL_BIN="$ac_path_OPENSSL_BIN" ac_path_OPENSSL_BIN_found=:
+ fi],,
+ $OPENSSL_PATH)])
+ AC_SUBST(OPENSSL_BIN, [$ac_cv_path_OPENSSL_BIN])
+
+ AC_SUBST(OPENSSL_TLS_PROTOCOLS, [])
+ if test -n "$ac_cv_path_OPENSSL_BIN"; then
+ AC_DEFINE_UNQUOTED(PATH_OPENSSL_BIN, ["$ac_cv_path_OPENSSL_BIN"], [Define path to OpenSSL command-line tool])
+ AC_MSG_CHECKING([for $TLS_BACK_END supported SSL/TLS protocols])
+ AC_DEFINE_UNQUOTED(TLS_BACK_END_PROTOCOL_VERSIONS, ["$TLS_PROTOCOLS"], [Define TLS protocols supported by OpenSSL/libssl])
+ AC_SUBST(OPENSSL_TLS_PROTOCOLS, ["$TLS_PROTOCOLS"])
+ AC_MSG_RESULT($TLS_PROTOCOLS)
+ fi
- AC_DEFINE(HAVE_OPENSSL, 1, [Have OpenSSL library])
- AC_PATH_PROG([OPENSSL_BIN], [openssl])
- AC_SUBST(OPENSSL_BIN)
+ AC_MSG_CHECKING([if Cherokee supports all OpenSSL SSL/TLS protocol versions])
+ AC_RUN_IFELSE([AC_LANG_PROGRAM([#include ],
+ [#ifdef TLS_MAX_VERSION
+ int max = TLS_MAX_VERSION;
+ #ifdef TLS1_2_VERSION
+ if (max > TLS1_2_VERSION) exit(1);
+ #else
+ if (max > 0x0303) exit(1);
+ #endif
+ #endif])],
+ [AC_MSG_RESULT(yes)],
+ [AC_MSG_RESULT(no)
+ AC_MSG_ERROR([
+*** OpenSSL supports more recent versions of SSL/TLS protocols, which are not recognized by Cherokee.
+*** Please check for the latest Cherokee source or update Cherokee source code.])])
fi
fi
AC_MSG_CHECKING(for libssl)
@@ -1274,6 +1308,7 @@ AC_ARG_WITH(wwwroot, AC_HELP_STRING([--with-wwwroot=DIR], [Set the WWW root dire
WWW_ROOT="$localstatedir/www"
fi
])
+AC_DEFINE_UNQUOTED(WWW_ROOT, "$WWW_ROOT", [WWW root directory])
AC_SUBST(WWW_ROOT)
AC_ARG_WITH(cgiroot, AC_HELP_STRING([--with-cgiroot=DIR], [Set the CGI directory]),
From 4a527ebc19b4eb8367acb76ad74d8e17e67e15f8 Mon Sep 17 00:00:00 2001
From: Thomas Reim
Date: Thu, 1 Apr 2021 12:45:37 +0200
Subject: [PATCH 03/14] Default SSL/TLS cipher set is outdated
When using modern and safer certificates, e. g. with ECDSA (Elliptic Curve
Digital Signature Algorithm) keys, HTTPS connection setup may be rejected
by OpenSSL/libssl due to outdated the cipher set configured by Cherokee
webserver. Windows 7 clients for example cannot retrieve automatic proxy
configuration via HTTPS anymore. Communication is aborted with typical
strange error notifications, e. g.:
- System
- Provider
[ Name] Schannel
[ Guid] {1F678132-5938-4686-9FDC-C8FF68F15C85}
EventID 36887
Version 0
Level 2
Task 0
Opcode 0
Keywords 0x8000000000000000
- TimeCreated
[ SystemTime] 2021-02-03T00:36:24.530185900Z
EventRecordID 319818
Correlation
- Execution
[ ProcessID] 716
[ ThreadID] 764
Channel System
Computer local@local.domain
- Security
[ UserID] S-1-5-18
- EventData
AlertDesc 40
Even recent OpenSSL clients may not be able to securely connect to Cherokee
webserver. Also here error notifications are not too helpful:
CONNECTED(00000003)
139835650114880:error:14094438:SSL routines:ssl3_read_bytes:tlsv1 alert internal error:../ssl/record/rec_layer_s3.c:1543:SSL alert number 80
---
admin/PageVServer.py | 2 +-
cherokee/cryptor.h | 3 ++-
cherokee/cryptor_libssl.c | 8 ++++++++
3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/admin/PageVServer.py b/admin/PageVServer.py
index 14f6e676f..5c4246161 100644
--- a/admin/PageVServer.py
+++ b/admin/PageVServer.py
@@ -45,7 +45,7 @@
NOTE_CERT = N_('This directive points to the PEM-encoded Certificate file for the server (Full path to the file)')
NOTE_CERT_KEY = N_('PEM-encoded Private Key file for the server (Full path to the file)')
NOTE_CA_LIST = N_('File containing the trusted CA certificates, utilized for checking the client certificates (Full path to the file)')
-NOTE_CIPHERS = N_('Ciphers that TLS/SSL is allowed to use. Reference. (Default enables Forward Secrecy).')
+NOTE_CIPHERS = N_('Ciphers that TLSv1.2 and below is allowed to use.')
NOTE_CIPHER_SERVER_PREFERENCE = N_('The cipher sequence that is specified by the server should have preference over the client preference. (Default: True).')
NOTE_COMPRESSION = N_('Explicitly enable or disable serverside compression support. (Default: Disabled).')
NOTE_DH_LENGTH = N_('Explicitely sets the Diffie-Hellman parameters length. (Default: Let openssl choose).')
diff --git a/cherokee/cryptor.h b/cherokee/cryptor.h
index ca5c590f7..c1cf0b71e 100644
--- a/cherokee/cryptor.h
+++ b/cherokee/cryptor.h
@@ -35,7 +35,8 @@
CHEROKEE_BEGIN_DECLS
-#define CHEROKEE_CIPHERS_DEFAULT "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"
+#define CHEROKEE_CIPHERS_DEFAULT "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
+#define CHEROKEE_CIPHERS_OLD "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"
/* Callback function prototipes
*/
diff --git a/cherokee/cryptor_libssl.c b/cherokee/cryptor_libssl.c
index 16b03b468..fd9163787 100644
--- a/cherokee/cryptor_libssl.c
+++ b/cherokee/cryptor_libssl.c
@@ -391,6 +391,14 @@ _vserver_new (cherokee_cryptor_t *cryp,
EC_KEY *ecdh;
#endif
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ /* Old OpenSSL back-ends require Mozilla Old cipher configuration
+ * in order to support SSL/TLS encryption
+ */
+ cherokee_buffer_clean (&vsrv->ciphers);
+ cherokee_buffer_add_str (&vsrv->ciphers, CHEROKEE_CIPHERS_OLD);
+#endif
+
CHEROKEE_NEW_STRUCT (n, cryptor_vserver_libssl);
UNUSED(cryp);
From 9044f48fcec197b11146aafecce74ccd55d64731 Mon Sep 17 00:00:00 2001
From: Thomas Reim
Date: Thu, 1 Apr 2021 16:11:20 +0200
Subject: [PATCH 04/14] Support of OpenSSL 1.1.1 and TLS v1.3
OpenSSL 1.1.1 was released on 11 September 2018. This is the latest LTS (Long
Term Support) release, supported until September 2023. The headline new feature
of OpenSSL 1.1.1 is TLSv1.3. This new version of the Transport Layer Security
(formerly known as SSL) protocol was published by the IETF as RFC8446. This is a
major rewrite of the standard and introduces significant changes, features and
improvements which have been reflected in the new OpenSSL version. Main changes
to be considered by Cherokee webserver:
- Fully compliant implementation of TLSv1.3 (RFC8446) on by default
- Support for all five new RFC8446 ciphersuites (TLS v1.3)
- Full support of minimum and maximum available TLS protocol version configuration
Recently OS distribution maintainers have started to improve OpenSSL security
by hardcoded configuration of the min. available TLS protocol version for clients
that want to connect to a server using TLS encryption. Cherokee command-line
option cherokee -i now reports this hardcoded setting to users.
Fixes: https://github.com/cherokee/webserver/issues/1256
Signed-off-by: Thomas Reim
---
cherokee/cryptor.c | 16 ++-
cherokee/cryptor.h | 4 +
cherokee/cryptor_libssl.c | 271 +++++++++++++++++++++++++++++++++-----
cherokee/error_list.py | 8 ++
cherokee/main_worker.c | 38 +++---
cherokee/virtual_server.c | 7 +
cherokee/virtual_server.h | 1 +
configure.ac | 34 +++--
8 files changed, 319 insertions(+), 60 deletions(-)
diff --git a/cherokee/cryptor.c b/cherokee/cryptor.c
index 843589956..91e50a250 100644
--- a/cherokee/cryptor.c
+++ b/cherokee/cryptor.c
@@ -49,12 +49,15 @@ cherokee_cryptor_init_base (cherokee_cryptor_t *cryp,
/* Properties
*/
cryp->hardcoded_ssl_options = 0;
- cryp->timeout_handshake = TIMEOUT_DEFAULT;
- cryp->allow_SSLv2 = false;
- cryp->allow_SSLv3 = false;
- cryp->allow_TLSv1 = true;
- cryp->allow_TLSv1_1 = true;
- cryp->allow_TLSv1_2 = true;
+ cryp->timeout_handshake = TIMEOUT_DEFAULT;
+ cryp->min_tls_protocol = 0;
+ cryp->max_tls_protocol = 0;
+ cryp->allow_SSLv2 = false;
+ cryp->allow_SSLv3 = false;
+ cryp->allow_TLSv1 = true;
+ cryp->allow_TLSv1_1 = true;
+ cryp->allow_TLSv1_2 = true;
+ cryp->allow_TLSv1_3 = true;
return ret_ok;
}
@@ -93,6 +96,7 @@ cherokee_cryptor_configure (cherokee_cryptor_t *cryp,
cherokee_config_node_read_bool (conf, "protocol!TLSv1", &cryp->allow_TLSv1);
cherokee_config_node_read_bool (conf, "protocol!TLSv1_1", &cryp->allow_TLSv1_1);
cherokee_config_node_read_bool (conf, "protocol!TLSv1_2", &cryp->allow_TLSv1_2);
+ cherokee_config_node_read_bool (conf, "protocol!TLSv1_3", &cryp->allow_TLSv1_3);
/* Call the its virtual method
*/
diff --git a/cherokee/cryptor.h b/cherokee/cryptor.h
index c1cf0b71e..efd5425e8 100644
--- a/cherokee/cryptor.h
+++ b/cherokee/cryptor.h
@@ -35,6 +35,7 @@
CHEROKEE_BEGIN_DECLS
+#define CHEROKEE_CIPHERSUITES_DEFAULT "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256"
#define CHEROKEE_CIPHERS_DEFAULT "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
#define CHEROKEE_CIPHERS_OLD "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"
@@ -69,11 +70,14 @@ typedef struct {
cherokee_module_t module;
long hardcoded_ssl_options;
cint_t timeout_handshake;
+ int min_tls_protocol;
+ int max_tls_protocol;
cherokee_boolean_t allow_SSLv2;
cherokee_boolean_t allow_SSLv3;
cherokee_boolean_t allow_TLSv1;
cherokee_boolean_t allow_TLSv1_1;
cherokee_boolean_t allow_TLSv1_2;
+ cherokee_boolean_t allow_TLSv1_3;
/* Methods */
cryptor_func_configure_t configure;
diff --git a/cherokee/cryptor_libssl.c b/cherokee/cryptor_libssl.c
index fd9163787..088cdd0e9 100644
--- a/cherokee/cryptor_libssl.c
+++ b/cherokee/cryptor_libssl.c
@@ -70,6 +70,10 @@ static DH *dh_param_4096 = NULL;
} while(0)
#define CRYPTOR_VSRV_SSL_OPTIONS(v) \
VSERVER_SRV(v)->cryptor->hardcoded_ssl_options
+#define CRYPTOR_VSRV_MIN_TLS_PROTOCOL(v) \
+ VSERVER_SRV(v)->cryptor->min_tls_protocol
+#define CRYPTOR_VSRV_MAX_TLS_PROTOCOL(v) \
+ VSERVER_SRV(v)->cryptor->max_tls_protocol
#define PROTOCOL_SWITCHED_OFF(prot, opt, vsrv) \
((opt & prot) | (CRYPTOR_VSRV_SSL_OPTIONS(vsrv) & prot)) > 0
@@ -111,6 +115,97 @@ _free (cherokee_cryptor_libssl_t *cryp)
return ret_ok;
}
+static ret_t
+_tls_protocol_version_to_text(int protocol_version,
+ cherokee_buffer_t *buf)
+{
+ switch (protocol_version) {
+#ifdef SSL_TXT_SSLV3
+ case SSL3_VERSION:
+ cherokee_buffer_add_str (buf, SSL_TXT_SSLV3);
+ break;
+#endif
+#ifdef SSL_TXT_TLSV1
+ case TLS1_VERSION:
+ cherokee_buffer_add_str (buf, SSL_TXT_TLSV1);
+ break;
+#endif
+#ifdef SSL_TXT_TLSV1_1
+ case TLS1_1_VERSION:
+ cherokee_buffer_add_str (buf, SSL_TXT_TLSV1_1);
+ break;
+#endif
+#ifdef SSL_TXT_TLSV1_2
+ case TLS1_2_VERSION:
+ cherokee_buffer_add_str (buf, SSL_TXT_TLSV1_2);
+ break;
+#endif
+#ifdef TLS1_3_VERSION
+ case TLS1_3_VERSION:
+ cherokee_buffer_add_str (buf, "TLSv1.3");
+ break;
+#endif
+ default:
+ return ret_error;
+ }
+ return ret_ok;
+}
+
+static ret_t
+try_read_protocol_version(cherokee_config_node_t *conf,
+ const char *key,
+ int *protocol_version) {
+ ret_t ret;
+ cherokee_buffer_t *buf;
+ char *tmp;
+
+ ret = cherokee_config_node_read(conf, key, &buf);
+ if (ret != ret_ok) {
+ ret = ret_ok;
+ goto out;
+ }
+#ifdef SSL_TXT_SSLV3
+ if (equal_buf_str(buf, "SSLv3")) {
+ *protocol_version = SSL3_VERSION;
+ goto out;
+ }
+#endif
+#ifdef SSL_TXT_TLSV1
+ if (equal_buf_str(buf, "TLSv1")) {
+ *protocol_version = TLS1_VERSION;
+ goto out;
+ }
+#endif
+#ifdef SSL_TXT_TLSV1_1
+ if (equal_buf_str(buf, "TLSv1.1")) {
+ *protocol_version = TLS1_1_VERSION;
+ goto out;
+ }
+#endif
+#ifdef SSL_TXT_TLSV1_2
+ if (equal_buf_str(buf, "TLSv1.2")) {
+ *protocol_version = TLS1_2_VERSION;
+ goto out;
+ }
+#endif
+#ifdef TLS1_3_VERSION
+ if (equal_buf_str(buf, "TLSv1.3")) {
+ *protocol_version = TLS1_3_VERSION;
+ goto out;
+ }
+#endif
+ tmp = strstr(buf->buf, "auto");
+ if (tmp != NULL) {
+ *protocol_version = 0;
+ } else {
+ TRACE(ENTRIES, "Unknown TLS protocol version: %s\n", buf->buf);
+ ret = ret_error;
+ }
+
+out:
+ return ret;
+}
+
static ret_t
try_read_dh_param(cherokee_config_node_t *conf,
DH **dh,
@@ -172,6 +267,14 @@ _configure (cherokee_cryptor_t *cryp,
UNUSED(cryp);
UNUSED(srv);
+#ifdef HAVE_SSL_CTX_GET_MIN_PROTO_VERSION
+ ret = try_read_protocol_version(conf, "protocol!min", &cryp->min_tls_protocol);
+ if (ret != ret_ok)
+ return ret;
+ ret = try_read_protocol_version(conf, "protocol!max", &cryp->max_tls_protocol);
+ if (ret != ret_ok)
+ return ret;
+#endif
ret = try_read_dh_param (conf, &dh_param_512, 512);
if (ret != ret_ok)
return ret;
@@ -415,7 +518,11 @@ _vserver_new (cherokee_cryptor_t *cryp,
/* Init the OpenSSL context
*/
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
n->context = SSL_CTX_new (SSLv23_server_method());
+#else
+ n->context = SSL_CTX_new (TLS_server_method());
+#endif
if (n->context == NULL) {
LOG_ERROR_S(CHEROKEE_ERROR_SSL_ALLOCATE_CTX);
goto error;
@@ -458,6 +565,42 @@ _vserver_new (cherokee_cryptor_t *cryp,
}
#endif
+ /* Set minimum and maximum supported TLS protocol version parameters
+ */
+#ifdef HAVE_SSL_CTX_GET_MIN_PROTO_VERSION
+ if (cryp->min_tls_protocol > 0) {
+ rc = SSL_CTX_set_min_proto_version (n->context,
+ cryp->min_tls_protocol);
+ if (rc != 1) {
+ cherokee_buffer_t tmp = CHEROKEE_BUF_INIT;
+ if (_tls_protocol_version_to_text(cryp->min_tls_protocol, &tmp) != ret_ok) {
+ cherokee_buffer_add_str (&tmp, "unknown protocol");
+ }
+ cherokee_buffer_add_va (&tmp, " (%d)", cryp->min_tls_protocol);
+ OPENSSL_LAST_ERROR(error);
+ LOG_ERROR(CHEROKEE_ERROR_SSL_MIN_MAX_PROTOCOL, tmp.buf, error);
+ cherokee_buffer_mrproper(&tmp);
+ goto error;
+ }
+ }
+ if (cryp->max_tls_protocol > 0) {
+ rc = SSL_CTX_set_max_proto_version (n->context, cryp->max_tls_protocol);
+ if (rc != 1) {
+ cherokee_buffer_t tmp = CHEROKEE_BUF_INIT;
+ if (_tls_protocol_version_to_text(cryp->max_tls_protocol, &tmp) != ret_ok) {
+ cherokee_buffer_add_str (&tmp, "unknown protocol");
+ }
+ cherokee_buffer_add_va (&tmp, " (%d)", cryp->max_tls_protocol);
+ OPENSSL_LAST_ERROR(error);
+ LOG_ERROR(CHEROKEE_ERROR_SSL_MIN_MAX_PROTOCOL, tmp.buf, error);
+ cherokee_buffer_mrproper(&tmp);
+ goto error;
+ }
+ }
+
+ CLEAR_LIBSSL_ERRORS;
+#endif
+
/* Set the SSL context options:
*/
options = SSL_OP_ALL;
@@ -470,27 +613,36 @@ _vserver_new (cherokee_cryptor_t *cryp,
options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
#endif
+#ifdef SSL_OP_NO_SSLv2
if (! cryp->allow_SSLv2) {
options |= SSL_OP_NO_SSLv2;
}
-
+#endif
+#ifdef SSL_OP_NO_SSLv3
if (! cryp->allow_SSLv3) {
options |= SSL_OP_NO_SSLv3;
}
-
+#endif
+#ifdef SSL_OP_NO_TLSv1
if (! cryp->allow_TLSv1) {
options |= SSL_OP_NO_TLSv1;
}
-
-#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+#endif
+#ifdef SSL_OP_NO_TLSv1_1
if (! cryp->allow_TLSv1_1) {
options |= SSL_OP_NO_TLSv1_1;
}
-
+#endif
+#ifdef SSL_OP_NO_TLSv1_2
if (! cryp->allow_TLSv1_2) {
options |= SSL_OP_NO_TLSv1_2;
}
#endif
+#ifdef SSL_OP_NO_TLSv1_3
+ if (! cryp->allow_TLSv1_3) {
+ options |= SSL_OP_NO_TLSv1_3;
+ }
+#endif
#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
if (vsrv->cipher_server_preference) {
@@ -522,6 +674,20 @@ _vserver_new (cherokee_cryptor_t *cryp,
}
}
+#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
+ /* Set ciphersuites that vserver will accept.
+ */
+ if (! cherokee_buffer_is_empty (&vsrv->ciphersuites)) {
+ rc = SSL_CTX_set_ciphersuites (n->context, vsrv->ciphersuites.buf);
+ if (rc != 1) {
+ OPENSSL_LAST_ERROR(error);
+ LOG_ERROR(CHEROKEE_ERROR_SSL_CIPHERSUITE,
+ vsrv->ciphersuites.buf, error);
+ goto error;
+ }
+ }
+#endif
+
CLEAR_LIBSSL_ERRORS;
/* Certificate
@@ -1146,7 +1312,11 @@ _client_init_tls (cherokee_cryptor_client_libssl_t *cryp,
/* New context
*/
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
cryp->ssl_ctx = SSL_CTX_new (SSLv23_client_method());
+#else
+ cryp->ssl_ctx = SSL_CTX_new (TLS_client_method());
+#endif
if (cryp->ssl_ctx == NULL) {
OPENSSL_LAST_ERROR(error);
LOG_CRITICAL (CHEROKEE_ERROR_SSL_CREATE_CTX, error);
@@ -1306,13 +1476,15 @@ _tls_backend_info (cherokee_cryptor_t *cryp,
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
# ifdef SSL_TXT_SSLV3
+ if (SSL_set_min_proto_version (session, SSL3_VERSION) == 1) {
# ifdef SSL_OP_NO_SSLv3
- if ((options & SSL_OP_NO_SSLv3) == 0) {
- cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_SSLV3);
- }
+ if ((options & SSL_OP_NO_SSLv3) == 0) {
+ cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_SSLV3);
+ }
# else
- cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_SSLV3);
+ cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_SSLV3);
# endif
+ }
# ifdef SSL_OP_NO_SSLv3
if ((cryp->hardcoded_ssl_options & SSL_OP_NO_SSLv3) > 0) {
cherokee_buffer_add_str (deactivated_protocols_buf, SSL_TXT_SSLV3);
@@ -1320,19 +1492,21 @@ _tls_backend_info (cherokee_cryptor_t *cryp,
# endif
# endif
# ifdef SSL_TXT_TLSV1
+ if (SSL_set_min_proto_version (session, TLS1_VERSION) == 1) {
# ifdef SSL_OP_NO_TLSv1
- if ((options & SSL_OP_NO_TLSv1) == 0) {
+ if ((options & SSL_OP_NO_TLSv1) == 0) {
+ if (! cherokee_buffer_is_empty(tls_protocols_buf)) {
+ cherokee_buffer_add_str (tls_protocols_buf, ", ");
+ }
+ cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_TLSV1);
+ }
+# else
if (! cherokee_buffer_is_empty(tls_protocols_buf)) {
cherokee_buffer_add_str (tls_protocols_buf, ", ");
}
cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_TLSV1);
- }
-# else
- if (! cherokee_buffer_is_empty(tls_protocols_buf)) {
- cherokee_buffer_add_str (tls_protocols_buf, ", ");
- }
- cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_TLSV1);
# endif
+ }
# ifdef SSL_OP_NO_TLSv1
if ((cryp->hardcoded_ssl_options & SSL_OP_NO_TLSv1) > 0) {
if (! cherokee_buffer_is_empty(deactivated_protocols_buf)) {
@@ -1342,19 +1516,21 @@ _tls_backend_info (cherokee_cryptor_t *cryp,
}
# endif
# endif
+ if (SSL_set_min_proto_version (session, TLS1_1_VERSION) == 1) {
# ifdef SSL_OP_NO_TLSv1_1
- if ((options & SSL_OP_NO_TLSv1_1) == 0) {
+ if ((options & SSL_OP_NO_TLSv1_1) == 0) {
+ if (! cherokee_buffer_is_empty(tls_protocols_buf)) {
+ cherokee_buffer_add_str (tls_protocols_buf, ", ");
+ }
+ cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_TLSV1_1);
+ }
+# else
if (! cherokee_buffer_is_empty(tls_protocols_buf)) {
cherokee_buffer_add_str (tls_protocols_buf, ", ");
}
cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_TLSV1_1);
- }
-# else
- if (! cherokee_buffer_is_empty(tls_protocols_buf)) {
- cherokee_buffer_add_str (tls_protocols_buf, ", ");
- }
- cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_TLSV1_1);
# endif
+ }
# ifdef SSL_OP_NO_TLSv1_1
if ((cryp->hardcoded_ssl_options & SSL_OP_NO_TLSv1_1) > 0) {
if (! cherokee_buffer_is_empty(deactivated_protocols_buf)) {
@@ -1363,19 +1539,21 @@ _tls_backend_info (cherokee_cryptor_t *cryp,
cherokee_buffer_add_str (deactivated_protocols_buf, SSL_TXT_TLSV1_1);
}
# endif
+ if (SSL_set_min_proto_version (session, TLS1_2_VERSION) == 1) {
# ifdef SSL_OP_NO_TLSv1_2
- if ((options & SSL_OP_NO_TLSv1_2) == 0) {
+ if ((options & SSL_OP_NO_TLSv1_2) == 0) {
+ if (! cherokee_buffer_is_empty(tls_protocols_buf)) {
+ cherokee_buffer_add_str (tls_protocols_buf, ", ");
+ }
+ cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_TLSV1_2);
+ }
+# else
if (! cherokee_buffer_is_empty(tls_protocols_buf)) {
cherokee_buffer_add_str (tls_protocols_buf, ", ");
}
cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_TLSV1_2);
- }
-# else
- if (! cherokee_buffer_is_empty(tls_protocols_buf)) {
- cherokee_buffer_add_str (tls_protocols_buf, ", ");
- }
- cherokee_buffer_add_str (tls_protocols_buf, SSL_TXT_TLSV1_2);
# endif
+ }
# ifdef SSL_OP_NO_TLSv1_2
if ((cryp->hardcoded_ssl_options & SSL_OP_NO_TLSv1_2) > 0) {
if (! cherokee_buffer_is_empty(deactivated_protocols_buf)) {
@@ -1383,6 +1561,39 @@ _tls_backend_info (cherokee_cryptor_t *cryp,
}
cherokee_buffer_add_str (deactivated_protocols_buf, SSL_TXT_TLSV1_2);
}
+# endif
+ if (SSL_set_min_proto_version (session, TLS1_3_VERSION) == 1) {
+# ifdef SSL_OP_NO_TLSv1_3
+ if ((options & SSL_OP_NO_TLSv1_3) == 0) {
+ if (! cherokee_buffer_is_empty(tls_protocols_buf)) {
+ cherokee_buffer_add_str (tls_protocols_buf, ", ");
+ }
+ cherokee_buffer_add_str (tls_protocols_buf, "TLSv1.3");
+ }
+# else
+ if (! cherokee_buffer_is_empty(tls_protocols_buf)) {
+ cherokee_buffer_add_str (tls_protocols_buf, ", ");
+ }
+ cherokee_buffer_add_str (tls_protocols_buf, "TLSv1.3");
+# endif
+ }
+# ifdef SSL_OP_NO_TLSv1_3
+ if ((cryp->hardcoded_ssl_options & SSL_OP_NO_TLSv1_3) > 0) {
+ if (! cherokee_buffer_is_empty(deactivated_protocols_buf)) {
+ cherokee_buffer_add_str (deactivated_protocols_buf, ", ");
+ }
+ cherokee_buffer_add_str (deactivated_protocols_buf, "TLSv1.3");
+ }
+# endif
+# ifdef HAVE_SSL_CTX_GET_MIN_PROTO_VERSION
+ version = SSL_CTX_get_min_proto_version (ctx);
+ if (version > 0) {
+ cherokee_buffer_add_str (tls_protocols_buf, " - default min. protocol: ");
+ if (_tls_protocol_version_to_text(version, tls_protocols_buf) != ret_ok) {
+ cherokee_buffer_add_va (tls_protocols_buf,
+ "unknown protocol (%04x)", version);
+ }
+ }
# endif
#endif
diff --git a/cherokee/error_list.py b/cherokee/error_list.py
index dd86cef6a..5f67c83da 100644
--- a/cherokee/error_list.py
+++ b/cherokee/error_list.py
@@ -1300,6 +1300,14 @@
title = "OpenSSL: cannot set cipher list '%s': %s",
desc = SYSTEM_ISSUE)
+e('SSL_CIPHERSUITE',
+ title = "OpenSSL: cannot set ciphersuite list '%s': %s",
+ desc = SYSTEM_ISSUE)
+
+e('SSL_MIN_MAX_PROTOCOL',
+ title = "OpenSSL: cannot set minimum and maximum supported TLS protocol version '%s': %s",
+ desc = SYSTEM_ISSUE)
+
e('SSL_CERTIFICATE',
title = "OpenSSL: cannot use certificate file '%s': %s",
desc = "An error occured while trying to load a certificate into the SSL context structure. Most likely the certificate file is wrong or has been corrupted.")
diff --git a/cherokee/main_worker.c b/cherokee/main_worker.c
index f8610d73f..cd82c2388 100644
--- a/cherokee/main_worker.c
+++ b/cherokee/main_worker.c
@@ -173,42 +173,48 @@ check_for_tls_protocols(cherokee_buffer_t *buf, const char *regex, int options,
static ret_t
add_hardcoded_ssl_options (cherokee_server_t *srv)
{
- unsigned int tls_protocols[] = {
+ unsigned int tls_protocols[] = {
#ifdef SSL_OP_NO_SSLv2
- SSL_OP_NO_SSLv2,
+ SSL_OP_NO_SSLv2,
#endif
#ifdef SSL_OP_NO_SSLv3
- SSL_OP_NO_SSLv3,
+ SSL_OP_NO_SSLv3,
#endif
#ifdef SSL_OP_NO_TLSv1
- SSL_OP_NO_TLSv1,
+ SSL_OP_NO_TLSv1,
#endif
#ifdef SSL_OP_NO_TLSv1_1
- SSL_OP_NO_TLSv1_1,
+ SSL_OP_NO_TLSv1_1,
#endif
#ifdef SSL_OP_NO_TLSv1_2
- SSL_OP_NO_TLSv1_2,
+ SSL_OP_NO_TLSv1_2,
#endif
- 0,
- };
- const char *openssl_tls_options[] = {
+#ifdef SSL_OP_NO_TLSv1_3
+ SSL_OP_NO_TLSv1_3,
+#endif
+ 0,
+ };
+ const char *openssl_tls_options[] = {
#ifdef SSL_OP_NO_SSLv2
- "-ssl2",
+ "-ssl2",
#endif
#ifdef SSL_OP_NO_SSLv3
- "-ssl3",
+ "-ssl3",
#endif
#ifdef SSL_OP_NO_TLSv1
- "-tls1",
+ "-tls1",
#endif
#ifdef SSL_OP_NO_TLSv1_1
- "-tls1_1",
+ "-tls1_1",
#endif
#ifdef SSL_OP_NO_TLSv1_2
- "-tls1_2",
+ "-tls1_2",
+#endif
+#ifdef SSL_OP_NO_TLSv1_3
+ "-tls1_3",
#endif
- NULL
- };
+ NULL
+ };
const char *pattern = "(-(ssl|tls)[[:digit:]]+_*[[:digit:]]*)";
FILE *f;
char tmp[256];
diff --git a/cherokee/virtual_server.c b/cherokee/virtual_server.c
index 18e48c767..2a43cd105 100644
--- a/cherokee/virtual_server.c
+++ b/cherokee/virtual_server.c
@@ -92,6 +92,8 @@ cherokee_virtual_server_new (cherokee_virtual_server_t **vserver, void *server)
cherokee_buffer_init (&n->ciphers);
cherokee_buffer_add_str (&n->ciphers, CHEROKEE_CIPHERS_DEFAULT);
+ cherokee_buffer_init (&n->ciphersuites);
+ cherokee_buffer_add_str (&n->ciphersuites, CHEROKEE_CIPHERSUITES_DEFAULT);
ret = cherokee_buffer_init (&n->root);
if (unlikely(ret < ret_ok))
@@ -119,6 +121,7 @@ cherokee_virtual_server_free (cherokee_virtual_server_t *vserver)
cherokee_buffer_mrproper (&vserver->server_key);
cherokee_buffer_mrproper (&vserver->certs_ca);
cherokee_buffer_mrproper (&vserver->ciphers);
+ cherokee_buffer_mrproper (&vserver->ciphersuites);
if (vserver->flcache != NULL) {
cherokee_flcache_free (vserver->flcache);
@@ -1156,6 +1159,10 @@ configure_virtual_server_property (cherokee_config_node_t *conf, void *data)
return ret_error;
}
+ } else if (equal_buf_str (&conf->key, "ssl_ciphersuites")) {
+ cherokee_buffer_clean (&vserver->ciphersuites);
+ cherokee_buffer_add_buffer (&vserver->ciphersuites, &conf->val);
+
} else if (equal_buf_str (&conf->key, "ssl_ciphers")) {
cherokee_buffer_clean (&vserver->ciphers);
cherokee_buffer_add_buffer (&vserver->ciphers, &conf->val);
diff --git a/cherokee/virtual_server.h b/cherokee/virtual_server.h
index b7eaacb41..8b989caeb 100644
--- a/cherokee/virtual_server.h
+++ b/cherokee/virtual_server.h
@@ -82,6 +82,7 @@ typedef struct {
cherokee_buffer_t certs_ca;
cherokee_req_client_cert_t req_client_certs;
cherokee_buffer_t ciphers;
+ cherokee_buffer_t ciphersuites;
cherokee_boolean_t cipher_server_preference;
cherokee_boolean_t ssl_compression;
cuint_t ssl_dh_length;
diff --git a/configure.ac b/configure.ac
index 9501adb70..96cdeec05 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1090,17 +1090,19 @@ if test "$WITH_OPENSSL" != "no"; then
LIBSSL_LIBS="-L$WITH_OPENSSL/lib"
fi
- AC_CHECK_LIB(crypto, BN_init)
- AC_CHECK_HEADERS([openssl/engine.h])
- AC_CHECK_LIB(ssl, SSL_accept, [have_openssl=yes], [have_openssl=no], $LIBSSL_LIBS)
+ AC_CHECK_LIB(crypto, CRYPTO_new_ex_data, [], [AC_MSG_ERROR([library 'crypto' is required for OpenSSL])])
+ have_openssl="no"
+ AC_CHECK_LIB(ssl, OPENSSL_init_ssl, [have_openssl="yes"])
+ AC_CHECK_LIB(ssl, SSL_library_init, [have_openssl="yes"])
+ AC_CHECK_LIB(ssl, SSL_accept, [have_openssl=yes])
if test "$have_openssl" = "yes"; then
TLS_BACK_END="OpenSSL/libssl"
OPENSSL_PATH="$OPENSSL_PATH$PATH_SEPARATOR$PATH"
LIBSSL_LIBS="$LIBSSL_LIBS -lssl -lcrypto"
- AC_SUBST(LIBSSL_LIBS)
- AC_SUBST(LIBSSL_CFLAGS)
+ AC_SUBST(LIBSSL_LIBS)
+ AC_SUBST(LIBSSL_CFLAGS)
AC_DEFINE(HAVE_OPENSSL, 1, [Have OpenSSL library])
AC_CACHE_CHECK([for a suitable test server to verify $TLS_BACK_END TLS protocol support], [ac_cv_path_OPENSSL_BIN],
@@ -1126,10 +1128,10 @@ if test "$WITH_OPENSSL" != "no"; then
AC_RUN_IFELSE([AC_LANG_PROGRAM([#include ],
[#ifdef TLS_MAX_VERSION
int max = TLS_MAX_VERSION;
- #ifdef TLS1_2_VERSION
- if (max > TLS1_2_VERSION) exit(1);
+ #ifdef TLS1_3_VERSION
+ if (max > TLS1_3_VERSION) exit(1);
#else
- if (max > 0x0303) exit(1);
+ if (max > 0x0304) exit(1);
#endif
#endif])],
[AC_MSG_RESULT(yes)],
@@ -1137,6 +1139,22 @@ if test "$WITH_OPENSSL" != "no"; then
AC_MSG_ERROR([
*** OpenSSL supports more recent versions of SSL/TLS protocols, which are not recognized by Cherokee.
*** Please check for the latest Cherokee source or update Cherokee source code.])])
+
+ AC_SUBST(USE_OPENSSL_MIN_MAX_PROTOCOL, [False])
+ AC_MSG_CHECKING([for SSL_CTX_get_min_proto_version() in $TLS_BACK_END])
+ AC_RUN_IFELSE([AC_LANG_PROGRAM([#include ],
+ [#ifndef SSL_CTRL_GET_MIN_PROTO_VERSION
+ exit(1);
+ #endif])],
+ [AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_SSL_CTX_GET_MIN_PROTO_VERSION, [1], [OpenSSL/libssl has SSL_CTX_get_min_proto_version() function])
+ AC_SUBST(USE_OPENSSL_MIN_MAX_PROTOCOL, [True])],
+ [AC_MSG_RESULT(no)])
+
+ AC_CHECK_LIB(ssl, SSL_CTX_set_ciphersuites, [have_openssl_ciphersuites=yes], [have_openssl_ciphersuites=no])
+ if test "$have_openssl_ciphersuites" = "yes"; then
+ AC_DEFINE(HAVE_SSL_CTX_SET_CIPHERSUITES, 1, [OpenSSL/libssl has SSL_CTX_set_ciphersuites() function])
+ fi
fi
fi
AC_MSG_CHECKING(for libssl)
From 92d543d0468ca80837325be9575e3f1eb7a77f08 Mon Sep 17 00:00:00 2001
From: Thomas Reim
Date: Thu, 1 Apr 2021 16:48:39 +0200
Subject: [PATCH 05/14] Cherokee Admin: Configuration of new OpenSSL 1.1.1
features
The Advanced page has been restructured and is now displayed in two flavours:
1.) OpenSSL version 1.1.1 and later
Configuration of SSL/TLS protocols is now focused on setting minimum and
maximum available protocol versions. Since OpenSSL 1.1.1 disabling of
selected has been deprecated. This section plus a warning has been moved to
the page's bottom.
2.) OpenSSL version 1.1.0 and below
System administrator still have to disable selected SSL/TLS protocol
versions that Cherokee webserver should not offer to its clients.
TLS v1.3 has been added to the page.
On the Virtual Server page Ciphersuites have been added and the hint where to
find suited and safe cipher sets has been adapted to recommend Mozilla
Intermediate compatibility ciphers for OpenSSL 1.1.1 and later. Mozilla
Old compatibility ciphers are recommended of using OpenSSL version 1.1.0 and
below as TLS back-end.
Fixes: https://github.com/cherokee/webserver/issues/1256
Signed-off-by: Thomas Reim
---
admin/Makefile.am | 4 ++-
admin/PageAdvanced.py | 67 +++++++++++++++++++++++++++++++++++------
admin/PageVServer.py | 11 ++++++-
admin/configured.py.pre | 1 +
4 files changed, 72 insertions(+), 11 deletions(-)
diff --git a/admin/Makefile.am b/admin/Makefile.am
index 3109e7287..0a090e981 100644
--- a/admin/Makefile.am
+++ b/admin/Makefile.am
@@ -9,13 +9,15 @@ endif
SUFFIXES = .py.pre .py
if USE_OPENSSL
+minmaxtls = @USE_OPENSSL_MIN_MAX_PROTOCOL@
tlsprotocols = @OPENSSL_TLS_PROTOCOLS@
else
+minmaxtls = False
tlsprotocols =
endif
.py.pre.py:
- sed -e "s|%sysconfdir%|${sysconfdir}|g; s|%sbindir%|${sbindir}|g; s|%docdir%|${docdir}|g; s|%prefix%|${prefix}|g; s|%localstatedir%|${localstatedir}|g; s|%libdir%|${libdir}|g; s|%wwwroot%|${WWW_ROOT}|g; s|%version%|${PACKAGE_VERSION}|g; s|%phpcgi%|${PHPCGI}|g; s|%datadir%|${datadir}|g; s|%localedir%|${localedir}|g; s|%tlsprotocols%|$(tlsprotocols)|g" $< > $@
+ sed -e "s|%sysconfdir%|${sysconfdir}|g; s|%sbindir%|${sbindir}|g; s|%docdir%|${docdir}|g; s|%prefix%|${prefix}|g; s|%localstatedir%|${localstatedir}|g; s|%libdir%|${libdir}|g; s|%wwwroot%|${WWW_ROOT}|g; s|%version%|${PACKAGE_VERSION}|g; s|%phpcgi%|${PHPCGI}|g; s|%datadir%|${datadir}|g; s|%localedir%|${localedir}|g; s|%minmaxtls%|$(minmaxtls)|g; s|%tlsprotocols%|$(tlsprotocols)|g" $< > $@
PY_PRE = \
configured.py.pre
diff --git a/admin/PageAdvanced.py b/admin/PageAdvanced.py
index 7b977e833..074b52105 100644
--- a/admin/PageAdvanced.py
+++ b/admin/PageAdvanced.py
@@ -39,7 +39,8 @@
('SSLv3', 'SSL version 3'),
('TLSv1', 'TLS version 1'),
('TLSv1.1', 'TLS version 1.1'),
- ('TLSv1.2', 'TLS version 1.2')
+ ('TLSv1.2', 'TLS version 1.2'),
+ ('TLSv1.3', 'TLS version 1.3')
]
# Keep aligned with cherokee_cryptor_init_base()
@@ -48,7 +49,8 @@
'SSLv3': True,
'TLSv1': False,
'TLSv1.1': False,
- 'TLSv1.2': False
+ 'TLSv1.2': False,
+ 'TLSv1.3': False
}
TLS_WARNING = N_("""WARNING: The SSL/TLS back-end supports more recent
@@ -81,6 +83,7 @@
("server!tls!protocol!TLSv1", validations.is_boolean),
("server!tls!protocol!TLSv1_1", validations.is_boolean),
("server!tls!protocol!TLSv1_2", validations.is_boolean),
+ ("server!tls!protocol!TLSv1_3", validations.is_boolean),
("server!tls!timeout_handshake", validations.is_positive_int),
("server!tls!dh_param512", validations.is_local_file_exists),
("server!tls!dh_param1024", validations.is_local_file_exists),
@@ -124,13 +127,19 @@
NOTE_TLS_TLSv1 = N_('TLSv1 is deprecated')
NOTE_TLS_TLSv1_1 = N_('TLSv1.1 is deprecated')
NOTE_TLS_TLSv1_2 = N_(' ')
+NOTE_TLS_TLSv1_3 = N_(' ')
+NOTE_TLS_DISABLE = N_('The following options work in combination with above min/max TLS protocol version settings. '
+ 'They are deprecated, only set the minimum and maximum supported protocol versions instead.')
+NOTE_TLS_MIN = N_('Minimum required TLS protocol version from clients (Default: auto-configured by libssl)')
+NOTE_TLS_MAX = N_('Maximum supported TLS protocol version (Default: auto-configured by libssl)')
TLS_PROTOCOL_NOTES = {
'SSLv2': NOTE_TLS_SSLv2,
'SSLv3': NOTE_TLS_SSLv3,
'TLSv1': NOTE_TLS_TLSv1,
'TLSv1.1': NOTE_TLS_TLSv1_1,
- 'TLSv1.2': NOTE_TLS_TLSv1_2
+ 'TLSv1.2': NOTE_TLS_TLSv1_2,
+ 'TLSv1.3': NOTE_TLS_TLSv1_3
}
HELPS = [('config_advanced', N_('Advanced'))]
@@ -235,17 +244,53 @@ def add_tls_version_config (self, table):
else:
table.Add ('%s%s' % (_('Disable '), protocol[1]), CTK.CheckCfgText('server!tls!protocol!' + protocol[0].replace('.','_'), True, _("Protocol deacrivated") if protocol[0].startswith('SSLv') else _("Protocol not supported"), props_disable), TLS_PROTOCOL_NOTES[protocol[0]])
+ @staticmethod
+ def add_tls_version_disable_section (self):
+ props = {}
+ props = {'mode': 'inverse'}
+ props_disable = props.copy()
+ props_disable["disabled"] = 1
+ table = CTK.PropsAuto(URL_APPLY)
+ for protocol in KNOWN_TLS_PROTOCOLS:
+ if self.available_tls_protocols.has_key(protocol[0]):
+ table.Add ('%s%s' % (_('Disable '), self.available_tls_protocols[protocol[0]]), CTK.CheckCfgText('server!tls!protocol!' + protocol[0].replace('.','_'), TLS_PROTOCOL_CHECKBOX_DEFAULT[protocol[0]], _("Disable"), props), "")
+ else:
+ table.Add ('%s%s' % (_('Disable '), protocol[1]), CTK.CheckCfgText('server!tls!protocol!' + protocol[0].replace('.','_'), True, _("Protocol deactivated"), props_disable), "")
+
+ self += CTK.RawHTML ("
%s
" %(_('Turn off selected TLS Protocols (Deprecated)')))
+ tip = '%s' %(_(NOTE_TLS_DISABLE))
+ props = {'class': 'dialog-information-slim'}
+ self += CTK.Indenter (CTK.Notice ('information', CTK.RawHTML(tip), props))
+ self += CTK.Indenter(table)
+
def __init__ (self):
CTK.Container.__init__ (self)
+ tls_min_protocols = [('auto', N_('auto-configured'))]
+ tls_max_protocols = [('auto', N_('auto-configured'))]
+
+ if MINMAXTLS == True and KNOWN_TLS_PROTOCOLS[0][0] == 'SSLv2':
+ # Staring with OpenSSL 1.1.1, i. e. MINMAXTLS = True SSLv2 has been removed
+ KNOWN_TLS_PROTOCOLS.pop(0)
self.available_tls_protocols, self.unknown_tls_protocols = TLSWidget.check_for_tls_protocol_versions (self)
table = CTK.PropsAuto(URL_APPLY)
- TLSWidget.add_tls_version_config (self, table)
- table.Add (_('Handshake Timeout'), CTK.TextCfg('server!tls!timeout_handshake', True), _(NOTE_TLS_TIMEOUT))
- table.Add (_('DH parameters: 512 bits'), CTK.TextCfg('server!tls!dh_param512', True), _(NOTE_DH512))
- table.Add (_('DH parameters: 1024 bits'), CTK.TextCfg('server!tls!dh_param1024', True), _(NOTE_DH1024))
- table.Add (_('DH parameters: 2048 bits'), CTK.TextCfg('server!tls!dh_param2048', True), _(NOTE_DH2048))
- table.Add (_('DH parameters: 4096 bits'), CTK.TextCfg('server!tls!dh_param4096', True), _(NOTE_DH4096))
+ if MINMAXTLS == False:
+ TLSWidget.add_tls_version_config (self, table)
+ else:
+ for protocol in KNOWN_TLS_PROTOCOLS:
+ if self.available_tls_protocols.has_key(protocol[0]):
+ tls_min_protocols.append((protocol[0], N_(self.available_tls_protocols[protocol[0]])))
+ table.Add (_('Min. TLS protocol version'), CTK.ComboCfg('server!tls!protocol!min', trans_options(tls_min_protocols)), _(NOTE_TLS_MIN))
+ KNOWN_TLS_PROTOCOLS.reverse()
+ for protocol in KNOWN_TLS_PROTOCOLS:
+ if self.available_tls_protocols.has_key(protocol[0]):
+ tls_max_protocols.append((protocol[0], N_(self.available_tls_protocols[protocol[0]])))
+ table.Add (_('Max. TLS protocol version'), CTK.ComboCfg('server!tls!protocol!max', trans_options(tls_max_protocols)), _(NOTE_TLS_MAX))
+ table.Add (_('Handshake Timeout'), CTK.TextCfg('server!tls!timeout_handshake', True), _(NOTE_TLS_TIMEOUT))
+ table.Add (_('DH parameters: 512 bits'), CTK.TextCfg('server!tls!dh_param512', True), _(NOTE_DH512))
+ table.Add (_('DH parameters: 1024 bits'), CTK.TextCfg('server!tls!dh_param1024', True), _(NOTE_DH1024))
+ table.Add (_('DH parameters: 2048 bits'), CTK.TextCfg('server!tls!dh_param2048', True), _(NOTE_DH2048))
+ table.Add (_('DH parameters: 4096 bits'), CTK.TextCfg('server!tls!dh_param4096', True), _(NOTE_DH4096))
self += CTK.RawHTML ("%s
" %(_('TLS')))
if len(self.unknown_tls_protocols) > 0:
tip = '%s %s' % (_(TLS_WARNING), join(self.unknown_tls_protocols))
@@ -256,6 +301,10 @@ def __init__ (self):
else:
self += CTK.Indenter(table)
+ if MINMAXTLS == True:
+ KNOWN_TLS_PROTOCOLS.reverse()
+ TLSWidget.add_tls_version_disable_section (self)
+
class Render:
def __call__ (self):
tabs = CTK.Tab()
diff --git a/admin/PageVServer.py b/admin/PageVServer.py
index 5c4246161..ab1be7f9c 100644
--- a/admin/PageVServer.py
+++ b/admin/PageVServer.py
@@ -41,11 +41,15 @@
URL_BASE = '/vserver/content'
URL_APPLY = '/vserver/content/apply'
+URL_OPENSSL_REFERENCE = '%s' % (_('Reference'))
+URL_MOZILLA = 'Mozilla'
+
NOTE_NICKNAME = N_('Nickname for the virtual server.')
NOTE_CERT = N_('This directive points to the PEM-encoded Certificate file for the server (Full path to the file)')
NOTE_CERT_KEY = N_('PEM-encoded Private Key file for the server (Full path to the file)')
NOTE_CA_LIST = N_('File containing the trusted CA certificates, utilized for checking the client certificates (Full path to the file)')
NOTE_CIPHERS = N_('Ciphers that TLSv1.2 and below is allowed to use.')
+NOTE_CIPHERSUITES = N_('Ciphersuites that TLSv1.3 is allowed to use.')
NOTE_CIPHER_SERVER_PREFERENCE = N_('The cipher sequence that is specified by the server should have preference over the client preference. (Default: True).')
NOTE_COMPRESSION = N_('Explicitly enable or disable serverside compression support. (Default: Disabled).')
NOTE_DH_LENGTH = N_('Explicitely sets the Diffie-Hellman parameters length. (Default: Let openssl choose).')
@@ -661,9 +665,14 @@ def __init__ (self, vsrv_num, refreshable):
self += CTK.RawHTML ('%s
' % (_('Required SSL/TLS Values')))
self += CTK.Indenter (submit)
+ CIPHER_COMP = 'Intermediate Compatibility'
+ if MINMAXTLS == False:
+ CIPHER_COMP = 'Old Compatibility'
+
# Advanced options
table = CTK.PropsTable()
- table.Add (_('Ciphers'), CTK.TextCfg ('%s!ssl_ciphers' %(pre), True), _(NOTE_CIPHERS))
+ table.Add (_('Ciphersuites'), CTK.TextCfg ('%s!ssl_ciphersuites' %(pre), True), '%s %s. %s: %s Intermediate Compatibility %s.' % (_(NOTE_CIPHERSUITES), URL_OPENSSL_REFERENCE, _('Default'), URL_MOZILLA, _('Ciphersuites')))
+ table.Add (_('Ciphers'), CTK.TextCfg ('%s!ssl_ciphers' %(pre), True), '%s %s. %s: %s %s %s' % (_(NOTE_CIPHERS), URL_OPENSSL_REFERENCE, _('Default'), URL_MOZILLA, CIPHER_COMP, _('Ciphers')))
table.Add (_('Server Preference'), CTK.CheckCfgText ('%s!ssl_cipher_server_preference' % (pre), True, _('Prefer')), _(NOTE_CIPHER_SERVER_PREFERENCE))
table.Add (_('Client Certs. Request'), CTK.ComboCfg('%s!ssl_client_certs' %(pre), trans_options(CLIENT_CERTS)), _(NOTE_CLIENT_CERTS))
table.Add (_('Compression'), CTK.CheckCfgText ('%s!ssl_compression' % (pre), False, _('Enable')), _(NOTE_COMPRESSION))
diff --git a/admin/configured.py.pre b/admin/configured.py.pre
index 364c9031f..e3cc8e593 100644
--- a/admin/configured.py.pre
+++ b/admin/configured.py.pre
@@ -10,6 +10,7 @@ SYSCONFDIR = "%sysconfdir%"
LOCALSTATE = "%localstatedir%"
VERSION = "%version%"
TLSPROTOCOLS = "%tlsprotocols%"
+MINMAXTLS = %minmaxtls%
CHEROKEE_SERVER = join (PREFIX, "sbin/cherokee")
CHEROKEE_WORKER = join (PREFIX, "sbin/cherokee-worker")
From dd1d6669aa055bf4a0a1badc09df434e5c9b396b Mon Sep 17 00:00:00 2001
From: Thomas Reim
Date: Thu, 1 Apr 2021 17:11:00 +0200
Subject: [PATCH 06/14] Remove prohibited use of SSL protocols from
configuration file
Vulnerable and insecure SSL protocols SSLv2 and SSLv3 must not be used
by applications (RFC7568 and RFC6176).
Recent scans of the Internet at large show that more than 5.9 million Web
servers, comprising 17 percent of all HTTPS-protected machines, directly support
SSLv2. That's a troubling finding, given widely repeated advice that SSLv2 be
disabled. More troubling still, even when a server doesn't allow SSLv2
connections, it may still be susceptible to attack if the underlying RSA key
pair is reused on a separate server that does support the old protocol.
A website, e. g., that forbids SSLv2 may still be vulnerable if its key is used
on an e-mail server that allows SSLv2.
Cherokee disables both protocols by default. Users can override this by
explicitly enabling the protocols in the configuration file. On the other
hand IETF does not allow to use both SSL protocols anymore due to their
vulnerabilities, which also affect security of more recent TLS protocols.
Remove SSLv2 and SSLv3 settings from existing (legacy) configuration files
during installation to allow safe operation of Cherokee webserver.
Users that for whatever reason still require use of SSLv2 or SSLv3 can
enable the protocol again after installation using cherokee-admin.
Note: Use of SSLv2 or SSLv3 also depends on OpenSSL. Recent versions
of OpenSSL removed SSLv2 and most distribution now also disable
SSLv3.
This patch adds a new mechanism to Cherokee that allows for intermediate
security updates of the configuration file when a regular update to a new
Cherokee version is not (yet) available.
Fixes: https://github.com/cherokee/webserver/issues/1253
Signed-off-by: Thomas Reim
---
admin/config_version.py | 49 +++++++++++++++++++++++++++++++++++++++++
admin/upgrade_config.py | 11 ++++++++-
2 files changed, 59 insertions(+), 1 deletion(-)
diff --git a/admin/config_version.py b/admin/config_version.py
index 687348eaa..2f6346f2d 100644
--- a/admin/config_version.py
+++ b/admin/config_version.py
@@ -111,6 +111,37 @@ def upgrade_to_1_2_103 (cfg):
cfg.get_val('vserver!%s!rule!%s!match!directory'%(v,r)) == "/icons":
cfg['vserver!%s!rule!%s!match!directory'%(v,r)] = '/cherokee_icons'
+PROHIBITED_TLS_PROTOCOLS = [('SSLv2', 'SSL version 2'),
+ ('SSLv3', 'SSL version 3')
+ ]
+def update_tls_protocols (cfg):
+ # Vulnerable and insecure SSL protocols SSLv2 and SSLv3 must not be used
+ # by applications (RFC7568 and RFC6176).
+ # Cherokee now diables both protocols by default. Users can override this by
+ # explicitly enabling the protocols in the configuration file. On the other
+ # hand IETF does not allow to use both SSL protocols anymore due to their
+ # vulnerabilities, which also affects security of more recent TLS protocols.
+ # Search for SSLv2 and SSLv3 settings and remove them in order to apply
+ # Cherokee's safe default TLS protocol settings.
+ retVal = False
+ for protocol in PROHIBITED_TLS_PROTOCOLS:
+ v = cfg.pop('server!tls!protocol!%s' % protocol[0])
+ if v is not None:
+ if v.isdigit() and bool(int(v) == True):
+ print "\n" " WARNING!! %s was enabled in the onfiguration file. This SSL/TLS protocol is " % protocol[1]
+ print " obsolete and must not be used anymore. Configuration entry removed."
+ print " In case of need you can temporarily enable %s again using cherokee-admin." % protocol[0]
+ retVal = True
+ return retVal
+
+# Converts to 1.2.104 or later (if any)
+def upgrade_to_1_2_105 (cfg):
+ # Placeholder for required configuration file updates in a future version
+ # following 1.2.104:
+ # - Remove explicit use of insecure SSL/TLS protocols that are not allowed
+ # by IETF
+ update_tls_protocols(cfg)
+
def config_version_get_current():
ver = configured.VERSION.split ('b')[0]
v1,v2,v3 = ver.split (".")
@@ -151,6 +182,20 @@ def config_version_cfg_is_up_to_date (cfg):
return False
+def config_version_cfg_sanity_check (cfg):
+ # Critical corrections and updates of existing configuration files to be
+ # applied during Cherokee installation independent of a version change
+ # - Remove explicit use of insecure SSL/TLS protocols that are not allowed
+ # by IETF
+
+ # Do not proceed if it's empty
+ if not cfg.has_tree():
+ return False
+
+ # Perform
+ return update_tls_protocols (cfg)
+
+
def config_version_update_cfg (cfg):
# Do not proceed if it's empty
if not cfg.has_tree():
@@ -192,5 +237,9 @@ def config_version_update_cfg (cfg):
if ver_config_i < 1002103:
upgrade_to_1_2_103 (cfg)
+ # Update to.. post 1.2.104
+ if ver_config_i < 1002105:
+ upgrade_to_1_2_105 (cfg)
+
cfg["config!version"] = ver_release_s
return True
diff --git a/admin/upgrade_config.py b/admin/upgrade_config.py
index 2ea37e572..51c4f5162 100755
--- a/admin/upgrade_config.py
+++ b/admin/upgrade_config.py
@@ -30,7 +30,7 @@ def main():
# Parse the configuration file
cfg = Config(cfg_file)
- # Update the configuration file if needed
+ # Upgrade the configuration file if needed
ver_config = int (cfg.get_val('config!version', '000099028'))
ver_release = int (config_version_get_current())
@@ -40,6 +40,15 @@ def main():
updated = config_version_update_cfg (cfg)
print ["Not upgraded.", "Upgraded."][updated]
+ # Critical corrections and updates of existing configuration files
+ # if required
+ if updated == False:
+
+ print "Applying security updates to '%s'.." % (cfg_file),
+
+ updated = config_version_cfg_sanity_check (cfg)
+ print ["No update required.", "Updated."][updated]
+
# Save it
if updated:
print "Saving new configuration..",
From 687f5dc28ee1d2559bd36d59276aea648337902a Mon Sep 17 00:00:00 2001
From: Thomas Reim
Date: Thu, 1 Apr 2021 17:25:35 +0200
Subject: [PATCH 07/14] Fix build with local installation of OpenSSL
Compile and linker flags are not correctly set when building Cherokee webserver
using a local (additional) installation of OpenSSL (e. g. in /usr/local/openssl).
When users pass an OpenSSL path, with option, e. g., --with-libssl=/usr/local/openssl
Cherokee checks for existence of:
- libssl and libcrypto in /usr/local/openssl/lib
- openssl in /usr/local/openssl/bin
During further build process these paths are used to analyze, compile and link
Cherokee against the correct TLS back-end.
This patch also adds some further checks of OpenSSL header files.
Fixes: https://github.com/cherokee/webserver/issues/1251
Signed-off-by: Thomas Reim
---
configure.ac | 28 ++++++++++++++++++++++++----
1 file changed, 24 insertions(+), 4 deletions(-)
diff --git a/configure.ac b/configure.ac
index 96cdeec05..5844812a9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1085,17 +1085,35 @@ AC_ARG_WITH(libssl,
have_openssl=no
if test "$WITH_OPENSSL" != "no"; then
+ dnl backup the pre-ssl variables
+ CLEANLDFLAGS="$LDFLAGS"
+ CLEANCFLAGS="$CFLAGS"
+ CLEANLIBS="$LIBS"
if test "$WITH_OPENSSL" != "yes"; then
- LIBSSL_CFLAGS="-I$WITH_OPENSSL/include"
+ LIBSSL_CFLAGS="-I$WITH_OPENSSL/include -I$WITH_OPENSSL/include/openssl"
LIBSSL_LIBS="-L$WITH_OPENSSL/lib"
+ LIBCRYPTO_LIBS="-L$WITH_OPENSSL/lib"
+ OPENSSL_PATH="$WITH_OPENSSL/bin"
fi
+ CFLAGS="$CFLAGS $LIBSSL_CFLAGS"
+ LDFLAGS="$LDFLAGS $LIBSSL_LIBS"
+
AC_CHECK_LIB(crypto, CRYPTO_new_ex_data, [], [AC_MSG_ERROR([library 'crypto' is required for OpenSSL])])
have_openssl="no"
AC_CHECK_LIB(ssl, OPENSSL_init_ssl, [have_openssl="yes"])
AC_CHECK_LIB(ssl, SSL_library_init, [have_openssl="yes"])
AC_CHECK_LIB(ssl, SSL_accept, [have_openssl=yes])
+ if test "$have_openssl" = "yes"; then
+ AC_CHECK_HEADERS(openssl/dh.h openssl/rand.h openssl/crypto.h \
+ openssl/lhash.h openssl/ssl.h openssl/err.h,
+ AC_DEFINE(HAVE_OPENSSL, 1, [Have OpenSSL/libssl library]),
+ have_openssl="no"
+ )
+ fi
+ AS_IF([test "x$have_openssl" = xno], [AC_MSG_ERROR([library 'ssl' is required for OpenSSL])])
+
if test "$have_openssl" = "yes"; then
TLS_BACK_END="OpenSSL/libssl"
OPENSSL_PATH="$OPENSSL_PATH$PATH_SEPARATOR$PATH"
@@ -1104,7 +1122,6 @@ if test "$WITH_OPENSSL" != "no"; then
AC_SUBST(LIBSSL_LIBS)
AC_SUBST(LIBSSL_CFLAGS)
- AC_DEFINE(HAVE_OPENSSL, 1, [Have OpenSSL library])
AC_CACHE_CHECK([for a suitable test server to verify $TLS_BACK_END TLS protocol support], [ac_cv_path_OPENSSL_BIN],
[AC_PATH_PROGS_FEATURE_CHECK([OPENSSL_BIN], [openssl],
[v=$($ac_path_OPENSSL_BIN s_server -help 2>&1)
@@ -1155,10 +1172,13 @@ if test "$WITH_OPENSSL" != "no"; then
if test "$have_openssl_ciphersuites" = "yes"; then
AC_DEFINE(HAVE_SSL_CTX_SET_CIPHERSUITES, 1, [OpenSSL/libssl has SSL_CTX_set_ciphersuites() function])
fi
+
+ AC_CHECK_HEADERS([openssl/engine.h])
fi
+ # restore CFLAGS and LDFLAGS
+ CFLAGS="${CLEANCFLAGS}"
+ LDFLAGS="${CLEANLDFLAGS}"
fi
-AC_MSG_CHECKING(for libssl)
-AC_MSG_RESULT([$have_openssl])
AM_CONDITIONAL(USE_OPENSSL, test "x$have_openssl" = "xyes")
From 9c4f0006cfc1a02fcdc3243baf25da64db8f61e8 Mon Sep 17 00:00:00 2001
From: Thomas Reim
Date: Thu, 1 Apr 2021 17:48:52 +0200
Subject: [PATCH 08/14] Cherokee Trace function ignores SSL/TLS
OpenSSL silently discards invalid ciphers that are provided
within the configured cipher set list. The actually available SSL/TLS
protocols and TLS v1.3 ciphersuites for a virtual server are important
information for system administrators in case of Cherokee not accepting
HTTPS connections.
Without this information it is extremely difficult to investigate on the
root cause of encrypt web traffic issues in the network.
Trace information for each virtual server on the main SSL/TLS configuration
settings.
Fixes: https://github.com/cherokee/webserver/issues/1252
Signed-off-by: Thomas Reim
---
cherokee/cryptor_libssl.c | 203 +++++++++++++++++++++++++++++++++++++-
cherokee/error_list.py | 4 +
2 files changed, 206 insertions(+), 1 deletion(-)
diff --git a/cherokee/cryptor_libssl.c b/cherokee/cryptor_libssl.c
index 088cdd0e9..5ad567e1b 100644
--- a/cherokee/cryptor_libssl.c
+++ b/cherokee/cryptor_libssl.c
@@ -480,6 +480,168 @@ verify_tolerate_cb(int preverify_ok, X509_STORE_CTX *x509_store)
return 1;
}
+#ifdef TRACE_ENABLED
+static void
+trace_libssl_tls_settings(SSL_CTX *ctx, cherokee_virtual_server_t *vsrv)
+{
+ long options;
+ int verify_mode = SSL_VERIFY_NONE;
+ int libssl_security_level;
+ int version;
+ const char *trace_str;
+
+ cherokee_buffer_t tmp = CHEROKEE_BUF_INIT;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ cherokee_buffer_add_va (&tmp, "libssl security level for vserver %s: ",
+ vsrv->name.buf);
+ libssl_security_level = SSL_CTX_get_security_level (ctx);
+ switch (libssl_security_level) {
+ case 0:
+ cherokee_buffer_add_va (&tmp,
+ "%d (no SSL/TLS protocol restrictions)",
+ libssl_security_level);
+ break;
+ case 1:
+ cherokee_buffer_add_va (&tmp,
+ "%d (SSLv2 not allowed)",
+ libssl_security_level);
+ break;
+ case 2:
+ cherokee_buffer_add_va (&tmp,
+ "%d (%s and below not allowed)",
+ libssl_security_level, SSL_TXT_SSLV3);
+ break;
+ case 3:
+ cherokee_buffer_add_va (&tmp,
+ "%d (%s and below not allowed)",
+ libssl_security_level, SSL_TXT_TLSV1);
+ break;
+ case 4:
+ cherokee_buffer_add_va (&tmp,
+ "%d (%s and below not allowed)",
+ libssl_security_level, SSL_TXT_TLSV1_1);
+ break;
+ case 5:
+ cherokee_buffer_add_va (&tmp,
+ "%d (%s and below not allowed)",
+ libssl_security_level, SSL_TXT_TLSV1_1);
+ break;
+ default:
+ cherokee_buffer_add_str (&tmp, "unknown libssl security level");
+ break;
+ }
+ TRACE(ENTRIES, "%s\n", tmp.buf);
+
+ cherokee_buffer_clean(&tmp);
+
+ CLEAR_LIBSSL_ERRORS;
+#endif
+#ifdef HAVE_SSL_CTX_GET_MIN_PROTO_VERSION
+ cherokee_buffer_add_va (&tmp,
+ "Minimum supported TLS/SSL protocol version for vserver %s: ",
+ vsrv->name.buf);
+ version = SSL_CTX_get_min_proto_version (ctx);
+ if (version > 0) {
+ if (_tls_protocol_version_to_text(version, &tmp) != ret_ok) {
+ cherokee_buffer_add_str (&tmp, "unknown protocol");
+ }
+ }
+ if (CRYPTOR_VSRV_MIN_TLS_PROTOCOL(vsrv) == 0) {
+ if (version > 0) {
+ cherokee_buffer_add_str (&tmp, " (");
+ }
+ cherokee_buffer_add_str (&tmp, "auto-configured by libssl");
+ if (version > 0) {
+ cherokee_buffer_add_char (&tmp, ')');
+ }
+ }
+ TRACE(ENTRIES, "%s\n", tmp.buf);
+
+ cherokee_buffer_clean(&tmp);
+
+ cherokee_buffer_add_va (&tmp,
+ "Maximum supported TLS/SSL protocol version for vserver %s: ",
+ vsrv->name.buf);
+ version = SSL_CTX_get_max_proto_version (ctx);
+ if (version > 0) {
+ if (_tls_protocol_version_to_text(version, &tmp) != ret_ok) {
+ cherokee_buffer_add_str (&tmp, "unknown protocol");
+ }
+ }
+ if (CRYPTOR_VSRV_MAX_TLS_PROTOCOL(vsrv) == 0) {
+ if (version > 0) {
+ cherokee_buffer_add_str (&tmp, " (");
+ }
+ cherokee_buffer_add_str (&tmp, "auto-configured by libssl");
+ if (version > 0) {
+ cherokee_buffer_add_char (&tmp, ')');
+ }
+ }
+ TRACE(ENTRIES, "%s\n", tmp.buf);
+
+ cherokee_buffer_clean(&tmp);
+
+ CLEAR_LIBSSL_ERRORS;
+#endif
+
+ options = SSL_CTX_get_options (ctx);
+
+#ifdef SSL_OP_NO_SSLv2
+ if (options & SSL_OP_NO_SSLv2) {
+ if (! cherokee_buffer_is_empty(&tmp)) {
+ cherokee_buffer_add_str (&tmp, ", ");
+ }
+ cherokee_buffer_add_str (&tmp, "SSLv2");
+ }
+#endif
+#ifdef SSL_OP_NO_SSLv3
+ if (PROTOCOL_SWITCHED_OFF(SSL_OP_NO_SSLv3, options, vsrv)) {
+ if (! cherokee_buffer_is_empty(&tmp)) {
+ cherokee_buffer_add_str (&tmp, ", ");
+ }
+ cherokee_buffer_add_str (&tmp, SSL_TXT_SSLV3);
+ }
+#endif
+#ifdef SSL_OP_NO_TLSv1
+ if (PROTOCOL_SWITCHED_OFF(SSL_OP_NO_TLSv1, options, vsrv)) {
+ if (! cherokee_buffer_is_empty(&tmp)) {
+ cherokee_buffer_add_str (&tmp, ", ");
+ }
+ cherokee_buffer_add_str (&tmp, SSL_TXT_TLSV1);
+ }
+#endif
+#ifdef SSL_OP_NO_TLSv1_1
+ if (PROTOCOL_SWITCHED_OFF(SSL_OP_NO_TLSv1_1, options, vsrv)) {
+ if (! cherokee_buffer_is_empty(&tmp)) {
+ cherokee_buffer_add_str (&tmp, ", ");
+ }
+ cherokee_buffer_add_str (&tmp, SSL_TXT_TLSV1_1);
+ }
+#endif
+#ifdef SSL_OP_NO_TLSv1_2
+ if (PROTOCOL_SWITCHED_OFF(SSL_OP_NO_TLSv1_2, options, vsrv)) {
+ if (! cherokee_buffer_is_empty(&tmp)) {
+ cherokee_buffer_add_str (&tmp, ", ");
+ }
+ cherokee_buffer_add_str (&tmp, SSL_TXT_TLSV1_2);
+ }
+#endif
+#ifdef SSL_OP_NO_TLSv1_3
+ if (PROTOCOL_SWITCHED_OFF(SSL_OP_NO_TLSv1_3, options, vsrv)) {
+ if (! cherokee_buffer_is_empty(&tmp)) {
+ cherokee_buffer_add_str (&tmp, ", ");
+ }
+ cherokee_buffer_add_str (&tmp, "TLSv1.3");
+ }
+#endif
+ TRACE(ENTRIES, "TLS/SSL protocols switched off for vserver %s: %s\n",
+ vsrv->name.buf, tmp.buf);
+ cherokee_buffer_mrproper(&tmp);
+
+ CLEAR_LIBSSL_ERRORS;
+}
+#endif
+
static ret_t
_vserver_new (cherokee_cryptor_t *cryp,
cherokee_virtual_server_t *vsrv,
@@ -490,6 +652,7 @@ _vserver_new (cherokee_cryptor_t *cryp,
const char *error;
long options;
int verify_mode = SSL_VERIFY_NONE;
+ STACK_OF(SSL_CIPHER) *libssl_ciphers = NULL;
#if !defined(OPENSSL_NO_EC) && OPENSSL_VERSION_NUMBER >= 0x0090800L && OPENSSL_VERSION_NUMBER < 0x10002000L
EC_KEY *ecdh;
#endif
@@ -662,6 +825,17 @@ _vserver_new (cherokee_cryptor_t *cryp,
SSL_CTX_set_options (n->context, options);
+ CLEAR_LIBSSL_ERRORS;
+
+ /* OpenSSL is significantly changing and improving communication
+ security and privacy. So do Linux distributions by enabling
+ or disabling OpenSSL capabilities. This is especially true
+ for support of TLS/SSL protocols.
+ */
+#ifdef TRACE_ENABLED
+ trace_libssl_tls_settings(n->context, vsrv);
+#endif
+
/* Set cipher list that vserver will accept.
*/
if (! cherokee_buffer_is_empty (&vsrv->ciphers)) {
@@ -669,7 +843,7 @@ _vserver_new (cherokee_cryptor_t *cryp,
if (rc != 1) {
OPENSSL_LAST_ERROR(error);
LOG_ERROR(CHEROKEE_ERROR_SSL_CIPHER,
- vsrv->ciphers.buf, error);
+ vsrv->ciphers.buf, error);
goto error;
}
}
@@ -688,6 +862,33 @@ _vserver_new (cherokee_cryptor_t *cryp,
}
#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ /* OpenSSL silently discards invalid ciphers within the provided
+ cipher list. This may lead to fatal handshake errors without
+ proper indication about the root cause to users.
+ */
+ libssl_ciphers = SSL_CTX_get_ciphers(n->context);
+ if (libssl_ciphers == NULL) {
+ LOG_WARNING (CHEROKEE_ERROR_SSL_NOCIPHERS);
+# ifdef TRACE_ENABLED
+ } else {
+ const char *cname;
+ cherokee_buffer_t tmp = CHEROKEE_BUF_INIT;
+ for(int i=0; iname.buf, tmp.buf);
+
+ cherokee_buffer_mrproper(&tmp);
+# endif
+ }
+#endif
+
CLEAR_LIBSSL_ERRORS;
/* Certificate
diff --git a/cherokee/error_list.py b/cherokee/error_list.py
index 5f67c83da..acad7f35a 100644
--- a/cherokee/error_list.py
+++ b/cherokee/error_list.py
@@ -1308,6 +1308,10 @@
title = "OpenSSL: cannot set minimum and maximum supported TLS protocol version '%s': %s",
desc = SYSTEM_ISSUE)
+e('SSL_NOCIPHERS',
+ title = "OpenSSL: no ciphers available for TLS/SSL encryption",
+ desc = "TLS/SSL cannot be used. Please check your cypher and cyphersuite configuration.")
+
e('SSL_CERTIFICATE',
title = "OpenSSL: cannot use certificate file '%s': %s",
desc = "An error occured while trying to load a certificate into the SSL context structure. Most likely the certificate file is wrong or has been corrupted.")
From 4609a818490d106f9d92a265127cfb29f88911b5 Mon Sep 17 00:00:00 2001
From: Thomas Reim
Date: Thu, 1 Apr 2021 18:18:44 +0200
Subject: [PATCH 09/14] Cherokee may use wrong Python interpreter
On modern systems Python3 is now standard. Cherokee has been prepared for this
change. Users can use option --with-python to specify the correct path to
Python2 but some Makefiles ignore this directive (e. g. make test). Furthermore,
Python byte code files that are created during build process are not removed
by the clean target of some Makefiles.
This patch configures Autoconf to detect the correct path to a Python2
interpreter. Autoconf will terminate the build process with error if a Python2
interpreter cannot be found. This is required as Python is also used to compile
Cherokee's error header file.
Python byte code files are no cleaned by make clean.
Fixes: https://github.com/cherokee/webserver/issues/1250
Signed-off-by: Thomas Reim
---
admin/Makefile.am | 5 ++++-
cherokee/main_admin.c | 4 ++++
configure.ac | 30 +++++++++++++++++++-----------
qa/Makefile.am | 11 +++++++----
qa/conf.py.pre | 2 +-
qa/util.py | 2 +-
6 files changed, 36 insertions(+), 18 deletions(-)
diff --git a/admin/Makefile.am b/admin/Makefile.am
index 0a090e981..23bae8b0c 100644
--- a/admin/Makefile.am
+++ b/admin/Makefile.am
@@ -111,4 +111,7 @@ CLEANFILES = \
$(generated_DATA)
test:
- python -m compileall .
+ $(PYTHON) -m compileall .
+
+clean-local:
+ find . -type f -name "*.pyc" -exec rm -f {} \;
diff --git a/cherokee/main_admin.c b/cherokee/main_admin.c
index f2629b798..19b7530e8 100644
--- a/cherokee/main_admin.c
+++ b/cherokee/main_admin.c
@@ -653,7 +653,11 @@ check_for_python (void)
int re;
pid_t pid;
int exitcode = -1;
+#ifdef PATH_PYTHON2
+ char const *args[] = {"env", PATH_PYTHON2, "-c", "raise SystemExit", NULL};
+#else
char const *args[] = {"env", "python2", "-c", "raise SystemExit", NULL};
+#endif
pid = fork();
if (pid == -1) {
diff --git a/configure.ac b/configure.ac
index 5844812a9..bdbbf5db0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1432,30 +1432,38 @@ have_python="yes"
AC_ARG_WITH([python], AC_HELP_STRING([--with-python=/path/to/python],[Path to Python interpreter]),
PYTHON="$withval",
[
- AC_PATH_PROGS(PYTHON, python python2 python2.7 python2.6 python2.5 python2.4)
-])
+ AC_CACHE_VAL([ac_cv_path_PYTHON],
+ [AC_PATH_PROGS_FEATURE_CHECK([PYTHON], [python2.4 python2.5 python2.6 python2.7 python2 python],
+ [v=$($ac_path_PYTHON -c "import sys; print (sys.version_info[[0]])")
+ if test "x$v" == "x2"; then
+ ac_cv_path_PYTHON=$ac_path_PYTHON aac_path_PYTHON_found=:
+ fi])])
+ if test "x$ac_cv_path_PYTHON" != "x"; then
+ AC_SUBST(PYTHON, [$ac_cv_path_PYTHON])
+ fi
+]
+)
if test "x$PYTHON" != "x"; then
- AC_MSG_CHECKING([Python 2.x interpreter])
+ AC_MSG_CHECKING([for Python 2.x interpreter])
v=`$PYTHON -c 'import sys; print (sys.version_info[[0]])'`
if test "x$v" != "x2"; then
have_python="no"
wants_admin="no"
AC_MSG_RESULT([no])
- AC_MSG_WARN([
-*** Cherokee-admin requires Python 2.x. Please, use the --with-python
-*** parameter to specify a Python 2.4, 2.5, 2.6 or 2.7 interpreter.
-*** BEWARE: Cherokee-admin support has been disabled.])
+ AC_MSG_ERROR([
+*** Cherokee requires Python 2.x. Please, use the --with-python
+*** parameter to specify a Python 2.4, 2.5, 2.6 or 2.7 interpreter.])
else
- AC_MSG_RESULT([yes])
+ AC_MSG_RESULT([$PYTHON])
+ AC_DEFINE_UNQUOTED(PATH_PYTHON2, ["$PYTHON"], [Define path to Python 2.x interpreter])
fi
else
have_python="no"
wants_admin="no"
- AC_MSG_WARN([
+ AC_MSG_ERROR([
*** Python must be installed on your system but python interpreter couldn't
-*** be found in path. Please check that python is in path, or install it.
-*** BEWARE: Cherokee-admin support has been disabled.])
+*** be found in path. Please check that python is in path, or install it.])
fi
dnl
diff --git a/qa/Makefile.am b/qa/Makefile.am
index f7c9ed719..d274f5033 100644
--- a/qa/Makefile.am
+++ b/qa/Makefile.am
@@ -1,7 +1,7 @@
SUFFIXES = .py.pre .py
.py.pre.py:
- sed -e "s|%sysconfdir%|${sysconfdir}|g; s|%sbindir%|${sbindir}|g; s|%docdir%|${docdir}|g; s|%prefix%|${prefix}|g; s|%localstatedir%|${localstatedir}|g; s|%libdir%|${libdir}|g; s|%wwwroot%|${WWW_ROOT}|g; s|%cgiroot%|${CGI_ROOT}|g; s|%version%|${PACKAGE_VERSION}|g; s|%phpcgi%|${PHPCGI}|g; s|%datadir%|${datadir}|g; s|%localedir%|${localedir}|g" $< > $@
+ sed -e "s|%sysconfdir%|${sysconfdir}|g; s|%sbindir%|${sbindir}|g; s|%docdir%|${docdir}|g; s|%prefix%|${prefix}|g; s|%localstatedir%|${localstatedir}|g; s|%libdir%|${libdir}|g; s|%wwwroot%|${WWW_ROOT}|g; s|%cgiroot%|${CGI_ROOT}|g; s|%version%|${PACKAGE_VERSION}|g; s|%pythonpath%|${PYTHON}|g; s|%phpcgi%|${PHPCGI}|g; s|%datadir%|${datadir}|g; s|%localedir%|${localedir}|g" $< > $@
PY_PRE = \
conf.py.pre
@@ -340,7 +340,7 @@ ssl-keys/set-1.key:
mkdir -p ssl-keys
@OPENSSL_BIN@ genrsa -out ssl-keys/set-1.key 2048
test-ssl: ssl-keys/set-1.pem
- ./run-tests.py -s
+ $(PYTHON) ./run-tests.py -s
test: test-non-ssl test-ssl
DISTCLEANFILES += ssl-keys/set-1.pem ssl-keys/set-1.key
@@ -350,5 +350,8 @@ test: test-non-ssl
endif
test-non-ssl:
- python -m compileall .
- ./run-tests.py
+ $(PYTHON) -m compileall .
+ $(PYTHON) ./run-tests.py
+
+clean-local:
+ find . -type f -name "*.pyc" -exec rm -f {} \;
diff --git a/qa/conf.py.pre b/qa/conf.py.pre
index 977222366..7c0ed1193 100644
--- a/qa/conf.py.pre
+++ b/qa/conf.py.pre
@@ -24,7 +24,7 @@ SSL_CERT_KEY_FILE = "ssl-keys/set-1.key"
SERVER_DELAY = 10
# Third party utilities
-PYTHON_PATH = "auto"
+PYTHON_PATH = "%pythonpath%"
PHPCGI_PATH = "%phpcgi%"
PHP_FCGI_PORT = 1980
diff --git a/qa/util.py b/qa/util.py
index 7f055ef0f..974d77557 100644
--- a/qa/util.py
+++ b/qa/util.py
@@ -139,7 +139,7 @@ def look_for_python():
if __python_ref != None:
return __python_ref
- if PYTHON_PATH != "auto":
+ if PYTHON_PATH != "%pythonpath%":
__python_ref = PYTHON_PATH
return __python_ref
From 19fa9f6ab9d04a5369f768be1202c330eaff3278 Mon Sep 17 00:00:00 2001
From: Thomas Reim
Date: Thu, 1 Apr 2021 18:39:48 +0200
Subject: [PATCH 10/14] Fix Autoconf files
Cherokee's Autoconf files are pretty outdated. Checking with autoscan results
in several warnings.
Fix autoscan warnings and correct deprecated syntax (e. g. AC_CONFIG_FILES,
AC_OUTPUT).
Fixes: https://github.com/cherokee/webserver/issues/1249
Signed-off-by: Thomas Reim
---
configure.ac | 114 +++++++++++++++++++++++++++++----------------------
1 file changed, 64 insertions(+), 50 deletions(-)
diff --git a/configure.ac b/configure.ac
index bdbbf5db0..1bbc2009a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -29,7 +29,7 @@ m4_define([cherokee_micro_version], [104])
m4_define([cherokee_version], m4_format('%s.%s.%s', cherokee_major_version, cherokee_minor_version, cherokee_micro_version))
dnl Init autoconf and automake
-AC_INIT([cherokee], [cherokee_version], [http://bugs.cherokee-project.com/], [cherokee])
+AC_INIT([cherokee], [cherokee_version], [https://github.com/cherokee/webserver/issues], [cherokee])
AC_CONFIG_SRCDIR([cherokee/server.c])
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([no-define])
@@ -209,8 +209,12 @@ m4_ifdef([LT_INIT], [LT_INIT([dlopen shared static])], [AC_LIBTOOL_DLOPEN])
dnl Paths
AC_PROG_CC
AC_PROG_CC_STDC
+AC_PROG_CXX
AC_PROG_INSTALL
AC_PROG_MAKE_SET
+AC_PROG_YACC
+AC_PROG_LEX
+AC_PROG_LN_S
AM_PROG_LIBTOOL
AC_PATH_PROG(cherokeepath, cherokee)
@@ -240,16 +244,24 @@ AC_CHECK_HEADERS(sys/socket.h sys/un.h netinet/in.h arpa/inet.h netinet/tcp.h sy
AC_CHECK_HEADERS(sys/resource.h resource.h unistd.h syslog.h stdint.h inttypes.h error.h pwd.h sys/uio.h)
AC_CHECK_HEADERS(pthread.h netdb.h stdarg.h sys/filio.h sys/varargs.h sys/select.h sys/mman.h sys/uio.h grp.h)
AC_CHECK_HEADERS(sched.h execinfo.h)
+AC_CHECK_HEADERS(fenv.h float.h libintl.h limits.h locale.h stddef.h stdlib.h string.h strings.h sys/param.h wchar.h wctype.h)
AC_SYS_LARGEFILE
-AC_SIZE_T
+AC_CHECK_HEADER_STDBOOL
AC_TYPE_UID_T
-AC_TYPE_MODE_T
+AC_C_INLINE
+AC_TYPE_INT16_T
+AC_TYPE_INT32_T
AC_TYPE_OFF_T
AC_TYPE_SIZE_T
AC_TYPE_PID_T
-AC_STRUCT_ST_RDEV
+AC_C_RESTRICT
+AC_TYPE_SSIZE_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT64_T
+AC_TYPE_UINT8_T
AC_CHECK_TYPE(ino_t, unsigned)
AC_CHECK_TYPE(loff_t, off_t)
AC_CHECK_TYPE(offset_t, loff_t)
@@ -271,14 +283,17 @@ AC_TRY_RUN([#include
main() { long long x = 1000000; x *= x; exit(((x/1000000) == 1000000)? 0: 1); }],
samba_cv_have_longlong=yes,samba_cv_have_longlong=no,samba_cv_have_longlong=cross)])
if test x"$samba_cv_have_longlong" = x"yes"; then
- AC_DEFINE(HAVE_LONGLONG,1,[Whether the host supports long long'])
+ AC_DEFINE(HAVE_LONGLONG,1,[Whether the host supports long long])
fi
AC_FUNC_MEMCMP
AC_FUNC_MMAP
AC_FUNC_FORK
+AC_FUNC_CHOWN
+AC_FUNC_ERROR_AT_LINE
+AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK
-AC_CHECK_FUNCS(gmtime gmtime_r localtime localtime_r getrlimit getdtablesize readdir readdir_r flockfile funlockfile strnstr backtrace random srandom srandomdev)
+AC_CHECK_FUNCS(dup2 ftruncate getcwd gettimeofday inet_ntoa gmtime gmtime_r localtime localtime_r getrlimit getdtablesize memset mkdir readdir readdir_r flockfile funlockfile backtrace munmap pathconf pstat_getdynamic realpath rmdir strchr strcspn strdup strncasecmp strpbrk strrchr strspn strnstr strstr strtol strtoul random srandom srandomdev)
FW_CHECK_PWD
FW_CHECK_GRP
@@ -1197,7 +1212,6 @@ if test "x$USE_NLS" = "xyes"; then
fi
AM_CONDITIONAL(NLS_ENABLED, test "x$USE_NLS" = "xyes")
-AC_CONFIG_FILES([po/admin/Makefile.in])
dnl
dnl LDAP
@@ -1613,49 +1627,49 @@ AM_CONDITIONAL(STATIC_RULE_NOT, grep _not_ $conf_h >/dev/
AM_CONDITIONAL(STATIC_RULE_AND, grep _and_ $conf_h >/dev/null)
AM_CONDITIONAL(STATIC_RULE_OR, grep _or_ $conf_h >/dev/null)
-AC_OUTPUT([
-Makefile
-cget/Makefile
-cherokee-config
-cherokee.pc
-cherokee.spec
-org.cherokee.webserver.plist
-cherokee/Makefile
-contrib/Makefile
-icons/Makefile
-m4/Makefile
-qa/Makefile
-doc/Makefile
-themes/Makefile
-themes/default/Makefile
-themes/firefox3/Makefile
-themes/white/Makefile
-themes/plain/Makefile
-packages/Makefile
-packages/osx/Makefile
-packages/osx/Info.plist
-packages/osx/Description.plist
-www/Makefile
-dbslayer/Makefile
-admin/Makefile
-admin/icons/Makefile
-admin/plugins/Makefile
-admin/wizards/Makefile
-admin/static/Makefile
-admin/static/js/Makefile
-admin/static/css/Makefile
-admin/static/images/Makefile
-admin/static/images/flags/Makefile
-admin/static/images/wizards/Makefile
-admin/static/images/other/Makefile
-admin/CTK/Makefile
-admin/CTK/CTK/Makefile
-admin/CTK/static/css/Makefile
-admin/CTK/static/images/Makefile
-admin/CTK/static/js/Makefile
-admin/CTK/static/Makefile
-po/Makefile
-])
+AC_CONFIG_FILES([Makefile
+ admin/CTK/CTK/Makefile
+ admin/CTK/Makefile
+ admin/CTK/static/Makefile
+ admin/CTK/static/css/Makefile
+ admin/CTK/static/images/Makefile
+ admin/CTK/static/js/Makefile
+ admin/Makefile
+ admin/icons/Makefile
+ admin/plugins/Makefile
+ admin/static/Makefile
+ admin/static/css/Makefile
+ admin/static/images/Makefile
+ admin/static/images/flags/Makefile
+ admin/static/images/other/Makefile
+ admin/static/images/wizards/Makefile
+ admin/static/js/Makefile
+ admin/wizards/Makefile
+ cget/Makefile
+ cherokee-config
+ cherokee.pc
+ cherokee.spec
+ cherokee/Makefile
+ contrib/Makefile
+ dbslayer/Makefile
+ doc/Makefile
+ icons/Makefile
+ m4/Makefile
+ org.cherokee.webserver.plist
+ packages/Makefile
+ packages/osx/Description.plist
+ packages/osx/Info.plist
+ packages/osx/Makefile
+ po/Makefile
+ po/admin/Makefile.in
+ qa/Makefile
+ themes/Makefile
+ themes/default/Makefile
+ themes/firefox3/Makefile
+ themes/plain/Makefile
+ themes/white/Makefile
+ www/Makefile])
+AC_OUTPUT
methods=""
From b69ab56f673adf7727036ecf4f00652a8743fe70 Mon Sep 17 00:00:00 2001
From: Thomas Reim
Date: Thu, 1 Apr 2021 18:52:43 +0200
Subject: [PATCH 11/14] Fix dead bug report link
Fixes: https://github.com/cherokee/webserver/issues/1248
Signed-off-by: Thomas Reim
---
admin/Makefile.am | 2 +-
admin/PageException.py | 2 +-
admin/PageIndex.py | 2 +-
admin/configured.py.pre | 1 +
cherokee/cherokee-panic | 2 +-
cherokee/error_list.py | 4 ++--
po/admin/Makevars | 2 +-
qa/128-ValidMethod-common.py | 2 +-
qa/299-MethodsRequestBodyHandling.py | 2 +-
qa/Makefile.am | 2 +-
qa/conf.py.pre | 1 +
qa/help.py | 2 +-
12 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/admin/Makefile.am b/admin/Makefile.am
index 23bae8b0c..8f318e1b3 100644
--- a/admin/Makefile.am
+++ b/admin/Makefile.am
@@ -17,7 +17,7 @@ tlsprotocols =
endif
.py.pre.py:
- sed -e "s|%sysconfdir%|${sysconfdir}|g; s|%sbindir%|${sbindir}|g; s|%docdir%|${docdir}|g; s|%prefix%|${prefix}|g; s|%localstatedir%|${localstatedir}|g; s|%libdir%|${libdir}|g; s|%wwwroot%|${WWW_ROOT}|g; s|%version%|${PACKAGE_VERSION}|g; s|%phpcgi%|${PHPCGI}|g; s|%datadir%|${datadir}|g; s|%localedir%|${localedir}|g; s|%minmaxtls%|$(minmaxtls)|g; s|%tlsprotocols%|$(tlsprotocols)|g" $< > $@
+ sed -e "s|%sysconfdir%|${sysconfdir}|g; s|%sbindir%|${sbindir}|g; s|%docdir%|${docdir}|g; s|%prefix%|${prefix}|g; s|%localstatedir%|${localstatedir}|g; s|%libdir%|${libdir}|g; s|%wwwroot%|${WWW_ROOT}|g; s|%version%|${PACKAGE_VERSION}|g; s|%bugreportlink%|${PACKAGE_BUGREPORT}|g; s|%phpcgi%|${PHPCGI}|g; s|%datadir%|${datadir}|g; s|%localedir%|${localedir}|g; s|%minmaxtls%|$(minmaxtls)|g; s|%tlsprotocols%|$(tlsprotocols)|g" $< > $@
PY_PRE = \
configured.py.pre
diff --git a/admin/PageException.py b/admin/PageException.py
index 6ae43dbc2..15f513403 100644
--- a/admin/PageException.py
+++ b/admin/PageException.py
@@ -31,7 +31,7 @@
from urllib import quote, unquote, urlencode
from httplib import HTTPConnection
-URL_BTS = 'http://bugs.cherokee-project.com/new'
+URL_BTS = configured.BUGREPORTLINK
URL_REPORT_HOST = 'www.cherokee-project.com'
URL_REPORT_URL = '/CTK_ok.html'
diff --git a/admin/PageIndex.py b/admin/PageIndex.py
index a1997538d..5f2ea0e94 100644
--- a/admin/PageIndex.py
+++ b/admin/PageIndex.py
@@ -42,7 +42,7 @@
from configured import *
# Links
-LINK_BUGTRACKER = 'http://bugs.cherokee-project.com/'
+LINK_BUGTRACKER = BUGREPORTLINK
LINK_TWITTER = 'http://twitter.com/webserver'
LINK_FACEBOOK = 'http://www.facebook.com/cherokee.project'
LINK_GOOGLEPLUS = 'https://plus.google.com/u/1/communities/109478817835447552345'
diff --git a/admin/configured.py.pre b/admin/configured.py.pre
index e3cc8e593..408bd177f 100644
--- a/admin/configured.py.pre
+++ b/admin/configured.py.pre
@@ -9,6 +9,7 @@ WWWROOT = "%wwwroot%"
SYSCONFDIR = "%sysconfdir%"
LOCALSTATE = "%localstatedir%"
VERSION = "%version%"
+BUGREPORTLINK = "%bugreportlink%"
TLSPROTOCOLS = "%tlsprotocols%"
MINMAXTLS = %minmaxtls%
diff --git a/cherokee/cherokee-panic b/cherokee/cherokee-panic
index ea5d7339c..caaad9bb4 100755
--- a/cherokee/cherokee-panic
+++ b/cherokee/cherokee-panic
@@ -90,7 +90,7 @@ exec 2>&1
echo "Below is a backtrace for this process generated with gdb, which shows"
echo "the state of the program at the time the error occured. You are"
echo "encouraged to submit this information as a bug report in the Cherokee"
- echo "bug tracking system: http://bugs.cherokee-project.com"
+ echo "bug tracking system: https://github.com/cherokee/webserver/issues"
echo
echo "Operating System: `uname -a`"
echo "Debugger: $debugger"
diff --git a/cherokee/error_list.py b/cherokee/error_list.py
index acad7f35a..892aed09b 100644
--- a/cherokee/error_list.py
+++ b/cherokee/error_list.py
@@ -1,12 +1,12 @@
# Macros
#
CODING_BUG = """It looks like you've hit a bug in the server. Please, \
-do not hesitate to report it at http://bugs.cherokee-project.com/ so \
+do not hesitate to report it at https://github.com/cherokee/webserver/issues so \
the developer team can fix it."""
UNKNOWN_CAUSE = """An unexpected error has just occurred in the \
server. The cause of the issue is unknown. Please, do not hesitate to \
-report it at http://bugs.cherokee-project.com/ so the developer team \
+report it at https://github.com/cherokee/webserver/issues so the developer team \
can fix it."""
SYSTEM_ISSUE = """The issue seems to be related to your system."""
diff --git a/po/admin/Makevars b/po/admin/Makevars
index add37f4d7..ba3f6ac02 100644
--- a/po/admin/Makevars
+++ b/po/admin/Makevars
@@ -34,7 +34,7 @@ COPYRIGHT_HOLDER = Alvaro Lopez Ortega
# It can be your email address, or a mailing list address where translators
# can write to without being subscribed, or the URL of a web page through
# which the translators can contact you.
-MSGID_BUGS_ADDRESS = http://bugs.cherokee-project.com/
+MSGID_BUGS_ADDRESS = $(PACKAGE_BUGREPORT)
# This is the list of locale categories, beyond LC_MESSAGES, for which the
# message catalogs shall be used. It is usually empty.
diff --git a/qa/128-ValidMethod-common.py b/qa/128-ValidMethod-common.py
index effc054fc..9fc3d42a1 100644
--- a/qa/128-ValidMethod-common.py
+++ b/qa/128-ValidMethod-common.py
@@ -1,7 +1,7 @@
from base import *
DIR = "common_valid_methods1"
-MAGIC = "Report bugs to http://bugs.cherokee-project.com"
+MAGIC = "Report bugs to " + BUG_REPORT_LINK
CONF = """
vserver!1!rule!1280!match = directory
diff --git a/qa/299-MethodsRequestBodyHandling.py b/qa/299-MethodsRequestBodyHandling.py
index b6a7de5bf..04856c5ff 100644
--- a/qa/299-MethodsRequestBodyHandling.py
+++ b/qa/299-MethodsRequestBodyHandling.py
@@ -2,7 +2,7 @@
from base import *
DIR = "/299-MethodsRequestBodyHandling/"
-MAGIC = "Report bugs to http://bugs.cherokee-project.com"
+MAGIC = "Report bugs to " + BUG_REPORT_LINK
PORT = get_free_port()
PYTHON = look_for_python()
SOURCE = get_next_source()
diff --git a/qa/Makefile.am b/qa/Makefile.am
index d274f5033..fcbe9733b 100644
--- a/qa/Makefile.am
+++ b/qa/Makefile.am
@@ -1,7 +1,7 @@
SUFFIXES = .py.pre .py
.py.pre.py:
- sed -e "s|%sysconfdir%|${sysconfdir}|g; s|%sbindir%|${sbindir}|g; s|%docdir%|${docdir}|g; s|%prefix%|${prefix}|g; s|%localstatedir%|${localstatedir}|g; s|%libdir%|${libdir}|g; s|%wwwroot%|${WWW_ROOT}|g; s|%cgiroot%|${CGI_ROOT}|g; s|%version%|${PACKAGE_VERSION}|g; s|%pythonpath%|${PYTHON}|g; s|%phpcgi%|${PHPCGI}|g; s|%datadir%|${datadir}|g; s|%localedir%|${localedir}|g" $< > $@
+ sed -e "s|%sysconfdir%|${sysconfdir}|g; s|%sbindir%|${sbindir}|g; s|%docdir%|${docdir}|g; s|%prefix%|${prefix}|g; s|%localstatedir%|${localstatedir}|g; s|%libdir%|${libdir}|g; s|%wwwroot%|${WWW_ROOT}|g; s|%cgiroot%|${CGI_ROOT}|g; s|%version%|${PACKAGE_VERSION}|g; s|%bugreportlink%|${PACKAGE_BUGREPORT}|g; s|%pythonpath%|${PYTHON}|g; s|%phpcgi%|${PHPCGI}|g; s|%datadir%|${datadir}|g; s|%localedir%|${localedir}|g" $< > $@
PY_PRE = \
conf.py.pre
diff --git a/qa/conf.py.pre b/qa/conf.py.pre
index 7c0ed1193..064641f69 100644
--- a/qa/conf.py.pre
+++ b/qa/conf.py.pre
@@ -22,6 +22,7 @@ SSL_CERT_KEY_FILE = "ssl-keys/set-1.key"
# Misc options
SERVER_DELAY = 10
+BUG_REPORT_LINK = "%bugreportlink%"
# Third party utilities
PYTHON_PATH = "%pythonpath%"
diff --git a/qa/help.py b/qa/help.py
index f4a659d82..34052604b 100644
--- a/qa/help.py
+++ b/qa/help.py
@@ -26,7 +26,7 @@
-v Run under Valgrind, string=['', 'hel', 'cac', 'cal']
-P Proxy address: host:ip[:public_ip]
-Report bugs to http://bugs.cherokee-project.com/
+Report bugs to https://github.com/cherokee/webserver/issues
"""
def help_print_parameters():
From a16eb03600ad27170e36747a8f001240d33e7c8f Mon Sep 17 00:00:00 2001
From: Thomas Reim
Date: Thu, 1 Apr 2021 21:04:25 +0200
Subject: [PATCH 12/14] Remove deprecated OpenSSL/libssl function calls
Since OpenSSL version 1.1.0 the OpenSSL team has implemented a new strategy and
has extended automatic configuration of the libssl back-end. This increases
security and removes the burden from application developers to keep care of the
many and various bits and pieces that are required to setup a safe and powerful
TLS back-end. Several functions have no effect anymore and have been deprecated.
Remove deprecated OpenSSL functions functions from the code if Cherokee is
operated using OpenSSL/libssl version 1.1.0 or later.
In addition, fix OpenSSL related traces. OpenSSL tries to load a PKCS11 engine
for support of smartcard stored keys. This engine is not part of regular OpenSSL
packages and has to be installed on top of OpenSSL. If libcrypto cannot find the
PKCS11 engine library error notifications are issued, which are ignored by
Cherokee, as the missing engine does not harm operation. But system administrators
will find the error messages when operating Cherokee with traces enabled. This is
confusing as the hidden OpenSSL internal errors are not immediately queried during
libssl setup. The first OpenSSL error query is during virtual server setup.
Fix this by immediately querying potential OpenSSL errors during engine setup and
informing users about the requested OpenSSL task.
Signed-off-by: Thomas Reim
---
cherokee/cryptor_libssl.c | 44 ++++++++++++++++++++++++---------------
1 file changed, 27 insertions(+), 17 deletions(-)
diff --git a/cherokee/cryptor_libssl.c b/cherokee/cryptor_libssl.c
index 5ad567e1b..a5b6067e3 100644
--- a/cherokee/cryptor_libssl.c
+++ b/cherokee/cryptor_libssl.c
@@ -107,9 +107,11 @@ _free (cherokee_cryptor_libssl_t *cryp)
*/
ERR_free_strings();
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
/* Free all ciphers and digests
*/
EVP_cleanup();
+#endif
cherokee_cryptor_free_base (CRYPTOR(cryp));
return ret_ok;
@@ -1887,15 +1889,14 @@ PLUGIN_INIT_NAME(libssl) (cherokee_plugin_loader_t *loader)
PLUGIN_INIT_ONCE_CHECK (libssl);
/* Init OpenSSL
+ * In OpenSSL version 1.1.0 and later only needed if non-default
+ * settings are required.
*/
#if OPENSSL_VERSION_NUMBER < 0x10100000L
OPENSSL_config(NULL);
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
-#else
- OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS | OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
- OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
#endif
/* Ensure PRNG has been seeded with enough data
@@ -1923,28 +1924,37 @@ PLUGIN_INIT_NAME(libssl) (cherokee_plugin_loader_t *loader)
}
-# if HAVE_OPENSSL_ENGINE_H
+#if HAVE_OPENSSL_ENGINE_H
# if OPENSSL_VERSION_NUMBER >= 0x00907000L
ENGINE_load_builtin_engines();
+# if OPENSSL_VERSION_NUMBER < 0x10100000L
OpenSSL_add_all_algorithms();
+# endif
# endif
+ TRACE(ENTRIES, "Checking for pkcs11 engine ...\n");
e = ENGINE_by_id("pkcs11");
- while (e != NULL) {
- if(! ENGINE_init(e)) {
- ENGINE_free (e);
- LOG_CRITICAL_S (CHEROKEE_ERROR_SSL_PKCS11);
- break;
- }
+ if (!e) {
+ CLEAR_LIBSSL_ERRORS;
+ TRACE(ENTRIES, "pkcs11 engine is not installed\n");
+ } else {
+ while (e != NULL) {
+ TRACE(ENTRIES, "Loading pkcs11 engine ...\n");
+ if(! ENGINE_init(e)) {
+ ENGINE_free (e);
+ LOG_CRITICAL_S (CHEROKEE_ERROR_SSL_PKCS11);
+ break;
+ }
- if(! ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
- ENGINE_free (e);
- LOG_CRITICAL_S (CHEROKEE_ERROR_SSL_DEFAULTS);
+ if(! ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
+ ENGINE_free (e);
+ LOG_CRITICAL_S (CHEROKEE_ERROR_SSL_DEFAULTS);
+ break;
+ }
+ ENGINE_finish(e);
+ ENGINE_free(e);
break;
}
-
- ENGINE_finish(e);
- ENGINE_free(e);
- break;
+ CLEAR_LIBSSL_ERRORS;
}
#endif
}
From ce1f6fa9db50e0eee3c987099a79fe32d78d4b6e Mon Sep 17 00:00:00 2001
From: Thomas Reim
Date: Thu, 1 Apr 2021 21:28:24 +0200
Subject: [PATCH 13/14] Update of Cherokee documentation
Signed-off-by: Thomas Reim
---
admin/PageGeneral.py | 2 +-
doc/Makefile.am | 1 +
doc/config_advanced.txt | 135 +++++++++++++++++-
doc/config_virtual_servers.txt | 10 +-
doc/cookbook_ssl.txt | 62 +++++---
doc/media/images/admin_advanced5.png | Bin 12941 -> 141955 bytes
doc/media/images/admin_advanced6.png | Bin 0 -> 114010 bytes
doc/media/images/admin_vserver_security.png | Bin 14877 -> 90676 bytes
.../images/admin_vserver_security_ssl.png | Bin 34729 -> 103197 bytes
9 files changed, 183 insertions(+), 27 deletions(-)
create mode 100644 doc/media/images/admin_advanced6.png
diff --git a/admin/PageGeneral.py b/admin/PageGeneral.py
index 9449aef44..6b416b2fa 100644
--- a/admin/PageGeneral.py
+++ b/admin/PageGeneral.py
@@ -55,7 +55,7 @@
NOTE_DELETE_DIALOG = N_('You are about to delete an binding. Are you sure you want to proceed?')
HELPS = [('config_general', N_("General Configuration")),
- ('config_quickstart', N_("Configuration Quickstart"))]
+ ('config_walkthrough', N_("Configuration Quickstart"))]
VALIDATIONS = [
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 7e97bb8b5..aee61ea2f 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -149,6 +149,7 @@ media/images/admin_advanced2.png \
media/images/admin_advanced3.png \
media/images/admin_advanced4.png \
media/images/admin_advanced5.png \
+media/images/admin_advanced6.png \
media/images/admin_behavior.png \
media/images/admin_general.png \
media/images/admin_general_networking.png \
diff --git a/doc/config_advanced.txt b/doc/config_advanced.txt
index 81536ad54..ec354a7e0 100644
--- a/doc/config_advanced.txt
+++ b/doc/config_advanced.txt
@@ -145,9 +145,138 @@ image::media/images/admin_advanced4.png[Cherokee Admin interface]
[[tls]]
TLS
~~~
-* DH parameters:
- Here you can specify the paths to your Diffie Hellman parameters PEM
- files for 512, 1024, 2048 and 4096 bits.
+
+[[tls-protocols]]
+.TLS/SSL Protocols
+* Support of TLS/SSL protocols for traffic encryption largely depends on the
+ capabilities of the underlying link:config_general.html[TLS/SSL engine] and
+ operating system policies. In addition, suited link:cookbook_ssl.html[cipher
+ suites] and link:cookbook_ssl.html[ciphers] are required for encryption.
+ Cherokee TLS/SSL default configuration is aligned to
+ link:https://wiki.mozilla.org/Security/Server_Side_TLS#[Mozilla]
+ recommended Server Side TLS configuration (Intermediate compatibility).
+ It supports encrypted communication with nearly every client released in the
+ last five (or more) years including, e. g. Windows 7 workstations.
+
+* Only TLS v1.2 and newer protocols are enabled and provided with valid
+ cipher suites by the Cherokee default configuration.
+
+* Availability of TLS v1.1 and below depends on the capabilities of your
+ link:config_general.html[SSL/TLS back-end]. You have to be aware of the fact
+ that support of deprecated or obsolete SSL/TLS protocols impose a security
+ risk on your webserver and operating system. Therefore, most distributions
+ have disabled or removed support of TLS v1.1 and below in the TLS back-end.
.TLS
image::media/images/admin_advanced5.png[Cherokee Admin interface]
+
+Min. TLS protocol version
+^^^^^^^^^^^^^^^^^^^^^^^^^
+* Default option 'auto-configured' (recommended) enables protocol versions down
+ to the lowest version supported by the link:config_general.html[SSL/TLS back-end].
+* Note that a minimum allowed SSL/TLS protocol may have been hardcoded in the
+ link:config_general.html[SSL/TLS back-end] by your distribution. In this case,
+ option 'auto-configured' enables protocol versions down to the hardcoded minimum
+ TLS protocol version.
+* Setting this parameter to a particular listed SSL/TLS protocol enables all
+ protocols down to the chosen protocol version that are supported by
+ link:config_general.html[SSL/TLS back-end].
+
+Max. TLS protocol version
+^^^^^^^^^^^^^^^^^^^^^^^^^
+* Default option 'auto-configured' (recommended) enables protocol versions up
+ to the highest version supported by the link:config_general.html[SSL/TLS back-end].
+* Setting this parameter to a particular listed SSL/TLS protocol enables all
+ protocols up the chosen protocol version that are supported by the
+ link:config_general.html[SSL/TLS back-end].
+
+Handshake timeout
+^^^^^^^^^^^^^^^^^
+* Specify a timeout period for the negotiation of ciphers for traffic
+ encryption between client and server.
+
+DH parameters
+^^^^^^^^^^^^^
+ Here you can specify the paths to your Diffie Hellman parameters PEM
+ files for 512, 1024, 2048 and 4096 bits.
+
+Disable SSL/TLS version (deprecated)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+* These options may be used to disable selected SSL/TLS protocols in addition to
+ the min./max. settings above. If the link:config_general.html[SSL/TLS back-end]
+ does not support configuration of min./max. TLS protocols the TLS configuration
+ page starts with this section.
+* This section also outlines SSL/TLS protocol versions that are not supported by
+ the link:config_general.html[SSL/TLS back-end] at all.
+
+[[tls-advanced]]
+.Advanced TLS/SSL Configuration
+* As outlined above Cherokee's default cipher configuration is following
+ link:https://wiki.mozilla.org/Security/Server_Side_TLS#[Mozilla] Intermediate
+ Server Side TLS configuration recommendations. This configuration does not
+ allow communication using TLS v1.1 or below. Therefore, Cherokee does not
+ provide any (insecure) ciphers for encrypted communication to such outdated
+ clients. If you really require offering web services to old clients, e. g.
+ Windows XP or OpenSSL 0.9.8 clients, you need to enable a suited TLS/SSL
+ protocol. This depends on the capabilities of your SSL/TLS back-end. You
+ have to be aware of the fact that support of deprecated or obsolete SSL/TLS
+ protocols impose a security risk for your webserver and system. Therefore,
+ most distributions have disabled or removed support of TLS v1.1 and below
+ in their TLS back-ends. Cherokee provides a command-line option to inspect
+ the capabilities of your TLS back-end:
+----
+ $ cherokee -i
+
+ Compilation
+ Version: 1.2.104
+ [...]
+ OpenSSL support: libssl (OpenSSL 1.1.1j 16 Feb 2021)
+
+ [...]
+
+ Support
+ [...]
+ SSL/TLS: libssl (OpenSSL 1.1.1j 16 Feb 2021)
+ supported protocols: SSLv3, TLSv1, TLSv1.1, TLSv1.2, TLSv1.3 - default min. protocol: TLSv1.2
+ protocols deactivated by maintainer: SSLv3
+ TLS SNI: yes
+----
+
+* The command's output first shows name and version of the SSL/TLS back-end,
+ which has been used to build Cherokee (section 'Compilation'). In section
+ 'Support' the actually installed back-end is shown. Cherokee also reports
+ SSL/TLS protocols supported by the used back-end. Protocols not listed there
+ have been removed from the back-end. Finally, protocols are listed that
+ cannot be used because your distribution has disabled (deactivated) them.
+ In addition, the command's output indicates if a minimum allowed
+ SSL/TLS protocol has been hardcoded in the back-end. Unsupported or
+ deactivated protocols cannot be used by Cherokee at all. Use of protocol
+ versions below a hardcoded minimum allowed SSL/TLS protocol depends on
+ Cherokee's TLS settings above: In mode 'auto-configured' only the specified
+ minimum protocol version and versions above are available for use. Setting option
+ 'Min. TLS protocol version' to one of the listed protocols will configure
+ the back-end to allow use of the selected protocol version and all versions
+ above if they are part of the supported protocols list and not shown in the
+ deactivated protocols list.
+* After a suited TLS/SSL protocol has been enabled you have to configure an
+ extended set of link:cookbook_ssl.html[ciphers] for the affected virtual servers.
+ The set must include some insecure and outdated link:cookbook_ssl.html[ciphers]
+ that are supported by your old clients.
+* Cherokee still can be built with previous versions of OpenSSL/libssl back-end, e. g.
+ OpenSSL version 0.9.8. On modern secure servers system administrators
+ are very likely not able to put a suited TLS protocol into operation that is
+ supported by old legacy clients. If this is the case you can build and locally
+ install an additional, insecure previous version of OpenSSL on your server
+ and build Cherokee using this back-end. Cherokee's default cipher configuration
+ is adapted to this legacy OpenSSL back-end and now supports the cipher suites
+ recommended by link:https://wiki.mozilla.org/Security/Server_Side_TLS#[Mozilla] Old
+ compatibility configuration. As previous versions of OpenSSL did not support
+ configuration of minimum or maximum allowed TLS protocols, the advanced TLS
+ configuration screen is also different:
+
+.TLS for back-ends without min./max. TLS protocol configuration
+image::media/images/admin_advanced6.png[Cherokee Admin interface]
+
+* Hint: When starting Cherokee Admin with an empty configuration file (rename
+ the existing file to, e. g. cherokee.conf.sik, Cherokee will report its
+ default SSL/TLS protocol availability settings.
diff --git a/doc/config_virtual_servers.txt b/doc/config_virtual_servers.txt
index 19fdb7b80..ab04ed9bb 100644
--- a/doc/config_virtual_servers.txt
+++ b/doc/config_virtual_servers.txt
@@ -487,11 +487,11 @@ image::media/images/admin_vserver_security.png[Certificates]
If you want HTTPS to work, you must remember this:
. Providing the PEM-encoded files is mandatory for both `Certificate`
- and `Certificate key` fields. Providing the `CA List` and the
- `Client Certs` is optional. The trusted CA certificates file should
- be a single file with all the certificates concatenated. The `Client
- Certs`, also PEM-encoded, is used to check the client
- certificates. You can read more about
+ and `Certificate key` fields. Providing `Client Certs` is optional.
+ The trusted server certificates file should be a single file with
+ the server certificate and the issuing CA chain certificates
+ concatenated. The `Client Certs`, also PEM-encoded, is used to
+ check the client certificates. You can read more about
link:cookbook_ssl.html#ssl-support[enabling SSL support] on the
SSL/TLS section of the documentation.
diff --git a/doc/cookbook_ssl.txt b/doc/cookbook_ssl.txt
index 882f01f1e..651c89102 100644
--- a/doc/cookbook_ssl.txt
+++ b/doc/cookbook_ssl.txt
@@ -143,7 +143,7 @@ straightforward actions is order to enable it:
intermediate file which links such CA to a higher-level CA that
works with all browsers and operating systems. You'll need a
chained certificate file in order to instruct your visitors on how
- to follow the chain of trust. You can reed more about
+ to follow the chain of trust. You can read more about
link:#chained-certificates[chained certificates] later on this
document. You will only have to concatenate the certificate files
and put the combined text into a file used for the
@@ -160,23 +160,49 @@ cat public.pem inter_ca_public.pem > chained.pem
[[advanced]]
.Advanced options
-In order to make SSL more secure, Cherokee offers the following advanced
-options:
-
- . The `Ciphers` field can be set up to to allow specific cipher suites,
- making communication with the client bound to stronger ciphers.
- We made the following the default:
- `ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:
- ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:
- ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:
- DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:
- DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:
- DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:RC4-SHA:
- AES256-GCM-SHA384:AES256-SHA256:CAMELLIA256-SHA:
- ECDHE-RSA-AES128-SHA:AES128-GCM-SHA256:AES128-SHA256:
- AES128-SHA:CAMELLIA128-SHA`
-
- . In normal SSL communication the clients cipher suite preference
+In order to make TLS/SSL more secure, Cherokee aligns its default TLS encryption
+configuration to link:https://wiki.mozilla.org/Security/Server_Side_TLS#[Mozilla]
+recommended Server Side TLS configuration. Intermediate compatibility has been
+chosen to allow services that don't need compatibility with legacy clients, such
+as Windows XP or old versions of OpenSSL. This is the recommended configuration
+for the vast majority of services, as it is highly secure and compatible with
+nearly every client released in the last five (or more) years.
+In order to customise TLS/SSL security settings Cherokee offers the following
+advanced options:
+
+ . The `Ciphersuites` field (TLS v1.3)
+ can be set up to offer a customer specific set of cipher suites to clients
+ for encryption of the communication with the clients.
+ The default configuration provides intermediate compatibility using the
+ following default cipher suites for encryption of TLS v1.3 traffic:
+ `TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256`
+
+ . The `Ciphers` field (TLS v1.2 and below)
+ can be set up to offer a customer specific set of cipher suites to clients
+ for encryption of the communication with the clients.
+ The configuration provides intermediate compatibility using the following
+ default cipher suites for encryption of TLS v1.2 traffic:
+ `ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:
+ ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:
+ ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:
+ DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384`.
+ Note that link:https://wiki.mozilla.org/Security/Server_Side_TLS#[Mozilla]
+ Intermediate configuration does not allow communication using TLS v1.1 or
+ below. Therefore, Cherokee default cipher configuration does not provide
+ any (insecure) ciphers for encrypted communication to such outdated clients.
+ If you really require offering web services to old clients first you need
+ to link:config_advanced.html[enable a suited TLS/SSL protocol]. This depends
+ on the capabilities of your SSL/TLS back-end. You have to be aware of the
+ fact that support of deprecated or obsolete SSL/TLS protocols impose a
+ security risk for your webserver and system. Therefore, most distributions
+ have disabled or removed support of TLS v1.1 and below in the TLS back-end.
+ After a suited TLS/SSL protocol has been enabled an extended set of ciphers
+ needs to be configured in the field. Enter the default ciphers above plus
+ some insecure and outdated ciphers that are supported by your legacy clients.
+ Please check link:https://wiki.mozilla.org/Security/Server_Side_TLS#[Mozilla]
+ Old compatibility configuration for further details.
+
+ . In normal TLS/SSL communication the clients cipher suite preference
is respected. You may understand that if a client prefers a weak
cipher but supports a strong cipher, the strong cipher would never
be used. We have introduced the option `Server Preference` to
diff --git a/doc/media/images/admin_advanced5.png b/doc/media/images/admin_advanced5.png
index d956e051b48c29648cba05539188f8811b932269..e3d29dc96459e58d3fff48db7232bda4355fc3d7 100644
GIT binary patch
literal 141955
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