From: Wietse Venema
Execute as root (prime group generation can take a +
With Postfix ≥ 3.7 built against OpenSSL version is 3.0.0 or later, when +the value of smtpd_tls_dh1024_param_file is either empty or "auto", the +EDH parameter selection is delegated to the OpenSSL library, which selects +appropriate parameters based on the TLS handshake. This choice is likely to be +the most interoperable with SMTP clients using various TLS libraries, and +custom local parameters are no longer recommended when using Postfix ≥ 3.7 +built against OpenSSL 3.0.0. Just leave smtpd_tls_dh1024_param_file at its +default value (both in main.cf(5) and any master.cf(5) overrides, and let +OpenSSL do the work.
+ +Otherwise, execute as root (prime group generation can take a few seconds to a few minutes):
@@ -350,7 +360,7 @@ few seconds to a few minutes):The Postfix SMTP server EDH parameter files are not secret, after all these parameters are sent to all remote SMTP clients in -the clear. Mode 0644 is fine.
+the clear. Mode 0644 is appropriate.You can improve security against pre-computation attacks further by regenerating the Postfix SMTP server EDH parameters periodically diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index 35acee727..ab2f350e5 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -5714,6 +5714,17 @@ configuration parameter. See there for details.
This feature is available in Postfix 2.3 and later.
+ + +lmtp_tls_wrappermode +(default: no) + + The LMTP-specific version of the smtp_tls_wrappermode configuration +parameter. See there for details.
+ +This feature is available in Postfix 3.0 and later.
+ +lmtp_use_tls @@ -11353,7 +11364,7 @@ href="https://tools.ietf.org/html/rfc7672">DANE is not applicable to hosts resolved via "native" lookups. As mentioned above, Postfix is not a validating https://tools.ietf.org/html/rfc4035#section-4.9">stub +href="https://tools.ietf.org/html/rfc4035#section-4.9">stub resolver; it relies on the system's configured DNSSEC-validating recursive nameserver to perform all DNSSEC validation. Since this @@ -14428,7 +14439,7 @@ contain the ":" character, and would otherwise be confused with a
Pattern matching of domain names is controlled by the presence or absence of "smtpd_client_event_limit_exceptions" in the -parent_domain_matches_subdomains parameter value (postfix 3.0 and +parent_domain_matches_subdomains parameter value (Postfix 3.0 and later).
@@ -17668,6 +17679,14 @@ The DSA algorithm is obsolete and should not be used.
File with DH parameters that the Postfix SMTP server should use with non-export EDH ciphers.
+With Postfix ≥ 3.7, built with OpenSSL version is 3.0.0 or later, if the +parameter value is either empty or "auto", then the DH parameter +selection is delegated to the OpenSSL library, which selects appropriate +parameters based on the TLS handshake. This choice is likely to be the most +interoperable with SMTP clients using various TLS libraries, and custom local +parameters are no longer recommended when using Postfix ≥ 3.7 built against +OpenSSL 3.0.0.
+The best-practice choice of parameters uses a 2048-bit prime. This is fine, despite the historical "1024" in the parameter name. Do not be tempted to use much larger values, performance degrades quickly, and you may also cease to @@ -18898,7 +18917,7 @@ whitespace. Each digest name may be followed by an optional "=<number>" suffix. For example, "sha512" may instead be specified as "sha512=2" and "sha256" may instead be specified as "sha256=1". The optional number must match the https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml#matching-types" +href="https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml#matching-types" >IANA assigned TLSA matching type number the algorithm in question. Postfix will check this constraint for the algorithms it knows about. Additional matching type algorithms registered with IANA can be added @@ -19065,7 +19084,7 @@ is unwise to choose an "bleeding-edge" curve supported by only a small subset of clients.
The default "strong" curve is rated in NSA https://web.archive.org/web/20160330034144/https://www.nsa.gov/ia/programs/suiteb_cryptography/">Suite +href="https://web.archive.org/web/20160330034144/https://www.nsa.gov/ia/programs/suiteb_cryptography/">Suite B for information classified up to SECRET.
Note: elliptic curve names are poorly standardized; different @@ -19106,7 +19125,7 @@ curve must be implemented by OpenSSL (as reported by ecparam(1) with the of RFC 4492. You should not generally change this setting.
This default "ultra" curve is rated in NSA https://web.archive.org/web/20160330034144/https://www.nsa.gov/ia/programs/suiteb_cryptography/">Suite +href="https://web.archive.org/web/20160330034144/https://www.nsa.gov/ia/programs/suiteb_cryptography/">Suite B for information classified up to TOP SECRET.
If you want to take maximal advantage of ciphers that offer = 3.7, built with OpenSSL version is 3.0.0 or later, if the +parameter value is either empty or "\fBauto\fR", then the DH parameter +selection is delegated to the OpenSSL library, which selects appropriate +parameters based on the TLS handshake. This choice is likely to be the most +interoperable with SMTP clients using various TLS libraries, and custom local +parameters are no longer recommended when using Postfix >= 3.7 built against +OpenSSL 3.0.0. +.PP The best\-practice choice of parameters uses a 2048\-bit prime. This is fine, despite the historical "1024" in the parameter name. Do not be tempted to use much larger values, performance degrades quickly, and you may also cease to diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink index 01075f455..d4902b41a 100755 --- a/postfix/mantools/postlink +++ b/postfix/mantools/postlink @@ -270,6 +270,7 @@ while (<>) { s;\blmtp_tls_loglevel\b;$&;g; s;\blmtp_tls_session_cache_database\b;$&;g; s;\blmtp_tls_session_cache_timeout\b;$&;g; + s;\blmtp_tls_wrappermode\b;$&;g; s;\blmtp_generic_maps\b;$&;g; s;\blmtp_pix_workaround_threshold_time\b;$&;g; s;\blmtp_pix_workaround_delay_time\b;$&;g; @@ -1157,7 +1158,7 @@ while (<>) { # Hyperlink URLs and RFC documents - s/(https?:\/\/[^ ,"\(\)]*[^ ,"\(\):;!?.])/$1<\/a>/; + if (!/href=/) { s/(https?:\/\/[^ ,"\(\)]*[^ ,"\(\):;!?.])/$1<\/a>/; } s/(ftp:\/\/[^ ,"\(\)]*[^ ,"\(\):;!?.])/$1<\/a>/; s/\bRFC\s*([1-9]\d*)/$&<\/a>/g; diff --git a/postfix/proto/FORWARD_SECRECY_README.html b/postfix/proto/FORWARD_SECRECY_README.html index 6cc9a60b9..d2bab2af7 100644 --- a/postfix/proto/FORWARD_SECRECY_README.html +++ b/postfix/proto/FORWARD_SECRECY_README.html @@ -334,7 +334,17 @@ for improved security against pre-computation attacks and for compatibility with Debian-patched Exim SMTP clients that require a ≥ 2048-bit length for the non-export prime.
-Execute as root (prime group generation can take a +
With Postfix ≥ 3.7 built against OpenSSL version is 3.0.0 or later, when +the value of smtpd_tls_dh1024_param_file is either empty or "auto", the +EDH parameter selection is delegated to the OpenSSL library, which selects +appropriate parameters based on the TLS handshake. This choice is likely to be +the most interoperable with SMTP clients using various TLS libraries, and +custom local parameters are no longer recommended when using Postfix ≥ 3.7 +built against OpenSSL 3.0.0. Just leave smtpd_tls_dh1024_param_file at its +default value (both in main.cf(5) and any master.cf(5) overrides, and let +OpenSSL do the work.
+ +Otherwise, execute as root (prime group generation can take a few seconds to a few minutes):
@@ -350,7 +360,7 @@ few seconds to a few minutes):The Postfix SMTP server EDH parameter files are not secret, after all these parameters are sent to all remote SMTP clients in -the clear. Mode 0644 is fine.
+the clear. Mode 0644 is appropriate.You can improve security against pre-computation attacks further by regenerating the Postfix SMTP server EDH parameters periodically diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index 29164fb35..a44a51b99 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -4908,7 +4908,7 @@ contain the ":" character, and would otherwise be confused with a
Pattern matching of domain names is controlled by the presence or absence of "smtpd_client_event_limit_exceptions" in the -parent_domain_matches_subdomains parameter value (postfix 3.0 and +parent_domain_matches_subdomains parameter value (Postfix 3.0 and later).
@@ -9810,6 +9810,14 @@ Postfix 2.3 and later; use smtpd_tls_mandatory_ciphers instead.
File with DH parameters that the Postfix SMTP server should use with non-export EDH ciphers.
+With Postfix ≥ 3.7, built with OpenSSL version is 3.0.0 or later, if the +parameter value is either empty or "auto", then the DH parameter +selection is delegated to the OpenSSL library, which selects appropriate +parameters based on the TLS handshake. This choice is likely to be the most +interoperable with SMTP clients using various TLS libraries, and custom local +parameters are no longer recommended when using Postfix ≥ 3.7 built against +OpenSSL 3.0.0.
+The best-practice choice of parameters uses a 2048-bit prime. This is fine, despite the historical "1024" in the parameter name. Do not be tempted to use much larger values, performance degrades quickly, and you may also cease to @@ -16925,6 +16933,13 @@ Postfix versions.
This feature is available in Postfix 3.0 and later.
+%PARAM lmtp_tls_wrappermode no + +The LMTP-specific version of the smtp_tls_wrappermode configuration +parameter. See there for details.
+ +This feature is available in Postfix 3.0 and later.
+ %PARAM smtp_tls_connection_reuse noTry to make multiple deliveries per TLS-encrypted connection. diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 1e03ab6a3..e7679e072 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -4111,7 +4111,7 @@ extern int var_smtpd_min_data_rate; #define VAR_SMTP_MIN_DATA_RATE "smtp_min_data_rate" #define DEF_SMTP_MIN_DATA_RATE 500 -#define VAR_LMTP_MIN_DATA_RATE "smtp_min_data_rate" +#define VAR_LMTP_MIN_DATA_RATE "lmtp_min_data_rate" #define DEF_LMTP_MIN_DATA_RATE 500 extern int var_smtp_min_data_rate; diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index d0e749a70..c85f9e648 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20210815" +#define MAIL_RELEASE_DATE "20210926" #define MAIL_VERSION_NUMBER "3.7" #ifdef SNAPSHOT diff --git a/postfix/src/postconf/postconf_builtin.c b/postfix/src/postconf/postconf_builtin.c index 8def3911c..f430568bb 100644 --- a/postfix/src/postconf/postconf_builtin.c +++ b/postfix/src/postconf/postconf_builtin.c @@ -250,6 +250,7 @@ static const char *pcf_check_mydomainname(void) static const char *pcf_mynetworks(void) { static const char *networks; + VSTRING *exp_buf; const char *junk; /* @@ -258,10 +259,12 @@ static const char *pcf_mynetworks(void) if (networks) return (networks); + exp_buf = vstring_alloc(100); + if (var_inet_interfaces == 0) { if ((pcf_cmd_mode & PCF_SHOW_DEFS) || (junk = mail_conf_lookup_eval(VAR_INET_INTERFACES)) == 0) - junk = pcf_expand_parameter_value((VSTRING *) 0, pcf_cmd_mode, + junk = pcf_expand_parameter_value(exp_buf, pcf_cmd_mode, DEF_INET_INTERFACES, (PCF_MASTER_ENT *) 0); var_inet_interfaces = mystrdup(junk); @@ -269,7 +272,7 @@ static const char *pcf_mynetworks(void) if (var_mynetworks_style == 0) { if ((pcf_cmd_mode & PCF_SHOW_DEFS) || (junk = mail_conf_lookup_eval(VAR_MYNETWORKS_STYLE)) == 0) - junk = pcf_expand_parameter_value((VSTRING *) 0, pcf_cmd_mode, + junk = pcf_expand_parameter_value(exp_buf, pcf_cmd_mode, DEF_MYNETWORKS_STYLE, (PCF_MASTER_ENT *) 0); var_mynetworks_style = mystrdup(junk); @@ -277,12 +280,13 @@ static const char *pcf_mynetworks(void) if (var_inet_protocols == 0) { if ((pcf_cmd_mode & PCF_SHOW_DEFS) || (junk = mail_conf_lookup_eval(VAR_INET_PROTOCOLS)) == 0) - junk = pcf_expand_parameter_value((VSTRING *) 0, pcf_cmd_mode, + junk = pcf_expand_parameter_value(exp_buf, pcf_cmd_mode, DEF_INET_PROTOCOLS, (PCF_MASTER_ENT *) 0); var_inet_protocols = mystrdup(junk); (void) inet_proto_init(VAR_INET_PROTOCOLS, var_inet_protocols); } + vstring_free(exp_buf); return (networks = mystrdup(mynetworks())); } diff --git a/postfix/src/postconf/postconf_lookup.c b/postfix/src/postconf/postconf_lookup.c index 3cfa9f3b0..2237d9d89 100644 --- a/postfix/src/postconf/postconf_lookup.c +++ b/postfix/src/postconf/postconf_lookup.c @@ -41,7 +41,7 @@ /* /* Arguments: /* .IP buf -/* Null buffer pointer, or pointer to user-supplied buffer. +/* Pointer to user-supplied buffer; must not be null. /* .IP mode /* Bit-wise OR of zero or one of the following (other flags /* are ignored): @@ -163,13 +163,10 @@ char *pcf_expand_parameter_value(VSTRING *buf, int mode, const char *value, PCF_EVAL_CTX eval_ctx; /* - * Initialize. + * Sanity check. */ - if (buf == 0) { - if (local_buf == 0) - local_buf = vstring_alloc(10); - buf = local_buf; - } + if (buf == 0) + msg_panic("%s: null buffer pointer", myname); /* * Expand macros recursively. diff --git a/postfix/src/postconf/postconf_main.c b/postfix/src/postconf/postconf_main.c index 07485f188..8e6e226f3 100644 --- a/postfix/src/postconf/postconf_main.c +++ b/postfix/src/postconf/postconf_main.c @@ -139,8 +139,12 @@ void pcf_set_parameters(char **name_val_array) static void pcf_print_parameter(VSTREAM *fp, int mode, const char *name, PCF_PARAM_NODE *node) { + static VSTRING *exp_buf = 0; const char *value; + if (exp_buf == 0) + exp_buf = vstring_alloc(100); + /* * Use the default or actual value. */ @@ -155,7 +159,7 @@ static void pcf_print_parameter(VSTREAM *fp, int mode, const char *name, pcf_print_line(fp, mode, "%s\n", name); } else { if ((mode & PCF_SHOW_EVAL) != 0 && PCF_RAW_PARAMETER(node) == 0) - value = pcf_expand_parameter_value((VSTRING *) 0, mode, value, + value = pcf_expand_parameter_value(exp_buf, mode, value, (PCF_MASTER_ENT *) 0); if ((mode & PCF_HIDE_NAME) == 0) { pcf_print_line(fp, mode, "%s = %s\n", name, value); diff --git a/postfix/src/postconf/postconf_master.c b/postfix/src/postconf/postconf_master.c index 1a70b5dcf..f4cb854da 100644 --- a/postfix/src/postconf/postconf_master.c +++ b/postfix/src/postconf/postconf_master.c @@ -196,6 +196,8 @@ static const char *pcf_valid_master_types[] = { static const char pcf_valid_bool_types[] = "yn-"; +static VSTRING *pcf_exp_buf; + #define STR(x) vstring_str(x) /* pcf_extract_field - extract text from {}, trim leading/trailing blanks */ @@ -492,6 +494,9 @@ void pcf_print_master_entry(VSTREAM *fp, int mode, PCF_MASTER_ENT *masterp) while (0) #define ADD_SPACE ADD_TEXT(" ", 1) + if (pcf_exp_buf == 0) + pcf_exp_buf = vstring_alloc(100); + /* * Show the standard fields at their preferred column position. Use at * least one-space column separation. @@ -545,7 +550,7 @@ void pcf_print_master_entry(VSTREAM *fp, int mode, PCF_MASTER_ENT *masterp) */ if (strcmp(arg, "-o") == 0 && (mode & PCF_SHOW_EVAL) != 0) - aval = pcf_expand_parameter_value((VSTRING *) 0, mode, + aval = pcf_expand_parameter_value(pcf_exp_buf, mode, aval, masterp); /* @@ -665,6 +670,9 @@ static void pcf_print_master_field(VSTREAM *fp, int mode, int in_daemon_options; int need_parens; + if (pcf_exp_buf == 0) + pcf_exp_buf = vstring_alloc(100); + /* * Show the field value, or the first value in the case of a multi-column * field. @@ -720,7 +728,7 @@ static void pcf_print_master_field(VSTREAM *fp, int mode, */ if (strcmp(arg, "-o") == 0 && (mode & PCF_SHOW_EVAL) != 0) - aval = pcf_expand_parameter_value((VSTRING *) 0, mode, + aval = pcf_expand_parameter_value(pcf_exp_buf, mode, aval, masterp); /* @@ -876,13 +884,16 @@ static void pcf_print_master_param(VSTREAM *fp, int mode, const char *param_name, const char *param_value) { + if (pcf_exp_buf == 0) + pcf_exp_buf = vstring_alloc(100); + if (mode & PCF_HIDE_VALUE) { pcf_print_line(fp, mode, "%s%c%s\n", masterp->name_space, PCF_NAMESP_SEP_CH, param_name); } else { if ((mode & PCF_SHOW_EVAL) != 0) - param_value = pcf_expand_parameter_value((VSTRING *) 0, mode, + param_value = pcf_expand_parameter_value(pcf_exp_buf, mode, param_value, masterp); if ((mode & PCF_HIDE_NAME) == 0) { pcf_print_line(fp, mode, "%s%c%s = %s\n", diff --git a/postfix/src/tls/tls.h b/postfix/src/tls/tls.h index 2fdc90ae9..1e8d8f306 100644 --- a/postfix/src/tls/tls.h +++ b/postfix/src/tls/tls.h @@ -74,6 +74,7 @@ extern const char *str_tls_level(int); #include
#include #include /* Legacy SSLEAY_VERSION_NUMBER */ +#include /* New OpenSSL 3.0 EVP_PKEY APIs */ #include /* OPENSSL_VERSION_NUMBER */ #include @@ -83,6 +84,15 @@ extern const char *str_tls_level(int); #define ssl_cipher_stack_t STACK_OF(SSL_CIPHER) #define ssl_comp_stack_t STACK_OF(SSL_COMP) +/*- + * Official way to check minimum OpenSSL API version from 3.0 onward. + * We simply define it false for all prior versions, where we typically also + * need the patch level to determine API compatibility. + */ +#ifndef OPENSSL_VERSION_PREREQ +#define OPENSSL_VERSION_PREREQ(m,n) 0 +#endif + #if (OPENSSL_VERSION_NUMBER < 0x1010100fUL) #error "OpenSSL releases prior to 1.1.1 are no longer supported" #endif @@ -101,6 +111,16 @@ extern const char *str_tls_level(int); #define tls_get_peer_dh_pubkey SSL_get_server_tmp_key #else #define tls_get_peer_dh_pubkey SSL_get_peer_tmp_key +#endif + +#if OPENSSL_VERSION_PREREQ(3,0) +#define TLS_PEEK_PEER_CERT(ssl) SSL_get0_peer_certificate(ssl) +#define TLS_FREE_PEER_CERT(x) ((void) 0) +#define tls_set_bio_callback BIO_set_callback_ex +#else +#define TLS_PEEK_PEER_CERT(ssl) SSL_get_peer_certificate(ssl) +#define TLS_FREE_PEER_CERT(x) X509_free(x) +#define tls_set_bio_callback BIO_set_callback #endif /* @@ -604,7 +624,7 @@ extern int tls_bio(int, int, TLS_SESS_STATE *, * tls_dh.c */ extern void tls_set_dh_from_file(const char *); -extern void tls_tmp_dh(SSL_CTX *); +extern void tls_tmp_dh(SSL_CTX *, int); extern void tls_auto_eecdh_curves(SSL_CTX *, const char *); /* @@ -655,7 +675,15 @@ extern void tls_check_version(void); extern long tls_bug_bits(void); extern void tls_print_errors(void); extern void tls_info_callback(const SSL *, int, int); + +#if OPENSSL_VERSION_PREREQ(3,0) +extern long tls_bio_dump_cb(BIO *, int, const char *, size_t, int, long, + int, size_t *); + +#else extern long tls_bio_dump_cb(BIO *, int, const char *, int, long, long); + +#endif extern const EVP_MD *tls_validate_digest(const char *); /* diff --git a/postfix/src/tls/tls_client.c b/postfix/src/tls/tls_client.c index 2c4a0e4cd..c9f259916 100644 --- a/postfix/src/tls/tls_client.c +++ b/postfix/src/tls/tls_client.c @@ -1110,7 +1110,7 @@ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) * created for us, so we can use it for debugging purposes. */ if (log_mask & TLS_LOG_TLSPKTS) - BIO_set_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb); + tls_set_bio_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb); /* * If we don't trigger the handshake in the library, leave control over @@ -1161,7 +1161,7 @@ TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *TLScontext, /* Turn off packet dump if only dumping the handshake */ if ((TLScontext->log_mask & TLS_LOG_ALLPKTS) == 0) - BIO_set_callback(SSL_get_rbio(TLScontext->con), 0); + tls_set_bio_callback(SSL_get_rbio(TLScontext->con), 0); /* * The caller may want to know if this session was reused or if a new @@ -1175,7 +1175,7 @@ TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *TLScontext, * Do peername verification if requested and extract useful information * from the certificate for later use. */ - if ((peercert = SSL_get_peer_certificate(TLScontext->con)) != 0) { + if ((peercert = TLS_PEEK_PEER_CERT(TLScontext->con)) != 0) { TLScontext->peer_status |= TLS_CERT_FLAG_PRESENT; /* @@ -1195,7 +1195,6 @@ TLS_SESS_STATE *tls_client_post_connect(TLS_SESS_STATE *TLScontext, TLScontext->peer_CN, TLScontext->issuer_CN, TLScontext->peer_cert_fprint, TLScontext->peer_pkey_fprint); - X509_free(peercert); } else { TLScontext->issuer_CN = mystrdup(""); TLScontext->peer_CN = mystrdup(""); diff --git a/postfix/src/tls/tls_dh.c b/postfix/src/tls/tls_dh.c index a39a63908..2992a591a 100644 --- a/postfix/src/tls/tls_dh.c +++ b/postfix/src/tls/tls_dh.c @@ -14,8 +14,9 @@ /* SSL_CTX *ctx; /* char *configured; /* -/* void tls_tmp_dh(ctx) +/* void tls_tmp_dh(ctx, useauto) /* SSL_CTX *ctx; +/* int useauto; /* DESCRIPTION /* This module maintains parameters for Diffie-Hellman key generation. /* @@ -27,7 +28,10 @@ /* is as expected by the PEM_read_DHparams() routine. /* /* tls_auto_eecdh_curves() enables negotiation of the most preferred curve -/* among the curves specified by the "configured" argument. +/* among the curves specified by the "configured" argument. The useauto +/* argument enables OpenSSL-builtin group selection in preference to our +/* own compiled-in group. This may interoperate better with overly strict +/* peers that accept only "standard" groups (bogus threat model). /* DIAGNOSTICS /* In case of error, tls_set_dh_from_file() logs a warning and /* ignores the request. @@ -78,12 +82,20 @@ #ifndef OPENSSL_NO_ECDH #include #endif +#if OPENSSL_VERSION_PREREQ(3,0) +#include +#endif /* Application-specific. */ /* * Compiled-in FFDHE (finite-field ephemeral Diffie-Hellman) parameters. * Used when no parameters are explicitly loaded from a site-specific file. + * + * With OpenSSL 3.0 and later when no explicit parameter file is specified by + * the administrator (or the setting is "auto"), we delegate group selection + * to OpenSSL via SSL_CTX_set_dh_auto(3). + * * Using an ASN.1 DER encoding avoids the need to explicitly manipulate the * internal representation of DH parameter objects. * @@ -94,90 +106,199 @@ /*- * Generated via: * $ openssl dhparam -2 -outform DER 2048 2>/dev/null | - * hexdump -ve '/1 "0x%02x, "' | fmt + * hexdump -ve '/1 "0x%02x, "' | fmt -73 * TODO: generate at compile-time. But that is no good for the majority of * sites that install pre-compiled binaries, and breaks reproducible builds. * Instead, generate at installation time and use main.cf configuration. */ -static unsigned char dh2048_der[] = { - 0x30, 0x82, 0x01, 0x08, 0x02, 0x82, 0x01, 0x01, 0x00, 0x9e, 0x28, 0x15, - 0xc5, 0xcc, 0x9b, 0x5a, 0xb0, 0xe9, 0xab, 0x74, 0x8b, 0x2a, 0x23, 0xce, - 0xea, 0x87, 0xa0, 0x18, 0x09, 0xd0, 0x40, 0x2c, 0x93, 0x23, 0x5d, 0xc0, - 0xe9, 0x78, 0x2c, 0x53, 0xd9, 0x3e, 0x21, 0x14, 0x89, 0x5c, 0x79, 0x73, - 0x1e, 0xbd, 0x23, 0x1e, 0x18, 0x65, 0x6d, 0xd2, 0x3c, 0xeb, 0x41, 0xca, - 0xbb, 0xa9, 0x99, 0x55, 0x84, 0xae, 0x9e, 0x70, 0x57, 0x25, 0x21, 0x42, - 0xaa, 0xdb, 0x82, 0xc6, 0xe6, 0xf1, 0xcf, 0xb7, 0xbc, 0x2a, 0x56, 0xcc, - 0x55, 0x1f, 0xad, 0xe9, 0x68, 0x18, 0x22, 0xfc, 0x09, 0x62, 0xc3, 0x32, - 0x1b, 0x05, 0x1f, 0xce, 0xec, 0xe3, 0x6d, 0xb5, 0x79, 0xe0, 0x89, 0x45, - 0xf3, 0xf3, 0x26, 0xa3, 0x81, 0xd9, 0x59, 0xee, 0xed, 0x78, 0xbe, 0x0e, - 0xdd, 0xf7, 0xef, 0xcb, 0x81, 0x3f, 0x01, 0xb7, 0x10, 0x8f, 0x0d, 0xbe, - 0x29, 0x21, 0x13, 0xff, 0x2a, 0x13, 0x25, 0x75, 0x99, 0xec, 0xf5, 0x2d, - 0x49, 0x01, 0x1d, 0xa4, 0x13, 0xe8, 0x2c, 0xc8, 0x13, 0x60, 0x57, 0x98, - 0xb1, 0x06, 0x45, 0x77, 0xa4, 0x24, 0xf9, 0x27, 0x3f, 0x08, 0xe6, 0x9b, - 0x4b, 0x20, 0x3b, 0x43, 0x69, 0xa3, 0xcc, 0x9a, 0xc4, 0x3c, 0x1e, 0xec, - 0xb7, 0x35, 0xe4, 0x59, 0x6b, 0x6d, 0x2a, 0xdf, 0xf7, 0x0b, 0xd4, 0x5a, - 0x0f, 0x79, 0x80, 0xe1, 0x75, 0x4c, 0x10, 0xea, 0x26, 0xf0, 0xd5, 0xf3, - 0xa6, 0x15, 0xa9, 0x3e, 0x3d, 0x0d, 0xb8, 0x53, 0x50, 0x49, 0x77, 0x49, - 0x47, 0x43, 0x39, 0xee, 0xb8, 0x8a, 0xe5, 0x14, 0xc4, 0xe3, 0x10, 0xfb, - 0xf5, 0x52, 0xef, 0xa5, 0x8f, 0xa4, 0x7e, 0x57, 0xb9, 0x5f, 0xda, 0x00, - 0x18, 0xf0, 0x72, 0x29, 0xd4, 0xfe, 0x90, 0x5a, 0x1f, 0x1a, 0x40, 0xee, - 0x4e, 0xfa, 0x3e, 0xf3, 0x72, 0x4b, 0xea, 0x44, 0x53, 0x43, 0x53, 0x57, - 0x9b, 0x02, 0x01, 0x02, +static unsigned char builtin_der[] = { + 0x30, 0x82, 0x01, 0x08, 0x02, 0x82, 0x01, 0x01, 0x00, 0xec, 0x02, 0x7b, + 0x74, 0xc6, 0xd4, 0xb4, 0x89, 0x68, 0xfd, 0xbc, 0xe0, 0x82, 0xae, 0xd6, + 0xf1, 0x4d, 0x93, 0xaa, 0x47, 0x07, 0x84, 0x3d, 0x86, 0xf8, 0x47, 0xf7, + 0xdf, 0x08, 0x7b, 0xca, 0x04, 0xa4, 0x72, 0xec, 0x11, 0xe2, 0x38, 0x43, + 0xb7, 0x94, 0xab, 0xaf, 0xe2, 0x85, 0x59, 0x43, 0x4e, 0x71, 0x85, 0xfe, + 0x52, 0x0c, 0xe0, 0x1c, 0xb6, 0xc7, 0xb0, 0x1b, 0x06, 0xb3, 0x4d, 0x1b, + 0x4f, 0xf6, 0x4b, 0x45, 0xbd, 0x1d, 0xb8, 0xe4, 0xa4, 0x48, 0x09, 0x28, + 0x19, 0xd7, 0xce, 0xb1, 0xe5, 0x9a, 0xc4, 0x94, 0x55, 0xde, 0x4d, 0x86, + 0x0f, 0x4c, 0x5e, 0x25, 0x51, 0x6c, 0x96, 0xca, 0xfa, 0xe3, 0x01, 0x69, + 0x82, 0x6c, 0x8f, 0xf5, 0xe7, 0x0e, 0xb7, 0x8e, 0x52, 0xf1, 0xcf, 0x0b, + 0x67, 0x10, 0xd0, 0xb3, 0x77, 0x79, 0xa4, 0xc1, 0xd0, 0x0f, 0x3f, 0xf5, + 0x5c, 0x35, 0xf9, 0x46, 0xd2, 0xc7, 0xfb, 0x97, 0x6d, 0xd5, 0xbe, 0xe4, + 0x8b, 0x5a, 0xf2, 0x88, 0xfa, 0x47, 0xdc, 0xc2, 0x4a, 0x4d, 0x69, 0xd3, + 0x2a, 0xdf, 0x55, 0x6c, 0x5f, 0x71, 0x11, 0x1e, 0x87, 0x03, 0x68, 0xe1, + 0xf4, 0x21, 0x06, 0x63, 0xd9, 0x65, 0xd4, 0x0c, 0x4d, 0xa7, 0x1f, 0x15, + 0x53, 0x3a, 0x50, 0x1a, 0xf5, 0x9b, 0x50, 0x35, 0xe0, 0x16, 0xa1, 0xd7, + 0xe6, 0xbf, 0xd7, 0xd9, 0xd9, 0x53, 0xe5, 0x8b, 0xf8, 0x7b, 0x45, 0x46, + 0xb6, 0xac, 0x50, 0x16, 0x46, 0x42, 0xca, 0x76, 0x38, 0x4b, 0x8e, 0x83, + 0xc6, 0x73, 0x13, 0x9c, 0x03, 0xd1, 0x7a, 0x3d, 0x8d, 0x99, 0x34, 0x10, + 0x79, 0x67, 0x21, 0x23, 0xf9, 0x6f, 0x48, 0x9a, 0xa6, 0xde, 0xbf, 0x7f, + 0x9c, 0x16, 0x53, 0xff, 0xf7, 0x20, 0x96, 0xeb, 0x34, 0xcb, 0x5b, 0x85, + 0x2b, 0x7c, 0x98, 0x00, 0x23, 0x47, 0xce, 0xc2, 0x58, 0x12, 0x86, 0x2c, + 0x57, 0x02, 0x01, 0x02, }; - /* - * Cached results. - */ -static DH *dh_2048 = 0; +#if OPENSSL_VERSION_PREREQ(3,0) + +/* ------------------------------------- 3.0 API */ + +static EVP_PKEY *dhp = 0; + +/* load_builtin - load compile-time FFDHE group */ + +static void load_builtin(void) +{ + EVP_PKEY *tmp = 0; + OSSL_DECODER_CTX *d; + const unsigned char *endp = builtin_der; + size_t dlen = sizeof(builtin_der); + + d = OSSL_DECODER_CTX_new_for_pkey(&tmp, "DER", NULL, "DH", + OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, + NULL, NULL); + /* Check decode succeeds and consumes all data (final dlen == 0) */ + if (d && OSSL_DECODER_from_data(d, &endp, &dlen) && tmp && !dlen) { + dhp = tmp; + } else { + EVP_PKEY_free(tmp); + msg_warn("error loading compiled-in DH parameters"); + tls_print_errors(); + } + OSSL_DECODER_CTX_free(d); +} /* tls_set_dh_from_file - set Diffie-Hellman parameters from file */ void tls_set_dh_from_file(const char *path) { - FILE *paramfile; + FILE *fp; + EVP_PKEY *tmp = 0; + OSSL_DECODER_CTX *d; /* * This function is the first to set the DH parameters, but free any * prior value just in case the call sequence changes some day. */ - if (dh_2048) { - DH_free(dh_2048); - dh_2048 = 0; + if (dhp) { + EVP_PKEY_free(dhp); + dhp = 0; } - if ((paramfile = fopen(path, "r")) != 0) { - if ((dh_2048 = PEM_read_DHparams(paramfile, 0, 0, 0)) == 0) { - msg_warn("cannot load DH parameters from file %s" - " -- using compiled-in defaults", path); - tls_print_errors(); - } - (void) fclose(paramfile); /* 200411 */ - } else { + if (strcmp(path, "auto") == 0) + return; + + if ((fp = fopen(path, "r")) == 0) { msg_warn("cannot load DH parameters from file %s: %m" " -- using compiled-in defaults", path); + return; + } + d = OSSL_DECODER_CTX_new_for_pkey(&tmp, "PEM", NULL, "DH", + OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, + NULL, NULL); + if (!d || !OSSL_DECODER_from_fp(d, fp) || !tmp) { + msg_warn("error loading compiled-in DH parameters"); + tls_print_errors(); + } else { + dhp = tmp; } + OSSL_DECODER_CTX_free(d); + (void) fclose(fp); } /* tls_tmp_dh - configure FFDHE group */ -void tls_tmp_dh(SSL_CTX *ctx) +void tls_tmp_dh(SSL_CTX *ctx, int useauto) { - if (dh_2048 == 0) { - const unsigned char *endp = dh2048_der; - DH *dh = 0; - - if (d2i_DHparams(&dh, &endp, sizeof(dh2048_der)) - && sizeof(dh2048_der) == endp - dh2048_der) { - dh_2048 = dh; - } else { - DH_free(dh); /* Unlikely non-zero, but by - * the book */ - msg_warn("error loading compiled-in DH parameters"); - } + if (!dhp && !useauto) + load_builtin(); + if (!ctx) + return; + if (dhp) { + EVP_PKEY *tmp = EVP_PKEY_dup(dhp); + + if (tmp && SSL_CTX_set0_tmp_dh_pkey(ctx, tmp) > 0) + return; + EVP_PKEY_free(tmp); + msg_warn("error configuring explicit DH parameters"); + tls_print_errors(); + } else { + if (SSL_CTX_set_dh_auto(ctx, 1) > 0) + return; + msg_warn("error configuring auto DH parameters"); + tls_print_errors(); } - if (ctx != 0 && dh_2048 != 0) - SSL_CTX_set_tmp_dh(ctx, dh_2048); } +#else /* OPENSSL_VERSION_PREREQ(3,0) */ + +/* ------------------------------------- 1.1.1 API */ + +static DH *dhp = 0; + +static void load_builtin(void) +{ + DH *tmp = 0; + const unsigned char *endp = builtin_der; + + if (d2i_DHparams(&tmp, &endp, sizeof(builtin_der)) + && sizeof(builtin_der) == endp - builtin_der) { + dhp = tmp; + } else { + DH_free(tmp); + msg_warn("error loading compiled-in DH parameters"); + tls_print_errors(); + } +} + +/* tls_set_dh_from_file - set Diffie-Hellman parameters from file */ + +void tls_set_dh_from_file(const char *path) +{ + FILE *fp; + + /* + * This function is the first to set the DH parameters, but free any + * prior value just in case the call sequence changes some day. + */ + if (dhp) { + DH_free(dhp); + dhp = 0; + } + + /* + * Forwards compatibility, support "auto" by using the builtin group when + * OpenSSL is < 3.0 and does not support automatic FFDHE group selection. + */ + if (strcmp(path, "auto") == 0) + return; + + if ((fp = fopen(path, "r")) == 0) { + msg_warn("cannot load DH parameters from file %s: %m" + " -- using compiled-in defaults", path); + return; + } + if ((dhp = PEM_read_DHparams(fp, 0, 0, 0)) == 0) { + msg_warn("cannot load DH parameters from file %s" + " -- using compiled-in defaults", path); + tls_print_errors(); + } + (void) fclose(fp); +} + +/* tls_tmp_dh - configure FFDHE group */ + +void tls_tmp_dh(SSL_CTX *ctx, int useauto) +{ + if (!dhp) + load_builtin(); + if (!ctx || !dhp || SSL_CTX_set_tmp_dh(ctx, dhp) > 0) + return; + msg_warn("error configuring explicit DH parameters"); + tls_print_errors(); +} + +#endif /* OPENSSL_VERSION_PREREQ(3,0) */ + +/* ------------------------------------- Common API */ + void tls_auto_eecdh_curves(SSL_CTX *ctx, const char *configured) { #ifndef OPENSSL_NO_ECDH @@ -253,8 +374,8 @@ void tls_auto_eecdh_curves(SSL_CTX *ctx, const char *configured) int main(int unused_argc, char **unused_argv) { - tls_tmp_dh(0); - return (dh_2048 == 0); + tls_tmp_dh(0, 0); + return (dhp == 0); } #endif diff --git a/postfix/src/tls/tls_misc.c b/postfix/src/tls/tls_misc.c index 55af34d3c..338a63be4 100644 --- a/postfix/src/tls/tls_misc.c +++ b/postfix/src/tls/tls_misc.c @@ -95,13 +95,15 @@ /* int where; /* int ret; /* -/* long tls_bio_dump_cb(bio, cmd, argp, argi, argl, ret) +/* long tls_bio_dump_cb(bio, cmd, argp, len, argi, argl, ret, processed) /* BIO *bio; /* int cmd; /* const char *argp; +/* size_t len; /* int argi; /* long argl; /* unused */ -/* long ret; +/* int ret; +/* size_t *processed; /* /* int tls_log_mask(log_param, log_level) /* const char *log_param; @@ -861,18 +863,50 @@ const char *tls_set_ciphers(TLS_SESS_STATE *TLScontext, const char *grade, return (vstring_str(buf)); } +/* ec_curve_name - copy EC key curve group name */ + +#ifndef OPENSSL_NO_EC +static char *ec_curve_name(EVP_PKEY *pkey) +{ + char *curve = 0; + +#if OPENSSL_VERSION_PREREQ(3,0) + size_t namelen; + + if (EVP_PKEY_get_group_name(pkey, 0, 0, &namelen)) { + curve = mymalloc(++namelen); + if (!EVP_PKEY_get_group_name(pkey, curve, namelen, 0)) { + myfree(curve); + curve = 0; + } + } +#else + EC_KEY *eckey = EVP_PKEY_get0_EC_KEY(pkey); + int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey)); + const char *tmp = EC_curve_nid2nist(nid); + + if (!tmp) + tmp = OBJ_nid2sn(nid); + if (tmp) + curve = mystrdup(tmp); +#endif + return (curve); +} + +#endif + /* tls_get_signature_params - TLS 1.3 signature details */ void tls_get_signature_params(TLS_SESS_STATE *TLScontext) { const char *kex_name = 0; - const char *kex_curve = 0; const char *locl_sig_name = 0; - const char *locl_sig_curve = 0; const char *locl_sig_dgst = 0; const char *peer_sig_name = 0; - const char *peer_sig_curve = 0; const char *peer_sig_dgst = 0; + char *kex_curve = 0; + char *locl_sig_curve = 0; + char *peer_sig_curve = 0; int nid; SSL *ssl = TLScontext->con; int srvr = SSL_is_server(ssl); @@ -882,11 +916,6 @@ void tls_get_signature_params(TLS_SESS_STATE *TLScontext) X509 *peer_cert; EVP_PKEY *peer_pkey = 0; -#ifndef OPENSSL_NO_EC - EC_KEY *eckey; - -#endif - #define SIG_PROP(c, s, p) (*((s) ? &c->srvr_sig_##p : &c->clnt_sig_##p)) if (SSL_version(ssl) < TLS1_3_VERSION) @@ -906,11 +935,7 @@ void tls_get_signature_params(TLS_SESS_STATE *TLScontext) #ifndef OPENSSL_NO_EC case EVP_PKEY_EC: kex_name = "ECDHE"; - eckey = EVP_PKEY_get0_EC_KEY(dh_pkey); - nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey)); - kex_curve = EC_curve_nid2nist(nid); - if (!kex_curve) - kex_curve = OBJ_nid2sn(nid); + kex_curve = ec_curve_name(dh_pkey); break; #endif } @@ -951,11 +976,7 @@ void tls_get_signature_params(TLS_SESS_STATE *TLScontext) #ifndef OPENSSL_NO_EC case EVP_PKEY_EC: locl_sig_name = "ECDSA"; - eckey = EVP_PKEY_get0_EC_KEY(local_pkey); - nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey)); - locl_sig_curve = EC_curve_nid2nist(nid); - if (!locl_sig_curve) - locl_sig_curve = OBJ_nid2sn(nid); + locl_sig_curve = ec_curve_name(local_pkey); break; #endif } @@ -970,7 +991,7 @@ void tls_get_signature_params(TLS_SESS_STATE *TLScontext) locl_sig_dgst = OBJ_nid2sn(nid); } /* Signature algorithms for the peer end of the connection */ - if ((peer_cert = SSL_get_peer_certificate(ssl)) != 0) { + if ((peer_cert = TLS_PEEK_PEER_CERT(ssl)) != 0) { peer_pkey = X509_get0_pubkey(peer_cert); /* @@ -993,11 +1014,7 @@ void tls_get_signature_params(TLS_SESS_STATE *TLScontext) #ifndef OPENSSL_NO_EC case EVP_PKEY_EC: peer_sig_name = "ECDSA"; - eckey = EVP_PKEY_get0_EC_KEY(peer_pkey); - nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey)); - peer_sig_curve = EC_curve_nid2nist(nid); - if (!peer_sig_curve) - peer_sig_curve = OBJ_nid2sn(nid); + peer_sig_curve = ec_curve_name(peer_pkey); break; #endif } @@ -1010,24 +1027,21 @@ void tls_get_signature_params(TLS_SESS_STATE *TLScontext) if (SSL_get_peer_signature_nid(ssl, &nid) && nid != NID_undef) peer_sig_dgst = OBJ_nid2sn(nid); - X509_free(peer_cert); + TLS_FREE_PEER_CERT(peer_cert); } if (kex_name) { TLScontext->kex_name = mystrdup(kex_name); - if (kex_curve) - TLScontext->kex_curve = mystrdup(kex_curve); + TLScontext->kex_curve = kex_curve; } if (locl_sig_name) { SIG_PROP(TLScontext, srvr, name) = mystrdup(locl_sig_name); - if (locl_sig_curve) - SIG_PROP(TLScontext, srvr, curve) = mystrdup(locl_sig_curve); + SIG_PROP(TLScontext, srvr, curve) = locl_sig_curve; if (locl_sig_dgst) SIG_PROP(TLScontext, srvr, dgst) = mystrdup(locl_sig_dgst); } if (peer_sig_name) { SIG_PROP(TLScontext, !srvr, name) = mystrdup(peer_sig_name); - if (peer_sig_curve) - SIG_PROP(TLScontext, !srvr, curve) = mystrdup(peer_sig_curve); + SIG_PROP(TLScontext, !srvr, curve) = peer_sig_curve; if (peer_sig_dgst) SIG_PROP(TLScontext, !srvr, dgst) = mystrdup(peer_sig_dgst); } @@ -1369,7 +1383,14 @@ void tls_print_errors(void) int line; int flags; - while ((err = ERR_get_error_line_data(&file, &line, &data, &flags)) != 0) { +#if OPENSSL_VERSION_PREREQ(3,0) +/* XXX: We're ignoring the function name, do we want to log it? */ +#define ERRGET(fi, l, d, fl) ERR_get_error_all(fi, l, 0, d, fl) +#else +#define ERRGET(fi, l, d, fl) ERR_get_error_line_data(fi, l, d, fl) +#endif + + while ((err = ERRGET(&file, &line, &data, &flags)) != 0) { ERR_error_string_n(err, buffer, sizeof(buffer)); if (flags & ERR_TXT_STRING) msg_warn("TLS library problem: %s:%s:%d:%s:", @@ -1493,6 +1514,7 @@ static void tls_dump_buffer(const unsigned char *start, int len) /* taken from OpenSSL apps/s_cb.c */ +#if !OPENSSL_VERSION_PREREQ(3,0) long tls_bio_dump_cb(BIO *bio, int cmd, const char *argp, int argi, long unused_argl, long ret) { @@ -1510,6 +1532,40 @@ long tls_bio_dump_cb(BIO *bio, int cmd, const char *argp, int argi, return (ret); } +#else +long tls_bio_dump_cb(BIO *bio, int cmd, const char *argp, size_t len, + int argi, long unused_argl, int ret, size_t *processed) +{ + size_t bytes = (ret > 0 && processed != NULL) ? *processed : len; + + if (cmd == (BIO_CB_READ | BIO_CB_RETURN)) { + if (ret > 0) { + msg_info("read from %08lX [%08lX] (%ld bytes => %ld (0x%lX))", + (unsigned long) bio, (unsigned long) argp, (long) len, + (long) bytes, (long) bytes); + tls_dump_buffer((unsigned char *) argp, (int) bytes); + } else { + msg_info("read from %08lX [%08lX] (%ld bytes => %d)", + (unsigned long) bio, (unsigned long) argp, + (long) len, ret); + } + } else if (cmd == (BIO_CB_WRITE | BIO_CB_RETURN)) { + if (ret > 0) { + msg_info("write to %08lX [%08lX] (%ld bytes => %ld (0x%lX))", + (unsigned long) bio, (unsigned long) argp, (long) len, + (long) bytes, (long) bytes); + tls_dump_buffer((unsigned char *) argp, (int) bytes); + } else { + msg_info("write to %08lX [%08lX] (%ld bytes => %d)", + (unsigned long) bio, (unsigned long) argp, + (long) len, ret); + } + } + return ret; +} + +#endif + const EVP_MD *tls_validate_digest(const char *dgst) { const EVP_MD *md_alg; diff --git a/postfix/src/tls/tls_scache.c b/postfix/src/tls/tls_scache.c index 408d28681..cf722ee7e 100644 --- a/postfix/src/tls/tls_scache.c +++ b/postfix/src/tls/tls_scache.c @@ -481,14 +481,9 @@ TLS_SCACHE *tls_scache_open(const char *dbname, const char *cache_label, * Open the dictionary with O_TRUNC, so that we never have to worry about * opening a damaged file after some process terminated abnormally. */ -#ifdef SINGLE_UPDATER -#define DICT_FLAGS (DICT_FLAG_DUP_REPLACE | DICT_FLAG_OPEN_LOCK \ - | DICT_FLAG_UTF8_REQUEST) -#else #define DICT_FLAGS \ - (DICT_FLAG_DUP_REPLACE | DICT_FLAG_LOCK | DICT_FLAG_SYNC_UPDATE \ + (DICT_FLAG_DUP_REPLACE | DICT_FLAG_OPEN_LOCK | DICT_FLAG_SYNC_UPDATE \ | DICT_FLAG_UTF8_REQUEST) -#endif dict = dict_open(dbname, O_RDWR | O_CREAT | O_TRUNC, DICT_FLAGS); diff --git a/postfix/src/tls/tls_server.c b/postfix/src/tls/tls_server.c index 6162191d6..5b549f34e 100644 --- a/postfix/src/tls/tls_server.c +++ b/postfix/src/tls/tls_server.c @@ -151,6 +151,9 @@ #include #define TLS_INTERNAL #include +#if OPENSSL_VERSION_PREREQ(3,0) +#include /* EVP_MAC parameters */ +#endif #define STR(x) vstring_str(x) #define LEN(x) VSTRING_LEN(x) @@ -290,12 +293,57 @@ static int new_server_session_cb(SSL *ssl, SSL_SESSION *session) #define TLS_TKT_ACCEPT 1 /* Ticket decryptable and re-usable */ #define TLS_TKT_REISSUE 2 /* Ticket decryptable, not re-usable */ +#if defined(SSL_OP_NO_TICKET) && !defined(OPENSSL_NO_TLSEXT) + +#if OPENSSL_VERSION_PREREQ(3,0) + /* ticket_cb - configure tls session ticket encrypt/decrypt context */ -#if defined(SSL_OP_NO_TICKET) && !defined(OPENSSL_NO_TLSEXT) +static int ticket_cb(SSL *con, unsigned char name[], unsigned char iv[], + EVP_CIPHER_CTX *ctx, EVP_MAC_CTX *hctx, int create) +{ + OSSL_PARAM params[3]; + static const EVP_CIPHER *ciph; + TLS_TICKET_KEY *key; + TLS_SESS_STATE *TLScontext = SSL_get_ex_data(con, TLScontext_index); + int timeout = ((int) SSL_CTX_get_timeout(SSL_get_SSL_CTX(con))) / 2; + + if ((!ciph && (ciph = EVP_get_cipherbyname(var_tls_tkt_cipher)) == 0) + || (key = tls_mgr_key(create ? 0 : name, timeout)) == 0 + || (create && RAND_bytes(iv, TLS_TICKET_IVLEN) <= 0)) + return (create ? TLS_TKT_NOKEYS : TLS_TKT_STALE); + + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, + LN_sha256, 0); + params[1] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY, + (char *) key->hmac, + TLS_TICKET_MACLEN); + params[2] = OSSL_PARAM_construct_end(); + if (!EVP_MAC_CTX_set_params(hctx, params)) + return (create ? TLS_TKT_NOKEYS : TLS_TKT_STALE); + + if (create) { + EVP_EncryptInit_ex(ctx, ciph, NOENGINE, key->bits, iv); + memcpy((void *) name, (void *) key->name, TLS_TICKET_NAMELEN); + if (TLScontext->log_mask & TLS_LOG_CACHE) + msg_info("%s: Issuing session ticket, key expiration: %ld", + TLScontext->namaddr, (long) key->tout); + } else { + EVP_DecryptInit_ex(ctx, ciph, NOENGINE, key->bits, iv); + if (TLScontext->log_mask & TLS_LOG_CACHE) + msg_info("%s: Decrypting session ticket, key expiration: %ld", + TLScontext->namaddr, (long) key->tout); + } + TLScontext->ticketed = 1; + return (TLS_TKT_ACCEPT); +} + +#else /* OPENSSL_VERSION_PREREQ(3,0) */ + +/* ticket_cb - configure tls session ticket encrypt/decrypt context */ static int ticket_cb(SSL *con, unsigned char name[], unsigned char iv[], - EVP_CIPHER_CTX * ctx, HMAC_CTX * hctx, int create) + EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int create) { static const EVP_MD *sha256; static const EVP_CIPHER *ciph; @@ -327,7 +375,10 @@ static int ticket_cb(SSL *con, unsigned char name[], unsigned char iv[], return (TLS_TKT_ACCEPT); } -#endif +#endif /* OPENSSL_VERSION_PREREQ(3,0) */ + +#endif /* defined(SSL_OP_NO_TICKET) && + * !defined(OPENSSL_NO_TLSEXT) */ /* tls_server_init - initialize the server-side TLS engine */ @@ -488,7 +539,11 @@ TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props) } } if (ticketable) { +#if OPENSSL_VERSION_PREREQ(3,0) + SSL_CTX_set_tlsext_ticket_key_evp_cb(server_ctx, ticket_cb); +#else SSL_CTX_set_tlsext_ticket_key_cb(server_ctx, ticket_cb); +#endif /* * OpenSSL 1.1.1 introduces support for TLS 1.3, which can issue more @@ -605,8 +660,8 @@ TLS_APPL_STATE *tls_server_init(const TLS_SERVER_INIT_PROPS *props) */ if (*props->dh1024_param_file != 0) tls_set_dh_from_file(props->dh1024_param_file); - tls_tmp_dh(server_ctx); - tls_tmp_dh(sni_ctx); + tls_tmp_dh(server_ctx, 1); + tls_tmp_dh(sni_ctx, 1); /* * Enable EECDH if available, errors are not fatal, we just keep going @@ -822,7 +877,7 @@ TLS_SESS_STATE *tls_server_start(const TLS_SERVER_START_PROPS *props) * created for us, so we can use it for debugging purposes. */ if (log_mask & TLS_LOG_TLSPKTS) - BIO_set_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb); + tls_set_bio_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb); /* * If we don't trigger the handshake in the library, leave control over @@ -872,7 +927,7 @@ TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *TLScontext) /* Turn off packet dump if only dumping the handshake */ if ((TLScontext->log_mask & TLS_LOG_ALLPKTS) == 0) - BIO_set_callback(SSL_get_rbio(TLScontext->con), 0); + tls_set_bio_callback(SSL_get_rbio(TLScontext->con), 0); /* * The caller may want to know if this session was reused or if a new @@ -887,7 +942,7 @@ TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *TLScontext) * Let's see whether a peer certificate is available and what is the * actual information. We want to save it for later use. */ - peer = SSL_get_peer_certificate(TLScontext->con); + peer = TLS_PEEK_PEER_CERT(TLScontext->con); if (peer != NULL) { TLScontext->peer_status |= TLS_CERT_FLAG_PRESENT; if (SSL_get_verify_result(TLScontext->con) == X509_V_OK) @@ -914,7 +969,7 @@ TLS_SESS_STATE *tls_server_post_accept(TLS_SESS_STATE *TLScontext) TLScontext->peer_cert_fprint, TLScontext->peer_pkey_fprint); } - X509_free(peer); + TLS_FREE_PEER_CERT(peer); /* * Give them a clue. Problems with trust chain verification are