From: Wietse Z Venema Date: Wed, 15 Apr 2026 05:00:00 +0000 (-0500) Subject: postfix-3.12-20260415 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fcdd2230252465040b685d272d7df9590f6f3121;p=thirdparty%2Fpostfix.git postfix-3.12-20260415 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index a972260f3..9bf3151a1 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -30907,6 +30907,48 @@ Apologies for any names omitted. Testing: updated postscreen tests for changes in make_attr() API. File: src/postscreen/postscreen_dnsbl_test.c. +20260415 + + Code health: verified zero functionality change. Factored + out tlsproxy code for client role management, in preparation + for overhaul of the code for server role management (which + will become very similar). Next up will be renaming identifiers + for consistency. Files: tlsproxy/tlsproxy.[hc], + tlsproxy/tlsproxy_client.[hc], tlsproxy/tlsproxy_diff.[hc]. + + Code health: renamed some identifiers for consistency. + Files: tlsproxy/tlsproxy_client.[hc]. + + Claude scan: the memcached server rejects control characters + in a text-protocol key, but it is better if Postfix rejects + such keys, because that produces a better-quality error + message. File: global/dict_memcache.c. + + Claude scan: the MySQL client used an unsigned int for the + server port variable, but the find_inet_service() function + returns -1 after an error which is converted to unsigned, + resulting in no error message from Postfix, and a low-quality + error message from the MySQL client library. Changed the + port variable type to a signed int, so that Postfix will + log its better error message. File: global/dict_mysql.c. + + Claude scan: the PostgreSQL client strategy to recover from + an error in PQescapeStringConn() needed clarification. File: + global/dict_pgsql.c. + + Claude scan: remove %m (errno to string) from non-error logging. + File: tls/tls_prng_file.c. + + Claude scan: the recent argv.c fix should count the number + of bytes per (char *) pointer. File: util/argv.c. + + Claude scan: dict_walk() was broken (but never called). File: + util/dict.c. + + Claude scan: handle a pcre2_match_data_create_from_pattern() + out-of-memory error, instead of letting the PCRE2 library + silently skip a rule. File: util/dict_pcre.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 60aa8aff8..13a35af1d 100644 --- a/postfix/proto/stop.double-history +++ b/postfix/proto/stop.double-history @@ -237,3 +237,4 @@ proto proto stop proto stop double cc proxymap proxymap c smtp smtp_proto c smtpd smtpd_expand c Files tlsproxy tlsproxy c tlsproxy tlsproxy h Files tlsproxy tlsproxy c tlsproxy tlsproxy h + for consistency Files tlsproxy tlsproxy hc diff --git a/postfix/src/global/dict_memcache.c b/postfix/src/global/dict_memcache.c index 0b6dcc027..0351c0bf1 100644 --- a/postfix/src/global/dict_memcache.c +++ b/postfix/src/global/dict_memcache.c @@ -343,8 +343,9 @@ static int dict_memcache_valid_key(DICT_MC *dict_mc, if (rc < 0) DICT_ERR_VAL_RETURN(dict_mc, rc, 0); for (cp = (unsigned char *) STR(dict_mc->key_buf); *cp; cp++) - if (isascii(*cp) && isspace(*cp)) - DICT_MC_SKIP("name contains space"); + /* 202604 Claude: memcached rejects control characters in key. */ + if (ISSPACE(*cp) || ISCNTRL(*cp)) + DICT_MC_SKIP("name contains space or controls"); DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_NONE, 1); } diff --git a/postfix/src/global/dict_mysql.c b/postfix/src/global/dict_mysql.c index c0affafec..482e6180d 100644 --- a/postfix/src/global/dict_mysql.c +++ b/postfix/src/global/dict_mysql.c @@ -135,7 +135,8 @@ typedef struct { MYSQL *db; char *hostname; char *name; - unsigned port; + /* 202604 Claude: find_inet_service() returns -1 on error. */ + int port; unsigned type; /* TYPEUNIX | TYPEINET */ unsigned stat; /* STATUNTRIED | STATFAIL | STATCUR */ time_t ts; /* used for attempting reconnection diff --git a/postfix/src/global/dict_pgsql.c b/postfix/src/global/dict_pgsql.c index 6bf2622bc..65cd138af 100644 --- a/postfix/src/global/dict_pgsql.c +++ b/postfix/src/global/dict_pgsql.c @@ -191,11 +191,17 @@ static void dict_pgsql_quote(DICT *dict, const char *name, VSTRING *result) buflen = 2 * len + 1; /* - * XXX Workaround: stop further processing when PQescapeStringConn() - * (below) fails. A more proper fix requires invasive changes, not - * suitable for a stable release. + * 202604 Claude: error recovery strategy needs clarification. + * + * When the PQescapeStringConn() call below fails, we flag the host via + * active_host->stat = STATFAIL and return without writing to `result`. + * plpgsql_query() examines host->stat immediately after + * db_common_expand() returns, calls plpgsql_down_host(), and tries the + * next available server (i.e. not already flagged as STATFAIL). + * dict_pgsql_lookup() sets dict->error = DICT_ERR_RETRY if all servers + * fail. */ - if (active_host->stat == STATFAIL) + if (active_host->stat == STATFAIL) /* Can't happen */ return; /* diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 4653dea22..499f20c26 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 "20260413" +#define MAIL_RELEASE_DATE "20260415" #define MAIL_VERSION_NUMBER "3.12" #ifdef SNAPSHOT diff --git a/postfix/src/tls/tls_prng_file.c b/postfix/src/tls/tls_prng_file.c index 23865be39..c2a05b006 100644 --- a/postfix/src/tls/tls_prng_file.c +++ b/postfix/src/tls/tls_prng_file.c @@ -118,7 +118,7 @@ ssize_t tls_prng_file_read(TLS_PRNG_SRC *fh, size_t len) msg_info("cannot seek entropy file %s: %m", fh->name); return (-1); } - errno = 0; + /* 202604 Claude: no need to reset errno. */ for (to_read = len; to_read > 0; to_read -= count) { if ((count = timed_read(fh->fd, buffer, to_read > sizeof(buffer) ? sizeof(buffer) : to_read, @@ -132,7 +132,8 @@ ssize_t tls_prng_file_read(TLS_PRNG_SRC *fh, size_t len) RAND_seed(buffer, count); } if (msg_verbose) - msg_info("read %ld bytes from entropy file %s: %m", + /* 202604 Claude: remove '%m' from non-error logging. */ + msg_info("read %ld bytes from entropy file %s", (long) (len - to_read), fh->name); return (len - to_read); } diff --git a/postfix/src/tlsproxy/Makefile.in b/postfix/src/tlsproxy/Makefile.in index 2b6d34589..69805eb9f 100644 --- a/postfix/src/tlsproxy/Makefile.in +++ b/postfix/src/tlsproxy/Makefile.in @@ -1,7 +1,7 @@ SHELL = /bin/sh -SRCS = tlsproxy.c tlsproxy_state.c -OBJS = tlsproxy.o tlsproxy_state.o -HDRS = +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 TESTSRC = DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) @@ -75,12 +75,86 @@ tlsproxy.o: ../../include/split_at.h tlsproxy.o: ../../include/sys_defs.h tlsproxy.o: ../../include/tls.h tlsproxy.o: ../../include/tls_proxy.h +tlsproxy.o: ../../include/tls_proxy_attr.h +tlsproxy.o: ../../include/tls_proxy_client_init_proto.h +tlsproxy.o: ../../include/tls_proxy_client_param_proto.h +tlsproxy.o: ../../include/tls_proxy_client_start_proto.h +tlsproxy.o: ../../include/tls_proxy_server_init_proto.h +tlsproxy.o: ../../include/tls_proxy_server_param_proto.h +tlsproxy.o: ../../include/tls_proxy_server_start_proto.h tlsproxy.o: ../../include/tlsrpt_wrapper.h tlsproxy.o: ../../include/vbuf.h tlsproxy.o: ../../include/vstream.h tlsproxy.o: ../../include/vstring.h tlsproxy.o: tlsproxy.c tlsproxy.o: tlsproxy.h +tlsproxy.o: tlsproxy_client.h +tlsproxy_client.o: ../../include/argv.h +tlsproxy_client.o: ../../include/attr.h +tlsproxy_client.o: ../../include/been_here.h +tlsproxy_client.o: ../../include/check_arg.h +tlsproxy_client.o: ../../include/dns.h +tlsproxy_client.o: ../../include/events.h +tlsproxy_client.o: ../../include/htable.h +tlsproxy_client.o: ../../include/iostuff.h +tlsproxy_client.o: ../../include/mail_params.h +tlsproxy_client.o: ../../include/mail_proto.h +tlsproxy_client.o: ../../include/msg.h +tlsproxy_client.o: ../../include/myaddrinfo.h +tlsproxy_client.o: ../../include/mymalloc.h +tlsproxy_client.o: ../../include/name_code.h +tlsproxy_client.o: ../../include/name_mask.h +tlsproxy_client.o: ../../include/nbbio.h +tlsproxy_client.o: ../../include/nvtable.h +tlsproxy_client.o: ../../include/sock_addr.h +tlsproxy_client.o: ../../include/sys_defs.h +tlsproxy_client.o: ../../include/tls.h +tlsproxy_client.o: ../../include/tls_proxy.h +tlsproxy_client.o: ../../include/tls_proxy_attr.h +tlsproxy_client.o: ../../include/tls_proxy_client_init_proto.h +tlsproxy_client.o: ../../include/tls_proxy_client_param_proto.h +tlsproxy_client.o: ../../include/tls_proxy_client_start_proto.h +tlsproxy_client.o: ../../include/tls_proxy_server_init_proto.h +tlsproxy_client.o: ../../include/tls_proxy_server_param_proto.h +tlsproxy_client.o: ../../include/tls_proxy_server_start_proto.h +tlsproxy_client.o: ../../include/vbuf.h +tlsproxy_client.o: ../../include/vstream.h +tlsproxy_client.o: ../../include/vstring.h +tlsproxy_client.o: tlsproxy.h +tlsproxy_client.o: tlsproxy_client.c +tlsproxy_client.o: tlsproxy_client.h +tlsproxy_client.o: tlsproxy_diff.h +tlsproxy_diff.o: ../../include/argv.h +tlsproxy_diff.o: ../../include/attr.h +tlsproxy_diff.o: ../../include/check_arg.h +tlsproxy_diff.o: ../../include/dns.h +tlsproxy_diff.o: ../../include/events.h +tlsproxy_diff.o: ../../include/htable.h +tlsproxy_diff.o: ../../include/msg.h +tlsproxy_diff.o: ../../include/myaddrinfo.h +tlsproxy_diff.o: ../../include/mymalloc.h +tlsproxy_diff.o: ../../include/name_code.h +tlsproxy_diff.o: ../../include/name_mask.h +tlsproxy_diff.o: ../../include/nbbio.h +tlsproxy_diff.o: ../../include/nvtable.h +tlsproxy_diff.o: ../../include/sock_addr.h +tlsproxy_diff.o: ../../include/split_at.h +tlsproxy_diff.o: ../../include/sys_defs.h +tlsproxy_diff.o: ../../include/tls.h +tlsproxy_diff.o: ../../include/tls_proxy.h +tlsproxy_diff.o: ../../include/tls_proxy_attr.h +tlsproxy_diff.o: ../../include/tls_proxy_client_init_proto.h +tlsproxy_diff.o: ../../include/tls_proxy_client_param_proto.h +tlsproxy_diff.o: ../../include/tls_proxy_client_start_proto.h +tlsproxy_diff.o: ../../include/tls_proxy_server_init_proto.h +tlsproxy_diff.o: ../../include/tls_proxy_server_param_proto.h +tlsproxy_diff.o: ../../include/tls_proxy_server_start_proto.h +tlsproxy_diff.o: ../../include/vbuf.h +tlsproxy_diff.o: ../../include/vstream.h +tlsproxy_diff.o: ../../include/vstring.h +tlsproxy_diff.o: tlsproxy.h +tlsproxy_diff.o: tlsproxy_diff.c +tlsproxy_diff.o: tlsproxy_diff.h tlsproxy_state.o: ../../include/argv.h tlsproxy_state.o: ../../include/attr.h tlsproxy_state.o: ../../include/check_arg.h @@ -100,6 +174,13 @@ tlsproxy_state.o: ../../include/sock_addr.h tlsproxy_state.o: ../../include/sys_defs.h tlsproxy_state.o: ../../include/tls.h tlsproxy_state.o: ../../include/tls_proxy.h +tlsproxy_state.o: ../../include/tls_proxy_attr.h +tlsproxy_state.o: ../../include/tls_proxy_client_init_proto.h +tlsproxy_state.o: ../../include/tls_proxy_client_param_proto.h +tlsproxy_state.o: ../../include/tls_proxy_client_start_proto.h +tlsproxy_state.o: ../../include/tls_proxy_server_init_proto.h +tlsproxy_state.o: ../../include/tls_proxy_server_param_proto.h +tlsproxy_state.o: ../../include/tls_proxy_server_start_proto.h tlsproxy_state.o: ../../include/vbuf.h tlsproxy_state.o: ../../include/vstream.h tlsproxy_state.o: ../../include/vstring.h diff --git a/postfix/src/tlsproxy/tlsproxy.c b/postfix/src/tlsproxy/tlsproxy.c index d9c48001a..e17b897b9 100644 --- a/postfix/src/tlsproxy/tlsproxy.c +++ b/postfix/src/tlsproxy/tlsproxy.c @@ -435,6 +435,7 @@ * Application-specific. */ #include +#include /* * Tunable parameters. We define our clones of the smtpd(8) parameters to @@ -545,36 +546,9 @@ char *var_tlsp_clnt_policy; * TLS per-process status. */ static TLS_APPL_STATE *tlsp_server_ctx; -static bool tlsp_pre_jail_done; static int ask_client_cert; -static char *tlsp_pre_jail_client_param_key; /* pre-jail global params */ -static char *tlsp_pre_jail_client_init_key; /* pre-jail init props */ static const char *server_role_disabled; - /* - * TLS per-client status. - */ -static HTABLE *tlsp_client_app_cache; /* per-client init props */ -static BH_TABLE *tlsp_params_mismatch_filter; /* per-client nag filter */ - - /* - * Error handling: if a function detects an error, then that function is - * responsible for destroying TLSP_STATE. Exceptions to this principle are - * indicated in the code. - */ - - /* - * Internal status API. - */ -#define TLSP_STAT_OK 0 -#define TLSP_STAT_ERR (-1) - - /* - * SLMs. - */ -#define STR(x) vstring_str(x) -#define LEN(x) VSTRING_LEN(x) - /* * 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. @@ -1047,20 +1021,6 @@ static void tlsp_ciphertext_event(int event, void *context) } } -/* tlsp_client_start_pre_handshake - turn on TLS or force disconnect */ - -static int tlsp_client_start_pre_handshake(TLSP_STATE *state) -{ - state->client_start_props->ctx = state->appl_state; - state->client_start_props->fd = state->ciphertext_fd; - state->tls_context = tls_client_start(state->client_start_props); - if (state->tls_context != 0) - return (TLSP_STAT_OK); - - tlsp_state_free(state); - return (TLSP_STAT_ERR); -} - /* tlsp_server_start_pre_handshake - turn on TLS or force disconnect */ static int tlsp_server_start_pre_handshake(TLSP_STATE *state) @@ -1207,176 +1167,6 @@ static void tlsp_get_fd_event(int event, void *context) /* At this point, state could be a dangling pointer. */ } -/* tlsp_config_diff - report server-client config differences */ - -static void tlsp_log_config_diff(const char *server_cfg, const char *client_cfg) -{ - VSTRING *diff_summary = vstring_alloc(100); - char *saved_server = mystrdup(server_cfg); - char *saved_client = mystrdup(client_cfg); - char *server_field; - char *client_field; - char *server_next; - char *client_next; - - /* - * Not using argv_split(), because it would treat multiple consecutive - * newline characters as one. - */ - for (server_field = saved_server, client_field = saved_client; - server_field && client_field; - server_field = server_next, client_field = client_next) { - server_next = split_at(server_field, '\n'); - client_next = split_at(client_field, '\n'); - if (strcmp(server_field, client_field) != 0) { - if (LEN(diff_summary) > 0) - vstring_sprintf_append(diff_summary, "; "); - vstring_sprintf_append(diff_summary, - "(server) '%s' != (client) '%s'", - server_field, client_field); - } - } - msg_warn("%s", STR(diff_summary)); - - vstring_free(diff_summary); - myfree(saved_client); - myfree(saved_server); -} - -/* tlsp_client_init - initialize a TLS client engine */ - -static TLS_APPL_STATE *tlsp_client_init(TLS_CLIENT_PARAMS *tls_params, - TLS_CLIENT_INIT_PROPS *init_props) -{ - TLS_APPL_STATE *appl_state; - VSTRING *param_buf; - char *param_key; - VSTRING *init_buf; - char *init_key; - int log_hints = 0; - - /* - * Use one TLS_APPL_STATE object for all requests that specify the same - * TLS_CLIENT_INIT_PROPS. Each TLS_APPL_STATE owns an SSL_CTX, which is - * expensive to create. Bug: TLS_CLIENT_PARAMS are not used when creating - * a TLS_APPL_STATE instance. - * - * First, compute the TLS_APPL_STATE cache lookup key. Save a copy of the - * pre-jail request TLS_CLIENT_PARAMS and TLSPROXY_CLIENT_INIT_PROPS - * settings, so that we can detect post-jail requests that do not match. - */ - param_buf = vstring_alloc(100); - param_key = tls_proxy_client_param_serialize(attr_print_plain, param_buf, - tls_params); - init_buf = vstring_alloc(100); - init_key = tls_proxy_client_init_serialize(attr_print_plain, init_buf, - init_props); -#define TLSP_CLIENT_INIT_RETURN(retval) do { \ - vstring_free(init_buf); \ - vstring_free(param_buf); \ - return (retval); \ - } while (0) - - if (tlsp_pre_jail_done == 0) { - if (tlsp_pre_jail_client_param_key == 0 - || tlsp_pre_jail_client_init_key == 0) { - tlsp_pre_jail_client_param_key = mystrdup(param_key); - tlsp_pre_jail_client_init_key = mystrdup(init_key); - } else if (strcmp(tlsp_pre_jail_client_param_key, param_key) != 0 - || strcmp(tlsp_pre_jail_client_init_key, init_key) != 0) { - msg_panic("tlsp_client_init: too many pre-jail calls"); - } - } - - /* - * Log a warning if a post-jail request uses unexpected TLS_CLIENT_PARAMS - * settings. Bug: TLS_CLIENT_PARAMS settings are not used when creating a - * TLS_APPL_STATE instance; this makes a mismatch of TLS_CLIENT_PARAMS - * settings problematic. - */ - else if (tlsp_pre_jail_client_param_key == 0 - || tlsp_pre_jail_client_init_key == 0) { - msg_warn("TLS client role is disabled by configuration"); - TLSP_CLIENT_INIT_RETURN(0); - } else if (!been_here_fixed(tlsp_params_mismatch_filter, param_key) - && strcmp(tlsp_pre_jail_client_param_key, param_key) != 0) { - msg_warn("request from tlsproxy client with unexpected settings"); - tlsp_log_config_diff(tlsp_pre_jail_client_param_key, param_key); - log_hints = 1; - } - - /* - * Look up the cached TLS_APPL_STATE for this tls_client_init request. - */ - if ((appl_state = (TLS_APPL_STATE *) - htable_find(tlsp_client_app_cache, init_key)) == 0) { - - /* - * Before creating a TLS_APPL_STATE instance, log a warning if a - * post-jail request differs from the saved pre-jail request AND the - * post-jail request specifies file/directory pathname arguments. - * Unexpected requests containing pathnames are problematic after - * chroot (pathname resolution) and after dropping privileges (key - * files must be root read-only). Unexpected requests are not a - * problem as long as they contain no pathnames (for example a - * tls_loglevel change). - * - * We could eliminate some of this complication by adding code that - * opens a cert/key lookup table at pre-jail time, and by reading - * cert/key info on-the-fly from that table. But then all requests - * would still have to specify the same table. - */ -#define NOT_EMPTY(x) ((x) && *(x)) - - if (tlsp_pre_jail_done - && strcmp(tlsp_pre_jail_client_init_key, init_key) != 0 - && (NOT_EMPTY(init_props->chain_files) - || NOT_EMPTY(init_props->cert_file) - || NOT_EMPTY(init_props->key_file) - || NOT_EMPTY(init_props->dcert_file) - || NOT_EMPTY(init_props->dkey_file) - || NOT_EMPTY(init_props->eccert_file) - || NOT_EMPTY(init_props->eckey_file) - || NOT_EMPTY(init_props->CAfile) - || NOT_EMPTY(init_props->CApath))) { - msg_warn("request from tlsproxy client with unexpected settings"); - tlsp_log_config_diff(tlsp_pre_jail_client_init_key, init_key); - log_hints = 1; - } - } - if (log_hints) - msg_warn("to avoid this warning, 1) identify the tlsproxy " - "client that is making this request, 2) configure " - "a custom tlsproxy service with settings that " - "match that tlsproxy client, and 3) configure " - "that tlsproxy client with a tlsproxy_service_name " - "setting that resolves to that custom tlsproxy " - "service"); - - /* - * TLS_APPL_STATE creation may fail when a post-jail request specifies - * unexpected cert/key information, but that is OK because we already - * logged a warning with configuration suggestions. - */ - if (appl_state == 0 - && (appl_state = tls_client_init(init_props)) != 0) { - (void) htable_enter(tlsp_client_app_cache, init_key, - (void *) appl_state); - - /* - * 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. - */ - SSL_CTX_set_mode(appl_state->ssl_ctx, - SSL_MODE_ENABLE_PARTIAL_WRITE - | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - } - TLSP_CLIENT_INIT_RETURN(appl_state); -} - /* tlsp_close_event - pre-handshake plaintext-client close event */ static void tlsp_close_event(int event, void *context) @@ -1691,126 +1481,6 @@ static void pre_jail_init_server(void) | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); } -/* pre_jail_init_client - pre-jail initialization */ - -static void pre_jail_init_client(void) -{ - int clnt_use_tls; - - /* - * The cache with TLS_APPL_STATE instances for different TLS_CLIENT_INIT - * configurations. - */ - tlsp_client_app_cache = htable_create(10); - - /* Postfix <= 3.10 backwards compatibility. */ - if (warn_compat_break_tlsp_clnt_level - && warn_compat_break_smtp_tls_level) - msg_info("using backwards-compatible default setting " - VAR_TLSP_CLNT_LEVEL "=(empty)"); - - /* - * Most sites don't use TLS client certs/keys. In that case, enabling - * tlsproxy-based connection caching is trivial. - * - * But some sites do use TLS client certs/keys, and that is challenging when - * tlsproxy runs in a post-jail environment: chroot breaks pathname - * resolution, and an unprivileged process should not be able to open - * files with secrets. The workaround: assume that most of those sites - * will use a fixed TLS client identity. In that case, tlsproxy can load - * the corresponding certs/keys at pre-jail time, so that secrets can - * remain read-only for root. As long as the tlsproxy pre-jail TLS client - * configuration with cert or key pathnames is the same as the one used - * in the Postfix SMTP client, sites can selectively or globally enable - * tlsproxy-based connection caching without additional TLS - * configuration. - * - * Loading one TLS client configuration at pre-jail time is not sufficient - * for the minority of sites that want to use TLS connection caching with - * multiple TLS client identities. To alert the operator, tlsproxy will - * log a warning when a TLS_CLIENT_INIT message specifies a different - * configuration than the tlsproxy pre-jail client configuration, and - * that different configuration specifies file/directory pathname - * arguments. The workaround is to have one tlsproxy process per TLS - * client identity. - * - * The general solution for single-identity or multi-identity clients is to - * stop loading certs and keys from individual files. Instead, have a - * cert/key map, indexed by client identity, read-only by root. After - * opening the map as root at pre-jail time, tlsproxy can read certs/keys - * on-the-fly as an unprivileged process at post-jail time. This is the - * approach that was already proposed for server-side SNI support, and it - * could be reused here. It would also end the proliferation of RSA - * cert/key parameters, DSA cert/key parameters, EC cert/key parameters, - * and so on. - * - * Horror: In order to create the same pre-jail TLS client context as the - * one used in the Postfix SMTP client, we have to duplicate intricate - * SMTP client code, including a handful configuration parameters that - * tlsproxy does not need. We must duplicate the logic, so that we only - * load certs and keys when the SMTP client would load them. - */ - if (*var_tlsp_clnt_level != 0) - switch (tls_level_lookup(var_tlsp_clnt_level)) { - case TLS_LEV_SECURE: - case TLS_LEV_VERIFY: - case TLS_LEV_DANE_ONLY: - case TLS_LEV_FPRINT: - case TLS_LEV_ENCRYPT: - var_tlsp_clnt_use_tls = var_tlsp_clnt_enforce_tls = 1; - break; - case TLS_LEV_DANE: - case TLS_LEV_MAY: - var_tlsp_clnt_use_tls = 1; - var_tlsp_clnt_enforce_tls = 0; - break; - case TLS_LEV_NONE: - var_tlsp_clnt_use_tls = var_tlsp_clnt_enforce_tls = 0; - break; - default: - /* tls_level_lookup() logs no warning. */ - /* session_tls_init() assumes that var_tlsp_clnt_level is sane. */ - msg_fatal("Invalid TLS level \"%s\"", var_tlsp_clnt_level); - } - clnt_use_tls = (var_tlsp_clnt_use_tls || var_tlsp_clnt_enforce_tls); - - /* - * Initialize the TLS data before entering the chroot jail. - */ - if (clnt_use_tls || var_tlsp_clnt_per_site[0] || var_tlsp_clnt_policy[0]) { - TLS_CLIENT_PARAMS tls_params; - TLS_CLIENT_INIT_PROPS init_props; - - tls_pre_jail_init(TLS_ROLE_CLIENT); - - /* - * We get stronger type safety and a cleaner interface by combining - * the various parameters into a single tls_client_props structure. - * - * Large parameter lists are error-prone, so we emulate a language - * feature that C does not have natively: named parameter lists. - */ - (void) tls_proxy_client_param_from_config(&tls_params); - (void) TLS_CLIENT_INIT_ARGS(&init_props, - log_param = var_tlsp_clnt_logparam, - log_level = var_tlsp_clnt_loglevel, - verifydepth = var_tlsp_clnt_scert_vd, - cache_type = TLS_MGR_SCACHE_SMTP, - chain_files = var_tlsp_clnt_chain_files, - cert_file = var_tlsp_clnt_cert_file, - key_file = var_tlsp_clnt_key_file, - dcert_file = var_tlsp_clnt_dcert_file, - dkey_file = var_tlsp_clnt_dkey_file, - eccert_file = var_tlsp_clnt_eccert_file, - eckey_file = var_tlsp_clnt_eckey_file, - CAfile = var_tlsp_clnt_CAfile, - CApath = var_tlsp_clnt_CApath, - mdalg = var_tlsp_clnt_fpt_dgst); - if (tlsp_client_init(&tls_params, &init_props) == 0) - msg_warn("TLS client initialization failed"); - } -} - /* pre_jail_init - pre-jail initialization */ static void pre_jail_init(char *unused_name, char **unused_argv) @@ -1820,19 +1490,7 @@ static void pre_jail_init(char *unused_name, char **unused_argv) * Initialize roles separately. */ pre_jail_init_server(); - pre_jail_init_client(); - - /* - * tlsp_client_init() needs to know if it is called pre-jail or - * post-jail. - */ - tlsp_pre_jail_done = 1; - - /* - * Bug: TLS_CLIENT_PARAMS attributes are not used when creating a - * TLS_APPL_STATE instance; we can only warn about attribute mismatches. - */ - tlsp_params_mismatch_filter = been_here_init(BH_BOUND_NONE, BH_FLAG_NONE); + tlsp_pre_jail_client_init(); } MAIL_VERSION_STAMP_DECLARE; diff --git a/postfix/src/tlsproxy/tlsproxy.h b/postfix/src/tlsproxy/tlsproxy.h index b3223ab74..6fbc294f4 100644 --- a/postfix/src/tlsproxy/tlsproxy.h +++ b/postfix/src/tlsproxy/tlsproxy.h @@ -1,3 +1,6 @@ +#ifndef _TLSPROXY_H_INCLUDED_ +#define _TLSPROXY_H_INCLUDED_ + /*++ /* NAME /* tlsproxy 3h @@ -18,6 +21,7 @@ * TLS library. */ #include +#include /* * Internal interface. @@ -53,6 +57,28 @@ typedef struct { extern TLSP_STATE *tlsp_state_create(const char *, VSTREAM *); extern void tlsp_state_free(TLSP_STATE *); + /* + * Error handling: if a function detects an error, then that function is + * responsible for destroying TLSP_STATE. Exceptions to this principle are + * indicated in the code. + * + * TODO(wietse) Revisit this contract. Destroying state violates layering. + * Maybe encapsulate the error returning action in a wrapper that destroys + * state. + */ + + /* + * Internal status API. + */ +#define TLSP_STAT_OK 0 +#define TLSP_STAT_ERR (-1) + + /* + * SLMs. + */ +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) + /* LICENSE /* .ad /* .fi @@ -71,3 +97,5 @@ extern void tlsp_state_free(TLSP_STATE *); /* Wietse Venema /* porcupine.org /*--*/ + +#endif diff --git a/postfix/src/tlsproxy/tlsproxy_client.c b/postfix/src/tlsproxy/tlsproxy_client.c new file mode 100644 index 000000000..cb638b3f7 --- /dev/null +++ b/postfix/src/tlsproxy/tlsproxy_client.c @@ -0,0 +1,389 @@ +/*++ +/* NAME +/* tlsproxy_client 3 +/* SUMMARY +/* Postfix TLS proxy client role support +/* SYNOPSIS +/* #include +/* +/* void tlsp_pre_jail_client_init(void) +/* +/* TLS_APPL_STATE *tlsp_client_init( +/* TLS_CLIENT_PARAMS *tls_params, +/* TLS_CLIENT_INIT_PROPS *init_props) +/* +/* int tlsp_client_start_pre_handshake(TLSP_STATE *state) +/* DESCRIPTION +/* This module implements TLS proxy client role support. +/* +/* tlsp_pre_jail_client_init() saves a copy of serialized +/* TLS_CLIENT_PARAMS and TLS_CLIENT_INIT_PROPS based on configuration +/* parameter settings. These will be used as a reference when +/* receiving a request for the client role. +/* +/* tlsp_client_init() processes a request for the TLS proxy client +/* role. If the request has not been seen before it checks the +/* request for relevant differences that would conflict with +/* tlsproxy(8) client configuration. The result is null when +/* TLS is not available. +/* +/* tlsp_client_start_pre_handshake() requests the tls_client_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 and transactions 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 + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + + /* + * Utility library. + */ +#include +#include + + /* + * Global library. + */ +#include +#include +#include + + /* + * TLS library. + */ +#ifdef USE_TLS +#define TLS_INTERNAL /* XXX */ +#include +#include + + /* + * Application-specific. + */ +#include +#include + + /* + * TLS per-process status. + */ +static bool tlsp_pre_jail_client_done; +static char *tlsp_pre_jail_client_param_key; /* pre-jail global params */ +static char *tlsp_pre_jail_client_init_key; /* pre-jail init props */ + + /* + * TLS per-client status. + */ +static HTABLE *tlsp_client_app_cache; /* per-client init props */ +static BH_TABLE *tlsp_client_params_nag_filter; /* per-client nag filter */ + +/* tlsp_client_start_pre_handshake - turn on TLS or force disconnect */ + +int tlsp_client_start_pre_handshake(TLSP_STATE *state) +{ + state->client_start_props->ctx = state->appl_state; + state->client_start_props->fd = state->ciphertext_fd; + state->tls_context = tls_client_start(state->client_start_props); + if (state->tls_context != 0) + return (TLSP_STAT_OK); + + tlsp_state_free(state); + return (TLSP_STAT_ERR); +} + +/* tlsp_client_init - initialize a TLS client engine */ + +TLS_APPL_STATE *tlsp_client_init(TLS_CLIENT_PARAMS *tls_params, + TLS_CLIENT_INIT_PROPS *init_props) +{ + TLS_APPL_STATE *appl_state; + VSTRING *param_buf; + char *param_key; + VSTRING *init_buf; + char *init_key; + int log_hints = 0; + + /* + * Use one TLS_APPL_STATE object for all requests that specify the same + * TLS_CLIENT_INIT_PROPS. Each TLS_APPL_STATE owns an SSL_CTX, which is + * expensive to create. Bug: TLS_CLIENT_PARAMS are not used when creating + * a TLS_APPL_STATE instance. + * + * First, compute the TLS_APPL_STATE cache lookup key. Save a copy of the + * pre-jail request TLS_CLIENT_PARAMS and TLSPROXY_CLIENT_INIT_PROPS + * settings, so that we can detect post-jail requests that do not match. + */ + param_buf = vstring_alloc(100); + param_key = tls_proxy_client_param_serialize(attr_print_plain, param_buf, + tls_params); + init_buf = vstring_alloc(100); + init_key = tls_proxy_client_init_serialize(attr_print_plain, init_buf, + init_props); +#define TLSP_CLIENT_INIT_RETURN(retval) do { \ + vstring_free(init_buf); \ + vstring_free(param_buf); \ + return (retval); \ + } while (0) + + if (tlsp_pre_jail_client_done == 0) { + if (tlsp_pre_jail_client_param_key == 0 + || tlsp_pre_jail_client_init_key == 0) { + tlsp_pre_jail_client_param_key = mystrdup(param_key); + tlsp_pre_jail_client_init_key = mystrdup(init_key); + } else if (strcmp(tlsp_pre_jail_client_param_key, param_key) != 0 + || strcmp(tlsp_pre_jail_client_init_key, init_key) != 0) { + msg_panic("tlsp_client_init: too many pre-jail calls"); + } + } + + /* + * Log a warning if a post-jail request uses unexpected TLS_CLIENT_PARAMS + * settings. Bug: TLS_CLIENT_PARAMS settings are not used when creating a + * TLS_APPL_STATE instance; this makes a mismatch of TLS_CLIENT_PARAMS + * settings problematic. + */ + else if (tlsp_pre_jail_client_param_key == 0 + || tlsp_pre_jail_client_init_key == 0) { + msg_warn("TLS client role is disabled by configuration"); + TLSP_CLIENT_INIT_RETURN(0); + } else if (!been_here_fixed(tlsp_client_params_nag_filter, param_key) + && strcmp(tlsp_pre_jail_client_param_key, param_key) != 0) { + msg_warn("request from tlsproxy client with unexpected settings"); + tlsp_log_config_diff(tlsp_pre_jail_client_param_key, param_key); + log_hints = 1; + } + + /* + * Look up the cached TLS_APPL_STATE for this tls_client_init request. + */ + if ((appl_state = (TLS_APPL_STATE *) + htable_find(tlsp_client_app_cache, init_key)) == 0) { + + /* + * Before creating a TLS_APPL_STATE instance, log a warning if a + * post-jail request differs from the saved pre-jail request AND the + * post-jail request specifies file/directory pathname arguments. + * Unexpected requests containing pathnames are problematic after + * chroot (pathname resolution) and after dropping privileges (key + * files must be root read-only). Unexpected requests are not a + * problem as long as they contain no pathnames (for example a + * tls_loglevel change). + * + * We could eliminate some of this complication by adding code that + * opens a cert/key lookup table at pre-jail time, and by reading + * cert/key info on-the-fly from that table. But then all requests + * would still have to specify the same table. + */ +#define NOT_EMPTY(x) ((x) && *(x)) + + if (tlsp_pre_jail_client_done + && strcmp(tlsp_pre_jail_client_init_key, init_key) != 0 + && (NOT_EMPTY(init_props->chain_files) + || NOT_EMPTY(init_props->cert_file) + || NOT_EMPTY(init_props->key_file) + || NOT_EMPTY(init_props->dcert_file) + || NOT_EMPTY(init_props->dkey_file) + || NOT_EMPTY(init_props->eccert_file) + || NOT_EMPTY(init_props->eckey_file) + || NOT_EMPTY(init_props->CAfile) + || NOT_EMPTY(init_props->CApath))) { + msg_warn("request from tlsproxy client with unexpected settings"); + tlsp_log_config_diff(tlsp_pre_jail_client_init_key, init_key); + log_hints = 1; + } + } + if (log_hints) + msg_warn("to avoid this warning, 1) identify the tlsproxy " + "client that is making this request, 2) configure " + "a custom tlsproxy service with settings that " + "match that tlsproxy client, and 3) configure " + "that tlsproxy client with a tlsproxy_service_name " + "setting that resolves to that custom tlsproxy " + "service"); + + /* + * TLS_APPL_STATE creation may fail when a post-jail request specifies + * unexpected cert/key information, but that is OK because we already + * logged a warning with configuration suggestions. + */ + if (appl_state == 0 + && (appl_state = tls_client_init(init_props)) != 0) { + (void) htable_enter(tlsp_client_app_cache, init_key, + (void *) appl_state); + + /* + * 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. + */ + SSL_CTX_set_mode(appl_state->ssl_ctx, + SSL_MODE_ENABLE_PARTIAL_WRITE + | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + } + TLSP_CLIENT_INIT_RETURN(appl_state); +} + +/* tlsp_pre_jail_client_init - pre-jail initialization */ + +void tlsp_pre_jail_client_init(void) +{ + int clnt_use_tls; + + /* + * The cache with TLS_APPL_STATE instances for different TLS_CLIENT_INIT + * configurations. + */ + tlsp_client_app_cache = htable_create(10); + + /* Postfix <= 3.10 backwards compatibility. */ + if (warn_compat_break_tlsp_clnt_level + && warn_compat_break_smtp_tls_level) + msg_info("using backwards-compatible default setting " + VAR_TLSP_CLNT_LEVEL "=(empty)"); + + /* + * Most sites don't use TLS client certs/keys. In that case, enabling + * tlsproxy-based connection caching is trivial. + * + * But some sites do use TLS client certs/keys, and that is challenging when + * tlsproxy runs in a post-jail environment: chroot breaks pathname + * resolution, and an unprivileged process should not be able to open + * files with secrets. The workaround: assume that most of those sites + * will use a fixed TLS client identity. In that case, tlsproxy can load + * the corresponding certs/keys at pre-jail time, so that secrets can + * remain read-only for root. As long as the tlsproxy pre-jail TLS client + * configuration with cert or key pathnames is the same as the one used + * in the Postfix SMTP client, sites can selectively or globally enable + * tlsproxy-based connection caching without additional TLS + * configuration. + * + * Loading one TLS client configuration at pre-jail time is not sufficient + * for the minority of sites that want to use TLS connection caching with + * multiple TLS client identities. To alert the operator, tlsproxy will + * log a warning when a TLS_CLIENT_INIT message specifies a different + * configuration than the tlsproxy pre-jail client configuration, and + * that different configuration specifies file/directory pathname + * arguments. The workaround is to have one tlsproxy process per TLS + * client identity. + * + * The general solution for single-identity or multi-identity clients is to + * stop loading certs and keys from individual files. Instead, have a + * cert/key map, indexed by client identity, read-only by root. After + * opening the map as root at pre-jail time, tlsproxy can read certs/keys + * on-the-fly as an unprivileged process at post-jail time. This is the + * approach that was already proposed for server-side SNI support, and it + * could be reused here. It would also end the proliferation of RSA + * cert/key parameters, DSA cert/key parameters, EC cert/key parameters, + * and so on. + * + * Horror: In order to create the same pre-jail TLS client context as the + * one used in the Postfix SMTP client, we have to duplicate intricate + * SMTP client code, including a handful configuration parameters that + * tlsproxy does not need. We must duplicate the logic, so that we only + * load certs and keys when the SMTP client would load them. + */ + if (*var_tlsp_clnt_level != 0) + switch (tls_level_lookup(var_tlsp_clnt_level)) { + case TLS_LEV_SECURE: + case TLS_LEV_VERIFY: + case TLS_LEV_DANE_ONLY: + case TLS_LEV_FPRINT: + case TLS_LEV_ENCRYPT: + var_tlsp_clnt_use_tls = var_tlsp_clnt_enforce_tls = 1; + break; + case TLS_LEV_DANE: + case TLS_LEV_MAY: + var_tlsp_clnt_use_tls = 1; + var_tlsp_clnt_enforce_tls = 0; + break; + case TLS_LEV_NONE: + var_tlsp_clnt_use_tls = var_tlsp_clnt_enforce_tls = 0; + break; + default: + /* tls_level_lookup() logs no warning. */ + /* session_tls_init() assumes that var_tlsp_clnt_level is sane. */ + msg_fatal("Invalid TLS level \"%s\"", var_tlsp_clnt_level); + } + clnt_use_tls = (var_tlsp_clnt_use_tls || var_tlsp_clnt_enforce_tls); + + /* + * Initialize the TLS data before entering the chroot jail. + */ + if (clnt_use_tls || var_tlsp_clnt_per_site[0] || var_tlsp_clnt_policy[0]) { + TLS_CLIENT_PARAMS tls_params; + TLS_CLIENT_INIT_PROPS init_props; + + tls_pre_jail_init(TLS_ROLE_CLIENT); + + /* + * We get stronger type safety and a cleaner interface by combining + * the various parameters into a single tls_client_props structure. + * + * Large parameter lists are error-prone, so we emulate a language + * feature that C does not have natively: named parameter lists. + */ + (void) tls_proxy_client_param_from_config(&tls_params); + (void) TLS_CLIENT_INIT_ARGS(&init_props, + log_param = var_tlsp_clnt_logparam, + log_level = var_tlsp_clnt_loglevel, + verifydepth = var_tlsp_clnt_scert_vd, + cache_type = TLS_MGR_SCACHE_SMTP, + chain_files = var_tlsp_clnt_chain_files, + cert_file = var_tlsp_clnt_cert_file, + key_file = var_tlsp_clnt_key_file, + dcert_file = var_tlsp_clnt_dcert_file, + dkey_file = var_tlsp_clnt_dkey_file, + eccert_file = var_tlsp_clnt_eccert_file, + eckey_file = var_tlsp_clnt_eckey_file, + CAfile = var_tlsp_clnt_CAfile, + CApath = var_tlsp_clnt_CApath, + mdalg = var_tlsp_clnt_fpt_dgst); + if (tlsp_client_init(&tls_params, &init_props) == 0) + msg_warn("TLS client initialization failed"); + } + + /* + * Bug: TLS_CLIENT_PARAMS attributes are not used when creating a + * TLS_APPL_STATE instance; we can only warn about attribute mismatches. + */ + tlsp_client_params_nag_filter = been_here_init(BH_BOUND_NONE, BH_FLAG_NONE); + + /* + * Any of the static global variables would suffice, but this is more + * explicit. + */ + tlsp_pre_jail_client_done = 1; +} + +#endif diff --git a/postfix/src/tlsproxy/tlsproxy_client.h b/postfix/src/tlsproxy/tlsproxy_client.h new file mode 100644 index 000000000..ed15197cf --- /dev/null +++ b/postfix/src/tlsproxy/tlsproxy_client.h @@ -0,0 +1,48 @@ +#ifndef _TLSPROXY_CLIENT_H_ +#define _TLSPROXY_CLIENT_H_ + +/*++ +/* NAME +/* tlsproxy_client 3h +/* SUMMARY +/* tlsproxy client role support +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * TLS library. + */ +#include +#include + + /* + * Internal API. + */ +#include + +extern void tlsp_pre_jail_client_init(void); +extern TLS_APPL_STATE *tlsp_client_init(TLS_CLIENT_PARAMS *, TLS_CLIENT_INIT_PROPS *); +extern int tlsp_client_start_pre_handshake(TLSP_STATE *); + +/* 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/tlsproxy/tlsproxy_diff.c b/postfix/src/tlsproxy/tlsproxy_diff.c new file mode 100644 index 000000000..a5fe64083 --- /dev/null +++ b/postfix/src/tlsproxy/tlsproxy_diff.c @@ -0,0 +1,92 @@ +/*++ +/* NAME +/* tlsproxy_diff 3 +/* SUMMARY +/* Diff TLS proxy client attribute lists +/* SYNOPSIS +/* #include +/* +/* void tlsp_log_config_diff( +/* const char *server_cfg, +/* const char *client_cfg) +/* DESCRIPTION +/* tlsp_log_config_diff() logs the difference between TLS +/* configuration attribute lists. The format is zer or more "name +/* = value\n". The server_cfg argument specifies attributes from +/* the tlsproxy server, and client_cfg lists attributes from a +/* tlsproxy client. +/* 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 + + /* + * Utility library. + */ +#include +#include + + /* + * Internal API. + */ +#include +#include + +/* tlsp_config_diff - report server-client config differences */ + +void tlsp_log_config_diff(const char *server_cfg, const char *client_cfg) +{ + VSTRING *diff_summary = vstring_alloc(100); + char *saved_server = mystrdup(server_cfg); + char *saved_client = mystrdup(client_cfg); + char *server_field; + char *client_field; + char *server_next; + char *client_next; + + /* + * Not using argv_split(), because it would treat multiple consecutive + * newline characters as one. + */ + for (server_field = saved_server, client_field = saved_client; + server_field && client_field; + server_field = server_next, client_field = client_next) { + server_next = split_at(server_field, '\n'); + client_next = split_at(client_field, '\n'); + if (strcmp(server_field, client_field) != 0) { + if (LEN(diff_summary) > 0) + vstring_sprintf_append(diff_summary, "; "); + vstring_sprintf_append(diff_summary, + "(server) '%s' != (client) '%s'", + server_field, client_field); + } + } + msg_warn("%s", STR(diff_summary)); + + vstring_free(diff_summary); + myfree(saved_client); + myfree(saved_server); +} diff --git a/postfix/src/tlsproxy/tlsproxy_diff.h b/postfix/src/tlsproxy/tlsproxy_diff.h new file mode 100644 index 000000000..bee21121e --- /dev/null +++ b/postfix/src/tlsproxy/tlsproxy_diff.h @@ -0,0 +1,38 @@ +#ifndef _TLSPROXY_DIFF_H_ +#define _TLSPROXY_DIFF_H_ + +/*++ +/* NAME +/* tlsproxy_diff 3h +/* SUMMARY +/* log serialized attribute differences +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Internal API. + */ +extern void tlsp_log_config_diff(const char *, const char *); + +/* 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 a0dc395f3..56fc45013 100644 --- a/postfix/src/util/argv.c +++ b/postfix/src/util/argv.c @@ -262,8 +262,8 @@ static void argv_extend(ARGV *argvp) { ssize_t new_len; - /* 202604 Claude: avoid small non-negative expression 'new_len + 1'. */ - if (argvp->len > SSIZE_MAX / 2) + /* 202604 Claude: avoid overflowing (new_len + 1) * sizeof(char *). */ + if (argvp->len + 1 > SSIZE_MAX / (2 * sizeof(char *))) msg_panic("argv_extend: array length overflow"); new_len = argvp->len * 2; argvp->argv = (char **) diff --git a/postfix/src/util/dict.c b/postfix/src/util/dict.c index 8189b5e89..afe6594e0 100644 --- a/postfix/src/util/dict.c +++ b/postfix/src/util/dict.c @@ -620,7 +620,8 @@ void dict_walk(DICT_WALK_ACTION action, void *ptr) ht_info_list = htable_list(dict_table); for (ht = ht_info_list; (h = *ht) != 0; ht++) - action(h->key, (DICT *) h->value, ptr); + /* 202604 Claude: h->value is (DICT_NODE *) not (DICT *). */ + action(h->key, ((DICT_NODE *) h->value)->dict, ptr); myfree((void *) ht_info_list); } diff --git a/postfix/src/util/dict_pcre.c b/postfix/src/util/dict_pcre.c index f65f440a6..29e4f0ca7 100644 --- a/postfix/src/util/dict_pcre.c +++ b/postfix/src/util/dict_pcre.c @@ -728,6 +728,9 @@ static int dict_pcre_compile(const char *mapname, int lineno, } engine->match_data = pcre2_match_data_create_from_pattern( engine->pattern, (void *) 0); + /* 202604 Claude: handle error result. */ + if (engine->match_data == 0) + msg_fatal("out of memory in pcre2_match_data_create_from_pattern()"); #endif return (1); }