From: Wietse Z Venema Date: Thu, 16 Apr 2026 05:00:00 +0000 (-0500) Subject: postfix-3.12-20260416 X-Git-Url: http://git.ipfire.org/index.cgi?a=commitdiff_plain;h=HEAD;p=thirdparty%2Fpostfix.git postfix-3.12-20260416 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index 9bf3151a1..8fa7e5c05 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -30949,6 +30949,21 @@ Apologies for any names omitted. out-of-memory error, instead of letting the PCRE2 library silently skip a rule. File: util/dict_pcre.c. +20260416 + + Code health: verified zero functionality change. Factored + out the tlsproxy support for the server role. Next up is + to make server role support consistent with client role + support. Files: tlsproxy/tlsproxy.c, tlsproxy/tlsproxy_server.[hc]. + + Cleanup: defensive comment for code scanner, fixed an + almost-correct overflow defense. File: util/argv.c. + + Added #ifdef guards to un-break non-TLS builds. Files: + tlsproxy/tlsproxy_diff.c, tlsproxy/tlsproxy.h. + + Added missing text to manpages. Files: tls/tls_proxy_*_proto.c. + TODO Reorganize PTEST_LIB, PMOCK_LIB, TESTLIB, TESTLIBS, etc. diff --git a/postfix/proto/stop.double-history b/postfix/proto/stop.double-history index 13a35af1d..ddccc17fb 100644 --- a/postfix/proto/stop.double-history +++ b/postfix/proto/stop.double-history @@ -238,3 +238,4 @@ proto proto stop proto stop double cc Files tlsproxy tlsproxy c tlsproxy tlsproxy h Files tlsproxy tlsproxy c tlsproxy tlsproxy h for consistency Files tlsproxy tlsproxy hc + tlsproxy tlsproxy_diff c tlsproxy tlsproxy h diff --git a/postfix/proto/stop.spell-cc b/postfix/proto/stop.spell-cc index 627642a49..77f4e7417 100644 --- a/postfix/proto/stop.spell-cc +++ b/postfix/proto/stop.spell-cc @@ -1980,3 +1980,5 @@ PQerrorMessage deref openUTS xff +nameN +valueN diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 499f20c26..3bff4b416 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 "20260415" +#define MAIL_RELEASE_DATE "20260416" #define MAIL_VERSION_NUMBER "3.12" #ifdef SNAPSHOT diff --git a/postfix/src/tls/tls_proxy_client_init_proto.c b/postfix/src/tls/tls_proxy_client_init_proto.c index 18b293ae4..eecdbdda3 100644 --- a/postfix/src/tls/tls_proxy_client_init_proto.c +++ b/postfix/src/tls/tls_proxy_client_init_proto.c @@ -6,6 +6,8 @@ /* SYNOPSIS /* #include /* +/* TLS_PROXY_CLIENT_INIT_PROPS(props, name1 = value1, ..., nameN = valueN) +/* /* char *tls_proxy_client_init_serialize(print_fn, buf, init_props) /* ATTR_PRINT_COMMON_FN print_fn; /* VSTRING *buf; @@ -30,6 +32,10 @@ /* void tls_proxy_client_init_free(init_props) /* TLS_CLIENT_INIT_PROPS *init_props; /* DESCRIPTION +/* TLS_PROXY_CLIENT_INIT_PROPS() makes shallow copies of the form +/* (props)->name1 = value1, ..., (props)->nameN = valueN. The result +/* should not be passed to tls_proxy_client_init_free(). +/* /* tls_proxy_client_init_serialize() serializes the specified /* object to a memory buffer, using the specified print function /* (typically, attr_print_plain). The result can be used diff --git a/postfix/src/tls/tls_proxy_client_param_proto.c b/postfix/src/tls/tls_proxy_client_param_proto.c index b9bdb863e..d0720519c 100644 --- a/postfix/src/tls/tls_proxy_client_param_proto.c +++ b/postfix/src/tls/tls_proxy_client_param_proto.c @@ -6,6 +6,8 @@ /* SYNOPSIS /* #include /* +/* TLS_PROXY_CLIENT_PARAMS(params, name1 = value1, ..., nameN = valueN) +/* /* TLS_CLIENT_PARAMS *tls_proxy_client_param_from_config(params) /* TLS_CLIENT_PARAMS *params; /* @@ -38,6 +40,11 @@ /* argument. Strings are not copied. The result must therefore /* not be passed to tls_proxy_client_param_free(). /* +/* TLS_PROXY_CLIENT_PARAMS() makes shallow copies of the form +/* (params)->name1 = value1, ..., (params)->nameN = valueN. As with +/* tls_proxy_client_param_from_config(), the result should not be +/* passed to tls_proxy_client_param_free(). +/* /* tls_proxy_client_param_serialize() serializes the specified /* object to a memory buffer, using the specified print function /* (typically, attr_print_plain). The result can be used diff --git a/postfix/src/tls/tls_proxy_client_start_proto.c b/postfix/src/tls/tls_proxy_client_start_proto.c index 5e1bc913b..aef615aca 100644 --- a/postfix/src/tls/tls_proxy_client_start_proto.c +++ b/postfix/src/tls/tls_proxy_client_start_proto.c @@ -6,6 +6,8 @@ /* SYNOPSIS /* #include /* +/* TLS_PROXY_CLIENT_START_PROPS(props, name1 = value1, ..., nameN = valueN) +/* /* int tls_proxy_client_start_print(print_fn, stream, flags, ptr) /* ATTR_PRINT_COMMON_FN print_fn; /* VSTREAM *stream; @@ -21,6 +23,10 @@ /* void tls_proxy_client_start_free(start_props) /* TLS_CLIENT_START_PROPS *start_props; /* DESCRIPTION +/* TLS_PROXY_CLIENT_START_PROPS() makes shallow copies of the form +/* (props)->name1 = value1, ..., (props)->nameN = valueN. The result +/* should not be passed to tls_proxy_client_start_free(). +/* /* tls_proxy_client_start_print() writes a TLS_CLIENT_START_PROPS /* structure, without stream or file descriptor members, to /* the named stream using the specified attribute print routine. diff --git a/postfix/src/tls/tls_proxy_server_init_proto.c b/postfix/src/tls/tls_proxy_server_init_proto.c index 65860dfc2..f4cb7caa2 100644 --- a/postfix/src/tls/tls_proxy_server_init_proto.c +++ b/postfix/src/tls/tls_proxy_server_init_proto.c @@ -6,6 +6,8 @@ /* SYNOPSIS /* #include /* +/* TLS_PROXY_SERVER_INIT_PROPS(props, name1 = value1, ..., nameN = valueN) +/* /* char *tls_proxy_server_init_serialize(print_fn, buf, init_props) /* ATTR_PRINT_COMMON_FN print_fn; /* VSTRING *buf; @@ -30,6 +32,10 @@ /* tls_proxy_server_init_free(init_props) /* TLS_SERVER_INIT_PROPS *init_props; /* DESCRIPTION +/* TLS_PROXY_SERVER_INIT_PROPS() makes shallow copies of the form +/* (props)->name1 = value1, ..., (props)->nameN = valueN. The result +/* should not be passed to tls_proxy_server_init_free(). +/* /* tls_proxy_server_init_serialize() serializes the specified object /* to a memory buffer, using the specified print function (typically, /* attr_print_plain). The result can be used determine whether diff --git a/postfix/src/tls/tls_proxy_server_param_proto.c b/postfix/src/tls/tls_proxy_server_param_proto.c index 7675f2642..f7d059166 100644 --- a/postfix/src/tls/tls_proxy_server_param_proto.c +++ b/postfix/src/tls/tls_proxy_server_param_proto.c @@ -9,6 +9,8 @@ /* TLS_SERVER_PARAMS *tls_proxy_server_param_from_config(params) /* TLS_SERVER_PARAMS *params; /* +/* TLS_PROXY_SERVER_PARAMS(params, name1 = value1, ..., nameN = valueN) +/* /* char *tls_proxy_server_param_serialize(print_fn, buf, params) /* ATTR_PRINT_COMMON_FN print_fn; /* VSTRING *buf; @@ -38,6 +40,11 @@ /* returns its argument. Strings are not copied. The result must /* therefore not be passed to tls_proxy_server_param_free(). /* +/* TLS_PROXY_SERVER_PARAMS() makes shallow copies of the form +/* (params)->name1 = value1, ..., (params)->nameN = valueN. As with +/* tls_proxy_server_param_from_config(), the result should not be +/* passed to tls_proxy_server_param_free(). +/* /* tls_proxy_server_param_serialize() serializes the specified object /* to a memory buffer, using the specified print function (typically, /* attr_print_plain). The result can be used determine whether diff --git a/postfix/src/tls/tls_proxy_server_start_proto.c b/postfix/src/tls/tls_proxy_server_start_proto.c index d480ec235..b64dab3ee 100644 --- a/postfix/src/tls/tls_proxy_server_start_proto.c +++ b/postfix/src/tls/tls_proxy_server_start_proto.c @@ -6,6 +6,8 @@ /* SYNOPSIS /* #include /* +/* TLS_PROXY_SERVER_START_PROPS(props, name1 = value1, ..., nameN = valueN) +/* /* int tls_proxy_server_start_print(print_fn, stream, flags, ptr) /* ATTR_PRINT_COMMON_FN print_fn; /* VSTREAM *stream; @@ -21,12 +23,31 @@ /* void tls_proxy_server_start_free(start_props) /* TLS_SERVER_START_PROPS *start_props; /* DESCRIPTION +/* TLS_PROXY_SERVER_START_PROPS() makes shallow copies of the form +/* (props)->name1 = value1, ..., (props)->nameN = valueN. The result +/* should not be passed to tls_proxy_server_start_free(). +/* /* tls_proxy_server_start_print() writes a TLS_SERVER_START_PROPS /* structure to the named stream using the specified attribute print /* routine. tls_proxy_server_start_print() is meant to be passed as /* a call-back to attr_print(), thusly: /* /* ... SEND_ATTR_FUNC(tls_proxy_server_start_print, (const void *) start_props), ... +/* +/* tls_proxy_server_start_scan() reads a TLS_SERVER_START_PROPS +/* structure from the named stream using the specified attribute +/* scan routine. tls_proxy_server_start_scan() is meant to be passed +/* as a call-back function to attr_scan(), as shown below. +/* +/* tls_proxy_server_start_free() destroys a TLS_SERVER_START_PROPS +/* structure that was created by tls_proxy_server_start_scan(). +/* +/* TLS_SERVER_START_PROPS *start_props = 0; +/* ... +/* ... RECV_ATTR_FUNC(tls_proxy_server_start_scan, (void *) &start_props) +/* ... +/* if (start_props) +/* tls_proxy_server_start_free(start_props); /* DIAGNOSTICS /* Fatal: out of memory. /* LICENSE diff --git a/postfix/src/tlsproxy/Makefile.in b/postfix/src/tlsproxy/Makefile.in index 69805eb9f..b2a401947 100644 --- a/postfix/src/tlsproxy/Makefile.in +++ b/postfix/src/tlsproxy/Makefile.in @@ -1,7 +1,9 @@ SHELL = /bin/sh -SRCS = tlsproxy.c tlsproxy_state.c tlsproxy_client.c tlsproxy_diff.c -OBJS = tlsproxy.o tlsproxy_state.o tlsproxy_client.o tlsproxy_diff.o -HDRS = tlsproxy.h tlsproxy_client.h tlsproxy_diff.h +SRCS = tlsproxy.c tlsproxy_state.c tlsproxy_client.c tlsproxy_diff.c \ + tlsproxy_server.c +OBJS = tlsproxy.o tlsproxy_state.o tlsproxy_client.o tlsproxy_diff.o \ + tlsproxy_server.o +HDRS = tlsproxy.h tlsproxy_client.h tlsproxy_diff.h tlsproxy_server.h TESTSRC = DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) @@ -89,6 +91,7 @@ tlsproxy.o: ../../include/vstring.h tlsproxy.o: tlsproxy.c tlsproxy.o: tlsproxy.h tlsproxy.o: tlsproxy_client.h +tlsproxy.o: tlsproxy_server.h tlsproxy_client.o: ../../include/argv.h tlsproxy_client.o: ../../include/attr.h tlsproxy_client.o: ../../include/been_here.h @@ -155,6 +158,37 @@ tlsproxy_diff.o: ../../include/vstring.h tlsproxy_diff.o: tlsproxy.h tlsproxy_diff.o: tlsproxy_diff.c tlsproxy_diff.o: tlsproxy_diff.h +tlsproxy_server.o: ../../include/argv.h +tlsproxy_server.o: ../../include/attr.h +tlsproxy_server.o: ../../include/check_arg.h +tlsproxy_server.o: ../../include/dns.h +tlsproxy_server.o: ../../include/events.h +tlsproxy_server.o: ../../include/htable.h +tlsproxy_server.o: ../../include/mail_params.h +tlsproxy_server.o: ../../include/msg.h +tlsproxy_server.o: ../../include/myaddrinfo.h +tlsproxy_server.o: ../../include/mymalloc.h +tlsproxy_server.o: ../../include/name_code.h +tlsproxy_server.o: ../../include/name_mask.h +tlsproxy_server.o: ../../include/nbbio.h +tlsproxy_server.o: ../../include/nvtable.h +tlsproxy_server.o: ../../include/sock_addr.h +tlsproxy_server.o: ../../include/sys_defs.h +tlsproxy_server.o: ../../include/tls.h +tlsproxy_server.o: ../../include/tls_proxy.h +tlsproxy_server.o: ../../include/tls_proxy_attr.h +tlsproxy_server.o: ../../include/tls_proxy_client_init_proto.h +tlsproxy_server.o: ../../include/tls_proxy_client_param_proto.h +tlsproxy_server.o: ../../include/tls_proxy_client_start_proto.h +tlsproxy_server.o: ../../include/tls_proxy_server_init_proto.h +tlsproxy_server.o: ../../include/tls_proxy_server_param_proto.h +tlsproxy_server.o: ../../include/tls_proxy_server_start_proto.h +tlsproxy_server.o: ../../include/vbuf.h +tlsproxy_server.o: ../../include/vstream.h +tlsproxy_server.o: ../../include/vstring.h +tlsproxy_server.o: tlsproxy.h +tlsproxy_server.o: tlsproxy_server.c +tlsproxy_server.o: tlsproxy_server.h tlsproxy_state.o: ../../include/argv.h tlsproxy_state.o: ../../include/attr.h tlsproxy_state.o: ../../include/check_arg.h diff --git a/postfix/src/tlsproxy/tlsproxy.c b/postfix/src/tlsproxy/tlsproxy.c index e17b897b9..32951e471 100644 --- a/postfix/src/tlsproxy/tlsproxy.c +++ b/postfix/src/tlsproxy/tlsproxy.c @@ -436,6 +436,7 @@ */ #include #include +#include /* * Tunable parameters. We define our clones of the smtpd(8) parameters to @@ -542,13 +543,6 @@ bool var_tlsp_clnt_enforce_tls; char *var_tlsp_clnt_per_site; char *var_tlsp_clnt_policy; - /* - * TLS per-process status. - */ -static TLS_APPL_STATE *tlsp_server_ctx; -static int ask_client_cert; -static const char *server_role_disabled; - /* * The code that implements the TLS engine looks simpler than expected. That * is the result of a great deal of effort, mainly in design and analysis. @@ -1021,75 +1015,6 @@ static void tlsp_ciphertext_event(int event, void *context) } } -/* tlsp_server_start_pre_handshake - turn on TLS or force disconnect */ - -static int tlsp_server_start_pre_handshake(TLSP_STATE *state) -{ - TLS_SERVER_START_PROPS props; - static char *cipher_grade; - static VSTRING *cipher_exclusions; - - /* - * The code in this routine is pasted literally from smtpd(8). I am not - * going to sanitize this because doing so surely will break things in - * unexpected ways. - */ - - /* - * Perform the before-handshake portion of per-session initialization. - * Pass a null VSTREAM to indicate that this program will do the - * ciphertext I/O, not libtls. - * - * The cipher grade and exclusions don't change between sessions. Compute - * just once and cache. - */ -#define ADD_EXCLUDE(vstr, str) \ - do { \ - if (*(str)) \ - vstring_sprintf_append((vstr), "%s%s", \ - VSTRING_LEN(vstr) ? " " : "", (str)); \ - } while (0) - - if (cipher_grade == 0) { - cipher_grade = - var_tlsp_enforce_tls ? var_tlsp_tls_mand_ciph : var_tlsp_tls_ciph; - cipher_exclusions = vstring_alloc(10); - ADD_EXCLUDE(cipher_exclusions, var_tlsp_tls_excl_ciph); - if (var_tlsp_enforce_tls) - ADD_EXCLUDE(cipher_exclusions, var_tlsp_tls_mand_excl); - if (ask_client_cert) - ADD_EXCLUDE(cipher_exclusions, "aNULL"); - } - state->tls_context = - TLS_SERVER_START(&props, - ctx = tlsp_server_ctx, - stream = (VSTREAM *) 0,/* unused */ - fd = state->ciphertext_fd, - timeout = 0, /* unused */ - requirecert = (var_tlsp_tls_req_ccert - && var_tlsp_enforce_tls), - enable_rpk = var_tlsp_tls_enable_rpk, - serverid = state->server_id, - namaddr = state->remote_endpt, - cipher_grade = cipher_grade, - cipher_exclusions = STR(cipher_exclusions), - mdalg = var_tlsp_tls_fpt_dgst); - - if (state->tls_context == 0) { - tlsp_state_free(state); - return (TLSP_STAT_ERR); - } - - /* - * XXX Do we care about TLS session rate limits? Good postscreen(8) - * clients will occasionally require the tlsproxy to renew their - * allowlist status, but bad clients hammering the server can suck up - * lots of CPU cycles. Per-client concurrency limits in postscreen(8) - * will divert only naive security "researchers". - */ - return (TLSP_STAT_OK); -} - /* * From here on down is low-level code that sets up the plumbing before * passing control to the TLS engine above. @@ -1348,139 +1273,6 @@ static void tlsp_service(VSTREAM *plaintext_stream, TLSP_INIT_TIMEOUT, (void *) state); } -/* pre_jail_init_server - pre-jail initialization */ - -static void pre_jail_init_server(void) -{ - TLS_SERVER_INIT_PROPS props; - const char *cert_file; - int have_server_cert; - int no_server_cert_ok; - int require_server_cert; - - /* - * The code in this routine is pasted literally from smtpd(8). I am not - * going to sanitize this because doing so surely will break things in - * unexpected ways. - */ - if (*var_tlsp_tls_level) { - switch (tls_level_lookup(var_tlsp_tls_level)) { - default: - msg_fatal("Invalid TLS level \"%s\"", var_tlsp_tls_level); - /* NOTREACHED */ - break; - case TLS_LEV_SECURE: - case TLS_LEV_VERIFY: - case TLS_LEV_FPRINT: - msg_warn("%s: unsupported TLS level \"%s\", using \"encrypt\"", - VAR_TLSP_TLS_LEVEL, var_tlsp_tls_level); - /* FALLTHROUGH */ - case TLS_LEV_ENCRYPT: - var_tlsp_enforce_tls = var_tlsp_use_tls = 1; - break; - case TLS_LEV_MAY: - var_tlsp_enforce_tls = 0; - var_tlsp_use_tls = 1; - break; - case TLS_LEV_NONE: - var_tlsp_enforce_tls = var_tlsp_use_tls = 0; - break; - } - } - var_tlsp_use_tls = var_tlsp_use_tls || var_tlsp_enforce_tls; - if (!var_tlsp_use_tls) { - server_role_disabled = "TLS server role is disabled by configuration"; - return; - } - - /* - * Load TLS keys before dropping privileges. - * - * Can't use anonymous ciphers if we want client certificates. Must use - * anonymous ciphers if we have no certificates. - */ - ask_client_cert = require_server_cert = - (var_tlsp_tls_ask_ccert - || (var_tlsp_enforce_tls && var_tlsp_tls_req_ccert)); - if (strcasecmp(var_tlsp_tls_cert_file, "none") == 0) { - no_server_cert_ok = 1; - cert_file = ""; - } else { - no_server_cert_ok = 0; - cert_file = var_tlsp_tls_cert_file; - } - have_server_cert = - (*cert_file || *var_tlsp_tls_dcert_file || *var_tlsp_tls_eccert_file); - - if (*var_tlsp_tls_chain_files != 0) { - if (!have_server_cert) - have_server_cert = 1; - else - msg_warn("Both %s and one or more of the legacy " - " %s, %s or %s are non-empty; the legacy " - " parameters will be ignored", - VAR_TLSP_TLS_CHAIN_FILES, - VAR_TLSP_TLS_CERT_FILE, - VAR_TLSP_TLS_ECCERT_FILE, - VAR_TLSP_TLS_DCERT_FILE); - } - /* Some TLS configuration errors are not show stoppers. */ - if (!have_server_cert && require_server_cert) - msg_warn("Need a server cert to request client certs"); - if (!var_tlsp_enforce_tls && var_tlsp_tls_req_ccert) - msg_warn("Can't require client certs unless TLS is required"); - /* After a show-stopper error, log a warning. */ - if (have_server_cert || (no_server_cert_ok && !require_server_cert)) { - - tls_pre_jail_init(TLS_ROLE_SERVER); - - /* - * Large parameter lists are error-prone, so we emulate a language - * feature that C does not have natively: named parameter lists. - */ - tlsp_server_ctx = - TLS_SERVER_INIT(&props, - log_param = VAR_TLSP_TLS_LOGLEVEL, - log_level = var_tlsp_tls_loglevel, - verifydepth = var_tlsp_tls_ccert_vd, - cache_type = TLS_MGR_SCACHE_SMTPD, - set_sessid = var_tlsp_tls_set_sessid, - chain_files = var_tlsp_tls_chain_files, - cert_file = cert_file, - key_file = var_tlsp_tls_key_file, - dcert_file = var_tlsp_tls_dcert_file, - dkey_file = var_tlsp_tls_dkey_file, - eccert_file = var_tlsp_tls_eccert_file, - eckey_file = var_tlsp_tls_eckey_file, - CAfile = var_tlsp_tls_CAfile, - CApath = var_tlsp_tls_CApath, - dh1024_param_file - = var_tlsp_tls_dh1024_param_file, - dh512_param_file - = var_tlsp_tls_dh512_param_file, - eecdh_grade = var_tlsp_tls_eecdh, - protocols = var_tlsp_enforce_tls ? - var_tlsp_tls_mand_proto : - var_tlsp_tls_proto, - ask_ccert = ask_client_cert, - mdalg = var_tlsp_tls_fpt_dgst); - } else { - msg_warn("No server certs available. TLS can't be enabled"); - } - - /* - * To maintain sanity, allow partial SSL_write() operations, and allow - * SSL_write() buffer pointers to change after a WANT_READ or WANT_WRITE - * result. This is based on OpenSSL developers talking on a mailing list, - * but is not supported by documentation. If this code stops working then - * no-one can be held responsible. - */ - if (tlsp_server_ctx) - SSL_CTX_set_mode(tlsp_server_ctx->ssl_ctx, - SSL_MODE_ENABLE_PARTIAL_WRITE - | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); -} - /* pre_jail_init - pre-jail initialization */ static void pre_jail_init(char *unused_name, char **unused_argv) diff --git a/postfix/src/tlsproxy/tlsproxy.h b/postfix/src/tlsproxy/tlsproxy.h index 6fbc294f4..a8a718cf3 100644 --- a/postfix/src/tlsproxy/tlsproxy.h +++ b/postfix/src/tlsproxy/tlsproxy.h @@ -20,6 +20,8 @@ /* * TLS library. */ +#ifdef USE_TLS + #include #include @@ -99,3 +101,5 @@ extern void tlsp_state_free(TLSP_STATE *); /*--*/ #endif + +#endif diff --git a/postfix/src/tlsproxy/tlsproxy_diff.c b/postfix/src/tlsproxy/tlsproxy_diff.c index a5fe64083..a9d057713 100644 --- a/postfix/src/tlsproxy/tlsproxy_diff.c +++ b/postfix/src/tlsproxy/tlsproxy_diff.c @@ -41,13 +41,18 @@ /* * System library. */ +#ifdef USE_TLS + #include +#include /* * Utility library. */ #include +#include #include +#include /* * Internal API. @@ -90,3 +95,5 @@ void tlsp_log_config_diff(const char *server_cfg, const char *client_cfg) myfree(saved_client); myfree(saved_server); } + +#endif diff --git a/postfix/src/tlsproxy/tlsproxy_server.c b/postfix/src/tlsproxy/tlsproxy_server.c new file mode 100644 index 000000000..c733bcbbb --- /dev/null +++ b/postfix/src/tlsproxy/tlsproxy_server.c @@ -0,0 +1,311 @@ +/*++ +/* NAME +/* tlsproxy_server 3 +/* SUMMARY +/* Postfix TLS proxy server role support +/* SYNOPSIS +/* #include +/* +/* void pre_jail_init_server(void) +/*Begin TODO +/* TLS_APPL_STATE *tlsp_server_init( +/* TLS_SERVER_PARAMS *tls_params, +/* TLS_SERVER_INIT_PROPS *init_props) +/*End TODO +/* int tlsp_server_start_pre_handshake(TLSP_STATE *state) +/* DESCRIPTION +/* This module implements TLS proxy server role support. The legacy +/* implementation uses the same tlsproxy(8) configuration for all +/* tls_server_init() and tls_server_start() calls. +/* +/* pre_jail_init_server() creates an SSL context based on tlsproxy(8) +/* server configuration. +/*Begin TODO +/* A future version will save a copy of serialized TLS_SERVER_PARAMS +/* and TLS_SERVER_INIT_PROPS based on tlsproxy(8) server +/* configuration. These will be used as a reference when receiving +/* a request for the server role. +/* +/* tlsp_server_init() processes a request for the TLS proxy server +/* role. If the request has not been seen before it checks the +/* request for relevant differences that would conflict with +/* tlsproxy(8) server configuration. The result is null when TLS +/* is not available. +/*End TODO +/* tlsp_server_start_pre_handshake() requests the tls_server_start() +/* handshake. It returns TLSP_STAT_OK when the request succeeds. +/* Otherwise, it returns TLSP_STAT_ERR and state becomes a dangling +/* pointer. +/* DIAGNOSTICS +/* Problems are logged to \fBsyslogd\fR(8) or \fBpostlogd\fR(8). +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* HISTORY +/* .ad +/* .fi +/* This service was introduced with Postfix version 2.8. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/* +/* Wietse Venema +/* porcupine.org +/*--*/ + + /* + * System library. + */ +#include +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + + /* + * Utility library. + */ +#include +#include + + /* + * Global library. + */ +#include + + /* + * TLS library. + */ +#ifdef USE_TLS +#define TLS_INTERNAL /* XXX */ +#include +#include + + /* + * Application-specific. + */ +#include +#include + + /* + * TLS per-process status. + * + * TODO(wietse) delete externally visible state after tlsp_server_init() is + * implemented. + */ +TLS_APPL_STATE *tlsp_server_ctx; +static int ask_client_cert; +const char *server_role_disabled; + +/* tlsp_server_start_pre_handshake - turn on TLS or force disconnect */ + +int tlsp_server_start_pre_handshake(TLSP_STATE *state) +{ + TLS_SERVER_START_PROPS props; + static char *cipher_grade; + static VSTRING *cipher_exclusions; + + /* + * The code in this routine is pasted literally from smtpd(8). I am not + * going to sanitize this because doing so surely will break things in + * unexpected ways. + */ + + /* + * Perform the before-handshake portion of per-session initialization. + * Pass a null VSTREAM to indicate that this program will do the + * ciphertext I/O, not libtls. + * + * The cipher grade and exclusions don't change between sessions. Compute + * just once and cache. + */ +#define ADD_EXCLUDE(vstr, str) \ + do { \ + if (*(str)) \ + vstring_sprintf_append((vstr), "%s%s", \ + VSTRING_LEN(vstr) ? " " : "", (str)); \ + } while (0) + + if (cipher_grade == 0) { + cipher_grade = + var_tlsp_enforce_tls ? var_tlsp_tls_mand_ciph : var_tlsp_tls_ciph; + cipher_exclusions = vstring_alloc(10); + ADD_EXCLUDE(cipher_exclusions, var_tlsp_tls_excl_ciph); + if (var_tlsp_enforce_tls) + ADD_EXCLUDE(cipher_exclusions, var_tlsp_tls_mand_excl); + if (ask_client_cert) + ADD_EXCLUDE(cipher_exclusions, "aNULL"); + } + state->tls_context = + TLS_SERVER_START(&props, + ctx = tlsp_server_ctx, + stream = (VSTREAM *) 0,/* unused */ + fd = state->ciphertext_fd, + timeout = 0, /* unused */ + requirecert = (var_tlsp_tls_req_ccert + && var_tlsp_enforce_tls), + enable_rpk = var_tlsp_tls_enable_rpk, + serverid = state->server_id, + namaddr = state->remote_endpt, + cipher_grade = cipher_grade, + cipher_exclusions = STR(cipher_exclusions), + mdalg = var_tlsp_tls_fpt_dgst); + + if (state->tls_context == 0) { + tlsp_state_free(state); + return (TLSP_STAT_ERR); + } + + /* + * XXX Do we care about TLS session rate limits? Good postscreen(8) + * clients will occasionally require the tlsproxy to renew their + * allowlist status, but bad clients hammering the server can suck up + * lots of CPU cycles. Per-client concurrency limits in postscreen(8) + * will divert only naive security "researchers". + */ + return (TLSP_STAT_OK); +} + +/* pre_jail_init_server - pre-jail initialization */ + +void pre_jail_init_server(void) +{ + TLS_SERVER_INIT_PROPS props; + const char *cert_file; + int have_server_cert; + int no_server_cert_ok; + int require_server_cert; + + /* + * The code in this routine is pasted literally from smtpd(8). I am not + * going to sanitize this because doing so surely will break things in + * unexpected ways. + */ + if (*var_tlsp_tls_level) { + switch (tls_level_lookup(var_tlsp_tls_level)) { + default: + msg_fatal("Invalid TLS level \"%s\"", var_tlsp_tls_level); + /* NOTREACHED */ + break; + case TLS_LEV_SECURE: + case TLS_LEV_VERIFY: + case TLS_LEV_FPRINT: + msg_warn("%s: unsupported TLS level \"%s\", using \"encrypt\"", + VAR_TLSP_TLS_LEVEL, var_tlsp_tls_level); + /* FALLTHROUGH */ + case TLS_LEV_ENCRYPT: + var_tlsp_enforce_tls = var_tlsp_use_tls = 1; + break; + case TLS_LEV_MAY: + var_tlsp_enforce_tls = 0; + var_tlsp_use_tls = 1; + break; + case TLS_LEV_NONE: + var_tlsp_enforce_tls = var_tlsp_use_tls = 0; + break; + } + } + var_tlsp_use_tls = var_tlsp_use_tls || var_tlsp_enforce_tls; + if (!var_tlsp_use_tls) { + server_role_disabled = "TLS server role is disabled by configuration"; + return; + } + + /* + * Load TLS keys before dropping privileges. + * + * Can't use anonymous ciphers if we want client certificates. Must use + * anonymous ciphers if we have no certificates. + */ + ask_client_cert = require_server_cert = + (var_tlsp_tls_ask_ccert + || (var_tlsp_enforce_tls && var_tlsp_tls_req_ccert)); + if (strcasecmp(var_tlsp_tls_cert_file, "none") == 0) { + no_server_cert_ok = 1; + cert_file = ""; + } else { + no_server_cert_ok = 0; + cert_file = var_tlsp_tls_cert_file; + } + have_server_cert = + (*cert_file || *var_tlsp_tls_dcert_file || *var_tlsp_tls_eccert_file); + + if (*var_tlsp_tls_chain_files != 0) { + if (!have_server_cert) + have_server_cert = 1; + else + msg_warn("Both %s and one or more of the legacy " + " %s, %s or %s are non-empty; the legacy " + " parameters will be ignored", + VAR_TLSP_TLS_CHAIN_FILES, + VAR_TLSP_TLS_CERT_FILE, + VAR_TLSP_TLS_ECCERT_FILE, + VAR_TLSP_TLS_DCERT_FILE); + } + /* Some TLS configuration errors are not show stoppers. */ + if (!have_server_cert && require_server_cert) + msg_warn("Need a server cert to request client certs"); + if (!var_tlsp_enforce_tls && var_tlsp_tls_req_ccert) + msg_warn("Can't require client certs unless TLS is required"); + /* After a show-stopper error, log a warning. */ + if (have_server_cert || (no_server_cert_ok && !require_server_cert)) { + + tls_pre_jail_init(TLS_ROLE_SERVER); + + /* + * Large parameter lists are error-prone, so we emulate a language + * feature that C does not have natively: named parameter lists. + */ + tlsp_server_ctx = + TLS_SERVER_INIT(&props, + log_param = VAR_TLSP_TLS_LOGLEVEL, + log_level = var_tlsp_tls_loglevel, + verifydepth = var_tlsp_tls_ccert_vd, + cache_type = TLS_MGR_SCACHE_SMTPD, + set_sessid = var_tlsp_tls_set_sessid, + chain_files = var_tlsp_tls_chain_files, + cert_file = cert_file, + key_file = var_tlsp_tls_key_file, + dcert_file = var_tlsp_tls_dcert_file, + dkey_file = var_tlsp_tls_dkey_file, + eccert_file = var_tlsp_tls_eccert_file, + eckey_file = var_tlsp_tls_eckey_file, + CAfile = var_tlsp_tls_CAfile, + CApath = var_tlsp_tls_CApath, + dh1024_param_file + = var_tlsp_tls_dh1024_param_file, + dh512_param_file + = var_tlsp_tls_dh512_param_file, + eecdh_grade = var_tlsp_tls_eecdh, + protocols = var_tlsp_enforce_tls ? + var_tlsp_tls_mand_proto : + var_tlsp_tls_proto, + ask_ccert = ask_client_cert, + mdalg = var_tlsp_tls_fpt_dgst); + } else { + msg_warn("No server certs available. TLS can't be enabled"); + } + + /* + * To maintain sanity, allow partial SSL_write() operations, and allow + * SSL_write() buffer pointers to change after a WANT_READ or WANT_WRITE + * result. This is based on OpenSSL developers talking on a mailing list, + * but is not supported by documentation. If this code stops working then + * no-one can be held responsible. + */ + if (tlsp_server_ctx) + SSL_CTX_set_mode(tlsp_server_ctx->ssl_ctx, + SSL_MODE_ENABLE_PARTIAL_WRITE + | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); +} + +#endif diff --git a/postfix/src/tlsproxy/tlsproxy_server.h b/postfix/src/tlsproxy/tlsproxy_server.h new file mode 100644 index 000000000..f4938d22f --- /dev/null +++ b/postfix/src/tlsproxy/tlsproxy_server.h @@ -0,0 +1,54 @@ +#ifndef _TLSPROXY_SERVER_H_ +#define _TLSPROXY_SERVER_H_ + +/*++ +/* NAME +/* tlsproxy_server 3h +/* SUMMARY +/* tlsproxy server role support +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * TLS library. + */ +#include +#include + + /* + * Internal API. + */ +#include + +extern void pre_jail_init_server(void); +extern TLS_APPL_STATE *tlsp_server_init(TLS_SERVER_PARAMS *, TLS_SERVER_INIT_PROPS *); +extern int tlsp_server_start_pre_handshake(TLSP_STATE *); + + /* + * TODO(wietse): delete these after tlsp_server_init() is implemented. + */ +extern TLS_APPL_STATE *tlsp_server_ctx; +extern const char *server_role_disabled; + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/* +/* Wietse Venema +/* porcupine.org +/*--*/ + +#endif diff --git a/postfix/src/util/argv.c b/postfix/src/util/argv.c index 56fc45013..8bc412f18 100644 --- a/postfix/src/util/argv.c +++ b/postfix/src/util/argv.c @@ -204,6 +204,9 @@ ARGV *argv_alloc(ssize_t len) argvp = (ARGV *) mymalloc(sizeof(*argvp)); argvp->len = 0; sane_len = (len < 2 ? 2 : len); + /* Peace, scanner. */ + if (sane_len > SSIZE_MAX - 1) + msg_panic("argv_alloc: array length overflow"); argvp->argv = (char **) mymalloc((sane_len + 1) * sizeof(char *)); argvp->len = sane_len; argvp->argc = 0; @@ -263,7 +266,7 @@ static void argv_extend(ARGV *argvp) ssize_t new_len; /* 202604 Claude: avoid overflowing (new_len + 1) * sizeof(char *). */ - if (argvp->len + 1 > SSIZE_MAX / (2 * sizeof(char *))) + if (argvp->len > SSIZE_MAX / (2 * sizeof(char *)) - 1) msg_panic("argv_extend: array length overflow"); new_len = argvp->len * 2; argvp->argv = (char **)