25RCSID(
"$Id: 5fec103472f42db31d3fbac38fd9f7dbf458a070 $")
29#define LOG_PREFIX "tls"
34#include <openssl/conf.h>
35#include <openssl/pem.h>
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>
60 {
L(
"ASN1"), SSL_FILETYPE_ASN1 },
61 {
L(
"DER"), SSL_FILETYPE_ASN1 },
62 {
L(
"PEM"), SSL_FILETYPE_PEM }
64static size_t certificate_format_table_len =
NUM_ELEMENTS(certificate_format_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 }
71static size_t chain_verify_mode_table_len =
NUM_ELEMENTS(chain_verify_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 }
79static size_t cache_mode_table_len =
NUM_ELEMENTS(cache_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 }
88static size_t verify_mode_table_len =
NUM_ELEMENTS(verify_mode_table);
92 .
func = tls_conf_parse_cache_mode,
94 .table = cache_mode_table,
95 .len = &cache_mode_table_len
100 {
FR_CONF_OFFSET(
"lifetime", fr_tls_ticket_conf_t, lifetime), .func = tls_ticket_lifetime_parse, .dflt =
"1d" },
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" },
107 {
FR_CONF_OFFSET(
"session_ticket_key", fr_tls_ticket_conf_t, session_ticket_key) },
123 .table = certificate_format_table,
124 .len = &certificate_format_table_len
135 .func = tls_chain_private_key_file_parse },
137 {
FR_CONF_OFFSET(
"verify_mode", fr_tls_chain_conf_t, verify_mode),
140 .table = chain_verify_mode_table,
141 .len = &chain_verify_mode_table_len
144 {
FR_CONF_OFFSET(
"include_root_ca", fr_tls_chain_conf_t, include_root_ca), .dflt =
"no" },
152 .table = verify_mode_table,
153 .len = &verify_mode_table_len
156 {
FR_CONF_OFFSET(
"attribute_mode", fr_tls_verify_conf_t, attribute_mode),
159 .table = verify_mode_table,
160 .len = &verify_mode_table_len
162 .dflt =
"client-and-issuer" },
163 {
FR_CONF_OFFSET(
"der_decode", fr_tls_verify_conf_t, der_decode) },
167#ifdef PSK_MAX_IDENTITY_LEN
179 .subcs_type =
"fr_tls_chain_conf_t", .name2 =
CF_IDENT_ANY },
186 {
FR_CONF_OFFSET(
"verify_depth", fr_tls_conf_t, verify_depth), .dflt =
"0" },
190#ifdef PSK_MAX_IDENTITY_LEN
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 },
202 {
FR_CONF_OFFSET(
"disable_single_dh_use", fr_tls_conf_t, disable_single_dh_use) },
205 {
FR_CONF_OFFSET(
"cipher_suites", fr_tls_conf_t, cipher_suites) },
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" },
212#ifndef OPENSSL_NO_ECDH
213 {
FR_CONF_OFFSET(
"ecdh_curve", fr_tls_conf_t, ecdh_curve), .dflt =
"prime256v1" },
215 {
FR_CONF_OFFSET(
"tls_max_version", fr_tls_conf_t, tls_max_version) },
217 {
FR_CONF_OFFSET(
"tls_min_version", fr_tls_conf_t, tls_min_version), .dflt =
"1.2" },
219 {
FR_CONF_OFFSET(
"client_hello_parse", fr_tls_conf_t, client_hello_parse )},
239 .
func = tls_virtual_server_cf_parse },
242 .subcs_type =
"fr_tls_chain_conf_t" },
253#ifdef PSK_MAX_IDENTITY_LEN
261 {
FR_CONF_OFFSET(
"verify_depth", fr_tls_conf_t, verify_depth), .dflt =
"0" },
267 {
FR_CONF_OFFSET(
"fragment_size", fr_tls_conf_t, fragment_size), .func = tls_fragment_size_parse, .dflt =
"1024" },
270 {
FR_CONF_OFFSET(
"cipher_suites", fr_tls_conf_t, cipher_suites) },
272#ifndef OPENSSL_NO_ECDH
273 {
FR_CONF_OFFSET(
"ecdh_curve", fr_tls_conf_t, ecdh_curve), .dflt =
"prime256v1" },
276 {
FR_CONF_OFFSET(
"tls_max_version", fr_tls_conf_t, tls_max_version) },
278 {
FR_CONF_OFFSET(
"tls_min_version", fr_tls_conf_t, tls_min_version), .dflt =
"1.2" },
288 fr_tls_conf_t *
conf = talloc_get_type_abort(
parent, fr_tls_conf_t);
305#ifdef PSK_MAX_IDENTITY_LEN
309 cf_log_err(ci,
"Specified virtual_server has a \"load psk { ... }\" section, "
310 "but OpenSSL was built without PSK support");
319 fr_tls_conf_t *
conf = talloc_get_type_abort((
uint8_t *)
parent - offsetof(fr_tls_conf_t, cache), fr_tls_conf_t);
329 switch (cache_mode) {
330 case FR_TLS_TICKET_DISABLED:
331 case FR_TLS_TICKET_STATELESS:
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\"");
342 cf_log_err(ci,
"Specified virtual_server must contain a \"load session { ... }\" section "
343 "when session.mode = \"stateful\"");
348 cf_log_err(ci,
"Specified virtual_server must contain a \"store session { ... }\" section "
349 "when session.mode = \"stateful\"");
354 cf_log_err(ci,
"Specified virtual_server must contain a \"clear session { ... }\" section "
355 "when session.mode = \"stateful\"");
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");
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\"");
370 cache_mode = FR_TLS_TICKET_STATELESS;
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;
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;
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;
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;
403 if (cache_mode & FR_TLS_TICKET_STATELESS) {
408 if (!
conf->cache.session_ticket_key) {
411 talloc_array_length(
conf->cache.session_ticket_key));
415 *((
int *)
out) = cache_mode;
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;
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;
461 if (!*padding)
return 0;
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;
469 *padding =
ROUND_UP_POW2(*padding, FR_TLS_BLOCK_PADDING_MULTIPLE);
485 cf_log_warn(ci,
"Ignoring \"lifetime = %pV\", forcing to \"lifetime = %pV\"",
498static char const *special_string =
"Apple:UsecertAdmin";
508static int tls_chain_password_certadmin(fr_tls_chain_conf_t *chain,
CONF_ITEM *ci)
511 char *password, *buf;
512 long const max_password_len = 128;
515 if (!chain->password)
return 0;
516 if (strncmp(chain->password, special_string, strlen(special_string)) != 0)
return 0;
518 snprintf(cmd,
sizeof(cmd) - 1,
"/usr/sbin/certadmin --get-private-key-passphrase \"%s\"",
519 chain->private_key_file);
521 DEBUG2(
"Getting private key passphrase using command \"%s\"", cmd);
523 cmd_pipe = popen(cmd,
"r");
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);
530 MEM(password = talloc_array(chain,
char, max_password_len));
532 buf = fgets(password, max_password_len, cmd_pipe);
533 if (!buf || ferror(cmd_pipe)) {
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);
543 for (buf = password; buf < (password + max_password_len); buf++) {
544 if ((
unsigned char)*buf <
' ') {
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);
556 DEBUG3(
"Password from command = \"%s\"", password);
558 chain->password = password;
566static int _tls_chain_conf_free(fr_tls_chain_conf_t *chain)
568 if (chain->leaf) X509_free(chain->leaf);
570 if (chain->extra) sk_X509_pop_free(chain->extra, X509_free);
572 if (chain->private_key) EVP_PKEY_free(chain->private_key);
588static X509 *tls_chain_cert_read(
CONF_ITEM *ci, FILE *fp,
int format,
bool aux)
593 case SSL_FILETYPE_PEM:
594 cert = aux ? PEM_read_X509_AUX(fp, NULL, NULL, NULL) : PEM_read_X509(fp, NULL, NULL, NULL);
596 unsigned long err = ERR_peek_last_error();
603 if ((ERR_GET_LIB(err) == ERR_LIB_PEM) && (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
610 case SSL_FILETYPE_ASN1:
611 return d2i_X509_fp(fp, NULL);
614 cf_log_err(ci,
"Unknown certificate format %d", format);
632 fr_tls_chain_conf_t *chain = talloc_get_type_abort(
parent, fr_tls_chain_conf_t);
641 if (tls_chain_password_certadmin(chain, ci) < 0)
return -1;
644 talloc_set_destructor(chain, _tls_chain_conf_free);
649 fp = fopen(chain->certificate_file,
"r");
651 cf_log_err(ci,
"Failed opening certificate_file \"%s\": %s", chain->certificate_file,
fr_syserror(errno));
655 chain->leaf = tls_chain_cert_read(ci, fp, chain->file_format,
true);
657 fr_tls_strerror_printf(
"No certificate found");
658 cf_log_perr(ci,
"Failed reading certificate_file \"%s\"", chain->certificate_file);
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) {
670 MEM(sk_X509_push(chain->extra, cert) > 0);
674 if (ERR_peek_error()) {
675 fr_tls_strerror_printf(NULL);
676 cf_log_perr(ci,
"Failed reading certificate_file \"%s\"", chain->certificate_file);
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");
690 cert = tls_chain_cert_read(ci, fp, chain->file_format,
false);
693 fr_tls_strerror_printf(
"No certificate found");
694 cf_log_perr(ci,
"Failed reading ca_file \"%s\"", chain->ca_files[i]);
698 MEM(sk_X509_push(chain->extra, cert) > 0);
706 fp = fopen(chain->private_key_file,
"r");
708 cf_log_err(ci,
"Failed opening private_key_file \"%s\": %s", chain->private_key_file,
fr_syserror(errno));
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));
717 case SSL_FILETYPE_ASN1:
718 chain->private_key = d2i_PrivateKey_fp(fp, NULL);
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);
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");
743 for (
n = -1;
n < sk_X509_num(chain->extra);
n++) {
746 cert = (
n < 0) ? chain->leaf : sk_X509_value(chain->extra,
n);
748 cf_log_perr(ci,
"Failed parsing notAfter time of certificate %d in the chain",
n + 2);
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
#define USES_APPLE_DEPRECATED_API
#define DIAG_OFF_OPTIONAL(_x)
#define L(_str)
Helper for initialising arrays of string literals.
#define DIAG_ON_OPTIONAL(_x)
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.
#define CONF_PARSER_TERMINATOR
cf_parse_t func
Override default parsing behaviour for the specified type with a custom parsing function.
#define FR_CONF_V3_DEPRECATED(_name, _struct, _field)
conf_parser_t entry which raises an error if a deprecated v3 item is found
#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
#define FR_CONF_SUBSECTION_ALLOC_MULTI(_name, _flags, _struct, _field, _subcs)
conf_parser_t which allocates one struct for each instance of a subsection
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.
@ CONF_FLAG_MULTI
CONF_PAIR can have multiple copies.
@ CONF_FLAG_V3_DEPRECATED
If a matching CONF_PAIR is found, error out with a deprecated message.
@ CONF_FLAG_SECRET
Only print value if debug level >= 3.
@ CONF_FLAG_FILE_READABLE
File matching value must exist, and must be readable.
@ CONF_FLAG_OK_MISSING
OK if it's missing.
@ CONF_FLAG_FILE_EXISTS
File matching value must exist.
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,...
#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
#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
#define FR_CONF_OFFSET_SUBSECTION(_name, _flags, _struct, _field, _subcs)
conf_parser_t which populates a sub-struct using a CONF_SECTION
#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
Defines a CONF_PAIR to C data type mapping.
Common header for all CONF_* types.
A section grouping multiple CONF_PAIR.
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
#define cf_log_err(_cf, _fmt,...)
#define cf_log_perr(_cf, _fmt,...)
#define cf_log_warn(_cf, _fmt,...)
#define cf_log_debug(_cf, _fmt,...)
#define ROUND_UP_POW2(_num, _mul)
Round up - Only works if _mul is a power of 2 but avoids division.
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_STRING
String of printable characters.
void fr_rand_buffer(void *start, size_t length)
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
An element in a lexicographically sorted array of name to num mappings.
static int talloc_const_free(void const *ptr)
Free const'd memory.
#define fr_unix_time_ispos(_a)
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
#define fr_unix_time_gt(_a, _b)
#define fr_unix_time_wrap(_time)
static fr_unix_time_t fr_unix_time_from_time(time_t time)
Convert a time_t into out internal fr_unix_time_t.
#define fr_time_delta_gt(_a, _b)
A time delta, a difference in time measured in nanoseconds.
int fr_tls_utils_asn1time_to_epoch(time_t *out, ASN1_TIME const *asn1)
Convert OpenSSL's ASN1_TIME to an epoch time.
static fr_sbuff_err_t char size_t fr_sbuff_t size_t max
static fr_sbuff_err_t char ** out
#define fr_box_time_delta(_val)
#define fr_box_date(_val)
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.