summaryrefslogtreecommitdiffstats
path: root/src/lib-master/master-service-ssl-settings.c
blob: 5ddf18cc8a4d9cb41c87087cc7c16138730c3f56 (plain)
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "settings-parser.h"
#include "master-service-private.h"
#include "master-service-ssl-settings.h"
#include "iostream-ssl.h"

#include <stddef.h>

#undef DEF
#define DEF(type, name) \
	SETTING_DEFINE_STRUCT_##type(#name, name, struct master_service_ssl_settings)

static bool
master_service_ssl_settings_check(void *_set, pool_t pool, const char **error_r);

static const struct setting_define master_service_ssl_setting_defines[] = {
	DEF(ENUM, ssl),
	DEF(STR, ssl_ca),
	DEF(STR, ssl_client_ca_file),
	DEF(STR, ssl_client_ca_dir),
	DEF(STR, ssl_client_cert),
	DEF(STR, ssl_client_key),
	DEF(STR, ssl_cipher_list),
	DEF(STR, ssl_cipher_suites),
	DEF(STR, ssl_curve_list),
	DEF(STR, ssl_min_protocol),
	DEF(STR, ssl_cert_username_field),
	DEF(STR, ssl_crypto_device),
	DEF(BOOL, ssl_verify_client_cert),
	DEF(BOOL, ssl_client_require_valid_cert),
	DEF(BOOL, ssl_require_crl),
	DEF(BOOL, verbose_ssl),
	DEF(BOOL, ssl_prefer_server_ciphers),
	DEF(STR, ssl_options), /* parsed as a string to set bools */

	SETTING_DEFINE_LIST_END
};

static const struct master_service_ssl_settings master_service_ssl_default_settings = {
#ifdef HAVE_SSL
	.ssl = "yes:no:required",
#else
	.ssl = "no:yes:required",
#endif
	.ssl_ca = "",
	.ssl_client_ca_file = "",
	.ssl_client_ca_dir = "",
	.ssl_client_cert = "",
	.ssl_client_key = "",
	.ssl_cipher_list = "ALL:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW@STRENGTH",
	.ssl_cipher_suites = "", /* Use TLS library provided value */
	.ssl_curve_list = "",
	.ssl_min_protocol = "TLSv1.2",
	.ssl_cert_username_field = "commonName",
	.ssl_crypto_device = "",
	.ssl_verify_client_cert = FALSE,
	.ssl_client_require_valid_cert = TRUE,
	.ssl_require_crl = TRUE,
	.verbose_ssl = FALSE,
	.ssl_prefer_server_ciphers = FALSE,
	.ssl_options = "",
};

const struct setting_parser_info master_service_ssl_setting_parser_info = {
	.module_name = "ssl",
	.defines = master_service_ssl_setting_defines,
	.defaults = &master_service_ssl_default_settings,

	.type_offset = SIZE_MAX,
	.struct_size = sizeof(struct master_service_ssl_settings),

	.parent_offset = SIZE_MAX,
	.check_func = master_service_ssl_settings_check
};

#undef DEF
#define DEF(type, name) \
	SETTING_DEFINE_STRUCT_##type(#name, name, struct master_service_ssl_server_settings)

static const struct setting_define master_service_ssl_server_setting_defines[] = {
	DEF(STR, ssl_cert),
	DEF(STR, ssl_key),
	DEF(STR, ssl_alt_cert),
	DEF(STR, ssl_alt_key),
	DEF(STR, ssl_key_password),
	DEF(STR, ssl_dh),

	SETTING_DEFINE_LIST_END
};

static const struct master_service_ssl_server_settings master_service_ssl_server_default_settings = {
	.ssl_cert = "",
	.ssl_key = "",
	.ssl_alt_cert = "",
	.ssl_alt_key = "",
	.ssl_key_password = "",
	.ssl_dh = "",
};

static const struct setting_parser_info *master_service_ssl_server_setting_dependencies[] = {
	&master_service_ssl_setting_parser_info,
	NULL
};

const struct setting_parser_info master_service_ssl_server_setting_parser_info = {
	.module_name = "ssl-server",
	.defines = master_service_ssl_server_setting_defines,
	.defaults = &master_service_ssl_server_default_settings,

	.type_offset = SIZE_MAX,
	.struct_size = sizeof(struct master_service_ssl_server_settings),

	.parent_offset = SIZE_MAX,
	.dependencies = master_service_ssl_server_setting_dependencies,
};

