/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) T_BEGIN { |
132 | 0 | const char *proto = t_str_ucase(set->ssl_min_protocol); |
133 | 0 | if (strstr(proto, "ANY") != NULL) |
134 | 0 | i_warning("ssl_min_protocol=ANY is used - This is " |
135 | 0 | "insecure and intended only for testing"); |
136 | 0 | } T_END; |
137 | | |
138 | | /* Now explode the ssl_options string into individual flags */ |
139 | | /* First set them all to defaults */ |
140 | 0 | set->parsed_opts.compression = FALSE; |
141 | 0 | set->parsed_opts.tickets = TRUE; |
142 | | |
143 | | /* Then modify anything specified in the string */ |
144 | 0 | const char **opts = t_strsplit_spaces(set->ssl_options, ", "); |
145 | 0 | const char *opt; |
146 | 0 | while ((opt = *opts++) != NULL) { |
147 | 0 | if (strcasecmp(opt, "compression") == 0) { |
148 | 0 | set->parsed_opts.compression = TRUE; |
149 | 0 | } else if (strcasecmp(opt, "no_ticket") == 0) { |
150 | 0 | set->parsed_opts.tickets = FALSE; |
151 | 0 | } else { |
152 | 0 | *error_r = t_strdup_printf("ssl_options: unknown flag: '%s'", |
153 | 0 | opt); |
154 | 0 | return FALSE; |
155 | 0 | } |
156 | 0 | } |
157 | | |
158 | | /* Hashing algorithms considered unsafe for |
159 | | fingerprinting purposes. */ |
160 | 0 | static const char *const unsafe_hash_algos[] = { |
161 | 0 | "md4", "md5", "sha1", "rmd160", "sm3", NULL |
162 | 0 | }; |
163 | |
|
164 | 0 | if (str_array_icase_find(unsafe_hash_algos, |
165 | 0 | set->ssl_peer_certificate_fingerprint_hash)) { |
166 | 0 | *error_r = t_strdup_printf( |
167 | 0 | "ssl_peer_certificate_fingerprint_hash: " |
168 | 0 | "Unsafe hash algorithm '%s' used", |
169 | 0 | set->ssl_peer_certificate_fingerprint_hash); |
170 | 0 | return FALSE; |
171 | 0 | } |
172 | | |
173 | 0 | return TRUE; |
174 | 0 | } |
175 | | |
176 | | static bool |
177 | | ssl_server_settings_check(void *_set, pool_t pool ATTR_UNUSED, |
178 | | const char **error_r) |
179 | 0 | { |
180 | 0 | struct ssl_server_settings *set = _set; |
181 | |
|
182 | 0 | if (strcmp(set->ssl, "no") == 0) { |
183 | | /* disabled */ |
184 | 0 | return TRUE; |
185 | 0 | } |
186 | | |
187 | 0 | if (strcmp(set->ssl_server_request_client_cert, "no") == 0) { |
188 | 0 | set->parsed_opts.request_client_cert = FALSE; |
189 | 0 | set->parsed_opts.verify_client_cert = FALSE; |
190 | 0 | } else if (strcmp(set->ssl_server_request_client_cert, "yes") == 0) { |
191 | 0 | set->parsed_opts.request_client_cert = TRUE; |
192 | 0 | set->parsed_opts.verify_client_cert = TRUE; |
193 | 0 | } else if (strcmp(set->ssl_server_request_client_cert, "any-cert") == 0) { |
194 | 0 | set->parsed_opts.request_client_cert = TRUE; |
195 | 0 | set->parsed_opts.verify_client_cert = FALSE; |
196 | 0 | } |
197 | |
|
198 | 0 | if (set->parsed_opts.request_client_cert && |
199 | 0 | set->parsed_opts.verify_client_cert && |
200 | 0 | *set->ssl_server_ca_file == '\0') { |
201 | 0 | *error_r = "ssl_server_request_client_cert=yes, but ssl_server_ca_file not provided"; |
202 | 0 | return FALSE; |
203 | 0 | } |
204 | 0 | return TRUE; |
205 | 0 | } |
206 | | /* </settings checks> */ |
207 | | |
208 | | int ssl_client_settings_get(struct event *event, |
209 | | const struct ssl_settings **set_r, |
210 | | const char **error_r) |
211 | 0 | { |
212 | 0 | event = event_create(event); |
213 | 0 | settings_event_add_filter_name(event, "ssl_client"); |
214 | 0 | int ret = settings_get(event, &ssl_setting_parser_info, 0, |
215 | 0 | set_r, error_r); |
216 | 0 | event_unref(&event); |
217 | 0 | return ret; |
218 | 0 | } |
219 | | |
220 | | int ssl_server_settings_get(struct event *event, |
221 | | const struct ssl_settings **set_r, |
222 | | const struct ssl_server_settings **server_set_r, |
223 | | const char **error_r) |
224 | 0 | { |
225 | 0 | event = event_create(event); |
226 | 0 | settings_event_add_filter_name(event, "ssl_server"); |
227 | 0 | int ret = settings_get(event, &ssl_setting_parser_info, 0, |
228 | 0 | set_r, error_r); |
229 | 0 | if (ret == 0) { |
230 | 0 | ret = settings_get(event, &ssl_server_setting_parser_info, 0, |
231 | 0 | server_set_r, error_r); |
232 | 0 | if (ret < 0) |
233 | 0 | settings_free(*set_r); |
234 | 0 | } |
235 | 0 | event_unref(&event); |
236 | 0 | return ret; |
237 | 0 | } |
238 | | |
239 | | static struct ssl_iostream_settings * |
240 | | ssl_common_settings_to_iostream_set(const struct ssl_settings *ssl_set) |
241 | 0 | { |
242 | 0 | struct ssl_iostream_settings *set; |
243 | 0 | pool_t pool = pool_alloconly_create("ssl iostream settings", 512); |
244 | 0 | set = p_new(pool, struct ssl_iostream_settings, 1); |
245 | 0 | pool_add_external_ref(pool, ssl_set->pool); |
246 | 0 | set->pool = pool; |
247 | 0 | set->min_protocol = ssl_set->ssl_min_protocol; |
248 | 0 | set->cipher_list = ssl_set->ssl_cipher_list; |
249 | 0 | set->ciphersuites = ssl_set->ssl_cipher_suites; |
250 | 0 | set->crypto_device = ssl_set->ssl_crypto_device; |
251 | |
|
252 | 0 | set->compression = ssl_set->parsed_opts.compression; |
253 | 0 | set->tickets = ssl_set->parsed_opts.tickets; |
254 | 0 | set->curve_list = ssl_set->ssl_curve_list; |
255 | 0 | set->cert_hash_algo = ssl_set->ssl_peer_certificate_fingerprint_hash; |
256 | |
|
257 | 0 | return set; |
258 | 0 | } |
259 | | |
260 | | void ssl_client_settings_to_iostream_set( |
261 | | const struct ssl_settings *ssl_set, |
262 | | const struct ssl_iostream_settings **set_r) |
263 | 0 | { |
264 | 0 | struct ssl_iostream_settings *set = |
265 | 0 | ssl_common_settings_to_iostream_set(ssl_set); |
266 | |
|
267 | 0 | settings_file_get(ssl_set->ssl_client_ca_file, |
268 | 0 | set->pool, &set->ca); |
269 | 0 | set->ca_dir = ssl_set->ssl_client_ca_dir; |
270 | 0 | settings_file_get(ssl_set->ssl_client_cert_file, |
271 | 0 | set->pool, &set->cert.cert); |
272 | 0 | settings_file_get(ssl_set->ssl_client_key_file, |
273 | 0 | set->pool, &set->cert.key); |
274 | 0 | set->cert.key_password = ssl_set->ssl_client_key_password; |
275 | 0 | set->verify_remote_cert = ssl_set->ssl_client_require_valid_cert; |
276 | 0 | set->allow_invalid_cert = !set->verify_remote_cert; |
277 | | /* client-side CRL checking not supported currently */ |
278 | 0 | set->skip_crl_check = TRUE; |
279 | 0 | *set_r = set; |
280 | 0 | } |
281 | | |
282 | | void ssl_server_settings_to_iostream_set( |
283 | | const struct ssl_settings *ssl_set, |
284 | | const struct ssl_server_settings *ssl_server_set, |
285 | | const struct ssl_iostream_settings **set_r) |
286 | 0 | { |
287 | 0 | struct ssl_iostream_settings *set = |
288 | 0 | ssl_common_settings_to_iostream_set(ssl_set); |
289 | 0 | pool_add_external_ref(set->pool, ssl_server_set->pool); |
290 | |
|
291 | 0 | settings_file_get(ssl_server_set->ssl_server_ca_file, set->pool, &set->ca); |
292 | 0 | settings_file_get(ssl_server_set->ssl_server_cert_file, |
293 | 0 | set->pool, &set->cert.cert); |
294 | 0 | settings_file_get(ssl_server_set->ssl_server_key_file, |
295 | 0 | set->pool, &set->cert.key); |
296 | 0 | set->cert.key_password = ssl_server_set->ssl_server_key_password; |
297 | 0 | if (ssl_server_set->ssl_server_alt_cert_file != NULL && |
298 | 0 | *ssl_server_set->ssl_server_alt_cert_file != '\0') { |
299 | 0 | settings_file_get(ssl_server_set->ssl_server_alt_cert_file, |
300 | 0 | set->pool, &set->alt_cert.cert); |
301 | 0 | settings_file_get(ssl_server_set->ssl_server_alt_key_file, |
302 | 0 | set->pool, &set->alt_cert.key); |
303 | 0 | set->alt_cert.key_password = |
304 | 0 | ssl_server_set->ssl_server_key_password; |
305 | 0 | } |
306 | 0 | settings_file_get(ssl_server_set->ssl_server_dh_file, |
307 | 0 | set->pool, &set->dh); |
308 | 0 | set->cert_username_field = |
309 | 0 | ssl_server_set->ssl_server_cert_username_field; |
310 | 0 | set->prefer_server_ciphers = |
311 | 0 | strcmp(ssl_server_set->ssl_server_prefer_ciphers, "server") == 0; |
312 | |
|
313 | 0 | set->verify_remote_cert = ssl_server_set->parsed_opts.request_client_cert; |
314 | 0 | set->allow_invalid_cert = !ssl_server_set->parsed_opts.verify_client_cert || |
315 | 0 | !set->verify_remote_cert; |
316 | | /* ssl_server_require_crl is used only for checking client-provided SSL |
317 | | certificate's CRL. */ |
318 | 0 | set->skip_crl_check = !ssl_server_set->ssl_server_require_crl; |
319 | 0 | *set_r = set; |
320 | 0 | } |