/*++
/* NAME
/* tls_dh
/* SUMMARY
/* Diffie-Hellman parameter support
/* SYNOPSIS
/* #define TLS_INTERNAL
/* #include <tls.h>
/*
/* void tls_set_dh_from_file(path)
/* const char *path;
/*
/* void tls_auto_groups(ctx, eecdh, ffdhe)
/* SSL_CTX *ctx;
/* char *eecdh;
/* char *ffdhe;
/*
/* void tls_tmp_dh(ctx, useauto)
/* SSL_CTX *ctx;
/* int useauto;
/* DESCRIPTION
/* This module maintains parameters for Diffie-Hellman key generation.
/*
/* tls_tmp_dh() returns the configured or compiled-in FFDHE
/* group parameters. 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.
/*
/* tls_set_dh_from_file() overrides compiled-in DH parameters
/* with those specified in the named files. The file format
/* is as expected by the PEM_read_DHparams() routine.
/*
/* tls_auto_groups() enables negotiation of the most preferred key
/* exchange group among those specified by the "eecdh" and "ffdhe"
/* arguments. The "ffdhe" argument is only used with OpenSSL 3.0
/* and later, and applies to TLS 1.3 and up.
/* DIAGNOSTICS
/* In case of error, tls_set_dh_from_file() logs a warning and
/* ignores the request.
/* LICENSE
/* .ad
/* .fi
/* This software is free. You can do with it whatever you want.
/* The original author kindly requests that you acknowledge
/* the use of his software.
/* AUTHOR(S)
/* Originally written by:
/* Lutz Jaenicke
/* BTU Cottbus
/* Allgemeine Elektrotechnik
/* Universitaetsplatz 3-4
/* D-03044 Cottbus, Germany
/*
/* Updated by:
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System library. */
#include <sys_defs.h>
#ifdef USE_TLS
#include <stdio.h>
/* Utility library. */
#include <msg.h>
#include <mymalloc.h>
#include <stringops.h>
/*
* Global library
*/
#include <been_here.h>
#include <mail_params.h>
/* TLS library. */
#define TLS_INTERNAL
#include <tls.h>
#include <openssl/dh.h>
#ifndef OPENSSL_NO_ECDH
#include <openssl/ec.h>
#endif
#if OPENSSL_VERSION_PREREQ(3 ,0 )
#include <openssl/decoder.h>
#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 .
*
* The FFDHE group is now 2048 - bit , as 1024 bits is increasingly considered to
* weak by clients . When greater security is required , use EECDH .
*/
/*-
* Generated via :
* $ openssl dhparam - 2 - outform DER 2048 2 > / dev / null |
* hexdump - ve ' / 1 " 0 x % 02 x , " ' | 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 builtin_der[] = {
0 x30, 0 x82, 0 x01, 0 x08, 0 x02, 0 x82, 0 x01, 0 x01, 0 x00, 0 xec, 0 x02, 0 x7b,
0 x74, 0 xc6, 0 xd4, 0 xb4, 0 x89, 0 x68, 0 xfd, 0 xbc, 0 xe0, 0 x82, 0 xae, 0 xd6,
0 xf1, 0 x4d, 0 x93, 0 xaa, 0 x47, 0 x07, 0 x84, 0 x3d, 0 x86, 0 xf8, 0 x47, 0 xf7,
0 xdf, 0 x08, 0 x7b, 0 xca, 0 x04, 0 xa4, 0 x72, 0 xec, 0 x11, 0 xe2, 0 x38, 0 x43,
0 xb7, 0 x94, 0 xab, 0 xaf, 0 xe2, 0 x85, 0 x59, 0 x43, 0 x4e, 0 x71, 0 x85, 0 xfe,
0 x52, 0 x0c, 0 xe0, 0 x1c, 0 xb6, 0 xc7, 0 xb0, 0 x1b, 0 x06, 0 xb3, 0 x4d, 0 x1b,
0 x4f, 0 xf6, 0 x4b, 0 x45, 0 xbd, 0 x1d, 0 xb8, 0 xe4, 0 xa4, 0 x48, 0 x09, 0 x28,
0 x19, 0 xd7, 0 xce, 0 xb1, 0 xe5, 0 x9a, 0 xc4, 0 x94, 0 x55, 0 xde, 0 x4d, 0 x86,
0 x0f, 0 x4c, 0 x5e, 0 x25, 0 x51, 0 x6c, 0 x96, 0 xca, 0 xfa, 0 xe3, 0 x01, 0 x69,
0 x82, 0 x6c, 0 x8f, 0 xf5, 0 xe7, 0 x0e, 0 xb7, 0 x8e, 0 x52, 0 xf1, 0 xcf, 0 x0b,
0 x67, 0 x10, 0 xd0, 0 xb3, 0 x77, 0 x79, 0 xa4, 0 xc1, 0 xd0, 0 x0f, 0 x3f, 0 xf5,
0 x5c, 0 x35, 0 xf9, 0 x46, 0 xd2, 0 xc7, 0 xfb, 0 x97, 0 x6d, 0 xd5, 0 xbe, 0 xe4,
0 x8b, 0 x5a, 0 xf2, 0 x88, 0 xfa, 0 x47, 0 xdc, 0 xc2, 0 x4a, 0 x4d, 0 x69, 0 xd3,
0 x2a, 0 xdf, 0 x55, 0 x6c, 0 x5f, 0 x71, 0 x11, 0 x1e, 0 x87, 0 x03, 0 x68, 0 xe1,
0 xf4, 0 x21, 0 x06, 0 x63, 0 xd9, 0 x65, 0 xd4, 0 x0c, 0 x4d, 0 xa7, 0 x1f, 0 x15,
0 x53, 0 x3a, 0 x50, 0 x1a, 0 xf5, 0 x9b, 0 x50, 0 x35, 0 xe0, 0 x16, 0 xa1, 0 xd7,
0 xe6, 0 xbf, 0 xd7, 0 xd9, 0 xd9, 0 x53, 0 xe5, 0 x8b, 0 xf8, 0 x7b, 0 x45, 0 x46,
0 xb6, 0 xac, 0 x50, 0 x16, 0 x46, 0 x42, 0 xca, 0 x76, 0 x38, 0 x4b, 0 x8e, 0 x83,
0 xc6, 0 x73, 0 x13, 0 x9c, 0 x03, 0 xd1, 0 x7a, 0 x3d, 0 x8d, 0 x99, 0 x34, 0 x10,
0 x79, 0 x67, 0 x21, 0 x23, 0 xf9, 0 x6f, 0 x48, 0 x9a, 0 xa6, 0 xde, 0 xbf, 0 x7f,
0 x9c, 0 x16, 0 x53, 0 xff, 0 xf7, 0 x20, 0 x96, 0 xeb, 0 x34, 0 xcb, 0 x5b, 0 x85,
0 x2b, 0 x7c, 0 x98, 0 x00, 0 x23, 0 x47, 0 xce, 0 xc2, 0 x58, 0 x12, 0 x86, 0 x2c,
0 x57, 0 x02, 0 x01, 0 x02,
};
#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 *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 (dhp) {
EVP_PKEY_free(dhp);
dhp = 0 ;
}
if (strcmp(path, "auto" ) == 0 )
return ;
if ((fp = fopen(path, "r" )) == 0 ) {
msg_warn("error opening DH parameter 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 decoding DH parameters from file \" %s\""
" -- using compiled-in defaults" , path);
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, int useauto)
{
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();
}
}
#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 */
#define AG_STAT_OK (0 )
#define AG_STAT_NO_GROUP (-1 ) /* no usable group, may retry */
#define AG_STAT_NO_RETRY (-2 ) /* other error, don't retry */
static int setup_auto_groups(SSL_CTX *ctx, const char *origin,
const char *eecdh,
const char *ffdhe)
{
#ifndef OPENSSL_NO_ECDH
SSL_CTX *tmpctx;
BH_TABLE *seen;
char *save;
char *groups;
char *group;
static VSTRING *names;
if ((tmpctx = SSL_CTX_new(TLS_method())) == 0 ) {
msg_warn("cannot allocate temp SSL_CTX" );
tls_print_errors();
return (AG_STAT_NO_RETRY);
}
if (!names)
names = vstring_alloc(sizeof DEF_TLS_EECDH_AUTO +
sizeof DEF_TLS_FFDHE_AUTO);
VSTRING_RESET(names);
/*
* OpenSSL does not tolerate duplicate groups in the requested list .
* Deduplicate case - insensitively , just in case OpenSSL some day supports
* case - insensitive group lookup . Deduplicate only verified extant
* groups we ' re going to ask OpenSSL to use .
*
* OpenSSL 3 . 3 supports " ? < name > " as a syntax for optionally ignoring
* unsupported groups , so we could skip checking against the throw - away
* CTX when linked against 3 . 3 or higher , but the cost savings don ' t
* justify the # ifdef overhead for now .
*/
seen = been_here_init(0 , BH_FLAG_FOLD);
#define GROUPS_SEP CHARS_COMMA_SP ":"
#define SETUP_AG_RETURN(val) do { \
been_here_free(seen); \
myfree(save); \
SSL_CTX_free(tmpctx); \
return (val); \
} while (0 )
groups = save = concatenate(eecdh, " " , ffdhe, NULL);
if ((group = mystrtok(&groups, GROUPS_SEP)) == 0 ) {
msg_warn("no %s key exchange group - OpenSSL requires at least one" ,
origin);
SETUP_AG_RETURN(AG_STAT_NO_GROUP);
}
for (; group != 0 ; group = mystrtok(&groups, GROUPS_SEP)) {
/*
* Validate the group name by trying it as the group for a throw - away
* SSL context . This way , we can ask for new groups that may not yet
* be supported by the underlying OpenSSL runtime . Unsupported
* groups are silently ignored .
*/
ERR_set_mark();
if (SSL_CTX_set1_curves_list(tmpctx, group) > 0 &&
!been_here_fixed(seen, group)) {
if (VSTRING_LEN(names) > 0 )
VSTRING_ADDCH(names, ':' );
vstring_strcat(names, group);
}
ERR_pop_to_mark();
}
if (VSTRING_LEN(names) == 0 ) {
/* The names may be case-sensitive */
msg_warn("none of the %s key exchange groups are supported" , origin);
SETUP_AG_RETURN(AG_STAT_NO_GROUP);
}
VSTRING_TERMINATE(names);
if (SSL_CTX_set1_curves_list(ctx, vstring_str(names)) <= 0 ) {
msg_warn("failed to set up the %s key exchange groups" , origin);
tls_print_errors();
SETUP_AG_RETURN(AG_STAT_NO_RETRY);
}
SETUP_AG_RETURN(AG_STAT_OK);
#endif
}
void tls_auto_groups(SSL_CTX *ctx, const char *eecdh, const char *ffdhe)
{
char *def_eecdh = DEF_TLS_EECDH_AUTO;
#if OPENSSL_VERSION_PREREQ(3 , 0 )
char *def_ffdhe = DEF_TLS_FFDHE_AUTO;
#else
char *def_ffdhe = "" ;
/* Has no effect prior to OpenSSL 3.0 */
ffdhe = def_ffdhe;
#endif
const char *origin;
/* Use OpenSSL defaults */
if (!*eecdh && !*ffdhe)
return ;
/*
* Try the user - specified list first . If that fails ( empty list or no
* known group name ) , try again with the Postfix defaults . We assume that
* group selection is mere performance tuning and not security critical .
* All the groups supported for negotiation should be strong enough .
*/
for (origin = "configured" ; /* void */ ; /* void */ ) {
switch (setup_auto_groups(ctx, origin, eecdh, ffdhe)) {
case AG_STAT_OK:
return ;
case AG_STAT_NO_GROUP:
if (strcmp(eecdh, def_eecdh) != 0
|| strcmp(ffdhe, def_ffdhe) != 0 ) {
msg_warn("using Postfix default key exchange groups instead" );
origin = "Postfix default" ;
eecdh = def_eecdh;
ffdhe = def_ffdhe;
break ;
}
/* FALLTHROUGH */
default :
msg_warn("using OpenSSL default key exchange groups instead" );
return ;
}
}
}
#ifdef TEST
int main(int unused_argc, char **unused_argv)
{
tls_tmp_dh(0 , 0 );
return (dhp == 0 );
}
#endif
#endif
Messung V0.5 in Prozent C=81 H=93 G=86
¤ Dauer der Verarbeitung: 0.24 Sekunden
(vorverarbeitet am 2026-08-09)
¤
*© Formatika GbR, Deutschland