/* <settings checks> */
static bool
master_service_ssl_settings_check(void *_set, pool_t pool ATTR_UNUSED,
				  const char **error_r)
{
	struct master_service_ssl_settings *set = _set;

	if (strcmp(set->ssl, "no") == 0) {
		/* disabled */
		return TRUE;
	}
#ifndef HAVE_SSL
	*error_r = t_strdup_printf("SSL support not compiled in but ssl=%s",
				   set->ssl);
	return FALSE;
#else
	/* we get called from many different tools, possibly with -O parameter,
	   and few of those tools care about SSL settings. so don't check
	   ssl_cert/ssl_key/etc validity here except in doveconf, because it
	   usually is just an extra annoyance. */
#ifdef CONFIG_BINARY
	if (*set->ssl_cert == '\0') {
		*error_r = "ssl enabled, but ssl_cert not set";
		return FALSE;
	}
	if (*set->ssl_key == '\0') {
		*error_r = "ssl enabled, but ssl_key not set";
		return FALSE;
	}
#endif
	if (set->ssl_verify_client_cert && *set->ssl_ca == '\0') {
		*error_r = "ssl_verify_client_cert set, but ssl_ca not";
		return FALSE;
	}

	/* Now explode the ssl_options string into individual flags */
	/* First set them all to defaults */
	set->parsed_opts.compression = FALSE;
	set->parsed_opts.tickets = TRUE;

	/* Then modify anything specified in the string */
	const char **opts = t_strsplit_spaces(set->ssl_options, ", ");
	const char *opt;
	while ((opt = *opts++) != NULL) {
		if (strcasecmp(opt, "compression") == 0) {
			set->parsed_opts.compression = TRUE;
		} else if (strcasecmp(opt, "no_compression") == 0) {
#ifdef CONFIG_BINARY
			i_warning("DEPRECATED: no_compression is default, "
				  "so it is redundant in ssl_options");
#endif
		} else if (strcasecmp(opt, "no_ticket") == 0) {
			set->parsed_opts.tickets = FALSE;
		} else {
			*error_r = t_strdup_printf("ssl_options: unknown flag: '%s'",
						   opt);
			return FALSE;
		}
	}

#ifndef HAVE_SSL_CTX_SET1_CURVES_LIST
	if (*set->ssl_curve_list != '\0') {
		*error_r = "ssl_curve_list is set, but the linked openssl "
			   "version does not support it";
		return FALSE;
	}
#endif

	return TRUE;
#endif
}
/* </settings checks> */

const struct master_service_ssl_settings *
master_service_ssl_settings_get(struct master_service *service)
{
	return master_service_ssl_settings_get_from_parser(service->set_parser);
}

const struct master_service_ssl_settings *
master_service_ssl_settings_get_from_parser(struct setting_parser_context *set_parser)
{
	void **sets;

	sets = settings_parser_get_list(set_parser);
	return sets[1];
}

const struct master_service_ssl_server_settings *
master_service_ssl_server_settings_get(struct master_service *service)
{
	void **sets;

	i_assert(service->want_ssl_server);
	sets = settings_parser_get_list(service->set_parser);
	return sets[2];
}

static void master_service_ssl_common_settings_to_iostream_set(
	const struct master_service_ssl_settings *ssl_set, pool_t pool,
	struct ssl_iostream_settings *set_r)
{
	i_zero(set_r);
	set_r->min_protocol = p_strdup(pool, ssl_set->ssl_min_protocol);
	set_r->cipher_list = p_strdup(pool, ssl_set->ssl_cipher_list);
	/* leave NULL if empty - let library decide */
	set_r->ciphersuites = p_strdup_empty(pool, ssl_set->ssl_cipher_suites);
	/* NOTE: It's a bit questionable whether ssl_ca should be used for
	   clients. But at least for now it's needed for login-proxy. */
	set_r->ca = p_strdup_empty(pool, ssl_set->ssl_ca);

	set_r->crypto_device = p_strdup(pool, ssl_set->ssl_crypto_device);
	set_r->cert_username_field = p_strdup(pool, ssl_set->ssl_cert_username_field);

	set_r->verbose = ssl_set->verbose_ssl;
	set_r->verbose_invalid_cert = ssl_set->verbose_ssl;
	set_r->skip_crl_check = !ssl_set->ssl_require_crl;
	set_r->prefer_server_ciphers = ssl_set->ssl_prefer_server_ciphers;
	set_r->compression = ssl_set->parsed_opts.compression;
	set_r->tickets = ssl_set->parsed_opts.tickets;
	set_r->curve_list = p_strdup(pool, ssl_set->ssl_curve_list);
}

void master_service_ssl_client_settings_to_iostream_set(
	const struct master_service_ssl_settings *ssl_set, pool_t pool,
	struct ssl_iostream_settings *set_r)
{
	master_service_ssl_common_settings_to_iostream_set(ssl_set, pool, set_r);

	set_r->ca_file = p_strdup_empty(pool, ssl_set->ssl_client_ca_file);
	set_r->ca_dir = p_strdup_empty(pool, ssl_set->ssl_client_ca_dir);
	set_r->cert.cert = p_strdup_empty(pool, ssl_set->ssl_client_cert);
	set_r->cert.key = p_strdup_empty(pool, ssl_set->ssl_client_key);
	set_r->verify_remote_cert = ssl_set->ssl_client_require_valid_cert;
	set_r->allow_invalid_cert = !set_r->verify_remote_cert;
}

void master_service_ssl_server_settings_to_iostream_set(
	const struct master_service_ssl_settings *ssl_set,
	const struct master_service_ssl_server_settings *ssl_server_set,
	pool_t pool, struct ssl_iostream_settings *set_r)
{
	master_service_ssl_common_settings_to_iostream_set(ssl_set, pool, set_r);

	set_r->cert.cert = p_strdup(pool, ssl_server_set->ssl_cert);
	set_r->cert.key = p_strdup(pool, ssl_server_set->ssl_key);
	set_r->cert.key_password = p_strdup(pool, ssl_server_set->ssl_key_password);
	if (ssl_server_set->ssl_alt_cert != NULL &&
	    *ssl_server_set->ssl_alt_cert != '\0') {
		set_r->alt_cert.cert = p_strdup(pool, ssl_server_set->ssl_alt_cert);
		set_r->alt_cert.key = p_strdup(pool, ssl_server_set->ssl_alt_key);
		set_r->alt_cert.key_password = p_strdup(pool, ssl_server_set->ssl_key_password);
	}
	set_r->dh = p_strdup(pool, ssl_server_set->ssl_dh);
	set_r->verify_remote_cert = ssl_set->ssl_verify_client_cert;
	set_r->allow_invalid_cert = !set_r->verify_remote_cert;
}