forked from Kozea/Radicale
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils.py
186 lines (165 loc) · 9.2 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# This file is part of Radicale - CalDAV and CardDAV server
# Copyright © 2014 Jean-Marc Martins
# Copyright © 2012-2017 Guillaume Ayoub
# Copyright © 2017-2018 Unrud <[email protected]>
# Copyright © 2024-2024 Peter Bieringer <[email protected]>
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Radicale. If not, see <http://www.gnu.org/licenses/>.
import ssl
from importlib import import_module, metadata
from typing import Callable, Sequence, Type, TypeVar, Union
from radicale import config
from radicale.log import logger
_T_co = TypeVar("_T_co", covariant=True)
RADICALE_MODULES: Sequence[str] = ("radicale", "vobject", "passlib", "defusedxml")
def load_plugin(internal_types: Sequence[str], module_name: str,
class_name: str, base_class: Type[_T_co],
configuration: "config.Configuration") -> _T_co:
type_: Union[str, Callable] = configuration.get(module_name, "type")
if callable(type_):
logger.info("%s type is %r", module_name, type_)
return type_(configuration)
if type_ in internal_types:
module = "radicale.%s.%s" % (module_name, type_)
else:
module = type_
try:
class_ = getattr(import_module(module), class_name)
except Exception as e:
raise RuntimeError("Failed to load %s module %r: %s" %
(module_name, module, e)) from e
logger.info("%s type is %r", module_name, module)
return class_(configuration)
def package_version(name):
return metadata.version(name)
def packages_version():
versions = []
for pkg in RADICALE_MODULES:
versions.append("%s=%s" % (pkg, package_version(pkg)))
return " ".join(versions)
def ssl_context_options_by_protocol(protocol: str, ssl_context_options):
logger.debug("SSL protocol string: '%s' and current SSL context options: '0x%x'", protocol, ssl_context_options)
# disable any protocol by default
logger.debug("SSL context options, disable ALL by default")
ssl_context_options |= ssl.OP_NO_SSLv2
ssl_context_options |= ssl.OP_NO_SSLv3
ssl_context_options |= ssl.OP_NO_TLSv1
ssl_context_options |= ssl.OP_NO_TLSv1_1
ssl_context_options |= ssl.OP_NO_TLSv1_2
ssl_context_options |= ssl.OP_NO_TLSv1_3
logger.debug("SSL cleared SSL context options: '0x%x'", ssl_context_options)
for entry in protocol.split():
entry = entry.strip('+') # remove trailing '+'
if entry == "ALL":
logger.debug("SSL context options, enable ALL (some maybe not supported by underlying OpenSSL, SSLv2 not enabled at all)")
ssl_context_options &= ~ssl.OP_NO_SSLv3
ssl_context_options &= ~ssl.OP_NO_TLSv1
ssl_context_options &= ~ssl.OP_NO_TLSv1_1
ssl_context_options &= ~ssl.OP_NO_TLSv1_2
ssl_context_options &= ~ssl.OP_NO_TLSv1_3
elif entry == "SSLv2":
logger.warning("SSL context options, ignore SSLv2 (totally insecure)")
elif entry == "SSLv3":
ssl_context_options &= ~ssl.OP_NO_SSLv3
logger.debug("SSL context options, enable SSLv3 (maybe not supported by underlying OpenSSL)")
elif entry == "TLSv1":
ssl_context_options &= ~ssl.OP_NO_TLSv1
logger.debug("SSL context options, enable TLSv1 (maybe not supported by underlying OpenSSL)")
elif entry == "TLSv1.1":
logger.debug("SSL context options, enable TLSv1.1 (maybe not supported by underlying OpenSSL)")
ssl_context_options &= ~ssl.OP_NO_TLSv1_1
elif entry == "TLSv1.2":
logger.debug("SSL context options, enable TLSv1.2")
ssl_context_options &= ~ssl.OP_NO_TLSv1_2
elif entry == "TLSv1.3":
logger.debug("SSL context options, enable TLSv1.3")
ssl_context_options &= ~ssl.OP_NO_TLSv1_3
elif entry == "-ALL":
logger.debug("SSL context options, disable ALL")
ssl_context_options |= ssl.OP_NO_SSLv2
ssl_context_options |= ssl.OP_NO_SSLv3
ssl_context_options |= ssl.OP_NO_TLSv1
ssl_context_options |= ssl.OP_NO_TLSv1_1
ssl_context_options |= ssl.OP_NO_TLSv1_2
ssl_context_options |= ssl.OP_NO_TLSv1_3
elif entry == "-SSLv2":
ssl_context_options |= ssl.OP_NO_SSLv2
logger.debug("SSL context options, disable SSLv2")
elif entry == "-SSLv3":
ssl_context_options |= ssl.OP_NO_SSLv3
logger.debug("SSL context options, disable SSLv3")
elif entry == "-TLSv1":
logger.debug("SSL context options, disable TLSv1")
ssl_context_options |= ssl.OP_NO_TLSv1
elif entry == "-TLSv1.1":
logger.debug("SSL context options, disable TLSv1.1")
ssl_context_options |= ssl.OP_NO_TLSv1_1
elif entry == "-TLSv1.2":
logger.debug("SSL context options, disable TLSv1.2")
ssl_context_options |= ssl.OP_NO_TLSv1_2
elif entry == "-TLSv1.3":
logger.debug("SSL context options, disable TLSv1.3")
ssl_context_options |= ssl.OP_NO_TLSv1_3
else:
raise RuntimeError("SSL protocol config contains unsupported entry '%s'" % (entry))
logger.debug("SSL resulting context options: '0x%x'", ssl_context_options)
return ssl_context_options
def ssl_context_minimum_version_by_options(ssl_context_options):
logger.debug("SSL calculate minimum version by context options: '0x%x'", ssl_context_options)
ssl_context_minimum_version = ssl.TLSVersion.SSLv3 # default
if ((ssl_context_options & ssl.OP_NO_SSLv3) and (ssl_context_minimum_version == ssl.TLSVersion.SSLv3)):
ssl_context_minimum_version = ssl.TLSVersion.TLSv1
if ((ssl_context_options & ssl.OP_NO_TLSv1) and (ssl_context_minimum_version == ssl.TLSVersion.TLSv1)):
ssl_context_minimum_version = ssl.TLSVersion.TLSv1_1
if ((ssl_context_options & ssl.OP_NO_TLSv1_1) and (ssl_context_minimum_version == ssl.TLSVersion.TLSv1_1)):
ssl_context_minimum_version = ssl.TLSVersion.TLSv1_2
if ((ssl_context_options & ssl.OP_NO_TLSv1_2) and (ssl_context_minimum_version == ssl.TLSVersion.TLSv1_2)):
ssl_context_minimum_version = ssl.TLSVersion.TLSv1_3
if ((ssl_context_options & ssl.OP_NO_TLSv1_3) and (ssl_context_minimum_version == ssl.TLSVersion.TLSv1_3)):
ssl_context_minimum_version = 0 # all disabled
logger.debug("SSL context options: '0x%x' results in minimum version: %s", ssl_context_options, ssl_context_minimum_version)
return ssl_context_minimum_version
def ssl_context_maximum_version_by_options(ssl_context_options):
logger.debug("SSL calculate maximum version by context options: '0x%x'", ssl_context_options)
ssl_context_maximum_version = ssl.TLSVersion.TLSv1_3 # default
if ((ssl_context_options & ssl.OP_NO_TLSv1_3) and (ssl_context_maximum_version == ssl.TLSVersion.TLSv1_3)):
ssl_context_maximum_version = ssl.TLSVersion.TLSv1_2
if ((ssl_context_options & ssl.OP_NO_TLSv1_2) and (ssl_context_maximum_version == ssl.TLSVersion.TLSv1_2)):
ssl_context_maximum_version = ssl.TLSVersion.TLSv1_1
if ((ssl_context_options & ssl.OP_NO_TLSv1_1) and (ssl_context_maximum_version == ssl.TLSVersion.TLSv1_1)):
ssl_context_maximum_version = ssl.TLSVersion.TLSv1
if ((ssl_context_options & ssl.OP_NO_TLSv1) and (ssl_context_maximum_version == ssl.TLSVersion.TLSv1)):
ssl_context_maximum_version = ssl.TLSVersion.SSLv3
if ((ssl_context_options & ssl.OP_NO_SSLv3) and (ssl_context_maximum_version == ssl.TLSVersion.SSLv3)):
ssl_context_maximum_version = 0
logger.debug("SSL context options: '0x%x' results in maximum version: %s", ssl_context_options, ssl_context_maximum_version)
return ssl_context_maximum_version
def ssl_get_protocols(context):
protocols = []
if not (context.options & ssl.OP_NO_SSLv3):
if (context.minimum_version < ssl.TLSVersion.TLSv1):
protocols.append("SSLv3")
if not (context.options & ssl.OP_NO_TLSv1):
if (context.minimum_version < ssl.TLSVersion.TLSv1_1) and (context.maximum_version >= ssl.TLSVersion.TLSv1):
protocols.append("TLSv1")
if not (context.options & ssl.OP_NO_TLSv1_1):
if (context.minimum_version < ssl.TLSVersion.TLSv1_2) and (context.maximum_version >= ssl.TLSVersion.TLSv1_1):
protocols.append("TLSv1.1")
if not (context.options & ssl.OP_NO_TLSv1_2):
if (context.minimum_version <= ssl.TLSVersion.TLSv1_2) and (context.maximum_version >= ssl.TLSVersion.TLSv1_2):
protocols.append("TLSv1.2")
if not (context.options & ssl.OP_NO_TLSv1_3):
if (context.minimum_version <= ssl.TLSVersion.TLSv1_3) and (context.maximum_version >= ssl.TLSVersion.TLSv1_3):
protocols.append("TLSv1.3")
return protocols