/src/dovecot/src/lib-ssl-iostream/ssl-settings.c
Line | Count | Source |
1 | | /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "settings.h" |
5 | | #include "settings-parser.h" |
6 | | #include "iostream-ssl.h" |
7 | | |
8 | | #undef DEF |
9 | | #define DEF(type, name) \ |
10 | | SETTING_DEFINE_STRUCT_##type(#name, name, struct ssl_settings) |
11 | | |
12 | | static bool |
13 | | ssl_settings_check(void *_set, pool_t pool, const char **error_r); |
14 | | static bool |
15 | | ssl_server_settings_check(void *_set, pool_t pool, const char **error_r); |
16 | | |
17 | | static const struct setting_define ssl_setting_defines[] = { |
18 | | { .type = SET_FILTER_NAME, .key = "ssl_client", }, |
19 | | { .type = SET_FILTER_NAME, .key = "ssl_server", }, |
20 | | |
21 | | DEF(FILE, ssl_client_ca_file), |
22 | | DEF(STR, ssl_client_ca_dir), |
23 | | DEF(FILE, ssl_client_cert_file), |
24 | | DEF(FILE, ssl_client_key_file), |
25 | | DEF(STR, ssl_client_key_password), |
26 | | |
27 | | DEF(STR, ssl_cipher_list), |
28 | | DEF(STR, ssl_cipher_suites), |
29 | | DEF(STR, ssl_curve_list), |
30 | | DEF(STR, ssl_min_protocol), |
31 | | DEF(STR, ssl_crypto_device), |
32 | | |
33 | | DEF(BOOL, ssl_client_require_valid_cert), |
34 | | DEF(STR, ssl_options), /* parsed as a string to set bools */ |
35 | | DEF(STR, ssl_peer_certificate_fingerprint_hash), |
36 | | |
37 | | SETTING_DEFINE_LIST_END |
38 | | }; |
39 | | |
40 | | const struct ssl_settings ssl_default_settings = { |
41 | | .ssl_client_ca_file = "", |
42 | | .ssl_client_ca_dir = "", |
43 | | .ssl_client_cert_file = "", |
44 | | .ssl_client_key_file = "", |
45 | | .ssl_client_key_password = "", |
46 | | |
47 | | .ssl_cipher_list = "ALL:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW@STRENGTH", |
48 | | .ssl_cipher_suites = "", /* Use TLS library provided value */ |
49 | | .ssl_curve_list = "", |
50 | | .ssl_min_protocol = "TLSv1.2", |
51 | | .ssl_crypto_device = "", |
52 | | |
53 | | .ssl_client_require_valid_cert = TRUE, |
54 | | .ssl_options = "", |
55 | | |
56 | | .ssl_peer_certificate_fingerprint_hash = "", |
57 | | }; |
58 | | |
59 | | static const struct setting_keyvalue ssl_default_settings_keyvalue[] = { |
60 | | { "ssl_client/ssl_cipher_list", "" }, |
61 | | { NULL, NULL } |
62 | | }; |
63 | | |
64 | | const struct setting_parser_info ssl_setting_parser_info = { |
65 | | .name = "ssl", |
66 | | .defines = ssl_setting_defines, |
67 | | .defaults = &ssl_default_settings, |
68 | | .default_settings = ssl_default_settings_keyvalue, |
69 | | |
70 | | .pool_offset1 = 1 + offsetof(struct ssl_settings, pool), |
71 | | .struct_size = sizeof(struct ssl_settings), |
72 | | .check_func = ssl_settings_check |
73 | | }; |
74 | | |
75 | | #undef DEF |
76 | | #define DEF(type, name) \ |
77 | | SETTING_DEFINE_STRUCT_##type(#name, name, struct ssl_server_settings) |
78 | | |
79 | | static const struct setting_define ssl_server_setting_defines[] = { |
80 | | DEF(ENUM, ssl), |
81 | | DEF(FILE, ssl_server_ca_file), |
82 | | DEF(FILE, ssl_server_cert_file), |
83 | | DEF(FILE, ssl_server_key_file), |
84 | | DEF(FILE, ssl_server_alt_cert_file), |
85 | | DEF(FILE, ssl_server_alt_key_file), |
86 | | DEF(STR, ssl_server_key_password), |
87 | | DEF(FILE, ssl_server_dh_file), |
88 | | DEF(STR, ssl_server_cert_username_field), |
89 | | DEF(ENUM, ssl_server_prefer_ciphers), |
90 | | |
91 | | DEF(BOOL, ssl_server_require_crl), |
92 | | DEF(ENUM, ssl_server_request_client_cert), |
93 | | |
94 | | SETTING_DEFINE_LIST_END |
95 | | }; |
96 | | |
97 | | static const struct ssl_server_settings ssl_server_default_settings = { |
98 | | .ssl = "yes:no:required", |
99 | | .ssl_server_ca_file = "", |
100 | | .ssl_server_cert_file = "", |
101 | | .ssl_server_key_file = "", |
102 | | .ssl_server_alt_cert_file = "", |
103 | | .ssl_server_alt_key_file = "", |
104 | | .ssl_server_key_password = "", |
105 | | .ssl_server_dh_file = "", |
106 | | .ssl_server_cert_username_field = "commonName", |
107 | | .ssl_server_prefer_ciphers = "client:server", |
108 | | |
109 | | .ssl_server_require_crl = TRUE, |
110 | | .ssl_server_request_client_cert = "no:yes:any-cert", |
111 | | }; |
112 | | |
113 | | const struct setting_parser_info ssl_server_setting_parser_info = { |
114 | | .name = "ssl_server", |
115 | | |
116 | | .defines = ssl_server_setting_defines, |
117 | | .defaults = &ssl_server_default_settings, |
118 | | |
119 | | .pool_offset1 = 1 + offsetof(struct ssl_server_settings, pool), |
120 | | .struct_size = sizeof(struct ssl_server_settings), |
121 | | .check_func = ssl_server_settings_check, |
122 | | }; |
123 | | |
124 | | /* <settings checks> */ |
125 | | static bool |
126 | | ssl_settings_check(void *_set, pool_t pool ATTR_UNUSED, |
127 | | const char **error_r) |
128 | 0 | { |
129 | 0 | struct ssl_settings *set = _set; |
130 | |
|
131 | 0 | if (settings_get_config_binary() != SETTINGS_BINARY_OTHER) { |
132 | 0 | const char *proto = t_str_ucase(set->ssl_min_protocol); |
133 | 0 | if (*proto == '\0') { |
134 | 0 | *error_r = "ssl_min_protocol cannot be empty"; |
135 | 0 | return FALSE; |
136 | 0 | } |
137 | 0 | if (strstr(proto, "ANY") != NULL) |
138 | 0 | i_warning("ssl_min_protocol=ANY is used - This is " |
139 | 0 | "insecure and intended only for testing"); |
140 | 0 | } |
141 | | |
142 | | /* Now explode the ssl_options string into individual flags */ |
143 | | /* First set them all to defaults */ |
144 | 0 | set->parsed_opts.compression = FALSE; |
145 | 0 | set->parsed_opts.tickets = TRUE; |
146 | | |
147 | | /* Then modify anything specified in the string */ |
148 | 0 | const char **opts = t_strsplit_spaces(set->ssl_options, ", "); |
149 | 0 | const char *opt; |
150 | 0 | while ((opt = *opts++) != NULL) { |
151 | 0 | if (strcasecmp(opt, "compression") == 0) { |
152 | 0 | set->parsed_opts.compression = TRUE; |
153 | 0 | } else if (strcasecmp(opt, "no_ticket") == 0) { |
154 | 0 | set->parsed_opts.tickets = FALSE; |
155 | 0 | } else { |
156 | 0 | *error_r = t_strdup_printf("ssl_options: unknown flag: '%s'", |
157 | 0 | opt); |
158 | 0 | return FALSE; |
159 | 0 | } |
160 | 0 | } |
161 | | |
162 | | /* Hashing algorithms considered unsafe for |
163 | | fingerprinting purposes. */ |
164 | 0 | static const char *const unsafe_hash_algos[] = { |
165 | 0 | "md4", "md5", "sha1", "rmd160", "sm3", NULL |
166 | 0 | }; |
167 | |
|
168 | 0 | if (str_array_icase_find(unsafe_hash_algos, |
169 | 0 | set->ssl_peer_certificate_fingerprint_hash)) { |
170 | 0 | *error_r = t_strdup_printf( |
171 | 0 | "ssl_peer_certificate_fingerprint_hash: " |
172 | 0 | "Unsafe hash algorithm '%s' used", |
173 | 0 | set->ssl_peer_certificate_fingerprint_hash); |
174 | 0 | return FALSE; |
175 | 0 | } |
176 | | |
177 | 0 | return TRUE; |
178 | 0 | } |
179 | | |
180 | | static bool |
181 | | ssl_server_settings_check(void *_set, pool_t pool ATTR_UNUSED, |
182 | | const char **error_r) |
183 | 0 | { |
184 | 0 | struct ssl_server_settings *set = _set; |
185 | |
|
186 | 0 | if (strcmp(set->ssl, "no") == 0) { |
187 | | /* disabled */ |
188 | 0 | return TRUE; |
189 | 0 | } |
190 | | |
191 | 0 | if (strcmp(set->ssl_server_request_client_cert, "no") == 0) { |
192 | 0 | set->parsed_opts.request_client_cert = FALSE; |
193 | 0 | set->parsed_opts.verify_client_cert = FALSE; |
194 | 0 | } else if (strcmp(set->ssl_server_request_client_cert, "yes") == 0) { |
195 | 0 | set->parsed_opts.request_client_cert = TRUE; |
196 | 0 | set->parsed_opts.verify_client_cert = TRUE; |
197 | 0 | } else if (strcmp(set->ssl_server_request_client_cert, "any-cert") == 0) { |
198 | 0 | set->parsed_opts.request_client_cert = TRUE; |
199 | 0 | set->parsed_opts.verify_client_cert = FALSE; |
200 | 0 | } |
201 | |
|
202 | 0 | if (set->parsed_opts.request_client_cert && |
203 | 0 | set->parsed_opts.verify_client_cert && |
204 | 0 | *set->ssl_server_ca_file == '\0') { |
205 | 0 | *error_r = "ssl_server_request_client_cert=yes, but ssl_server_ca_file not provided"; |
206 | 0 | return FALSE; |
207 | 0 | } |
208 | 0 | return TRUE; |
209 | 0 | } |
210 | | /* </settings checks> */ |
211 | | |
212 | | int ssl_client_settings_get(struct event *event, |
213 | | const struct ssl_settings **set_r, |
214 | | const char **error_r) |
215 | 0 | { |
216 | 0 | event = event_create(event); |
217 | 0 | settings_event_add_filter_name(event, "ssl_client"); |
218 | 0 | int ret = settings_get(event, &ssl_setting_parser_info, 0, |
219 | 0 | set_r, error_r); |
220 | 0 | event_unref(&event); |
221 | 0 | return ret; |
222 | 0 | } |
223 | | |
224 | | int ssl_server_settings_get(struct event *event, |
225 | | const struct ssl_settings **set_r, |
226 | | const struct ssl_server_settings **server_set_r, |
227 | | const char **error_r) |
228 | 0 | { |
229 | 0 | event = event_create(event); |
230 | 0 | settings_event_add_filter_name(event, "ssl_server"); |
231 | 0 | int ret = settings_get(event, &ssl_setting_parser_info, 0, |
232 | 0 | set_r, error_r); |
233 | 0 | if (ret == 0) { |
234 | 0 | ret = settings_get(event, &ssl_server_setting_parser_info, 0, |
235 | 0 | server_set_r, error_r); |
236 | 0 | if (ret < 0) |
237 | 0 | settings_free(*set_r); |
238 | 0 | } |
239 | 0 | event_unref(&event); |
240 | 0 | return ret; |
241 | 0 | } |
242 | | |
243 | | static struct ssl_iostream_settings * |
244 | | ssl_common_settings_to_iostream_set(const struct ssl_settings *ssl_set) |
245 | 0 | { |
246 | 0 | struct ssl_iostream_settings *set; |
247 | 0 | pool_t pool = pool_alloconly_create("ssl iostream settings", 512); |
248 | 0 | set = p_new(pool, struct ssl_iostream_settings, 1); |
249 | 0 | pool_add_external_ref(pool, ssl_set->pool); |
250 | 0 | set->pool = pool; |
251 | 0 | set->min_protocol = ssl_set->ssl_min_protocol; |
252 | 0 | set->cipher_list = ssl_set->ssl_cipher_list; |
253 | 0 | set->ciphersuites = ssl_set->ssl_cipher_suites; |
254 | 0 | set->crypto_device = ssl_set->ssl_crypto_device; |
255 | |
|
256 | 0 | set->compression = ssl_set->parsed_opts.compression; |
257 | 0 | set->tickets = ssl_set->parsed_opts.tickets; |
258 | 0 | set->curve_list = ssl_set->ssl_curve_list; |
259 | 0 | set->cert_hash_algo = ssl_set->ssl_peer_certificate_fingerprint_hash; |
260 | |
|
261 | 0 | return set; |
262 | 0 | } |
263 | | |
264 | | void ssl_client_settings_to_iostream_set( |
265 | | const struct ssl_settings *ssl_set, |
266 | | const struct ssl_iostream_settings **set_r) |
267 | 0 | { |
268 | 0 | struct ssl_iostream_settings *set = |
269 | 0 | ssl_common_settings_to_iostream_set(ssl_set); |
270 | |
|
271 | 0 | settings_file_get(ssl_set->ssl_client_ca_file, |
272 | 0 | set->pool, &set->ca); |
273 | 0 | set->ca_dir = ssl_set->ssl_client_ca_dir; |
274 | 0 | settings_file_get(ssl_set->ssl_client_cert_file, |
275 | 0 | set->pool, &set->cert.cert); |
276 | 0 | settings_file_get(ssl_set->ssl_client_key_file, |
277 | 0 | set->pool, &set->cert.key); |
278 | 0 | set->cert.key_password = ssl_set->ssl_client_key_password; |
279 | 0 | set->verify_remote_cert = ssl_set->ssl_client_require_valid_cert; |
280 | 0 | set->allow_invalid_cert = !set->verify_remote_cert; |
281 | | /* client-side CRL checking not supported currently */ |
282 | 0 | set->skip_crl_check = TRUE; |
283 | 0 | *set_r = set; |
284 | 0 | } |
285 | | |
286 | | void ssl_server_settings_to_iostream_set( |
287 | | const struct ssl_settings *ssl_set, |
288 | | const struct ssl_server_settings *ssl_server_set, |
289 | | const struct ssl_iostream_settings **set_r) |
290 | 0 | { |
291 | 0 | struct ssl_iostream_settings *set = |
292 | 0 | ssl_common_settings_to_iostream_set(ssl_set); |
293 | 0 | pool_add_external_ref(set->pool, ssl_server_set->pool); |
294 | |
|
295 | 0 | settings_file_get(ssl_server_set->ssl_server_ca_file, set->pool, &set->ca); |
296 | 0 | settings_file_get(ssl_server_set->ssl_server_cert_file, |
297 | 0 | set->pool, &set->cert.cert); |
298 | 0 | settings_file_get(ssl_server_set->ssl_server_key_file, |
299 | 0 | set->pool, &set->cert.key); |
300 | 0 | set->cert.key_password = ssl_server_set->ssl_server_key_password; |
301 | 0 | if (ssl_server_set->ssl_server_alt_cert_file != NULL && |
302 | 0 | *ssl_server_set->ssl_server_alt_cert_file != '\0') { |
303 | 0 | settings_file_get(ssl_server_set->ssl_server_alt_cert_file, |
304 | 0 | set->pool, &set->alt_cert.cert); |
305 | 0 | settings_file_get(ssl_server_set->ssl_server_alt_key_file, |
306 | 0 | set->pool, &set->alt_cert.key); |
307 | 0 | set->alt_cert.key_password = |
308 | 0 | ssl_server_set->ssl_server_key_password; |
309 | 0 | } |
310 | 0 | settings_file_get(ssl_server_set->ssl_server_dh_file, |
311 | 0 | set->pool, &set->dh); |
312 | 0 | set->cert_username_field = |
313 | 0 | ssl_server_set->ssl_server_cert_username_field; |
314 | 0 | set->prefer_server_ciphers = |
315 | 0 | strcmp(ssl_server_set->ssl_server_prefer_ciphers, "server") == 0; |
316 | |
|
317 | 0 | set->verify_remote_cert = ssl_server_set->parsed_opts.request_client_cert; |
318 | 0 | set->allow_invalid_cert = !ssl_server_set->parsed_opts.verify_client_cert || |
319 | 0 | !set->verify_remote_cert; |
320 | | /* ssl_server_require_crl is used only for checking client-provided SSL |
321 | | certificate's CRL. */ |
322 | 0 | set->skip_crl_check = !ssl_server_set->ssl_server_require_crl; |
323 | 0 | *set_r = set; |
324 | 0 | } |