The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
conf.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 5fec103472f42db31d3fbac38fd9f7dbf458a070 $
19 *
20 * @file tls/conf.c
21 * @brief Configuration parsing for TLS servers and clients.
22 *
23 * @copyright 2026 Network RADIUS SAS (legal@networkradius.com)
24 */
25RCSID("$Id: 5fec103472f42db31d3fbac38fd9f7dbf458a070 $")
26USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */
27
28#ifdef WITH_TLS
29#define LOG_PREFIX "tls"
30
31#ifdef HAVE_SYS_STAT_H
32# include <sys/stat.h>
33#endif
34#include <openssl/conf.h>
35#include <openssl/pem.h>
36
37#include <freeradius-devel/server/module.h>
38#include <freeradius-devel/server/virtual_servers.h>
39#include <freeradius-devel/util/debug.h>
40#include <freeradius-devel/util/syserror.h>
41#include <freeradius-devel/util/rand.h>
42#include <freeradius-devel/util/math.h>
43
44#include "base.h"
45#include "log.h"
46#include "strerror.h"
47#include "utils.h"
48
49static int tls_conf_parse_cache_mode(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
50static int tls_virtual_server_cf_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
51static int tls_fragment_size_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
52static int tls_padding_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
53static int tls_ticket_lifetime_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
54static int tls_chain_private_key_file_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
55
56/** Certificate formats
57 *
58 */
59static fr_table_num_sorted_t const certificate_format_table[] = {
60 { L("ASN1"), SSL_FILETYPE_ASN1 },
61 { L("DER"), SSL_FILETYPE_ASN1 }, /* Alternate name for ASN1 */
62 { L("PEM"), SSL_FILETYPE_PEM }
63};
64static size_t certificate_format_table_len = NUM_ELEMENTS(certificate_format_table);
65
66static fr_table_num_sorted_t const chain_verify_mode_table[] = {
67 { L("hard"), FR_TLS_CHAIN_VERIFY_HARD },
68 { L("none"), FR_TLS_CHAIN_VERIFY_NONE },
69 { L("soft"), FR_TLS_CHAIN_VERIFY_SOFT }
70};
71static size_t chain_verify_mode_table_len = NUM_ELEMENTS(chain_verify_mode_table);
72
73static fr_table_num_sorted_t const cache_mode_table[] = {
74 { L("auto"), FR_TLS_TICKET_AUTO },
75 { L("disabled"), FR_TLS_TICKET_DISABLED },
76 { L("stateful"), FR_TLS_TICKET_STATEFUL },
77 { L("stateless"), FR_TLS_TICKET_STATELESS }
78};
79static size_t cache_mode_table_len = NUM_ELEMENTS(cache_mode_table);
80
81static fr_table_num_sorted_t const verify_mode_table[] = {
82 { L("all"), FR_TLS_VERIFY_MODE_ALL },
83 { L("client"), FR_TLS_VERIFY_MODE_LEAF },
84 { L("client-and-issuer"), FR_TLS_VERIFY_MODE_LEAF | FR_TLS_VERIFY_MODE_ISSUER },
85 { L("disabled"), FR_TLS_VERIFY_MODE_DISABLED },
86 { L("untrusted"), FR_TLS_VERIFY_MODE_UNTRUSTED }
87};
88static size_t verify_mode_table_len = NUM_ELEMENTS(verify_mode_table);
89
90static conf_parser_t tls_ticket_config[] = {
91 { FR_CONF_OFFSET("mode", fr_tls_ticket_conf_t, mode),
92 .func = tls_conf_parse_cache_mode,
93 .uctx = &(cf_table_parse_ctx_t){
94 .table = cache_mode_table,
95 .len = &cache_mode_table_len
96 },
97 .dflt = "auto" },
98 { FR_CONF_OFFSET_HINT_TYPE("name", FR_TYPE_STRING, fr_tls_ticket_conf_t, id_name),
99 .dflt = "%{EAP-Type}%interpreter('server')", .quote = T_DOUBLE_QUOTED_STRING },
100 { FR_CONF_OFFSET("lifetime", fr_tls_ticket_conf_t, lifetime), .func = tls_ticket_lifetime_parse, .dflt = "1d" },
101 { FR_CONF_OFFSET_IS_SET("min_lifetime", FR_TYPE_TIME_DELTA, 0, fr_tls_ticket_conf_t, min_lifetime),
102 .dflt = "10s" },
103
104 { FR_CONF_OFFSET("require_extended_master_secret", fr_tls_ticket_conf_t, require_extms), .dflt = "yes" },
105 { FR_CONF_OFFSET("require_perfect_forward_secrecy", fr_tls_ticket_conf_t, require_pfs), .dflt = "no" },
106
107 { FR_CONF_OFFSET("session_ticket_key", fr_tls_ticket_conf_t, session_ticket_key) },
108
109 /*
110 * Deprecated
111 */
112 { FR_CONF_V3_DEPRECATED("enable", fr_tls_ticket_conf_t, NULL) },
113 { FR_CONF_V3_DEPRECATED("max_entries", fr_tls_ticket_conf_t, NULL) },
114 { FR_CONF_V3_DEPRECATED("persist_dir", fr_tls_ticket_conf_t, NULL) },
115
117};
118
119static conf_parser_t tls_chain_config[] = {
120 { FR_CONF_OFFSET("format", fr_tls_chain_conf_t, file_format),
122 .uctx = &(cf_table_parse_ctx_t){
123 .table = certificate_format_table,
124 .len = &certificate_format_table_len
125 },
126 .dflt = "pem" },
127 /*
128 * private_key_file must be the last file row. Its parse
129 * callback loads every file in the section.
130 */
131 { FR_CONF_OFFSET_FLAGS("ca_file", CONF_FLAG_FILE_READABLE | CONF_FLAG_MULTI, fr_tls_chain_conf_t, ca_files) },
132 { FR_CONF_OFFSET_FLAGS("certificate_file", CONF_FLAG_FILE_READABLE | CONF_FLAG_FILE_EXISTS | CONF_FLAG_REQUIRED, fr_tls_chain_conf_t, certificate_file) },
133 { FR_CONF_OFFSET_FLAGS("private_key_password", CONF_FLAG_SECRET, fr_tls_chain_conf_t, password) },
134 { FR_CONF_OFFSET_FLAGS("private_key_file", CONF_FLAG_FILE_READABLE | CONF_FLAG_FILE_EXISTS | CONF_FLAG_REQUIRED, fr_tls_chain_conf_t, private_key_file),
135 .func = tls_chain_private_key_file_parse },
136
137 { FR_CONF_OFFSET("verify_mode", fr_tls_chain_conf_t, verify_mode),
138 .func = cf_table_parse_uint8,
139 .uctx = &(cf_table_parse_ctx_t){
140 .table = chain_verify_mode_table,
141 .len = &chain_verify_mode_table_len
142 },
143 .dflt = "hard" },
144 { FR_CONF_OFFSET("include_root_ca", fr_tls_chain_conf_t, include_root_ca), .dflt = "no" },
146};
147
148static conf_parser_t tls_verify_config[] = {
149 { FR_CONF_OFFSET("mode", fr_tls_verify_conf_t, mode),
151 .uctx = &(cf_table_parse_ctx_t){
152 .table = verify_mode_table,
153 .len = &verify_mode_table_len
154 },
155 .dflt = "all" },
156 { FR_CONF_OFFSET("attribute_mode", fr_tls_verify_conf_t, attribute_mode),
157 .func = cf_table_parse_uint8,
158 .uctx = &(cf_table_parse_ctx_t){
159 .table = verify_mode_table,
160 .len = &verify_mode_table_len
161 },
162 .dflt = "client-and-issuer" },
163 { FR_CONF_OFFSET("der_decode", fr_tls_verify_conf_t, der_decode) },
165};
166
167#ifdef PSK_MAX_IDENTITY_LEN
168static conf_parser_t tls_psk_config[] = {
169 { FR_CONF_OFFSET("identity", fr_tls_psk_conf_t, identity) },
170 { FR_CONF_OFFSET_FLAGS("key", CONF_FLAG_SECRET, fr_tls_psk_conf_t, key) },
172};
173#endif
174
175conf_parser_t fr_tls_server_config[] = {
176 { FR_CONF_OFFSET_TYPE_FLAGS("virtual_server", FR_TYPE_VOID, 0, fr_tls_conf_t, virtual_server), .func = tls_virtual_server_cf_parse },
177
178 { FR_CONF_SUBSECTION_ALLOC_MULTI("chain", 0, fr_tls_conf_t, chains, tls_chain_config),
179 .subcs_type = "fr_tls_chain_conf_t", .name2 = CF_IDENT_ANY },
180
181 { FR_CONF_V3_DEPRECATED("pem_file_type", fr_tls_conf_t, NULL) },
182 { FR_CONF_V3_DEPRECATED("certificate_file", fr_tls_conf_t, NULL) },
183 { FR_CONF_V3_DEPRECATED("private_key_password", fr_tls_conf_t, NULL) },
184 { FR_CONF_V3_DEPRECATED("private_key_file", fr_tls_conf_t, NULL) },
185
186 { FR_CONF_OFFSET("verify_depth", fr_tls_conf_t, verify_depth), .dflt = "0" },
187 { FR_CONF_OFFSET_FLAGS("ca_path", CONF_FLAG_FILE_READABLE, fr_tls_conf_t, ca_path) },
188 { FR_CONF_OFFSET_FLAGS("ca_file", CONF_FLAG_FILE_READABLE, fr_tls_conf_t, ca_file) },
189
190#ifdef PSK_MAX_IDENTITY_LEN
191 { FR_CONF_OFFSET_SUBSECTION("psk", 0, fr_tls_conf_t, psk, tls_psk_config) },
192 { FR_CONF_V3_DEPRECATED("psk_identity", fr_tls_conf_t, NULL) },
193 { FR_CONF_V3_DEPRECATED("psk_hexphrase", fr_tls_conf_t, NULL) },
194 { FR_CONF_V3_DEPRECATED("psk_query", fr_tls_conf_t, NULL) },
195#endif
196 { FR_CONF_OFFSET("keylog_file", fr_tls_conf_t, keylog_file) },
197
198 { FR_CONF_OFFSET_FLAGS("dh_file", CONF_FLAG_FILE_READABLE, fr_tls_conf_t, dh_file) },
199 { FR_CONF_OFFSET("fragment_size", fr_tls_conf_t, fragment_size), .func = tls_fragment_size_parse, .dflt = "1024" },
200 { FR_CONF_OFFSET("padding", fr_tls_conf_t, padding_block_size), .func = tls_padding_parse },
201
202 { FR_CONF_OFFSET("disable_single_dh_use", fr_tls_conf_t, disable_single_dh_use) },
203
204 { FR_CONF_OFFSET("cipher_list", fr_tls_conf_t, cipher_list) },
205 { FR_CONF_OFFSET("cipher_suites", fr_tls_conf_t, cipher_suites) },
206 { FR_CONF_OFFSET("group_list", fr_tls_conf_t, group_list) },
207 { FR_CONF_OFFSET("cipher_server_preference", fr_tls_conf_t, cipher_server_preference), .dflt = "yes" },
208#ifdef SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS
209 { FR_CONF_OFFSET("allow_renegotiation", fr_tls_conf_t, allow_renegotiation), .dflt = "no" },
210#endif
211
212#ifndef OPENSSL_NO_ECDH
213 { FR_CONF_OFFSET("ecdh_curve", fr_tls_conf_t, ecdh_curve), .dflt = "prime256v1" },
214#endif
215 { FR_CONF_OFFSET("tls_max_version", fr_tls_conf_t, tls_max_version) },
216
217 { FR_CONF_OFFSET("tls_min_version", fr_tls_conf_t, tls_min_version), .dflt = "1.2" },
218
219 { FR_CONF_OFFSET("client_hello_parse", fr_tls_conf_t, client_hello_parse )},
220
221 { FR_CONF_OFFSET_SUBSECTION("session", 0, fr_tls_conf_t, cache, tls_ticket_config) },
222
223 { FR_CONF_OFFSET_SUBSECTION("verify", 0, fr_tls_conf_t, verify, tls_verify_config) },
224
225 { FR_CONF_OFFSET_SUBSECTION("cache", CONF_FLAG_V3_DEPRECATED, fr_tls_conf_t, cache, tls_ticket_config) },
226 { FR_CONF_V3_DEPRECATED("check_cert_issuer", fr_tls_conf_t, check_cert_issuer) },
227 { FR_CONF_V3_DEPRECATED("check_cert_cn", fr_tls_conf_t, check_cert_cn) },
229};
230
231conf_parser_t fr_tls_client_config[] = {
232 /*
233 * Optional. Without it a client validates the server certificate
234 * with OpenSSL alone, which is what a caller that owns its own
235 * transport wants. With it, fr_tls_session_alloc_client() runs
236 * the `verify certificate` section of the named virtual server.
237 */
238 { FR_CONF_OFFSET_TYPE_FLAGS("virtual_server", FR_TYPE_VOID, CONF_FLAG_OK_MISSING, fr_tls_conf_t, virtual_server),
239 .func = tls_virtual_server_cf_parse },
240
241 { FR_CONF_SUBSECTION_ALLOC_MULTI("chain", CONF_FLAG_OK_MISSING, fr_tls_conf_t, chains, tls_chain_config),
242 .subcs_type = "fr_tls_chain_conf_t" },
243
244 { FR_CONF_OFFSET_SUBSECTION("verify", 0, fr_tls_conf_t, verify, tls_verify_config) },
245
246 { FR_CONF_OFFSET_SUBSECTION("session", 0, fr_tls_conf_t, cache, tls_ticket_config) },
247
248 { FR_CONF_V3_DEPRECATED("pem_file_type", fr_tls_conf_t, NULL) },
249 { FR_CONF_V3_DEPRECATED("certificate_file", fr_tls_conf_t, NULL) },
250 { FR_CONF_V3_DEPRECATED("private_key_password", fr_tls_conf_t, NULL) },
251 { FR_CONF_V3_DEPRECATED("private_key_file", fr_tls_conf_t, NULL) },
252
253#ifdef PSK_MAX_IDENTITY_LEN
254 { FR_CONF_OFFSET_SUBSECTION("psk", 0, fr_tls_conf_t, psk, tls_psk_config) },
255 { FR_CONF_V3_DEPRECATED("psk_identity", fr_tls_conf_t, NULL) },
256 { FR_CONF_V3_DEPRECATED("psk_hexphrase", fr_tls_conf_t, NULL) },
257#endif
258
259 { FR_CONF_OFFSET("keylog_file", fr_tls_conf_t, keylog_file) },
260
261 { FR_CONF_OFFSET("verify_depth", fr_tls_conf_t, verify_depth), .dflt = "0" },
262 { FR_CONF_OFFSET_FLAGS("ca_path", CONF_FLAG_FILE_READABLE, fr_tls_conf_t, ca_path) },
263
264 { FR_CONF_OFFSET_FLAGS("ca_file", CONF_FLAG_FILE_READABLE, fr_tls_conf_t, ca_file) },
265 { FR_CONF_OFFSET("dh_file", fr_tls_conf_t, dh_file) },
266 { FR_CONF_OFFSET("random_file", fr_tls_conf_t, random_file) },
267 { FR_CONF_OFFSET("fragment_size", fr_tls_conf_t, fragment_size), .func = tls_fragment_size_parse, .dflt = "1024" },
268
269 { FR_CONF_OFFSET("cipher_list", fr_tls_conf_t, cipher_list) },
270 { FR_CONF_OFFSET("cipher_suites", fr_tls_conf_t, cipher_suites) },
271
272#ifndef OPENSSL_NO_ECDH
273 { FR_CONF_OFFSET("ecdh_curve", fr_tls_conf_t, ecdh_curve), .dflt = "prime256v1" },
274#endif
275
276 { FR_CONF_OFFSET("tls_max_version", fr_tls_conf_t, tls_max_version) },
277
278 { FR_CONF_OFFSET("tls_min_version", fr_tls_conf_t, tls_min_version), .dflt = "1.2" },
279
280 { FR_CONF_OFFSET_SUBSECTION("cache", CONF_FLAG_V3_DEPRECATED, fr_tls_conf_t, cache, tls_ticket_config) },
281 { FR_CONF_V3_DEPRECATED("check_cert_issuer", fr_tls_conf_t, check_cert_issuer) },
282 { FR_CONF_V3_DEPRECATED("check_cert_cn", fr_tls_conf_t, check_cert_cn) },
284};
285
286static int tls_virtual_server_cf_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
287{
288 fr_tls_conf_t *conf = talloc_get_type_abort(parent, fr_tls_conf_t);
289 virtual_server_t const *vs = NULL;
290
291 if (virtual_server_cf_parse(ctx, &vs, parent, ci, rule) < 0) return -1;
292
293 if (!vs) return 0;
294
295 /*
296 * `out` points to conf->virtual_server
297 */
298 *((CONF_SECTION const **)out) = virtual_server_cs(vs);
299 conf->verify_certificate = cf_section_find(conf->virtual_server, "verify", "certificate") ? true : false;
300 conf->new_session = cf_section_find(conf->virtual_server, "new", "session") ? true : false;
301 conf->establish_session = cf_section_find(conf->virtual_server, "establish", "session") ? true : false;
302 conf->fail_session = cf_section_find(conf->virtual_server, "fail", "session") ? true : false;
303 conf->encode_session = cf_section_find(conf->virtual_server, "encode", "session") ? true : false;
304 conf->decode_session = cf_section_find(conf->virtual_server, "decode", "session") ? true : false;
305#ifdef PSK_MAX_IDENTITY_LEN
306 conf->load_psk = cf_section_find(conf->virtual_server, "load", "psk") ? true : false;
307#else
308 if (cf_section_find(conf->virtual_server, "load", "psk")) {
309 cf_log_err(ci, "Specified virtual_server has a \"load psk { ... }\" section, "
310 "but OpenSSL was built without PSK support");
311 return -1;
312 }
313#endif
314 return 0;
315}
316
317static int tls_conf_parse_cache_mode(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
318{
319 fr_tls_conf_t *conf = talloc_get_type_abort((uint8_t *)parent - offsetof(fr_tls_conf_t, cache), fr_tls_conf_t);
320 int cache_mode;
321
322 if (cf_table_parse_int(ctx, &cache_mode, parent, ci, rule) < 0) return -1;
323
324 /*
325 * Ensure our virtual server contains the
326 * correct sections for the specified
327 * cache mode.
328 */
329 switch (cache_mode) {
330 case FR_TLS_TICKET_DISABLED:
331 case FR_TLS_TICKET_STATELESS:
332 break;
333
334 case FR_TLS_TICKET_STATEFUL:
335 if (!conf->virtual_server) {
336 cf_log_err(ci, "A virtual_server must be set when session.mode = \"stateful\"");
337 error:
338 return -1;
339 }
340
341 if (!cf_section_find(conf->virtual_server, "load", "session")) {
342 cf_log_err(ci, "Specified virtual_server must contain a \"load session { ... }\" section "
343 "when session.mode = \"stateful\"");
344 goto error;
345 }
346
347 if (!cf_section_find(conf->virtual_server, "store", "session")) {
348 cf_log_err(ci, "Specified virtual_server must contain a \"store session { ... }\" section "
349 "when session.mode = \"stateful\"");
350 goto error;
351 }
352
353 if (!cf_section_find(conf->virtual_server, "clear", "session")) {
354 cf_log_err(ci, "Specified virtual_server must contain a \"clear session { ... }\" section "
355 "when session.mode = \"stateful\"");
356 goto error;
357 }
358
359 if (conf->tls_min_version >= (float)1.3) {
360 cf_log_err(ci, "session.mode = \"stateful\" is not supported with tls_min_version >= 1.3");
361 goto error;
362 }
363 break;
364
365 case FR_TLS_TICKET_AUTO:
366 if (!conf->virtual_server) {
367 WARN("A virtual_server must be provided for stateful caching. "
368 "session.mode = \"auto\" rewritten to session.mode = \"stateless\"");
369 cache_stateless:
370 cache_mode = FR_TLS_TICKET_STATELESS;
371 break;
372 }
373
374 if (!cf_section_find(conf->virtual_server, "load", "session")) {
375 cf_log_warn(ci, "Specified virtual_server missing \"load session { ... }\" section. "
376 "session.mode = \"auto\" rewritten to session.mode = \"stateless\"");
377 goto cache_stateless;
378 }
379
380 if (!cf_section_find(conf->virtual_server, "store", "session")) {
381 cf_log_warn(ci, "Specified virtual_server missing \"store session { ... }\" section. "
382 "session.mode = \"auto\" rewritten to session.mode = \"stateless\"");
383 goto cache_stateless;
384 }
385
386 if (!cf_section_find(conf->virtual_server, "clear", "session")) {
387 cf_log_warn(ci, "Specified virtual_server missing \"clear cache { ... }\" section. "
388 "session.mode = \"auto\" rewritten to session.mode = \"stateless\"");
389 goto cache_stateless;
390 }
391
392 if (conf->tls_min_version >= (float)1.3) {
393 cf_log_err(ci, "stateful session-resumption is not supported with tls_min_version >= 1.3. "
394 "session.mode = \"auto\" rewritten to session.mode = \"stateless\"");
395 goto cache_stateless;
396 }
397 break;
398 }
399
400 /*
401 * Generate random, ephemeral, session-ticket keys.
402 */
403 if (cache_mode & FR_TLS_TICKET_STATELESS) {
404 /*
405 * Fill the key with randomness if one
406 * wasn't specified by the user.
407 */
408 if (!conf->cache.session_ticket_key) {
409 MEM(conf->cache.session_ticket_key = talloc_array(conf, uint8_t, 256));
410 fr_rand_buffer(UNCONST(uint8_t *, conf->cache.session_ticket_key),
411 talloc_array_length(conf->cache.session_ticket_key));
412 }
413 }
414
415 *((int *)out) = cache_mode;
416
417 return 0;
418}
419
420/** Keep `fragment_size` between 100 and the TLS record limit
421 *
422 * A TLS record holds at most SSL3_RT_MAX_PLAIN_LENGTH bytes, so a larger
423 * fragment size gains nothing. The EAP RFCs ask for fragments of at least
424 * 1020 bytes, but the server accepts down to 100.
425 */
426static int tls_fragment_size_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
427{
428 uint32_t *fragment_size = out;
429
430 if (cf_pair_parse_value(ctx, out, parent, ci, rule) < 0) return -1;
431
432 if (*fragment_size < 100) {
433 cf_log_warn(ci, "Ignoring \"fragment_size = %u\", forcing to \"fragment_size = 100\"", *fragment_size);
434 *fragment_size = 100;
435 }
436 if (*fragment_size > SSL3_RT_MAX_PLAIN_LENGTH) {
437 cf_log_warn(ci, "Ignoring \"fragment_size = %u\", forcing to \"fragment_size = %u\"",
438 *fragment_size, (unsigned int)SSL3_RT_MAX_PLAIN_LENGTH);
439 *fragment_size = SSL3_RT_MAX_PLAIN_LENGTH;
440 }
441
442 return 0;
443}
444
445/** Parse padding, and clamp / round it.
446 *
447 * - 0 means "no padding"
448 * - >256 is clamped to 256
449 * - we round to multiples of 16.
450 *
451 * We mangle the padding before writing it to the configuration,
452 * because we can't write to the configuration afterwards, due to
453 * mprotect().
454 */
455static int tls_padding_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
456{
457 uint32_t *padding = out;
458
459 if (cf_pair_parse_value(ctx, out, parent, ci, rule) < 0) return -1;
460
461 if (!*padding) return 0;
462
463 if (*padding > FR_TLS_MAX_BLOCK_PADDING) {
464 cf_log_warn(ci, "Ignoring \"padding = %u\", forcing to \"padding = %u\"",
465 *padding, (unsigned int) FR_TLS_MAX_BLOCK_PADDING);
466 *padding = FR_TLS_MAX_BLOCK_PADDING;
467 }
468
469 *padding = ROUND_UP_POW2(*padding, FR_TLS_BLOCK_PADDING_MULTIPLE);
470
471 return 0;
472}
473
474/** Keep `session.lifetime` at or below #FR_TLS_MAX_SESSION_LIFETIME
475 *
476 */
477static int tls_ticket_lifetime_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
478{
479 fr_time_delta_t *lifetime = out;
480 fr_time_delta_t const max = fr_time_delta_from_sec(FR_TLS_MAX_SESSION_LIFETIME);
481
482 if (cf_pair_parse_value(ctx, out, parent, ci, rule) < 0) return -1;
483
484 if (fr_time_delta_gt(*lifetime, max)) {
485 cf_log_warn(ci, "Ignoring \"lifetime = %pV\", forcing to \"lifetime = %pV\"",
487 *lifetime = max;
488 }
489
490 return 0;
491}
492
493#ifdef __APPLE__
494/*
495 * Setting private_key_password to this marker reads the
496 * password from certadmin instead.
497 */
498static char const *special_string = "Apple:UsecertAdmin";
499
500/** Read the private key password from certadmin
501 *
502 * @param[in,out] chain whose password is replaced when set to the marker.
503 * @param[in] ci for error messages.
504 * @return
505 * - 0 on success, or if the password is not the marker.
506 * - -1 if certadmin failed.
507 */
508static int tls_chain_password_certadmin(fr_tls_chain_conf_t *chain, CONF_ITEM *ci)
509{
510 char cmd[256];
511 char *password, *buf;
512 long const max_password_len = 128;
513 FILE *cmd_pipe;
514
515 if (!chain->password) return 0;
516 if (strncmp(chain->password, special_string, strlen(special_string)) != 0) return 0;
517
518 snprintf(cmd, sizeof(cmd) - 1, "/usr/sbin/certadmin --get-private-key-passphrase \"%s\"",
519 chain->private_key_file);
520
521 DEBUG2("Getting private key passphrase using command \"%s\"", cmd);
522
523 cmd_pipe = popen(cmd, "r");
524 if (!cmd_pipe) {
525 cf_log_err(ci, "%s command failed: Unable to get private_key_password", cmd);
526 cf_log_err(ci, "Error reading private_key_file %s", chain->private_key_file);
527 return -1;
528 }
529
530 MEM(password = talloc_array(chain, char, max_password_len));
531
532 buf = fgets(password, max_password_len, cmd_pipe);
533 if (!buf || ferror(cmd_pipe)) {
534 pclose(cmd_pipe);
535 talloc_free(password);
536 cf_log_err(ci, "%s command failed: Unable to get private_key_password", cmd);
537 cf_log_err(ci, "Error reading private_key_file %s", chain->private_key_file);
538 return -1;
539 }
540 pclose(cmd_pipe);
541
542 /* Get rid of newline at end of password. */
543 for (buf = password; buf < (password + max_password_len); buf++) {
544 if ((unsigned char)*buf < ' ') {
545 *buf = '\0';
546 goto found;
547 }
548 }
549
550 talloc_free(password);
551 cf_log_err(ci, "%s command failed: Unable to get private_key_password", cmd);
552 cf_log_err(ci, "Error reading private_key_file %s - password is too long", chain->private_key_file);
553 return -1;
554
555found:
556 DEBUG3("Password from command = \"%s\"", password);
557 talloc_const_free(chain->password);
558 chain->password = password;
559
560 return 0;
561}
562#endif
563
564/** Free the OpenSSL objects that a chain section holds
565 */
566static int _tls_chain_conf_free(fr_tls_chain_conf_t *chain)
567{
568 if (chain->leaf) X509_free(chain->leaf);
569DIAG_OFF_OPTIONAL(used-but-marked-unused) /* fix spurious warnings for sk macros */
570 if (chain->extra) sk_X509_pop_free(chain->extra, X509_free);
571DIAG_ON_OPTIONAL(used-but-marked-unused)
572 if (chain->private_key) EVP_PKEY_free(chain->private_key);
573
574 return 0;
575}
576
577/** Read one certificate from a file
578 *
579 * @param[in] ci for error messages.
580 * @param[in] fp positioned at the next certificate.
581 * @param[in] format SSL_FILETYPE_PEM or SSL_FILETYPE_ASN1.
582 * @param[in] aux read the PEM auxiliary trust data, as OpenSSL does for the leaf.
583 * @return
584 * - The certificate.
585 * - NULL at the end of a PEM file, with the error queue empty.
586 * - NULL on error, with the error left on the queue.
587 */
588static X509 *tls_chain_cert_read(CONF_ITEM *ci, FILE *fp, int format, bool aux)
589{
590 X509 *cert;
591
592 switch (format) {
593 case SSL_FILETYPE_PEM:
594 cert = aux ? PEM_read_X509_AUX(fp, NULL, NULL, NULL) : PEM_read_X509(fp, NULL, NULL, NULL);
595 if (!cert) {
596 unsigned long err = ERR_peek_last_error();
597
598 /*
599 * OpenSSL reports the end of a file by writing
600 * PEM_R_NO_START_LINE to the thread local buffer.
601 */
602DIAG_OFF_OPTIONAL(used-but-marked-unused) /* fix spurious warnings for ERR_GET macros */
603 if ((ERR_GET_LIB(err) == ERR_LIB_PEM) && (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
604 ERR_clear_error();
605 }
606DIAG_ON_OPTIONAL(used-but-marked-unused)
607 }
608 return cert;
609
610 case SSL_FILETYPE_ASN1:
611 return d2i_X509_fp(fp, NULL);
612
613 default:
614 cf_log_err(ci, "Unknown certificate format %d", format);
615 return NULL;
616 }
617}
618
619/** Load the certificates and the private key of a `chain { ... }` section
620 *
621 * Reads certificate files in this order:
622 *
623 * 1. The leaf, then the remaining certificates, from certificate_file.
624 * 2. One certificate from each ca_file.
625 * 3. The private key from private_key_file.
626 *
627 * This callback stores the loaded objects in leaf, extra, and private_key,
628 * and records the earliest notAfter in valid_until.
629 */
630static int tls_chain_private_key_file_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
631{
632 fr_tls_chain_conf_t *chain = talloc_get_type_abort(parent, fr_tls_chain_conf_t);
633 FILE *fp;
634 X509 *cert;
635 size_t i, ca_cnt;
636 int n;
637
638 if (cf_pair_parse_value(ctx, out, parent, ci, rule) < 0) return -1;
639
640#ifdef __APPLE__
641 if (tls_chain_password_certadmin(chain, ci) < 0) return -1;
642#endif
643
644 talloc_set_destructor(chain, _tls_chain_conf_free);
645
646 /*
647 * The leaf comes first, then the rest of the chain.
648 */
649 fp = fopen(chain->certificate_file, "r");
650 if (!fp) {
651 cf_log_err(ci, "Failed opening certificate_file \"%s\": %s", chain->certificate_file, fr_syserror(errno));
652 return -1;
653 }
654
655 chain->leaf = tls_chain_cert_read(ci, fp, chain->file_format, true);
656 if (!chain->leaf) {
657 fr_tls_strerror_printf("No certificate found");
658 cf_log_perr(ci, "Failed reading certificate_file \"%s\"", chain->certificate_file);
659 fclose(fp);
660 return -1;
661 }
662
663 MEM(chain->extra = sk_X509_new_null());
664 while ((cert = tls_chain_cert_read(ci, fp, chain->file_format, false))) {
665 if (chain->file_format != SSL_FILETYPE_PEM) {
666 X509_free(cert);
667 break; /* A DER file holds exactly one certificate */
668 }
669DIAG_OFF_OPTIONAL(used-but-marked-unused) /* fix spurious warnings for sk macros */
670 MEM(sk_X509_push(chain->extra, cert) > 0);
671DIAG_ON_OPTIONAL(used-but-marked-unused)
672 }
673 fclose(fp);
674 if (ERR_peek_error()) {
675 fr_tls_strerror_printf(NULL);
676 cf_log_perr(ci, "Failed reading certificate_file \"%s\"", chain->certificate_file);
677 return -1;
678 }
679
680 /*
681 * One certificate per ca_file.
682 */
683 ca_cnt = chain->ca_files ? talloc_array_length(chain->ca_files) : 0;
684 for (i = 0; i < ca_cnt; i++) {
685 fp = fopen(chain->ca_files[i], "r");
686 if (!fp) {
687 cf_log_err(ci, "Failed opening ca_file \"%s\": %s", chain->ca_files[i], fr_syserror(errno));
688 return -1;
689 }
690 cert = tls_chain_cert_read(ci, fp, chain->file_format, false);
691 fclose(fp);
692 if (!cert) {
693 fr_tls_strerror_printf("No certificate found");
694 cf_log_perr(ci, "Failed reading ca_file \"%s\"", chain->ca_files[i]);
695 return -1;
696 }
697DIAG_OFF_OPTIONAL(used-but-marked-unused) /* fix spurious warnings for sk macros */
698 MEM(sk_X509_push(chain->extra, cert) > 0);
699DIAG_ON_OPTIONAL(used-but-marked-unused)
700 }
701
702 /*
703 * The password callback stops OpenSSL prompting on stdin
704 * when the key is encrypted and no password is set.
705 */
706 fp = fopen(chain->private_key_file, "r");
707 if (!fp) {
708 cf_log_err(ci, "Failed opening private_key_file \"%s\": %s", chain->private_key_file, fr_syserror(errno));
709 return -1;
710 }
711 switch (chain->file_format) {
712 case SSL_FILETYPE_PEM:
713 chain->private_key = PEM_read_PrivateKey(fp, NULL, fr_tls_session_password_cb,
714 UNCONST(char *, chain->password));
715 break;
716
717 case SSL_FILETYPE_ASN1:
718 chain->private_key = d2i_PrivateKey_fp(fp, NULL);
719 break;
720
721 default:
722 fr_assert(0);
723 break;
724 }
725 fclose(fp);
726 if (!chain->private_key) {
727 fr_tls_strerror_printf(NULL);
728 cf_log_perr(ci, "Failed reading private_key_file \"%s\"", chain->private_key_file);
729 return -1;
730 }
731
732 if (X509_check_private_key(chain->leaf, chain->private_key) != 1) {
733 fr_tls_strerror_printf(NULL);
734 cf_log_perr(ci, "Private key does not match the certificate public key");
735 return -1;
736 }
737
738 /*
739 * Record the earliest notAfter.
740 */
741 chain->valid_until = fr_unix_time_wrap(0);
742DIAG_OFF_OPTIONAL(used-but-marked-unused) /* fix spurious warnings for sk macros */
743 for (n = -1; n < sk_X509_num(chain->extra); n++) {
744 time_t not_after;
745
746 cert = (n < 0) ? chain->leaf : sk_X509_value(chain->extra, n);
747 if (fr_tls_utils_asn1time_to_epoch(&not_after, X509_get0_notAfter(cert)) < 0) {
748 cf_log_perr(ci, "Failed parsing notAfter time of certificate %d in the chain", n + 2);
749 return -1;
750 }
751 if (!fr_unix_time_ispos(chain->valid_until) ||
752 fr_unix_time_gt(chain->valid_until, fr_unix_time_from_time(not_after))) {
753 chain->valid_until = fr_unix_time_from_time(not_after);
754 }
755 }
756DIAG_ON_OPTIONAL(used-but-marked-unused)
757
758 cf_log_debug(ci, "Certificate chain valid until %pV", fr_box_date(chain->valid_until));
759
760 return 0;
761}
762#endif /* WITH_TLS */
int n
Definition acutest.h:642
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:203
#define USES_APPLE_DEPRECATED_API
Definition build.h:590
#define RCSID(id)
Definition build.h:603
#define DIAG_OFF_OPTIONAL(_x)
Definition build.h:577
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:245
#define NUM_ELEMENTS(_t)
Definition build.h:437
#define DIAG_ON_OPTIONAL(_x)
Definition build.h:578
int cf_pair_parse_value(TALLOC_CTX *ctx, void *out, UNUSED void *base, CONF_ITEM *ci, conf_parser_t const *rule)
Parses a CONF_PAIR into a C data type.
Definition cf_parse.c:252
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:712
cf_parse_t func
Override default parsing behaviour for the specified type with a custom parsing function.
Definition cf_parse.h:666
#define FR_CONF_V3_DEPRECATED(_name, _struct, _field)
conf_parser_t entry which raises an error if a deprecated v3 item is found
Definition cf_parse.h:446
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:280
#define FR_CONF_SUBSECTION_ALLOC_MULTI(_name, _flags, _struct, _field, _subcs)
conf_parser_t which allocates one struct for each instance of a subsection
Definition cf_parse.h:403
int cf_table_parse_int(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
@ CONF_FLAG_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition cf_parse.h:466
@ CONF_FLAG_MULTI
CONF_PAIR can have multiple copies.
Definition cf_parse.h:483
@ CONF_FLAG_V3_DEPRECATED
If a matching CONF_PAIR is found, error out with a deprecated message.
Definition cf_parse.h:464
@ CONF_FLAG_SECRET
Only print value if debug level >= 3.
Definition cf_parse.h:470
@ CONF_FLAG_FILE_READABLE
File matching value must exist, and must be readable.
Definition cf_parse.h:472
@ CONF_FLAG_OK_MISSING
OK if it's missing.
Definition cf_parse.h:491
@ CONF_FLAG_FILE_EXISTS
File matching value must exist.
Definition cf_parse.h:478
int cf_table_parse_uint8(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
#define FR_CONF_OFFSET_IS_SET(_name, _type, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct,...
Definition cf_parse.h:294
#define FR_CONF_OFFSET_HINT_TYPE(_name, _type, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:253
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:268
#define FR_CONF_OFFSET_SUBSECTION(_name, _flags, _struct, _field, _subcs)
conf_parser_t which populates a sub-struct using a CONF_SECTION
Definition cf_parse.h:309
#define FR_CONF_OFFSET_TYPE_FLAGS(_name, _type, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:238
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:649
Common header for all CONF_* types.
Definition cf_priv.h:54
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
Definition cf_util.c:1247
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:340
#define cf_log_perr(_cf, _fmt,...)
Definition cf_util.h:347
#define cf_log_warn(_cf, _fmt,...)
Definition cf_util.h:341
#define cf_log_debug(_cf, _fmt,...)
Definition cf_util.h:343
#define CF_IDENT_ANY
Definition cf_util.h:80
#define MEM(x)
Definition debug.h:38
talloc_free(hp)
#define DEBUG3(_fmt,...)
Definition log.h:271
#define ROUND_UP_POW2(_num, _mul)
Round up - Only works if _mul is a power of 2 but avoids division.
Definition math.h:205
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_VOID
User data.
unsigned int uint32_t
unsigned char uint8_t
static size_t used
#define fr_assert(_expr)
Definition rad_assert.h:37
#define DEBUG2(fmt,...)
#define WARN(fmt,...)
static rs_t * conf
Definition radsniff.c:52
void fr_rand_buffer(void *start, size_t length)
Definition rand.c:148
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:289
#define fr_unix_time_ispos(_a)
Definition time.h:372
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
#define fr_unix_time_gt(_a, _b)
Definition time.h:365
#define fr_unix_time_wrap(_time)
Definition time.h:160
static fr_unix_time_t fr_unix_time_from_time(time_t time)
Convert a time_t into out internal fr_unix_time_t.
Definition time.h:536
#define fr_time_delta_gt(_a, _b)
Definition time.h:283
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
@ T_DOUBLE_QUOTED_STRING
Definition token.h:119
static fr_slen_t parent
Definition pair.h:864
int fr_tls_utils_asn1time_to_epoch(time_t *out, ASN1_TIME const *asn1)
Convert OpenSSL's ASN1_TIME to an epoch time.
Definition utils.c:115
static fr_sbuff_err_t char size_t fr_sbuff_t size_t max
Definition value.h:1061
static fr_sbuff_err_t char ** out
Definition value.h:1061
#define fr_box_time_delta(_val)
Definition value.h:397
#define fr_box_date(_val)
Definition value.h:378
CONF_SECTION * virtual_server_cs(virtual_server_t const *vs)
Return the configuration section for a virtual server.
int virtual_server_cf_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Wrapper for the config parser to allow pass1 resolution of virtual servers.