Coverage Report

Created: 2025-11-15 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}