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.
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
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);
}
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
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;
/*
* 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
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,
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);
}
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)
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
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
* Application-specific.
*/
#include <tlsproxy.h>
+#include <tlsproxy_client.h>
/*
* Tunable parameters. We define our clones of the smtpd(8) parameters to
* 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.
}
}
-/* 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)
/* 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)
| 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)
* 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;
+#ifndef _TLSPROXY_H_INCLUDED_
+#define _TLSPROXY_H_INCLUDED_
+
/*++
/* NAME
/* tlsproxy 3h
* TLS library.
*/
#include <tls.h>
+#include <tls_proxy.h>
/*
* Internal interface.
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
/* Wietse Venema
/* porcupine.org
/*--*/
+
+#endif
--- /dev/null
+/*++
+/* NAME
+/* tlsproxy_client 3
+/* SUMMARY
+/* Postfix TLS proxy client role support
+/* SYNOPSIS
+/* #include <tlsproxy_client.h>
+/*
+/* 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 <sys_defs.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+ /*
+ * Utility library.
+ */
+#include <msg.h>
+#include <mymalloc.h>
+
+ /*
+ * Global library.
+ */
+#include <been_here.h>
+#include <mail_proto.h>
+#include <mail_params.h>
+
+ /*
+ * TLS library.
+ */
+#ifdef USE_TLS
+#define TLS_INTERNAL /* XXX */
+#include <tls.h>
+#include <tls_proxy.h>
+
+ /*
+ * Application-specific.
+ */
+#include <tlsproxy_client.h>
+#include <tlsproxy_diff.h>
+
+ /*
+ * 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
--- /dev/null
+#ifndef _TLSPROXY_CLIENT_H_
+#define _TLSPROXY_CLIENT_H_
+
+/*++
+/* NAME
+/* tlsproxy_client 3h
+/* SUMMARY
+/* tlsproxy client role support
+/* SYNOPSIS
+/* #include <tlsproxy_client.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * TLS library.
+ */
+#include <tls.h>
+#include <tls_proxy.h>
+
+ /*
+ * Internal API.
+ */
+#include <tlsproxy.h>
+
+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
--- /dev/null
+/*++
+/* NAME
+/* tlsproxy_diff 3
+/* SUMMARY
+/* Diff TLS proxy client attribute lists
+/* SYNOPSIS
+/* #include <tlsproxy_diff.h>
+/*
+/* 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 <sys_defs.h>
+
+ /*
+ * Utility library.
+ */
+#include <msg.h>
+#include <split_at.h>
+
+ /*
+ * Internal API.
+ */
+#include <tlsproxy.h>
+#include <tlsproxy_diff.h>
+
+/* 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);
+}
--- /dev/null
+#ifndef _TLSPROXY_DIFF_H_
+#define _TLSPROXY_DIFF_H_
+
+/*++
+/* NAME
+/* tlsproxy_diff 3h
+/* SUMMARY
+/* log serialized attribute differences
+/* SYNOPSIS
+/* #include <tlsproxy_diff.h>
+/* 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
{
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 **)
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);
}
}
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);
}