commit 9f1a75f126ae217a3a3568b106c9133b3c5c413a Merge: 57318bfd2 89318c714 Author: Jeremy Harris Date: Sun Jun 26 12:10:03 2022 +0100 Merge branch '4.next' diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index 78f5516a7..0c64d45d4 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -1,4 +1,5 @@ # This file is the basis of the main makefile for Exim and friends. The +# # makefile at the top level arranges to build the main makefile by calling # scripts/Configure-Makefile from within the build directory. This # concatenates the configuration settings from Local/Makefile and other, @@ -485,9 +486,9 @@ OBJ_LOOKUPS = lookups/lf_quote.o lookups/lf_check_file.o lookups/lf_sqlperform.o OBJ_EXIM = acl.o base64.o child.o crypt16.o daemon.o dbfn.o debug.o deliver.o \ directory.o dns.o drtables.o enq.o exim.o expand.o filter.o \ filtertest.o globals.o dkim.o dkim_transport.o dnsbl.o hash.o \ - header.o host.o ip.o log.o lss.o match.o md5.o moan.o \ + header.o host.o host_address.o ip.o log.o lss.o match.o md5.o moan.o \ os.o parse.o priv.o queue.o \ - rda.o readconf.o receive.o retry.o rewrite.o rfc2047.o \ + rda.o readconf.o receive.o retry.o rewrite.o rfc2047.o regex_cache.o \ route.o search.o sieve.o smtp_in.o smtp_out.o spool_in.o spool_out.o \ std-crypto.o store.o string.o tls.o tod.o transport.o tree.o verify.o \ environment.o macro.o \ @@ -601,10 +602,11 @@ MONBIN = em_StripChart.o $(EXIMON_TEXTPOP) em_globals.o em_init.o \ # The complete modules list also includes some specially compiled versions of # code from the main Exim source tree. -OBJ_MONBIN = util-spool_in.o \ +OBJ_MONBIN = util-host_address.o \ + util-queue.o \ + util-spool_in.o \ util-store.o \ util-string.o \ - util-queue.o \ util-tod.o \ util-tree.o \ $(MONBIN) @@ -721,6 +723,14 @@ exim_dbmbuild.o: $(HDRS) exim_dbmbuild.c # Utilities use special versions of some modules - typically with debugging # calls cut out. +util-host_address.o: $(HDRS) host_address.c + @echo "$(CC) -DCOMPILE_UTILITY host_address.c" + $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) -DCOMPILE_UTILITY -o util-host_address.o host_address.c + +util-md5.o: $(HDRS) md5.c + @echo "$(CC) -DCOMPILE_UTILITY queue.c" + $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) -DCOMPILE_UTILITY -o util-md5.o md5.c + util-spool_in.o: $(HDRS) spool_in.c @echo "$(CC) -DCOMPILE_UTILITY spool_in.c" $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) -DCOMPILE_UTILITY -o util-spool_in.o spool_in.c @@ -733,10 +743,6 @@ util-string.o: $(HDRS) string.c @echo "$(CC) -DCOMPILE_UTILITY string.c" $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) -DCOMPILE_UTILITY -o util-string.o string.c -util-md5.o: $(HDRS) md5.c - @echo "$(CC) -DCOMPILE_UTILITY queue.c" - $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) -DCOMPILE_UTILITY -o util-md5.o md5.c - util-queue.o: $(HDRS) queue.c @echo "$(CC) -DCOMPILE_UTILITY queue.c" $(FE)$(CC) -c $(CFLAGS) $(INCLUDE) -DCOMPILE_UTILITY -o util-queue.o queue.c @@ -787,6 +793,7 @@ globals.o: $(HDRS) globals.c hash.o: $(HDRS) hash.c header.o: $(HDRS) header.c host.o: $(HDRS) host.c +host_address.o: $(HDRS) host_address.c ip.o: $(HDRS) ip.c log.o: $(HDRS) log.c lss.o: $(HDRS) lss.c @@ -802,6 +809,7 @@ readconf.o: $(HDRS) readconf.c receive.o: $(HDRS) receive.c retry.o: $(HDRS) retry.c rewrite.o: $(HDRS) rewrite.c +regex_cache.o: $(HDRS) regex_cache.c rfc2047.o: $(HDRS) rfc2047.c route.o: $(HDRS) route.c search.o: $(HDRS) search.c @@ -962,13 +970,13 @@ test_dbfn: config.h dbfn.c dummies.o sa-globals.o sa-os.o store.o \ rm -f dbfn.o store.o test_host: config.h child.c host.c dns.c dummies.c sa-globals.o os.o \ - store.o string.o tod.o tree.o + host_address.o store.o string.o tod.o tree.o $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE -DTEST_HOST child.c $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE -DTEST_HOST host.c $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE -DTEST_HOST dns.c $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE -DTEST_HOST dummies.c $(LNCC) -o test_host $(LFLAGS) \ - host.o child.o dns.o dummies.o sa-globals.o os.o store.o string.o \ + host.o host_address.o child.o dns.o dummies.o sa-globals.o os.o store.o string.o \ tod.o tree.o $(LIBS) $(LIBRESOLV) rm -f child.o dummies.o host.o dns.o diff --git a/src/OS/Makefile-FreeBSD b/src/OS/Makefile-FreeBSD index 02253d9c6..4793a438b 100644 --- a/src/OS/Makefile-FreeBSD +++ b/src/OS/Makefile-FreeBSD @@ -13,7 +13,7 @@ PERL_COMMAND=/usr/local/bin/perl HAVE_SA_LEN=YES # crypt() is in a separate library -LIBS=-lcrypt -lm -lutil +LIBS=-lcrypt -lm -lutil -lexecinfo # Dynamically loaded modules need to be built with -fPIC CFLAGS_DYNAMIC=-shared -rdynamic -fPIC diff --git a/src/OS/Makefile-OpenBSD b/src/OS/Makefile-OpenBSD index 697632682..7c451e2cc 100644 --- a/src/OS/Makefile-OpenBSD +++ b/src/OS/Makefile-OpenBSD @@ -23,6 +23,7 @@ EXIWHAT_EGREP_ARG='/exim( |$$)' EXIWHAT_KILL_SIGNAL=-USR1 HAVE_IPV6=YES +CFLAGS += -DNO_EXECINFO # OpenBSD ships with a too-old Berkeley DB. NDBM is the default if we don't specify one. #USE_DB=yes diff --git a/src/OS/Makefile-SunOS5 b/src/OS/Makefile-SunOS5 index 3b436f4bf..d07adcff4 100644 --- a/src/OS/Makefile-SunOS5 +++ b/src/OS/Makefile-SunOS5 @@ -20,5 +20,6 @@ X11LIB=$(X11)/lib OS_C_INCLUDES=setenv.c CFLAGS += -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED=1 -D__EXTENSIONS__ +CFLAGS += -DNO_EXECINFO # End diff --git a/src/exim_monitor/em_main.c b/src/exim_monitor/em_main.c index 5714b999c..86caf71eb 100644 --- a/src/exim_monitor/em_main.c +++ b/src/exim_monitor/em_main.c @@ -179,70 +179,6 @@ va_end(ap); -/************************************************* -* Extract port from address string * -*************************************************/ - -/* In the spool file, a host plus port is given as an IP address followed by a -dot and a port number. This function decodes this. It is needed by the -spool-reading function, and copied here to avoid having to include the whole -host.c module. One day the interaction between exim and eximon with regard to -included code MUST be tidied up! - -Argument: - address points to the string; if there is a port, the '.' in the string - is overwritten with zero to terminate the address - -Returns: 0 if there is no port, else the port number. -*/ - -int -host_address_extract_port(uschar * address) -{ -int port = 0; -uschar *endptr; - -/* Handle the "bracketed with colon on the end" format */ - -if (*address == '[') - { - uschar *rb = address + 1; - while (*rb != 0 && *rb != ']') rb++; - if (*rb++ == 0) return 0; /* Missing ]; leave invalid address */ - if (*rb == ':') - { - port = Ustrtol(rb + 1, &endptr, 10); - if (*endptr != 0) return 0; /* Invalid port; leave invalid address */ - } - else if (*rb != 0) return 0; /* Bad syntax; leave invalid address */ - memmove(address, address + 1, rb - address - 2); - rb[-2] = 0; - } - -/* Handle the "dot on the end" format */ - -else - { - int skip = -3; /* Skip 3 dots in IPv4 addresses */ - address--; - while (*(++address) != 0) - { - int ch = *address; - if (ch == ':') skip = 0; /* Skip 0 dots in IPv6 addresses */ - else if (ch == '.' && skip++ >= 0) break; - } - if (*address == 0) return 0; - port = Ustrtol(address + 1, &endptr, 10); - if (*endptr != 0) return 0; /* Invalid port; leave invalid address */ - *address = 0; - } - -return port; -} - - - - /************************************************* * SIGCHLD handler * *************************************************/ diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks index 9cdb931f3..471b3a369 100755 --- a/src/scripts/MakeLinks +++ b/src/scripts/MakeLinks @@ -102,9 +102,9 @@ for f in blob.h dbfunctions.h exim.h functions.h globals.h \ acl.c buildconfig.c base64.c child.c crypt16.c daemon.c dbfn.c debug.c \ deliver.c directory.c dns.c dnsbl.c drtables.c dummies.c enq.c exim.c \ exim_dbmbuild.c exim_dbutil.c exim_lock.c expand.c filter.c filtertest.c \ - globals.c hash.c header.c host.c ip.c log.c lss.c match.c md5.c moan.c \ + globals.c hash.c header.c host.c host_address.c ip.c log.c lss.c match.c md5.c moan.c \ parse.c perl.c priv.c queue.c rda.c readconf.c receive.c retry.c rewrite.c \ - rfc2047.c route.c search.c setenv.c environment.c \ + regex_cache.c rfc2047.c route.c search.c setenv.c environment.c \ sieve.c smtp_in.c smtp_out.c spool_in.c spool_out.c std-crypto.c store.c \ string.c tls.c tlscert-gnu.c tlscert-openssl.c tls-cipher-stdname.c \ tls-gnu.c tls-openssl.c \ diff --git a/src/src/acl.c b/src/src/acl.c index fb78a7b5f..0078aca7d 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -3125,8 +3125,9 @@ int sep = -'/'; for (; cb; cb = cb->next) { - const uschar *arg; + const uschar * arg; int control_type; + BOOL textonly = FALSE; /* The message and log_message items set up messages to be used in case of rejection. They are expanded later. */ @@ -3160,7 +3161,8 @@ for (; cb; cb = cb->next) if (!conditions[cb->type].expand_at_top) arg = cb->arg; - else if (!(arg = expand_string(cb->arg))) + + else if (!(arg = expand_string_2(cb->arg, &textonly))) { if (f.expand_string_forcedfail) continue; *log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s", @@ -3217,8 +3219,8 @@ for (; cb; cb = cb->next) switch(cb->type) { case ACLC_ADD_HEADER: - setup_header(arg); - break; + setup_header(arg); + break; /* A nested ACL that returns "discard" makes sense only for an "accept" or "discard" verb. */ @@ -3232,12 +3234,12 @@ for (; cb; cb = cb->next) verbs[verb]); return ERROR; } - break; + break; case ACLC_AUTHENTICATED: rc = sender_host_authenticated ? match_isinlist(sender_host_authenticated, &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL) : FAIL; - break; + break; #ifdef EXPERIMENTAL_BRIGHTMAIL case ACLC_BMI_OPTIN: @@ -3254,21 +3256,21 @@ for (; cb; cb = cb->next) /* The true/false parsing here should be kept in sync with that used in expand.c when dealing with ECOND_BOOL so that we don't have too many different definitions of what can be a boolean. */ - if (*arg == '-' - ? Ustrspn(arg+1, "0123456789") == Ustrlen(arg+1) /* Negative number */ - : Ustrspn(arg, "0123456789") == Ustrlen(arg)) /* Digits, or empty */ - rc = (Uatoi(arg) == 0)? FAIL : OK; - else - rc = (strcmpic(arg, US"no") == 0 || - strcmpic(arg, US"false") == 0)? FAIL : - (strcmpic(arg, US"yes") == 0 || - strcmpic(arg, US"true") == 0)? OK : DEFER; - if (rc == DEFER) - *log_msgptr = string_sprintf("invalid \"condition\" value \"%s\"", arg); - break; + if (*arg == '-' + ? Ustrspn(arg+1, "0123456789") == Ustrlen(arg+1) /* Negative number */ + : Ustrspn(arg, "0123456789") == Ustrlen(arg)) /* Digits, or empty */ + rc = (Uatoi(arg) == 0)? FAIL : OK; + else + rc = (strcmpic(arg, US"no") == 0 || + strcmpic(arg, US"false") == 0)? FAIL : + (strcmpic(arg, US"yes") == 0 || + strcmpic(arg, US"true") == 0)? OK : DEFER; + if (rc == DEFER) + *log_msgptr = string_sprintf("invalid \"condition\" value \"%s\"", arg); + break; case ACLC_CONTINUE: /* Always succeeds */ - break; + break; case ACLC_CONTROL: { @@ -3647,14 +3649,14 @@ for (; cb; cb = cb->next) while ((ss = string_nextinlist(&list, &sep, NULL, 0))) if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER) rc = FAIL; /* FAIL so that the message is passed to the next ACL */ + break; } - break; #endif #ifdef WITH_CONTENT_SCAN case ACLC_DECODE: - rc = mime_decode(&arg); - break; + rc = mime_decode(&arg); + break; #endif case ACLC_DELAY: @@ -3719,44 +3721,44 @@ for (; cb; cb = cb->next) #endif } } + break; } - break; #ifndef DISABLE_DKIM case ACLC_DKIM_SIGNER: - if (dkim_cur_signer) - rc = match_isinlist(dkim_cur_signer, + if (dkim_cur_signer) + rc = match_isinlist(dkim_cur_signer, &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL); - else - rc = FAIL; - break; + else + rc = FAIL; + break; case ACLC_DKIM_STATUS: - rc = match_isinlist(dkim_verify_status, - &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL); - break; + rc = match_isinlist(dkim_verify_status, + &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL); + break; #endif #ifdef SUPPORT_DMARC case ACLC_DMARC_STATUS: - if (!f.dmarc_has_been_checked) - dmarc_process(); - f.dmarc_has_been_checked = TRUE; - /* used long way of dmarc_exim_expand_query() in case we need more - * view into the process in the future. */ - rc = match_isinlist(dmarc_exim_expand_query(DMARC_VERIFY_STATUS), - &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL); - break; + if (!f.dmarc_has_been_checked) + dmarc_process(); + f.dmarc_has_been_checked = TRUE; + /* used long way of dmarc_exim_expand_query() in case we need more + * view into the process in the future. */ + rc = match_isinlist(dmarc_exim_expand_query(DMARC_VERIFY_STATUS), + &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL); + break; #endif case ACLC_DNSLISTS: - rc = verify_check_dnsbl(where, &arg, log_msgptr); - break; + rc = verify_check_dnsbl(where, &arg, log_msgptr); + break; case ACLC_DOMAINS: - rc = match_isinlist(addr->domain, &arg, 0, &domainlist_anchor, - addr->domain_cache, MCL_DOMAIN, TRUE, CUSS &deliver_domain_data); - break; + rc = match_isinlist(addr->domain, &arg, 0, &domainlist_anchor, + addr->domain_cache, MCL_DOMAIN, TRUE, CUSS &deliver_domain_data); + break; /* The value in tls_cipher is the full cipher name, for example, TLSv1:DES-CBC3-SHA:168, whereas the values to test for are just the @@ -3765,19 +3767,20 @@ for (; cb; cb = cb->next) writing is poorly documented. */ case ACLC_ENCRYPTED: - if (tls_in.cipher == NULL) rc = FAIL; else - { - uschar *endcipher = NULL; - uschar *cipher = Ustrchr(tls_in.cipher, ':'); - if (!cipher) cipher = tls_in.cipher; else - { - endcipher = Ustrchr(++cipher, ':'); - if (endcipher) *endcipher = 0; - } - rc = match_isinlist(cipher, &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL); - if (endcipher) *endcipher = ':'; - } - break; + if (!tls_in.cipher) rc = FAIL; + else + { + uschar *endcipher = NULL; + uschar *cipher = Ustrchr(tls_in.cipher, ':'); + if (!cipher) cipher = tls_in.cipher; else + { + endcipher = Ustrchr(++cipher, ':'); + if (endcipher) *endcipher = 0; + } + rc = match_isinlist(cipher, &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL); + if (endcipher) *endcipher = ':'; + } + break; /* Use verify_check_this_host() instead of verify_check_host() so that we can pass over &host_data to catch any looked up data. Once it has been @@ -3787,17 +3790,17 @@ for (; cb; cb = cb->next) message in the same SMTP connection. */ case ACLC_HOSTS: - rc = verify_check_this_host(&arg, sender_host_cache, NULL, - sender_host_address ? sender_host_address : US"", CUSS &host_data); - if (rc == DEFER) *log_msgptr = search_error_message; - if (host_data) host_data = string_copy_perm(host_data, TRUE); - break; + rc = verify_check_this_host(&arg, sender_host_cache, NULL, + sender_host_address ? sender_host_address : US"", CUSS &host_data); + if (rc == DEFER) *log_msgptr = search_error_message; + if (host_data) host_data = string_copy_perm(host_data, TRUE); + break; case ACLC_LOCAL_PARTS: - rc = match_isinlist(addr->cc_local_part, &arg, 0, - &localpartlist_anchor, addr->localpart_cache, MCL_LOCALPART, TRUE, - CUSS &deliver_localpart_data); - break; + rc = match_isinlist(addr->cc_local_part, &arg, 0, + &localpartlist_anchor, addr->localpart_cache, MCL_LOCALPART, TRUE, + CUSS &deliver_localpart_data); + break; case ACLC_LOG_REJECT_TARGET: { @@ -3818,8 +3821,8 @@ for (; cb; cb = cb->next) } } log_reject_target = logbits; + break; } - break; case ACLC_LOGWRITE: { @@ -3850,8 +3853,8 @@ for (; cb; cb = cb->next) if (logbits == 0) logbits = LOG_MAIN; log_write(0, logbits, "%s", string_printing(s)); + break; } - break; #ifdef WITH_CONTENT_SCAN case ACLC_MALWARE: /* Run the malware backend. */ @@ -3874,55 +3877,55 @@ for (; cb; cb = cb->next) return ERROR; } - rc = malware(ss, timeout); + rc = malware(ss, textonly, timeout); if (rc == DEFER && defer_ok) rc = FAIL; /* FAIL so that the message is passed to the next ACL */ + break; } - break; case ACLC_MIME_REGEX: - rc = mime_regex(&arg); - break; + rc = mime_regex(&arg, textonly); + break; #endif case ACLC_QUEUE: - if (is_tainted(arg)) - { - *log_msgptr = string_sprintf("Tainted name '%s' for queue not permitted", - arg); - return ERROR; - } - if (Ustrchr(arg, '/')) - { - *log_msgptr = string_sprintf( - "Directory separator not permitted in queue name: '%s'", arg); - return ERROR; - } - queue_name = string_copy_perm(arg, FALSE); - break; + if (is_tainted(arg)) + { + *log_msgptr = string_sprintf("Tainted name '%s' for queue not permitted", + arg); + return ERROR; + } + if (Ustrchr(arg, '/')) + { + *log_msgptr = string_sprintf( + "Directory separator not permitted in queue name: '%s'", arg); + return ERROR; + } + queue_name = string_copy_perm(arg, FALSE); + break; case ACLC_RATELIMIT: - rc = acl_ratelimit(arg, where, log_msgptr); - break; + rc = acl_ratelimit(arg, where, log_msgptr); + break; case ACLC_RECIPIENTS: - rc = match_address_list(CUS addr->address, TRUE, TRUE, &arg, NULL, -1, 0, - CUSS &recipient_data); - break; + rc = match_address_list(CUS addr->address, TRUE, TRUE, &arg, NULL, -1, 0, + CUSS &recipient_data); + break; #ifdef WITH_CONTENT_SCAN case ACLC_REGEX: - rc = regex(&arg); - break; + rc = regex(&arg, textonly); + break; #endif case ACLC_REMOVE_HEADER: - setup_remove_header(arg); - break; + setup_remove_header(arg); + break; case ACLC_SEEN: - rc = acl_seen(arg, where, log_msgptr); - break; + rc = acl_seen(arg, where, log_msgptr); + break; case ACLC_SENDER_DOMAINS: { @@ -3931,13 +3934,13 @@ for (; cb; cb = cb->next) sdomain = sdomain ? sdomain + 1 : US""; rc = match_isinlist(sdomain, &arg, 0, &domainlist_anchor, sender_domain_cache, MCL_DOMAIN, TRUE, NULL); + break; } - break; case ACLC_SENDERS: - rc = match_address_list(CUS sender_address, TRUE, TRUE, &arg, - sender_address_cache, -1, 0, CUSS &sender_data); - break; + rc = match_address_list(CUS sender_address, TRUE, TRUE, &arg, + sender_address_cache, -1, 0, CUSS &sender_data); + break; /* Connection variables must persist forever; message variables not */ @@ -3959,8 +3962,8 @@ for (; cb; cb = cb->next) #endif acl_var_create(cb->u.varname)->data.ptr = string_copy(arg); store_pool = old_pool; + break; } - break; #ifdef WITH_CONTENT_SCAN case ACLC_SPAM: @@ -3974,22 +3977,23 @@ for (; cb; cb = cb->next) while ((ss = string_nextinlist(&list, &sep, NULL, 0))) if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER) rc = FAIL; /* FAIL so that the message is passed to the next ACL */ + break; } - break; #endif #ifdef SUPPORT_SPF case ACLC_SPF: rc = spf_process(&arg, sender_address, SPF_PROCESS_NORMAL); - break; + break; + case ACLC_SPF_GUESS: rc = spf_process(&arg, sender_address, SPF_PROCESS_GUESS); - break; + break; #endif case ACLC_UDPSEND: - rc = acl_udpsend(arg, log_msgptr); - break; + rc = acl_udpsend(arg, log_msgptr); + break; /* If the verb is WARN, discard any user message from verification, because such messages are SMTP responses, not header additions. The latter come @@ -3998,16 +4002,16 @@ for (; cb; cb = cb->next) (until something changes it). */ case ACLC_VERIFY: - rc = acl_verify(where, addr, arg, user_msgptr, log_msgptr, basic_errno); - if (*user_msgptr) - acl_verify_message = *user_msgptr; - if (verb == ACL_WARN) *user_msgptr = NULL; - break; + rc = acl_verify(where, addr, arg, user_msgptr, log_msgptr, basic_errno); + if (*user_msgptr) + acl_verify_message = *user_msgptr; + if (verb == ACL_WARN) *user_msgptr = NULL; + break; default: - log_write(0, LOG_MAIN|LOG_PANIC_DIE, "internal ACL error: unknown " - "condition %d", cb->type); - break; + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "internal ACL error: unknown " + "condition %d", cb->type); + break; } /* If a condition was negated, invert OK/FAIL. */ diff --git a/src/src/daemon.c b/src/src/daemon.c index 8e8a515e4..54725e07d 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -1132,13 +1132,43 @@ exim_exit(EXIT_SUCCESS); * Listener socket for local work prompts * *************************************************/ +ssize_t +daemon_client_sockname(struct sockaddr_un * sup, uschar ** sname) +{ +#ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS +sup->sun_path[0] = 0; /* Abstract local socket addr - Linux-specific? */ +return offsetof(struct sockaddr_un, sun_path) + 1 + + snprintf(sup->sun_path+1, sizeof(sup->sun_path)-1, "exim_%d", getpid()); +#else +*sname = string_sprintf("%s/p_%d", spool_directory, getpid()); +return offsetof(struct sockaddr_un, sun_path) + + snprintf(sup->sun_path, sizeof(sup->sun_path), "%s", sname); +#endif +} + +ssize_t +daemon_notifier_sockname(struct sockaddr_un * sup) +{ +#ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS +sup->sun_path[0] = 0; /* Abstract local socket addr - Linux-specific? */ +return offsetof(struct sockaddr_un, sun_path) + 1 + + snprintf(sup->sun_path+1, sizeof(sup->sun_path)-1, "%s", + expand_string(notifier_socket)); +#else +return offsetof(struct sockaddr_un, sun_path) + + snprintf(sup->sun_path, sizeof(sup->sun_path), "%s", + expand_string(notifier_socket)); +#endif +} + + static void daemon_notifier_socket(void) { int fd; const uschar * where; struct sockaddr_un sa_un = {.sun_family = AF_UNIX}; -int len; +ssize_t len; if (!notifier_socket || !*notifier_socket) { @@ -1163,20 +1193,15 @@ if ((fd = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0) (void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); #endif +len = daemon_notifier_sockname(&sa_un); + #ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS -sa_un.sun_path[0] = 0; /* Abstract local socket addr - Linux-specific? */ -len = offsetof(struct sockaddr_un, sun_path) + 1 - + snprintf(sa_un.sun_path+1, sizeof(sa_un.sun_path)-1, "%s", - expand_string(notifier_socket)); DEBUG(D_any) debug_printf(" @%s\n", sa_un.sun_path+1); #else /* filesystem-visible and persistent; will neeed removal */ -len = offsetof(struct sockaddr_un, sun_path) - + snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), "%s", - expand_string(notifier_socket)); DEBUG(D_any) debug_printf(" %s\n", sa_un.sun_path); #endif -if (bind(fd, (const struct sockaddr *)&sa_un, len) < 0) +if (bind(fd, (const struct sockaddr *)&sa_un, (socklen_t)len) < 0) { where = US"bind"; goto bad; } #ifdef SO_PASSCRED /* Linux */ @@ -1205,7 +1230,11 @@ bad: static uschar queuerun_msgid[MESSAGE_ID_LENGTH+1]; -/* Return TRUE if a sigalrm should be emulated */ +/* The notifier socket has something to read. Pull the message from it, decode +and do the action. + +Return TRUE if a sigalrm should be emulated */ + static BOOL daemon_notification(void) { @@ -1255,7 +1284,6 @@ for (struct cmsghdr * cp = CMSG_FIRSTHDR(&msg); { DEBUG(D_queue_run) debug_printf("%s: sender creds pid %d uid %d gid %d\n", __FUNCTION__, (int)cr->pid, (int)cr->uid, (int)cr->gid); - return FALSE; } # elif defined(LOCAL_CREDS) /* BSD-ish */ struct sockcred * cr = (struct sockcred *) CMSG_DATA(cp); @@ -1263,7 +1291,6 @@ for (struct cmsghdr * cp = CMSG_FIRSTHDR(&msg); { DEBUG(D_queue_run) debug_printf("%s: sender creds pid ??? uid %d gid %d\n", __FUNCTION__, (int)cr->sc_uid, (int)cr->sc_gid); - return FALSE; } # endif break; @@ -1294,15 +1321,18 @@ switch (buf[0]) (const struct sockaddr *)&sa_un, msg.msg_namelen) < 0) log_write(0, LOG_MAIN|LOG_PANIC, "%s: sendto: %s\n", __FUNCTION__, strerror(errno)); - return FALSE; + break; } + + case NOTIFY_REGEX: + regex_at_daemon(buf); + break; } return FALSE; } - /************************************************* * Exim Daemon Mainline * *************************************************/ diff --git a/src/src/deliver.c b/src/src/deliver.c index 8a9a174e3..725d0c872 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -7203,7 +7203,7 @@ local and remote LMTP deliveries. */ if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA = - regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE); + regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", MCS_NOFLAGS, TRUE); /* Handle local deliveries */ diff --git a/src/src/dns.c b/src/src/dns.c index 7d7ee0c04..4071c5822 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -1324,7 +1324,7 @@ dns_pattern_init(void) { if (check_dns_names_pattern[0] != 0 && !regex_check_dns_names) regex_check_dns_names = - regex_must_compile(check_dns_names_pattern, FALSE, TRUE); + regex_must_compile(check_dns_names_pattern, MCS_NOFLAGS, TRUE); } /* vi: aw ai sw=2 diff --git a/src/src/drtables.c b/src/src/drtables.c index 513ef6c4a..b2f2a4b33 100644 --- a/src/src/drtables.c +++ b/src/src/drtables.c @@ -728,8 +728,8 @@ if (!(dd = exim_opendir(LOOKUP_MODULE_DIR))) } else { - const pcre2_code *regex_islookupmod = regex_must_compile( - US"\\." DYNLIB_FN_EXT "$", FALSE, TRUE); + const pcre2_code * regex_islookupmod = regex_must_compile( + US"\\." DYNLIB_FN_EXT "$", MCS_NOFLAGS, TRUE); DEBUG(D_lookup) debug_printf("Loading lookup modules from %s\n", LOOKUP_MODULE_DIR); while ((ent = readdir(dd))) diff --git a/src/src/exim.c b/src/src/exim.c index fd01d1355..dec8de4b4 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -17,6 +17,13 @@ Also a few functions that don't naturally fit elsewhere. */ # include #endif +#ifndef _TIME_H +# include +#endif +#ifndef NO_EXECINFO +# include +#endif + #ifdef USE_GNUTLS # include # if GNUTLS_VERSION_NUMBER < 0x030103 && !defined(DISABLE_OCSP) @@ -24,10 +31,6 @@ Also a few functions that don't naturally fit elsewhere. */ # endif #endif -#ifndef _TIME_H -# include -#endif - extern void init_lookup_list(void); @@ -56,78 +59,40 @@ if (block) store_free(block); } +static void * +function_store_get(PCRE2_SIZE size, void * tag) +{ +return store_get((int)size, GET_UNTAINTED); /* loses track of taint */ +} - -/************************************************* -* Enums for cmdline interface * -*************************************************/ - -enum commandline_info { CMDINFO_NONE=0, - CMDINFO_HELP, CMDINFO_SIEVE, CMDINFO_DSCP }; +static void +function_store_nullfree(void * block, void * tag) +{ +} /************************************************* -* Compile regular expression and panic on fail * +* Enums for cmdline interface * *************************************************/ -/* This function is called when failure to compile a regular expression leads -to a panic exit. In other cases, pcre_compile() is called directly. In many -cases where this function is used, the results of the compilation are to be -placed in long-lived store, so we temporarily reset the store management -functions that PCRE uses if the use_malloc flag is set. - -Argument: - pattern the pattern to compile - caseless TRUE if caseless matching is required - use_malloc TRUE if compile into malloc store - -Returns: pointer to the compiled pattern -*/ - -const pcre2_code * -regex_must_compile(const uschar * pattern, BOOL caseless, BOOL use_malloc) -{ -size_t offset; -int options = caseless ? PCRE_COPT|PCRE2_CASELESS : PCRE_COPT; -const pcre2_code * yield; -int err; -pcre2_general_context * gctx; -pcre2_compile_context * cctx; - -if (use_malloc) - { - gctx = pcre2_general_context_create(function_store_malloc, function_store_free, NULL); - cctx = pcre2_compile_context_create(gctx); - } -else - cctx = pcre_cmp_ctx; +enum commandline_info { CMDINFO_NONE=0, + CMDINFO_HELP, CMDINFO_SIEVE, CMDINFO_DSCP }; -if (!(yield = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, options, - &err, &offset, cctx))) - { - uschar errbuf[128]; - pcre2_get_error_message(err, errbuf, sizeof(errbuf)); - log_write(0, LOG_MAIN|LOG_PANIC_DIE, "regular expression error: " - "%s at offset %ld while compiling %s", errbuf, (long)offset, pattern); - } -if (use_malloc) - { - pcre2_compile_context_free(cctx); - pcre2_general_context_free(gctx); - } -return yield; -} static void pcre_init(void) { -pcre_gen_ctx = pcre2_general_context_create(function_store_malloc, function_store_free, NULL); -pcre_cmp_ctx = pcre2_compile_context_create(pcre_gen_ctx); -pcre_mtc_ctx = pcre2_match_context_create(pcre_gen_ctx); +pcre_mlc_ctx = pcre2_general_context_create(function_store_malloc, function_store_free, NULL); +pcre_gen_ctx = pcre2_general_context_create(function_store_get, function_store_nullfree, NULL); + +pcre_mlc_cmp_ctx = pcre2_compile_context_create(pcre_mlc_ctx); +pcre_gen_cmp_ctx = pcre2_compile_context_create(pcre_gen_ctx); + +pcre_gen_mtc_ctx = pcre2_match_context_create(pcre_gen_ctx); } @@ -157,7 +122,7 @@ regex_match_and_setup(const pcre2_code * re, const uschar * subject, int options { pcre2_match_data * md = pcre2_match_data_create_from_pattern(re, pcre_gen_ctx); int res = pcre2_match(re, (PCRE2_SPTR)subject, PCRE2_ZERO_TERMINATED, 0, - PCRE_EOPT | options, md, pcre_mtc_ctx); + PCRE_EOPT | options, md, pcre_gen_mtc_ctx); BOOL yield; if ((yield = (res >= 0))) @@ -179,7 +144,7 @@ else if (res != PCRE2_ERROR_NOMATCH) DEBUG(D_any) pcre2_get_error_message(res, errbuf, sizeof(errbuf)); debug_printf_indent("pcre2: %s\n", errbuf); } -pcre2_match_data_free(md); +/* pcre2_match_data_free(md); gen ctx needs no free */ return yield; } @@ -201,13 +166,18 @@ regex_match(const pcre2_code * re, const uschar * subject, int slen, uschar ** r pcre2_match_data * md = pcre2_match_data_create(1, pcre_gen_ctx); int rc = pcre2_match(re, (PCRE2_SPTR)subject, slen >= 0 ? slen : PCRE2_ZERO_TERMINATED, - 0, PCRE_EOPT, md, pcre_mtc_ctx); + 0, PCRE_EOPT, md, pcre_gen_mtc_ctx); PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md); -if (rc < 0) - return FALSE; -if (rptr) - *rptr = string_copyn(subject + ovec[0], ovec[1] - ovec[0]); -return TRUE; +BOOL ret = FALSE; + +if (rc >= 0) + { + if (rptr) + *rptr = string_copyn(subject + ovec[0], ovec[1] - ovec[0]); + ret = TRUE; + } +/* pcre2_match_data_free(md); gen ctx needs no free */ +return ret; } @@ -261,6 +231,31 @@ exit(1); * Handler for SIGSEGV * ***********************************************/ +#define STACKDUMP_MAX 24 +static void +stackdump(void) +{ +#ifndef NO_EXECINFO +void * buf[STACKDUMP_MAX]; +char ** ss; +int nptrs = backtrace(buf, STACKDUMP_MAX); + +log_write(0, LOG_MAIN|LOG_PANIC, "backtrace\n"); +log_write(0, LOG_MAIN|LOG_PANIC, "---\n"); +if ((ss = backtrace_symbols(buf, nptrs))) + { + for (int i = 0; i < nptrs; i++) + log_write(0, LOG_MAIN|LOG_PANIC, "\t%s\n", ss[i]); + free(ss); + } +else + log_write(0, LOG_MAIN|LOG_PANIC, "backtrace_symbols: %s\n", strerror(errno)); +log_write(0, LOG_MAIN|LOG_PANIC, "---\n"); +#endif +} +#undef STACKDUMP_MAX + + static void #ifdef SA_SIGINFO segv_handler(int sig, siginfo_t * info, void * uctx) @@ -281,6 +276,7 @@ else log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (maybe attempt to write to immutable memory)"); if (process_info_len > 0) log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (%.*s)", process_info_len, process_info); +stackdump(); signal(SIGSEGV, SIG_DFL); kill(getpid(), sig); } @@ -291,6 +287,7 @@ segv_handler(int sig) log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (maybe attempt to write to immutable memory)"); if (process_info_len > 0) log_write(0, LOG_MAIN|LOG_PANIC, "SIGSEGV (%.*s)", process_info_len, process_info); +stackdump(); signal(SIGSEGV, SIG_DFL); kill(getpid(), sig); } @@ -1983,7 +1980,7 @@ this here, because the -M options check their arguments for syntactic validity using mac_ismsgid, which uses this. */ regex_ismsgid = - regex_must_compile(US"^(?:[^\\W_]{6}-){2}[^\\W_]{2}$", FALSE, TRUE); + regex_must_compile(US"^(?:[^\\W_]{6}-){2}[^\\W_]{2}$", MCS_NOFLAGS, TRUE); /* Precompile the regular expression that is used for matching an SMTP error code, possibly extended, at the start of an error message. Note that the @@ -1991,14 +1988,14 @@ terminating whitespace character is included. */ regex_smtp_code = regex_must_compile(US"^\\d\\d\\d\\s(?:\\d\\.\\d\\d?\\d?\\.\\d\\d?\\d?\\s)?", - FALSE, TRUE); + MCS_NOFLAGS, TRUE); #ifdef WHITELIST_D_MACROS /* Precompile the regular expression used to filter the content of macros given to -D for permissibility. */ regex_whitelisted_macro = - regex_must_compile(US"^[A-Za-z0-9_/.-]*$", FALSE, TRUE); + regex_must_compile(US"^[A-Za-z0-9_/.-]*$", MCS_NOFLAGS, TRUE); #endif for (i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; @@ -2216,7 +2213,7 @@ on the second character (the one after '-'), to save some effort. */ -bdf: Ditto, but in the foreground. */ case 'd': - f.daemon_listen = TRUE; + f.daemon_listen = f.daemon_scion = TRUE; if (*argrest == 'f') f.background_daemon = FALSE; else if (*argrest) badarg = TRUE; break; @@ -2476,7 +2473,7 @@ on the second character (the one after '-'), to save some effort. */ case 'w': f.inetd_wait_mode = TRUE; f.background_daemon = FALSE; - f.daemon_listen = TRUE; + f.daemon_listen = f.daemon_scion = TRUE; if (*argrest) if ((inetd_wait_timeout = readconf_readtime(argrest, 0, FALSE)) <= 0) exim_fail("exim: bad time value %s: abandoned\n", argv[i]); @@ -5003,7 +5000,7 @@ for (i = 0;;) if (gecos_pattern && gecos_name) { const pcre2_code *re; - re = regex_must_compile(gecos_pattern, FALSE, TRUE); /* Use malloc */ + re = regex_must_compile(gecos_pattern, MCS_NOFLAGS, TRUE); /* Use malloc */ if (regex_match_and_setup(re, name, 0, -1)) { @@ -5399,7 +5396,10 @@ if (host_checking) memset(sender_host_cache, 0, sizeof(sender_host_cache)); if (verify_check_host(&hosts_connection_nolog) == OK) + { BIT_CLEAR(log_selector, log_selector_size, Li_smtp_connection); + BIT_CLEAR(log_selector, log_selector_size, Li_smtp_no_mail); + } log_write(L_smtp_connection, LOG_MAIN, "%s", smtp_get_connection_info()); /* NOTE: We do *not* call smtp_log_no_mail() if smtp_start_session() fails, @@ -5588,7 +5588,10 @@ if (smtp_input) smtp_out = stdout; memset(sender_host_cache, 0, sizeof(sender_host_cache)); if (verify_check_host(&hosts_connection_nolog) == OK) + { BIT_CLEAR(log_selector, log_selector_size, Li_smtp_connection); + BIT_CLEAR(log_selector, log_selector_size, Li_smtp_no_mail); + } log_write(L_smtp_connection, LOG_MAIN, "%s", smtp_get_connection_info()); if (!smtp_start_session()) { diff --git a/src/src/expand.c b/src/src/expand.c index 36c9f423b..ffbdc14e5 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -12,9 +12,15 @@ #include "exim.h" +typedef unsigned esi_flags; +#define ESI_NOFLAGS 0 +#define ESI_BRACE_ENDS BIT(0) /* expansion should stop at } */ +#define ESI_HONOR_DOLLAR BIT(1) /* $ is meaningfull */ +#define ESI_SKIPPING BIT(2) /* value will not be needed */ + /* Recursively called function */ -static uschar *expand_string_internal(const uschar *, BOOL, const uschar **, BOOL, BOOL, BOOL *); +static uschar *expand_string_internal(const uschar *, esi_flags, const uschar **, BOOL *, BOOL *); static int_eximarith_t expanded_string_integer(const uschar *, BOOL); #ifdef STAND_ALONE @@ -686,6 +692,7 @@ static var_entry var_table[] = { { "recipient_verify_failure",vtype_stringptr,&recipient_verify_failure }, { "recipients", vtype_string_func, (void *) &fn_recipients }, { "recipients_count", vtype_int, &recipients_count }, + { "regex_cachesize", vtype_int, ®ex_cachesize },/* undocumented; devel observability */ #ifdef WITH_CONTENT_SCAN { "regex_match_string", vtype_stringptr, ®ex_match_string }, #endif @@ -1748,9 +1755,7 @@ uschar buf[16]; int fd; ssize_t len; const uschar * where; -#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS uschar * sname; -#endif if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { @@ -1758,17 +1763,9 @@ if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) return NULL; } -#ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS -sa_un.sun_path[0] = 0; /* Abstract local socket addr - Linux-specific? */ -len = offsetof(struct sockaddr_un, sun_path) + 1 - + snprintf(sa_un.sun_path+1, sizeof(sa_un.sun_path)-1, "exim_%d", getpid()); -#else -sname = string_sprintf("%s/p_%d", spool_directory, getpid()); -len = offsetof(struct sockaddr_un, sun_path) - + snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), "%s", sname); -#endif +len = daemon_client_sockname(&sa_un, &sname); -if (bind(fd, (const struct sockaddr *)&sa_un, len) < 0) +if (bind(fd, (const struct sockaddr *)&sa_un, (socklen_t)len) < 0) { where = US"bind"; goto bad; } #ifdef notdef @@ -1777,17 +1774,7 @@ debug_printf("local addr '%s%s'\n", sa_un.sun_path + (*sa_un.sun_path ? 0 : 1)); #endif -#ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS -sa_un.sun_path[0] = 0; /* Abstract local socket addr - Linux-specific? */ -len = offsetof(struct sockaddr_un, sun_path) + 1 - + snprintf(sa_un.sun_path+1, sizeof(sa_un.sun_path)-1, "%s", - expand_string(notifier_socket)); -#else -len = offsetof(struct sockaddr_un, sun_path) - + snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), "%s", - expand_string(notifier_socket)); -#endif - +len = daemon_notifier_sockname(&sa_un); if (connect(fd, (const struct sockaddr *)&sa_un, len) < 0) { where = US"connect"; goto bad2; } @@ -2114,27 +2101,33 @@ Arguments: n maximum number of substrings m minimum required sptr points to current string pointer - skipping the skipping flag + flags + skipping the skipping flag check_end if TRUE, check for final '}' name name of item, for error message resetok if not NULL, pointer to flag - write FALSE if unsafe to reset - the store. + the store + textonly_p if not NULL, pointer to bitmask of which subs were text-only + (did not change when expended) -Returns: 0 OK; string pointer updated +Returns: -1 OK; string pointer updated, but in "skipping" mode + 0 OK; string pointer updated 1 curly bracketing error (too few arguments) 2 too many arguments (only if check_end is set); message set 3 other error (expansion failure) */ static int -read_subs(uschar **sub, int n, int m, const uschar **sptr, BOOL skipping, - BOOL check_end, uschar *name, BOOL *resetok) +read_subs(uschar ** sub, int n, int m, const uschar ** sptr, esi_flags flags, + BOOL check_end, uschar * name, BOOL * resetok, unsigned * textonly_p) { -const uschar *s = *sptr; +const uschar * s = *sptr; +unsigned textonly_l = 0; Uskip_whitespace(&s); for (int i = 0; i < n; i++) { + BOOL textonly; if (*s != '{') { if (i < m) @@ -2146,11 +2139,14 @@ for (int i = 0; i < n; i++) sub[i] = NULL; break; } - if (!(sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, resetok))) + if (!(sub[i] = expand_string_internal(s+1, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags & ESI_SKIPPING, &s, resetok, + textonly_p ? &textonly : NULL))) return 3; if (*s++ != '}') return 1; + if (textonly_p && textonly) textonly_l |= BIT(i); Uskip_whitespace(&s); - } + } /*{*/ if (check_end && *s++ != '}') { if (s[-1] == '{') @@ -2163,8 +2159,9 @@ if (check_end && *s++ != '}') return 1; } +if (textonly_p) *textonly_p = textonly_l; *sptr = s; -return 0; +return flags & ESI_SKIPPING ? -1 : 0; } @@ -2523,11 +2520,11 @@ Returns: a pointer to the first character after the condition, or */ static const uschar * -eval_condition(const uschar *s, BOOL *resetok, BOOL *yield) +eval_condition(const uschar * s, BOOL * resetok, BOOL * yield) { BOOL testfor = TRUE; BOOL tempcond, combined_cond; -BOOL *subcondptr; +BOOL * subcondptr; BOOL sub2_honour_dollar = TRUE; BOOL is_forany, is_json, is_jsons; int rc, cond_type; @@ -2535,7 +2532,8 @@ int_eximarith_t num[2]; struct stat statbuf; uschar * opname; uschar name[256]; -const uschar *sub[10]; +const uschar * sub[10]; +unsigned sub_textonly = 0; for (;;) if (Uskip_whitespace(&s) == '!') { testfor = !testfor; s++; } else break; @@ -2629,8 +2627,14 @@ switch(cond_type = identify_operator(&s, &opname)) if (Uskip_whitespace(&s) != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */ - sub[0] = expand_string_internal(s+1, TRUE, &s, yield == NULL, TRUE, resetok); - if (!sub[0]) return NULL; + { + BOOL textonly; + sub[0] = expand_string_internal(s+1, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | (yield ? ESI_NOFLAGS : ESI_SKIPPING), + &s, resetok, &textonly); + if (!sub[0]) return NULL; + if (textonly) sub_textonly |= BIT(0); + } /* {-for-text-editors */ if (*s++ != '}') goto COND_FAILED_CURLY_END; @@ -2727,8 +2731,8 @@ switch(cond_type = identify_operator(&s, &opname)) Uskip_whitespace(&s); if (*s++ != '{') goto COND_FAILED_CURLY_START; /*}*/ - switch(read_subs(sub, nelem(sub), 1, - &s, yield == NULL, TRUE, name, resetok)) + switch(read_subs(sub, nelem(sub), 1, &s, + yield ? ESI_NOFLAGS : ESI_SKIPPING, TRUE, name, resetok, NULL)) { case 1: expand_string_message = US"too few arguments or bracketing " "error for acl"; @@ -2779,8 +2783,8 @@ switch(cond_type = identify_operator(&s, &opname)) uschar *sub[4]; Uskip_whitespace(&s); if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */ - switch(read_subs(sub, nelem(sub), 2, &s, yield == NULL, TRUE, name, - resetok)) + switch(read_subs(sub, nelem(sub), 2, &s, + yield ? ESI_NOFLAGS : ESI_SKIPPING, TRUE, name, resetok, NULL)) { case 1: expand_string_message = US"too few arguments or bracketing " "error for saslauthd"; @@ -2848,12 +2852,16 @@ switch(cond_type = identify_operator(&s, &opname)) for (int i = 0; i < 2; i++) { + BOOL textonly; /* Sometimes, we don't expand substrings; too many insecure configurations created using match_address{}{} and friends, where the second param includes information from untrustworthy sources. */ - BOOL honour_dollar = TRUE; - if ((i > 0) && !sub2_honour_dollar) - honour_dollar = FALSE; + /*XXX is this moot given taint-tracking? */ + + esi_flags flags = ESI_BRACE_ENDS; + + if (!(i > 0 && !sub2_honour_dollar)) flags |= ESI_HONOR_DOLLAR; + if (!yield) flags |= ESI_SKIPPING; if (Uskip_whitespace(&s) != '{') { @@ -2862,9 +2870,9 @@ switch(cond_type = identify_operator(&s, &opname)) "after \"%s\"", opname); return NULL; } - if (!(sub[i] = expand_string_internal(s+1, TRUE, &s, yield == NULL, - honour_dollar, resetok))) + if (!(sub[i] = expand_string_internal(s+1, flags, &s, resetok, &textonly))) return NULL; + if (textonly) sub_textonly |= BIT(i); DEBUG(D_expand) if (i == 1 && !sub2_honour_dollar && Ustrchr(sub[1], '$')) debug_printf_indent("WARNING: the second arg is NOT expanded," " for security reasons\n"); @@ -2944,19 +2952,11 @@ switch(cond_type = identify_operator(&s, &opname)) case ECOND_MATCH: /* Regular expression match */ { - const pcre2_code * re; - PCRE2_SIZE offset; - int err; - - if (!(re = pcre2_compile((PCRE2_SPTR)sub[1], PCRE2_ZERO_TERMINATED, - PCRE_COPT, &err, &offset, pcre_cmp_ctx))) - { - uschar errbuf[128]; - pcre2_get_error_message(err, errbuf, sizeof(errbuf)); - expand_string_message = string_sprintf("regular expression error in " - "\"%s\": %s at offset %ld", sub[1], errbuf, (long)offset); + const pcre2_code * re = regex_compile(sub[1], + sub_textonly & BIT(1) ? MCS_CACHEABLE : MCS_NOFLAGS, + &expand_string_message, pcre_gen_cmp_ctx); + if (!re) return NULL; - } tempcond = regex_match_and_setup(re, sub[0], 0, -1); break; @@ -3274,7 +3274,9 @@ switch(cond_type = identify_operator(&s, &opname)) Uskip_whitespace(&s); if (*s++ != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */ - if (!(sub[0] = expand_string_internal(s, TRUE, &s, yield == NULL, TRUE, resetok))) + if (!(sub[0] = expand_string_internal(s, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | (yield ? ESI_NOFLAGS : ESI_SKIPPING), + &s, resetok, NULL))) return NULL; /* {-for-text-editors */ if (*s++ != '}') goto COND_FAILED_CURLY_END; @@ -3362,7 +3364,8 @@ switch(cond_type = identify_operator(&s, &opname)) if (Uskip_whitespace(&s) != '{') goto COND_FAILED_CURLY_START; /* }-for-text-editors */ ourname = cond_type == ECOND_BOOL_LAX ? US"bool_lax" : US"bool"; - switch(read_subs(sub_arg, 1, 1, &s, yield == NULL, FALSE, ourname, resetok)) + switch(read_subs(sub_arg, 1, 1, &s, + yield ? ESI_NOFLAGS : ESI_SKIPPING, FALSE, ourname, resetok, NULL)) { case 1: expand_string_message = string_sprintf( "too few arguments or bracketing error for %s", @@ -3430,7 +3433,8 @@ switch(cond_type = identify_operator(&s, &opname)) uschar cksum[4]; BOOL boolvalue = FALSE; - switch(read_subs(sub, 2, 2, CUSS &s, yield == NULL, FALSE, name, resetok)) + switch(read_subs(sub, 2, 2, CUSS &s, + yield ? ESI_NOFLAGS : ESI_SKIPPING, FALSE, name, resetok, NULL)) { case 1: expand_string_message = US"too few arguments or bracketing " "error for inbound_srs"; @@ -3441,10 +3445,10 @@ switch(cond_type = identify_operator(&s, &opname)) /* Match the given local_part against the SRS-encoded pattern */ re = regex_must_compile(US"^(?i)SRS0=([^=]+)=([A-Z2-7]+)=([^=]*)=(.*)$", - TRUE, FALSE); + MCS_CASELESS | MCS_CACHEABLE, FALSE); md = pcre2_match_data_create(4+1, pcre_gen_ctx); if (pcre2_match(re, sub[0], PCRE2_ZERO_TERMINATED, 0, PCRE_EOPT, - md, pcre_mtc_ctx) < 0) + md, pcre_gen_mtc_ctx) < 0) { DEBUG(D_expand) debug_printf("no match for SRS'd local-part pattern\n"); goto srs_result; @@ -3521,6 +3525,7 @@ switch(cond_type = identify_operator(&s, &opname)) boolvalue = TRUE; srs_result: + /* pcre2_match_data_free(md); gen ctx needs no free */ if (yield) *yield = (boolvalue == testfor); return s; } @@ -3628,7 +3633,8 @@ expanded, to check their syntax, but "skipping" is set when the result is not needed - this avoids unnecessary nested lookups. Arguments: - skipping TRUE if we were skipping when this item was reached + flags + skipping TRUE if we were skipping when this item was reached yes TRUE if the first string is to be used, else use the second save_lookup a value to put back into lookup_value before the 2nd expansion sptr points to the input string pointer @@ -3644,7 +3650,7 @@ Returns: 0 OK; lookup_value has been reset to save_lookup */ static int -process_yesno(BOOL skipping, BOOL yes, uschar *save_lookup, const uschar **sptr, +process_yesno(esi_flags flags, BOOL yes, uschar *save_lookup, const uschar **sptr, gstring ** yieldptr, uschar *type, BOOL *resetok) { int rc = 0; @@ -3652,6 +3658,8 @@ const uschar *s = *sptr; /* Local value */ uschar *sub1, *sub2; const uschar * errwhere; +flags &= ESI_SKIPPING; /* Ignore all buf the skipping flag */ + /* If there are no following strings, we substitute the contents of $value for lookups and for extractions in the success case. For the ${if item, the string "true" is substituted. In the fail case, nothing is substituted for all three @@ -3661,12 +3669,12 @@ if (skip_whitespace(&s) == '}') { if (type[0] == 'i') { - if (yes && !skipping) + if (yes && !(flags & ESI_SKIPPING)) *yieldptr = string_catn(*yieldptr, US"true", 4); } else { - if (yes && lookup_value && !skipping) + if (yes && lookup_value && !(flags & ESI_SKIPPING)) *yieldptr = string_cat(*yieldptr, lookup_value); lookup_value = save_lookup; } @@ -3678,7 +3686,7 @@ if (skip_whitespace(&s) == '}') if (*s++ != '{') { - errwhere = US"'yes' part did not start with '{'"; + errwhere = US"'yes' part did not start with '{'"; /*}}*/ goto FAILED_CURLY; } @@ -3686,9 +3694,12 @@ if (*s++ != '{') want this string. Set skipping in the call in the fail case (this will always be the case if we were already skipping). */ -sub1 = expand_string_internal(s, TRUE, &s, !yes, TRUE, resetok); +sub1 = expand_string_internal(s, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | (yes ? ESI_NOFLAGS : ESI_SKIPPING), + &s, resetok, NULL); if (sub1 == NULL && (yes || !f.expand_string_forcedfail)) goto FAILED; f.expand_string_forcedfail = FALSE; + /*{{*/ if (*s++ != '}') { errwhere = US"'yes' part did not end with '}'"; @@ -3713,14 +3724,16 @@ time, forced failures are noticed only if we want the second string. We must set skipping in the nested call if we don't want this string, or if we were already skipping. */ -if (skip_whitespace(&s) == '{') +if (skip_whitespace(&s) == '{') /*}*/ { - sub2 = expand_string_internal(s+1, TRUE, &s, yes || skipping, TRUE, resetok); - if (sub2 == NULL && (!yes || !f.expand_string_forcedfail)) goto FAILED; - f.expand_string_forcedfail = FALSE; + esi_flags s_flags = ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags; + if (yes) s_flags |= ESI_SKIPPING; + sub2 = expand_string_internal(s+1, s_flags, &s, resetok, NULL); + if (!sub2 && (!yes || !f.expand_string_forcedfail)) goto FAILED; + f.expand_string_forcedfail = FALSE; /*{*/ if (*s++ != '}') { - errwhere = US"'no' part did not start with '{'"; + errwhere = US"'no' part did not start with '{'"; /*}*/ goto FAILED_CURLY; } @@ -3729,7 +3742,7 @@ if (skip_whitespace(&s) == '{') if (!yes) *yieldptr = string_cat(*yieldptr, sub2); } - + /*{{*/ /* If there is no second string, but the word "fail" is present when the use of the second string is wanted, set a flag indicating it was a forced failure rather than a syntactic error. Swallow the terminating } in case this is nested @@ -3742,9 +3755,9 @@ else if (*s != '}') s = US read_name(name, sizeof(name), s, US"_"); if (Ustrcmp(name, "fail") == 0) { - if (!yes && !skipping) + if (!yes && !(flags & ESI_SKIPPING)) { - Uskip_whitespace(&s); + Uskip_whitespace(&s); /*{{*/ if (*s++ != '}') { errwhere = US"did not close with '}' after forcedfail"; @@ -3766,7 +3779,7 @@ else if (*s != '}') /* All we have to do now is to check on the final closing brace. */ -skip_whitespace(&s); +skip_whitespace(&s); /*{{*/ if (*s++ != '}') { errwhere = US"did not close with '}'"; @@ -4445,15 +4458,17 @@ string expansion becoming too powerful. Arguments: string the string to be expanded - ket_ends true if expansion is to stop at } + flags + brace_ends expansion is to stop at } + honour_dollar TRUE if $ is to be expanded, + FALSE if it's just another character + skipping TRUE for recursive calls when the value isn't actually going + to be used (to allow for optimisation) left if not NULL, a pointer to the first character after the - expansion is placed here (typically used with ket_ends) - skipping TRUE for recursive calls when the value isn't actually going - to be used (to allow for optimisation) - honour_dollar TRUE if $ is to be expanded, - FALSE if it's just another character + expansion is placed here (typically used with brace_ends) resetok_p if not NULL, pointer to flag - write FALSE if unsafe to reset the store. + textonly_p if not NULL, pointer to flag - write bool for only-met-text Returns: NULL if expansion fails: expand_string_forcedfail is set TRUE if failure was forced @@ -4462,8 +4477,8 @@ Returns: NULL if expansion fails: */ static uschar * -expand_string_internal(const uschar *string, BOOL ket_ends, const uschar **left, - BOOL skipping, BOOL honour_dollar, BOOL *resetok_p) +expand_string_internal(const uschar * string, esi_flags flags, const uschar ** left, + BOOL *resetok_p, BOOL * textonly_p) { rmark reset_point = store_mark(); gstring * yield = string_get(Ustrlen(string) + 64); @@ -4471,7 +4486,7 @@ int item_type; const uschar * s = string; const uschar * save_expand_nstring[EXPAND_MAXN+1]; int save_expand_nlength[EXPAND_MAXN+1]; -BOOL resetok = TRUE, first = TRUE; +BOOL resetok = TRUE, first = TRUE, textonly = TRUE; expand_level++; f.expand_string_forcedfail = FALSE; @@ -4494,11 +4509,11 @@ while (*s) DEBUG(D_noutf8) debug_printf_indent("%c%s: %s\n", first ? '/' : '|', - skipping ? "---scanning" : "considering", s); + flags & ESI_SKIPPING ? "---scanning" : "considering", s); else debug_printf_indent("%s%s: %s\n", first ? UTF8_DOWN_RIGHT : UTF8_VERT_RIGHT, - skipping + flags & ESI_SKIPPING ? UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ "scanning" : "considering", s); @@ -4524,7 +4539,7 @@ while (*s) for (s = t; *s ; s++) if (*s == '\\' && s[1] == 'N') break; DEBUG(D_expand) - debug_expansion_interim(US"protected", t, (int)(s - t), skipping); + debug_expansion_interim(US"protected", t, (int)(s - t), !!(flags & ESI_SKIPPING)); yield = string_catn(yield, t, s - t); if (*s) s += 2; } @@ -4547,20 +4562,21 @@ while (*s) /* Anything other than $ is just copied verbatim, unless we are looking for a terminating } character. */ - if (ket_ends && *s == '}') break; + if (flags & ESI_BRACE_ENDS && *s == '}') break; - if (*s != '$' || !honour_dollar) + if (*s != '$' || !(flags & ESI_HONOR_DOLLAR)) { int i = 1; /*{*/ for (const uschar * t = s+1; *t && *t != '$' && *t != '}' && *t != '\\'; t++) i++; - DEBUG(D_expand) debug_expansion_interim(US"text", s, i, skipping); + DEBUG(D_expand) debug_expansion_interim(US"text", s, i, !!(flags & ESI_SKIPPING)); yield = string_catn(yield, s, i); s += i; continue; } + textonly = FALSE; /* No { after the $ - must be a plain name or a number for string match variable. There has to be a fudge for variables that are the @@ -4622,7 +4638,7 @@ while (*s) /* Variable */ - else if (!(value = find_variable(name, FALSE, skipping, &newsize))) + else if (!(value = find_variable(name, FALSE, !!(flags & ESI_SKIPPING), &newsize))) { expand_string_message = string_sprintf("unknown variable name \"%s\"", name); @@ -4722,14 +4738,13 @@ while (*s) uschar * user_msg; int rc; - switch(read_subs(sub, nelem(sub), 1, &s, skipping, TRUE, name, - &resetok)) + switch(read_subs(sub, nelem(sub), 1, &s, flags, TRUE, name, &resetok, NULL)) { + case -1: continue; /* skipping */ case 1: goto EXPAND_FAILED_CURLY; case 2: case 3: goto EXPAND_FAILED; } - if (skipping) continue; resetok = FALSE; switch(rc = eval_acl(sub, nelem(sub), &user_msg)) @@ -4758,13 +4773,13 @@ while (*s) { uschar * sub_arg[1]; - switch(read_subs(sub_arg, nelem(sub_arg), 1, &s, skipping, TRUE, name, - &resetok)) + switch(read_subs(sub_arg, nelem(sub_arg), 1, &s, flags, TRUE, name, &resetok, NULL)) { case 1: goto EXPAND_FAILED_CURLY; case 2: case 3: goto EXPAND_FAILED; } + /*XXX no skipping-optimisation? */ yield = string_append(yield, 3, US"Authentication-Results: ", sub_arg[0], US"; none"); @@ -4802,14 +4817,14 @@ while (*s) uschar * save_lookup_value = lookup_value; Uskip_whitespace(&s); - if (!(next_s = eval_condition(s, &resetok, skipping ? NULL : &cond))) + if (!(next_s = eval_condition(s, &resetok, flags & ESI_SKIPPING ? NULL : &cond))) goto EXPAND_FAILED; /* message already set */ DEBUG(D_expand) { - debug_expansion_interim(US"condition", s, (int)(next_s - s), skipping); + debug_expansion_interim(US"condition", s, (int)(next_s - s), !!(flags & ESI_SKIPPING)); debug_expansion_interim(US"result", - cond ? US"true" : US"false", cond ? 4 : 5, skipping); + cond ? US"true" : US"false", cond ? 4 : 5, !!(flags & ESI_SKIPPING)); } s = next_s; @@ -4818,12 +4833,12 @@ while (*s) function that is also used by ${lookup} and ${extract} and ${run}. */ switch(process_yesno( - skipping, /* were previously skipping */ - cond, /* success/failure indicator */ - lookup_value, /* value to reset for string2 */ - &s, /* input pointer */ - &yield, /* output pointer */ - US"if", /* condition type */ + flags, /* were previously skipping */ + cond, /* success/failure indicator */ + lookup_value, /* value to reset for string2 */ + &s, /* input pointer */ + &yield, /* output pointer */ + US"if", /* condition type */ &resetok)) { case 1: goto EXPAND_FAILED; /* when all is well, the */ @@ -4845,13 +4860,13 @@ while (*s) uschar *sub_arg[3]; uschar *encoded; - switch(read_subs(sub_arg, nelem(sub_arg), 1, &s, skipping, TRUE, name, - &resetok)) + switch(read_subs(sub_arg, nelem(sub_arg), 1, &s, flags, TRUE, name, &resetok, NULL)) { case 1: goto EXPAND_FAILED_CURLY; case 2: case 3: goto EXPAND_FAILED; } + /*XXX no skipping-optimisation? */ if (!sub_arg[1]) /* One argument */ { @@ -4867,7 +4882,7 @@ while (*s) goto EXPAND_FAILED; } - if (skipping) continue; + if (flags & ESI_SKIPPING) continue; if (!(encoded = imap_utf7_encode(sub_arg[0], headers_charset, sub_arg[1][0], sub_arg[2], &expand_string_message))) @@ -4906,7 +4921,8 @@ while (*s) if (Uskip_whitespace(&s) == '{') /*}*/ { - key = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok); + key = expand_string_internal(s+1, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags, &s, &resetok, NULL); if (!key) goto EXPAND_FAILED; /*{{*/ if (*s++ != '}') { @@ -4976,7 +4992,8 @@ while (*s) expand_string_message = US"missing '{' for lookup file-or-query arg"; goto EXPAND_FAILED_CURLY; /*}}*/ } - if (!(filename = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok))) + if (!(filename = expand_string_internal(s+1, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags, &s, &resetok, NULL))) goto EXPAND_FAILED; /*{{*/ if (*s++ != '}') @@ -5007,7 +5024,7 @@ while (*s) since new variables will have been set. Note that at the end of this "lookup" section, the old numeric variables are restored. */ - if (skipping) + if (flags & ESI_SKIPPING) lookup_value = NULL; else { @@ -5033,12 +5050,12 @@ while (*s) function that is also used by ${if} and ${extract}. */ switch(process_yesno( - skipping, /* were previously skipping */ - lookup_value != NULL, /* success/failure indicator */ - save_lookup_value, /* value to reset for string2 */ - &s, /* input pointer */ - &yield, /* output pointer */ - US"lookup", /* condition type */ + flags, /* were previously skipping */ + lookup_value != NULL, /* success/failure indicator */ + save_lookup_value, /* value to reset for string2 */ + &s, /* input pointer */ + &yield, /* output pointer */ + US"lookup", /* condition type */ &resetok)) { case 1: goto EXPAND_FAILED; /* when all is well, the */ @@ -5051,7 +5068,7 @@ while (*s) restore_expand_strings(save_expand_nmax, save_expand_nstring, save_expand_nlength); - if (skipping) continue; + if (flags & ESI_SKIPPING) continue; break; } @@ -5079,18 +5096,15 @@ while (*s) goto EXPAND_FAILED; } - switch(read_subs(sub_arg, EXIM_PERL_MAX_ARGS + 1, 1, &s, skipping, TRUE, - name, &resetok)) + switch(read_subs(sub_arg, EXIM_PERL_MAX_ARGS + 1, 1, &s, flags, TRUE, + name, &resetok, NULL)) { + case -1: continue; /* If skipping, we don't actually do anything */ case 1: goto EXPAND_FAILED_CURLY; case 2: case 3: goto EXPAND_FAILED; } - /* If skipping, we don't actually do anything */ - - if (skipping) continue; - /* Start the interpreter if necessary */ if (!opt_perl_started) @@ -5150,16 +5164,14 @@ while (*s) { uschar * sub_arg[3], * p, * domain; - switch(read_subs(sub_arg, 3, 2, &s, skipping, TRUE, name, &resetok)) + switch(read_subs(sub_arg, 3, 2, &s, flags, TRUE, name, &resetok, NULL)) { + case -1: continue; /* If skipping, we don't actually do anything */ case 1: goto EXPAND_FAILED_CURLY; case 2: case 3: goto EXPAND_FAILED; } - /* If skipping, we don't actually do anything */ - if (skipping) continue; - /* sub_arg[0] is the address */ if ( !(domain = Ustrrchr(sub_arg[0],'@')) || domain == sub_arg[0] || Ustrlen(domain) == 1) @@ -5208,30 +5220,21 @@ while (*s) gstring * g; const pcre2_code * re; - /* TF: Ugliness: We want to expand parameter 1 first, then set - up expansion variables that are used in the expansion of - parameter 2. So we clone the string for the first - expansion, where we only expand parameter 1. - - PH: Actually, that isn't necessary. The read_subs() function is - designed to work this way for the ${if and ${lookup expansions. I've - tidied the code. - */ /*}}*/ - /* Reset expansion variables */ prvscheck_result = NULL; prvscheck_address = NULL; prvscheck_keynum = NULL; - switch(read_subs(sub_arg, 1, 1, &s, skipping, FALSE, name, &resetok)) + switch(read_subs(sub_arg, 1, 1, &s, flags, FALSE, name, &resetok, NULL)) { case 1: goto EXPAND_FAILED_CURLY; case 2: case 3: goto EXPAND_FAILED; } - re = regex_must_compile(US"^prvs\\=([0-9])([0-9]{3})([A-F0-9]{6})\\=(.+)\\@(.+)$", - TRUE,FALSE); + re = regex_must_compile( + US"^prvs\\=([0-9])([0-9]{3})([A-F0-9]{6})\\=(.+)\\@(.+)$", + MCS_CASELESS | MCS_CACHEABLE, FALSE); if (regex_match_and_setup(re,sub_arg[0],0,-1)) { @@ -5241,11 +5244,14 @@ while (*s) uschar * hash = string_copyn(expand_nstring[3],expand_nlength[3]); uschar * domain = string_copyn(expand_nstring[5],expand_nlength[5]); - DEBUG(D_expand) debug_printf_indent("prvscheck localpart: %s\n", local_part); - DEBUG(D_expand) debug_printf_indent("prvscheck key number: %s\n", key_num); - DEBUG(D_expand) debug_printf_indent("prvscheck daystamp: %s\n", daystamp); - DEBUG(D_expand) debug_printf_indent("prvscheck hash: %s\n", hash); - DEBUG(D_expand) debug_printf_indent("prvscheck domain: %s\n", domain); + DEBUG(D_expand) + { + debug_printf_indent("prvscheck localpart: %s\n", local_part); + debug_printf_indent("prvscheck key number: %s\n", key_num); + debug_printf_indent("prvscheck daystamp: %s\n", daystamp); + debug_printf_indent("prvscheck hash: %s\n", hash); + debug_printf_indent("prvscheck domain: %s\n", domain); + } /* Set up expansion variables */ g = string_cat (NULL, local_part); @@ -5255,7 +5261,7 @@ while (*s) prvscheck_keynum = string_copy(key_num); /* Now expand the second argument */ - switch(read_subs(sub_arg, 1, 1, &s, skipping, FALSE, name, &resetok)) + switch(read_subs(sub_arg, 1, 1, &s, flags, FALSE, name, &resetok, NULL)) { case 1: goto EXPAND_FAILED_CURLY; case 2: @@ -5266,7 +5272,6 @@ while (*s) p = prvs_hmac_sha1(prvscheck_address, sub_arg[0], prvscheck_keynum, daystamp); - if (!p) { expand_string_message = US"hmac-sha1 conversion failed"; @@ -5309,7 +5314,7 @@ while (*s) /* Now expand the final argument. We leave this till now so that it can include $prvscheck_result. */ - switch(read_subs(sub_arg, 1, 0, &s, skipping, TRUE, name, &resetok)) + switch(read_subs(sub_arg, 1, 0, &s, flags, TRUE, name, &resetok, NULL)) { case 1: goto EXPAND_FAILED_CURLY; case 2: @@ -5330,14 +5335,14 @@ while (*s) We need to make sure all subs are expanded first, so as to skip over the entire item. */ - switch(read_subs(sub_arg, 2, 1, &s, skipping, TRUE, name, &resetok)) + switch(read_subs(sub_arg, 2, 1, &s, flags, TRUE, name, &resetok, NULL)) { case 1: goto EXPAND_FAILED_CURLY; case 2: case 3: goto EXPAND_FAILED; } - if (skipping) continue; + if (flags & ESI_SKIPPING) continue; break; } @@ -5354,7 +5359,7 @@ while (*s) goto EXPAND_FAILED; } - switch(read_subs(sub_arg, 2, 1, &s, skipping, TRUE, name, &resetok)) + switch(read_subs(sub_arg, 2, 1, &s, flags, TRUE, name, &resetok, NULL)) { case 1: goto EXPAND_FAILED_CURLY; case 2: @@ -5363,7 +5368,7 @@ while (*s) /* If skipping, we don't actually do anything */ - if (skipping) continue; + if (flags & ESI_SKIPPING) continue; /* Open the file and read it */ @@ -5395,7 +5400,7 @@ while (*s) /* Read up to 4 arguments, but don't do the end of item check afterwards, because there may be a string for expansion on failure. */ - switch(read_subs(sub_arg, 4, 2, &s, skipping, FALSE, name, &resetok)) + switch(read_subs(sub_arg, 4, 2, &s, flags, FALSE, name, &resetok, NULL)) { case 1: goto EXPAND_FAILED_CURLY; case 2: /* Won't occur: no end check */ @@ -5405,7 +5410,7 @@ while (*s) /* If skipping, we don't actually do anything. Otherwise, arrange to connect to either an IP or a Unix socket. */ - if (!skipping) + if (!(flags & ESI_SKIPPING)) { int stype = search_findtype(US"readsock", 8); gstring * g = NULL; @@ -5483,7 +5488,8 @@ while (*s) if (*s == '{') /*}*/ { - if (!expand_string_internal(s+1, TRUE, &s, TRUE, TRUE, &resetok)) + if (!expand_string_internal(s+1, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | ESI_SKIPPING, &s, &resetok, NULL)) goto EXPAND_FAILED; /*{*/ if (*s++ != '}') { /*{*/ @@ -5499,7 +5505,7 @@ while (*s) expand_string_message = US"missing '}' closing readsocket"; goto EXPAND_FAILED_CURLY; } - if (skipping) continue; + if (flags & ESI_SKIPPING) continue; break; /* Come here on failure to create socket, connect socket, write to the @@ -5509,7 +5515,8 @@ while (*s) SOCK_FAIL: if (*s != '{') goto EXPAND_FAILED; /*}*/ DEBUG(D_any) debug_printf("%s\n", expand_string_message); - if (!(arg = expand_string_internal(s+1, TRUE, &s, FALSE, TRUE, &resetok))) + if (!(arg = expand_string_internal(s+1, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR, &s, &resetok, NULL))) goto EXPAND_FAILED; yield = string_cat(yield, arg); /*{*/ if (*s++ != '}') @@ -5560,14 +5567,15 @@ while (*s) s++; if (late_expand) /* this is the default case */ - { + { /*{*/ int n = Ustrcspn(s, "}"); - arg = skipping ? NULL : string_copyn(s, n); + arg = flags & ESI_SKIPPING ? NULL : string_copyn(s, n); s += n; } else { - if (!(arg = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok))) + if (!(arg = expand_string_internal(s, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags, &s, &resetok, NULL))) goto EXPAND_FAILED; Uskip_whitespace(&s); } @@ -5578,7 +5586,7 @@ while (*s) goto EXPAND_FAILED_CURLY; } - if (skipping) /* Just pretend it worked when we're skipping */ + if (flags & ESI_SKIPPING) /* Just pretend it worked when we're skipping */ { runrc = 0; lookup_value = NULL; @@ -5652,19 +5660,19 @@ while (*s) /* Process the yes/no strings; $value may be useful in both cases */ switch(process_yesno( - skipping, /* were previously skipping */ - runrc == 0, /* success/failure indicator */ - lookup_value, /* value to reset for string2 */ - &s, /* input pointer */ - &yield, /* output pointer */ - US"run", /* condition type */ + flags, /* were previously skipping */ + runrc == 0, /* success/failure indicator */ + lookup_value, /* value to reset for string2 */ + &s, /* input pointer */ + &yield, /* output pointer */ + US"run", /* condition type */ &resetok)) { case 1: goto EXPAND_FAILED; /* when all is well, the */ case 2: goto EXPAND_FAILED_CURLY; /* returned value is 0 */ } - if (skipping) continue; + if (flags & ESI_SKIPPING) continue; break; } @@ -5676,8 +5684,9 @@ while (*s) int o2m; uschar * sub[3]; - switch(read_subs(sub, 3, 3, &s, skipping, TRUE, name, &resetok)) + switch(read_subs(sub, 3, 3, &s, flags, TRUE, name, &resetok, NULL)) { + case -1: continue; /* skipping */ case 1: goto EXPAND_FAILED_CURLY; case 2: case 3: goto EXPAND_FAILED; @@ -5696,7 +5705,6 @@ while (*s) } } - if (skipping) continue; break; } @@ -5717,9 +5725,10 @@ while (*s) Ensure that sub[2] is set in the ${length } case. */ sub[2] = NULL; - switch(read_subs(sub, (item_type == EITEM_LENGTH)? 2:3, 2, &s, skipping, - TRUE, name, &resetok)) + switch(read_subs(sub, item_type == EITEM_LENGTH ? 2:3, 2, &s, flags, + TRUE, name, &resetok, NULL)) { + case -1: continue; /* skipping */ case 1: goto EXPAND_FAILED_CURLY; case 2: case 3: goto EXPAND_FAILED; @@ -5760,7 +5769,6 @@ while (*s) if (!ret) goto EXPAND_FAILED; yield = string_catn(yield, ret, len); - if (skipping) continue; break; } @@ -5793,15 +5801,14 @@ while (*s) uschar innerkey[MAX_HASHBLOCKLEN]; uschar outerkey[MAX_HASHBLOCKLEN]; - switch (read_subs(sub, 3, 3, &s, skipping, TRUE, name, &resetok)) + switch (read_subs(sub, 3, 3, &s, flags, TRUE, name, &resetok, NULL)) { + case -1: continue; /* skipping */ case 1: goto EXPAND_FAILED_CURLY; case 2: case 3: goto EXPAND_FAILED; } - if (skipping) continue; - if (Ustrcmp(sub[0], "md5") == 0) { type = HMAC_MD5; @@ -5881,32 +5888,29 @@ while (*s) { const pcre2_code * re; int moffset, moffsetextra, slen; - PCRE2_SIZE roffset; pcre2_match_data * md; - int err, emptyopt; + int emptyopt; uschar * subject, * sub[3]; int save_expand_nmax = save_expand_strings(save_expand_nstring, save_expand_nlength); + unsigned sub_textonly = 0; - switch(read_subs(sub, 3, 3, &s, skipping, TRUE, name, &resetok)) + switch(read_subs(sub, 3, 3, &s, flags, TRUE, name, &resetok, &sub_textonly)) { + case -1: continue; /* skipping */ case 1: goto EXPAND_FAILED_CURLY; case 2: case 3: goto EXPAND_FAILED; } - /*XXX no handling of skipping? */ /* Compile the regular expression */ - if (!(re = pcre2_compile((PCRE2_SPTR)sub[1], PCRE2_ZERO_TERMINATED, - PCRE_COPT, &err, &roffset, pcre_cmp_ctx))) - { - uschar errbuf[128]; - pcre2_get_error_message(err, errbuf, sizeof(errbuf)); - expand_string_message = string_sprintf("regular expression error in " - "\"%s\": %s at offset %ld", sub[1], errbuf, (long)roffset); + re = regex_compile(sub[1], + sub_textonly & BIT(1) ? MCS_CACHEABLE : MCS_NOFLAGS, + &expand_string_message, pcre_gen_cmp_ctx); + if (!re) goto EXPAND_FAILED; - } + md = pcre2_match_data_create(EXPAND_MAXN + 1, pcre_gen_ctx); /* Now run a loop to do the substitutions as often as necessary. It ends @@ -5922,7 +5926,7 @@ while (*s) { PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md); int n = pcre2_match(re, (PCRE2_SPTR)subject, slen, moffset + moffsetextra, - PCRE_EOPT | emptyopt, md, pcre_mtc_ctx); + PCRE_EOPT | emptyopt, md, pcre_gen_mtc_ctx); uschar * insert; /* No match - if we previously set PCRE_NOTEMPTY after a null match, this @@ -5984,9 +5988,9 @@ while (*s) /* All done - restore numerical variables. */ + /* pcre2_match_data_free(md); gen ctx needs no free */ restore_expand_strings(save_expand_nmax, save_expand_nstring, save_expand_nlength); - if (skipping) continue; break; } @@ -6021,11 +6025,12 @@ while (*s) available (eg. $item) hence cannot decide on numeric vs. keyed. Read a maximum of 5 arguments (including the yes/no) */ - if (skipping) + if (flags & ESI_SKIPPING) { for (int j = 5; j > 0 && *s == '{'; j--) /*'}'*/ { - if (!expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok)) + if (!expand_string_internal(s+1, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags, &s, &resetok, NULL)) goto EXPAND_FAILED; /*'{'*/ if (*s++ != '}') { @@ -6052,7 +6057,8 @@ while (*s) { if (Uskip_whitespace(&s) == '{') /*'}'*/ { - if (!(sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok))) + if (!(sub[i] = expand_string_internal(s+1, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags, &s, &resetok, NULL))) goto EXPAND_FAILED; /*'{'*/ if (*s++ != '}') { @@ -6111,7 +6117,7 @@ while (*s) /* Extract either the numbered or the keyed substring into $value. If skipping, just pretend the extraction failed. */ - if (skipping) + if (flags & ESI_SKIPPING) lookup_value = NULL; else switch (fmt) { @@ -6205,12 +6211,12 @@ while (*s) be yes/no strings, as for lookup or if. */ switch(process_yesno( - skipping, /* were previously skipping */ - lookup_value != NULL, /* success/failure indicator */ - save_lookup_value, /* value to reset for string2 */ - &s, /* input pointer */ - &yield, /* output pointer */ - US"extract", /* condition type */ + flags, /* were previously skipping */ + lookup_value != NULL, /* success/failure indicator */ + save_lookup_value, /* value to reset for string2 */ + &s, /* input pointer */ + &yield, /* output pointer */ + US"extract", /* condition type */ &resetok)) { case 1: goto EXPAND_FAILED; /* when all is well, the */ @@ -6222,7 +6228,7 @@ while (*s) restore_expand_strings(save_expand_nmax, save_expand_nstring, save_expand_nlength); - if (skipping) continue; + if (flags & ESI_SKIPPING) continue; break; } @@ -6246,7 +6252,8 @@ while (*s) goto EXPAND_FAILED_CURLY; } - sub[i] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok); + sub[i] = expand_string_internal(s+1, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags, &s, &resetok, NULL); if (!sub[i]) goto EXPAND_FAILED; /*{{*/ if (*s++ != '}') { @@ -6271,7 +6278,7 @@ while (*s) while (len > 0 && isspace(p[len-1])) len--; p[len] = 0; - if (!*p && !skipping) + if (!*p && !(flags & ESI_SKIPPING)) { expand_string_message = US"first argument of \"listextract\" must " "not be empty"; @@ -6297,18 +6304,18 @@ while (*s) /* Extract the numbered element into $value. If skipping, just pretend the extraction failed. */ - lookup_value = skipping ? NULL : expand_getlistele(field_number, sub[1]); + lookup_value = flags & ESI_SKIPPING ? NULL : expand_getlistele(field_number, sub[1]); /* If no string follows, $value gets substituted; otherwise there can be yes/no strings, as for lookup or if. */ switch(process_yesno( - skipping, /* were previously skipping */ - lookup_value != NULL, /* success/failure indicator */ - save_lookup_value, /* value to reset for string2 */ - &s, /* input pointer */ - &yield, /* output pointer */ - US"listextract", /* condition type */ + flags, /* were previously skipping */ + lookup_value != NULL, /* success/failure indicator */ + save_lookup_value, /* value to reset for string2 */ + &s, /* input pointer */ + &yield, /* output pointer */ + US"listextract", /* condition type */ &resetok)) { case 1: goto EXPAND_FAILED; /* when all is well, the */ @@ -6320,15 +6327,16 @@ while (*s) restore_expand_strings(save_expand_nmax, save_expand_nstring, save_expand_nlength); - if (skipping) continue; + if (flags & ESI_SKIPPING) continue; break; } case EITEM_LISTQUOTE: { uschar * sub[2]; - switch(read_subs(sub, 2, 2, &s, skipping, TRUE, name, &resetok)) + switch(read_subs(sub, 2, 2, &s, flags, TRUE, name, &resetok, NULL)) { + case -1: continue; /* skipping */ case 1: goto EXPAND_FAILED_CURLY; case 2: case 3: goto EXPAND_FAILED; @@ -6339,7 +6347,6 @@ while (*s) yield = string_catn(yield, sub[1], 1); } else yield = string_catn(yield, US" ", 1); - if (skipping) continue; break; } @@ -6356,7 +6363,8 @@ while (*s) expand_string_message = US"missing '{' for field arg of certextract"; goto EXPAND_FAILED_CURLY; /*}*/ } - sub[0] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok); + sub[0] = expand_string_internal(s+1, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags, &s, &resetok, NULL); if (!sub[0]) goto EXPAND_FAILED; /*{{*/ if (*s++ != '}') { @@ -6388,7 +6396,8 @@ while (*s) "be a certificate variable"; goto EXPAND_FAILED; } - sub[1] = expand_string_internal(s+1, TRUE, &s, skipping, FALSE, &resetok); + sub[1] = expand_string_internal(s+1, + ESI_BRACE_ENDS | flags & ESI_SKIPPING, &s, &resetok, NULL); if (!sub[1]) goto EXPAND_FAILED; /*{{*/ if (*s++ != '}') { @@ -6396,7 +6405,7 @@ while (*s) goto EXPAND_FAILED_CURLY; } - if (skipping) + if (flags & ESI_SKIPPING) lookup_value = NULL; else { @@ -6404,12 +6413,12 @@ while (*s) if (*expand_string_message) goto EXPAND_FAILED; } switch(process_yesno( - skipping, /* were previously skipping */ - lookup_value != NULL, /* success/failure indicator */ - save_lookup_value, /* value to reset for string2 */ - &s, /* input pointer */ - &yield, /* output pointer */ - US"certextract", /* condition type */ + flags, /* were previously skipping */ + lookup_value != NULL, /* success/failure indicator */ + save_lookup_value, /* value to reset for string2 */ + &s, /* input pointer */ + &yield, /* output pointer */ + US"certextract", /* condition type */ &resetok)) { case 1: goto EXPAND_FAILED; /* when all is well, the */ @@ -6418,7 +6427,7 @@ while (*s) restore_expand_strings(save_expand_nmax, save_expand_nstring, save_expand_nlength); - if (skipping) continue; + if (flags & ESI_SKIPPING) continue; break; } #endif /*DISABLE_TLS*/ @@ -6443,7 +6452,8 @@ while (*s) goto EXPAND_FAILED_CURLY; /*}*/ } - if (!(list = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok))) + if (!(list = expand_string_internal(s, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags, &s, &resetok, NULL))) goto EXPAND_FAILED; /*{{*/ if (*s++ != '}') { @@ -6461,7 +6471,8 @@ while (*s) expand_string_message = US"missing '{' for second arg of reduce"; goto EXPAND_FAILED_CURLY; /*}*/ } - t = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok); + t = expand_string_internal(s, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags, &s, &resetok, NULL); if (!t) goto EXPAND_FAILED; lookup_value = t; /*{{*/ if (*s++ != '}') @@ -6488,7 +6499,8 @@ while (*s) the normal internal expansion function. */ if (item_type != EITEM_FILTER) - temp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok); + temp = expand_string_internal(s, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | ESI_SKIPPING, &s, &resetok, NULL); else if ((temp = eval_condition(expr, &resetok, NULL))) s = temp; @@ -6519,7 +6531,7 @@ while (*s) /* If we are skipping, we can now just move on to the next item. When processing for real, we perform the iteration. */ - if (skipping) continue; + if (flags & ESI_SKIPPING) continue; while ((iterate_item = string_nextinlist(&list, &sep, NULL, 0))) { *outsep = (uschar)sep; /* Separator as a string */ @@ -6550,7 +6562,8 @@ while (*s) else { - uschar * t = expand_string_internal(expr, TRUE, NULL, skipping, TRUE, &resetok); + uschar * t = expand_string_internal(expr, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags, NULL, &resetok, NULL); temp = t; if (!temp) { @@ -6622,7 +6635,7 @@ while (*s) /* Restore preserved $item */ iterate_item = save_iterate_item; - if (skipping) continue; + if (flags & ESI_SKIPPING) continue; break; } @@ -6641,7 +6654,8 @@ while (*s) goto EXPAND_FAILED_CURLY; /*}*/ } - srclist = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok); + srclist = expand_string_internal(s, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags, &s, &resetok, NULL); if (!srclist) goto EXPAND_FAILED; /*{{*/ if (*s++ != '}') { @@ -6656,7 +6670,8 @@ while (*s) goto EXPAND_FAILED_CURLY; /*}*/ } - cmp = expand_string_internal(s, TRUE, &s, skipping, FALSE, &resetok); + cmp = expand_string_internal(s, + ESI_BRACE_ENDS | flags & ESI_SKIPPING, &s, &resetok, NULL); if (!cmp) goto EXPAND_FAILED; /*{{*/ if (*s++ != '}') { @@ -6691,7 +6706,8 @@ while (*s) } xtract = s; - if (!(tmp = expand_string_internal(s, TRUE, &s, TRUE, TRUE, &resetok))) + if (!(tmp = expand_string_internal(s, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | ESI_SKIPPING, &s, &resetok, NULL))) goto EXPAND_FAILED; xtract = string_copyn(xtract, s - xtract); /*{{*/ @@ -6707,7 +6723,7 @@ while (*s) goto EXPAND_FAILED; } - if (skipping) continue; + if (flags & ESI_SKIPPING) continue; while ((srcitem = string_nextinlist(&srclist, &sep, NULL, 0))) { @@ -6718,8 +6734,8 @@ while (*s) /* extract field for comparisons */ iterate_item = srcitem; - if ( !(srcfield = expand_string_internal(xtract, FALSE, NULL, FALSE, - TRUE, &resetok)) + if ( !(srcfield = expand_string_internal(xtract, + ESI_HONOR_DOLLAR, NULL, &resetok, NULL)) || !*srcfield) { expand_string_message = string_sprintf( @@ -6824,18 +6840,15 @@ while (*s) goto EXPAND_FAILED; } - switch(read_subs(argv, EXPAND_DLFUNC_MAX_ARGS + 2, 2, &s, skipping, - TRUE, name, &resetok)) + switch(read_subs(argv, EXPAND_DLFUNC_MAX_ARGS + 2, 2, &s, flags, + TRUE, name, &resetok, NULL)) { + case -1: continue; /* skipping */ case 1: goto EXPAND_FAILED_CURLY; case 2: case 3: goto EXPAND_FAILED; } - /* If skipping, we don't actually do anything */ - - if (skipping) continue; - /* Look up the dynamically loaded object handle in the tree. If it isn't found, dlopen() the file and put the handle in the tree for next time. */ @@ -6901,7 +6914,8 @@ while (*s) if (Uskip_whitespace(&s) != '{') /*}*/ goto EXPAND_FAILED; - key = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok); + key = expand_string_internal(s+1, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags, &s, &resetok, NULL); if (!key) goto EXPAND_FAILED; /*{{*/ if (*s++ != '}') { @@ -6912,18 +6926,18 @@ while (*s) lookup_value = US getenv(CS key); switch(process_yesno( - skipping, /* were previously skipping */ - lookup_value != NULL, /* success/failure indicator */ - save_lookup_value, /* value to reset for string2 */ - &s, /* input pointer */ - &yield, /* output pointer */ - US"env", /* condition type */ + flags, /* were previously skipping */ + lookup_value != NULL, /* success/failure indicator */ + save_lookup_value, /* value to reset for string2 */ + &s, /* input pointer */ + &yield, /* output pointer */ + US"env", /* condition type */ &resetok)) { case 1: goto EXPAND_FAILED; /* when all is well, the */ case 2: goto EXPAND_FAILED_CURLY; /* returned value is 0 */ } - if (skipping) continue; + if (flags & ESI_SKIPPING) continue; break; } @@ -6936,13 +6950,13 @@ while (*s) gstring * g = NULL; BOOL quoted = FALSE; - switch (read_subs(sub, 3, 3, CUSS &s, skipping, TRUE, name, &resetok)) + switch (read_subs(sub, 3, 3, CUSS &s, flags, TRUE, name, &resetok, NULL)) { + case -1: continue; /* skipping */ case 1: goto EXPAND_FAILED_CURLY; case 2: case 3: goto EXPAND_FAILED; } - if (skipping) continue; if (sub[1] && *(sub[1])) { @@ -7024,7 +7038,7 @@ while (*s) DEBUG(D_expand) if (yield && (start > 0 || *s)) /* only if not the sole expansion of the line */ debug_expansion_interim(US"item-res", - yield->s + start, yield->ptr - start, skipping); + yield->s + start, yield->ptr - start, !!(flags & ESI_SKIPPING)); continue; NOT_ITEM: ; @@ -7070,8 +7084,8 @@ NOT_ITEM: ; if (s[1] == '$') { const uschar * s1 = s; - sub = expand_string_internal(s+2, TRUE, &s1, skipping, - FALSE, &resetok); + sub = expand_string_internal(s+2, + ESI_BRACE_ENDS | flags & ESI_SKIPPING, &s1, &resetok, NULL); if (!sub) goto EXPAND_FAILED; /*{*/ if (*s1 != '}') { /*{*/ @@ -7089,7 +7103,8 @@ NOT_ITEM: ; /*FALLTHROUGH*/ #endif default: - sub = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok); + sub = expand_string_internal(s+1, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags, &s, &resetok, NULL); if (!sub) goto EXPAND_FAILED; s++; break; @@ -7101,7 +7116,7 @@ NOT_ITEM: ; for the existence of $sender_host_address before trying to mask it. For other operations, doing them may not fail, but it is a waste of time. */ - if (skipping && c >= 0) continue; + if (flags & ESI_SKIPPING && c >= 0) continue; /* Otherwise, switch on the operator type. After handling go back to the main loop top. */ @@ -7186,7 +7201,8 @@ NOT_ITEM: ; case EOP_EXPAND: { - uschar *expanded = expand_string_internal(sub, FALSE, NULL, skipping, TRUE, &resetok); + uschar *expanded = expand_string_internal(sub, + ESI_HONOR_DOLLAR | flags & ESI_SKIPPING, NULL, &resetok, NULL); if (!expanded) { expand_string_message = @@ -8177,7 +8193,7 @@ NOT_ITEM: ; debug_printf_indent("|-----op-res: %.*s\n", i, s); if (tainted) { - debug_printf_indent("%s \\__", skipping ? "| " : " "); + debug_printf_indent("%s \\__", flags & ESI_SKIPPING ? "| " : " "); debug_print_taint(yield->s); } } @@ -8189,7 +8205,7 @@ NOT_ITEM: ; if (tainted) { debug_printf_indent("%s", - skipping + flags & ESI_SKIPPING ? UTF8_VERT " " : " " UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ); debug_print_taint(yield->s); } @@ -8223,7 +8239,7 @@ NOT_ITEM: ; reset_point = store_mark(); g = store_get(sizeof(gstring), GET_UNTAINTED); /* alloc _before_ calling find_variable() */ } - if (!(value = find_variable(name, FALSE, skipping, &newsize))) + if (!(value = find_variable(name, FALSE, !!(flags & ESI_SKIPPING), &newsize))) { expand_string_message = string_sprintf("unknown variable in \"${%s}\"", name); @@ -8251,11 +8267,11 @@ NOT_ITEM: ; goto EXPAND_FAILED; } -/* If we hit the end of the string when ket_ends is set, there is a missing +/* If we hit the end of the string when brace_ends is set, there is a missing terminating brace. */ -if (ket_ends && !*s) - { +if (flags & ESI_BRACE_ENDS && !*s) + { /*{{*/ expand_string_message = malformed_header ? US"missing } at end of string - could be header name not terminated by colon" : US"missing } at end of string"; @@ -8285,13 +8301,13 @@ DEBUG(D_expand) { debug_printf_indent("|--expanding: %.*s\n", (int)(s - string), string); debug_printf_indent("%sresult: %s\n", - skipping ? "|-----" : "\\_____", yield->s); + flags & ESI_SKIPPING ? "|-----" : "\\_____", yield->s); if (tainted) { - debug_printf_indent("%s \\__", skipping ? "| " : " "); + debug_printf_indent("%s \\__", flags & ESI_SKIPPING ? "| " : " "); debug_print_taint(yield->s); } - if (skipping) + if (flags & ESI_SKIPPING) debug_printf_indent("\\___skipping: result is not used\n"); } else @@ -8301,20 +8317,21 @@ DEBUG(D_expand) (int)(s - string), string); debug_printf_indent("%s" UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ "result: %s\n", - skipping ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT, + flags & ESI_SKIPPING ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT, yield->s); if (tainted) { debug_printf_indent("%s", - skipping + flags & ESI_SKIPPING ? UTF8_VERT " " : " " UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ); debug_print_taint(yield->s); } - if (skipping) + if (flags & ESI_SKIPPING) debug_printf_indent(UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ "skipping: result is not used\n"); } } +if (textonly_p) *textonly_p = textonly; expand_level--; return yield->s; @@ -8363,16 +8380,20 @@ return NULL; } + /* This is the external function call. Do a quick check for any expansion metacharacters, and if there are none, just return the input string. -Argument: the string to be expanded +Arguments + the string to be expanded + optional pointer for return boolean indicating no-dynamic-expansions + Returns: the expanded string, or NULL if expansion failed; if failure was due to a lookup deferring, search_find_defer will be TRUE */ const uschar * -expand_cstring(const uschar * string) +expand_string_2(const uschar * string, BOOL * textonly_p) { if (Ustrpbrk(string, "$\\") != NULL) { @@ -8382,19 +8403,22 @@ if (Ustrpbrk(string, "$\\") != NULL) f.search_find_defer = FALSE; malformed_header = FALSE; store_pool = POOL_MAIN; - s = expand_string_internal(string, FALSE, NULL, FALSE, TRUE, NULL); + s = expand_string_internal(string, ESI_HONOR_DOLLAR, NULL, NULL, textonly_p); store_pool = old_pool; return s; } +if (textonly_p) *textonly_p = TRUE; return string; } +const uschar * +expand_cstring(const uschar * string) +{ return expand_string_2(string, NULL); } uschar * expand_string(uschar * string) -{ -return US expand_cstring(CUS string); -} +{ return US expand_string_2(CUS string, NULL); } + diff --git a/src/src/filter.c b/src/src/filter.c index ad017e567..cc4af230e 100644 --- a/src/src/filter.c +++ b/src/src/filter.c @@ -1424,213 +1424,203 @@ Returns: TRUE if the condition is met */ static BOOL -test_condition(condition_block *c, BOOL toplevel) +test_condition(condition_block * c, BOOL toplevel) { -BOOL yield = FALSE; -const uschar *exp[2], * p, * pp; +BOOL yield = FALSE, textonly_re; +const uschar * exp[2], * p, * pp; int val[2]; -int i; -if (c == NULL) return TRUE; /* does this ever occur? */ +if (!c) return TRUE; /* does this ever occur? */ switch (c->type) { case cond_and: - yield = test_condition(c->left.c, FALSE) && - *error_pointer == NULL && - test_condition(c->right.c, FALSE); - break; + yield = test_condition(c->left.c, FALSE) && + *error_pointer == NULL && + test_condition(c->right.c, FALSE); + break; case cond_or: - yield = test_condition(c->left.c, FALSE) || - (*error_pointer == NULL && - test_condition(c->right.c, FALSE)); - break; + yield = test_condition(c->left.c, FALSE) || + (*error_pointer == NULL && + test_condition(c->right.c, FALSE)); + break; - /* The personal test is meaningless in a system filter. The tests are now in - a separate function (so Sieve can use them). However, an Exim filter does not - scan Cc: (hence the FALSE argument). */ + /* The personal test is meaningless in a system filter. The tests are now in + a separate function (so Sieve can use them). However, an Exim filter does not + scan Cc: (hence the FALSE argument). */ case cond_personal: - yield = f.system_filtering? FALSE : filter_personal(c->left.a, FALSE); - break; + yield = f.system_filtering? FALSE : filter_personal(c->left.a, FALSE); + break; case cond_delivered: - yield = filter_delivered; - break; + yield = filter_delivered; + break; - /* Only TRUE if a message is actually being processed; FALSE for address - testing and verification. */ + /* Only TRUE if a message is actually being processed; FALSE for address + testing and verification. */ case cond_errormsg: - yield = message_id[0] != 0 && - (sender_address == NULL || sender_address[0] == 0); - break; + yield = message_id[0] != 0 && + (sender_address == NULL || sender_address[0] == 0); + break; - /* Only FALSE if a message is actually being processed; TRUE for address - and filter testing and verification. */ + /* Only FALSE if a message is actually being processed; TRUE for address + and filter testing and verification. */ case cond_firsttime: - yield = filter_test != FTEST_NONE || message_id[0] == 0 || f.deliver_firsttime; - break; + yield = filter_test != FTEST_NONE || message_id[0] == 0 || f.deliver_firsttime; + break; - /* Only TRUE if a message is actually being processed; FALSE for address - testing and verification. */ + /* Only TRUE if a message is actually being processed; FALSE for address + testing and verification. */ case cond_manualthaw: - yield = message_id[0] != 0 && f.deliver_manual_thaw; - break; + yield = message_id[0] != 0 && f.deliver_manual_thaw; + break; - /* The foranyaddress condition loops through a list of addresses */ + /* The foranyaddress condition loops through a list of addresses */ case cond_foranyaddress: - p = c->left.u; - if (!(pp = expand_cstring(p))) - { - *error_pointer = string_sprintf("failed to expand \"%s\" in " - "filter file: %s", p, expand_string_message); - return FALSE; - } + p = c->left.u; + if (!(pp = expand_cstring(p))) + { + *error_pointer = string_sprintf("failed to expand \"%s\" in " + "filter file: %s", p, expand_string_message); + return FALSE; + } - yield = FALSE; - f.parse_allow_group = TRUE; /* Allow group syntax */ + yield = FALSE; + f.parse_allow_group = TRUE; /* Allow group syntax */ - while (*pp) - { - uschar *error; - int start, end, domain; - uschar * s; + while (*pp) + { + uschar *error; + int start, end, domain; + uschar * s; - p = parse_find_address_end(pp, FALSE); - s = string_copyn(pp, p - pp); + p = parse_find_address_end(pp, FALSE); + s = string_copyn(pp, p - pp); - filter_thisaddress = - parse_extract_address(s, &error, &start, &end, &domain, FALSE); + filter_thisaddress = + parse_extract_address(s, &error, &start, &end, &domain, FALSE); - if (filter_thisaddress) - { - if ((filter_test != FTEST_NONE && debug_selector != 0) || - (debug_selector & D_filter) != 0) - { - indent(); - debug_printf_indent("Extracted address %s\n", filter_thisaddress); - } - yield = test_condition(c->right.c, FALSE); - } + if (filter_thisaddress) + { + if ((filter_test != FTEST_NONE && debug_selector != 0) || + (debug_selector & D_filter) != 0) + { + indent(); + debug_printf_indent("Extracted address %s\n", filter_thisaddress); + } + yield = test_condition(c->right.c, FALSE); + } - if (yield) break; - if (!*p) break; - pp = p + 1; - } + if (yield) break; + if (!*p) break; + pp = p + 1; + } - f.parse_allow_group = FALSE; /* Reset group syntax flags */ - f.parse_found_group = FALSE; - break; + f.parse_allow_group = FALSE; /* Reset group syntax flags */ + f.parse_found_group = FALSE; + break; - /* All other conditions have left and right values that need expanding; - on error, it doesn't matter what value is returned. */ + /* All other conditions have left and right values that need expanding; + on error, it doesn't matter what value is returned. */ - default: - p = c->left.u; - for (i = 0; i < 2; i++) - { - if (!(exp[i] = expand_cstring(p))) + default: + p = c->left.u; + for (int i = 0; i < 2; i++) { - *error_pointer = string_sprintf("failed to expand \"%s\" in " - "filter file: %s", p, expand_string_message); - return FALSE; + if (!(exp[i] = expand_string_2(p, &textonly_re))) + { + *error_pointer = string_sprintf("failed to expand \"%s\" in " + "filter file: %s", p, expand_string_message); + return FALSE; + } + p = c->right.u; } - p = c->right.u; - } - /* Inner switch for the different cases */ - - switch(c->type) - { - case cond_is: - yield = strcmpic(exp[0], exp[1]) == 0; - break; + /* Inner switch for the different cases */ - case cond_IS: - yield = Ustrcmp(exp[0], exp[1]) == 0; - break; - - case cond_contains: - yield = strstric_c(exp[0], exp[1], FALSE) != NULL; - break; + switch(c->type) + { + case cond_is: + yield = strcmpic(exp[0], exp[1]) == 0; + break; - case cond_CONTAINS: - yield = Ustrstr(exp[0], exp[1]) != NULL; - break; + case cond_IS: + yield = Ustrcmp(exp[0], exp[1]) == 0; + break; - case cond_begins: - yield = strncmpic(exp[0], exp[1], Ustrlen(exp[1])) == 0; - break; + case cond_contains: + yield = strstric_c(exp[0], exp[1], FALSE) != NULL; + break; - case cond_BEGINS: - yield = Ustrncmp(exp[0], exp[1], Ustrlen(exp[1])) == 0; - break; + case cond_CONTAINS: + yield = Ustrstr(exp[0], exp[1]) != NULL; + break; - case cond_ends: - case cond_ENDS: - { - int len = Ustrlen(exp[1]); - const uschar *s = exp[0] + Ustrlen(exp[0]) - len; - yield = s < exp[0] - ? FALSE - : (c->type == cond_ends ? strcmpic(s, exp[1]) : Ustrcmp(s, exp[1])) == 0; - } - break; + case cond_begins: + yield = strncmpic(exp[0], exp[1], Ustrlen(exp[1])) == 0; + break; - case cond_matches: - case cond_MATCHES: - { - const pcre2_code *re; - int err; - PCRE2_SIZE offset; + case cond_BEGINS: + yield = Ustrncmp(exp[0], exp[1], Ustrlen(exp[1])) == 0; + break; - if ((filter_test != FTEST_NONE && debug_selector != 0) || - (debug_selector & D_filter) != 0) + case cond_ends: + case cond_ENDS: { - debug_printf_indent("Match expanded arguments:\n"); - debug_printf_indent(" Subject = %s\n", exp[0]); - debug_printf_indent(" Pattern = %s\n", exp[1]); + int len = Ustrlen(exp[1]); + const uschar *s = exp[0] + Ustrlen(exp[0]) - len; + yield = s < exp[0] + ? FALSE + : (c->type == cond_ends ? strcmpic(s, exp[1]) : Ustrcmp(s, exp[1])) == 0; + break; } - if (!(re = pcre2_compile((PCRE2_SPTR)exp[1], PCRE2_ZERO_TERMINATED, - PCRE_COPT | (c->type == cond_matches ? PCRE2_CASELESS : 0), - &err, &offset, pcre_cmp_ctx))) + case cond_matches: + case cond_MATCHES: { - uschar errbuf[128]; - pcre2_get_error_message(err, errbuf, sizeof(errbuf)); - *error_pointer = string_sprintf("error while compiling " - "regular expression \"%s\": %s at offset %ld", - exp[1], errbuf, (long)offset); - return FALSE; - } + const pcre2_code * re; + mcs_flags flags = textonly_re ? MCS_CACHEABLE : MCS_NOFLAGS; - yield = regex_match_and_setup(re, exp[0], PCRE_EOPT, -1); - break; - } + if ((filter_test != FTEST_NONE && debug_selector != 0) || + (debug_selector & D_filter) != 0) + { + debug_printf_indent("Match expanded arguments:\n"); + debug_printf_indent(" Subject = %s\n", exp[0]); + debug_printf_indent(" Pattern = %s\n", exp[1]); + } - /* For above and below, convert the strings to numbers */ + if (c->type == cond_matches) flags |= MCS_CASELESS; + if (!(re = regex_compile(exp[1], flags, error_pointer, pcre_gen_cmp_ctx))) + return FALSE; - case cond_above: - case cond_below: - for (i = 0; i < 2; i++) - { - val[i] = get_number(exp[i], &yield); - if (!yield) - { - *error_pointer = string_sprintf("malformed numerical string \"%s\"", - exp[i]); - return FALSE; - } + yield = regex_match_and_setup(re, exp[0], PCRE_EOPT, -1); + break; + } + + /* For above and below, convert the strings to numbers */ + + case cond_above: + case cond_below: + for (int i = 0; i < 2; i++) + { + val[i] = get_number(exp[i], &yield); + if (!yield) + { + *error_pointer = string_sprintf("malformed numerical string \"%s\"", + exp[i]); + return FALSE; + } + } + yield = c->type == cond_above ? (val[0] > val[1]) : (val[0] < val[1]); + break; } - yield = (c->type == cond_above)? (val[0] > val[1]) : (val[0] < val[1]); break; - } - break; } if ((filter_test != FTEST_NONE && debug_selector != 0) || @@ -2356,7 +2346,7 @@ while (commands) commands = commands->next; } -return filter_delivered? FF_DELIVERED : FF_NOTDELIVERED; +return filter_delivered ? FF_DELIVERED : FF_NOTDELIVERED; } diff --git a/src/src/functions.h b/src/src/functions.h index 224666cb1..4caae346d 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -182,6 +182,10 @@ extern BOOL cutthrough_predata(void); extern void release_cutthrough_connection(const uschar *); extern void daemon_go(void); +#ifndef COMPILE_UTILITY +extern ssize_t daemon_client_sockname(struct sockaddr_un *, uschar **); +extern ssize_t daemon_notifier_sockname(struct sockaddr_un *); +#endif #ifdef EXPERIMENTAL_DCC extern int dcc_process(uschar **); @@ -260,6 +264,7 @@ extern int exp_bool(address_item *addr, extern BOOL expand_check_condition(uschar *, uschar *, uschar *); extern uschar *expand_file_big_buffer(const uschar *); extern uschar *expand_string(uschar *); /* public, cannot make const */ +extern const uschar *expand_string_2(const uschar *, BOOL *); extern const uschar *expand_cstring(const uschar *); /* ... so use this one */ extern uschar *expand_getkeyed(const uschar *, const uschar *); @@ -332,7 +337,7 @@ extern BOOL macro_read_assignment(uschar *); extern uschar *macros_expand(int, int *, BOOL *); extern void mainlog_close(void); #ifdef WITH_CONTENT_SCAN -extern int malware(const uschar *, int); +extern int malware(const uschar *, BOOL, int); extern int malware_in_file(uschar *); extern void malware_init(void); extern gstring * malware_show_supported(gstring *); @@ -345,7 +350,7 @@ extern int match_check_list(const uschar **, int, tree_node **, unsigned int const uschar *, const uschar **); extern int match_isinlist(const uschar *, const uschar **, int, tree_node **, unsigned int *, int, BOOL, const uschar **); -extern int match_check_string(const uschar *, const uschar *, int, BOOL, BOOL, BOOL, +extern int match_check_string(const uschar *, const uschar *, int, mcs_flags, const uschar **); extern void message_start(void); @@ -360,7 +365,7 @@ extern int mime_acl_check(uschar *acl, FILE *f, struct mime_boundary_context *, uschar **, uschar **); extern int mime_decode(const uschar **); extern ssize_t mime_decode_base64(FILE *, FILE *, uschar *); -extern int mime_regex(const uschar **); +extern int mime_regex(const uschar **, BOOL); extern void mime_set_anomaly(int); #endif extern uschar *moan_check_errorcopy(uschar *); @@ -433,11 +438,14 @@ extern BOOL receive_msg(BOOL); extern int_eximarith_t receive_statvfs(BOOL, int *); extern void receive_swallow_smtp(void); #ifdef WITH_CONTENT_SCAN -extern int regex(const uschar **); +extern int regex(const uschar **, BOOL); #endif +extern void regex_at_daemon(const uschar *); extern BOOL regex_match(const pcre2_code *, const uschar *, int, uschar **); extern BOOL regex_match_and_setup(const pcre2_code *, const uschar *, int, int); -extern const pcre2_code *regex_must_compile(const uschar *, BOOL, BOOL); +extern const pcre2_code *regex_compile(const uschar *, mcs_flags, uschar **, + pcre2_compile_context *); +extern const pcre2_code *regex_must_compile(const uschar *, mcs_flags, BOOL); extern void retry_add_item(address_item *, uschar *, int); extern BOOL retry_check_address(const uschar *, host_item *, uschar *, BOOL, uschar **, uschar **); @@ -1221,6 +1229,7 @@ pid_t pid; DEBUG(D_any) debug_printf("%s forking for %s\n", process_purpose, purpose); if ((pid = fork()) == 0) { + f.daemon_listen = FALSE; process_purpose = purpose; DEBUG(D_any) debug_printf("postfork: %s\n", purpose); } diff --git a/src/src/globals.c b/src/src/globals.c index ff246feb4..c95d24b47 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -234,6 +234,7 @@ struct global_flags f = .continue_more = FALSE, .daemon_listen = FALSE, + .daemon_scion = FALSE, .debug_daemon = FALSE, .deliver_firsttime = FALSE, .deliver_force = FALSE, @@ -391,7 +392,7 @@ BOOL proxy_session = FALSE; #endif #ifndef DISABLE_QUEUE_RAMP -BOOL queue_fast_ramp = FALSE; +BOOL queue_fast_ramp = TRUE; #endif BOOL queue_list_requires_admin = TRUE; BOOL queue_only = FALSE; @@ -1207,9 +1208,12 @@ uid_t originator_uid; uschar *override_local_interfaces = NULL; uschar *override_pid_file_path = NULL; +BOOL panic_coredump = FALSE; pcre2_general_context * pcre_gen_ctx = NULL; -pcre2_compile_context * pcre_cmp_ctx = NULL; -pcre2_match_context * pcre_mtc_ctx = NULL; +pcre2_compile_context * pcre_gen_cmp_ctx = NULL; +pcre2_match_context * pcre_gen_mtc_ctx = NULL; +pcre2_general_context * pcre_mlc_ctx = NULL; +pcre2_compile_context * pcre_mlc_cmp_ctx = NULL; uschar *percent_hack_domains = NULL; uschar *pid_file_path = US PID_FILE_PATH @@ -1313,6 +1317,7 @@ const pcre2_code *regex_SIZE = NULL; #ifndef DISABLE_PIPE_CONNECT const pcre2_code *regex_EARLY_PIPE = NULL; #endif +int regex_cachesize = 0; const pcre2_code *regex_ismsgid = NULL; const pcre2_code *regex_smtp_code = NULL; const uschar *regex_vars[REGEX_VARS]; @@ -1323,7 +1328,7 @@ const pcre2_code *regex_whitelisted_macro = NULL; uschar *regex_match_string = NULL; #endif int remote_delivery_count = 0; -int remote_max_parallel = 2; +int remote_max_parallel = 4; uschar *remote_sort_domains = NULL; int retry_data_expire = 7*24*60*60; int retry_interval_max = 24*60*60; diff --git a/src/src/globals.h b/src/src/globals.h index fe099e402..c9ef5e484 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -199,6 +199,7 @@ extern struct global_flags { BOOL continue_more :1; /* Flag more addresses waiting */ BOOL daemon_listen :1; /* True if listening required */ + BOOL daemon_scion :1; /* Ancestor proc is daemon, and not re-exec'd */ BOOL debug_daemon :1; /* Debug the daemon process only */ BOOL deliver_firsttime :1; /* True for first delivery attempt */ BOOL deliver_force :1; /* TRUE if delivery was forced */ @@ -792,9 +793,12 @@ extern uid_t originator_uid; /* Uid of ditto */ extern uschar *override_local_interfaces; /* Value of -oX argument */ extern uschar *override_pid_file_path; /* Value of -oP argument */ +extern BOOL panic_coredump; /* SEGV rather than exit, on LOG_PANIC_DIE */ extern pcre2_general_context * pcre_gen_ctx; /* pcre memory management */ -extern pcre2_compile_context * pcre_cmp_ctx; -extern pcre2_match_context * pcre_mtc_ctx; +extern pcre2_compile_context * pcre_gen_cmp_ctx; +extern pcre2_match_context * pcre_gen_mtc_ctx; +extern pcre2_general_context * pcre_mlc_ctx; +extern pcre2_compile_context * pcre_mlc_cmp_ctx; extern uschar *percent_hack_domains; /* Local domains for which '% operates */ extern uschar *pid_file_path; /* For writing daemon pids */ @@ -895,6 +899,7 @@ extern const pcre2_code *regex_SIZE; /* For recognizing SIZE settings */ #ifndef DISABLE_PIPE_CONNECT extern const pcre2_code *regex_EARLY_PIPE; /* For recognizing PIPE_CONNCT */ #endif +extern int regex_cachesize; /* number of entries */ extern const pcre2_code *regex_ismsgid; /* Compiled r.e. for message ID */ extern const pcre2_code *regex_smtp_code; /* For recognizing SMTP codes */ extern const uschar *regex_vars[]; /* $regexN variables */ diff --git a/src/src/header.c b/src/src/header.c index 898d8d5c4..7ef59ff53 100644 --- a/src/src/header.c +++ b/src/src/header.c @@ -368,7 +368,7 @@ Returns: cond if the header exists and contains one of the strings; /* First we have a local subroutine to handle a single pattern */ static BOOL -one_pattern_match(uschar *name, int slen, BOOL has_addresses, uschar *pattern) +one_pattern_match(uschar * name, int slen, BOOL has_addresses, uschar * pattern) { BOOL yield = FALSE; const pcre2_code *re = NULL; @@ -376,7 +376,7 @@ const pcre2_code *re = NULL; /* If the pattern is a regex, compile it. Bomb out if compiling fails; these patterns are all constructed internally and should be valid. */ -if (*pattern == '^') re = regex_must_compile(pattern, TRUE, FALSE); +if (*pattern == '^') re = regex_must_compile(pattern, MCS_CASELESS, FALSE); /* Scan for the required header(s) and scan each one */ @@ -443,7 +443,7 @@ return yield; /* The externally visible interface */ BOOL -header_match(uschar *name, BOOL has_addresses, BOOL cond, string_item *strings, +header_match(uschar * name, BOOL has_addresses, BOOL cond, string_item * strings, int count, ...) { va_list ap; diff --git a/src/src/host.c b/src/src/host.c index e43b507e5..fed9f4b5f 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -363,80 +363,6 @@ while ((name = string_nextinlist(&list, &sep, NULL, 0))) } - - - -/************************************************* -* Extract port from address string * -*************************************************/ - -/* In the -oMa and -oMi options, a host plus port is given as an IP address -followed by a dot and a port number. This function decodes this. - -An alternative format for the -oMa and -oMi options is [ip address]:port which -is what Exim uses for output, because it seems to becoming commonly used, -whereas the dot form confuses some programs/people. So we recognize that form -too. - -The spool file used to use the first form, but this breaks with a v4mapped ipv6 -hybrid, because the parsing here is not clever. So for spool we now use the -second form. - -Argument: - address points to the string; if there is a port, the '.' in the string - is overwritten with zero to terminate the address; if the string - is in the [xxx]:ppp format, the address is shifted left and the - brackets are removed - -Returns: 0 if there is no port, else the port number. If there's a syntax - error, leave the incoming address alone, and return 0. -*/ - -int -host_address_extract_port(uschar *address) -{ -int port = 0; -uschar *endptr; - -/* Handle the "bracketed with colon on the end" format */ - -if (*address == '[') - { - uschar *rb = address + 1; - while (*rb != 0 && *rb != ']') rb++; - if (*rb++ == 0) return 0; /* Missing ]; leave invalid address */ - if (*rb == ':') - { - port = Ustrtol(rb + 1, &endptr, 10); - if (*endptr != 0) return 0; /* Invalid port; leave invalid address */ - } - else if (*rb != 0) return 0; /* Bad syntax; leave invalid address */ - memmove(address, address + 1, rb - address - 2); - rb[-2] = 0; - } - -/* Handle the "dot on the end" format */ - -else - { - int skip = -3; /* Skip 3 dots in IPv4 addresses */ - address--; - while (*(++address) != 0) - { - int ch = *address; - if (ch == ':') skip = 0; /* Skip 0 dots in IPv6 addresses */ - else if (ch == '.' && skip++ >= 0) break; - } - if (*address == 0) return 0; - port = Ustrtol(address + 1, &endptr, 10); - if (*endptr != 0) return 0; /* Invalid port; leave invalid address */ - *address = 0; - } - -return port; -} - - /************************************************* * Get port from a host item's name * *************************************************/ diff --git a/src/src/host_address.c b/src/src/host_address.c new file mode 100644 index 000000000..9e6f958be --- /dev/null +++ b/src/src/host_address.c @@ -0,0 +1,80 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) University of Cambridge 1995 - 2018 */ +/* See the file NOTICE for conditions of use and distribution. */ + +#include "exim.h" + +/************************************************* +* Extract port from address string * +*************************************************/ + +/* In the spool file, and in the -oMa and -oMi options, a host plus port is +given as an IP address followed by a dot and a port number. This function +decodes this. + +An alternative format for the -oMa and -oMi options is [ip address]:port which +is what Exim 4 uses for output, because it seems to becoming commonly used, +whereas the dot form confuses some programs/people. So we recognize that form +too. + +Argument: + address points to the string; if there is a port, the '.' in the string + is overwritten with zero to terminate the address; if the string + is in the [xxx]:ppp format, the address is shifted left and the + brackets are removed + +Returns: 0 if there is no port, else the port number. If there's a syntax + error, leave the incoming address alone, and return 0. +*/ + +int +host_address_extract_port(uschar * address) +{ +int port = 0; +uschar *endptr; + +/* Handle the "bracketed with colon on the end" format */ + +if (*address == '[') + { + uschar *rb = address + 1; + while (*rb != 0 && *rb != ']') rb++; + if (*rb++ == 0) return 0; /* Missing ]; leave invalid address */ + if (*rb == ':') + { + port = Ustrtol(rb + 1, &endptr, 10); + if (*endptr != 0) return 0; /* Invalid port; leave invalid address */ + } + else if (*rb != 0) return 0; /* Bad syntax; leave invalid address */ + memmove(address, address + 1, rb - address - 2); + rb[-2] = 0; + } + +/* Handle the "dot on the end" format */ + +else + { + int skip = -3; /* Skip 3 dots in IPv4 addresses */ + address--; + while (*(++address) != 0) + { + int ch = *address; + if (ch == ':') skip = 0; /* Skip 0 dots in IPv6 addresses */ + else if (ch == '.' && skip++ >= 0) break; + } + if (*address == 0) return 0; + port = Ustrtol(address + 1, &endptr, 10); + if (*endptr != 0) return 0; /* Invalid port; leave invalid address */ + *address = 0; + } + +return port; +} + +/* vi: aw ai sw=2 +*/ +/* End of host.c */ diff --git a/src/src/log.c b/src/src/log.c index 8ca973f2d..a46d523db 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -1278,7 +1278,10 @@ if (flags & LOG_PANIC) /* Give up if the DIE flag is set */ if ((flags & LOG_PANIC_DIE) != LOG_PANIC) - die(NULL, US"Unexpected failure, please try later"); + if (panic_coredump) + kill(getpid(), SIGSEGV); /* deliberate trap */ + else + die(NULL, US"Unexpected failure, please try later"); } } diff --git a/src/src/macros.h b/src/src/macros.h index fa89de12d..adbe6a267 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -1111,7 +1111,17 @@ should not be one active. */ #define NOTIFIER_SOCKET_NAME "exim_daemon_notify" -#define NOTIFY_MSG_QRUN 1 /* Notify message types */ +/* Notify message types */ +#define NOTIFY_MSG_QRUN 1 #define NOTIFY_QUEUE_SIZE_REQ 2 +#define NOTIFY_REGEX 3 + +/* Flags for match_check_string() */ +typedef unsigned mcs_flags; +#define MCS_NOFLAGS 0 +#define MCS_PARTIAL BIT(0) /* permit partial- search types */ +#define MCS_CASELESS BIT(1) /* caseless matching where possible */ +#define MCS_AT_SPECIAL BIT(2) /* recognize @, @[], etc. */ +#define MCS_CACHEABLE BIT(3) /* no dynamic expansions used for pattern */ /* End of macros.h */ diff --git a/src/src/malware.c b/src/src/malware.c index 4719a5d61..8b5ec27c4 100644 --- a/src/src/malware.c +++ b/src/src/malware.c @@ -299,39 +299,29 @@ return sock; } static const pcre2_code * -m_pcre_compile(const uschar * re, uschar ** errstr) +m_pcre_compile(const uschar * re, BOOL cacheable, uschar ** errstr) { -int err; -PCRE2_SIZE roffset; -const pcre2_code * cre; - -if (!(cre = pcre2_compile((PCRE2_SPTR)re, PCRE2_ZERO_TERMINATED, - PCRE_COPT, &err, &roffset, pcre_cmp_ctx))) - { - uschar errbuf[128]; - pcre2_get_error_message(err, errbuf, sizeof(errbuf)); - *errstr= string_sprintf("regular expression error in '%s': %s at offset %ld", - re, errbuf, (long)roffset); - } -return cre; +return regex_compile(re, cacheable ? MCS_CACHEABLE : MCS_NOFLAGS, errstr, + pcre_gen_cmp_ctx); } uschar * m_pcre_exec(const pcre2_code * cre, uschar * text) { pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx); -int i = pcre2_match(cre, text, PCRE2_ZERO_TERMINATED, 0, 0, md, pcre_mtc_ctx); +int i = pcre2_match(cre, text, PCRE2_ZERO_TERMINATED, 0, 0, md, pcre_gen_mtc_ctx); PCRE2_UCHAR * substr = NULL; PCRE2_SIZE slen; if (i >= 2) /* Got it */ - pcre2_substring_get_bynumber(md, 1, &substr, &slen); + pcre2_substring_get_bynumber(md, 1, &substr, &slen); /* uses same ctx as md */ +/* pcre2_match_data_free(md); gen ctx needs no free */ return US substr; } static const pcre2_code * m_pcre_nextinlist(const uschar ** list, int * sep, - char * listerr, uschar ** errstr) + BOOL cacheable, char * listerr, uschar ** errstr) { const uschar * list_ele; const pcre2_code * cre = NULL; @@ -342,7 +332,7 @@ else { DEBUG(D_acl) debug_printf_indent("%15s%10s'%s'\n", "", "RE: ", string_printing(list_ele)); - cre = m_pcre_compile(CUS list_ele, errstr); + cre = m_pcre_compile(CUS list_ele, cacheable, errstr); } return cre; } @@ -568,6 +558,7 @@ is via malware(), or there's malware_in_file() used for testing/debugging. Arguments: malware_re match condition for "malware=" + cacheable the RE did not use any dynamic elements during expansion scan_filename the file holding the email to be scanned, if we're faking this up for the -bmalware test, else NULL timeout if nonzero, non-default timeoutl @@ -576,11 +567,12 @@ Returns: Exim message processing code (OK, FAIL, DEFER, ...) where true means malware was found (condition applies) */ static int -malware_internal(const uschar * malware_re, const uschar * scan_filename, - int timeout) +malware_internal(const uschar * malware_re, BOOL cacheable, + const uschar * scan_filename, int timeout) { int sep = 0; const uschar *av_scanner_work = av_scanner; +BOOL av_scanner_textonly; uschar *scanner_name; unsigned long mbox_size; FILE *mbox_file; @@ -607,30 +599,30 @@ the name), so we can close it right away. Get the directory too. */ eml_dir = string_copyn(eml_filename, Ustrrchr(eml_filename, '/') - eml_filename); /* parse 1st option */ -if (strcmpic(malware_re, US"false") == 0 || Ustrcmp(malware_re,"0") == 0) +if (strcmpic(malware_re, US"false") == 0 || Ustrcmp(malware_re, "0") == 0) return FAIL; /* explicitly no matching */ /* special cases (match anything except empty) */ -if ( strcmpic(malware_re,US"true") == 0 - || Ustrcmp(malware_re,"*") == 0 - || Ustrcmp(malware_re,"1") == 0 +if ( strcmpic(malware_re, US"true") == 0 + || Ustrcmp(malware_re, "*") == 0 + || Ustrcmp(malware_re, "1") == 0 ) { if ( !malware_default_re - && !(malware_default_re = m_pcre_compile(malware_regex_default, &errstr))) + && !(malware_default_re = m_pcre_compile(malware_regex_default, FALSE, &errstr))) return malware_panic_defer(errstr); malware_re = malware_regex_default; re = malware_default_re; } /* compile the regex, see if it works */ -else if (!(re = m_pcre_compile(malware_re, &errstr))) +else if (!(re = m_pcre_compile(malware_re, cacheable, &errstr))) return malware_panic_defer(errstr); /* if av_scanner starts with a dollar, expand it first */ if (*av_scanner == '$') { - if (!(av_scanner_work = expand_string(av_scanner))) + if (!(av_scanner_work = expand_string_2(av_scanner, &av_scanner_textonly))) return malware_panic_defer( string_sprintf("av_scanner starts with $, but expansion failed: %s", expand_string_message)); @@ -641,6 +633,8 @@ if (*av_scanner == '$') malware_name = NULL; malware_ok = FALSE; } +else + av_scanner_textonly = TRUE; /* Do not scan twice (unless av_scanner is dynamic). */ if (!malware_ok) @@ -745,13 +739,11 @@ if (!malware_ok) case M_FPROT6D: /* "f-prot6d" scanner type ----------------------------------- */ { int bread; - uschar * e; - uschar * linebuffer; - uschar * scanrequest; + uschar * e, * linebuffer, * scanrequest; uschar av_buffer[1024]; - if ((!fprot6d_re_virus && !(fprot6d_re_virus = m_pcre_compile(fprot6d_re_virus_str, &errstr))) - || (!fprot6d_re_error && !(fprot6d_re_error = m_pcre_compile(fprot6d_re_error_str, &errstr)))) + if ((!fprot6d_re_virus && !(fprot6d_re_virus = m_pcre_compile(fprot6d_re_virus_str, FALSE, &errstr))) + || (!fprot6d_re_error && !(fprot6d_re_error = m_pcre_compile(fprot6d_re_error_str, FALSE, &errstr)))) return malware_panic_defer(errstr); scanrequest = string_sprintf("SCAN FILE %s\n", eml_filename); @@ -921,7 +913,7 @@ badseek: err = errno; /* set up match regex */ if (!drweb_re) - drweb_re = m_pcre_compile(drweb_re_str, &errstr); + drweb_re = m_pcre_compile(drweb_re_str, FALSE, &errstr); /* read and concatenate virus names into one string */ for (int i = 0; i < drweb_vnum; i++) @@ -945,7 +937,7 @@ badseek: err = errno; /* try matcher on the line, grab substring */ result = pcre2_match(drweb_re, (PCRE2_SPTR)tmpbuf, PCRE2_ZERO_TERMINATED, - 0, 0, md, pcre_mtc_ctx); + 0, 0, md, pcre_gen_mtc_ctx); if (result >= 2) { PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md); @@ -959,6 +951,7 @@ badseek: err = errno; g = string_catn(g, US ovec[2], ovec[3] - ovec[2]); } } + /* pcre2_match_data_free(md); gen ctx needs no free */ } malware_name = string_from_gstring(g); } @@ -1099,7 +1092,7 @@ badseek: err = errno; /* set up match */ /* todo also SUSPICION\t */ if (!fsec_re) - fsec_re = m_pcre_compile(fsec_re_str, &errstr); + fsec_re = m_pcre_compile(fsec_re_str, FALSE, &errstr); /* read report, linewise. Apply a timeout as the Fsecure daemon sometimes wants an answer to "PING" but they won't tell us what */ @@ -1223,12 +1216,12 @@ badseek: err = errno; /* set up match regex, depends on retcode */ if (kav_rc == 3) { - if (!kav_re_sus) kav_re_sus = m_pcre_compile(kav_re_sus_str, &errstr); + if (!kav_re_sus) kav_re_sus = m_pcre_compile(kav_re_sus_str, FALSE, &errstr); kav_re = kav_re_sus; } else { - if (!kav_re_inf) kav_re_inf = m_pcre_compile(kav_re_inf_str, &errstr); + if (!kav_re_inf) kav_re_inf = m_pcre_compile(kav_re_inf_str, FALSE, &errstr); kav_re = kav_re_inf; } @@ -1277,13 +1270,13 @@ badseek: err = errno; return m_panic_defer(scanent, NULL, errstr); /* find scanner output trigger */ - cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep, + cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep, av_scanner_textonly, "missing trigger specification", &errstr); if (!cmdline_trigger_re) return m_panic_defer(scanent, NULL, errstr); /* find scanner name regex */ - cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep, + cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep, av_scanner_textonly, "missing virus name regex specification", &errstr); if (!cmdline_regex_re) return m_panic_defer(scanent, NULL, errstr); @@ -1906,13 +1899,13 @@ badseek: err = errno; string_printing(sockline_scanner)); /* find scanner output trigger */ - sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep, + sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep, av_scanner_textonly, "missing trigger specification", &errstr); if (!sockline_trig_re) return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock); /* find virus name regex */ - sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep, + sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep, av_scanner_textonly, "missing virus name regex specification", &errstr); if (!sockline_name_re) return m_panic_defer_3(scanent, NULL, errstr, malware_daemon_ctx.sock); @@ -2043,11 +2036,11 @@ badseek: err = errno; */ if ( ( !ava_re_clean - && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, &errstr))) + && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, FALSE, &errstr))) || ( !ava_re_virus - && !(ava_re_virus = m_pcre_compile(ava_re_virus_str, &errstr))) + && !(ava_re_virus = m_pcre_compile(ava_re_virus_str, FALSE, &errstr))) || ( !ava_re_error - && !(ava_re_error = m_pcre_compile(ava_re_error_str, &errstr))) + && !(ava_re_error = m_pcre_compile(ava_re_error_str, FALSE, &errstr))) ) return malware_panic_defer(errstr); @@ -2209,15 +2202,16 @@ filename; it's a wrapper around the malware_file function. Arguments: malware_re match condition for "malware=" + cacheable the RE did not use any dynamic elements during expansion timeout if nonzero, timeout in seconds Returns: Exim message processing code (OK, FAIL, DEFER, ...) where true means malware was found (condition applies) */ int -malware(const uschar * malware_re, int timeout) +malware(const uschar * malware_re, BOOL cacheable, int timeout) { -int ret = malware_internal(malware_re, NULL, timeout); +int ret = malware_internal(malware_re, cacheable, NULL, timeout); if (ret == DEFER) av_failed = TRUE; return ret; @@ -2257,7 +2251,7 @@ recipients_list = NULL; receive_add_recipient(US"malware-victim@example.net", -1); f.enable_dollar_recipients = TRUE; -ret = malware_internal(US"*", eml_filename, 0); +ret = malware_internal(US"*", TRUE, eml_filename, 0); Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id)); spool_mbox_ok = 1; @@ -2278,35 +2272,35 @@ void malware_init(void) { if (!malware_default_re) - malware_default_re = regex_must_compile(malware_regex_default, FALSE, TRUE); + malware_default_re = regex_must_compile(malware_regex_default, MCS_NOFLAGS, TRUE); #ifndef DISABLE_MAL_DRWEB if (!drweb_re) - drweb_re = regex_must_compile(drweb_re_str, FALSE, TRUE); + drweb_re = regex_must_compile(drweb_re_str, MCS_NOFLAGS, TRUE); #endif #ifndef DISABLE_MAL_FSECURE if (!fsec_re) - fsec_re = regex_must_compile(fsec_re_str, FALSE, TRUE); + fsec_re = regex_must_compile(fsec_re_str, MCS_NOFLAGS, TRUE); #endif #ifndef DISABLE_MAL_KAV if (!kav_re_sus) - kav_re_sus = regex_must_compile(kav_re_sus_str, FALSE, TRUE); + kav_re_sus = regex_must_compile(kav_re_sus_str, MCS_NOFLAGS, TRUE); if (!kav_re_inf) - kav_re_inf = regex_must_compile(kav_re_inf_str, FALSE, TRUE); + kav_re_inf = regex_must_compile(kav_re_inf_str, MCS_NOFLAGS, TRUE); #endif #ifndef DISABLE_MAL_AVAST if (!ava_re_clean) - ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE); + ava_re_clean = regex_must_compile(ava_re_clean_str, MCS_NOFLAGS, TRUE); if (!ava_re_virus) - ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE); + ava_re_virus = regex_must_compile(ava_re_virus_str, MCS_NOFLAGS, TRUE); if (!ava_re_error) - ava_re_error = regex_must_compile(ava_re_error_str, FALSE, TRUE); + ava_re_error = regex_must_compile(ava_re_error_str, MCS_NOFLAGS, TRUE); #endif #ifndef DISABLE_MAL_FFROT6D if (!fprot6d_re_error) - fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, FALSE, TRUE); + fprot6d_re_error = regex_must_compile(fprot6d_re_error_str, MCS_NOFLAGS, TRUE); if (!fprot6d_re_virus) - fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, FALSE, TRUE); + fprot6d_re_virus = regex_must_compile(fprot6d_re_virus_str, MCS_NOFLAGS, TRUE); #endif } diff --git a/src/src/match.c b/src/src/match.c index 2e4bff078..b4a0352ee 100644 --- a/src/src/match.c +++ b/src/src/match.c @@ -19,9 +19,7 @@ typedef struct check_string_block { const uschar *origsubject; /* caseful; keep these two first, in */ const uschar *subject; /* step with the block below */ int expand_setup; - BOOL use_partial; - BOOL caseless; - BOOL at_is_special; + mcs_flags flags; /* MCS_* defs in macros.h */ } check_string_block; @@ -32,7 +30,7 @@ typedef struct check_address_block { const uschar *origaddress; /* caseful; keep these two first, in */ uschar *address; /* step with the block above */ int expand_setup; - BOOL caseless; + mcs_flags flags; /* MCS_CASELESS, MCS_TEXTONLY_RE */ } check_address_block; @@ -93,9 +91,10 @@ Returns: OK if matched */ static int -check_string(void *arg, const uschar *pattern, const uschar **valueptr, uschar **error) +check_string(void * arg, const uschar * pattern, const uschar ** valueptr, + uschar ** error) { -const check_string_block *cb = arg; +const check_string_block * cb = arg; int search_type, partial, affixlen, starflags; int expand_setup = cb->expand_setup; const uschar * affix, * opts; @@ -128,7 +127,8 @@ required. */ if (pattern[0] == '^') { - const pcre2_code * re = regex_must_compile(pattern, cb->caseless, FALSE); + const pcre2_code * re = regex_must_compile(pattern, + cb->flags & (MCS_CACHEABLE | MCS_CASELESS), FALSE); if (expand_setup < 0 ? !regex_match(re, s, -1, NULL) : !regex_match_and_setup(re, s, 0, expand_setup) @@ -147,7 +147,7 @@ if (pattern[0] == '*') patlen = Ustrlen(++pattern); if (patlen > slen) return FAIL; - if (cb->caseless + if (cb->flags & MCS_CASELESS ? strncmpic(s + slen - patlen, pattern, patlen) != 0 : Ustrncmp(s + slen - patlen, pattern, patlen) != 0) return FAIL; @@ -166,7 +166,7 @@ the primary host name - implement this by changing the pattern. For the other cases we have to do some more work. If we don't recognize a special pattern, just fall through - the match will fail. */ -if (cb->at_is_special && pattern[0] == '@') +if (cb->flags & MCS_AT_SPECIAL && pattern[0] == '@') { if (pattern[1] == 0) { @@ -260,10 +260,10 @@ NOT_AT_SPECIAL: if ((semicolon = Ustrchr(pattern, ';')) == NULL) { - if (cb->caseless ? strcmpic(s, pattern) != 0 : Ustrcmp(s, pattern) != 0) + if (cb->flags & MCS_CASELESS ? strcmpic(s, pattern) != 0 : Ustrcmp(s, pattern) != 0) return FAIL; - if (expand_setup >= 0) expand_nmax = expand_setup; /* Original code! $0 gets the matched subject */ - if (valueptr) *valueptr = pattern; /* "value" gets the pattern */ + if (expand_setup >= 0) expand_nmax = expand_setup; /* $0 gets the matched subject */ + if (valueptr) *valueptr = pattern; /* "value" gets the pattern */ return OK; } @@ -280,7 +280,7 @@ if (search_type < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", /* Partial matching is not appropriate for certain lookups (e.g. when looking up user@domain for sender rejection). There's a flag to disable it. */ -if (!cb->use_partial) partial = -1; +if (!(cb->flags & MCS_PARTIAL)) partial = -1; /* Set the parameters for the three different kinds of lookup. */ @@ -316,9 +316,10 @@ Arguments: s the subject string to be checked pattern the pattern to check it against expand_setup expansion setup option (see check_string()) - use_partial if FALSE, override any partial- search types - caseless TRUE for caseless matching where possible - at_is_special TRUE to recognize @, @[], etc. + flags + use_partial if FALSE, override any partial- search types + caseless TRUE for caseless matching where possible + at_is_special TRUE to recognize @, @[], etc. valueptr if not NULL, and a file lookup was done, return the result here instead of discarding it; else set it to point to NULL @@ -328,16 +329,14 @@ Returns: OK if matched */ int -match_check_string(const uschar *s, const uschar *pattern, int expand_setup, - BOOL use_partial, BOOL caseless, BOOL at_is_special, const uschar **valueptr) +match_check_string(const uschar * s, const uschar * pattern, int expand_setup, + mcs_flags flags, const uschar ** valueptr) { check_string_block cb; cb.origsubject = s; -cb.subject = caseless ? string_copylc(s) : string_copy(s); +cb.subject = flags & MCS_CASELESS ? string_copylc(s) : string_copy(s); cb.expand_setup = expand_setup; -cb.use_partial = use_partial; -cb.caseless = caseless; -cb.at_is_special = at_is_special; +cb.flags = flags; return check_string(&cb, pattern, valueptr, NULL); } @@ -364,14 +363,9 @@ switch(type) { case MCL_STRING: case MCL_DOMAIN: - case MCL_LOCALPART: - return ((check_string_block *)arg)->subject; - - case MCL_HOST: - return ((check_host_block *)arg)->host_address; - - case MCL_ADDRESS: - return ((check_address_block *)arg)->address; + case MCL_LOCALPART: return ((check_string_block *)arg)->subject; + case MCL_HOST: return ((check_host_block *)arg)->host_address; + case MCL_ADDRESS: return ((check_address_block *)arg)->address; } return US""; /* In practice, should never happen */ } @@ -438,6 +432,7 @@ BOOL include_unknown = FALSE, ignore_unknown = FALSE, const uschar *list; uschar *sss; uschar *ot = NULL; +BOOL textonly_re; /* Save time by not scanning for the option name when we don't need it. */ @@ -465,6 +460,7 @@ if (type >= MCL_NOEXPAND) { list = *listptr; type -= MCL_NOEXPAND; /* Remove the "no expand" flag */ + textonly_re = TRUE; } else { @@ -475,11 +471,11 @@ else { check_string_block *cb = (check_string_block *)arg; deliver_domain = string_copy(cb->subject); - list = expand_cstring(*listptr); + list = expand_string_2(*listptr, &textonly_re); deliver_domain = NULL; } else - list = expand_cstring(*listptr); + list = expand_string_2(*listptr, &textonly_re); if (!list) { @@ -495,6 +491,15 @@ else } } +if (textonly_re) switch (type) + { + case MCL_STRING: + case MCL_DOMAIN: + case MCL_LOCALPART: ((check_string_block *)arg)->flags |= MCS_CACHEABLE; break; + case MCL_HOST: ((check_host_block *)arg)->flags |= MCS_CACHEABLE; break; + case MCL_ADDRESS: ((check_address_block *)arg)->flags |= MCS_CACHEABLE; break; + } + /* For an unnamed list, use the expanded version in comments */ #define LIST_LIMIT_PR 2048 @@ -530,7 +535,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) if (at) Ustrncpy(cb->address, cb->origaddress, at - cb->origaddress); - cb->caseless = FALSE; + cb->flags &= ~MCS_CASELESS; continue; } } @@ -543,7 +548,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) { check_string_block *cb = (check_string_block *)arg; Ustrcpy(US cb->subject, cb->origsubject); - cb->caseless = FALSE; + cb->flags &= ~MCS_CASELESS; continue; } } @@ -958,15 +963,13 @@ unsigned int *local_cache_bits = cache_bits; check_string_block cb; cb.origsubject = s; cb.subject = caseless ? string_copylc(s) : string_copy(s); -cb.at_is_special = FALSE; +cb.flags = caseless ? MCS_PARTIAL+MCS_CASELESS : MCS_PARTIAL; switch (type & ~MCL_NOEXPAND) { - case MCL_DOMAIN: cb.at_is_special = TRUE; /*FALLTHROUGH*/ + case MCL_DOMAIN: cb.flags |= MCS_AT_SPECIAL; /*FALLTHROUGH*/ case MCL_LOCALPART: cb.expand_setup = 0; break; default: cb.expand_setup = sep > UCHAR_MAX ? 0 : -1; break; } -cb.use_partial = TRUE; -cb.caseless = caseless; if (valueptr) *valueptr = NULL; return match_check_list(listptr, sep, anchorptr, &local_cache_bits, check_string, &cb, type, s, valueptr); @@ -1003,7 +1006,8 @@ Returns: OK for a match */ static int -check_address(void *arg, const uschar *pattern, const uschar **valueptr, uschar **error) +check_address(void * arg, const uschar * pattern, const uschar ** valueptr, + uschar ** error) { check_address_block * cb = (check_address_block *)arg; check_string_block csb; @@ -1026,7 +1030,7 @@ sdomain = Ustrrchr(subject, '@'); /* The only case where a subject may not have a domain is if the subject is empty. Otherwise, a subject with no domain is a serious configuration error. */ -if (sdomain == NULL && *subject != 0) +if (!sdomain && *subject) { log_write(0, LOG_MAIN|LOG_PANIC, "no @ found in the subject of an " "address list match: subject=\"%s\" pattern=\"%s\"", subject, pattern); @@ -1037,14 +1041,14 @@ if (sdomain == NULL && *subject != 0) This may be the empty address. */ if (*pattern == '^') - return match_check_string(subject, pattern, cb->expand_setup, TRUE, - cb->caseless, FALSE, NULL); + return match_check_string(subject, pattern, cb->expand_setup, + cb->flags | MCS_PARTIAL, NULL); /* Handle a pattern that is just a lookup. Skip over possible lookup names (letters, digits, hyphens). Skip over a possible * or *@ at the end. Then we must have a semicolon for it to be a lookup. */ -for (s = pattern; isalnum(*s) || *s == '-'; s++); +for (s = pattern; isalnum(*s) || *s == '-'; s++) ; if (*s == '*') s++; if (*s == '@') s++; @@ -1057,8 +1061,7 @@ if (*s == ';') if (Ustrncmp(pattern, "partial-", 8) == 0) log_write(0, LOG_MAIN|LOG_PANIC, "partial matching is not applicable to " "whole-address lookups: ignored \"partial-\" in \"%s\"", pattern); - return match_check_string(subject, pattern, -1, FALSE, cb->caseless, FALSE, - valueptr); + return match_check_string(subject, pattern, -1, cb->flags, valueptr); } /* For the remaining cases, an empty subject matches only an empty pattern, @@ -1085,19 +1088,20 @@ if (pattern[0] == '@' && pattern[1] == '@') { int sep = 0; - if ((rc = match_check_string(key, pattern + 2, -1, TRUE, FALSE, FALSE, - CUSS &list)) != OK) return rc; + if ((rc = match_check_string(key, pattern + 2, -1, MCS_PARTIAL, CUSS &list)) + != OK) + return rc; /* Check for chaining from the last item; set up the next key if one is found. */ ss = Ustrrchr(list, ':'); - if (ss == NULL) ss = list; else ss++; - while (isspace(*ss)) ss++; + if (!ss) ss = list; else ss++; + Uskip_whitespace(&ss); if (*ss == '>') { *ss++ = 0; - while (isspace(*ss)) ss++; + Uskip_whitespace(&ss); key = string_copy(ss); } else key = NULL; @@ -1117,8 +1121,7 @@ if (pattern[0] == '@' && pattern[1] == '@') else local_yield = OK; *sdomain = 0; - rc = match_check_string(subject, ss, -1, TRUE, cb->caseless, FALSE, - valueptr); + rc = match_check_string(subject, ss, -1, cb->flags + MCS_PARTIAL, valueptr); *sdomain = '@'; switch(rc) @@ -1148,8 +1151,7 @@ if (pattern[0] == '@' && pattern[1] == '@') /* We get here if the pattern is not a lookup or a regular expression. If it contains an @ there is both a local part and a domain. */ -pdomain = Ustrrchr(pattern, '@'); -if (pdomain != NULL) +if ((pdomain = Ustrrchr(pattern, '@'))) { int pllen, sllen; @@ -1177,7 +1179,7 @@ if (pdomain != NULL) { int cllen = pllen - 1; if (sllen < cllen) return FAIL; - if (cb->caseless + if (cb->flags & MCS_CASELESS ? strncmpic(subject+sllen-cllen, pattern + 1, cllen) != 0 : Ustrncmp(subject+sllen-cllen, pattern + 1, cllen) != 0) return FAIL; @@ -1192,7 +1194,7 @@ if (pdomain != NULL) else { if (sllen != pllen) return FAIL; - if (cb->caseless + if (cb->flags & MCS_CASELESS ? strncmpic(subject, pattern, sllen) != 0 : Ustrncmp(subject, pattern, sllen) != 0) return FAIL; } @@ -1205,18 +1207,17 @@ original code read as follows: return match_check_string(sdomain + 1, pdomain ? pdomain + 1 : pattern, - cb->expand_setup + expand_inc, TRUE, cb->caseless, TRUE, NULL); + cb->expand_setup + expand_inc, cb->flags, NULL); This supported only literal domains and *.x.y patterns. In order to allow for -named domain lists (so that you can right, for example, "senders=+xxxx"), it +named domain lists (so that you can write, for example, "senders=+xxxx"), it was changed to use the list scanning function. */ csb.origsubject = sdomain + 1; -csb.subject = cb->caseless ? string_copylc(sdomain+1) : string_copy(sdomain+1); +csb.subject = cb->flags & MCS_CASELESS + ? string_copylc(sdomain+1) : string_copy(sdomain+1); csb.expand_setup = cb->expand_setup + expand_inc; -csb.use_partial = TRUE; -csb.caseless = cb->caseless; -csb.at_is_special = TRUE; +csb.flags = MCS_PARTIAL | MCS_AT_SPECIAL | cb->flags & MCS_CASELESS; listptr = pdomain ? pdomain + 1 : pattern; if (valueptr) *valueptr = NULL; @@ -1321,10 +1322,10 @@ if (expand_setup == 0) ab.origaddress = address; /* ab.address is above */ ab.expand_setup = expand_setup; -ab.caseless = caseless; +ab.flags = caseless ? MCS_CASELESS : 0; return match_check_list(listptr, sep, &addresslist_anchor, &local_cache_bits, - check_address, &ab, MCL_ADDRESS + (expand? 0:MCL_NOEXPAND), address, + check_address, &ab, MCL_ADDRESS + (expand ? 0 : MCL_NOEXPAND), address, valueptr); } diff --git a/src/src/queue.c b/src/src/queue.c index 4bdd6fb14..6e47d2c8a 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -423,11 +423,11 @@ if (!recurse) /* If deliver_selectstring is a regex, compile it. */ if (deliver_selectstring && f.deliver_selectstring_regex) - selectstring_regex = regex_must_compile(deliver_selectstring, TRUE, FALSE); + selectstring_regex = regex_must_compile(deliver_selectstring, MCS_CASELESS, FALSE); if (deliver_selectstring_sender && f.deliver_selectstring_sender_regex) selectstring_regex_sender = - regex_must_compile(deliver_selectstring_sender, TRUE, FALSE); + regex_must_compile(deliver_selectstring_sender, MCS_CASELESS, FALSE); /* If the spool is split into subdirectories, we want to process it one directory at a time, so as to spread out the directory scanning and the @@ -1562,19 +1562,9 @@ memcpy(buf+1, msgid, MESSAGE_ID_LENGTH+1); if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) >= 0) { struct sockaddr_un sa_un = {.sun_family = AF_UNIX}; + ssize_t len = daemon_notifier_sockname(&sa_un); -#ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS - int len = offsetof(struct sockaddr_un, sun_path) + 1 - + snprintf(sa_un.sun_path+1, sizeof(sa_un.sun_path)-1, "%s", - expand_string(notifier_socket)); - sa_un.sun_path[0] = 0; -#else - int len = offsetof(struct sockaddr_un, sun_path) - + snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), "%s", - expand_string(notifier_socket)); -#endif - - if (sendto(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sa_un, len) < 0) + if (sendto(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sa_un, (socklen_t)len) < 0) DEBUG(D_queue_run) debug_printf("%s: sendto %s\n", __FUNCTION__, strerror(errno)); close(fd); diff --git a/src/src/readconf.c b/src/src/readconf.c index 06bc50fd8..5068dc60e 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -243,6 +243,7 @@ static optionlist optionlist_config[] = { #ifdef LOOKUP_ORACLE { "oracle_servers", opt_stringptr, {&oracle_servers} }, #endif + { "panic_coredump", opt_bool, {&panic_coredump} }, { "percent_hack_domains", opt_stringptr, {&percent_hack_domains} }, #ifdef EXIM_PERL { "perl_at_start", opt_bool, {&opt_perl_at_start} }, @@ -2667,8 +2668,8 @@ switch(ol->type & opt_mask) break; case opt_bit: - printf("%s%s\n", ((*((int *)value)) & (1 << ((ol->type >> 16) & 31)))? - "" : "no_", name); + printf("%s%s\n", (*((int *)value)) & (1 << ((ol->type >> 16) & 31)) + ? "" : "no_", name); break; case opt_expand_bool: @@ -2693,7 +2694,7 @@ switch(ol->type & opt_mask) case opt_bool: case opt_bool_verify: case opt_bool_set: - printf("%s%s\n", (*((BOOL *)value))? "" : "no_", name); + printf("%s%s\n", *((BOOL *)value) ? "" : "no_", name); break; case opt_func: @@ -3497,7 +3498,7 @@ if (!process_log_path || !*process_log_path) /* Compile the regex for matching a UUCP-style "From_" line in an incoming message. */ -regex_From = regex_must_compile(uucp_from_pattern, FALSE, TRUE); +regex_From = regex_must_compile(uucp_from_pattern, MCS_NOFLAGS, TRUE); /* Unpick the SMTP rate limiting options, if set */ diff --git a/src/src/regex.c b/src/src/regex.c index 5c0f7c4e0..5de1c1704 100644 --- a/src/src/regex.c +++ b/src/src/regex.c @@ -18,9 +18,9 @@ /* Structure to hold a list of Regular expressions */ typedef struct pcre_list { - pcre2_code *re; - uschar *pcre_text; - struct pcre_list *next; + const pcre2_code * re; + uschar * pcre_text; + struct pcre_list * next; } pcre_list; uschar regex_match_string_buffer[1024]; @@ -28,31 +28,27 @@ uschar regex_match_string_buffer[1024]; extern FILE *mime_stream; extern uschar *mime_current_boundary; + static pcre_list * -compile(const uschar * list) +compile(const uschar * list, BOOL cacheable) { int sep = 0; -uschar *regex_string; -pcre_list *re_list_head = NULL; -pcre_list *ri; +uschar * regex_string; +pcre_list * re_list_head = NULL; +pcre_list * ri; /* precompile our regexes */ while ((regex_string = string_nextinlist(&list, &sep, NULL, 0))) if (strcmpic(regex_string, US"false") != 0 && Ustrcmp(regex_string, "0") != 0) { - pcre2_code * re; - int err; - PCRE2_SIZE pcre_erroffset; - /* compile our regular expression */ - if (!(re = pcre2_compile( (PCRE2_SPTR) regex_string, PCRE2_ZERO_TERMINATED, - 0, &err, &pcre_erroffset, pcre_cmp_ctx))) + uschar * errstr; + const pcre2_code * re = regex_compile(regex_string, + cacheable ? MCS_CACHEABLE : MCS_NOFLAGS, &errstr, pcre_gen_cmp_ctx); + + if (!re) { - uschar errbuf[128]; - pcre2_get_error_message(err, errbuf, sizeof(errbuf)); - log_write(0, LOG_MAIN, - "regex acl condition warning - error in regex '%s': %s at offset %ld, skipped.", - regex_string, errbuf, (long)pcre_erroffset); + log_write(0, LOG_MAIN, "regex acl condition warning - %s, skipped", errstr); continue; } @@ -75,7 +71,7 @@ for (pcre_list * ri = re_list_head; ri; ri = ri->next) int n; /* try matcher on the line */ - if ((n = pcre2_match(ri->re, (PCRE2_SPTR)linebuffer, len, 0, 0, md, pcre_mtc_ctx)) > 0) + if ((n = pcre2_match(ri->re, (PCRE2_SPTR)linebuffer, len, 0, 0, md, pcre_gen_mtc_ctx)) > 0) { Ustrncpy(regex_match_string_buffer, ri->pcre_text, sizeof(regex_match_string_buffer)-1); @@ -85,19 +81,21 @@ for (pcre_list * ri = re_list_head; ri; ri = ri->next) { PCRE2_UCHAR * cstr; PCRE2_SIZE cslen; - pcre2_substring_get_bynumber(md, nn, &cstr, &cslen); + pcre2_substring_get_bynumber(md, nn, &cstr, &cslen); /* uses same ctx as md */ regex_vars[nn-1] = CUS cstr; } return OK; } } -pcre2_match_data_free(md); +/* pcre2_match_data_free(md); gen ctx needs no free */ return FAIL; } + + int -regex(const uschar **listptr) +regex(const uschar **listptr, BOOL cacheable) { unsigned long mbox_size; FILE *mbox_file; @@ -130,7 +128,7 @@ else } /* precompile our regexes */ -if (!(re_list_head = compile(*listptr))) +if (!(re_list_head = compile(*listptr, cacheable))) return FAIL; /* no regexes -> nothing to do */ /* match each line against all regexes */ @@ -167,7 +165,7 @@ return ret; int -mime_regex(const uschar **listptr) +mime_regex(const uschar **listptr, BOOL cacheable) { pcre_list *re_list_head = NULL; FILE *f; @@ -179,7 +177,7 @@ int ret; regex_match_string = NULL; /* precompile our regexes */ -if (!(re_list_head = compile(*listptr))) +if (!(re_list_head = compile(*listptr, cacheable))) return FAIL; /* no regexes -> nothing to do */ /* check if the file is already decoded */ diff --git a/src/src/regex_cache.c b/src/src/regex_cache.c new file mode 100644 index 000000000..63cddce1d --- /dev/null +++ b/src/src/regex_cache.c @@ -0,0 +1,252 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* + * Copyright (c) The Exim Maintainers 2022 + * License: GPL + */ + +/* Caching layers for compiled REs. There is a local layer in the process, +implemented as a tree for inserts and lookup. This cache is inherited from +the daemon, for the process tree deriving from there - but not by re-exec'd +proceses or commandline submission processes. + +If the process has to compile, and is not the daemon or a re-exec'd exim, +it notifies the use of the RE to the daemon via a unix-domain socket. +This is a fire-and-forget send with no response, hence cheap from the point-of +view of the sender. I have not measured the overall comms costs. The +daemon also compiles the RE, and caches the result. + +A second layer would be possible by asking the daemon via the notifier socket +(for a result from its cache, or a compile if it must). The comms overhead +is significant, not only for the channel but also for de/serialisation of +the compiled object. This makes it untenable for the primary use-case, the +transport process which has been re-exec'd to gain privs - and therefore does not +have the daemon-maintained cache. Using shared-memory might reduce that cost +(the attach time for the memory segment will matter); the implimentation +would require suitable R/W locks. +*/ + +#include "exim.h" + +typedef struct re_req { + uschar notifier_reqtype; + BOOL caseless; + uschar re[1]; /* extensible */ +} re_req; + +static tree_node * regex_cache = NULL; +static tree_node * regex_caseless_cache = NULL; + +#define REGEX_CACHESIZE_LIMIT 1000 + +/******************************************************************************/ + +static void +regex_to_daemon(const uschar * key, BOOL caseless) +{ +int klen = Ustrlen(key) + 1; +int rlen = sizeof(re_req) + klen; +re_req * req; +int fd, old_pool = store_pool; + +DEBUG(D_expand|D_lists) + debug_printf_indent("sending RE '%s' to daemon\n", key); + +store_pool = POOL_MAIN; + req = store_get(rlen, key); /* maybe need a size limit */ +store_pool = old_pool;; +req->notifier_reqtype = NOTIFY_REGEX; +req->caseless = caseless; +memcpy(req->re, key, klen); + +if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) >= 0) + { + struct sockaddr_un sa_un = {.sun_family = AF_UNIX}; + ssize_t len = daemon_notifier_sockname(&sa_un); + + if (sendto(fd, req, rlen, 0, (struct sockaddr *)&sa_un, (socklen_t)len) < 0) + DEBUG(D_queue_run) + debug_printf("%s: sendto %s\n", __FUNCTION__, strerror(errno)); + close(fd); + } +else DEBUG(D_queue_run) debug_printf(" socket: %s\n", strerror(errno)); +} + + +static const pcre2_code * +regex_from_cache(const uschar * key, BOOL caseless) +{ +tree_node * node = + tree_search(caseless ? regex_caseless_cache : regex_cache, key); +DEBUG(D_expand|D_lists) + debug_printf_indent("compiled %sRE '%s' %sfound in local cache\n", + caseless ? "caseless " : "", key, node ? "" : "not "); + +return node ? node->data.ptr : NULL; +} + + +static void +regex_to_cache(const uschar * key, BOOL caseless, const pcre2_code * cre) +{ +PCRE2_SIZE srelen; +uschar * sre; +tree_node * node; + +node = store_get(sizeof(tree_node) + Ustrlen(key) + 1, key); /* we are called with STORE_PERM */ +Ustrcpy(node->name, key); +node->data.ptr = (void *)cre; + +if (!tree_insertnode(caseless ? ®ex_caseless_cache : ®ex_cache, node)) + { DEBUG(D_expand|D_lists) debug_printf_indent("duplicate key!\n"); } +else DEBUG(D_expand|D_lists) + debug_printf_indent("compiled RE '%s' saved in local cache\n", key); + +/* Additionally, if not re-execed and not the daemon, tell the daemon of the RE +so it can add to the cache */ + +if (f.daemon_scion && !f.daemon_listen) + regex_to_daemon(key, caseless); + +return; +} + +/******************************************************************************/ + +/************************************************* +* Compile regular expression and panic on fail * +*************************************************/ + +/* This function is called when failure to compile a regular expression leads +to a panic exit. In other cases, pcre_compile() is called directly. In many +cases where this function is used, the results of the compilation are to be +placed in long-lived store, so we temporarily reset the store management +functions that PCRE uses if the use_malloc flag is set. + +Argument: + pattern the pattern to compile + flags + caseless caseless matching is required + cacheable use (writeback) cache + use_malloc TRUE if compile into malloc store + +Returns: pointer to the compiled pattern +*/ + +const pcre2_code * +regex_must_compile(const uschar * pattern, mcs_flags flags, BOOL use_malloc) +{ +BOOL caseless = !!(flags & MCS_CASELESS); +size_t offset; +const pcre2_code * yield; +int old_pool = store_pool, err; + +/* Optionall, check the cache and return if found */ + +if ( flags & MCS_CACHEABLE + && (yield = regex_from_cache(pattern, caseless))) + return yield; + +store_pool = POOL_PERM; + +if (!(yield = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, + caseless ? PCRE_COPT|PCRE2_CASELESS : PCRE_COPT, + &err, &offset, use_malloc ? pcre_mlc_cmp_ctx : pcre_gen_cmp_ctx))) + { + uschar errbuf[128]; + pcre2_get_error_message(err, errbuf, sizeof(errbuf)); + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "regular expression error: " + "%s at offset %ld while compiling %s", errbuf, (long)offset, pattern); + } + +if (use_malloc) + { + /*pcre2_general_context_free(gctx);*/ + } + +if (flags & MCS_CACHEABLE) + regex_to_cache(pattern, caseless, yield); + +store_pool = old_pool; +return yield; +} + + + + +/* Wrapper for pcre2_compile() and error-message handling. + +Arguments: pattern regex to compile + flags + caseless flag for match variant + cacheable use (writeback) cache + errstr on error, filled in with error message + cctx compile-context for pcre2 + +Return: NULL on error, with errstr set. Otherwise, the compiled RE object +*/ + +const pcre2_code * +regex_compile(const uschar * pattern, mcs_flags flags, uschar ** errstr, + pcre2_compile_context * cctx) +{ +const uschar * key = pattern; +BOOL caseless = !!(flags & MCS_CASELESS); +int err; +PCRE2_SIZE offset; +const pcre2_code * yield; +int old_pool = store_pool; + +/* Optionally, check the cache and return if found */ + +if ( flags & MCS_CACHEABLE + && (yield = regex_from_cache(key, caseless))) + return yield; + +DEBUG(D_expand|D_lists) debug_printf_indent("compiling %sRE '%s'\n", + caseless ? "caseless " : "", pattern); + +store_pool = POOL_PERM; +if (!(yield = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, + caseless ? PCRE_COPT|PCRE2_CASELESS : PCRE_COPT, + &err, &offset, cctx))) + { + uschar errbuf[128]; + pcre2_get_error_message(err, errbuf, sizeof(errbuf)); + store_pool = old_pool; + *errstr = string_sprintf("regular expression error in " + "\"%s\": %s at offset %ld", pattern, errbuf, (long)offset); + } +else if (flags & MCS_CACHEABLE) + regex_to_cache(key, caseless, yield); +store_pool = old_pool; + +return yield; +} + + + +/* Handle a regex notify arriving at the daemon. We get sent the original RE; +compile it (again) and write to the cache. Later forked procs will be able to +read from the cache, unless they re-execed. Therefore, those latter never bother +sending us a notification. */ + +void +regex_at_daemon(const uschar * reqbuf) +{ +const re_req * req = (const re_req *)reqbuf; +uschar * errstr; +const pcre2_code * cre; + +if (regex_cachesize >= REGEX_CACHESIZE_LIMIT) + errstr = US"regex cache size limit reached"; +else if ((cre = regex_compile(req->re, + req->caseless ? MCS_CASELESS | MCS_CACHEABLE : MCS_CACHEABLE, + &errstr, pcre_gen_cmp_ctx))) + regex_cachesize++; + +DEBUG(D_any) if (!cre) debug_printf("%s\n", errstr); +return; +} diff --git a/src/src/rewrite.c b/src/src/rewrite.c index 005dc51fe..bfd78b5f1 100644 --- a/src/src/rewrite.c +++ b/src/src/rewrite.c @@ -136,7 +136,8 @@ for (rewrite_rule * rule = rewrite_rules; if (flag & rewrite_smtp) { - uschar *key = expand_string(rule->key); + BOOL textonly_re; + const uschar * key = expand_string_2(rule->key, &textonly_re); if (!key) { if (!f.expand_string_forcedfail) @@ -144,7 +145,8 @@ for (rewrite_rule * rule = rewrite_rules; "checking for SMTP rewriting: %s", rule->key, expand_string_message); continue; } - if (match_check_string(subject, key, 0, TRUE, FALSE, FALSE, NULL) != OK) + if (match_check_string(subject, key, 0, + textonly_re ? MCS_CACHEABLE | MCS_PARTIAL : MCS_PARTIAL, NULL) != OK) continue; new = expand_string(rule->replacement); } diff --git a/src/src/routers/iplookup.c b/src/src/routers/iplookup.c index 94cde4e04..8b67f3116 100644 --- a/src/src/routers/iplookup.c +++ b/src/src/routers/iplookup.c @@ -84,10 +84,10 @@ iplookup_router_options_block iplookup_router_option_defaults = { consistency checks to be done, or anything else that needs to be set up. */ void -iplookup_router_init(router_instance *rblock) +iplookup_router_init(router_instance * rblock) { -iplookup_router_options_block *ob = - (iplookup_router_options_block *)(rblock->options_block); +iplookup_router_options_block * ob = + (iplookup_router_options_block *) rblock->options_block; /* A port and a host list must be given */ @@ -95,13 +95,13 @@ if (ob->port < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n " "a port must be specified", rblock->name); -if (ob->hosts == NULL) +if (!ob->hosts) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n " "a host list must be specified", rblock->name); /* Translate protocol name into value */ -if (ob->protocol_name != NULL) +if (ob->protocol_name) { if (Ustrcmp(ob->protocol_name, "udp") == 0) ob->protocol = ip_udp; else if (Ustrcmp(ob->protocol_name, "tcp") == 0) ob->protocol = ip_tcp; @@ -111,9 +111,9 @@ if (ob->protocol_name != NULL) /* If a response pattern is given, compile it now to get the error early. */ -if (ob->response_pattern != NULL) +if (ob->response_pattern) ob->re_response_pattern = - regex_must_compile(ob->response_pattern, FALSE, TRUE); + regex_must_compile(ob->response_pattern, MCS_NOFLAGS, TRUE); } diff --git a/src/src/structs.h b/src/src/structs.h index b38aa6a9d..06cd06084 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -895,7 +895,7 @@ typedef struct check_host_block { const uschar *host_name; const uschar *host_address; const uschar *host_ipv4; - BOOL negative; + mcs_flags flags; } check_host_block; /* Structure for remembering lookup data when caching the result of diff --git a/src/src/transports/appendfile.c b/src/src/transports/appendfile.c index 93281efda..7e29dd3bc 100644 --- a/src/src/transports/appendfile.c +++ b/src/src/transports/appendfile.c @@ -681,7 +681,7 @@ for (struct dirent *ent; ent = readdir(dir); ) { pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx); int rc = pcre2_match(re, (PCRE2_SPTR)name, PCRE2_ZERO_TERMINATED, - 0, 0, md, pcre_mtc_ctx); + 0, 0, md, pcre_gen_mtc_ctx); PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md); if ( rc >= 0 && (rc = pcre2_get_ovector_count(md)) >= 2) @@ -694,9 +694,11 @@ for (struct dirent *ent; ent = readdir(dir); ) DEBUG(D_transport) debug_printf("check_dir_size: size from %s is " OFF_T_FMT "\n", name, size); + /* pcre2_match_data_free(md); gen ctx needs no free */ continue; } } + /* pcre2_match_data_free(md); gen ctx needs no free */ DEBUG(D_transport) debug_printf("check_dir_size: regex did not match %s\n", name); } @@ -2211,23 +2213,14 @@ else if (ob->quota_value > 0 || THRESHOLD_CHECK || ob->maildir_use_size_file) { - PCRE2_SIZE offset; - int err; - /* Compile the regex if there is one. */ if (ob->quota_size_regex) { - if (!(re = pcre2_compile((PCRE2_SPTR)ob->quota_size_regex, - PCRE2_ZERO_TERMINATED, PCRE_COPT, &err, &offset, pcre_cmp_ctx))) - { - uschar errbuf[128]; - pcre2_get_error_message(err, errbuf, sizeof(errbuf)); - addr->message = string_sprintf("appendfile: regular expression " - "error: %s at offset %ld while compiling %s", errbuf, (long)offset, - ob->quota_size_regex); + if (!(re = regex_compile(ob->quota_size_regex, + MCS_NOFLAGS, &addr->message, pcre_gen_cmp_ctx))) return FALSE; - } + DEBUG(D_transport) debug_printf("using regex for file sizes: %s\n", ob->quota_size_regex); } @@ -2300,23 +2293,14 @@ else if (ob->maildir_use_size_file) { const pcre2_code * dir_regex = NULL; - PCRE2_SIZE offset; - int err; if (ob->maildir_dir_regex) { int check_path_len = Ustrlen(check_path); - if (!(dir_regex = pcre2_compile((PCRE2_SPTR)ob->maildir_dir_regex, - PCRE2_ZERO_TERMINATED, PCRE_COPT, &err, &offset, pcre_cmp_ctx))) - { - uschar errbuf[128]; - pcre2_get_error_message(err, errbuf, sizeof(errbuf)); - addr->message = string_sprintf("appendfile: regular expression " - "error: %s at offset %ld while compiling %s", errbuf, (long)offset, - ob->maildir_dir_regex); + if (!(dir_regex = regex_compile(ob->maildir_dir_regex, + MCS_NOFLAGS, &addr->message, pcre_gen_cmp_ctx))) return FALSE; - } DEBUG(D_transport) debug_printf("using regex for maildir directory selection: %s\n", diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 7f529b7ca..6eee04d03 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -275,7 +275,7 @@ struct list for (struct list * l = list; l < list + nelem(list); l++) if (!*l->re) - *l->re = regex_must_compile(l->string, FALSE, TRUE); + *l->re = regex_must_compile(l->string, MCS_NOFLAGS, TRUE); } @@ -1000,7 +1000,7 @@ uschar authnum; unsigned short authbits = 0; if (!sx->esmtp) return 0; -if (!regex_AUTH) regex_AUTH = regex_must_compile(AUTHS_REGEX, FALSE, TRUE); +if (!regex_AUTH) regex_AUTH = regex_must_compile(AUTHS_REGEX, MCS_NOFLAGS, TRUE); if (!regex_match_and_setup(regex_AUTH, sx->buffer, 0, -1)) return 0; expand_nmax = -1; /* reset */ names = string_copyn(expand_nstring[1], expand_nlength[1]); @@ -1563,7 +1563,7 @@ f.smtp_authenticated = FALSE; client_authenticator = client_authenticated_id = client_authenticated_sender = NULL; if (!regex_AUTH) - regex_AUTH = regex_must_compile(AUTHS_REGEX, FALSE, TRUE); + regex_AUTH = regex_must_compile(AUTHS_REGEX, MCS_NOFLAGS, TRUE); /* Is the server offering AUTH? */ @@ -1849,57 +1849,57 @@ pcre2_match_data * md = pcre2_match_data_create(1, pcre_gen_ctx); #ifndef DISABLE_TLS if ( checks & OPTION_TLS && pcre2_match(regex_STARTTLS, - (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0) + (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_gen_mtc_ctx) < 0) #endif checks &= ~OPTION_TLS; if ( checks & OPTION_IGNQ && pcre2_match(regex_IGNOREQUOTA, - (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0) + (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_gen_mtc_ctx) < 0) checks &= ~OPTION_IGNQ; if ( checks & OPTION_CHUNKING && pcre2_match(regex_CHUNKING, - (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0) + (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_gen_mtc_ctx) < 0) checks &= ~OPTION_CHUNKING; #ifndef DISABLE_PRDR if ( checks & OPTION_PRDR && pcre2_match(regex_PRDR, - (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0) + (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_gen_mtc_ctx) < 0) #endif checks &= ~OPTION_PRDR; #ifdef SUPPORT_I18N if ( checks & OPTION_UTF8 && pcre2_match(regex_UTF8, - (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0) + (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_gen_mtc_ctx) < 0) #endif checks &= ~OPTION_UTF8; if ( checks & OPTION_DSN && pcre2_match(regex_DSN, - (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0) + (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_gen_mtc_ctx) < 0) checks &= ~OPTION_DSN; if ( checks & OPTION_PIPE && pcre2_match(regex_PIPELINING, - (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0) + (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_gen_mtc_ctx) < 0) checks &= ~OPTION_PIPE; if ( checks & OPTION_SIZE && pcre2_match(regex_SIZE, - (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0) + (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_gen_mtc_ctx) < 0) checks &= ~OPTION_SIZE; #ifndef DISABLE_PIPE_CONNECT if ( checks & OPTION_EARLY_PIPE && pcre2_match(regex_EARLY_PIPE, - (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0) + (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_gen_mtc_ctx) < 0) #endif checks &= ~OPTION_EARLY_PIPE; -pcre2_match_data_free(md); +/* pcre2_match_data_free(md); gen ctx needs no free */ /* debug_printf("%s: found 0x%04x\n", __FUNCTION__, checks); */ return checks; } diff --git a/src/src/transports/tf_maildir.c b/src/src/transports/tf_maildir.c index a83fc6f09..205ee41cb 100644 --- a/src/src/transports/tf_maildir.c +++ b/src/src/transports/tf_maildir.c @@ -142,22 +142,13 @@ a subfolder, and should ensure that a maildirfolder file exists. */ if (maildirfolder_create_regex) { - int err; - PCRE2_SIZE offset; const pcre2_code * re; DEBUG(D_transport) debug_printf("checking for maildirfolder requirement\n"); - if (!(re = pcre2_compile((PCRE2_SPTR)maildirfolder_create_regex, - PCRE2_ZERO_TERMINATED, PCRE_COPT, &err, &offset, pcre_cmp_ctx))) - { - uschar errbuf[128]; - pcre2_get_error_message(err, errbuf, sizeof(errbuf)); - addr->message = string_sprintf("appendfile: regular expression " - "error: %s at offset %ld while compiling %s", errbuf, (long)offset, - maildirfolder_create_regex); + if (!(re = regex_compile(maildirfolder_create_regex, + MCS_NOFLAGS, &addr->message, pcre_gen_cmp_ctx))) return FALSE; - } if (regex_match(re, path, -1, NULL)) { diff --git a/src/src/verify.c b/src/src/verify.c index b4c2b9a8f..afc18d553 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -3074,7 +3074,7 @@ digits, full stops, and hyphens (the constituents of domain names). Allow underscores, as they are all too commonly found. Sigh. Also, if allow_utf8_domains is set, allow top-bit characters. */ -for (t = ss; *t != 0; t++) +for (t = ss; *t; t++) if (!isalnum(*t) && *t != '.' && *t != '-' && *t != '_' && (!allow_utf8_domains || *t < 128)) break; @@ -3082,7 +3082,7 @@ for (t = ss; *t != 0; t++) its IP address and match against that. Note that a multi-homed host will add items to the chain. */ -if (*t == 0) +if (!*t) { int rc; host_item h; @@ -3113,8 +3113,8 @@ outgoing hosts, the name is always given explicitly. If it is NULL, it means we must use sender_host_name and its aliases, looking them up if necessary. */ if (cb->host_name) /* Explicit host name given */ - return match_check_string(cb->host_name, ss, -1, TRUE, TRUE, TRUE, - valueptr); + return match_check_string(cb->host_name, ss, -1, + MCS_PARTIAL | MCS_CASELESS | MCS_AT_SPECIAL | cb->flags, valueptr); /* Host name not given; in principle we need the sender host name and its aliases. However, for query-style lookups, we do not need the name if the @@ -3143,7 +3143,9 @@ if ((semicolon = Ustrchr(ss, ';'))) if (isquery) { - switch(match_check_string(US"", ss, -1, TRUE, TRUE, TRUE, valueptr)) + switch(match_check_string(US"", ss, -1, + MCS_PARTIAL| MCS_CASELESS| MCS_AT_SPECIAL | (cb->flags & MCS_CACHEABLE), + valueptr)) { case OK: return OK; case DEFER: return DEFER; @@ -3169,7 +3171,9 @@ if (!sender_host_name) /* Match on the sender host name, using the general matching function */ -switch(match_check_string(sender_host_name, ss, -1, TRUE, TRUE, TRUE, valueptr)) +switch(match_check_string(sender_host_name, ss, -1, + MCS_PARTIAL| MCS_CASELESS| MCS_AT_SPECIAL | (cb->flags & MCS_CACHEABLE), + valueptr)) { case OK: return OK; case DEFER: return DEFER; @@ -3179,7 +3183,9 @@ switch(match_check_string(sender_host_name, ss, -1, TRUE, TRUE, TRUE, valueptr)) aliases = sender_host_aliases; while (*aliases) - switch(match_check_string(*aliases++, ss, -1, TRUE, TRUE, TRUE, valueptr)) + switch(match_check_string(*aliases++, ss, -1, + MCS_PARTIAL| MCS_CASELESS| MCS_AT_SPECIAL | (cb->flags & MCS_CACHEABLE), + valueptr)) { case OK: return OK; case DEFER: return DEFER; @@ -3255,8 +3261,8 @@ rc = match_check_list( check_host, /* function for testing */ &cb, /* argument for function */ MCL_HOST, /* type of check */ - (host_address == sender_host_address)? - US"host" : host_address, /* text for debugging */ + host_address == sender_host_address + ? US"host" : host_address, /* text for debugging */ valueptr); /* where to pass back data */ deliver_host_address = save_host_address; return rc; commit d05685413efd3262b4a5622717f90bba351f1074 Author: Jeremy Harris Date: Sun Jun 26 12:29:05 2022 +0100 typo diff --git a/src/src/drtables.c b/src/src/drtables.c index b2f2a4b33..30eb855e3 100644 --- a/src/src/drtables.c +++ b/src/src/drtables.c @@ -736,7 +736,7 @@ else { char * name = ent->d_name; int len = (int)strlen(name); - if (regex_match(regex_islookupmod, US name, len, NUL)) + if (regex_match(regex_islookupmod, US name, len, NULL)) { int pathnamelen = len + (int)strlen(LOOKUP_MODULE_DIR) + 2; void *dl; commit c6887a05b9c56d373086e9a79e20c26bebd300b2 Author: Jeremy Harris Date: Sun Jun 26 15:27:32 2022 +0100 Variable setting in -be diff --git a/src/src/acl.c b/src/src/acl.c index 0078aca7d..3af3a4eee 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -734,6 +734,78 @@ return -1; } +static BOOL +acl_varname_to_cond(const uschar ** sp, acl_condition_block * cond, uschar ** error) +{ +const uschar * s = *sp, * endptr; + +#ifndef DISABLE_DKIM +if ( Ustrncmp(s, "dkim_verify_status", 18) == 0 + || Ustrncmp(s, "dkim_verify_reason", 18) == 0) + { + endptr = s+18; + if (isalnum(*endptr)) + { + *error = string_sprintf("invalid variable name after \"set\" in ACL " + "modifier \"set %s\" " + "(only \"dkim_verify_status\" or \"dkim_verify_reason\" permitted)", + s); + return FALSE; + } + cond->u.varname = string_copyn(s, 18); + } +else +#endif + { + if (Ustrncmp(s, "acl_c", 5) != 0 && Ustrncmp(s, "acl_m", 5) != 0) + { + *error = string_sprintf("invalid variable name after \"set\" in ACL " + "modifier \"set %s\" (must start \"acl_c\" or \"acl_m\")", s); + return FALSE; + } + + endptr = s + 5; + if (!isdigit(*endptr) && *endptr != '_') + { + *error = string_sprintf("invalid variable name after \"set\" in ACL " + "modifier \"set %s\" (digit or underscore must follow acl_c or acl_m)", + s); + return FALSE; + } + + for ( ; *endptr && *endptr != '=' && !isspace(*endptr); endptr++) + if (!isalnum(*endptr) && *endptr != '_') + { + *error = string_sprintf("invalid character \"%c\" in variable name " + "in ACL modifier \"set %s\"", *endptr, s); + return FALSE; + } + + cond->u.varname = string_copyn(s + 4, endptr - s - 4); + } +s = endptr; +Uskip_whitespace(&s); +*sp = s; +return TRUE; +} + + +static BOOL +acl_data_to_cond(const uschar * s, acl_condition_block * cond, + const uschar * name, uschar ** error) +{ +if (*s++ != '=') + { + *error = string_sprintf("\"=\" missing after ACL \"%s\" %s", name, + conditions[cond->type].is_modifier ? US"modifier" : US"condition"); + return FALSE;; + } +Uskip_whitespace(&s); +cond->arg = string_copy(s); +return TRUE; +} + + /************************************************* * Read and parse one ACL * *************************************************/ @@ -760,7 +832,7 @@ acl_block **lastp = &yield; acl_block *this = NULL; acl_condition_block *cond; acl_condition_block **condp = NULL; -uschar * s; +const uschar * s; *error = NULL; @@ -768,7 +840,7 @@ while ((s = (*func)())) { int v, c; BOOL negated = FALSE; - uschar *saveline = s; + const uschar * saveline = s; uschar name[EXIM_DRIVERNAME_MAX]; /* Conditions (but not verbs) are allowed to be negated by an initial @@ -808,16 +880,15 @@ while ((s = (*func)())) *error = string_sprintf("malformed ACL line \"%s\"", saveline); return NULL; } - this = store_get(sizeof(acl_block), GET_UNTAINTED); - *lastp = this; - lastp = &(this->next); + *lastp = this = store_get(sizeof(acl_block), GET_UNTAINTED); + lastp = &this->next; this->next = NULL; this->condition = NULL; this->verb = v; this->srcline = config_lineno; /* for debug output */ this->srcfile = config_filename; /**/ - condp = &(this->condition); - if (*s == 0) continue; /* No condition on this line */ + condp = &this->condition; + if (!*s) continue; /* No condition on this line */ if (*s == '!') { negated = TRUE; @@ -861,7 +932,7 @@ while ((s = (*func)())) cond->u.negated = negated; *condp = cond; - condp = &(cond->next); + condp = &cond->next; /* The "set" modifier is different in that its argument is "name=value" rather than just a value, and we can check the validity of the name, which @@ -874,75 +945,13 @@ while ((s = (*func)())) compatibility. */ if (c == ACLC_SET) -#ifndef DISABLE_DKIM - if ( Ustrncmp(s, "dkim_verify_status", 18) == 0 - || Ustrncmp(s, "dkim_verify_reason", 18) == 0) - { - uschar * endptr = s+18; - - if (isalnum(*endptr)) - { - *error = string_sprintf("invalid variable name after \"set\" in ACL " - "modifier \"set %s\" " - "(only \"dkim_verify_status\" or \"dkim_verify_reason\" permitted)", - s); - return NULL; - } - cond->u.varname = string_copyn(s, 18); - s = endptr; - Uskip_whitespace(&s); - } - else -#endif - { - uschar *endptr; - - if (Ustrncmp(s, "acl_c", 5) != 0 && Ustrncmp(s, "acl_m", 5) != 0) - { - *error = string_sprintf("invalid variable name after \"set\" in ACL " - "modifier \"set %s\" (must start \"acl_c\" or \"acl_m\")", s); - return NULL; - } - - endptr = s + 5; - if (!isdigit(*endptr) && *endptr != '_') - { - *error = string_sprintf("invalid variable name after \"set\" in ACL " - "modifier \"set %s\" (digit or underscore must follow acl_c or acl_m)", - s); - return NULL; - } - - while (*endptr && *endptr != '=' && !isspace(*endptr)) - { - if (!isalnum(*endptr) && *endptr != '_') - { - *error = string_sprintf("invalid character \"%c\" in variable name " - "in ACL modifier \"set %s\"", *endptr, s); - return NULL; - } - endptr++; - } - - cond->u.varname = string_copyn(s + 4, endptr - s - 4); - s = endptr; - Uskip_whitespace(&s); - } + if (!acl_varname_to_cond(&s, cond, error)) return NULL; /* For "set", we are now positioned for the data. For the others, only "endpass" has no data */ if (c != ACLC_ENDPASS) - { - if (*s++ != '=') - { - *error = string_sprintf("\"=\" missing after ACL \"%s\" %s", name, - conditions[c].is_modifier ? US"modifier" : US"condition"); - return NULL; - } - Uskip_whitespace(&s); - cond->arg = string_copy(s); - } + if (!acl_data_to_cond(s, cond, name, error)) return NULL; } return yield; @@ -4894,6 +4903,29 @@ if (is_tainted(value)) fprintf(f, "acl%c %s %d\n%s\n", name[0], name+1, Ustrlen(value), value); } + + + +uschar * +acl_standalone_setvar(const uschar * s) +{ +acl_condition_block * cond = store_get(sizeof(acl_condition_block), GET_UNTAINTED); +uschar * errstr = NULL, * log_msg = NULL; +BOOL endpass_seen; +int e; + +cond->next = NULL; +cond->type = ACLC_SET; +if (!acl_varname_to_cond(&s, cond, &errstr)) return errstr; +if (!acl_data_to_cond(s, cond, US"'-be'", &errstr)) return errstr; + +if (acl_check_condition(ACL_WARN, cond, ACL_WHERE_UNKNOWN, + NULL, 0, &endpass_seen, &errstr, &log_msg, &e) != OK) + return string_sprintf("oops: %s", errstr); +return string_sprintf("variable %s set", cond->u.varname); +} + + #endif /* !MACRO_PREDEF */ /* vi: aw ai sw=2 */ diff --git a/src/src/exim.c b/src/src/exim.c index dec8de4b4..23e206d2a 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1670,6 +1670,8 @@ if (isupper(big_buffer[0])) if (macro_read_assignment(big_buffer)) printf("Defined macro '%s'\n", mlast->name); } +else if (Ustrncmp(big_buffer, "set ", 4) == 0) + printf("%s\n", acl_standalone_setvar(big_buffer+4)); else if ((s = expand_string(big_buffer))) printf("%s\n", CS s); else printf("Failed: %s\n", expand_string_message); diff --git a/src/src/functions.h b/src/src/functions.h index 4caae346d..e71823410 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -100,6 +100,7 @@ extern acl_block *acl_read(uschar *(*)(void), uschar **); extern int acl_check(int, uschar *, uschar *, uschar **, uschar **); extern uschar *acl_current_verb(void); extern int acl_eval(int, uschar *, uschar **, uschar **); +extern uschar *acl_standalone_setvar(const uschar *); extern tree_node *acl_var_create(uschar *); extern void acl_var_write(uschar *, uschar *, void *); @@ -425,7 +426,7 @@ extern void readconf_main(BOOL); extern void readconf_options_from_list(optionlist *, unsigned, const uschar *, uschar *); extern BOOL readconf_print(const uschar *, uschar *, BOOL); extern uschar *readconf_printtime(int); -extern uschar *readconf_readname(uschar *, int, uschar *); +extern const uschar *readconf_readname(uschar *, int, const uschar *); extern int readconf_readtime(const uschar *, int, BOOL); extern void readconf_rest(void); extern uschar *readconf_retry_error(const uschar *, const uschar *, int *, int *); diff --git a/src/src/readconf.c b/src/src/readconf.c index 5068dc60e..83ee51b65 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -1172,8 +1172,8 @@ Arguments: Returns: new input pointer */ -uschar * -readconf_readname(uschar *name, int len, uschar *s) +const uschar * +readconf_readname(uschar * name, int len, const uschar * s) { int p = 0; BOOL broken = FALSE; @@ -1632,7 +1632,7 @@ rmark reset_point; int intbase = 0; uschar *inttype = US""; uschar *sptr; -uschar *s = buffer; +const uschar * s = buffer; uschar **str_target; uschar name[EXIM_DRIVERNAME_MAX]; uschar name2[EXIM_DRIVERNAME_MAX]; @@ -1752,337 +1752,337 @@ switch (type) case opt_gidlist: case opt_rewrite: - reset_point = store_mark(); - sptr = read_string(s, name); + reset_point = store_mark(); + sptr = read_string(s, name); - /* Having read a string, we now have several different ways of using it, - depending on the data type, so do another switch. If keeping the actual - string is not required (because it is interpreted), freesptr is set TRUE, - and at the end we reset the pool. */ + /* Having read a string, we now have several different ways of using it, + depending on the data type, so do another switch. If keeping the actual + string is not required (because it is interpreted), freesptr is set TRUE, + and at the end we reset the pool. */ - switch (type) - { - /* If this was a string, set the variable to point to the new string, - and set the flag so its store isn't reclaimed. If it was a list of rewrite - rules, we still keep the string (for printing), and parse the rules into a - control block and flags word. */ - - case opt_stringptr: - str_target = data_block ? USS (US data_block + ol->v.offset) - : USS ol->v.value; - if (ol->type & opt_rep_con) - { - uschar * saved_condition; - /* We already have a condition, we're conducting a crude hack to let - multiple condition rules be chained together, despite storing them in - text form. */ - *str_target = string_copy_perm( (saved_condition = *str_target) - ? string_sprintf("${if and{{bool_lax{%s}}{bool_lax{%s}}}}", - saved_condition, sptr) - : sptr, - FALSE); - /* TODO(pdp): there is a memory leak here and just below - when we set 3 or more conditions; I still don't - understand the store mechanism enough to know - what's the safe way to free content from an earlier store. - AFAICT, stores stack, so freeing an early stored item also stores - all data alloc'd after it. If we knew conditions were adjacent, - we could survive that, but we don't. So I *think* we need to take - another bit from opt_type to indicate "malloced"; this seems like - quite a hack, especially for this one case. It also means that - we can't ever reclaim the store from the *first* condition. - - Because we only do this once, near process start-up, I'm prepared to - let this slide for the time being, even though it rankles. */ - } - else if (ol->type & opt_rep_str) + switch (type) { - uschar sep_o = - Ustrncmp(name, "headers_add", 11) == 0 ? '\n' - : Ustrncmp(name, "set", 3) == 0 ? ';' - : ':'; - int sep_i = -(int)sep_o; - const uschar * list = sptr; - uschar * s; - gstring * list_o = NULL; - - if (*str_target) - { - list_o = string_get(Ustrlen(*str_target) + Ustrlen(sptr)); - list_o = string_cat(list_o, *str_target); - } - - while ((s = string_nextinlist(&list, &sep_i, NULL, 0))) - list_o = string_append_listele(list_o, sep_o, s); + /* If this was a string, set the variable to point to the new string, + and set the flag so its store isn't reclaimed. If it was a list of rewrite + rules, we still keep the string (for printing), and parse the rules into a + control block and flags word. */ + + case opt_stringptr: + str_target = data_block ? USS (US data_block + ol->v.offset) + : USS ol->v.value; + if (ol->type & opt_rep_con) + { + uschar * saved_condition; + /* We already have a condition, we're conducting a crude hack to let + multiple condition rules be chained together, despite storing them in + text form. */ + *str_target = string_copy_perm( (saved_condition = *str_target) + ? string_sprintf("${if and{{bool_lax{%s}}{bool_lax{%s}}}}", + saved_condition, sptr) + : sptr, + FALSE); + /* TODO(pdp): there is a memory leak here and just below + when we set 3 or more conditions; I still don't + understand the store mechanism enough to know + what's the safe way to free content from an earlier store. + AFAICT, stores stack, so freeing an early stored item also stores + all data alloc'd after it. If we knew conditions were adjacent, + we could survive that, but we don't. So I *think* we need to take + another bit from opt_type to indicate "malloced"; this seems like + quite a hack, especially for this one case. It also means that + we can't ever reclaim the store from the *first* condition. + + Because we only do this once, near process start-up, I'm prepared to + let this slide for the time being, even though it rankles. */ + } + else if (ol->type & opt_rep_str) + { + uschar sep_o = + Ustrncmp(name, "headers_add", 11) == 0 ? '\n' + : Ustrncmp(name, "set", 3) == 0 ? ';' + : ':'; + int sep_i = -(int)sep_o; + const uschar * list = sptr; + uschar * s; + gstring * list_o = NULL; + + if (*str_target) + { + list_o = string_get(Ustrlen(*str_target) + Ustrlen(sptr)); + list_o = string_cat(list_o, *str_target); + } - if (list_o) - *str_target = string_copy_perm(string_from_gstring(list_o), FALSE); - } - else - { - *str_target = sptr; - freesptr = FALSE; - } - break; + while ((s = string_nextinlist(&list, &sep_i, NULL, 0))) + list_o = string_append_listele(list_o, sep_o, s); - case opt_rewrite: - if (data_block) - *USS (US data_block + ol->v.offset) = sptr; - else - *USS ol->v.value = sptr; - freesptr = FALSE; - if (type == opt_rewrite) - { - int sep = 0; - int *flagptr; - uschar *p = sptr; - rewrite_rule **chain; - optionlist *ol3; - - sprintf(CS name2, "*%.50s_rules", name); - ol2 = find_option(name2, oltop, last); - sprintf(CS name2, "*%.50s_flags", name); - ol3 = find_option(name2, oltop, last); - - if (!ol2 || !ol3) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, - "rewrite rules not available for driver"); + if (list_o) + *str_target = string_copy_perm(string_from_gstring(list_o), FALSE); + } + else + { + *str_target = sptr; + freesptr = FALSE; + } + break; - if (data_block) - { - chain = (rewrite_rule **)(US data_block + ol2->v.offset); - flagptr = (int *)(US data_block + ol3->v.offset); - } - else - { - chain = (rewrite_rule **)ol2->v.value; - flagptr = (int *)ol3->v.value; - } + case opt_rewrite: + if (data_block) + *USS (US data_block + ol->v.offset) = sptr; + else + *USS ol->v.value = sptr; + freesptr = FALSE; + if (type == opt_rewrite) + { + int sep = 0; + int *flagptr; + uschar *p = sptr; + rewrite_rule **chain; + optionlist *ol3; + + sprintf(CS name2, "*%.50s_rules", name); + ol2 = find_option(name2, oltop, last); + sprintf(CS name2, "*%.50s_flags", name); + ol3 = find_option(name2, oltop, last); + + if (!ol2 || !ol3) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, + "rewrite rules not available for driver"); + + if (data_block) + { + chain = (rewrite_rule **)(US data_block + ol2->v.offset); + flagptr = (int *)(US data_block + ol3->v.offset); + } + else + { + chain = (rewrite_rule **)ol2->v.value; + flagptr = (int *)ol3->v.value; + } - /* This will trap if sptr is tainted. Not sure if that can happen */ - while ((p = string_nextinlist(CUSS &sptr, &sep, big_buffer, BIG_BUFFER_SIZE))) - { - rewrite_rule *next = readconf_one_rewrite(p, flagptr, FALSE); - *chain = next; - chain = &(next->next); - } + /* This will trap if sptr is tainted. Not sure if that can happen */ + while ((p = string_nextinlist(CUSS &sptr, &sep, big_buffer, BIG_BUFFER_SIZE))) + { + rewrite_rule *next = readconf_one_rewrite(p, flagptr, FALSE); + *chain = next; + chain = &(next->next); + } - if ((*flagptr & (rewrite_all_envelope | rewrite_smtp)) != 0) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "rewrite rule specifies a " - "non-header rewrite - not allowed at transport time -"); - } - break; + if ((*flagptr & (rewrite_all_envelope | rewrite_smtp)) != 0) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "rewrite rule specifies a " + "non-header rewrite - not allowed at transport time -"); + } + break; - /* If it was an expanded uid, see if there is any expansion to be - done by checking for the presence of a $ character. If there is, save it - in the corresponding *expand_user option field. Otherwise, fall through - to treat it as a fixed uid. Ensure mutual exclusivity of the two kinds - of data. */ + /* If it was an expanded uid, see if there is any expansion to be + done by checking for the presence of a $ character. If there is, save it + in the corresponding *expand_user option field. Otherwise, fall through + to treat it as a fixed uid. Ensure mutual exclusivity of the two kinds + of data. */ - case opt_expand_uid: - sprintf(CS name2, "*expand_%.50s", name); - if ((ol2 = find_option(name2, oltop, last))) - { - uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL; + case opt_expand_uid: + sprintf(CS name2, "*expand_%.50s", name); + if ((ol2 = find_option(name2, oltop, last))) + { + uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL; - if (data_block) - *(USS(US data_block + ol2->v.offset)) = ss; - else - *(USS ol2->v.value) = ss; + if (data_block) + *(USS(US data_block + ol2->v.offset)) = ss; + else + *(USS ol2->v.value) = ss; - if (ss) - { - *(get_set_flag(name, oltop, last, data_block)) = FALSE; - freesptr = FALSE; - break; - } - } + if (ss) + { + *(get_set_flag(name, oltop, last, data_block)) = FALSE; + freesptr = FALSE; + break; + } + } - /* Look up a fixed uid, and also make use of the corresponding gid - if a passwd entry is returned and the gid has not been set. */ + /* Look up a fixed uid, and also make use of the corresponding gid + if a passwd entry is returned and the gid has not been set. */ - case opt_uid: - if (!route_finduser(sptr, &pw, &uid)) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found", sptr); - if (data_block) - *(uid_t *)(US data_block + ol->v.offset) = uid; - else - *(uid_t *)ol->v.value = uid; + case opt_uid: + if (!route_finduser(sptr, &pw, &uid)) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found", sptr); + if (data_block) + *(uid_t *)(US data_block + ol->v.offset) = uid; + else + *(uid_t *)ol->v.value = uid; - /* Set the flag indicating a fixed value is set */ + /* Set the flag indicating a fixed value is set */ - *(get_set_flag(name, oltop, last, data_block)) = TRUE; + *(get_set_flag(name, oltop, last, data_block)) = TRUE; - /* Handle matching gid if we have a passwd entry: done by finding the - same name with terminating "user" changed to "group"; if not found, - ignore. Also ignore if the value is already set. */ + /* Handle matching gid if we have a passwd entry: done by finding the + same name with terminating "user" changed to "group"; if not found, + ignore. Also ignore if the value is already set. */ - if (pw == NULL) break; - Ustrcpy(name+Ustrlen(name)-4, US"group"); - ol2 = find_option(name, oltop, last); - if (ol2 && ((ol2->type & opt_mask) == opt_gid || - (ol2->type & opt_mask) == opt_expand_gid)) - { - BOOL *set_flag = get_set_flag(name, oltop, last, data_block); - if (!*set_flag) - { - if (data_block) - *((gid_t *)(US data_block + ol2->v.offset)) = pw->pw_gid; - else - *((gid_t *)ol2->v.value) = pw->pw_gid; - *set_flag = TRUE; - } - } - break; + if (pw == NULL) break; + Ustrcpy(name+Ustrlen(name)-4, US"group"); + ol2 = find_option(name, oltop, last); + if (ol2 && ((ol2->type & opt_mask) == opt_gid || + (ol2->type & opt_mask) == opt_expand_gid)) + { + BOOL *set_flag = get_set_flag(name, oltop, last, data_block); + if (!*set_flag) + { + if (data_block) + *((gid_t *)(US data_block + ol2->v.offset)) = pw->pw_gid; + else + *((gid_t *)ol2->v.value) = pw->pw_gid; + *set_flag = TRUE; + } + } + break; - /* If it was an expanded gid, see if there is any expansion to be - done by checking for the presence of a $ character. If there is, save it - in the corresponding *expand_user option field. Otherwise, fall through - to treat it as a fixed gid. Ensure mutual exclusivity of the two kinds - of data. */ + /* If it was an expanded gid, see if there is any expansion to be + done by checking for the presence of a $ character. If there is, save it + in the corresponding *expand_user option field. Otherwise, fall through + to treat it as a fixed gid. Ensure mutual exclusivity of the two kinds + of data. */ - case opt_expand_gid: - sprintf(CS name2, "*expand_%.50s", name); - if ((ol2 = find_option(name2, oltop, last))) - { - uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL; + case opt_expand_gid: + sprintf(CS name2, "*expand_%.50s", name); + if ((ol2 = find_option(name2, oltop, last))) + { + uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL; - if (data_block) - *(USS(US data_block + ol2->v.offset)) = ss; - else - *(USS ol2->v.value) = ss; + if (data_block) + *(USS(US data_block + ol2->v.offset)) = ss; + else + *(USS ol2->v.value) = ss; - if (ss) - { - *(get_set_flag(name, oltop, last, data_block)) = FALSE; - freesptr = FALSE; - break; - } - } + if (ss) + { + *(get_set_flag(name, oltop, last, data_block)) = FALSE; + freesptr = FALSE; + break; + } + } - /* Handle freestanding gid */ + /* Handle freestanding gid */ - case opt_gid: - if (!route_findgroup(sptr, &gid)) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found", sptr); - if (data_block) - *((gid_t *)(US data_block + ol->v.offset)) = gid; - else - *((gid_t *)ol->v.value) = gid; - *(get_set_flag(name, oltop, last, data_block)) = TRUE; - break; + case opt_gid: + if (!route_findgroup(sptr, &gid)) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found", sptr); + if (data_block) + *((gid_t *)(US data_block + ol->v.offset)) = gid; + else + *((gid_t *)ol->v.value) = gid; + *(get_set_flag(name, oltop, last, data_block)) = TRUE; + break; - /* If it was a uid list, look up each individual entry, and build - a vector of uids, with a count in the first element. Put the vector - in malloc store so we can free the string. (We are reading into - permanent store already.) */ + /* If it was a uid list, look up each individual entry, and build + a vector of uids, with a count in the first element. Put the vector + in malloc store so we can free the string. (We are reading into + permanent store already.) */ - case opt_uidlist: - { - int count = 1; - uid_t *list; - int ptr = 0; - const uschar *p; - const uschar *op = expand_string (sptr); - - if (op == NULL) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s", - name, expand_string_message); - - p = op; - if (*p != 0) count++; - while (*p != 0) if (*p++ == ':' && *p != 0) count++; - list = store_malloc(count*sizeof(uid_t)); - list[ptr++] = (uid_t)(count - 1); - - if (data_block) - *((uid_t **)(US data_block + ol->v.offset)) = list; - else - *((uid_t **)ol->v.value) = list; + case opt_uidlist: + { + int count = 1; + uid_t *list; + int ptr = 0; + const uschar *p; + const uschar *op = expand_string (sptr); + + if (op == NULL) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s", + name, expand_string_message); + + p = op; + if (*p != 0) count++; + while (*p != 0) if (*p++ == ':' && *p != 0) count++; + list = store_malloc(count*sizeof(uid_t)); + list[ptr++] = (uid_t)(count - 1); + + if (data_block) + *((uid_t **)(US data_block + ol->v.offset)) = list; + else + *((uid_t **)ol->v.value) = list; - p = op; - while (count-- > 1) - { - int sep = 0; - /* If p is tainted we trap. Not sure that can happen */ - (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE); - if (!route_finduser(big_buffer, NULL, &uid)) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found", - big_buffer); - list[ptr++] = uid; - } - } - break; + p = op; + while (count-- > 1) + { + int sep = 0; + /* If p is tainted we trap. Not sure that can happen */ + (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE); + if (!route_finduser(big_buffer, NULL, &uid)) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found", + big_buffer); + list[ptr++] = uid; + } + break; + } - /* If it was a gid list, look up each individual entry, and build - a vector of gids, with a count in the first element. Put the vector - in malloc store so we can free the string. (We are reading into permanent - store already.) */ + /* If it was a gid list, look up each individual entry, and build + a vector of gids, with a count in the first element. Put the vector + in malloc store so we can free the string. (We are reading into permanent + store already.) */ - case opt_gidlist: - { - int count = 1; - gid_t *list; - int ptr = 0; - const uschar *p; - const uschar *op = expand_string (sptr); - - if (!op) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s", - name, expand_string_message); - - p = op; - if (*p != 0) count++; - while (*p != 0) if (*p++ == ':' && *p != 0) count++; - list = store_malloc(count*sizeof(gid_t)); - list[ptr++] = (gid_t)(count - 1); - - if (data_block) - *((gid_t **)(US data_block + ol->v.offset)) = list; - else - *((gid_t **)ol->v.value) = list; + case opt_gidlist: + { + int count = 1; + gid_t *list; + int ptr = 0; + const uschar *p; + const uschar *op = expand_string (sptr); + + if (!op) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s", + name, expand_string_message); + + p = op; + if (*p != 0) count++; + while (*p != 0) if (*p++ == ':' && *p != 0) count++; + list = store_malloc(count*sizeof(gid_t)); + list[ptr++] = (gid_t)(count - 1); + + if (data_block) + *((gid_t **)(US data_block + ol->v.offset)) = list; + else + *((gid_t **)ol->v.value) = list; - p = op; - while (count-- > 1) - { - int sep = 0; - /* If p is tainted we trap. Not sure that can happen */ - (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE); - if (!route_findgroup(big_buffer, &gid)) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found", - big_buffer); - list[ptr++] = gid; - } + p = op; + while (count-- > 1) + { + int sep = 0; + /* If p is tainted we trap. Not sure that can happen */ + (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE); + if (!route_findgroup(big_buffer, &gid)) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found", + big_buffer); + list[ptr++] = gid; + } + break; + } } - break; - } - /* Release store if the value of the string doesn't need to be kept. */ + /* Release store if the value of the string doesn't need to be kept. */ - if (freesptr) reset_point = store_reset(reset_point); - break; + if (freesptr) reset_point = store_reset(reset_point); + break; /* Expanded boolean: if no characters follow, or if there are no dollar characters, this is a fixed-valued boolean, and we fall through. Otherwise, save the string for later expansion in the alternate place. */ case opt_expand_bool: - if (*s && Ustrchr(s, '$') != 0) - { - sprintf(CS name2, "*expand_%.50s", name); - if ((ol2 = find_option(name2, oltop, last))) + if (*s && Ustrchr(s, '$') != 0) { - reset_point = store_mark(); - sptr = read_string(s, name); - if (data_block) - *(USS(US data_block + ol2->v.offset)) = sptr; - else - *(USS ol2->v.value) = sptr; - freesptr = FALSE; - break; + sprintf(CS name2, "*expand_%.50s", name); + if ((ol2 = find_option(name2, oltop, last))) + { + reset_point = store_mark(); + sptr = read_string(s, name); + if (data_block) + *(USS(US data_block + ol2->v.offset)) = sptr; + else + *(USS ol2->v.value) = sptr; + freesptr = FALSE; + break; + } } - } - /* Fall through */ + /* Fall through */ /* Boolean: if no characters follow, the value is boolvalue. Otherwise look for yes/not/true/false. Some booleans are stored in a single bit in @@ -2095,121 +2095,121 @@ switch (type) case opt_bit: case opt_bool_verify: case opt_bool_set: - if (*s != 0) - { - s = readconf_readname(name2, EXIM_DRIVERNAME_MAX, s); - if (strcmpic(name2, US"true") == 0 || strcmpic(name2, US"yes") == 0) - boolvalue = TRUE; - else if (strcmpic(name2, US"false") == 0 || strcmpic(name2, US"no") == 0) - boolvalue = FALSE; - else log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, - "\"%s\" is not a valid value for the \"%s\" option", name2, name); - if (*s != 0) extra_chars_error(s, string_sprintf("\"%s\" ", name2), - US"for boolean option ", name); - } + if (*s) + { + s = readconf_readname(name2, EXIM_DRIVERNAME_MAX, s); + if (strcmpic(name2, US"true") == 0 || strcmpic(name2, US"yes") == 0) + boolvalue = TRUE; + else if (strcmpic(name2, US"false") == 0 || strcmpic(name2, US"no") == 0) + boolvalue = FALSE; + else log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, + "\"%s\" is not a valid value for the \"%s\" option", name2, name); + if (*s != 0) extra_chars_error(s, string_sprintf("\"%s\" ", name2), + US"for boolean option ", name); + } - /* Handle single-bit type. */ + /* Handle single-bit type. */ - if (type == opt_bit) - { - int bit = 1 << ((ol->type >> 16) & 31); - int * ptr = data_block - ? (int *)(US data_block + ol->v.offset) - : (int *)ol->v.value; - if (boolvalue) *ptr |= bit; else *ptr &= ~bit; - break; - } + if (type == opt_bit) + { + int bit = 1 << ((ol->type >> 16) & 31); + int * ptr = data_block + ? (int *)(US data_block + ol->v.offset) + : (int *)ol->v.value; + if (boolvalue) *ptr |= bit; else *ptr &= ~bit; + break; + } - /* Handle full BOOL types */ + /* Handle full BOOL types */ - if (data_block) - *((BOOL *)(US data_block + ol->v.offset)) = boolvalue; - else - *((BOOL *)ol->v.value) = boolvalue; + if (data_block) + *((BOOL *)(US data_block + ol->v.offset)) = boolvalue; + else + *((BOOL *)ol->v.value) = boolvalue; - /* Verify fudge */ + /* Verify fudge */ - if (type == opt_bool_verify) - { - sprintf(CS name2, "%.50s_recipient", name + offset); - if ((ol2 = find_option(name2, oltop, last))) - if (data_block) - *((BOOL *)(US data_block + ol2->v.offset)) = boolvalue; - else - *((BOOL *)ol2->v.value) = boolvalue; - } + if (type == opt_bool_verify) + { + sprintf(CS name2, "%.50s_recipient", name + offset); + if ((ol2 = find_option(name2, oltop, last))) + if (data_block) + *((BOOL *)(US data_block + ol2->v.offset)) = boolvalue; + else + *((BOOL *)ol2->v.value) = boolvalue; + } - /* Note that opt_bool_set type is set, if there is somewhere to do so */ + /* Note that opt_bool_set type is set, if there is somewhere to do so */ - else if (type == opt_bool_set) - { - sprintf(CS name2, "*set_%.50s", name + offset); - if ((ol2 = find_option(name2, oltop, last))) - if (data_block) - *((BOOL *)(US data_block + ol2->v.offset)) = TRUE; - else - *((BOOL *)ol2->v.value) = TRUE; - } - break; + else if (type == opt_bool_set) + { + sprintf(CS name2, "*set_%.50s", name + offset); + if ((ol2 = find_option(name2, oltop, last))) + if (data_block) + *((BOOL *)(US data_block + ol2->v.offset)) = TRUE; + else + *((BOOL *)ol2->v.value) = TRUE; + } + break; /* Octal integer */ case opt_octint: - intbase = 8; - inttype = US"octal "; + intbase = 8; + inttype = US"octal "; /* Integer: a simple(ish) case; allow octal and hex formats, and suffixes K, M, G, and T. The different types affect output, not input. */ case opt_mkint: case opt_int: - { - uschar *endptr; - long int lvalue; + { + uschar *endptr; + long int lvalue; - errno = 0; - lvalue = strtol(CS s, CSS &endptr, intbase); + errno = 0; + lvalue = strtol(CS s, CSS &endptr, intbase); - if (endptr == s) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%sinteger expected for %s", - inttype, name); - - if (errno != ERANGE && *endptr) - { - uschar * mp = US"TtGgMmKk\0"; /* YyZzEePpTtGgMmKk */ + if (endptr == s) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%sinteger expected for %s", + inttype, name); - if ((mp = Ustrchr(mp, *endptr))) + if (errno != ERANGE && *endptr) { - endptr++; - do + uschar * mp = US"TtGgMmKk\0"; /* YyZzEePpTtGgMmKk */ + + if ((mp = Ustrchr(mp, *endptr))) { - if (lvalue > INT_MAX/1024 || lvalue < INT_MIN/1024) + endptr++; + do { - errno = ERANGE; - break; + if (lvalue > INT_MAX/1024 || lvalue < INT_MIN/1024) + { + errno = ERANGE; + break; + } + lvalue *= 1024; } - lvalue *= 1024; + while (*(mp += 2)); } - while (*(mp += 2)); } - } - if (errno == ERANGE || lvalue > INT_MAX || lvalue < INT_MIN) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, - "absolute value of integer \"%s\" is too large (overflow)", s); + if (errno == ERANGE || lvalue > INT_MAX || lvalue < INT_MIN) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, + "absolute value of integer \"%s\" is too large (overflow)", s); - while (isspace(*endptr)) endptr++; - if (*endptr) - extra_chars_error(endptr, inttype, US"integer value for ", name); + while (isspace(*endptr)) endptr++; + if (*endptr) + extra_chars_error(endptr, inttype, US"integer value for ", name); - value = (int)lvalue; - } + value = (int)lvalue; + } - if (data_block) - *(int *)(US data_block + ol->v.offset) = value; - else - *(int *)ol->v.value = value; - break; + if (data_block) + *(int *)(US data_block + ol->v.offset) = value; + else + *(int *)ol->v.value = value; + break; /* Integer held in K: again, allow formats and suffixes as above. */ @@ -2261,56 +2261,56 @@ switch (type) /* Fixed-point number: held to 3 decimal places. */ case opt_fixed: - if (sscanf(CS s, "%d%n", &value, &count) != 1) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, - "fixed-point number expected for %s", name); + if (sscanf(CS s, "%d%n", &value, &count) != 1) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, + "fixed-point number expected for %s", name); - if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, - "integer \"%s\" is too large (overflow)", s); + if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, + "integer \"%s\" is too large (overflow)", s); - value *= 1000; + value *= 1000; - if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, - "integer \"%s\" is too large (overflow)", s); + if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, + "integer \"%s\" is too large (overflow)", s); - /* We get a coverity error here for using count, as it derived - from the tainted buffer pointed to by s, as parsed by sscanf(). - By the definition of sscanf we must be accessing between start - and end of s (assuming it is nul-terminated...) so ignore the error. */ - /* coverity[tainted_data] */ - if (s[count] == '.') - { - int d = 100; - while (isdigit(s[++count])) + /* We get a coverity error here for using count, as it derived + from the tainted buffer pointed to by s, as parsed by sscanf(). + By the definition of sscanf we must be accessing between start + and end of s (assuming it is nul-terminated...) so ignore the error. */ + /* coverity[tainted_data] */ + if (s[count] == '.') { - value += (s[count] - '0') * d; - d /= 10; + int d = 100; + while (isdigit(s[++count])) + { + value += (s[count] - '0') * d; + d /= 10; + } } - } - while (isspace(s[count])) count++; + while (isspace(s[count])) count++; - if (s[count] != 0) - extra_chars_error(s+count, US"fixed-point value for ", name, US""); + if (s[count] != 0) + extra_chars_error(s+count, US"fixed-point value for ", name, US""); - if (data_block) - *((int *)(US data_block + ol->v.offset)) = value; - else - *((int *)ol->v.value) = value; - break; + if (data_block) + *((int *)(US data_block + ol->v.offset)) = value; + else + *((int *)ol->v.value) = value; + break; /* There's a special routine to read time values. */ case opt_time: - value = readconf_readtime(s, 0, FALSE); - if (value < 0) - log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "invalid time value for %s", - name); - if (data_block) - *((int *)(US data_block + ol->v.offset)) = value; - else - *((int *)ol->v.value) = value; - break; + value = readconf_readtime(s, 0, FALSE); + if (value < 0) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "invalid time value for %s", + name); + if (data_block) + *((int *)(US data_block + ol->v.offset)) = value; + else + *((int *)ol->v.value) = value; + break; /* A time list is a list of colon-separated times, with the first element holding the size of the list and the second the number of @@ -3714,14 +3714,14 @@ readconf_driver_init( optionlist *driver_optionlist, int driver_optionlist_count) { -driver_instance **p = anchor; -driver_instance *d = NULL; -uschar *buffer; +driver_instance ** p = anchor; +driver_instance * d = NULL; +uschar * buffer; while ((buffer = get_config_line())) { uschar name[EXIM_DRIVERNAME_MAX]; - uschar *s; + const uschar * s; /* Read the first name on the line and test for the start of a new driver. A macro definition indicates the end of the previous driver. If this isn't the @@ -4241,8 +4241,6 @@ Returns: nothing static void readconf_acl(void) { -uschar *p; - /* Read each ACL and add it into the tree. Macro (re)definitions are allowed between ACLs. */ @@ -4251,10 +4249,10 @@ acl_line = get_config_line(); while(acl_line) { uschar name[EXIM_DRIVERNAME_MAX]; - tree_node *node; - uschar *error; + tree_node * node; + uschar * error; + const uschar * p = readconf_readname(name, sizeof(name), acl_line); - p = readconf_readname(name, sizeof(name), acl_line); if (isupper(*name) && *p == '=') { if (!macro_read_assignment(acl_line)) exim_exit(EXIT_FAILURE); commit 5c0ee71aefd482e1bfa8720b62e46bfce280a6a3 Author: Jeremy Harris Date: Wed Jun 29 14:14:44 2022 +0100 Build: fix commentary describing configuration directives diff --git a/src/src/EDITME b/src/src/EDITME index 53022e593..3c33dc5f3 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -473,11 +473,11 @@ PCRE2_CONFIG=yes #------------------------------------------------------------------------------ -# Comment out the following line to remove DANE support -# Note: Enabling this unconditionally overrides DISABLE_DNSSEC -# forces you to have SUPPORT_TLS enabled (the default). For DANE under -# GnuTLS we need an additional library. See TLS_LIBS or USE_GNUTLS_PC -# below. +# Comment out the following line to remove DANE support. +# Note: DANE support requires DNSSEC support (the default) and +# SUPPORT_TLS (the default). For DANE under GnuTLS we need an additional +# library. See TLS_LIBS or USE_GNUTLS_PC below. + SUPPORT_DANE=yes #------------------------------------------------------------------------------ @@ -568,7 +568,7 @@ DISABLE_MAL_MKS=yes # By default, Exim has support for checking the AD bit in a DNS response, to # determine if DNSSEC validation was successful. If your system libraries # do not support that bit, then set DISABLE_DNSSEC to "yes" -# Note: Enabling SUPPORT_DANE unconditionally overrides this setting. +# Note: DNSSEC is required for DANE support. # DISABLE_DNSSEC=yes commit 6d05006594ffacbfde1c8c4e759332873a702087 Author: Jeremy Harris Date: Sun Jul 3 12:42:31 2022 +0100 OpenSSL: add IP & DN to OCSP fail log line diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 2b8a4e61c..4c61fc0e6 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2405,7 +2405,7 @@ BIO_puts(bp, "\n"); } static int -tls_client_stapling_cb(SSL *s, void *arg) +tls_client_stapling_cb(SSL * ssl, void * arg) { exim_openssl_state_st * cbinfo = arg; const unsigned char * p; @@ -2415,10 +2415,10 @@ OCSP_BASICRESP * bs; int i; DEBUG(D_tls) debug_printf("Received TLS status callback (OCSP stapling):\n"); -len = SSL_get_tlsext_status_ocsp_resp(s, &p); +len = SSL_get_tlsext_status_ocsp_resp(ssl, &p); if(!p) { /* Expect this when we requested ocsp but got none */ - if (SSL_session_reused(s) && tls_out.ocsp == OCSP_VFIED) + if (SSL_session_reused(ssl) && tls_out.ocsp == OCSP_VFIED) { DEBUG(D_tls) debug_printf(" null, but resumed; ocsp vfy stored with session is good\n"); return 1; @@ -2476,9 +2476,19 @@ if (!(bs = OCSP_response_get1_basic(rsp))) if (ERR_peek_error()) { tls_out.ocsp = OCSP_FAILED; - if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN, - "Received TLS cert status response, itself unverifiable: %s", - ERR_reason_error_string(ERR_peek_error())); + if (LOGGING(tls_cipher)) + { + const uschar * errstr = CUS ERR_reason_error_string(ERR_peek_error()); + static uschar peerdn[256]; + X509_NAME_oneline(X509_get_subject_name(SSL_get_peer_certificate(ssl)), + CS peerdn, sizeof(peerdn)); + log_write(0, LOG_MAIN, + "[%s] %s Received TLS cert (DN: '%.*s') status response, " + "itself unverifiable: %s", + sender_host_address, sender_host_name, + (int)sizeof(peerdn), peerdn, + errstr); + } DEBUG(D_tls) { BIO_printf(bp, "OCSP response verify failure\n"); commit e7ec503729970a03d4509921342bc81313976126 Author: Jeremy Harris Date: Tue Jul 12 22:14:04 2022 +0100 Fix exit on attempt to rewrite a malformed address. Bug 2903 diff --git a/src/src/rewrite.c b/src/src/rewrite.c index bfd78b5f1..90614e626 100644 --- a/src/src/rewrite.c +++ b/src/src/rewrite.c @@ -497,15 +497,14 @@ while (*s) if (!recipient) { - /* Handle unparesable addresses in the header. Slightly ugly because a + /* Log unparesable addresses in the header. Slightly ugly because a null output from the extract can also result from a header without an - address, "To: undisclosed recpients:;" being the classic case. */ + address, "To: undisclosed recpients:;" being the classic case. Ignore + this one and carry on. */ if ((rewrite_rules || routed_old) && Ustrcmp(errmess, "empty address") != 0) - { log_write(0, LOG_MAIN, "rewrite: %s", errmess); - exim_exit(EXIT_FAILURE); - } + loop_reset_point = store_reset(loop_reset_point); continue; } commit 7a97480c2bb86d9385597680ec2d4461e1656193 Author: Jeremy Harris Date: Tue Jul 19 23:37:27 2022 +0100 Event for auth fail diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index edb0adfaf..4a5c44714 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -3765,7 +3765,7 @@ smtp_respond(code, len, TRUE, user_msg); static int -smtp_in_auth(auth_instance *au, uschar ** s, uschar ** ss) +smtp_in_auth(auth_instance *au, uschar ** smtp_resp, uschar ** errmsg) { const uschar *set_id = NULL; int rc; @@ -3829,7 +3829,7 @@ switch(rc) received_protocol = (sender_host_address ? protocols : protocols_local) [pextend + pauthed + (tls_in.active.sock >= 0 ? pcrpted:0)]; - *s = *ss = US"235 Authentication succeeded"; + *smtp_resp = *errmsg = US"235 Authentication succeeded"; authenticated_by = au; break; } @@ -3842,34 +3842,34 @@ switch(rc) case DEFER: if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE); - *s = string_sprintf("435 Unable to authenticate at present%s", + *smtp_resp = string_sprintf("435 Unable to authenticate at present%s", auth_defer_user_msg); - *ss = string_sprintf("435 Unable to authenticate at present%s: %s", + *errmsg = string_sprintf("435 Unable to authenticate at present%s: %s", set_id, auth_defer_msg); break; case BAD64: - *s = *ss = US"501 Invalid base64 data"; + *smtp_resp = *errmsg = US"501 Invalid base64 data"; break; case CANCELLED: - *s = *ss = US"501 Authentication cancelled"; + *smtp_resp = *errmsg = US"501 Authentication cancelled"; break; case UNEXPECTED: - *s = *ss = US"553 Initial data not expected"; + *smtp_resp = *errmsg = US"553 Initial data not expected"; break; case FAIL: if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE); - *s = US"535 Incorrect authentication data"; - *ss = string_sprintf("535 Incorrect authentication data%s", set_id); + *smtp_resp = US"535 Incorrect authentication data"; + *errmsg = string_sprintf("535 Incorrect authentication data%s", set_id); break; default: if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE); - *s = US"435 Internal error"; - *ss = string_sprintf("435 Internal error%s: return %d from authentication " + *smtp_resp = US"435 Internal error"; + *errmsg = string_sprintf("435 Internal error%s: return %d from authentication " "check", set_id, rc); break; } @@ -4098,7 +4098,13 @@ while (done <= 0) if (smtp_in_auth(au, &s, &ss) == OK) { DEBUG(D_auth) debug_printf("tls auth succeeded\n"); } else - { DEBUG(D_auth) debug_printf("tls auth not succeeded\n"); } + { + uschar * save_name = sender_host_authenticated; + DEBUG(D_auth) debug_printf("tls auth not succeeded\n"); + sender_host_authenticated = au->name; + (void) event_raise(event_action, US"auth:fail", s, NULL); + sender_host_authenticated = save_name; + } } break; } @@ -4188,6 +4194,8 @@ while (done <= 0) { auth_instance * au; + uschar * smtp_resp, * errmsg; + for (au = auths; au; au = au->next) if (strcmpic(s, au->public_name) == 0 && au->server && (au->advertised || f.allow_auth_unadvertised)) @@ -4195,12 +4203,19 @@ while (done <= 0) if (au) { - c = smtp_in_auth(au, &s, &ss); + int rc = smtp_in_auth(au, &smtp_resp, &errmsg); + + smtp_printf("%s\r\n", FALSE, smtp_resp); + if (rc != OK) + { + uschar * save_name = sender_host_authenticated; - smtp_printf("%s\r\n", FALSE, s); - if (c != OK) log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s", - au->name, host_and_ident(FALSE), ss); + au->name, host_and_ident(FALSE), errmsg); + sender_host_authenticated = au->name; + (void) event_raise(event_action, US"auth:fail", smtp_resp, NULL); + sender_host_authenticated = save_name; + } } else done = synprot_error(L_smtp_protocol_error, 504, NULL, @@ -4389,7 +4404,7 @@ while (done <= 0) else { - char *ss; + char * ss; int codelen = 4; smtp_message_code(&smtp_code, &codelen, &user_msg, NULL, TRUE); s = string_sprintf("%.*s%s", codelen, smtp_code, user_msg); commit 9b3a1518f52a1de4469c85af8dde74489b974a66 Author: Jeremy Harris Date: Thu Jul 21 16:41:25 2022 +0100 Event for auth fail: client side diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 4a5c44714..11e7436b9 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -4099,11 +4099,16 @@ while (done <= 0) { DEBUG(D_auth) debug_printf("tls auth succeeded\n"); } else { - uschar * save_name = sender_host_authenticated; DEBUG(D_auth) debug_printf("tls auth not succeeded\n"); - sender_host_authenticated = au->name; - (void) event_raise(event_action, US"auth:fail", s, NULL); - sender_host_authenticated = save_name; +#ifndef DISABLE_EVENT + { + uschar * save_name = sender_host_authenticated, * logmsg; + sender_host_authenticated = au->name; + if ((logmsg = event_raise(event_action, US"auth:fail", s, NULL))) + log_write(0, LOG_MAIN, "%s", logmsg); + sender_host_authenticated = save_name; + } +#endif } } break; @@ -4208,13 +4213,19 @@ while (done <= 0) smtp_printf("%s\r\n", FALSE, smtp_resp); if (rc != OK) { - uschar * save_name = sender_host_authenticated; - - log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s", - au->name, host_and_ident(FALSE), errmsg); - sender_host_authenticated = au->name; - (void) event_raise(event_action, US"auth:fail", smtp_resp, NULL); - sender_host_authenticated = save_name; + uschar * logmsg = NULL; +#ifndef DISABLE_EVENT + {uschar * save_name = sender_host_authenticated; + sender_host_authenticated = au->name; + logmsg = event_raise(event_action, US"auth:fail", smtp_resp, NULL); + sender_host_authenticated = save_name; + } +#endif + if (logmsg) + log_write(0, LOG_MAIN|LOG_REJECT, "%s", logmsg); + else + log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s", + au->name, host_and_ident(FALSE), errmsg); } } else diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 6eee04d03..d7f251b0b 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1476,7 +1476,7 @@ rc = (au->info->clientcode)(au, sx, ob->command_timeout, sx->buffer, sizeof(sx->buffer)); sx->outblock.authenticating = FALSE; driver_srcfile = authenticator_name = NULL; driver_srcline = 0; -DEBUG(D_transport) debug_printf("%s authenticator yielded %d\n", au->name, rc); +DEBUG(D_transport) debug_printf("%s authenticator yielded %s\n", au->name, rc_names[rc]); /* A temporary authentication failure must hold up delivery to this host. After a permanent authentication failure, we carry on @@ -1500,10 +1500,25 @@ switch(rc) /* Failure after reading a response */ case FAIL: + { + uschar * logmsg = NULL; + if (errno != 0 || sx->buffer[0] != '5') return FAIL; - log_write(0, LOG_MAIN, "%s authenticator failed H=%s [%s] %s", - au->name, host->name, host->address, sx->buffer); +#ifndef DISABLE_EVENT + { + uschar * save_name = sender_host_authenticated; + sender_host_authenticated = au->name; + if ((logmsg = event_raise(sx->conn_args.tblock->event_action, US"auth:fail", + sx->buffer, NULL))) + log_write(0, LOG_MAIN, "%s", logmsg); + sender_host_authenticated = save_name; + } +#endif + if (!logmsg) + log_write(0, LOG_MAIN, "%s authenticator failed H=%s [%s] %s", + au->name, host->name, host->address, sx->buffer); break; + } /* Failure by some other means. In effect, the authenticator decided it wasn't prepared to handle this case. Typically this commit 93c722ce0549360af68269f088f4e59ed8fc130e Author: Jeremy Harris Date: Sun Aug 7 17:00:27 2022 +0100 SPF: fix memory accounting for error case diff --git a/src/src/spf.c b/src/src/spf.c index db6eea3a8..a8c0f75c4 100644 --- a/src/src/spf.c +++ b/src/src/spf.c @@ -204,7 +204,7 @@ spf_nxdomain = SPF_dns_rr_new_init(spf_dns_server, "", ns_t_any, 24 * 60 * 60, HOST_NOT_FOUND); if (!spf_nxdomain) { - free(spf_dns_server); + store_free(spf_dns_server); return NULL; } commit ef57b25bfa7623c3f8a8e65f927165c4ddc7c43b Author: Jeremy Harris Date: Mon Aug 8 19:46:03 2022 +0100 Symlink following for TLS creds files diff --git a/src/src/tls.c b/src/src/tls.c index 3ed37bbb0..76e72b5f5 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -143,15 +143,29 @@ static BOOL tls_set_one_watch(const uschar * filename) # ifdef EXIM_HAVE_INOTIFY { +uschar buf[PATH_MAX]; +ssize_t len; uschar * s; if (Ustrcmp(filename, "system,cache") == 0) return TRUE; - if (!(s = Ustrrchr(filename, '/'))) return FALSE; + +for (unsigned loop = 20; + (len = readlink(CCS filename, CS buf, sizeof(buf))) >= 0; ) + { /* a symlink */ + if (--loop == 0) { errno = ELOOP; return FALSE; } + filename = buf[0] == '/' + ? string_copyn(buf, (unsigned)len) /* mem released by tls_set_watch */ + : string_sprintf("%.*s/%.*s", (int)(s - filename), (int)len); + s = Ustrrchr(filename, '/'); + } +if (errno != EINVAL) + return FALSE; /* other error */ + +/* not a symlink */ s = string_copyn(filename, s - filename); /* mem released by tls_set_watch */ -DEBUG(D_tls) debug_printf("watch dir '%s'\n", s); -/*XXX unclear what effect symlinked files will have for inotify */ +DEBUG(D_tls) debug_printf("watch dir '%s'\n", s); if (inotify_add_watch(tls_watch_fd, CCS s, IN_ONESHOT | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF commit b855ee2fb3776667c8b08aa6b80453d60e4fb509 Author: Jeremy Harris Date: Tue Aug 9 10:57:56 2022 +0100 Filter rDNS returns for bad chars diff --git a/src/src/globals.c b/src/src/globals.c index c95d24b47..574ee60a4 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -1016,6 +1016,10 @@ uschar *keep_environment = NULL; int keep_malformed = 4*24*60*60; /* 4 days */ uschar *eldap_dn = NULL; +const uschar *letter_digit_hyphen_dot = + US"abcdefghijklmnopqrstuvwxyz" + ".-0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; #ifdef EXPERIMENTAL_ESMTP_LIMITS uschar *limits_advertise_hosts = US"*"; #endif diff --git a/src/src/globals.h b/src/src/globals.h index c9ef5e484..3f3c798b7 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -680,6 +680,7 @@ extern uschar *keep_environment; /* Whitelist for environment variables */ extern int keep_malformed; /* Time to keep malformed messages */ extern uschar *eldap_dn; /* Where LDAP DNs are left */ +extern const uschar *letter_digit_hyphen_dot; /* Legitimate DNS host name chars */ #ifdef EXPERIMENTAL_ESMTP_LIMITS extern uschar *limits_advertise_hosts; /* for banner/EHLO pipelining */ #endif diff --git a/src/src/host.c b/src/src/host.c index fed9f4b5f..b6c2ea082 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -84,13 +84,13 @@ random_number(int limit) if (limit < 1) return 0; if (random_seed == 0) - { - if (f.running_in_test_harness) random_seed = 42; else + if (f.running_in_test_harness) + random_seed = 42; + else { int p = (int)getpid(); random_seed = (int)time(NULL) ^ ((p << 16) | p); } - } random_seed = 1103515245 * random_seed + 12345; return (unsigned int)(random_seed >> 16) % limit; } @@ -1646,6 +1646,7 @@ while ((ordername = string_nextinlist(&list, &sep, NULL, 0))) rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == T_PTR) { uschar * s = store_get(ssize, GET_TAINTED); /* names are tainted */ + unsigned slen; /* If an overlong response was received, the data will have been truncated and dn_expand may fail. */ @@ -1658,13 +1659,19 @@ while ((ordername = string_nextinlist(&list, &sep, NULL, 0))) break; } - store_release_above(s + Ustrlen(s) + 1); - if (!s[0]) + store_release_above(s + (slen = Ustrlen(s)) + 1); + if (!*s) { HDEBUG(D_host_lookup) debug_printf("IP address lookup yielded an " "empty name: treated as non-existent host name\n"); continue; } + if (Ustrspn(s, letter_digit_hyphen_dot) != slen) + { + HDEBUG(D_host_lookup) debug_printf("IP address lookup yielded an " + "illegal name (bad char): treated as non-existent host name\n"); + continue; + } if (!sender_host_name) sender_host_name = s; else *aptr++ = s; while (*s) { *s = tolower(*s); s++; } commit 766af058762b2c4e21b69f335adf77265fe9509a Author: Jeremy Harris Date: Sat Aug 13 20:58:29 2022 +0100 Dump stack for "bad memory reference". Bug 2904 diff --git a/src/src/exim.c b/src/src/exim.c index 23e206d2a..ea4286af3 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -232,7 +232,7 @@ exit(1); ***********************************************/ #define STACKDUMP_MAX 24 -static void +void stackdump(void) { #ifndef NO_EXECINFO diff --git a/src/src/functions.h b/src/src/functions.h index e71823410..92a4831e3 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -547,6 +547,7 @@ extern int stdin_ferror(void); extern BOOL stdin_hasc(void); extern int stdin_ungetc(int); +extern void stackdump(void); extern void store_exit(void); extern void store_init(void); extern void store_writeprotect(int); diff --git a/src/src/store.c b/src/src/store.c index c98fcbf21..d99d2ab69 100644 --- a/src/src/store.c +++ b/src/src/store.c @@ -274,6 +274,9 @@ for (pp = paired_pools; pp < paired_pools + N_PAIRED_POOLS; pp++) for (b = pp->chainbase; b; b = b->next) if (is_pointer_in_block(b, p)) return pp; +#ifndef COMPILE_UTILITY +stackdump(); +#endif log_write(0, LOG_MAIN|LOG_PANIC_DIE, "bad memory reference; pool not found, at %s %d", func, linenumber); return NULL; commit fd9f0b7354ffc2986f0b2e7b074117feb29b5102 Author: Jeremy Harris Date: Sat Aug 13 20:19:16 2022 +0100 Partial workaround for TCP Fast Open issue. Bug 2907 diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index 7b7bdf752..7f364d942 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -372,7 +372,7 @@ if (!save_errno) # ifdef TCP_FASTOPEN_CONNECT else { /* expecting client data */ - debug_printf(" set up lazy-connect\n"); + DEBUG(D_transport|D_acl|D_v) debug_printf(" set up lazy-connect\n"); setsockopt(sock, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, US &on, sizeof(on)); /* fastopen_blob = NULL; lazy TFO, triggered by data write */ } diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index d7f251b0b..bbff1cad8 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -5312,6 +5312,17 @@ retry_non_continued: uschar *retry_message_key = NULL; uschar *serialize_key = NULL; + /* Deal slightly better with a possible Linux kernel bug that results + in intermittent TFO-conn fails deep into the TCP flow. Bug 2907 tracks. + Hack: Clear TFO option for any further hosts on this tpt run. */ + + if (total_hosts_tried > 0) + { + DEBUG(D_transport|D_acl|D_v) + debug_printf("Clearing TFO as not first host for message\n"); + ob->hosts_try_fastopen = US""; + } + /* Default next host is next host. :-) But this can vary if the hosts_max_try limit is hit (see below). It may also be reset if a host address is looked up here (in case the host was multihomed). */ commit a1ec98dd963767551514cae86c7af56c6aa3f36e Author: Ulrich Landgraf Date: Mon Aug 15 16:50:36 2022 +0100 Fix symlink-following. Bug 2909 Broken-by: ef57b25bfa diff --git a/src/src/tls.c b/src/src/tls.c index 76e72b5f5..32b29ee3e 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -156,7 +156,7 @@ for (unsigned loop = 20; if (--loop == 0) { errno = ELOOP; return FALSE; } filename = buf[0] == '/' ? string_copyn(buf, (unsigned)len) /* mem released by tls_set_watch */ - : string_sprintf("%.*s/%.*s", (int)(s - filename), (int)len); + : string_sprintf("%.*s/%.*s", (int)(s - filename), filename, (int)len, buf); s = Ustrrchr(filename, '/'); } if (errno != EINVAL) commit 9b5fdee115fdd09588bd03f3ebdc85cfe7357fc5 Author: Jeremy Harris Date: Mon Aug 15 18:42:28 2022 +0100 OpenSSL: strip spaces & newlines from cert signature expansion diff --git a/src/src/tlscert-openssl.c b/src/src/tlscert-openssl.c index 168e35b7f..ac353b25f 100644 --- a/src/src/tlscert-openssl.c +++ b/src/src/tlscert-openssl.c @@ -261,11 +261,19 @@ if (X509_print_ex(bp, (X509 *)cert, 0, X509_FLAG_NO_AUX) == 1) { long len = BIO_get_mem_data(bp, &cp); + gstring * g = NULL; /* Strip leading "Signature Algorithm" line */ while (*cp && *cp != '\n') { cp++; len--; } + if (*cp) { cp++; len--; } - cp = string_copyn(cp+1, len-1); + /* Strip possible leading " Signature Value:\n" (seen with OpenSSL 3.0.5) */ + if (Ustrncmp(cp, " Signature Value:\n", 21) == 0) { cp += 21; len -= 21; } + + /* Copy only hexchars and colon (different OpenSSL versions do different spacing) */ + for ( ; len-- && *cp; cp++) + if (Ustrchr("0123456789abcdef:", *cp)) g = string_catn(g, cp, 1); + cp = string_from_gstring(g); } BIO_free(bp); return cp; commit 4e3a01c2607937d5fbc477b6e14495adc2281941 Author: Jeremy Harris Date: Thu Aug 18 20:47:01 2022 +0100 GSASL: use tls-exporter for SCRAM*PLUS methods under TLSv1.3 diff --git a/src/src/auths/gsasl_exim.c b/src/src/auths/gsasl_exim.c index bae5f081b..e49e83b81 100644 --- a/src/src/auths/gsasl_exim.c +++ b/src/src/auths/gsasl_exim.c @@ -39,22 +39,34 @@ static void dummy(int x) { dummy2(x-1); } #include "gsasl_exim.h" -#if GSASL_VERSION_MINOR >= 10 -# define EXIM_GSASL_HAVE_SCRAM_SHA_256 -# define EXIM_GSASL_SCRAM_S_KEY +#if GSASL_VERSION_MAJOR == 2 -#elif GSASL_VERSION_MINOR == 9 # define EXIM_GSASL_HAVE_SCRAM_SHA_256 +# define EXIM_GSASL_SCRAM_S_KEY +# if GSASL_VERSION_MINOR >= 1 +# define EXIM_GSASL_HAVE_EXPORTER +# elif GSASL_VERSION_PATCH >= 1 +# define EXIM_GSASL_HAVE_EXPORTER +# endif -# if GSASL_VERSION_PATCH >= 1 +#elif GSASL_VERSION_MAJOR == 1 +# if GSASL_VERSION_MINOR >= 10 +# define EXIM_GSASL_HAVE_SCRAM_SHA_256 # define EXIM_GSASL_SCRAM_S_KEY -# endif -# if GSASL_VERSION_PATCH < 2 + +# elif GSASL_VERSION_MINOR == 9 +# define EXIM_GSASL_HAVE_SCRAM_SHA_256 + +# if GSASL_VERSION_PATCH >= 1 +# define EXIM_GSASL_SCRAM_S_KEY +# endif +# if GSASL_VERSION_PATCH < 2 +# define CHANNELBIND_HACK +# endif + +# else # define CHANNELBIND_HACK # endif - -#else -# define CHANNELBIND_HACK #endif /* Convenience for testing strings */ @@ -258,7 +270,7 @@ if (!cb_state) if (prop == GSASL_CB_TLS_UNIQUE) { uschar * s; - if ((s = gsasl_callback_hook_get(ctx))) + if ((s = gsasl_callback_hook_get(ctx))) /* Gross hack for early lib vers */ { HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE from ctx hook\n"); gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CS s); @@ -332,6 +344,9 @@ switch (prop) #ifdef EXIM_GSASL_SCRAM_S_KEY case GSASL_SCRAM_STOREDKEY: return US"SCRAM_STOREDKEY"; case GSASL_SCRAM_SERVERKEY: return US"SCRAM_SERVERKEY"; +#endif +#ifdef EXIM_GSASL_HAVE_EXPORTER /* v. 2.1.0 */ + case GSASL_CB_TLS_EXPORTER: return US"CB_TLS_EXPORTER"; #endif case GSASL_CB_TLS_UNIQUE: return US"CB_TLS_UNIQUE"; case GSASL_SAML20_IDP_IDENTIFIER: return US"SAML20_IDP_IDENTIFIER"; @@ -351,6 +366,14 @@ switch (prop) return CUS string_sprintf("(unknown prop: %d)", (int)prop); } +static void +preload_prop(Gsasl_session * sctx, Gsasl_property propcode, const uschar * val) +{ +DEBUG(D_auth) debug_printf("preloading prop %s val %s\n", + gsasl_prop_code_to_name(propcode), val); +gsasl_property_set(sctx, propcode, CCS val); +} + /************************************************* * Server entry point * *************************************************/ @@ -358,12 +381,12 @@ return CUS string_sprintf("(unknown prop: %d)", (int)prop); /* For interface, see auths/README */ int -auth_gsasl_server(auth_instance *ablock, uschar *initial_data) +auth_gsasl_server(auth_instance * ablock, uschar * initial_data) { -char *tmps; -char *to_send, *received; -Gsasl_session *sctx = NULL; -auth_gsasl_options_block *ob = +uschar * tmps; +char * to_send, * received; +Gsasl_session * sctx = NULL; +auth_gsasl_options_block * ob = (auth_gsasl_options_block *)(ablock->options_block); struct callback_exim_state cb_state; int rc, auth_result, exim_error, exim_error_override; @@ -406,18 +429,18 @@ cb_state.ablock = ablock; cb_state.currently = CURRENTLY_SERVER; gsasl_session_hook_set(sctx, &cb_state); -tmps = CS expand_string(ob->server_service); -gsasl_property_set(sctx, GSASL_SERVICE, tmps); -tmps = CS expand_string(ob->server_hostname); -gsasl_property_set(sctx, GSASL_HOSTNAME, tmps); +tmps = expand_string(ob->server_service); +preload_prop(sctx, GSASL_SERVICE, tmps); +tmps = expand_string(ob->server_hostname); +preload_prop(sctx, GSASL_HOSTNAME, tmps); if (ob->server_realm) { - tmps = CS expand_string(ob->server_realm); + tmps = expand_string(ob->server_realm); if (tmps && *tmps) - gsasl_property_set(sctx, GSASL_REALM, tmps); + preload_prop(sctx, GSASL_REALM, tmps); } /* We don't support protection layers. */ -gsasl_property_set(sctx, GSASL_QOPS, "qop-auth"); +preload_prop(sctx, GSASL_QOPS, US "qop-auth"); #ifndef DISABLE_TLS if (tls_in.channelbinding) @@ -451,7 +474,12 @@ if (tls_in.channelbinding) HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n", ablock->name); # ifndef CHANNELBIND_HACK - gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_in.channelbinding); + preload_prop(sctx, +# ifdef EXIM_GSASL_HAVE_EXPORTER + tls_in.channelbind_exporter ? GSASL_CB_TLS_EXPORTER : +# endif + GSASL_CB_TLS_UNIQUE, + tls_in.channelbinding); # endif } else @@ -811,13 +839,13 @@ return TRUE; int auth_gsasl_client( - auth_instance *ablock, /* authenticator block */ + auth_instance * ablock, /* authenticator block */ void * sx, /* connection */ int timeout, /* command timeout */ - uschar *buffer, /* buffer for reading response */ + uschar * buffer, /* buffer for reading response */ int buffsize) /* size of buffer */ { -auth_gsasl_options_block *ob = +auth_gsasl_options_block * ob = (auth_gsasl_options_block *)(ablock->options_block); Gsasl_session * sctx = NULL; struct callback_exim_state cb_state; @@ -883,7 +911,12 @@ if (tls_out.channelbinding) HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n", ablock->name); # ifndef CHANNELBIND_HACK - gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding); + preload_prop(sctx, +# ifdef EXIM_GSASL_HAVE_EXPORTER + tls_out.channelbind_exporter ? GSASL_CB_TLS_EXPORTER : +# endif + GSASL_CB_TLS_UNIQUE, + tls_out.channelbinding); # endif } else @@ -968,9 +1001,18 @@ HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as client\n", gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name); switch (prop) { - case GSASL_CB_TLS_UNIQUE: /*XXX should never get called for this */ - HDEBUG(D_auth) - debug_printf(" filling in\n"); +#ifdef EXIM_GSASL_HAVE_EXPORTER + case GSASL_CB_TLS_EXPORTER: /* Should never get called for this, as pre-set */ + if (!tls_out.channelbind_exporter) break; + HDEBUG(D_auth) debug_printf(" filling in\n"); + gsasl_property_set(sctx, GSASL_CB_TLS_EXPORTER, CCS tls_out.channelbinding); + return GSASL_OK; +#endif + case GSASL_CB_TLS_UNIQUE: /* Should never get called for this, as pre-set */ +#ifdef EXIM_GSASL_HAVE_EXPORTER + if (tls_out.channelbind_exporter) break; +#endif + HDEBUG(D_auth) debug_printf(" filling in\n"); gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding); return GSASL_OK; case GSASL_SCRAM_SALTED_PASSWORD: diff --git a/src/src/globals.h b/src/src/globals.h index 3f3c798b7..c40ae4beb 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -117,6 +117,7 @@ typedef struct { #endif BOOL verify_override:1; /* certificate_verified only due to tls_try_verify_hosts */ BOOL ext_master_secret:1; /* extended-master-secret was used */ + BOOL channelbind_exporter:1; /* channelbinding is EXPORTER not UNIQUE */ } tls_support; extern tls_support tls_in; extern tls_support tls_out; diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index fcb8f7ac4..7a6db94e1 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -121,6 +121,10 @@ require current GnuTLS, then we'll drop support for the ancient libraries). # endif #endif +#if GNUTLS_VERSION_NUMBER >= 0x030702 +# define HAVE_GNUTLS_EXPORTER +#endif + #ifndef DISABLE_OCSP # include #endif @@ -646,14 +650,20 @@ tlsp->channelbinding = NULL; #ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING { gnutls_datum_t channel = {.data = NULL, .size = 0}; - uschar * buf; int rc; -# ifdef HAVE_GNUTLS_PRF_RFC5705 +# ifdef HAVE_GNUTLS_EXPORTER + if (gnutls_protocol_get_version(state->session) >= GNUTLS_TLS1_3) + { + rc = gnutls_session_channel_binding(state->session, GNUTLS_CB_TLS_EXPORTER, &channel); + tlsp->channelbind_exporter = TRUE; + } + else +# elif defined(HAVE_GNUTLS_PRF_RFC5705) /* Older libraries may not have GNUTLS_TLS1_3 defined! */ if (gnutls_protocol_get_version(state->session) > GNUTLS_TLS1_2) { - buf = store_get(32, state->host ? GET_TAINTED : GET_UNTAINTED); + uschar * buf = store_get(32, state->host ? GET_TAINTED : GET_UNTAINTED); rc = gnutls_prf_rfc5705(state->session, (size_t)24, "EXPORTER-Channel-Binding", (size_t)0, "", 32, CS buf); @@ -670,11 +680,11 @@ tlsp->channelbinding = NULL; { int old_pool = store_pool; /* Declare the taintedness of the binding info. On server, untainted; on - client, tainted - being the Finish msg from the server. */ + client, tainted if we used the Finish msg from the server. */ store_pool = POOL_PERM; tlsp->channelbinding = b64encode_taint(CUS channel.data, (int)channel.size, - state->host ? GET_TAINTED : GET_UNTAINTED); + !tlsp->channelbind_exporter && state->host ? GET_TAINTED : GET_UNTAINTED); store_pool = old_pool; DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage\n"); } diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 4c61fc0e6..22750d273 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -94,6 +94,10 @@ change this guard and punt the issue for a while longer. */ # define EXIM_HAVE_OPENSSL_CIPHER_GET_ID #endif +#if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x030000000L) +# define EXIM_HAVE_EXPORT_CHNL_BNGNG +#endif + #if !defined(LIBRESSL_VERSION_NUMBER) \ || LIBRESSL_VERSION_NUMBER >= 0x20010000L # if !defined(OPENSSL_NO_ECDH) @@ -111,11 +115,16 @@ change this guard and punt the issue for a while longer. */ # define OPENSSL_HAVE_KEYLOG_CB # define OPENSSL_HAVE_NUM_TICKETS # define EXIM_HAVE_OPENSSL_CIPHER_STD_NAME +# define EXIM_HAVE_EXP_CHNL_BNGNG # else # define OPENSSL_BAD_SRVR_OURCERT # endif #endif +#if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x010002000L) +# define EXIM_HAVE_EXPORT_CHNL_BNGNG +#endif + #if !defined(EXIM_HAVE_OPENSSL_TLSEXT) && !defined(DISABLE_OCSP) # warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile" # define DISABLE_OCSP @@ -3170,6 +3179,52 @@ tls_dump_keylog(SSL * ssl) } +/* Channel-binding info for authenticators +See description in https://paquier.xyz/postgresql-2/channel-binding-openssl/ +for pre-TLS1.3 +*/ + +static void +tls_get_channel_binding(SSL * ssl, tls_support * tlsp, const void * taintval) +{ +uschar c, * s; +size_t len; + +#ifdef EXIM_HAVE_EXPORT_CHNL_BNGNG +if (SSL_version(ssl) >= TLS1_3_VERSION) + { + /* It's not documented by OpenSSL how big the output buffer must be. + The OpenSSL testcases use 80 bytes but don't say why. The GnuTLS impl only + serves out 32B. RFC 9266 says it is 32B. + Interop fails unless we use the same each end. */ + len = 32; + + tlsp->channelbind_exporter = TRUE; + taintval = GET_UNTAINTED; + if (SSL_export_keying_material(ssl, + s = store_get((int)len, taintval), len, + "EXPORTER-Channel-Binding", (size_t) 24, + NULL, 0, 0) != 1) + len = 0; + } +else +#endif + { + len = SSL_get_peer_finished(ssl, &c, 0); + len = SSL_get_peer_finished(ssl, s = store_get((int)len, taintval), len); + } + +if (len > 0) + { + int old_pool = store_pool; + store_pool = POOL_PERM; + tlsp->channelbinding = b64encode_taint(CUS s, (int)len, taintval); + store_pool = old_pool; + DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p %p\n", tlsp->channelbinding, tlsp); + } +} + + /************************************************* * Start a TLS session in a server * *************************************************/ @@ -3446,6 +3501,7 @@ else DEBUG(D_tls) adjust the input functions to read via TLS, and initialize things. */ #ifdef SSL_get_extms_support +/*XXX what does this return for tls1.3 ? */ tls_in.ext_master_secret = SSL_get_extms_support(ssl) == 1; #endif peer_cert(ssl, &tls_in, peerdn, sizeof(peerdn)); @@ -3478,19 +3534,7 @@ DEBUG(D_tls) tls_in.ourcert = crt ? X509_dup(crt) : NULL; } -/* Channel-binding info for authenticators -See description in https://paquier.xyz/postgresql-2/channel-binding-openssl/ */ - { - uschar c, * s; - size_t len = SSL_get_peer_finished(ssl, &c, 0); - int old_pool = store_pool; - - SSL_get_peer_finished(ssl, s = store_get((int)len, GET_UNTAINTED), len); - store_pool = POOL_PERM; - tls_in.channelbinding = b64encode_taint(CUS s, (int)len, GET_UNTAINTED); - store_pool = old_pool; - DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p\n", tls_in.channelbinding); - } +tls_get_channel_binding(ssl, &tls_in, GET_UNTAINTED); /* Only used by the server-side tls (tls_in), including tls_getc. Client-side (tls_out) reads (seem to?) go via @@ -4168,18 +4212,7 @@ tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl); } /*XXX will this work with continued-TLS? */ -/* Channel-binding info for authenticators */ - { - uschar c, * s; - size_t len = SSL_get_finished(exim_client_ctx->ssl, &c, 0); - int old_pool = store_pool; - - SSL_get_finished(exim_client_ctx->ssl, s = store_get((int)len, GET_TAINTED), len); - store_pool = POOL_PERM; - tlsp->channelbinding = b64encode_taint(CUS s, (int)len, GET_TAINTED); - store_pool = old_pool; - DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p %p\n", tlsp->channelbinding, tlsp); - } +tls_get_channel_binding(exim_client_ctx->ssl, tlsp, GET_TAINTED); tlsp->active.sock = cctx->sock; tlsp->active.tls_ctx = exim_client_ctx; diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index bbff1cad8..0fca4584d 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -4695,7 +4695,10 @@ if (sx->completed_addr && sx->ok && sx->send_quit) open, we must shut down TLS. Not all MTAs allow for the continuation of the SMTP session when TLS is shut down. We test for this by sending a new EHLO. If we don't get a good response, we don't attempt to pass - the socket on. */ + the socket on. + NB: TLS close is *required* per RFC 9266 when tls-exporter info has + been used, which we do under TLSv1.3 for the gsasl SCRAM*PLUS methods. + But we were always doing it anyway. */ tls_close(sx->cctx.tls_ctx, sx->send_tlsclose ? TLS_SHUTDOWN_WAIT : TLS_SHUTDOWN_WONLY); commit ba47f2909371787b76d213ea012c6b950f9f8080 Author: Jeremy Harris Date: Thu Aug 18 21:20:15 2022 +0100 OpenSSL: unbreak build with older library version Broken-by: 4e3a01c260 diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 22750d273..c46bc75a5 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -3191,7 +3191,7 @@ uschar c, * s; size_t len; #ifdef EXIM_HAVE_EXPORT_CHNL_BNGNG -if (SSL_version(ssl) >= TLS1_3_VERSION) +if (SSL_version(ssl) > TLS1_2_VERSION) { /* It's not documented by OpenSSL how big the output buffer must be. The OpenSSL testcases use 80 bytes but don't say why. The GnuTLS impl only commit 9641b6648d2d2d87e14856f9c3383deb86772757 Author: Jeremy Harris Date: Sat Aug 20 16:43:03 2022 +0100 OpenSSL: fix reload of changed OCSP proof diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index c46bc75a5..c63e56c0e 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -1477,12 +1477,12 @@ return; static void -ocsp_free_response_list(exim_openssl_state_st * cbinfo) +ocsp_free_response_list(exim_openssl_state_st * state) { -for (ocsp_resplist * olist = cbinfo->u_ocsp.server.olist; olist; +for (ocsp_resplist * olist = state->u_ocsp.server.olist; olist; olist = olist->next) OCSP_RESPONSE_free(olist->resp); -cbinfo->u_ocsp.server.olist = NULL; +state->u_ocsp.server.olist = NULL; } #endif /*!DISABLE_OCSP*/ @@ -1574,6 +1574,11 @@ else if (olist && !*olist) olist = NULL; + /* If doing a re-expand after SNI, avoid reloading the OCSP + responses when the list of filenames has not changed. + The creds-invali on content change wipes file_expanded, so that + always reloads here. */ + if ( state->u_ocsp.server.file_expanded && olist && (Ustrcmp(olist, state->u_ocsp.server.file_expanded) == 0)) { @@ -1918,6 +1923,7 @@ tls_server_creds_invalidate(void) { SSL_CTX_free(state_server.lib_state.lib_ctx); state_server.lib_state = null_tls_preload; +state_server.u_ocsp.server.file_expanded = NULL; } @@ -2763,7 +2769,7 @@ if (state->lib_state.conn_certs) else { #ifndef DISABLE_OCSP - if (!host) + if (!host) /* server */ { state->u_ocsp.server.file = ocsp_file; state->u_ocsp.server.file_expanded = NULL; diff --git a/src/src/tls.c b/src/src/tls.c index 32b29ee3e..d7cefce67 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -361,6 +361,8 @@ tls_watch_invalidate(); #endif tls_server_creds_invalidate(); + +/* _expire is for a time-limited selfsign server cert */ tls_creds_expire = (lifetime = tls_server_creds_init()) ? time(NULL) + lifetime : 0; commit 1072af868662ea8fec30454c2d62afdee24f2c8e Author: Jeremy Harris Date: Tue Aug 23 18:34:29 2022 +0100 OpenSSL: fix non-OCSP build Broken-by: 9641b6648d diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index c63e56c0e..043755c84 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -1923,7 +1923,9 @@ tls_server_creds_invalidate(void) { SSL_CTX_free(state_server.lib_state.lib_ctx); state_server.lib_state = null_tls_preload; +#ifndef DISABLE_OCSP state_server.u_ocsp.server.file_expanded = NULL; +#endif } commit 4e9ed49f8f12eb331b29bd5b6dc3693c520fddc2 Author: Jeremy Harris Date: Wed Aug 31 15:37:40 2022 +0100 Fix $regex use-after-free. Bug 2915 diff --git a/src/src/exim.c b/src/src/exim.c index ea4286af3..b9328f017 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -2000,8 +2000,6 @@ regex_whitelisted_macro = regex_must_compile(US"^[A-Za-z0-9_/.-]*$", MCS_NOFLAGS, TRUE); #endif -for (i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; - /* If the program is called as "mailq" treat it as equivalent to "exim -bp"; this seems to be a generally accepted convention, since one finds symbolic links called "mailq" in standard OS configurations. */ @@ -6089,7 +6087,7 @@ MORELOOP: deliver_localpart_data = deliver_domain_data = recipient_data = sender_data = NULL; acl_var_m = NULL; - for(int i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; + regex_vars_clear(); store_reset(reset_point); } diff --git a/src/src/expand.c b/src/src/expand.c index ffbdc14e5..89de56255 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1860,7 +1860,7 @@ else if (Ustrncmp(name, "r_", 2) == 0) return node ? node->data.ptr : strict_acl_vars ? NULL : US""; } -/* Handle $auth variables. */ +/* Handle $auth, $regex variables. */ if (Ustrncmp(name, "auth", 4) == 0) { diff --git a/src/src/functions.h b/src/src/functions.h index 92a4831e3..345d7bce6 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -447,6 +447,7 @@ extern BOOL regex_match_and_setup(const pcre2_code *, const uschar *, int, in extern const pcre2_code *regex_compile(const uschar *, mcs_flags, uschar **, pcre2_compile_context *); extern const pcre2_code *regex_must_compile(const uschar *, mcs_flags, BOOL); +extern void regex_vars_clear(void); extern void retry_add_item(address_item *, uschar *, int); extern BOOL retry_check_address(const uschar *, host_item *, uschar *, BOOL, uschar **, uschar **); diff --git a/src/src/globals.c b/src/src/globals.c index 574ee60a4..cafb15992 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -1324,7 +1324,7 @@ const pcre2_code *regex_EARLY_PIPE = NULL; int regex_cachesize = 0; const pcre2_code *regex_ismsgid = NULL; const pcre2_code *regex_smtp_code = NULL; -const uschar *regex_vars[REGEX_VARS]; +const uschar *regex_vars[REGEX_VARS] = { 0 };; #ifdef WHITELIST_D_MACROS const pcre2_code *regex_whitelisted_macro = NULL; #endif diff --git a/src/src/regex.c b/src/src/regex.c index 5de1c1704..25496f950 100644 --- a/src/src/regex.c +++ b/src/src/regex.c @@ -93,19 +93,27 @@ return FAIL; } +/* reset expansion variables */ +void +regex_vars_clear(void) +{ +regex_match_string = NULL; +for (int i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; +} + + int -regex(const uschar **listptr, BOOL cacheable) +regex(const uschar ** listptr, BOOL cacheable) { unsigned long mbox_size; -FILE *mbox_file; -pcre_list *re_list_head; -uschar *linebuffer; +FILE * mbox_file; +pcre_list * re_list_head; +uschar * linebuffer; long f_pos = 0; int ret = FAIL; -/* reset expansion variable */ -regex_match_string = NULL; +regex_vars_clear(); if (!mime_stream) /* We are in the DATA ACL */ { @@ -167,14 +175,13 @@ return ret; int mime_regex(const uschar **listptr, BOOL cacheable) { -pcre_list *re_list_head = NULL; -FILE *f; -uschar *mime_subject = NULL; +pcre_list * re_list_head = NULL; +FILE * f; +uschar * mime_subject = NULL; int mime_subject_len = 0; int ret; -/* reset expansion variable */ -regex_match_string = NULL; +regex_vars_clear(); /* precompile our regexes */ if (!(re_list_head = compile(*listptr, cacheable))) diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 11e7436b9..a15280bdc 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2157,8 +2157,10 @@ prdr_requested = FALSE; #ifdef SUPPORT_I18N message_smtputf8 = FALSE; #endif +regex_vars_clear(); body_linecount = body_zerocount = 0; +lookup_value = NULL; /* Can be set by ACL */ sender_rate = sender_rate_limit = sender_rate_period = NULL; ratelimiters_mail = NULL; /* Updated by ratelimit ACL condition */ /* Note that ratelimiters_conn persists across resets. */ commit d8ecc7bf97934a1e2244788c610c958cacd740bd Author: Jeremy Harris Date: Wed Aug 31 17:03:37 2022 +0100 Fix non-WITH_CONTENT_SCAN build. Broken-by: 4e9ed49f8f diff --git a/src/src/exim.c b/src/src/exim.c index b9328f017..82e833067 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1678,6 +1678,17 @@ else } +/* reset regex expansion variables */ +void +regex_vars_clear(void) +{ +regex_match_string = NULL; +for (int i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; +} + + + + /************************************************* * Entry point and high-level code * diff --git a/src/src/regex.c b/src/src/regex.c index 25496f950..3088f0066 100644 --- a/src/src/regex.c +++ b/src/src/regex.c @@ -93,16 +93,6 @@ return FAIL; } -/* reset expansion variables */ -void -regex_vars_clear(void) -{ -regex_match_string = NULL; -for (int i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; -} - - - int regex(const uschar ** listptr, BOOL cacheable) { commit 158dff9936e36a2d31d037d3988b9353458d6471 Author: Jeremy Harris Date: Wed Aug 31 17:17:59 2022 +0100 Fix non-WITH_CONTENT_SCAN build (2) Broken-by: d8ecc7bf97 diff --git a/src/src/exim.c b/src/src/exim.c index 82e833067..f2a787e8a 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1678,17 +1678,6 @@ else } -/* reset regex expansion variables */ -void -regex_vars_clear(void) -{ -regex_match_string = NULL; -for (int i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; -} - - - - /************************************************* * Entry point and high-level code * @@ -6092,13 +6081,13 @@ MORELOOP: dnslist_domain = dnslist_matched = NULL; #ifdef WITH_CONTENT_SCAN malware_name = NULL; + regex_vars_clear(); #endif callout_address = NULL; sending_ip_address = NULL; deliver_localpart_data = deliver_domain_data = recipient_data = sender_data = NULL; acl_var_m = NULL; - regex_vars_clear(); store_reset(reset_point); } diff --git a/src/src/functions.h b/src/src/functions.h index 345d7bce6..d5164547a 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -440,6 +440,7 @@ extern int_eximarith_t receive_statvfs(BOOL, int *); extern void receive_swallow_smtp(void); #ifdef WITH_CONTENT_SCAN extern int regex(const uschar **, BOOL); +extern void regex_vars_clear(void); #endif extern void regex_at_daemon(const uschar *); extern BOOL regex_match(const pcre2_code *, const uschar *, int, uschar **); @@ -447,7 +448,6 @@ extern BOOL regex_match_and_setup(const pcre2_code *, const uschar *, int, in extern const pcre2_code *regex_compile(const uschar *, mcs_flags, uschar **, pcre2_compile_context *); extern const pcre2_code *regex_must_compile(const uschar *, mcs_flags, BOOL); -extern void regex_vars_clear(void); extern void retry_add_item(address_item *, uschar *, int); extern BOOL retry_check_address(const uschar *, host_item *, uschar *, BOOL, uschar **, uschar **); diff --git a/src/src/globals.h b/src/src/globals.h index c40ae4beb..ae74147d4 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -904,12 +904,12 @@ extern const pcre2_code *regex_EARLY_PIPE; /* For recognizing PIPE_CONNCT */ extern int regex_cachesize; /* number of entries */ extern const pcre2_code *regex_ismsgid; /* Compiled r.e. for message ID */ extern const pcre2_code *regex_smtp_code; /* For recognizing SMTP codes */ -extern const uschar *regex_vars[]; /* $regexN variables */ #ifdef WHITELIST_D_MACROS extern const pcre2_code *regex_whitelisted_macro; /* For -D macro values */ #endif #ifdef WITH_CONTENT_SCAN extern uschar *regex_match_string; /* regex that matched a line (regex ACL condition) */ +extern const uschar *regex_vars[]; #endif extern int remote_delivery_count; /* Number of remote addresses */ extern int remote_max_parallel; /* Maximum parallel delivery */ diff --git a/src/src/regex.c b/src/src/regex.c index 3088f0066..25496f950 100644 --- a/src/src/regex.c +++ b/src/src/regex.c @@ -93,6 +93,16 @@ return FAIL; } +/* reset expansion variables */ +void +regex_vars_clear(void) +{ +regex_match_string = NULL; +for (int i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL; +} + + + int regex(const uschar ** listptr, BOOL cacheable) { diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index a15280bdc..28e529bae 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2157,7 +2157,9 @@ prdr_requested = FALSE; #ifdef SUPPORT_I18N message_smtputf8 = FALSE; #endif +#ifdef WITH_CONTENT_SCAN regex_vars_clear(); +#endif body_linecount = body_zerocount = 0; lookup_value = NULL; /* Can be set by ACL */ commit 32da6327e434e986a18b75a84f2d8c687ba14619 Author: Jeremy Harris Date: Thu Sep 1 15:54:35 2022 +0100 Fix non-WITH_CONTENT_SCAN build (3) Broken-by: d8ecc7bf97 diff --git a/src/src/expand.c b/src/src/expand.c index 89de56255..831ca2b75 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1869,6 +1869,7 @@ if (Ustrncmp(name, "auth", 4) == 0) if (!*endptr && n != 0 && n <= AUTH_VARS) return auth_vars[n-1] ? auth_vars[n-1] : US""; } +#ifdef WITH_CONTENT_SCAN else if (Ustrncmp(name, "regex", 5) == 0) { uschar *endptr; @@ -1876,6 +1877,7 @@ else if (Ustrncmp(name, "regex", 5) == 0) if (!*endptr && n != 0 && n <= REGEX_VARS) return regex_vars[n-1] ? regex_vars[n-1] : US""; } +#endif /* For all other variables, search the table */ @@ -8715,9 +8717,11 @@ assert_variable_notin() treats as const, so deconst is safe. */ for (int i = 0; i < AUTH_VARS; i++) if (auth_vars[i]) assert_variable_notin(US"auth", US auth_vars[i], &e); +#ifdef WITH_CONTENT_SCAN /* check regex variables. assert_variable_notin() treats as const. */ for (int i = 0; i < REGEX_VARS; i++) if (regex_vars[i]) assert_variable_notin(US"regex", US regex_vars[i], &e); +#endif /* check known-name variables */ for (var_entry * v = var_table; v < var_table + var_table_size; v++) commit 04644f4ca7c85d5e4dff1bd9e48b3429130e4fdd Author: Jeremy Harris Date: Fri Sep 2 11:15:33 2022 +0100 EXPERIMENTAL_DCC: make build without WITH_CONTENT_SCAN fail rather than auto-include the support diff --git a/src/src/EDITME b/src/src/EDITME index 3c33dc5f3..80bd07817 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -596,7 +596,7 @@ DISABLE_MAL_MKS=yes # Uncomment the following line to add support for talking to dccifd. This # defaults the socket path to /usr/local/dcc/var/dccifd. -# Doing so will also explicitly turn on the WITH_CONTENT_SCAN option. +# This support also requires WITH_CONTENT_SCAN enabled. # EXPERIMENTAL_DCC=yes diff --git a/src/src/acl.c b/src/src/acl.c index 3af3a4eee..1e7d28a90 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -3125,12 +3125,9 @@ acl_check_condition(int verb, acl_condition_block *cb, int where, address_item *addr, int level, BOOL *epp, uschar **user_msgptr, uschar **log_msgptr, int *basic_errno) { -uschar *user_message = NULL; -uschar *log_message = NULL; +uschar * user_message = NULL; +uschar * log_message = NULL; int rc = OK; -#ifdef WITH_CONTENT_SCAN -int sep = -'/'; -#endif for (; cb; cb = cb->next) { @@ -3646,12 +3643,13 @@ for (; cb; cb = cb->next) break; } - #ifdef EXPERIMENTAL_DCC +#ifdef EXPERIMENTAL_DCC case ACLC_DCC: { /* Separate the regular expression and any optional parameters. */ const uschar * list = arg; - uschar *ss = string_nextinlist(&list, &sep, NULL, 0); + int sep = -'/'; + uschar * ss = string_nextinlist(&list, &sep, NULL, 0); /* Run the dcc backend. */ rc = dcc_process(&ss); /* Modify return code based upon the existence of options. */ @@ -3660,13 +3658,13 @@ for (; cb; cb = cb->next) rc = FAIL; /* FAIL so that the message is passed to the next ACL */ break; } - #endif +#endif - #ifdef WITH_CONTENT_SCAN +#ifdef WITH_CONTENT_SCAN case ACLC_DECODE: rc = mime_decode(&arg); break; - #endif +#endif case ACLC_DELAY: { @@ -3813,11 +3811,10 @@ for (; cb; cb = cb->next) case ACLC_LOG_REJECT_TARGET: { - int logbits = 0; - int sep = 0; - const uschar *s = arg; - uschar * ss; - while ((ss = string_nextinlist(&s, &sep, NULL, 0))) + int logbits = 0, sep = 0; + const uschar * s = arg; + + for (uschar * ss; ss = string_nextinlist(&s, &sep, NULL, 0); ) { if (Ustrcmp(ss, "main") == 0) logbits |= LOG_MAIN; else if (Ustrcmp(ss, "panic") == 0) logbits |= LOG_PANIC; @@ -3865,17 +3862,16 @@ for (; cb; cb = cb->next) break; } - #ifdef WITH_CONTENT_SCAN +#ifdef WITH_CONTENT_SCAN case ACLC_MALWARE: /* Run the malware backend. */ { /* Separate the regular expression and any optional parameters. */ const uschar * list = arg; - uschar * ss = string_nextinlist(&list, &sep, NULL, 0); - uschar * opt; BOOL defer_ok = FALSE; - int timeout = 0; + int timeout = 0, sep = -'/'; + uschar * ss = string_nextinlist(&list, &sep, NULL, 0); - while ((opt = string_nextinlist(&list, &sep, NULL, 0))) + for (uschar * opt; opt = string_nextinlist(&list, &sep, NULL, 0); ) if (strcmpic(opt, US"defer_ok") == 0) defer_ok = TRUE; else if ( strncmpic(opt, US"tmo=", 4) == 0 @@ -3895,7 +3891,7 @@ for (; cb; cb = cb->next) case ACLC_MIME_REGEX: rc = mime_regex(&arg, textonly); break; - #endif +#endif case ACLC_QUEUE: if (is_tainted(arg)) @@ -3979,7 +3975,8 @@ for (; cb; cb = cb->next) { /* Separate the regular expression and any optional parameters. */ const uschar * list = arg; - uschar *ss = string_nextinlist(&list, &sep, NULL, 0); + int sep = -'/'; + uschar * ss = string_nextinlist(&list, &sep, NULL, 0); rc = spam(CUSS &ss); /* Modify return code based upon the existence of options. */ diff --git a/src/src/buildconfig.c b/src/src/buildconfig.c index f9a8febdf..bbbaefe9c 100644 --- a/src/src/buildconfig.c +++ b/src/src/buildconfig.c @@ -740,19 +740,6 @@ else if (isgroup) continue; } - /* WITH_CONTENT_SCAN is another special case: it must be set if it or - EXPERIMENTAL_DCC is set. */ - - if (strcmp(name, "WITH_CONTENT_SCAN") == 0) - { - char *wcs = getenv("WITH_CONTENT_SCAN"); - char *dcc = getenv("EXPERIMENTAL_DCC"); - fprintf(new, wcs || dcc - ? "#define WITH_CONTENT_SCAN yes\n" - : "/* WITH_CONTENT_SCAN not set */\n"); - continue; - } - /* DISABLE_DKIM is special; must be forced if DISABLE_TLS */ if (strcmp(name, "DISABLE_DKIM") == 0) { diff --git a/src/src/dcc.c b/src/src/dcc.c index a9124a013..56dd6d570 100644 --- a/src/src/dcc.c +++ b/src/src/dcc.c @@ -7,13 +7,17 @@ * wbreyha@gmx.net * See the file NOTICE for conditions of use and distribution. * - * Copyright (c) The Exim Maintainers 2015 - 2021 + * Copyright (c) The Exim Maintainers 2015 - 2022 */ /* Code for calling dccifd. Called from acl.c. */ #include "exim.h" #ifdef EXPERIMENTAL_DCC +#ifndef WITH_CONTENT_SCAN +# error EXPERIMENTAL_DCC requires WITH_CONTENT_SCAN +#endif + #include "dcc.h" #include "unistd.h" commit a3d3e7ef81a649d2bbd6599fc561cf22c6875e70 Author: Jeremy Harris Date: Thu Sep 22 15:09:07 2022 +0100 Fix ${filter } for conditions modifying $value diff --git a/src/src/expand.c b/src/src/expand.c index 831ca2b75..ff0b4d600 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -6544,6 +6544,9 @@ while (*s) if (item_type == EITEM_FILTER) { BOOL condresult; + /* the condition could modify $value, as a side-effect */ + uschar * save_value = lookup_value; + if (!eval_condition(expr, &resetok, &condresult)) { iterate_item = save_iterate_item; @@ -6552,6 +6555,7 @@ while (*s) expand_string_message, name); goto EXPAND_FAILED; } + lookup_value = save_value; DEBUG(D_expand) debug_printf_indent("%s: condition is %s\n", name, condresult? "true":"false"); if (condresult) @@ -6560,14 +6564,12 @@ while (*s) continue; /* FALSE => skip this item */ } - /* EITEM_MAP and EITEM_REDUCE */ - - else + else /* EITEM_MAP and EITEM_REDUCE */ { + /* the expansion could modify $value, as a side-effect */ uschar * t = expand_string_internal(expr, ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags, NULL, &resetok, NULL); - temp = t; - if (!temp) + if (!(temp = t)) { iterate_item = save_iterate_item; expand_string_message = string_sprintf("%s inside \"%s\" item", commit ece23f05d6a430a461a75639197271c23f6858ec Author: Jasen Betts Date: Fri Sep 30 13:49:41 2022 +0100 GnuTLS: fix for clients offering no TLS extensions diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 7a6db94e1..1fc7828cf 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -1142,8 +1142,9 @@ tls_server_clienthello_cb(gnutls_session_t session, unsigned int htype, unsigned when, unsigned int incoming, const gnutls_datum_t * msg) { /* Call fn for each extension seen. 3.6.3 onwards */ -return gnutls_ext_raw_parse(NULL, tls_server_clienthello_ext, msg, +int rc = gnutls_ext_raw_parse(NULL, tls_server_clienthello_ext, msg, GNUTLS_EXT_RAW_FLAG_TLS_CLIENT_HELLO); +return rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE ? 0 : rc; } diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 043755c84..0129fb93e 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -951,36 +951,35 @@ Returns: nothing */ static void -info_callback(SSL *s, int where, int ret) +info_callback(SSL * s, int where, int ret) { DEBUG(D_tls) { - const uschar * str; - - if (where & SSL_ST_CONNECT) - str = US"SSL_connect"; - else if (where & SSL_ST_ACCEPT) - str = US"SSL_accept"; - else - str = US"SSL info (undefined)"; + gstring * g = NULL; + + if (where & SSL_ST_CONNECT) g = string_append_listele(g, ',', US"SSL_connect"); + if (where & SSL_ST_ACCEPT) g = string_append_listele(g, ',', US"SSL_accept"); + if (where & SSL_CB_LOOP) g = string_append_listele(g, ',', US"state_chg"); + if (where & SSL_CB_EXIT) g = string_append_listele(g, ',', US"hshake_exit"); + if (where & SSL_CB_READ) g = string_append_listele(g, ',', US"read"); + if (where & SSL_CB_WRITE) g = string_append_listele(g, ',', US"write"); + if (where & SSL_CB_ALERT) g = string_append_listele(g, ',', US"alert"); + if (where & SSL_CB_HANDSHAKE_START) g = string_append_listele(g, ',', US"hshake_start"); + if (where & SSL_CB_HANDSHAKE_DONE) g = string_append_listele(g, ',', US"hshake_done"); if (where & SSL_CB_LOOP) - debug_printf("%s: %s\n", str, SSL_state_string_long(s)); + debug_printf("SSL %s: %s\n", g->s, SSL_state_string_long(s)); else if (where & SSL_CB_ALERT) - debug_printf("SSL3 alert %s:%s:%s\n", - str = where & SSL_CB_READ ? US"read" : US"write", + debug_printf("SSL %s %s:%s\n", g->s, SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); else if (where & SSL_CB_EXIT) { - if (ret == 0) - debug_printf("%s: failed in %s\n", str, SSL_state_string_long(s)); - else if (ret < 0) - debug_printf("%s: error in %s\n", str, SSL_state_string_long(s)); + if (ret <= 0) + debug_printf("SSL %s: %s in %s\n", g->s, + ret == 0 ? "failed" : "error", SSL_state_string_long(s)); } - else if (where & SSL_CB_HANDSHAKE_START) - debug_printf("%s: hshake start: %s\n", str, SSL_state_string_long(s)); - else if (where & SSL_CB_HANDSHAKE_DONE) - debug_printf("%s: hshake done: %s\n", str, SSL_state_string_long(s)); + else if (where & (SSL_CB_HANDSHAKE_START | SSL_CB_HANDSHAKE_DONE)) + debug_printf("SSL %s: %s\n", g->s, SSL_state_string_long(s)); } } commit 96751ae76e6c6db435f5a4f141511d6f973b6583 Author: Jeremy Harris Date: Tue Oct 4 13:20:29 2022 +0100 OpenSSL: fix configuration of older TLS protocol versions diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 0129fb93e..9ddb16fc4 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2210,7 +2210,9 @@ already exists. Might even need this selfsame callback, for reneg? */ SSL_CTX * ctx = state_server.lib_state.lib_ctx; SSL_CTX_set_info_callback(server_sni, SSL_CTX_get_info_callback(ctx)); SSL_CTX_set_mode(server_sni, SSL_CTX_get_mode(ctx)); + SSL_CTX_set_min_proto_version(server_sni, SSL3_VERSION); SSL_CTX_set_options(server_sni, SSL_CTX_get_options(ctx)); + SSL_CTX_clear_options(server_sni, ~SSL_CTX_get_options(ctx)); SSL_CTX_set_timeout(server_sni, SSL_CTX_get_timeout(ctx)); SSL_CTX_set_tlsext_servername_callback(server_sni, tls_servername_cb); SSL_CTX_set_tlsext_servername_arg(server_sni, state); @@ -2726,10 +2728,15 @@ if (init_options) } #endif - DEBUG(D_tls) debug_printf("setting SSL CTX options: %#lx\n", init_options); - if (!(SSL_CTX_set_options(ctx, init_options))) - return tls_error(string_sprintf( + SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION); + DEBUG(D_tls) debug_printf("setting SSL CTX options: %016lx\n", init_options); + SSL_CTX_set_options(ctx, init_options); + { + ulong readback = SSL_CTX_clear_options(ctx, ~init_options); + if (readback != init_options) + return tls_error(string_sprintf( "SSL_CTX_set_option(%#lx)", init_options), host, NULL, errstr); + } } else DEBUG(D_tls) debug_printf("no SSL CTX options to set\n"); commit 27e646d341ea843245b8305166686c2fd888bf0e Author: Jeremy Harris Date: Tue Oct 4 19:11:03 2022 +0100 Fix build with older OpenSSL. Broken-by: 96751ae76e diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 9ddb16fc4..b46c0c7fd 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -48,6 +48,7 @@ functions from the OpenSSL library. */ #if OPENSSL_VERSION_NUMBER >= 0x10100000L # define EXIM_HAVE_OCSP_RESP_COUNT # define OPENSSL_AUTO_SHA256 +# define OPENSSL_MIN_PROTO_VERSION #else # define EXIM_HAVE_EPHEM_RSA_KEX # define EXIM_HAVE_RAND_PSEUDO @@ -2210,7 +2211,9 @@ already exists. Might even need this selfsame callback, for reneg? */ SSL_CTX * ctx = state_server.lib_state.lib_ctx; SSL_CTX_set_info_callback(server_sni, SSL_CTX_get_info_callback(ctx)); SSL_CTX_set_mode(server_sni, SSL_CTX_get_mode(ctx)); +#ifdef OPENSSL_MIN_PROTO_VERSION SSL_CTX_set_min_proto_version(server_sni, SSL3_VERSION); +#endif SSL_CTX_set_options(server_sni, SSL_CTX_get_options(ctx)); SSL_CTX_clear_options(server_sni, ~SSL_CTX_get_options(ctx)); SSL_CTX_set_timeout(server_sni, SSL_CTX_get_timeout(ctx)); @@ -2728,7 +2731,9 @@ if (init_options) } #endif +#ifdef OPENSSL_MIN_PROTO_VERSION SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION); +#endif DEBUG(D_tls) debug_printf("setting SSL CTX options: %016lx\n", init_options); SSL_CTX_set_options(ctx, init_options); { commit 635e657ce2c75c781d40469bbdd28645e64d89b0 Author: Jeremy Harris Date: Tue Oct 4 19:12:13 2022 +0100 Quieten clang build diff --git a/src/src/mytypes.h b/src/src/mytypes.h index 49fed0a9d..141d2c613 100644 --- a/src/src/mytypes.h +++ b/src/src/mytypes.h @@ -42,8 +42,12 @@ so we have to give up on all of the available parameter checking. */ # define FUNC_MAYBE_UNUSED __attribute__((__unused__)) # define WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) # define ALLOC __attribute__((malloc)) -# define ALLOC_SIZE(A) __attribute__((alloc_size(A))) # define NORETURN __attribute__((noreturn)) +# ifndef __clang__ +# define ALLOC_SIZE(A) __attribute__((alloc_size(A))) +# else +# define ALLOC_SIZE(A) /**/ +# endif #else # define ARG_UNUSED /**/ # define FUNC_MAYBE_UNUSED /**/ commit 699f306744e5f0e1ad860a460454efe85fe63c74 Author: Jeremy Harris Date: Tue Oct 4 21:30:55 2022 +0100 Unbreak FreeBSD build Broken-by: 96751ae76e diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index b46c0c7fd..68ad6f15b 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2737,7 +2737,7 @@ if (init_options) DEBUG(D_tls) debug_printf("setting SSL CTX options: %016lx\n", init_options); SSL_CTX_set_options(ctx, init_options); { - ulong readback = SSL_CTX_clear_options(ctx, ~init_options); + uint64_t readback = SSL_CTX_clear_options(ctx, ~init_options); if (readback != init_options) return tls_error(string_sprintf( "SSL_CTX_set_option(%#lx)", init_options), host, NULL, errstr); commit 1561c5d88b3a23a4348d8e3c1ce28554fcbcfe46 Author: Heiko Schlittermann (HS12-RIPE) Date: Sat Oct 15 19:30:58 2022 +0200 Fix: Build with libopendmarc 1.4.x (fixes 2728) diff --git a/src/src/EDITME b/src/src/EDITME index 80bd07817..28d3d538e 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -602,14 +602,17 @@ DISABLE_MAL_MKS=yes # Uncomment the following line to add DMARC checking capability, implemented # using libopendmarc libraries. You must have SPF and DKIM support enabled also. -# Library version libopendmarc-1.4.1-1.fc33.x86_64 (on Fedora 33) is known broken; -# 1.3.2-3 works. I seems that the OpenDMARC project broke their API. # SUPPORT_DMARC=yes # CFLAGS += -I/usr/local/include # LDFLAGS += -lopendmarc # Uncomment the following if you need to change the default. You can # override it at runtime (main config option dmarc_tld_file) # DMARC_TLD_FILE=/etc/exim/opendmarc.tlds +# +# Library version libopendmarc-1.4.1-1.fc33.x86_64 (on Fedora 33) is known broken; +# 1.3.2-3 works. It seems that the OpenDMARC project broke their API. +# Use this option if you need to build with an old library (1.3.x) +# DMARC_API=100300 # Uncomment the following line to add ARC (Authenticated Received Chain) # support. You must have SPF and DKIM support enabled also. diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index 25ab75506..221705224 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -150,6 +150,7 @@ Do not put spaces between # and the 'define'. #define SUPPORT_CRYPTEQ #define SUPPORT_DANE #define SUPPORT_DMARC +#define DMARC_API 100400 #define DMARC_TLD_FILE "/etc/exim/opendmarc.tlds" #define SUPPORT_I18N #define SUPPORT_I18N_2008 diff --git a/src/src/dmarc.c b/src/src/dmarc.c index 17bba9d75..ad0c26c91 100644 --- a/src/src/dmarc.c +++ b/src/src/dmarc.c @@ -459,7 +459,12 @@ if (!dmarc_abort && !sender_host_authenticated) vs == PDKIM_VERIFY_INVALID ? DMARC_POLICY_DKIM_OUTCOME_TMPFAIL : DMARC_POLICY_DKIM_OUTCOME_NONE; libdm_status = opendmarc_policy_store_dkim(dmarc_pctx, US sig->domain, - dkim_result, US""); +/* The opendmarc project broke its API in a way we can't detect * easily. + * The EDITME provides a DMARC_API variable */ +#if DMARC_API >= 100400 + sig->selector, +#endif + dkim_result, US""); DEBUG(D_receive) debug_printf("DMARC adding DKIM sender domain = %s\n", sig->domain); if (libdm_status != DMARC_PARSE_OKAY) commit 12fb3842f81bcbd4a4519d5728f2d7e0e3ca1445 Author: Lorenz Brun Date: Fri Oct 14 21:02:51 2022 +0200 DMARC: fix use-after-free in dmarc_dns_lookup This fixes a use-after-free in dmarc_dns_lookup where the result of dns_lookup in dnsa is freed before the required data is copied out. Fixes: 9258363 ("DNS: explicit alloc/free of workspace") diff --git a/src/src/dmarc.c b/src/src/dmarc.c index ad0c26c91..53c2752ac 100644 --- a/src/src/dmarc.c +++ b/src/src/dmarc.c @@ -230,8 +230,9 @@ if (rc == DNS_SUCCEED) rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == T_TXT && rr->size > 3) { + uschar *record = string_copyn_taint(US rr->data, rr->size, GET_TAINTED); store_free_dns_answer(dnsa); - return string_copyn_taint(US rr->data, rr->size, GET_TAINTED); + return record; } store_free_dns_answer(dnsa); return NULL; commit d5eda1c258df2996ace50a723ba8b1d78bb86be6 Author: Jeremy Harris Date: Mon Oct 31 10:18:28 2022 +0000 Release process: permit minor version numbers after 99 commit 221321d2c51b83d1feced80ecd6c2fe33ec5456c Author: Jeremy Harris Date: Thu Nov 3 20:08:25 2022 +0000 Fix daemon startup. Bug 2930 Broken-by: 7d5055276a diff --git a/src/src/daemon.c b/src/src/daemon.c index 54725e07d..8446f81cb 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -1776,15 +1776,19 @@ if (f.background_daemon) daemon as the result of a SIGHUP. In this case, there is no need to do anything, because the controlling terminal has long gone. Otherwise, fork, in case current process is a process group leader (see 'man setsid' for an - explanation) before calling setsid(). */ + explanation) before calling setsid(). + All other forks want daemon_listen cleared. Rather than blow a register, jsut + restore it here. */ if (getppid() != 1) { + BOOL daemon_listen = f.daemon_listen; pid_t pid = exim_fork(US"daemon"); if (pid < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "fork() failed when starting daemon: %s", strerror(errno)); if (pid > 0) exit(EXIT_SUCCESS); /* in parent process, just exit */ (void)setsid(); /* release controlling terminal */ + f.daemon_listen = daemon_listen; } } @@ -2122,7 +2126,7 @@ else if (f.daemon_listen) if (*--p == '}') *p = '\0'; /* drop EOL */ while (isdigit(*--p)) ; /* char before port */ - i2->log = *p == ':' /* no list yet? */ + i2->log = *p == ':' /* no list yet? { */ ? string_sprintf("%.*s{%s,%d}", (int)(p - i2->log + 1), i2->log, p+1, ipa->port) : string_sprintf("%s,%d}", i2->log, ipa->port); commit 6b331d5834d12bdda21857cd6fffac17038ce3c7 Author: Ruben Jenster Date: Thu Nov 3 21:38:15 2022 +0000 Fix $reccipients after ${run...}. Bug 2929 Broken-by: cfe6acff2d diff --git a/src/src/transport.c b/src/src/transport.c index cce1c46ae..7477882cb 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -2344,9 +2344,10 @@ if (expand_arguments) else { const uschar *expanded_arg; + BOOL enable_dollar_recipients_g = f.enable_dollar_recipients; f.enable_dollar_recipients = allow_dollar_recipients; expanded_arg = expand_cstring(argv[i]); - f.enable_dollar_recipients = FALSE; + f.enable_dollar_recipients = enable_dollar_recipients_g; if (!expanded_arg) { commit 20edc59755798df57b094d1f5ccf24726acbe319 Author: Mehmet Suslu Date: Tue Mar 1 10:33:23 2022 +0300 typo: error message referenced wrong option Fix typo, -oMas -> -oMai (cherry picked from commit 330ab60ef29c01fb472b6ce14e935ccb93e905ac) diff --git a/src/src/exim.c b/src/src/exim.c index f2a787e8a..b3fd9eff0 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -3329,7 +3329,7 @@ on the second character (the one after '-'), to save some effort. */ else if (Ustrcmp(argrest, "ai") == 0) authenticated_id = string_copy_taint( - exim_str_fail_toolong(argv[++i], EXIM_EMAILADDR_MAX, "-oMas"), + exim_str_fail_toolong(argv[++i], EXIM_EMAILADDR_MAX, "-oMai"), GET_TAINTED); /* -oMi: Set incoming interface address */ commit 36d65c9fc4bd2f0f260439fa624b667e29e5a280 Author: Tim Gates Date: Thu Dec 17 21:07:18 2020 +1100 typo: code comment signture -> signature There is a small typo in src/src/globals.h. Should read `signature` rather than `signture`. diff --git a/src/src/globals.h b/src/src/globals.h index ae74147d4..1be96daec 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -545,7 +545,7 @@ extern uschar *dkim_signing_selector; /* Expansion variable, selector used for extern uschar *dkim_verify_hashes; /* Preference order for signatures */ extern uschar *dkim_verify_keytypes; /* Preference order for signatures */ extern uschar *dkim_verify_min_keysizes; /* list of minimum key sizes, keyed by algo */ -extern BOOL dkim_verify_minimal; /* Shortcircuit signture verification */ +extern BOOL dkim_verify_minimal; /* Shortcircuit signature verification */ extern uschar *dkim_verify_overall; /* First successful domain verified, or null */ extern uschar *dkim_verify_signers; /* Colon-separated list of domains for each of which we call the DKIM ACL */ extern uschar *dkim_verify_status; /* result for this signature */ commit e63825824cc406c160ccbf2b154c5d81b168604a Author: Jeremy Harris Date: Fri Nov 11 00:05:59 2022 +0000 Fix regext substring capture variables for null matches. Bug 2933 broken-by: 59d66fdc13f0 diff --git a/src/src/exim.c b/src/src/exim.c index b3fd9eff0..47a685aa7 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -134,6 +134,8 @@ if ((yield = (res >= 0))) PCRE2_SIZE len; pcre2_substring_get_bynumber(md, matchnum, (PCRE2_UCHAR **)&expand_nstring[expand_nmax], &len); + if (!expand_nstring[expand_nmax]) + { expand_nstring[expand_nmax] = US""; len = 0; } expand_nlength[expand_nmax++] = (int)len; } expand_nmax--; diff --git a/src/src/malware.c b/src/src/malware.c index 8b5ec27c4..423a5b692 100644 --- a/src/src/malware.c +++ b/src/src/malware.c @@ -314,7 +314,10 @@ PCRE2_UCHAR * substr = NULL; PCRE2_SIZE slen; if (i >= 2) /* Got it */ + { pcre2_substring_get_bynumber(md, 1, &substr, &slen); /* uses same ctx as md */ + if (!substr) substr = US""; + } /* pcre2_match_data_free(md); gen ctx needs no free */ return US substr; } diff --git a/src/src/regex.c b/src/src/regex.c index 25496f950..b401ba0d7 100644 --- a/src/src/regex.c +++ b/src/src/regex.c @@ -82,7 +82,7 @@ for (pcre_list * ri = re_list_head; ri; ri = ri->next) PCRE2_UCHAR * cstr; PCRE2_SIZE cslen; pcre2_substring_get_bynumber(md, nn, &cstr, &cslen); /* uses same ctx as md */ - regex_vars[nn-1] = CUS cstr; + regex_vars[nn-1] = cstr ? CUS cstr : CUS""; } return OK; commit 7ad1a2b2cc57b5f4bcb59186a9a8abcbed9f4f76 Author: Jeremy Harris Date: Fri Nov 11 18:22:00 2022 +0000 Fix regex substring capture variables for null matches (again). Bug 2933 Broken-by: 59d66fdc13f0 diff --git a/src/src/exim.c b/src/src/exim.c index 47a685aa7..16c0184e0 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -127,16 +127,15 @@ BOOL yield; if ((yield = (res >= 0))) { + PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md); res = pcre2_get_ovector_count(md); expand_nmax = setup < 0 ? 0 : setup + 1; for (int matchnum = setup < 0 ? 0 : 1; matchnum < res; matchnum++) { - PCRE2_SIZE len; - pcre2_substring_get_bynumber(md, matchnum, - (PCRE2_UCHAR **)&expand_nstring[expand_nmax], &len); - if (!expand_nstring[expand_nmax]) - { expand_nstring[expand_nmax] = US""; len = 0; } - expand_nlength[expand_nmax++] = (int)len; + int off = matchnum * 2; + int len = ovec[off + 1] - ovec[off]; + expand_nstring[expand_nmax] = string_copyn(subject + ovec[off], len); + expand_nlength[expand_nmax++] = len; } expand_nmax--; } diff --git a/src/src/malware.c b/src/src/malware.c index 423a5b692..01dd455ba 100644 --- a/src/src/malware.c +++ b/src/src/malware.c @@ -310,16 +310,16 @@ m_pcre_exec(const pcre2_code * cre, uschar * text) { pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx); int i = pcre2_match(cre, text, PCRE2_ZERO_TERMINATED, 0, 0, md, pcre_gen_mtc_ctx); -PCRE2_UCHAR * substr = NULL; -PCRE2_SIZE slen; +uschar * substr = NULL; if (i >= 2) /* Got it */ { - pcre2_substring_get_bynumber(md, 1, &substr, &slen); /* uses same ctx as md */ - if (!substr) substr = US""; + PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md); + int len = ovec[3] - ovec[2]; + substr = string_copyn(text + ovec[2], len); } /* pcre2_match_data_free(md); gen ctx needs no free */ -return US substr; +return substr; } static const pcre2_code * diff --git a/src/src/regex.c b/src/src/regex.c index b401ba0d7..210620f26 100644 --- a/src/src/regex.c +++ b/src/src/regex.c @@ -79,10 +79,10 @@ for (pcre_list * ri = re_list_head; ri; ri = ri->next) for (int nn = 1; nn < n; nn++) { - PCRE2_UCHAR * cstr; - PCRE2_SIZE cslen; - pcre2_substring_get_bynumber(md, nn, &cstr, &cslen); /* uses same ctx as md */ - regex_vars[nn-1] = cstr ? CUS cstr : CUS""; + PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md); + int off = nn * 2; + int len = ovec[off + 1] - ovec[off]; + regex_vars[nn-1] = string_copyn(linebuffer + ovec[off], len); } return OK; commit 9ba47886c71d40edc99b026a99edee269d9c9c6f Author: Jeremy Harris Date: Sat Nov 12 12:38:22 2022 +0000 Fix regex substring capture - commentary. Bug 2933 Broken-by (corrected): 22ed7a5295f1 diff --git a/src/src/exim.c b/src/src/exim.c index 16c0184e0..625494ce4 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -104,7 +104,9 @@ pcre_gen_mtc_ctx = pcre2_match_context_create(pcre_gen_ctx); /* This function runs a regular expression match, and sets up the pointers to the matched substrings. The matched strings are copied so the lifetime of -the subject is not a problem. +the subject is not a problem. Matched strings will have the same taint status +as the subject string (this is not a de-taint method, and must not be made so +given the support for wildcards in REs). Arguments: re the compiled expression @@ -132,6 +134,11 @@ if ((yield = (res >= 0))) expand_nmax = setup < 0 ? 0 : setup + 1; for (int matchnum = setup < 0 ? 0 : 1; matchnum < res; matchnum++) { + /* Although PCRE2 has a pcre2_substring_get_bynumber() conveneience, it + seems to return a bad pointer when a capture group had no data, eg. (.*) + matching zero letters. So use the underlying ovec and hope (!) that the + offsets are sane (including that case). Should we go further and range- + check each one vs. the subject string length? */ int off = matchnum * 2; int len = ovec[off + 1] - ovec[off]; expand_nstring[expand_nmax] = string_copyn(subject + ovec[off], len); commit dae16fc62c042f1c300db82ec1fc0d95cb8d66d3 Author: Jeremy Harris Date: Sat Nov 12 20:17:55 2022 +0000 tidy segv stacktrace logging diff --git a/src/src/exim.c b/src/src/exim.c index 625494ce4..3cd1d5f6d 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -248,17 +248,17 @@ void * buf[STACKDUMP_MAX]; char ** ss; int nptrs = backtrace(buf, STACKDUMP_MAX); -log_write(0, LOG_MAIN|LOG_PANIC, "backtrace\n"); -log_write(0, LOG_MAIN|LOG_PANIC, "---\n"); +log_write(0, LOG_MAIN|LOG_PANIC, "backtrace"); +log_write(0, LOG_MAIN|LOG_PANIC, "---"); if ((ss = backtrace_symbols(buf, nptrs))) { for (int i = 0; i < nptrs; i++) - log_write(0, LOG_MAIN|LOG_PANIC, "\t%s\n", ss[i]); + log_write(0, LOG_MAIN|LOG_PANIC, "\t%s", ss[i]); free(ss); } else - log_write(0, LOG_MAIN|LOG_PANIC, "backtrace_symbols: %s\n", strerror(errno)); -log_write(0, LOG_MAIN|LOG_PANIC, "---\n"); + log_write(0, LOG_MAIN|LOG_PANIC, "backtrace_symbols: %s", strerror(errno)); +log_write(0, LOG_MAIN|LOG_PANIC, "---"); #endif } #undef STACKDUMP_MAX commit 7f65a63b60c6ea86db683ac00e221939f3bb1d47 Author: Jeremy Harris Date: Tue Oct 25 21:26:30 2022 +0100 OpenSSL: when preloading creds do the server certs before the OCSP proofs so that the latter can ve verified before loading diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 68ad6f15b..fdf0d92b2 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -445,6 +445,8 @@ setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, /* Callbacks */ #ifndef DISABLE_OCSP static int tls_server_stapling_cb(SSL *s, void *arg); +static void x509_stack_dump_cert_s_names(const STACK_OF(X509) * sk); +static void x509_store_dump_cert_s_names(X509_STORE * store); #endif @@ -1311,7 +1313,6 @@ OCSP_BASICRESP * basic_response; OCSP_SINGLERESP * single_response; ASN1_GENERALIZEDTIME * rev, * thisupd, * nextupd; STACK_OF(X509) * sk; -unsigned long verify_flags; int status, reason, i; DEBUG(D_tls) @@ -1376,20 +1377,20 @@ if (!(basic_response = OCSP_response_get1_basic(resp))) goto bad; } -sk = state->verify_stack; -verify_flags = OCSP_NOVERIFY; /* check sigs, but not purpose */ +sk = state->verify_stack; /* set by setup_certs() / chain_from_pem_file() */ /* May need to expose ability to adjust those flags? OCSP_NOSIGS OCSP_NOVERIFY OCSP_NOCHAIN OCSP_NOCHECKS OCSP_NOEXPLICIT OCSP_TRUSTOTHER OCSP_NOINTERN */ -/* This does a full verify on the OCSP proof before we load it for serving -up; possibly overkill - just date-checks might be nice enough. +/* This does a partial verify (only the signer link, not the whole chain-to-CA) +on the OCSP proof before we load it for serving up; possibly overkill - +just date-checks might be nice enough. OCSP_basic_verify takes a "store" arg, but does not -use it for the chain verification, which is all we do -when OCSP_NOVERIFY is set. The content from the wire -"basic_response" and a cert-stack "sk" are all that is used. +use it for the chain verification, when OCSP_NOVERIFY is set. +The content from the wire "basic_response" and a cert-stack "sk" are all +that is used. We have a stack, loaded in setup_certs() if tls_verify_certificates was a file (not a directory, or "system"). It is unfortunate we @@ -1406,7 +1407,7 @@ But what with? We also use OCSP_basic_verify in the client stapling callback. And there we NEED it; we must verify that status... unless the library does it for us anyway? */ -if ((i = OCSP_basic_verify(basic_response, sk, NULL, verify_flags)) < 0) +if ((i = OCSP_basic_verify(basic_response, sk, NULL, OCSP_NOVERIFY)) < 0) { DEBUG(D_tls) { @@ -1751,53 +1752,10 @@ else DEBUG(D_tls) debug_printf("TLS: not preloading ECDH curve for server\n"); #if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT) -/* If we can, preload the server-side cert, key and ocsp */ - -if ( opt_set_and_noexpand(tls_certificate) -# ifndef DISABLE_OCSP - && opt_unset_or_noexpand(tls_ocsp_file) -#endif - && opt_unset_or_noexpand(tls_privatekey)) - { - /* Set watches on the filenames. The implementation does de-duplication - so we can just blindly do them all. */ - - if ( tls_set_watch(tls_certificate, TRUE) -# ifndef DISABLE_OCSP - && tls_set_watch(tls_ocsp_file, TRUE) -#endif - && tls_set_watch(tls_privatekey, TRUE)) - { - state_server.certificate = tls_certificate; - state_server.privatekey = tls_privatekey; -#ifndef DISABLE_OCSP - state_server.u_ocsp.server.file = tls_ocsp_file; -#endif - - DEBUG(D_tls) debug_printf("TLS: preloading server certs\n"); - if (tls_expand_session_files(ctx, &state_server, &dummy_errstr) == OK) - state_server.lib_state.conn_certs = TRUE; - } - } -else if ( !tls_certificate && !tls_privatekey -# ifndef DISABLE_OCSP - && !tls_ocsp_file -#endif - ) - { /* Generate & preload a selfsigned cert. No files to watch. */ - if (tls_expand_session_files(ctx, &state_server, &dummy_errstr) == OK) - { - state_server.lib_state.conn_certs = TRUE; - lifetime = f.running_in_test_harness ? 2 : 60 * 60; /* 1 hour */ - } - } -else - DEBUG(D_tls) debug_printf("TLS: not preloading server certs\n"); - - /* If we can, preload the Authorities for checking client certs against. Actual choice to do verify is made (tls_{,try_}verify_hosts) -at TLS conn startup */ +at TLS conn startup. +Do this before the server ocsp so that its info can verify the ocsp. */ if ( opt_set_and_noexpand(tls_verify_certificates) && opt_unset_or_noexpand(tls_crl)) @@ -1813,10 +1771,55 @@ if ( opt_set_and_noexpand(tls_verify_certificates) if (setup_certs(ctx, tls_verify_certificates, tls_crl, NULL, &dummy_errstr) == OK) state_server.lib_state.cabundle = TRUE; - } + + /* If we can, preload the server-side cert, key and ocsp */ + + if ( opt_set_and_noexpand(tls_certificate) +# ifndef DISABLE_OCSP + && opt_unset_or_noexpand(tls_ocsp_file) +# endif + && opt_unset_or_noexpand(tls_privatekey)) + { + /* Set watches on the filenames. The implementation does de-duplication + so we can just blindly do them all. */ + + if ( tls_set_watch(tls_certificate, TRUE) +# ifndef DISABLE_OCSP + && tls_set_watch(tls_ocsp_file, TRUE) +# endif + && tls_set_watch(tls_privatekey, TRUE)) + { + state_server.certificate = tls_certificate; + state_server.privatekey = tls_privatekey; +#ifndef DISABLE_OCSP + state_server.u_ocsp.server.file = tls_ocsp_file; +# endif + + DEBUG(D_tls) debug_printf("TLS: preloading server certs\n"); + if (tls_expand_session_files(ctx, &state_server, &dummy_errstr) == OK) + state_server.lib_state.conn_certs = TRUE; + } + } + else if ( !tls_certificate && !tls_privatekey +# ifndef DISABLE_OCSP + && !tls_ocsp_file +# endif + ) + { /* Generate & preload a selfsigned cert. No files to watch. */ + if (tls_expand_session_files(ctx, &state_server, &dummy_errstr) == OK) + { + state_server.lib_state.conn_certs = TRUE; + lifetime = f.running_in_test_harness ? 2 : 60 * 60; /* 1 hour */ + } + } + else + DEBUG(D_tls) debug_printf("TLS: not preloading server certs\n"); + } } else DEBUG(D_tls) debug_printf("TLS: not preloading CA bundle for server\n"); + + #endif /* EXIM_HAVE_INOTIFY */ commit 62b97c2ecf148ee86053d82e5509e4c3a5a20054 Author: Jeremy Harris Date: Sat Oct 29 22:33:43 2022 +0100 OpenSSL: fix double-expansion of tls_verify_certificates diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index fdf0d92b2..2e09882d2 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -439,7 +439,7 @@ exim_openssl_state_st *client_static_state = NULL; /*XXX should not use static; exim_openssl_state_st state_server = {.is_server = TRUE}; static int -setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, +setup_certs(SSL_CTX * sctx, uschar ** certs, uschar * crl, host_item * host, uschar ** errstr ); /* Callbacks */ @@ -1766,10 +1766,10 @@ if ( opt_set_and_noexpand(tls_verify_certificates) && tls_set_watch(tls_verify_certificates, FALSE) && tls_set_watch(tls_crl, FALSE)) { + uschar * v_certs = tls_verify_certificates; DEBUG(D_tls) debug_printf("TLS: preloading CA bundle for server\n"); - if (setup_certs(ctx, tls_verify_certificates, tls_crl, NULL, &dummy_errstr) - == OK) + if (setup_certs(ctx, &v_certs, tls_crl, NULL, &dummy_errstr) == OK) state_server.lib_state.cabundle = TRUE; /* If we can, preload the server-side cert, key and ocsp */ @@ -1901,10 +1901,11 @@ if ( opt_set_and_noexpand(ob->tls_verify_certificates) && tls_set_watch(ob->tls_crl, FALSE) ) { + uschar * v_certs = ob->tls_verify_certificates; DEBUG(D_tls) debug_printf("TLS: preloading CA bundle for transport '%s'\n", t->name); - if (setup_certs(ctx, ob->tls_verify_certificates, + if (setup_certs(ctx, &v_certs, ob->tls_crl, dummy_host, &dummy_errstr) == OK) ob->tls_preload.cabundle = TRUE; } @@ -2242,14 +2243,12 @@ if (state->u_ocsp.server.file) #endif { - uschar * expcerts; - if ( !expand_check(tls_verify_certificates, US"tls_verify_certificates", - &expcerts, &dummy_errstr) - || (rc = setup_certs(server_sni, expcerts, tls_crl, NULL, + uschar * v_certs = tls_verify_certificates; + if ((rc = setup_certs(server_sni, &v_certs, tls_crl, NULL, &dummy_errstr)) != OK) goto bad; - if (expcerts && *expcerts) + if (v_certs && *v_certs) setup_cert_verify(server_sni, FALSE, verify_callback_server); } @@ -3021,7 +3020,7 @@ repeated after a Server Name Indication. Arguments: sctx SSL_CTX* to initialise - certs certs file, expanded + certs certs file, returned expanded crl CRL file or NULL host NULL in a server; the remote host in a client errstr error string pointer @@ -3030,15 +3029,16 @@ Returns: OK/DEFER/FAIL */ static int -setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, +setup_certs(SSL_CTX * sctx, uschar ** certsp, uschar * crl, host_item * host, uschar ** errstr) { -uschar *expcerts, *expcrl; +uschar * expcerts, * expcrl; -if (!expand_check(certs, US"tls_verify_certificates", &expcerts, errstr)) +if (!expand_check(*certsp, US"tls_verify_certificates", &expcerts, errstr)) return DEFER; DEBUG(D_tls) debug_printf("tls_verify_certificates: %s\n", expcerts); +*certsp = expcerts; if (expcerts && *expcerts) { /* Tell the library to use its compiled-in location for the system default @@ -3334,20 +3334,20 @@ else goto skip_certs; { - uschar * expcerts; - if (!expand_check(tls_verify_certificates, US"tls_verify_certificates", - &expcerts, errstr)) - return DEFER; - DEBUG(D_tls) debug_printf("tls_verify_certificates: %s\n", expcerts); + uschar * v_certs = tls_verify_certificates; if (state_server.lib_state.cabundle) - { DEBUG(D_tls) debug_printf("TLS: CA bundle for server was preloaded\n"); } + { + DEBUG(D_tls) debug_printf("TLS: CA bundle for server was preloaded\n"); + setup_cert_verify(ctx, server_verify_optional, verify_callback_server); + } else - if ((rc = setup_certs(ctx, expcerts, tls_crl, NULL, errstr)) != OK) + { + if ((rc = setup_certs(ctx, &v_certs, tls_crl, NULL, errstr)) != OK) return rc; - - if (expcerts && *expcerts) - setup_cert_verify(ctx, server_verify_optional, verify_callback_server); + if (v_certs && *v_certs) + setup_cert_verify(ctx, server_verify_optional, verify_callback_server); + } } skip_certs: ; @@ -3610,20 +3610,20 @@ else return OK; { - uschar * expcerts; - if (!expand_check(ob->tls_verify_certificates, US"tls_verify_certificates", - &expcerts, errstr)) - return DEFER; - DEBUG(D_tls) debug_printf("tls_verify_certificates: %s\n", expcerts); + uschar * v_certs = ob->tls_verify_certificates; if (state->lib_state.cabundle) - { DEBUG(D_tls) debug_printf("TLS: CA bundle was preloaded\n"); } + { + DEBUG(D_tls) debug_printf("TLS: CA bundle for tpt was preloaded\n"); + setup_cert_verify(ctx, client_verify_optional, verify_callback_client); + } else - if ((rc = setup_certs(ctx, expcerts, ob->tls_crl, host, errstr)) != OK) + { + if ((rc = setup_certs(ctx, &v_certs, ob->tls_crl, host, errstr)) != OK) return rc; - - if (expcerts && *expcerts) - setup_cert_verify(ctx, client_verify_optional, verify_callback_client); + if (v_certs && *v_certs) + setup_cert_verify(ctx, client_verify_optional, verify_callback_client); + } } if (verify_check_given_host(CUSS &ob->tls_verify_cert_hostnames, host) == OK) commit 415c5379af11bf8777af1a082a336ad7c5369525 Author: Jeremy Harris Date: Tue Nov 22 22:32:59 2022 +0000 OpenSSL: OCSP under DANE diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 2e09882d2..3873bbba3 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -97,6 +97,7 @@ change this guard and punt the issue for a while longer. */ #if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x030000000L) # define EXIM_HAVE_EXPORT_CHNL_BNGNG +# define EXIM_HAVE_OPENSSL_X509_STORE_GET1_ALL_CERTS #endif #if !defined(LIBRESSL_VERSION_NUMBER) \ @@ -117,6 +118,7 @@ change this guard and punt the issue for a while longer. */ # define OPENSSL_HAVE_NUM_TICKETS # define EXIM_HAVE_OPENSSL_CIPHER_STD_NAME # define EXIM_HAVE_EXP_CHNL_BNGNG +# define EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_SIGNER # else # define OPENSSL_BAD_SRVR_OURCERT # endif @@ -408,15 +410,16 @@ typedef struct exim_openssl_state { uschar * privatekey; BOOL is_server; #ifndef DISABLE_OCSP - STACK_OF(X509) *verify_stack; /* chain for verifying the proof */ union { struct { uschar *file; const uschar *file_expanded; ocsp_resplist *olist; + STACK_OF(X509) *verify_stack; /* chain for verifying the proof */ } server; struct { X509_STORE *verify_store; /* non-null if status requested */ + uschar *verify_errstr; /* only if _required */ BOOL verify_required; } client; } u_ocsp; @@ -440,7 +443,7 @@ exim_openssl_state_st state_server = {.is_server = TRUE}; static int setup_certs(SSL_CTX * sctx, uschar ** certs, uschar * crl, host_item * host, - uschar ** errstr ); + uschar ** errstr); /* Callbacks */ #ifndef DISABLE_OCSP @@ -1119,18 +1122,6 @@ if (preverify_ok == 0) else if (depth != 0) { DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n", depth, dn); -#ifndef DISABLE_OCSP - if (tlsp == &tls_out && client_static_state->u_ocsp.client.verify_store) - { /* client, wanting stapling */ - /* Add the server cert's signing chain as the one - for the verification of the OCSP stapled information. */ - - if (!X509_STORE_add_cert(client_static_state->u_ocsp.client.verify_store, - cert)) - ERR_clear_error(); - sk_X509_push(client_static_state->verify_stack, cert); - } -#endif #ifndef DISABLE_EVENT if (verify_event(tlsp, cert, depth, dn, calledp, optionalp, US"SSL")) return 0; /* reject, with peercert set */ @@ -1258,21 +1249,7 @@ DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s depth %d %s\n", #endif if (preverify_ok == 1) - { tls_out.dane_verified = TRUE; -#ifndef DISABLE_OCSP - if (client_static_state->u_ocsp.client.verify_store) - { /* client, wanting stapling */ - /* Add the server cert's signing chain as the one - for the verification of the OCSP stapled information. */ - - if (!X509_STORE_add_cert(client_static_state->u_ocsp.client.verify_store, - cert)) - ERR_clear_error(); - sk_X509_push(client_static_state->verify_stack, cert); - } -#endif - } else { int err = X509_STORE_CTX_get_error(x509ctx); @@ -1288,6 +1265,14 @@ return preverify_ok; #ifndef DISABLE_OCSP +static void +time_print(BIO * bp, const char * str, ASN1_GENERALIZEDTIME * time) +{ +BIO_printf(bp, "\t%s: ", str); +ASN1_GENERALIZEDTIME_print(bp, time); +BIO_puts(bp, "\n"); +} + /************************************************* * Load OCSP information into state * *************************************************/ @@ -1377,7 +1362,7 @@ if (!(basic_response = OCSP_response_get1_basic(resp))) goto bad; } -sk = state->verify_stack; /* set by setup_certs() / chain_from_pem_file() */ +sk = state->u_ocsp.server.verify_stack; /* set by setup_certs() / chain_from_pem_file() */ /* May need to expose ability to adjust those flags? OCSP_NOSIGS OCSP_NOVERIFY OCSP_NOCHAIN OCSP_NOCHECKS OCSP_NOEXPLICIT @@ -1398,11 +1383,13 @@ cannot used the connection context store, as that would neatly handle the "system" case too, but there seems to be no library function for getting a stack from a store. [ In OpenSSL 1.1 - ? X509_STORE_CTX_get0_chain(ctx) ? ] +[ 3.0.0 - sk = X509_STORE_get1_all_certs(store) ] We do not free the stack since it could be needed a second time for SNI handling. Separately we might try to replace using OCSP_basic_verify() - which seems to not be a public interface into the OpenSSL library (there's no manual entry) - +(in 3.0.0 + is is public) But what with? We also use OCSP_basic_verify in the client stapling callback. And there we NEED it; we must verify that status... unless the library does it for us anyway? */ @@ -1412,7 +1399,7 @@ if ((i = OCSP_basic_verify(basic_response, sk, NULL, OCSP_NOVERIFY)) < 0) DEBUG(D_tls) { ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring)); - debug_printf("OCSP response verify failure: %s\n", US ssl_errstring); + debug_printf("OCSP response has bad signature: %s\n", US ssl_errstring); } goto bad; } @@ -1446,7 +1433,16 @@ if (status != V_OCSP_CERTSTATUS_GOOD) if (!OCSP_check_validity(thisupd, nextupd, EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE)) { - DEBUG(D_tls) debug_printf("OCSP status invalid times.\n"); + DEBUG(D_tls) + { + BIO * bp = BIO_new(BIO_s_mem()); + uschar * s = NULL; + int len; + time_print(bp, "This OCSP Update", thisupd); + if (nextupd) time_print(bp, "Next OCSP Update", nextupd); + if ((len = (int) BIO_get_mem_data(bp, CSS &s)) > 0) debug_printf("%.*s", len, s); + debug_printf("OCSP status invalid times.\n"); + } goto bad; } @@ -1921,7 +1917,7 @@ else #if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT) /* Invalidate the creds cached, by dropping the current ones. Call when we notice one of the source files has changed. */ - + static void tls_server_creds_invalidate(void) { @@ -1955,28 +1951,61 @@ tls_client_creds_invalidate(transport_instance * t) /* Extreme debug + * */ #ifndef DISABLE_OCSP -void -x509_store_dump_cert_s_names(X509_STORE * store) +static void +debug_print_sn(const X509 * cert) { -STACK_OF(X509_OBJECT) * roots= store->objs; +X509_NAME * sn = X509_get_subject_name(cert); static uschar name[256]; +if (X509_NAME_oneline(sn, CS name, sizeof(name))) + { + name[sizeof(name)-1] = '\0'; + debug_printf(" %s\n", name); + } +} -for (int i= 0; i < sk_X509_OBJECT_num(roots); i++) +static void +x509_stack_dump_cert_s_names(const STACK_OF(X509) * sk) +{ +if (!sk) + debug_printf(" (null)\n"); +else { - X509_OBJECT * tmp_obj= sk_X509_OBJECT_value(roots, i); - if(tmp_obj->type == X509_LU_X509) + int idx = sk_X509_num(sk); + if (!idx) + debug_printf(" (empty)\n"); + else + while (--idx >= 0) debug_print_sn(sk_X509_value(sk, idx)); + } +} + +static void +x509_store_dump_cert_s_names(X509_STORE * store) +{ +# ifdef EXIM_HAVE_OPENSSL_X509_STORE_GET1_ALL_CERTS +STACK_OF(X509) * sk = X509_STORE_get1_all_certs(store); +x509_stack_dump_cert_s_names(sk); +sk_X509_pop_free(sk, X509_free); + +# else +if (!store) + debug_printf(" (no store)\n"); +else + { + STACK_OF(X509_OBJECT) * objs = X509_STORE_get0_objects(store); + if (!objs) + debug_printf(" (null objectlist)\n"); + else for (int i = 0; i < sk_X509_OBJECT_num(objs); i++) { - X509_NAME * sn = X509_get_subject_name(tmp_obj->data.x509); - if (X509_NAME_oneline(sn, CS name, sizeof(name))) - { - name[sizeof(name)-1] = '\0'; - debug_printf(" %s\n", name); - } + X509 * cert = X509_OBJECT_get0_X509(sk_X509_OBJECT_value(objs, i)); + if (cert) debug_print_sn(cert); } } +# endif } -#endif +#endif /*!DISABLE_OCSP*/ +/* */ @@ -2420,11 +2449,21 @@ return SSL_TLSEXT_ERR_OK; static void -time_print(BIO * bp, const char * str, ASN1_GENERALIZEDTIME * time) +add_chain_to_store(X509_STORE * store, STACK_OF(X509) * sk, + const char * debug_text) { -BIO_printf(bp, "\t%s: ", str); -ASN1_GENERALIZEDTIME_print(bp, time); -BIO_puts(bp, "\n"); +int idx; + +DEBUG(D_tls) + { + debug_printf("chain for %s:\n", debug_text); + x509_stack_dump_cert_s_names(sk); + } +if (sk) + if ((idx = sk_X509_num(sk)) > 0) + while (--idx >= 0) + X509_STORE_add_cert(store, sk_X509_value(sk, idx)); + } static int @@ -2440,18 +2479,24 @@ int i; DEBUG(D_tls) debug_printf("Received TLS status callback (OCSP stapling):\n"); len = SSL_get_tlsext_status_ocsp_resp(ssl, &p); if(!p) - { /* Expect this when we requested ocsp but got none */ + { /* Expect this when we requested ocsp but got none */ if (SSL_session_reused(ssl) && tls_out.ocsp == OCSP_VFIED) { DEBUG(D_tls) debug_printf(" null, but resumed; ocsp vfy stored with session is good\n"); return 1; } + if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher)) log_write(0, LOG_MAIN, "Required TLS certificate status not received"); else DEBUG(D_tls) debug_printf(" null\n"); - return cbinfo->u_ocsp.client.verify_required ? 0 : 1; - } + + if (!cbinfo->u_ocsp.client.verify_required) + return 1; + cbinfo->u_ocsp.client.verify_errstr = + US"(SSL_connect) Required TLS certificate status not received"; + return 0; + } if (!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len))) { @@ -2483,39 +2528,140 @@ if (!(bs = OCSP_response_get1_basic(rsp))) */ { BIO * bp = NULL; + X509_STORE * verify_store = NULL; + BOOL have_verified_OCSP_signer = FALSE; #ifndef EXIM_HAVE_OCSP_RESP_COUNT STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses; #endif DEBUG(D_tls) bp = BIO_new(BIO_s_mem()); - /*OCSP_RESPONSE_print(bp, rsp, 0); extreme debug: stapling content */ + /* Use the CA & chain that verified the server cert to verify the stapled info */ + + { + /* If this routine is not available, we've avoided [in tls_client_start()] + asking for certificate-status under DANE, so this callback won't run for + that combination. It still will for non-DANE. */ + +#ifdef EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_SIGNER + X509 * signer; + + if ( tls_out.dane_verified + && (have_verified_OCSP_signer = + OCSP_resp_get0_signer(bs, &signer, SSL_get0_verified_chain(ssl)) == 1)) + { + DEBUG(D_tls) + debug_printf("signer for OCSP basicres is in the verified chain;" + " shortcut its verification\n"); + } + else +#endif + { + STACK_OF(X509) * verified_chain; + + verify_store = X509_STORE_new(); + + SSL_get0_chain_certs(ssl, &verified_chain); + add_chain_to_store(verify_store, verified_chain, + "'current cert' per SSL_get0_chain_certs()"); + + verified_chain = SSL_get0_verified_chain(ssl); + add_chain_to_store(verify_store, verified_chain, + "SSL_get0_verified_chain()"); + } + } + + DEBUG(D_tls) + { + debug_printf("Untrusted intermediate cert stack (from SSL_get_peer_cert_chain()):\n"); + x509_stack_dump_cert_s_names(SSL_get_peer_cert_chain(ssl)); + + debug_printf("will use this CA store for verifying basicresp:\n"); + x509_store_dump_cert_s_names(verify_store); + + /* OCSP_RESPONSE_print(bp, rsp, 0); extreme debug: stapling content */ + + debug_printf("certs contained in basicresp:\n"); + x509_stack_dump_cert_s_names((STACK_OF(X509 *))OCSP_resp_get0_certs(bs)); + +#ifdef EXIM_HAVE_OPENSSL_X509_STORE_GET1_ALL_CERTS /* else, could bodge via X509_STORE_get0_objects() + - but is OCSP_resp_get0_signer) avail? from 1.1.1 */ + { + X509 * signer; + if (OCSP_resp_get0_signer(bs, &signer, X509_STORE_get1_all_certs(verify_store)) == 1) + { + debug_printf("found signer for basicres:\n"); + debug_print_sn(signer); + } + else + { + debug_printf("failed to find signer for basicres:\n"); + ERR_print_errors(bp); + } + } +#endif + + } + + ERR_clear_error(); + + /* Under DANE the trust-anchor (at least in TA mode) is indicated by the TLSA + record in DNS, and probably is not the root of the chain of certificates. So + accept a partial chain for that case (and hope that anchor is visible for + verifying the OCSP stapling). + XXX for EE mode it won't even be that. Does that make OCSP useless for EE? - /* Use the chain that verified the server cert to verify the stapled info */ - /* DEBUG(D_tls) x509_store_dump_cert_s_names(cbinfo->u_ocsp.client.verify_store); */ + Worse, for LetsEncrypt-mode (ocsp signer is leaf-signer) under DANE, the + data used within OpenSSL for the signer has nil pointers for signing + algorithms - and a crash results. Avoid this by shortcutting verification, + having determined that the OCSP signer is in the (DANE-)validated set. + */ + +#ifndef OCSP_PARTIAL_CHAIN /* defined for 3.0.0 onwards */ +# define OCSP_PARTIAL_CHAIN 0 +#endif - if ((i = OCSP_basic_verify(bs, cbinfo->verify_stack, - cbinfo->u_ocsp.client.verify_store, OCSP_NOEXPLICIT)) <= 0) + if ((i = OCSP_basic_verify(bs, SSL_get_peer_cert_chain(ssl), + verify_store, + tls_out.dane_verified + ? have_verified_OCSP_signer + ? OCSP_NOVERIFY | OCSP_NOEXPLICIT + : OCSP_PARTIAL_CHAIN | OCSP_NOEXPLICIT + : OCSP_NOEXPLICIT)) <= 0) + { + DEBUG(D_tls) debug_printf("OCSP_basic_verify() fail: returned %d\n", i); if (ERR_peek_error()) { tls_out.ocsp = OCSP_FAILED; if (LOGGING(tls_cipher)) { - const uschar * errstr = CUS ERR_reason_error_string(ERR_peek_error()); static uschar peerdn[256]; + const uschar * errstr;; + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + ERR_peek_error_all(NULL, NULL, NULL, CCSS &errstr, NULL); + if (!errstr) +#endif + errstr = CUS ERR_reason_error_string(ERR_peek_error()); + X509_NAME_oneline(X509_get_subject_name(SSL_get_peer_certificate(ssl)), CS peerdn, sizeof(peerdn)); log_write(0, LOG_MAIN, "[%s] %s Received TLS cert (DN: '%.*s') status response, " "itself unverifiable: %s", - sender_host_address, sender_host_name, - (int)sizeof(peerdn), peerdn, - errstr); + deliver_host_address, deliver_host, + (int)sizeof(peerdn), peerdn, errstr); } DEBUG(D_tls) { BIO_printf(bp, "OCSP response verify failure\n"); ERR_print_errors(bp); + { + uschar * s = NULL; + int len = (int) BIO_get_mem_data(bp, CSS &s); + if (len > 0) debug_printf("%.*s", len, s); + BIO_reset(bp); + } OCSP_RESPONSE_print(bp, rsp, 0); } goto failed; @@ -2523,6 +2669,7 @@ if (!(bs = OCSP_response_get1_basic(rsp))) else DEBUG(D_tls) debug_printf("no explicit trust for OCSP signing" " in the root CA certificate; ignoring\n"); + } DEBUG(D_tls) debug_printf("OCSP response well-formed and signed OK\n"); @@ -2565,6 +2712,8 @@ if (!(bs = OCSP_response_get1_basic(rsp))) { tls_out.ocsp = OCSP_FAILED; DEBUG(D_tls) ERR_print_errors(bp); + cbinfo->u_ocsp.client.verify_errstr = + US"(SSL_connect) Server certificate status is out-of-date"; log_write(0, LOG_MAIN, "OCSP dates invalid"); goto failed; } @@ -2576,12 +2725,16 @@ if (!(bs = OCSP_response_get1_basic(rsp))) case V_OCSP_CERTSTATUS_GOOD: continue; /* the idx loop */ case V_OCSP_CERTSTATUS_REVOKED: + cbinfo->u_ocsp.client.verify_errstr = + US"(SSL_connect) Server certificate revoked"; log_write(0, LOG_MAIN, "Server certificate revoked%s%s", reason != -1 ? "; reason: " : "", reason != -1 ? OCSP_crl_reason_str(reason) : ""); DEBUG(D_tls) time_print(bp, "Revocation Time", rev); break; default: + cbinfo->u_ocsp.client.verify_errstr = + US"(SSL_connect) Server certificate has unknown status"; log_write(0, LOG_MAIN, "Server certificate status unknown, in OCSP stapling"); break; @@ -2635,8 +2788,7 @@ tls_init(host_item * host, smtp_transport_options_block * ob, uschar *ocsp_file, #endif address_item *addr, exim_openssl_state_st ** caller_state, - tls_support * tlsp, - uschar ** errstr) + tls_support * tlsp, uschar ** errstr) { SSL_CTX * ctx; exim_openssl_state_st * state; @@ -2798,7 +2950,7 @@ else #ifdef EXIM_HAVE_OPENSSL_TLSEXT # ifndef DISABLE_OCSP - if (!(state->verify_stack = sk_X509_new_null())) + if (!host && !(state->u_ocsp.server.verify_stack = sk_X509_new_null())) { DEBUG(D_tls) debug_printf("failed to create stack for stapling verify\n"); return FAIL; @@ -2847,11 +2999,12 @@ else /* client */ DEBUG(D_tls) debug_printf("failed to create store for stapling verify\n"); return FAIL; } + SSL_CTX_set_tlsext_status_cb(ctx, tls_client_stapling_cb); SSL_CTX_set_tlsext_status_arg(ctx, state); } # endif -#endif +#endif /*EXIM_HAVE_OPENSSL_TLSEXT*/ state->verify_cert_hostnames = NULL; @@ -2990,7 +3143,7 @@ if (tlsp->peercert) *************************************************/ #ifndef DISABLE_OCSP -/* Load certs from file, return TRUE on success */ +/* In the server, load certs from file, return TRUE on success */ static BOOL chain_from_pem_file(const uschar * file, STACK_OF(X509) ** vp) @@ -3020,7 +3173,7 @@ repeated after a Server Name Indication. Arguments: sctx SSL_CTX* to initialise - certs certs file, returned expanded + certsp certs file, returned expanded crl CRL file or NULL host NULL in a server; the remote host in a client errstr error string pointer @@ -3066,19 +3219,20 @@ if (expcerts && *expcerts) { STACK_OF(X509) * verify_stack = #ifndef DISABLE_OCSP - !host ? state_server.verify_stack : + !host ? state_server.u_ocsp.server.verify_stack : #endif NULL; STACK_OF(X509) ** vp = &verify_stack; file = expcerts; dir = NULL; #ifndef DISABLE_OCSP - /* In the server if we will be offering an OCSP proof, load chain from + /* In the server if we will be offering an OCSP proof; load chain from file for verifying the OCSP proof at load time. */ /*XXX Glitch! The file here is tls_verify_certs: the chain for verifying the client cert. This is inconsistent with the need to verify the OCSP proof of the server cert. */ +/* *debug_printf("file for checking server ocsp stapling is: %s\n", file); */ if ( !host && statbuf.st_size > 0 && state_server.u_ocsp.server.file @@ -3304,7 +3458,7 @@ TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256 if (state_server.lib_state.pri_string) { DEBUG(D_tls) debug_printf("TLS: cipher list was preloaded\n"); } -else +else { if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers, errstr)) return FAIL; @@ -3707,7 +3861,6 @@ tls_retrieve_session(tls_support * tlsp, SSL * ssl) { if (tlsp->host_resumable) { - const uschar * key = tlsp->resume_index; dbdata_tls_session * dt; int len; open_db dbblock, * dbm_file; @@ -3956,7 +4109,6 @@ tlsp->tlsa_usage = 0; #ifndef DISABLE_OCSP { # ifdef SUPPORT_DANE - /*XXX this should be moved to caller, to be common across gnutls/openssl */ if ( conn_args->dane && ob->hosts_request_ocsp[0] == '*' && ob->hosts_request_ocsp[1] == '\0' @@ -3979,6 +4131,15 @@ tlsp->tlsa_usage = 0; # endif request_ocsp = verify_check_given_host(CUSS &ob->hosts_request_ocsp, host) == OK; + +# if defined(SUPPORT_DANE) && !defined(EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_SIGNER) + if (conn_args->dane && (require_ocsp || request_ocsp)) + { + DEBUG(D_tls) debug_printf("OpenSSL version to early to combine OCSP" + " and DANE; disabling OCSP\n"); + require_ocsp = request_ocsp = FALSE; + } +# endif } #endif @@ -4050,6 +4211,7 @@ if (conn_args->dane) tls_error(US"context init", host, NULL, errstr); return FALSE; } + DEBUG(D_tls) debug_printf("since dane-mode conn, not loading the usual CA bundle\n"); } else @@ -4186,7 +4348,12 @@ if (conn_args->dane) if (rc <= 0) { - tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL, errstr); +#ifndef DISABLE_OCSP + if (client_static_state->u_ocsp.client.verify_errstr) + { if (errstr) *errstr = client_static_state->u_ocsp.client.verify_errstr; } + else +#endif + tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL, errstr); return FALSE; } @@ -4373,7 +4540,6 @@ tls_get_cache(unsigned lim) { #ifndef DISABLE_DKIM int n = ssl_xfer_buffer_hwm - ssl_xfer_buffer_lwm; -debug_printf("tls_get_cache\n"); if (n > lim) n = lim; if (n > 0) @@ -4633,8 +4799,8 @@ if (do_shutdown > TLS_NO_SHUTDOWN) if (!o_ctx) /* server side */ { #ifndef DISABLE_OCSP - sk_X509_pop_free(state_server.verify_stack, X509_free); - state_server.verify_stack = NULL; + sk_X509_pop_free(state_server.u_ocsp.server.verify_stack, X509_free); + state_server.u_ocsp.server.verify_stack = NULL; #endif receive_getc = smtp_getc; commit 6bf0021993572586f031ac7d973ca33358c2dac8 Author: Jeremy Harris Date: Fri Nov 25 12:33:28 2022 +0000 OpenSSL: fix build on older library version diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 3873bbba3..e673cb9f9 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -47,6 +47,7 @@ functions from the OpenSSL library. */ #endif #if OPENSSL_VERSION_NUMBER >= 0x10100000L # define EXIM_HAVE_OCSP_RESP_COUNT +# define EXIM_HAVE_SSL_GET0_VERIFIED_CHAIN # define OPENSSL_AUTO_SHA256 # define OPENSSL_MIN_PROTO_VERSION #else @@ -78,6 +79,7 @@ change this guard and punt the issue for a while longer. */ # define EXIM_HAVE_SESSION_TICKET # define EXIM_HAVE_OPESSL_TRACE # define EXIM_HAVE_OPESSL_GET0_SERIAL +# define EXIM_HAVE_OPESSL_OCSP_RESP_GET0_CERTS # ifndef DISABLE_OCSP # define EXIM_HAVE_OCSP # endif @@ -1956,7 +1958,7 @@ tls_client_creds_invalidate(transport_instance * t) static void debug_print_sn(const X509 * cert) { -X509_NAME * sn = X509_get_subject_name(cert); +X509_NAME * sn = X509_get_subject_name((X509 *)cert); static uschar name[256]; if (X509_NAME_oneline(sn, CS name, sizeof(name))) { @@ -1984,23 +1986,13 @@ static void x509_store_dump_cert_s_names(X509_STORE * store) { # ifdef EXIM_HAVE_OPENSSL_X509_STORE_GET1_ALL_CERTS -STACK_OF(X509) * sk = X509_STORE_get1_all_certs(store); -x509_stack_dump_cert_s_names(sk); -sk_X509_pop_free(sk, X509_free); - -# else if (!store) debug_printf(" (no store)\n"); else { - STACK_OF(X509_OBJECT) * objs = X509_STORE_get0_objects(store); - if (!objs) - debug_printf(" (null objectlist)\n"); - else for (int i = 0; i < sk_X509_OBJECT_num(objs); i++) - { - X509 * cert = X509_OBJECT_get0_X509(sk_X509_OBJECT_value(objs, i)); - if (cert) debug_print_sn(cert); - } + STACK_OF(X509) * sk = X509_STORE_get1_all_certs(store); + x509_stack_dump_cert_s_names(sk); + sk_X509_pop_free(sk, X509_free); } # endif } @@ -2564,10 +2556,11 @@ if (!(bs = OCSP_response_get1_basic(rsp))) SSL_get0_chain_certs(ssl, &verified_chain); add_chain_to_store(verify_store, verified_chain, "'current cert' per SSL_get0_chain_certs()"); - +#ifdef EXIM_HAVE_SSL_GET0_VERIFIED_CHAIN verified_chain = SSL_get0_verified_chain(ssl); add_chain_to_store(verify_store, verified_chain, "SSL_get0_verified_chain()"); +#endif } } @@ -2582,10 +2575,16 @@ if (!(bs = OCSP_response_get1_basic(rsp))) /* OCSP_RESPONSE_print(bp, rsp, 0); extreme debug: stapling content */ debug_printf("certs contained in basicresp:\n"); - x509_stack_dump_cert_s_names((STACK_OF(X509 *))OCSP_resp_get0_certs(bs)); + x509_stack_dump_cert_s_names( +#ifdef EXIM_HAVE_OPESSL_OCSP_RESP_GET0_CERTS + OCSP_resp_get0_certs(bs) +#else + bs->certs +#endif + ); -#ifdef EXIM_HAVE_OPENSSL_X509_STORE_GET1_ALL_CERTS /* else, could bodge via X509_STORE_get0_objects() - - but is OCSP_resp_get0_signer) avail? from 1.1.1 */ +#ifdef EXIM_HAVE_OPENSSL_X509_STORE_GET1_ALL_CERTS +/* could do via X509_STORE_get0_objects(); not worth it just for debug info */ { X509 * signer; if (OCSP_resp_get0_signer(bs, &signer, X509_STORE_get1_all_certs(verify_store)) == 1) @@ -2623,11 +2622,14 @@ if (!(bs = OCSP_response_get1_basic(rsp))) if ((i = OCSP_basic_verify(bs, SSL_get_peer_cert_chain(ssl), verify_store, +#ifdef SUPPORT_DANE tls_out.dane_verified ? have_verified_OCSP_signer ? OCSP_NOVERIFY | OCSP_NOEXPLICIT : OCSP_PARTIAL_CHAIN | OCSP_NOEXPLICIT - : OCSP_NOEXPLICIT)) <= 0) + : +#endif + OCSP_NOEXPLICIT)) <= 0) { DEBUG(D_tls) debug_printf("OCSP_basic_verify() fail: returned %d\n", i); if (ERR_peek_error()) commit 02a33248dc5981a63931530a57c76ff27464bde5 Author: Jeremy Harris Date: Sun Nov 27 11:29:40 2022 +0000 Fix LibreSSL build diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index e673cb9f9..8ed413e91 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -47,7 +47,6 @@ functions from the OpenSSL library. */ #endif #if OPENSSL_VERSION_NUMBER >= 0x10100000L # define EXIM_HAVE_OCSP_RESP_COUNT -# define EXIM_HAVE_SSL_GET0_VERIFIED_CHAIN # define OPENSSL_AUTO_SHA256 # define OPENSSL_MIN_PROTO_VERSION #else @@ -80,6 +79,7 @@ change this guard and punt the issue for a while longer. */ # define EXIM_HAVE_OPESSL_TRACE # define EXIM_HAVE_OPESSL_GET0_SERIAL # define EXIM_HAVE_OPESSL_OCSP_RESP_GET0_CERTS +# define EXIM_HAVE_SSL_GET0_VERIFIED_CHAIN # ifndef DISABLE_OCSP # define EXIM_HAVE_OCSP # endif @@ -2232,7 +2232,7 @@ if (lib_ctx_new(&server_sni, NULL, &dummy_errstr) != OK) /* Not sure how many of these are actually needed, since SSL object already exists. Might even need this selfsame callback, for reneg? */ - { + { SSL_CTX * ctx = state_server.lib_state.lib_ctx; SSL_CTX_set_info_callback(server_sni, SSL_CTX_get_info_callback(ctx)); SSL_CTX_set_mode(server_sni, SSL_CTX_get_mode(ctx)); @@ -2244,7 +2244,7 @@ already exists. Might even need this selfsame callback, for reneg? */ SSL_CTX_set_timeout(server_sni, SSL_CTX_get_timeout(ctx)); SSL_CTX_set_tlsext_servername_callback(server_sni, tls_servername_cb); SSL_CTX_set_tlsext_servername_arg(server_sni, state); - } + } if ( !init_dh(server_sni, state->dhparam, &dummy_errstr) || !init_ecdh(server_sni, &dummy_errstr) commit a85c067ba6c6940512cf57ec213277a370d87e70 Author: Jeremy Harris Date: Sun Nov 27 14:38:37 2022 +0000 SPDX: license tags (mostly by guesswork) diff --git a/src/exim_monitor/em_StripChart.c b/src/exim_monitor/em_StripChart.c index 3b94c2231..3eb98e4fb 100644 --- a/src/exim_monitor/em_StripChart.c +++ b/src/exim_monitor/em_StripChart.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /*********************************************************** Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts, and the Massachusetts Institute of Technology, Cambridge, Massachusetts. diff --git a/src/exim_monitor/em_TextPop.c b/src/exim_monitor/em_TextPop.c index ff5d1a8f7..0e87cb580 100644 --- a/src/exim_monitor/em_TextPop.c +++ b/src/exim_monitor/em_TextPop.c @@ -2,6 +2,7 @@ Copyright (c) The Exim Maintainers 2022 Copyright 1989 by the Massachusetts Institute of Technology, Cambridge, Massachusetts. +SPDX-License-Identifier: GPL-2.0-only All Rights Reserved diff --git a/src/exim_monitor/em_globals.c b/src/exim_monitor/em_globals.c index 3d452c6ec..2943065b4 100644 --- a/src/exim_monitor/em_globals.c +++ b/src/exim_monitor/em_globals.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "em_hdr.h" diff --git a/src/exim_monitor/em_hdr.h b/src/exim_monitor/em_hdr.h index ab37806b7..76970c997 100644 --- a/src/exim_monitor/em_hdr.h +++ b/src/exim_monitor/em_hdr.h @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This is the general header file for all the modules that comprise diff --git a/src/exim_monitor/em_init.c b/src/exim_monitor/em_init.c index e0bc3b066..e1f53fbba 100644 --- a/src/exim_monitor/em_init.c +++ b/src/exim_monitor/em_init.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This module contains code to initialize things from the environment and the arguments. */ diff --git a/src/exim_monitor/em_log.c b/src/exim_monitor/em_log.c index 8d85c13f1..d625056c5 100644 --- a/src/exim_monitor/em_log.c +++ b/src/exim_monitor/em_log.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainters 2021 - 2022 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This module contains code for scanning the main log, extracting information from it, and displaying a "tail". */ diff --git a/src/exim_monitor/em_main.c b/src/exim_monitor/em_main.c index 86caf71eb..919cde632 100644 --- a/src/exim_monitor/em_main.c +++ b/src/exim_monitor/em_main.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "em_hdr.h" diff --git a/src/exim_monitor/em_menu.c b/src/exim_monitor/em_menu.c index 881f3748c..afcd31540 100644 --- a/src/exim_monitor/em_menu.c +++ b/src/exim_monitor/em_menu.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "em_hdr.h" diff --git a/src/exim_monitor/em_queue.c b/src/exim_monitor/em_queue.c index 9badd2451..d9cfad38a 100644 --- a/src/exim_monitor/em_queue.c +++ b/src/exim_monitor/em_queue.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "em_hdr.h" diff --git a/src/exim_monitor/em_strip.c b/src/exim_monitor/em_strip.c index 03864d290..cfafe9257 100644 --- a/src/exim_monitor/em_strip.c +++ b/src/exim_monitor/em_strip.c @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "em_hdr.h" diff --git a/src/exim_monitor/em_text.c b/src/exim_monitor/em_text.c index 3a3682959..1077353e0 100644 --- a/src/exim_monitor/em_text.c +++ b/src/exim_monitor/em_text.c @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "em_hdr.h" diff --git a/src/exim_monitor/em_version.c b/src/exim_monitor/em_version.c index b627a6e2b..c5d4d62d8 100644 --- a/src/exim_monitor/em_version.c +++ b/src/exim_monitor/em_version.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #define EM_VERSION_C diff --git a/src/exim_monitor/em_xs.c b/src/exim_monitor/em_xs.c index ee91f7c15..dd19c7f43 100644 --- a/src/exim_monitor/em_xs.c +++ b/src/exim_monitor/em_xs.c @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge, 1995 - 2016 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This file contains a number of subroutines that are in effect just alternative packaging for calls to various X functions that diff --git a/src/scripts/Configure-config.h b/src/scripts/Configure-config.h index 75d366fca..c750f64f1 100755 --- a/src/scripts/Configure-config.h +++ b/src/scripts/Configure-config.h @@ -1,4 +1,5 @@ #! /bin/sh +# SPDX-License-Identifier: GPL-2.0-only # Build the config.h file, using the buildconfig program, first ensuring that # it exists. diff --git a/src/scripts/Configure-os.c b/src/scripts/Configure-os.c index f00a5627a..ab40ad183 100755 --- a/src/scripts/Configure-os.c +++ b/src/scripts/Configure-os.c @@ -1,4 +1,5 @@ #! /bin/sh +# SPDX-License-Identifier: GPL-2.0-only # Shell script to build os.c. There doesn't have to be an OS-specific os.c # file, but if there is, it gets copied at the start of os.c. The basic src diff --git a/src/scripts/Configure-os.h b/src/scripts/Configure-os.h index ae1ecf943..f95a61a5d 100755 --- a/src/scripts/Configure-os.h +++ b/src/scripts/Configure-os.h @@ -1,4 +1,5 @@ #! /bin/sh +# SPDX-License-Identifier: GPL-2.0-only # Shell script to create a link to the appropriate OS-specific header file. diff --git a/src/src/acl.c b/src/src/acl.c index 1e7d28a90..92af9991f 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Code for handling Access Control Lists (ACLs) */ diff --git a/src/src/arc.c b/src/src/arc.c index 86688f6e3..e3a2b3dad 100644 --- a/src/src/arc.c +++ b/src/src/arc.c @@ -5,6 +5,7 @@ Copyright (c) Jeremy Harris 2018 - 2020 Copyright (c) The Exim Maintainers 2021 - 2022 License: GPL + SPDX-License-Identifier: GPL-2.0-only */ #include "exim.h" diff --git a/src/src/auths/auth-spa.c b/src/src/auths/auth-spa.c index 8d886b6b6..32276c3f3 100644 --- a/src/src/auths/auth-spa.c +++ b/src/src/auths/auth-spa.c @@ -10,6 +10,7 @@ * Samba project (by Andrew Tridgell, Jeremy Allison, and others). * * Copyright (c) The Exim Maintainers 2021 + * SPDX-License-Identifier: GPL-2.0-only * Tom Kistner provided additional code, adding spa_build_auth_challenge() to * support server authentication mode. diff --git a/src/src/auths/auth-spa.h b/src/src/auths/auth-spa.h index cfe1b086d..df250d7a6 100644 --- a/src/src/auths/auth-spa.h +++ b/src/src/auths/auth-spa.h @@ -9,6 +9,7 @@ * All the code used here was torn by Marc Prud'hommeaux out of the * Samba project (by Andrew Tridgell, Jeremy Allison, and others). */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* December 2004: The spa_base64_to_bits() function has no length checking in it. I have added a check. PH */ diff --git a/src/src/auths/call_pam.c b/src/src/auths/call_pam.c index 80f80f1a8..483b083be 100644 --- a/src/src/auths/call_pam.c +++ b/src/src/auths/call_pam.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/auths/call_pwcheck.c b/src/src/auths/call_pwcheck.c index 0adde4471..436b1a8de 100644 --- a/src/src/auths/call_pwcheck.c +++ b/src/src/auths/call_pwcheck.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This module contains interface functions to the two Cyrus authentication daemons. The original one was "pwcheck", which gives its name to the source diff --git a/src/src/auths/call_radius.c b/src/src/auths/call_radius.c index e7f9f521d..f4d7a9569 100644 --- a/src/src/auths/call_radius.c +++ b/src/src/auths/call_radius.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2016 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This file was originally supplied by Ian Kirk. The libradius support came from Alex Kiernan. */ diff --git a/src/src/auths/check_serv_cond.c b/src/src/auths/check_serv_cond.c index 457a7150c..033d2026b 100644 --- a/src/src/auths/check_serv_cond.c +++ b/src/src/auths/check_serv_cond.c @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/auths/cram_md5.c b/src/src/auths/cram_md5.c index 2c0616ca2..60128b83e 100644 --- a/src/src/auths/cram_md5.c +++ b/src/src/auths/cram_md5.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* The stand-alone version just tests the algorithm. We have to drag diff --git a/src/src/auths/cram_md5.h b/src/src/auths/cram_md5.h index 95644db68..25470a61b 100644 --- a/src/src/auths/cram_md5.h +++ b/src/src/auths/cram_md5.h @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Private structure for the private options. */ diff --git a/src/src/auths/cyrus_sasl.c b/src/src/auths/cyrus_sasl.c index c8e2da5de..4fe257ba7 100644 --- a/src/src/auths/cyrus_sasl.c +++ b/src/src/auths/cyrus_sasl.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This code was originally contributed by Matthew Byng-Maddick */ diff --git a/src/src/auths/cyrus_sasl.h b/src/src/auths/cyrus_sasl.h index 6cf883454..144ac5c80 100644 --- a/src/src/auths/cyrus_sasl.h +++ b/src/src/auths/cyrus_sasl.h @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Copyright (c) A L Digital Ltd 2004 */ diff --git a/src/src/auths/dovecot.c b/src/src/auths/dovecot.c index 5d7713389..ed56ab8cd 100644 --- a/src/src/auths/dovecot.c +++ b/src/src/auths/dovecot.c @@ -1,6 +1,7 @@ /* * Copyright (c) The Exim Maintainers 2006 - 2022 * Copyright (c) 2004 Andrey Panin + * SPDX-License-Identifier: GPL-2.0-or-later * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published diff --git a/src/src/auths/dovecot.h b/src/src/auths/dovecot.h index bfe1f0775..b5eaf4f16 100644 --- a/src/src/auths/dovecot.h +++ b/src/src/auths/dovecot.h @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* Copyright (c) The Exim Maintainters 2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Private structure for the private options. */ diff --git a/src/src/auths/external.c b/src/src/auths/external.c index 7e7fca841..736c33982 100644 --- a/src/src/auths/external.c +++ b/src/src/auths/external.c @@ -4,6 +4,7 @@ /* Copyright (c) Jeremy Harris 2019-2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This file provides an Exim authenticator driver for a server to verify a client SSL certificate, using the EXTERNAL diff --git a/src/src/auths/external.h b/src/src/auths/external.h index 7d43650bb..9abb46a6b 100644 --- a/src/src/auths/external.h +++ b/src/src/auths/external.h @@ -4,6 +4,7 @@ /* Copyright (c) Jeremy Harris 2019 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Private structure for the private options. */ diff --git a/src/src/auths/get_data.c b/src/src/auths/get_data.c index e0d79db3c..0c85d2651 100644 --- a/src/src/auths/get_data.c +++ b/src/src/auths/get_data.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/auths/get_no64_data.c b/src/src/auths/get_no64_data.c index a019756e5..76f421473 100644 --- a/src/src/auths/get_no64_data.c +++ b/src/src/auths/get_no64_data.c @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/auths/gsasl_exim.c b/src/src/auths/gsasl_exim.c index e49e83b81..aac9c84e6 100644 --- a/src/src/auths/gsasl_exim.c +++ b/src/src/auths/gsasl_exim.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2019 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Copyright (c) Twitter Inc 2012 Author: Phil Pennock */ diff --git a/src/src/auths/gsasl_exim.h b/src/src/auths/gsasl_exim.h index 19c903611..691d7d706 100644 --- a/src/src/auths/gsasl_exim.h +++ b/src/src/auths/gsasl_exim.h @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2019 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Copyright (c) Twitter Inc 2012 */ diff --git a/src/src/auths/heimdal_gssapi.c b/src/src/auths/heimdal_gssapi.c index 381763299..12267e3fc 100644 --- a/src/src/auths/heimdal_gssapi.c +++ b/src/src/auths/heimdal_gssapi.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Copyright (c) Twitter Inc 2012 Author: Phil Pennock */ diff --git a/src/src/auths/heimdal_gssapi.h b/src/src/auths/heimdal_gssapi.h index 49775aff9..031b580ef 100644 --- a/src/src/auths/heimdal_gssapi.h +++ b/src/src/auths/heimdal_gssapi.h @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Copyright (c) Twitter Inc 2012 Author: Phil Pennock */ diff --git a/src/src/auths/plaintext.c b/src/src/auths/plaintext.c index 58d178359..61be5867e 100644 --- a/src/src/auths/plaintext.c +++ b/src/src/auths/plaintext.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" #include "plaintext.h" diff --git a/src/src/auths/plaintext.h b/src/src/auths/plaintext.h index 4c6d01136..49862ff9b 100644 --- a/src/src/auths/plaintext.h +++ b/src/src/auths/plaintext.h @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Private structure for the private options. */ diff --git a/src/src/auths/pwcheck.c b/src/src/auths/pwcheck.c index 7dd529fbc..aff5ed39c 100644 --- a/src/src/auths/pwcheck.c +++ b/src/src/auths/pwcheck.c @@ -4,6 +4,7 @@ * $Id: checkpw.c,v 1.49 2002/03/07 19:14:04 ken3 Exp $ */ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2001 Carnegie Mellon University. All rights reserved. * diff --git a/src/src/auths/pwcheck.h b/src/src/auths/pwcheck.h index 1287ea253..4247b4e8b 100644 --- a/src/src/auths/pwcheck.h +++ b/src/src/auths/pwcheck.h @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This file provides support for authentication via the Cyrus SASL pwcheck daemon (whence its name) and the newer saslauthd daemon. */ diff --git a/src/src/auths/spa.c b/src/src/auths/spa.c index ff90d33a3..c5e6d83d5 100644 --- a/src/src/auths/spa.c +++ b/src/src/auths/spa.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This file, which provides support for Microsoft's Secure Password Authentication, was contributed by Marc Prud'hommeaux. Tom Kistner added SPA diff --git a/src/src/auths/spa.h b/src/src/auths/spa.h index ca93469a3..4321971d9 100644 --- a/src/src/auths/spa.h +++ b/src/src/auths/spa.h @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This file, which provides support for Microsoft's Secure Password Authentication, was contributed by Marc Prud'hommeaux. */ diff --git a/src/src/auths/tls.c b/src/src/auths/tls.c index 325e7b4c7..d3ca8f796 100644 --- a/src/src/auths/tls.c +++ b/src/src/auths/tls.c @@ -4,6 +4,7 @@ /* Copyright (c) Jeremy Harris 1995 - 2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This file provides an Exim authenticator driver for a server to verify a client SSL certificate diff --git a/src/src/auths/tls.h b/src/src/auths/tls.h index 7aa95b6c7..24e116464 100644 --- a/src/src/auths/tls.h +++ b/src/src/auths/tls.h @@ -4,6 +4,7 @@ /* Copyright (c) Jeremy Harris 2015 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Private structure for the private options. */ diff --git a/src/src/auths/xtextdecode.c b/src/src/auths/xtextdecode.c index 746dfbdb4..39c1f2b60 100644 --- a/src/src/auths/xtextdecode.c +++ b/src/src/auths/xtextdecode.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/auths/xtextencode.c b/src/src/auths/xtextencode.c index fc571c799..89f12f1f1 100644 --- a/src/src/auths/xtextencode.c +++ b/src/src/auths/xtextencode.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/base64.c b/src/src/base64.c index fa06a7a34..14e8cfffe 100644 --- a/src/src/base64.c +++ b/src/src/base64.c @@ -4,6 +4,7 @@ /* Copyright (c) Tom Kistner 2004, 2015 */ /* License: GPL */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ diff --git a/src/src/blob.h b/src/src/blob.h index a3f1e24d4..95a987f0a 100644 --- a/src/src/blob.h +++ b/src/src/blob.h @@ -2,6 +2,7 @@ * Blob - a general pointer/size item for a memory chunk * * Copyright (C) 2016 Exim maintainers + * SPDX-License-Identifier: GPL-2.0-only */ #ifndef BLOB_H /* entire file */ diff --git a/src/src/bmi_spam.c b/src/src/bmi_spam.c index af4bc4640..140bec48f 100644 --- a/src/src/bmi_spam.c +++ b/src/src/bmi_spam.c @@ -6,6 +6,7 @@ Copyright (c) Tom Kistner 2004 License: GPL */ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "exim.h" #ifdef EXPERIMENTAL_BRIGHTMAIL diff --git a/src/src/bmi_spam.h b/src/src/bmi_spam.h index a9af7784e..a9833b200 100644 --- a/src/src/bmi_spam.h +++ b/src/src/bmi_spam.h @@ -5,6 +5,7 @@ /* Code for calling Brightmail AntiSpam. Copyright (c) Tom Kistner 2004 License: GPL */ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifdef EXPERIMENTAL_BRIGHTMAIL diff --git a/src/src/buildconfig.c b/src/src/buildconfig.c index bbbaefe9c..710b53550 100644 --- a/src/src/buildconfig.c +++ b/src/src/buildconfig.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /************************************************* diff --git a/src/src/child.c b/src/src/child.c index 1f38b585c..b94e814a1 100644 --- a/src/src/child.c +++ b/src/src/child.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "exim.h" diff --git a/src/src/crypt16.c b/src/src/crypt16.c index 56353c326..659bf88bd 100644 --- a/src/src/crypt16.c +++ b/src/src/crypt16.c @@ -2,6 +2,7 @@ * Copyright (c) 2000-2002 * Chris Adams * written for HiWAAY Internet Services + * SPDX-License-Identifier: GPL-2.0-or-later * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/src/daemon.c b/src/src/daemon.c index 8446f81cb..0afc7ca86 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions concerned with running Exim as a daemon */ diff --git a/src/src/dane.c b/src/src/dane.c index 5ba61961a..f2ad22481 100644 --- a/src/src/dane.c +++ b/src/src/dane.c @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2012, 2014 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This module provides DANE (RFC6659) support for Exim. See also the draft RFC for DANE-over-SMTP, "SMTP security via opportunistic DANE TLS" diff --git a/src/src/dbfn.c b/src/src/dbfn.c index ea94b7ff0..f932520f9 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "exim.h" diff --git a/src/src/dbfunctions.h b/src/src/dbfunctions.h index 07d4a6233..93e1d3405 100644 --- a/src/src/dbfunctions.h +++ b/src/src/dbfunctions.h @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 1995 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef DBFUNCTIONS_H #define DBFUNCTIONS_H diff --git a/src/src/dcc.c b/src/src/dcc.c index 56dd6d570..d8e22b30c 100644 --- a/src/src/dcc.c +++ b/src/src/dcc.c @@ -8,6 +8,7 @@ * See the file NOTICE for conditions of use and distribution. * * Copyright (c) The Exim Maintainers 2015 - 2022 + * SPDX-License-Identifier: GPL-2.0-only */ /* Code for calling dccifd. Called from acl.c. */ diff --git a/src/src/debug.c b/src/src/debug.c index 26d09ea2f..38e8f8001 100644 --- a/src/src/debug.c +++ b/src/src/debug.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2015 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "exim.h" diff --git a/src/src/deliver.c b/src/src/deliver.c index 725d0c872..719fa9d93 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* The main code for delivering a message. */ diff --git a/src/src/directory.c b/src/src/directory.c index 189020855..c3b341bbb 100644 --- a/src/src/directory.c +++ b/src/src/directory.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2010 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "exim.h" diff --git a/src/src/dkim.c b/src/src/dkim.c index bb916d2eb..9b6e14a3f 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge, 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Code for DKIM support. Other DKIM relevant code is in receive.c, transport.c and transports/smtp.c */ diff --git a/src/src/dkim.h b/src/src/dkim.h index 7b94f22ef..61d83a9df 100644 --- a/src/src/dkim.h +++ b/src/src/dkim.h @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge, 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ void dkim_exim_init(void); gstring * dkim_exim_sign(int, off_t, uschar *, struct ob_dkim *, const uschar **); diff --git a/src/src/dkim_transport.c b/src/src/dkim_transport.c index cfd4b9085..142f4552a 100644 --- a/src/src/dkim_transport.c +++ b/src/src/dkim_transport.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Transport shim for dkim signing */ diff --git a/src/src/dmarc.c b/src/src/dmarc.c index 53c2752ac..c0313bf5c 100644 --- a/src/src/dmarc.c +++ b/src/src/dmarc.c @@ -5,6 +5,7 @@ Copyright (c) The Exim Maintainers 2019 - 2022 Copyright (c) Todd Lyons 2012 - 2014 License: GPL */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Portions Copyright (c) 2012, 2013, The Trusted Domain Project; All rights reserved, licensed for use per LICENSE.opendmarc. */ diff --git a/src/src/dmarc.h b/src/src/dmarc.h index 899cd7e13..f71f1fd5a 100644 --- a/src/src/dmarc.h +++ b/src/src/dmarc.h @@ -6,6 +6,7 @@ Copyright (c) The Exim Maintainers 2021 - 2022 Copyright (c) Todd Lyons 2012 - 2014 License: GPL */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Portions Copyright (c) 2012, 2013, The Trusted Domain Project; All rights reserved, licensed for use per LICENSE.opendmarc. */ diff --git a/src/src/dns.c b/src/src/dns.c index 4071c5822..8106fb688 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions for interfacing with the DNS. */ diff --git a/src/src/dnsbl.c b/src/src/dnsbl.c index db839af04..af80f6be1 100644 --- a/src/src/dnsbl.c +++ b/src/src/dnsbl.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions concerned with dnsbls */ diff --git a/src/src/drtables.c b/src/src/drtables.c index 30eb855e3..a6cbede89 100644 --- a/src/src/drtables.c +++ b/src/src/drtables.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "exim.h" diff --git a/src/src/dummies.c b/src/src/dummies.c index 38b514b6c..a72767ccd 100644 --- a/src/src/dummies.c +++ b/src/src/dummies.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This file is not part of the main Exim code. There are little bits of test code for some of Exim's modules, and when they are used, the module they are diff --git a/src/src/enq.c b/src/src/enq.c index f7f8c9c16..054aeaed8 100644 --- a/src/src/enq.c +++ b/src/src/enq.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions concerned with serialization. */ diff --git a/src/src/environment.c b/src/src/environment.c index 9cb90c86f..06f804e69 100644 --- a/src/src/environment.c +++ b/src/src/environment.c @@ -5,6 +5,7 @@ /* Copyright (c) Heiko Schlittermann 2016 * hs@schlittermann.de * See the file NOTICE for conditions of use and distribution. + * SPDX-License-Identifier: GPL-2.0-only */ #include "exim.h" diff --git a/src/src/exim.c b/src/src/exim.c index 3cd1d5f6d..62413e367 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* The main function: entry point, initialization, and high-level control. diff --git a/src/src/exim.h b/src/src/exim.h index 61642b5e7..24fedcced 100644 --- a/src/src/exim.h +++ b/src/src/exim.h @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Source files for exim all #include this header, which drags in everything diff --git a/src/src/exim_dbmbuild.c b/src/src/exim_dbmbuild.c index 5c046347b..bb04adc02 100644 --- a/src/src/exim_dbmbuild.c +++ b/src/src/exim_dbmbuild.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* A small freestanding program to build dbm databases from serial input. For diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index 3824309b5..669f7098d 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This single source file is used to compile three utility programs for diff --git a/src/src/exim_lock.c b/src/src/exim_lock.c index 427d22c1e..1a54a92ad 100644 --- a/src/src/exim_lock.c +++ b/src/src/exim_lock.c @@ -11,6 +11,7 @@ Default is -fcntl -lockfile. Argument: the name of the lock file Copyright (c) The Exim Maintainers 2016 - 2021 +SPDX-License-Identifier: GPL-2.0-only */ #include "os.h" diff --git a/src/src/expand.c b/src/src/expand.c index ff0b4d600..050f01297 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions for handling string expansion. */ diff --git a/src/src/filter.c b/src/src/filter.c index cc4af230e..82a9122c6 100644 --- a/src/src/filter.c +++ b/src/src/filter.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Code for mail filtering functions. */ diff --git a/src/src/filtertest.c b/src/src/filtertest.c index 87ebfb14f..8bab65e78 100644 --- a/src/src/filtertest.c +++ b/src/src/filtertest.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Code for the filter test function. */ diff --git a/src/src/functions.h b/src/src/functions.h index d5164547a..be1fae00d 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Prototypes for functions that appear in various modules. Gathered together diff --git a/src/src/globals.c b/src/src/globals.c index cafb15992..429952edc 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* All the global variables are defined together in this one module, so that they are easy to find. */ diff --git a/src/src/globals.h b/src/src/globals.h index 1be96daec..48d93a1c3 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Almost all the global variables are defined together in this one header, so that they are easy to find. However, those that are visible during the diff --git a/src/src/hash.c b/src/src/hash.c index 51bcd4604..2b71463f1 100644 --- a/src/src/hash.c +++ b/src/src/hash.c @@ -3,6 +3,7 @@ * * Copyright (c) The Exim Maintainers 2010 - 2022 * Copyright (c) University of Cambridge 1995 - 2009 + * SPDX-License-Identifier: GPL-2.0-only * * Hash interface functions */ diff --git a/src/src/hash.h b/src/src/hash.h index 588325baf..c94e53129 100644 --- a/src/src/hash.h +++ b/src/src/hash.h @@ -1,6 +1,7 @@ /* * Exim - an Internet mail transport agent * Copyright (c) The Exim Maintainers 1995 - 2022 + * SPDX-License-Identifier: GPL-2.0-only * * Hash interface functions */ diff --git a/src/src/header.c b/src/src/header.c index 7ef59ff53..2a8fbfe64 100644 --- a/src/src/header.c +++ b/src/src/header.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2016 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "exim.h" diff --git a/src/src/hintsdb.h b/src/src/hintsdb.h index b8e6744d6..e79e6bd49 100644 --- a/src/src/hintsdb.h +++ b/src/src/hintsdb.h @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This header file contains macro definitions so that a variety of DBM libraries can be used by Exim. Nigel Metheringham provided the original set for diff --git a/src/src/hintsdb_structs.h b/src/src/hintsdb_structs.h index e0670a10e..27cd9edf9 100644 --- a/src/src/hintsdb_structs.h +++ b/src/src/hintsdb_structs.h @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This header file contains the definitions of the structures used in the various hints databases are also kept in this file, which is used by the diff --git a/src/src/host.c b/src/src/host.c index b6c2ea082..874e19a08 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions for finding hosts, either by gethostbyname(), gethostbyaddr(), or directly via the DNS. When IPv6 is supported, getipnodebyname() and diff --git a/src/src/host_address.c b/src/src/host_address.c index 9e6f958be..28a8a685f 100644 --- a/src/src/host_address.c +++ b/src/src/host_address.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "exim.h" diff --git a/src/src/imap_utf7.c b/src/src/imap_utf7.c index aac0fef6a..75ecb1a4b 100644 --- a/src/src/imap_utf7.c +++ b/src/src/imap_utf7.c @@ -1,5 +1,6 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "exim.h" diff --git a/src/src/ip.c b/src/src/ip.c index aa42343fb..2ac2b267d 100644 --- a/src/src/ip.c +++ b/src/src/ip.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions for doing things with sockets. With the advent of IPv6 this has got messier, so that it's worth pulling out the code into separate functions diff --git a/src/src/local_scan.c b/src/src/local_scan.c index 7a3bae7e3..85ecba8cb 100644 --- a/src/src/local_scan.c +++ b/src/src/local_scan.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /****************************************************************************** diff --git a/src/src/local_scan.h b/src/src/local_scan.h index c609a27e3..2eabc5929 100644 --- a/src/src/local_scan.h +++ b/src/src/local_scan.h @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This file is the header that is the only Exim header to be included in the source for the local_scan.c() function. It contains definitions that are made diff --git a/src/src/log.c b/src/src/log.c index a46d523db..f1d435ce4 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions for writing log files. The code for maintaining datestamped log files was originally contributed by Tony Sheen. */ diff --git a/src/src/lookupapi.h b/src/src/lookupapi.h index 41cc239ce..cec8f976b 100644 --- a/src/src/lookupapi.h +++ b/src/src/lookupapi.h @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* The "type" field in each item is a set of bit flags: diff --git a/src/src/lookups/cdb.c b/src/src/lookups/cdb.c index 966078f1a..696e52019 100644 --- a/src/src/lookups/cdb.c +++ b/src/src/lookups/cdb.c @@ -8,6 +8,7 @@ * * Copyright (c) The Exim Maintainers 2020 - 2022 * Copyright (c) 1998 Nigel Metheringham, Planet Online Ltd + * SPDX-License-Identifier: GPL-2.0-or-later * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/src/lookups/dbmdb.c b/src/src/lookups/dbmdb.c index 32514afcc..c99e948b5 100644 --- a/src/src/lookups/dbmdb.c +++ b/src/src/lookups/dbmdb.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" #include "lf_functions.h" diff --git a/src/src/lookups/dnsdb.c b/src/src/lookups/dnsdb.c index 355be1b5d..4f43cf674 100644 --- a/src/src/lookups/dnsdb.c +++ b/src/src/lookups/dnsdb.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" #include "lf_functions.h" diff --git a/src/src/lookups/dsearch.c b/src/src/lookups/dsearch.c index a7691024a..6cae0dafb 100644 --- a/src/src/lookups/dsearch.c +++ b/src/src/lookups/dsearch.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* The idea for this code came from Matthew Byng-Maddick, but his original has been heavily reworked a lot for Exim 4 (and it now uses stat() (more precisely: diff --git a/src/src/lookups/ibase.c b/src/src/lookups/ibase.c index c4fff71bd..d42e490c4 100644 --- a/src/src/lookups/ibase.c +++ b/src/src/lookups/ibase.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* The code in this module was contributed by Ard Biesheuvel. */ diff --git a/src/src/lookups/json.c b/src/src/lookups/json.c index c9abf8c4c..b1e5fb742 100644 --- a/src/src/lookups/json.c +++ b/src/src/lookups/json.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) Jeremy Harris 2019 - 2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" #include "lf_functions.h" diff --git a/src/src/lookups/ldap.c b/src/src/lookups/ldap.c index 9751fa3b3..17c431e5c 100644 --- a/src/src/lookups/ldap.c +++ b/src/src/lookups/ldap.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Many thanks to Stuart Lynne for contributing the original code for this driver. Further contributions from Michael Haardt, Brian Candler, Barry diff --git a/src/src/lookups/ldap.h b/src/src/lookups/ldap.h index ddfda8597..30228aebe 100644 --- a/src/src/lookups/ldap.h +++ b/src/src/lookups/ldap.h @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Header for eldapauth_find */ diff --git a/src/src/lookups/lf_check_file.c b/src/src/lookups/lf_check_file.c index 7f0f12806..1649f9f83 100644 --- a/src/src/lookups/lf_check_file.c +++ b/src/src/lookups/lf_check_file.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/lookups/lf_functions.h b/src/src/lookups/lf_functions.h index fd9eb303a..b83b9652a 100644 --- a/src/src/lookups/lf_functions.h +++ b/src/src/lookups/lf_functions.h @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Header for the functions that are shared by the lookups */ diff --git a/src/src/lookups/lf_quote.c b/src/src/lookups/lf_quote.c index 6f4143d9f..816fe01e2 100644 --- a/src/src/lookups/lf_quote.c +++ b/src/src/lookups/lf_quote.c @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/lookups/lf_sqlperform.c b/src/src/lookups/lf_sqlperform.c index ce6f1635a..cf4b9cd0b 100644 --- a/src/src/lookups/lf_sqlperform.c +++ b/src/src/lookups/lf_sqlperform.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/lookups/lmdb.c b/src/src/lookups/lmdb.c index a32c7f7fb..ccfdbe453 100644 --- a/src/src/lookups/lmdb.c +++ b/src/src/lookups/lmdb.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 2016 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/lookups/lsearch.c b/src/src/lookups/lsearch.c index dcfdec91e..da73ae8bf 100644 --- a/src/src/lookups/lsearch.c +++ b/src/src/lookups/lsearch.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" #include "lf_functions.h" diff --git a/src/src/lookups/mysql.c b/src/src/lookups/mysql.c index 78b8c2b19..a8dae4ade 100644 --- a/src/src/lookups/mysql.c +++ b/src/src/lookups/mysql.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Thanks to Paul Kelly for contributing the original code for these functions. */ diff --git a/src/src/lookups/nis.c b/src/src/lookups/nis.c index 0024f44cf..e7c124757 100644 --- a/src/src/lookups/nis.c +++ b/src/src/lookups/nis.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" #include "lf_functions.h" diff --git a/src/src/lookups/nisplus.c b/src/src/lookups/nisplus.c index d9f3f7d83..78c79563f 100644 --- a/src/src/lookups/nisplus.c +++ b/src/src/lookups/nisplus.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" #include "lf_functions.h" diff --git a/src/src/lookups/oracle.c b/src/src/lookups/oracle.c index d32b5e44d..3f3868fe9 100644 --- a/src/src/lookups/oracle.c +++ b/src/src/lookups/oracle.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Interface to an Oracle database. This code was originally supplied by Paul Kelly, but I have hacked it around for various reasons, and tried to add diff --git a/src/src/lookups/passwd.c b/src/src/lookups/passwd.c index eaf78b210..7df1a5aaf 100644 --- a/src/src/lookups/passwd.c +++ b/src/src/lookups/passwd.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/lookups/pgsql.c b/src/src/lookups/pgsql.c index 4bb693ad1..4fcd0e3ed 100644 --- a/src/src/lookups/pgsql.c +++ b/src/src/lookups/pgsql.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Thanks to Petr Cech for contributing the original code for these functions. Thanks to Joachim Wieland for the initial patch for the Unix domain diff --git a/src/src/lookups/readsock.c b/src/src/lookups/readsock.c index 22179c91c..7c7b9cfa8 100644 --- a/src/src/lookups/readsock.c +++ b/src/src/lookups/readsock.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) Jeremy Harris 2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" #include "lf_functions.h" diff --git a/src/src/lookups/redis.c b/src/src/lookups/redis.c index 9c8559c1e..c7fcb66f0 100644 --- a/src/src/lookups/redis.c +++ b/src/src/lookups/redis.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/lookups/spf.c b/src/src/lookups/spf.c index 78d954c8c..a1052d7fc 100644 --- a/src/src/lookups/spf.c +++ b/src/src/lookups/spf.c @@ -7,6 +7,7 @@ Copyright (c) The Exim Maintainers 2020 - 2022 Copyright (c) 2005 Chris Webb, Arachsys Internet Services Ltd +SPDX-License-Identifier: GPL-2.0-or-later This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/src/src/lookups/sqlite.c b/src/src/lookups/sqlite.c index 9080ae702..b9a735e18 100644 --- a/src/src/lookups/sqlite.c +++ b/src/src/lookups/sqlite.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" #include "lf_functions.h" diff --git a/src/src/lookups/testdb.c b/src/src/lookups/testdb.c index 48241615f..f94150b68 100644 --- a/src/src/lookups/testdb.c +++ b/src/src/lookups/testdb.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" #include "lf_functions.h" diff --git a/src/src/lookups/whoson.c b/src/src/lookups/whoson.c index 990703fd0..cd236787e 100644 --- a/src/src/lookups/whoson.c +++ b/src/src/lookups/whoson.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This code originally came from Robert Wal. */ diff --git a/src/src/lss.c b/src/src/lss.c index 167522d2f..0d20c07b6 100644 --- a/src/src/lss.c +++ b/src/src/lss.c @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Support functions for calling from local_scan(). These are mostly just wrappers for various internal functions. */ diff --git a/src/src/macro_predef.c b/src/src/macro_predef.c index a0c659c9f..fe814500d 100644 --- a/src/src/macro_predef.c +++ b/src/src/macro_predef.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) Jeremy Harris 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Create a static data structure with the predefined macros, to be included in the main Exim build */ diff --git a/src/src/macro_predef.h b/src/src/macro_predef.h index 59b1bbe3b..d3bc5c074 100644 --- a/src/src/macro_predef.h +++ b/src/src/macro_predef.h @@ -5,6 +5,7 @@ /* Copyright (c) Jeremy Harris 2017 - 2018 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Global functions */ diff --git a/src/src/macros.h b/src/src/macros.h index adbe6a267..db1c34b65 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* These two macros make it possible to obtain the result of macro-expanding diff --git a/src/src/malware.c b/src/src/malware.c index 01dd455ba..96514e276 100644 --- a/src/src/malware.c +++ b/src/src/malware.c @@ -6,6 +6,7 @@ * Copyright (c) The Exim Maintainers 2015 - 2022 * Copyright (c) Tom Kistner 2003 - 2015 * License: GPL + * SPDX-License-Identifier: GPL-2.0-only */ /* Code for calling virus (malware) scanners. Called from acl.c. */ diff --git a/src/src/match.c b/src/src/match.c index b4a0352ee..a877aef3b 100644 --- a/src/src/match.c +++ b/src/src/match.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions for matching strings */ diff --git a/src/src/md5.c b/src/src/md5.c index fdb144e67..e6df32c7a 100644 --- a/src/src/md5.c +++ b/src/src/md5.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef STAND_ALONE #include "exim.h" diff --git a/src/src/mime.c b/src/src/mime.c index c1921992e..bebe9bf44 100644 --- a/src/src/mime.c +++ b/src/src/mime.c @@ -6,6 +6,7 @@ * Copyright (c) The Exim Maintainers 2015 - 2022 * Copyright (c) Tom Kistner 2004 - 2015 * License: GPL + * SPDX-License-Identifier: GPL-2.0-only */ #include "exim.h" diff --git a/src/src/mime.h b/src/src/mime.h index 5fd4392d5..48bbd81de 100644 --- a/src/src/mime.h +++ b/src/src/mime.h @@ -5,6 +5,7 @@ /* Copyright (c) Tom Kistner 2004, 2015 * License: GPL * Copyright (c) The Exim Maintainers 2016 + * SPDX-License-Identifier: GPL-2.0-only */ #ifdef WITH_CONTENT_SCAN diff --git a/src/src/moan.c b/src/src/moan.c index 4f2550d82..387359458 100644 --- a/src/src/moan.c +++ b/src/src/moan.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions for sending messages to sender or to mailmaster. */ diff --git a/src/src/mytypes.h b/src/src/mytypes.h index 141d2c613..06426af7a 100644 --- a/src/src/mytypes.h +++ b/src/src/mytypes.h @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This header file contains type definitions and macros that I use as diff --git a/src/src/os.c b/src/src/os.c index ac5f61b65..4edb2ca98 100644 --- a/src/src/os.c +++ b/src/src/os.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifdef STAND_ALONE # include diff --git a/src/src/osfunctions.h b/src/src/osfunctions.h index 547cf1379..454e9f77f 100644 --- a/src/src/osfunctions.h +++ b/src/src/osfunctions.h @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2016 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Prototypes for os-specific functions. For utilities, we don't need the one that uses a type that isn't defined for them. */ diff --git a/src/src/parse.c b/src/src/parse.c index bdba3ecd0..93b12bc77 100644 --- a/src/src/parse.c +++ b/src/src/parse.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions for parsing addresses */ diff --git a/src/src/pdkim/crypt_ver.h b/src/src/pdkim/crypt_ver.h index a6d7e36af..367527e39 100644 --- a/src/src/pdkim/crypt_ver.h +++ b/src/src/pdkim/crypt_ver.h @@ -4,6 +4,7 @@ /* Copyright (c) Jeremy Harris 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Signing and hashing routine selection for PDKIM */ diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index e47bfc505..eb26b3864 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -4,6 +4,7 @@ * Copyright (c) The Exim Maintainers 2021 - 2022 * Copyright (C) 2009 - 2016 Tom Kistner * Copyright (C) 2016 - 2020 Jeremy Harris + * SPDX-License-Identifier: GPL-2.0-or-later * * http://duncanthrax.net/pdkim/ * diff --git a/src/src/pdkim/pdkim.h b/src/src/pdkim/pdkim.h index f6ff78251..f918938e8 100644 --- a/src/src/pdkim/pdkim.h +++ b/src/src/pdkim/pdkim.h @@ -3,6 +3,7 @@ * * Copyright (C) 2009 - 2012 Tom Kistner * Copyright (c) 2016 - 2020 Jeremy Harris + * SPDX-License-Identifier: GPL-2.0-or-later * * http://duncanthrax.net/pdkim/ * diff --git a/src/src/pdkim/pdkim_hash.h b/src/src/pdkim/pdkim_hash.h index 8f9a12628..e470299a4 100644 --- a/src/src/pdkim/pdkim_hash.h +++ b/src/src/pdkim/pdkim_hash.h @@ -2,6 +2,7 @@ * PDKIM - a RFC4871 (DKIM) implementation * * Copyright (C) 1995 - 2018 Exim maintainers + * SPDX-License-Identifier: GPL-2.0-only * * Hash interface functions */ diff --git a/src/src/pdkim/signing.c b/src/src/pdkim/signing.c index d78f31acf..8876f617c 100644 --- a/src/src/pdkim/signing.c +++ b/src/src/pdkim/signing.c @@ -1,6 +1,7 @@ /* * PDKIM - a RFC4871 (DKIM) implementation * Copyright (c) The Exim Maintainers 1995 - 2022 + * SPDX-License-Identifier: GPL-2.0-only * * signing/verification interface */ diff --git a/src/src/pdkim/signing.h b/src/src/pdkim/signing.h index ed6f397ce..880342b6f 100644 --- a/src/src/pdkim/signing.h +++ b/src/src/pdkim/signing.h @@ -2,6 +2,7 @@ * PDKIM - a RFC4871 (DKIM) implementation * * Copyright (C) 1995 - 2020 Exim maintainers + * SPDX-License-Identifier: GPL-2.0-only * * RSA signing/verification interface */ diff --git a/src/src/perl.c b/src/src/perl.c index f07ee2e27..b5c3f882e 100644 --- a/src/src/perl.c +++ b/src/src/perl.c @@ -4,6 +4,7 @@ /* Copyright (c) The Exim Maintainers 1999 - 2022 */ /* Copyright (c) 1998 Malcolm Beattie */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Modified by PH to get rid of the "na" usage, March 1999. Modified further by PH for general tidying for Exim 4. diff --git a/src/src/priv.c b/src/src/priv.c index 94d425401..c818b5b78 100644 --- a/src/src/priv.c +++ b/src/src/priv.c @@ -1,3 +1,6 @@ +/* Copyright (c) The Exim Maintainers 2022 * +/* SPDX-License-Identifier: GPL-2.0-only */ + #include "exim.h" #include #include diff --git a/src/src/queue.c b/src/src/queue.c index 6e47d2c8a..fd84d303f 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions that operate on the input queue. */ diff --git a/src/src/rda.c b/src/src/rda.c index b635ebfde..1bc322e34 100644 --- a/src/src/rda.c +++ b/src/src/rda.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This module contains code for extracting addresses from a forwarding list (from an alias or forward file) or by running the filter interpreter. It may do diff --git a/src/src/readconf.c b/src/src/readconf.c index 83ee51b65..b0d688772 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions for reading the configuration file, and for displaying overall configuration values. Thanks to Brian Candler for the original diff --git a/src/src/receive.c b/src/src/receive.c index 0a27c7950..d9d0757ef 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Code for receiving a message and setting up spool files. */ diff --git a/src/src/regex.c b/src/src/regex.c index 210620f26..eefba8ecf 100644 --- a/src/src/regex.c +++ b/src/src/regex.c @@ -6,6 +6,7 @@ * Copyright (c) The Exim Maintainers 2016 - 2022 * Copyright (c) Tom Kistner 2003-2015 * License: GPL + * SPDX-License-Identifier: GPL-2.0-only */ /* Code for matching regular expressions against headers and body. diff --git a/src/src/regex_cache.c b/src/src/regex_cache.c index 63cddce1d..e4771aab9 100644 --- a/src/src/regex_cache.c +++ b/src/src/regex_cache.c @@ -5,6 +5,7 @@ /* * Copyright (c) The Exim Maintainers 2022 * License: GPL + * SPDX-License-Identifier: GPL-2.0-only */ /* Caching layers for compiled REs. There is a local layer in the process, diff --git a/src/src/retry.c b/src/src/retry.c index 033afb4bf..e1490266c 100644 --- a/src/src/retry.c +++ b/src/src/retry.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions concerned with retrying unsuccessful deliveries. */ diff --git a/src/src/rewrite.c b/src/src/rewrite.c index 90614e626..040525ec5 100644 --- a/src/src/rewrite.c +++ b/src/src/rewrite.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions concerned with rewriting headers */ diff --git a/src/src/rfc2047.c b/src/src/rfc2047.c index 1ed1dd809..af8993695 100644 --- a/src/src/rfc2047.c +++ b/src/src/rfc2047.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This file contains a function for decoding message header lines that may contain encoded "words" according to the rules described in diff --git a/src/src/route.c b/src/src/route.c index fa69b8b74..7e6e4eb69 100644 --- a/src/src/route.c +++ b/src/src/route.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions concerned with routing, and the list of generic router options. */ diff --git a/src/src/routers/accept.c b/src/src/routers/accept.c index 110a1efe4..ab02716eb 100644 --- a/src/src/routers/accept.c +++ b/src/src/routers/accept.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/routers/accept.h b/src/src/routers/accept.h index 43494fbc4..e025179ee 100644 --- a/src/src/routers/accept.h +++ b/src/src/routers/accept.h @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Private structure for the private options (there aren't any). */ diff --git a/src/src/routers/dnslookup.c b/src/src/routers/dnslookup.c index a845b4e52..0b1abeb8e 100644 --- a/src/src/routers/dnslookup.c +++ b/src/src/routers/dnslookup.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" #include "rf_functions.h" diff --git a/src/src/routers/dnslookup.h b/src/src/routers/dnslookup.h index b7e091587..0272db300 100644 --- a/src/src/routers/dnslookup.h +++ b/src/src/routers/dnslookup.h @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Private structure for the private options. */ diff --git a/src/src/routers/ipliteral.c b/src/src/routers/ipliteral.c index 3d68642a2..cfbf276ff 100644 --- a/src/src/routers/ipliteral.c +++ b/src/src/routers/ipliteral.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/routers/ipliteral.h b/src/src/routers/ipliteral.h index 1ddb38b10..f7e99b9c2 100644 --- a/src/src/routers/ipliteral.h +++ b/src/src/routers/ipliteral.h @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Private structure for the private options. Some compilers do not like empty diff --git a/src/src/routers/iplookup.c b/src/src/routers/iplookup.c index 8b67f3116..970ec7704 100644 --- a/src/src/routers/iplookup.c +++ b/src/src/routers/iplookup.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/routers/iplookup.h b/src/src/routers/iplookup.h index dbcb03c9e..3b7295574 100644 --- a/src/src/routers/iplookup.h +++ b/src/src/routers/iplookup.h @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Private structure for the private options. */ diff --git a/src/src/routers/manualroute.c b/src/src/routers/manualroute.c index 974ad0cf4..6a500d09a 100644 --- a/src/src/routers/manualroute.c +++ b/src/src/routers/manualroute.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/routers/manualroute.h b/src/src/routers/manualroute.h index 9c20b6fa0..e27d90953 100644 --- a/src/src/routers/manualroute.h +++ b/src/src/routers/manualroute.h @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Header for the manualroute router */ diff --git a/src/src/routers/queryprogram.c b/src/src/routers/queryprogram.c index 55f03a477..51b7b7551 100644 --- a/src/src/routers/queryprogram.c +++ b/src/src/routers/queryprogram.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" #include "rf_functions.h" diff --git a/src/src/routers/queryprogram.h b/src/src/routers/queryprogram.h index 93046bd0f..148846fae 100644 --- a/src/src/routers/queryprogram.h +++ b/src/src/routers/queryprogram.h @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Private structure for the private options. */ diff --git a/src/src/routers/redirect.c b/src/src/routers/redirect.c index 31c07f518..6126555fe 100644 --- a/src/src/routers/redirect.c +++ b/src/src/routers/redirect.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/routers/redirect.h b/src/src/routers/redirect.h index 4c0399a36..76d02f261 100644 --- a/src/src/routers/redirect.h +++ b/src/src/routers/redirect.h @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Header for the redirect router */ diff --git a/src/src/routers/rf_change_domain.c b/src/src/routers/rf_change_domain.c index d7c9c1cb8..4f8d47299 100644 --- a/src/src/routers/rf_change_domain.c +++ b/src/src/routers/rf_change_domain.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/routers/rf_expand_data.c b/src/src/routers/rf_expand_data.c index 6a8ad1779..5fbf0ac78 100644 --- a/src/src/routers/rf_expand_data.c +++ b/src/src/routers/rf_expand_data.c @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/routers/rf_functions.h b/src/src/routers/rf_functions.h index f310d5a42..d8fca68c5 100644 --- a/src/src/routers/rf_functions.h +++ b/src/src/routers/rf_functions.h @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Header for the functions that are shared by the routers */ diff --git a/src/src/routers/rf_get_errors_address.c b/src/src/routers/rf_get_errors_address.c index b9cf78124..1189fd9e0 100644 --- a/src/src/routers/rf_get_errors_address.c +++ b/src/src/routers/rf_get_errors_address.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" #include "rf_functions.h" diff --git a/src/src/routers/rf_get_munge_headers.c b/src/src/routers/rf_get_munge_headers.c index d304d1145..be86a4e0e 100644 --- a/src/src/routers/rf_get_munge_headers.c +++ b/src/src/routers/rf_get_munge_headers.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" #include "rf_functions.h" diff --git a/src/src/routers/rf_get_transport.c b/src/src/routers/rf_get_transport.c index 2f639e037..0c1fa8173 100644 --- a/src/src/routers/rf_get_transport.c +++ b/src/src/routers/rf_get_transport.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" #include "rf_functions.h" diff --git a/src/src/routers/rf_get_ugid.c b/src/src/routers/rf_get_ugid.c index 1735e5919..15144beb1 100644 --- a/src/src/routers/rf_get_ugid.c +++ b/src/src/routers/rf_get_ugid.c @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" #include "rf_functions.h" diff --git a/src/src/routers/rf_lookup_hostlist.c b/src/src/routers/rf_lookup_hostlist.c index 79a779927..affd70b6e 100644 --- a/src/src/routers/rf_lookup_hostlist.c +++ b/src/src/routers/rf_lookup_hostlist.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/routers/rf_queue_add.c b/src/src/routers/rf_queue_add.c index 0693c8c6e..49dd83117 100644 --- a/src/src/routers/rf_queue_add.c +++ b/src/src/routers/rf_queue_add.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" #include "rf_functions.h" diff --git a/src/src/routers/rf_self_action.c b/src/src/routers/rf_self_action.c index 9a4dc3cc7..7cc592039 100644 --- a/src/src/routers/rf_self_action.c +++ b/src/src/routers/rf_self_action.c @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/routers/rf_set_ugid.c b/src/src/routers/rf_set_ugid.c index e1346b4be..65f5200a2 100644 --- a/src/src/routers/rf_set_ugid.c +++ b/src/src/routers/rf_set_ugid.c @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" #include "rf_functions.h" diff --git a/src/src/search.c b/src/src/search.c index eec543776..32099ab9e 100644 --- a/src/src/search.c +++ b/src/src/search.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* A set of functions to search databases in various formats. An open database is represented by a void * value which is returned from a lookup- diff --git a/src/src/setenv.c b/src/src/setenv.c index 90e679304..877fe388f 100644 --- a/src/src/setenv.c +++ b/src/src/setenv.c @@ -6,6 +6,7 @@ * Copyright (c) Jeremy Harris 2015 - 2016 * Copyright (c) The Exim Maintainers 2016 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This module provides (un)setenv routines for those environments lacking them in libraries. It is #include'd by OS/os.c-foo files. */ diff --git a/src/src/sha_ver.h b/src/src/sha_ver.h index bc2b2f89e..8385c1377 100644 --- a/src/src/sha_ver.h +++ b/src/src/sha_ver.h @@ -4,6 +4,7 @@ /* Copyright (c) Jeremy Harris 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* SHA routine selection */ diff --git a/src/src/sieve.c b/src/src/sieve.c index af3bc9d9a..033a9734a 100644 --- a/src/src/sieve.c +++ b/src/src/sieve.c @@ -6,6 +6,7 @@ * Copyright (c) The Exim Maintainers 2016 - 2022 * Copyright (c) Michael Haardt 2003 - 2015 * See the file NOTICE for conditions of use and distribution. + * SPDX-License-Identifier: GPL-2.0-only */ /* This code was contributed by Michael Haardt. */ diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 28e529bae..3c6339c82 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions for handling an incoming SMTP call. */ diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index 7f364d942..db39dcab5 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* A number of functions for driving outgoing SMTP calls. */ diff --git a/src/src/spam.c b/src/src/spam.c index a68b9bf60..f6575c293 100644 --- a/src/src/spam.c +++ b/src/src/spam.c @@ -6,6 +6,7 @@ * Copyright (c) The Exim Maintainers 2016 - 2022 * Copyright (c) Tom Kistner 2003 - 2015 * License: GPL + * SPDX-License-Identifier: GPL-2.0-only */ /* Code for calling spamassassin's spamd. Called from acl.c. */ diff --git a/src/src/spam.h b/src/src/spam.h index cc36ffd3f..c410198d7 100644 --- a/src/src/spam.h +++ b/src/src/spam.h @@ -5,6 +5,7 @@ /* Copyright (c) Tom Kistner 2003 - 2015 */ /* Copyright (c) The Exim Maintainers 2021 */ /* License: GPL */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* spam defines */ diff --git a/src/src/spf.c b/src/src/spf.c index a8c0f75c4..a94bc9677 100644 --- a/src/src/spf.c +++ b/src/src/spf.c @@ -6,6 +6,7 @@ Copyright (c) The Exim Maintainers 2015 - 2022 Copyright (c) Tom Kistner 2004 - 2014 License: GPL + SPDX-License-Identifier: GPL-2.0-only */ /* Code for calling spf checks via libspf-alt. Called from acl.c. */ diff --git a/src/src/spf.h b/src/src/spf.h index f32d06906..8fb7b04cb 100644 --- a/src/src/spf.h +++ b/src/src/spf.h @@ -6,6 +6,7 @@ Copyright (c) The Exim Maintainers 2016 - 2022 Copyright (c) Tom Kistner 2004 License: GPL + SPDX-License-Identifier: GPL-2.0-only */ #ifdef SUPPORT_SPF diff --git a/src/src/spool_in.c b/src/src/spool_in.c index 2aa0b0b55..82d11bf65 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions for reading spool files. When compiling for a utility (eximon), not all are needed, and some functionality can be cut out. */ diff --git a/src/src/spool_mbox.c b/src/src/spool_mbox.c index 8b2aae394..d9767ec3c 100644 --- a/src/src/spool_mbox.c +++ b/src/src/spool_mbox.c @@ -5,6 +5,7 @@ /* Copyright (c) Tom Kistner 2003 - 2015 * License: GPL * Copyright (c) The Exim Maintainers 2016 - 2021 + * SPDX-License-Identifier: GPL-2.0-only */ /* Code for setting up a MBOX style spool file inside a /scan/ diff --git a/src/src/spool_out.c b/src/src/spool_out.c index 510eda6c1..cb409c641 100644 --- a/src/src/spool_out.c +++ b/src/src/spool_out.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions for writing spool files, and moving them about. */ diff --git a/src/src/std-crypto.c b/src/src/std-crypto.c index 200fb7144..c1eef6c35 100644 --- a/src/src/std-crypto.c +++ b/src/src/std-crypto.c @@ -7,6 +7,8 @@ * But almost everything here is fixed published constants from RFCs, so also: * Copyright (C) The Internet Society (2003) * Copyright (C) The IETF Trust (2008) + * SPDX-License-Identifier: GPL-2.0-only + * * Most of the text in RFC referencing comments is copy/paste from RFC, * as is undoubtedly the intention. * The constants are generated from that text using util/gen_pkcs3.c invoked diff --git a/src/src/store.c b/src/src/store.c index d99d2ab69..449fb4ead 100644 --- a/src/src/store.c +++ b/src/src/store.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim maintainers 2019 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Exim gets and frees all its store through these functions. In the original implementation there was a lot of mallocing and freeing of small bits of store. diff --git a/src/src/store.h b/src/src/store.h index ee6d79c36..47ed963cd 100644 --- a/src/src/store.h +++ b/src/src/store.h @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Header for Exim's memory allocation functions */ diff --git a/src/src/string.c b/src/src/string.c index a5161bb31..ff4180b2b 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Miscellaneous string-handling functions. Some are not required for utilities and tests, and are cut out by the COMPILE_UTILITY macro. */ diff --git a/src/src/structs.h b/src/src/structs.h index 06cd06084..1cb69236e 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Definitions of various structures. In addition, those that are visible for diff --git a/src/src/tls-cipher-stdname.c b/src/src/tls-cipher-stdname.c index ab973af51..fe442a050 100644 --- a/src/src/tls-cipher-stdname.c +++ b/src/src/tls-cipher-stdname.c @@ -4,6 +4,7 @@ /* Copyright (c) Jeremy Harris 2019 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Translate an IETF TLS ciphersuite code to an IETF ciphersuite name, for use when the TLS library do not provide such names. diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 1fc7828cf..c98760202 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -6,6 +6,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) Phil Pennock 2012 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This file provides TLS/SSL support for Exim using the GnuTLS library, one of the available supported implementations. This file is #included into diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 8ed413e91..052d8161d 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2019 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Portions Copyright (c) The OpenSSL Project 1999 */ diff --git a/src/src/tls.c b/src/src/tls.c index d7cefce67..9e20b5bca 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This module provides TLS (aka SSL) support for Exim. The code for OpenSSL is based on a patch that was originally contributed by Steve Haslam. It was diff --git a/src/src/tlscert-gnu.c b/src/src/tlscert-gnu.c index a40bb30bf..8840d5cfd 100644 --- a/src/src/tlscert-gnu.c +++ b/src/src/tlscert-gnu.c @@ -4,6 +4,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) Jeremy Harris 2014 - 2018 */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This file provides TLS/SSL support for Exim using the GnuTLS library, one of the available supported implementations. This file is #included into diff --git a/src/src/tlscert-openssl.c b/src/src/tlscert-openssl.c index ac353b25f..a4c3d19fa 100644 --- a/src/src/tlscert-openssl.c +++ b/src/src/tlscert-openssl.c @@ -4,6 +4,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) Jeremy Harris 2014 - 2019 */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* This module provides TLS (aka SSL) support for Exim using the OpenSSL library. It is #included into the tls.c file when that library is used. diff --git a/src/src/tod.c b/src/src/tod.c index 1f0bcc05e..ac4ed35b9 100644 --- a/src/src/tod.c +++ b/src/src/tod.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* A function for returning the time of day in various formats */ diff --git a/src/src/transport.c b/src/src/transport.c index 7477882cb..ff2e0b1d4 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* General functions concerned with transportation, and generic options for all transports. */ diff --git a/src/src/transports/appendfile.c b/src/src/transports/appendfile.c index 7e29dd3bc..18badde86 100644 --- a/src/src/transports/appendfile.c +++ b/src/src/transports/appendfile.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/transports/appendfile.h b/src/src/transports/appendfile.h index 3fd2f467c..108114905 100644 --- a/src/src/transports/appendfile.h +++ b/src/src/transports/appendfile.h @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Private structure for the private options. */ diff --git a/src/src/transports/autoreply.c b/src/src/transports/autoreply.c index 211e32810..eff1a3792 100644 --- a/src/src/transports/autoreply.c +++ b/src/src/transports/autoreply.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/transports/autoreply.h b/src/src/transports/autoreply.h index fcfd981e7..5f278a51e 100644 --- a/src/src/transports/autoreply.h +++ b/src/src/transports/autoreply.h @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Private structure for the private options. */ diff --git a/src/src/transports/lmtp.c b/src/src/transports/lmtp.c index f751771c1..c734c3a26 100644 --- a/src/src/transports/lmtp.c +++ b/src/src/transports/lmtp.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/transports/lmtp.h b/src/src/transports/lmtp.h index 93f0f89cc..71fd77f26 100644 --- a/src/src/transports/lmtp.h +++ b/src/src/transports/lmtp.h @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Private structure for the private options. */ diff --git a/src/src/transports/pipe.c b/src/src/transports/pipe.c index bdbe27d57..b8103e2f6 100644 --- a/src/src/transports/pipe.c +++ b/src/src/transports/pipe.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" diff --git a/src/src/transports/pipe.h b/src/src/transports/pipe.h index ed5c142b3..416569608 100644 --- a/src/src/transports/pipe.h +++ b/src/src/transports/pipe.h @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2014 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Private structure for the private options. */ diff --git a/src/src/transports/queuefile.c b/src/src/transports/queuefile.c index 74131cc64..3a2bae22f 100644 --- a/src/src/transports/queuefile.c +++ b/src/src/transports/queuefile.c @@ -6,6 +6,7 @@ /* Copyright (c) University of Cambridge 2016 */ /* Copyright (c) The Exim Maintainers 1995 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ diff --git a/src/src/transports/queuefile.h b/src/src/transports/queuefile.h index 0e45b51b0..22759a7f7 100644 --- a/src/src/transports/queuefile.h +++ b/src/src/transports/queuefile.h @@ -5,6 +5,7 @@ /* Copyright (c) Andrew Colin Kissa 2016 */ /* Copyright (c) University of Cambridge 2016 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Private structure for the private options. */ diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 0fca4584d..2d2db1b10 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "../exim.h" #include "smtp.h" diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index 319e8494e..8793ece4f 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #define DELIVER_BUFFER_SIZE 4096 diff --git a/src/src/transports/smtp_socks.c b/src/src/transports/smtp_socks.c index 0e58732c6..353a69618 100644 --- a/src/src/transports/smtp_socks.c +++ b/src/src/transports/smtp_socks.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) Jeremy Harris 2015 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* SOCKS version 5 proxy, client-mode */ diff --git a/src/src/transports/tf_maildir.c b/src/src/transports/tf_maildir.c index 205ee41cb..925b8fac0 100644 --- a/src/src/transports/tf_maildir.c +++ b/src/src/transports/tf_maildir.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions in support of the use of maildirsize files for handling quotas in maildir directories. Some of the rules are a bit baroque: diff --git a/src/src/transports/tf_maildir.h b/src/src/transports/tf_maildir.h index b3707b1a9..d314c3203 100644 --- a/src/src/transports/tf_maildir.h +++ b/src/src/transports/tf_maildir.h @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Header file for the functions that are used to support the use of maildirsize files for quota handling in maildir directories. */ diff --git a/src/src/tree.c b/src/src/tree.c index bb8ad44db..e340d986a 100644 --- a/src/src/tree.c +++ b/src/src/tree.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions for maintaining binary balanced trees and some associated functions as well. */ diff --git a/src/src/utf8.c b/src/src/utf8.c index bc7adb828..6604727ff 100644 --- a/src/src/utf8.c +++ b/src/src/utf8.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) Jeremy Harris 2015 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ #include "exim.h" diff --git a/src/src/verify.c b/src/src/verify.c index afc18d553..3a8914e38 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -5,6 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Functions concerned with verifying things. The original code for callout caching was contributed by Kevin Fleming (but I hacked it around a bit). */ diff --git a/src/src/version.c b/src/src/version.c index 118ebbdf2..cbaaef24b 100644 --- a/src/src/version.c +++ b/src/src/version.c @@ -5,6 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* Copyright (c) The Exim Maintainers 2010 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Function for setting up the version string. */ diff --git a/src/util/chunking_fixqueue_finalnewlines.pl b/src/util/chunking_fixqueue_finalnewlines.pl index 5dddfa505..b8ab17bd7 100755 --- a/src/util/chunking_fixqueue_finalnewlines.pl +++ b/src/util/chunking_fixqueue_finalnewlines.pl @@ -1,4 +1,5 @@ #!/usr/bin/env perl +# SPDX-License-Identifier: GPL-2.0-only use warnings; use strict; diff --git a/src/util/cramtest.pl b/src/util/cramtest.pl index 48f989a0c..a6124628b 100755 --- a/src/util/cramtest.pl +++ b/src/util/cramtest.pl @@ -1,4 +1,5 @@ #!/usr/bin/perl +# SPDX-License-Identifier: GPL-2.0-only # This script is contributed by Vadim Vygonets to aid in debugging CRAM-MD5 # authentication. diff --git a/src/util/gen_pkcs3.c b/src/util/gen_pkcs3.c index 2fe9a6c5e..3ef3110de 100644 --- a/src/util/gen_pkcs3.c +++ b/src/util/gen_pkcs3.c @@ -2,6 +2,7 @@ * Copyright (c) The Exim Maintainers 2021 * This is distributed as part of Exim and licensed under the GPL. * See the file "NOTICE" for more details. + * SPDX-License-Identifier: GPL-2.0-only */ /* Build with: diff --git a/src/util/logargs.sh b/src/util/logargs.sh index 87369a64b..814228d10 100755 --- a/src/util/logargs.sh +++ b/src/util/logargs.sh @@ -1,4 +1,5 @@ #! /bin/sh +# SPDX-License-Identifier: GPL-2.0-only # This script can be interposed between a calling program and another # program, in order to log the arguments which are being used. This can diff --git a/src/util/mkcdb.pl b/src/util/mkcdb.pl index 691849dcd..e2b5931e7 100755 --- a/src/util/mkcdb.pl +++ b/src/util/mkcdb.pl @@ -1,4 +1,5 @@ #!/usr/bin/perl -wT +# SPDX-License-Identifier: GPL-2.0-only # # Create cdb file from flat alias file. DPC: 15/10/98. # Args: source (may be relative or absolute) diff --git a/src/util/ocsp_fetch.pl b/src/util/ocsp_fetch.pl index 08ca4cbe4..2afbdbdac 100755 --- a/src/util/ocsp_fetch.pl +++ b/src/util/ocsp_fetch.pl @@ -1,6 +1,8 @@ #!/usr/bin/perl # Copyright (C) 2012 Wizards Internet Ltd # License GPLv2: GNU GPL version 2 +# SPDX-License-Identifier: GPL-2.0-only + use strict; BEGIN { pop @INC if $INC[-1] eq '.' }; use Getopt::Std; diff --git a/src/util/proxy_protocol_client.pl b/src/util/proxy_protocol_client.pl index 67a171d5d..8b4311b64 100644 --- a/src/util/proxy_protocol_client.pl +++ b/src/util/proxy_protocol_client.pl @@ -3,6 +3,7 @@ # Copyright (C) 2014 Todd Lyons # License GPLv2: GNU GPL version 2 # +# SPDX-License-Identifier: GPL-2.0-only # # This script emulates a proxy which uses Proxy Protocol to communicate # to a backend server. It should be run from an IP which is configured diff --git a/src/util/ratelimit.pl b/src/util/ratelimit.pl index e212fa241..2ceeebbfa 100644 --- a/src/util/ratelimit.pl +++ b/src/util/ratelimit.pl @@ -1,4 +1,5 @@ #!/usr/bin/perl -wT +# SPDX-License-Identifier: GPL-2.0-only use strict; diff --git a/src/util/renew-opendmarc-tlds.sh b/src/util/renew-opendmarc-tlds.sh index 9967018e2..583dc0e40 100755 --- a/src/util/renew-opendmarc-tlds.sh +++ b/src/util/renew-opendmarc-tlds.sh @@ -1,4 +1,5 @@ #!/bin/sh -eu +# SPDX-License-Identifier: GPL-2.0-only # # Short version of this script: # curl -f -o /var/cache/exim/opendmarc.tlds https://publicsuffix.org/list/public_suffix_list.dat diff --git a/src/util/unknownuser.sh b/src/util/unknownuser.sh index fe04dc6b1..01e617978 100755 --- a/src/util/unknownuser.sh +++ b/src/util/unknownuser.sh @@ -1,4 +1,5 @@ #! /bin/sh +# SPDX-License-Identifier: GPL-2.0-only # This is a sample script for demonstrating how to handle unknown users in # a more friendly way than just returning a "user unknown" error. It can commit 1d28cc061677bd07d9bed48dd84bd5c590247043 Author: Jeremy Harris Date: Sun Nov 27 14:40:20 2022 +0000 SPDX: Mass-update to GPL-2.0-or-later This is based on the text in src/NOTICE which is taken to override text in individual files diff --git a/src/Makefile b/src/Makefile index b774b43dd..d190d9aa0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,6 +4,7 @@ # Copyright (c) The Exim Maintainers 2022 # Copyright (c) University of Cambridge, 1995 - 2018 +# SPDX-License-Identifier: GPL-2.0-or-later # See the file NOTICE for conditions of use and distribution. # IRIX make uses the shell that is in the SHELL variable, which often defaults diff --git a/src/NOTICE b/src/NOTICE index b4a5c407f..4aa562ea3 100644 --- a/src/NOTICE +++ b/src/NOTICE @@ -2,6 +2,7 @@ THE EXIM MAIL TRANSFER AGENT ---------------------------- Copyright (c) 2004 University of Cambridge +SPDX-License-Identifier: GPL-2.0-or-later This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index 0c64d45d4..4e1f61a26 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -1,5 +1,4 @@ # This file is the basis of the main makefile for Exim and friends. The -# # makefile at the top level arranges to build the main makefile by calling # scripts/Configure-Makefile from within the build directory. This # concatenates the configuration settings from Local/Makefile and other, @@ -7,6 +6,7 @@ # build directory. # # Copyright (c) The Exim Maintainers 1995 - 2022 +# SPDX-License-Identifier: GPL-2.0-or-later SHELL = $(MAKE_SHELL) SCRIPTS = ../scripts diff --git a/src/OS/Makefile-Default b/src/OS/Makefile-Default index 6e42db471..7481d3163 100644 --- a/src/OS/Makefile-Default +++ b/src/OS/Makefile-Default @@ -2,6 +2,9 @@ # The Exim mail transport agent # ################################################## +# Copyright (c) The Exim Maintainers 2022 +# SPDX-License-Identifier: GPL-2.0-or-later + # Generic default make file containing settings that relate to the OS or # to selectable features within the OS. The configuration options for Exim # itself live in Local/Makefile, which is constructed by editing src/EDITME. diff --git a/src/OS/Makefile-FreeBSD b/src/OS/Makefile-FreeBSD index 4793a438b..f0fb8f2c9 100644 --- a/src/OS/Makefile-FreeBSD +++ b/src/OS/Makefile-FreeBSD @@ -1,5 +1,7 @@ # Exim: OS-specific make file for FreeBSD +# # Copyright (c) The Exim Maintainers 2020 +# SPDX-License-Identifier: GPL-2.0-or-later CHOWN_COMMAND=/usr/sbin/chown #STRIP_COMMAND=/usr/bin/strip diff --git a/src/OS/Makefile-GNU b/src/OS/Makefile-GNU index b49976f8c..366ce4263 100644 --- a/src/OS/Makefile-GNU +++ b/src/OS/Makefile-GNU @@ -1,5 +1,7 @@ # Exim: OS-specific make file for GNU and variants. +# # Copyright (c) The Exim Maintainers 2020 +# SPDX-License-Identifier: GPL-2.0-or-later HAVE_ICONV=yes diff --git a/src/OS/Makefile-OpenBSD b/src/OS/Makefile-OpenBSD index 7c451e2cc..da648df43 100644 --- a/src/OS/Makefile-OpenBSD +++ b/src/OS/Makefile-OpenBSD @@ -1,5 +1,6 @@ # Exim: OS-specific make file for OpenBSD # Copyright (c) The Exim Maintainers 2022 +# SPDX-License-Identifier: GPL-2.0-or-later CHOWN_COMMAND=/usr/sbin/chown CHGRP_COMMAND=/usr/sbin/chgrp diff --git a/src/OS/Makefile-SunOS5 b/src/OS/Makefile-SunOS5 index d07adcff4..e8b0d9506 100644 --- a/src/OS/Makefile-SunOS5 +++ b/src/OS/Makefile-SunOS5 @@ -1,5 +1,6 @@ # Exim: OS-specific make file for SunOS5 # Copyright (c) The Exim Maintainers 2020 +# SPDX-License-Identifier: GPL-2.0-or-later HAVE_ICONV=yes diff --git a/src/OS/eximon.conf-Default b/src/OS/eximon.conf-Default index 2f874ef53..d003156e6 100644 --- a/src/OS/eximon.conf-Default +++ b/src/OS/eximon.conf-Default @@ -2,6 +2,9 @@ # These can be overridden by OS-specific scripts and local installation # scripts, and also at run time by shell variables. +# Copyright (c) The Exim Maintainers 2022 +# SPDX-License-Identifier: GPL-2.0-or-later + # The name of the eximon binary, usually the same as the eximon script, # with .bin stuck on the end. diff --git a/src/OS/os.c-FreeBSD b/src/OS/os.c-FreeBSD index 02b78587b..d65a789c0 100644 --- a/src/OS/os.c-FreeBSD +++ b/src/OS/os.c-FreeBSD @@ -4,6 +4,7 @@ /* Copyright (c) Jeremy Harris 1995 - 2020 */ /* Copyright (c) The Exim Maintainers 2021 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* See the file NOTICE for conditions of use and distribution. */ /* FreeBSD-specific code. This is concatenated onto the generic diff --git a/src/OS/os.c-GNU b/src/OS/os.c-GNU index dbd014918..2b2aeed22 100644 --- a/src/OS/os.c-GNU +++ b/src/OS/os.c-GNU @@ -3,6 +3,7 @@ *************************************************/ /* Copyright (c) The Exim Maintainers 2020 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* See the file NOTICE for conditions of use and distribution. */ /* GNU-specific code. This is concatenated onto the generic src/os.c file. diff --git a/src/OS/os.c-Linux b/src/OS/os.c-Linux index 59d81f8ad..34dd7c190 100644 --- a/src/OS/os.c-Linux +++ b/src/OS/os.c-Linux @@ -3,6 +3,7 @@ *************************************************/ /* Copyright (c) University of Cambridge 1997 - 2018 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* See the file NOTICE for conditions of use and distribution. */ /* Linux-specific code. This is concatenated onto the generic diff --git a/src/OS/os.c-SunOS5 b/src/OS/os.c-SunOS5 index 162486958..ec4bb8d42 100644 --- a/src/OS/os.c-SunOS5 +++ b/src/OS/os.c-SunOS5 @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 2016 */ /* Copyright (c) Jeremy Harris 2016 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* See the file NOTICE for conditions of use and distribution. */ /* Solaris-specific code. This is concatenated onto the generic diff --git a/src/OS/os.h-FreeBSD b/src/OS/os.h-FreeBSD index 06e217a44..6756d42f6 100644 --- a/src/OS/os.h-FreeBSD +++ b/src/OS/os.h-FreeBSD @@ -1,6 +1,7 @@ /* Exim: OS-specific C header file for FreeBSD */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* See the file NOTICE for conditions of use and distribution. */ diff --git a/src/OS/os.h-GNU b/src/OS/os.h-GNU index 4b222600e..4fe55fa5e 100644 --- a/src/OS/os.h-GNU +++ b/src/OS/os.h-GNU @@ -1,5 +1,6 @@ /* Exim: OS-specific C header file for GNU/Hurd */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include diff --git a/src/OS/os.h-Linux b/src/OS/os.h-Linux index e6a8d288e..25a12862b 100644 --- a/src/OS/os.h-Linux +++ b/src/OS/os.h-Linux @@ -1,6 +1,7 @@ /* Exim: OS-specific C header file for Linux */ /* Copyright (c) University of Cambridge 1995 - 2020 */ /* Copyright (c) The Exim Maintainers 2021 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* See the file NOTICE for conditions of use and distribution. */ diff --git a/src/OS/os.h-OpenBSD b/src/OS/os.h-OpenBSD index c690b6e4a..19db7189a 100644 --- a/src/OS/os.h-OpenBSD +++ b/src/OS/os.h-OpenBSD @@ -1,6 +1,7 @@ /* Exim: OS-specific C header file for OpenBSD */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2021 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* See the file NOTICE for conditions of use and distribution. */ diff --git a/src/OS/os.h-SunOS5 b/src/OS/os.h-SunOS5 index 71253213c..b3f55bf83 100644 --- a/src/OS/os.h-SunOS5 +++ b/src/OS/os.h-SunOS5 @@ -1,5 +1,6 @@ /* Exim: OS-specific C header file for SunOS5 aka Solaris */ /* Copyright (c) The Exim Maintainers 2021 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define CRYPT_H #define HAVE_MMAP diff --git a/src/OS/unsupported/Makefile-AIX b/src/OS/unsupported/Makefile-AIX index fc32aa286..f407cbcaf 100644 --- a/src/OS/unsupported/Makefile-AIX +++ b/src/OS/unsupported/Makefile-AIX @@ -1,6 +1,7 @@ # Exim: OS-specific make file for AIX # Written by Nick Waterman (nick@cimio.co.uk) # Modified by PH following a message from Mike Meredith +# SPDX-License-Identifier: GPL-2.0-or-later # Note that the output of uname -m is probably not what Philip expected, # so you might end up with more build-AIX-random_number directories than diff --git a/src/OS/unsupported/Makefile-BSDI b/src/OS/unsupported/Makefile-BSDI index d56aa9b57..6ea176319 100644 --- a/src/OS/unsupported/Makefile-BSDI +++ b/src/OS/unsupported/Makefile-BSDI @@ -1,5 +1,6 @@ # Exim: OS-specific make file for BSDI aka BSD/OS. Its antique link editor # cannot handle the TextPop overriding. +# SPDX-License-Identifier: GPL-2.0-or-later CFLAGS=-O CHOWN_COMMAND=/usr/sbin/chown diff --git a/src/OS/unsupported/Makefile-CYGWIN b/src/OS/unsupported/Makefile-CYGWIN index 5e608fe9e..bc4545b47 100644 --- a/src/OS/unsupported/Makefile-CYGWIN +++ b/src/OS/unsupported/Makefile-CYGWIN @@ -1,4 +1,5 @@ # OS-specific file for Cygwin. +# SPDX-License-Identifier: GPL-2.0-or-later # This file provided by Pierre A. Humblet diff --git a/src/OS/unsupported/Makefile-DGUX b/src/OS/unsupported/Makefile-DGUX index 667c63f49..1258ba4e4 100644 --- a/src/OS/unsupported/Makefile-DGUX +++ b/src/OS/unsupported/Makefile-DGUX @@ -1,4 +1,5 @@ # Exim: OS-specific make file for DGUX +# SPDX-License-Identifier: GPL-2.0-or-later # # Written by Ken Bailey (K.Bailey@rbgkew.org.uk) Feb 1998 # on dgux R4.11MU04 generic AViiON mc88100 diff --git a/src/OS/unsupported/Makefile-Darwin b/src/OS/unsupported/Makefile-Darwin index 517bbc493..bdd9d110d 100644 --- a/src/OS/unsupported/Makefile-Darwin +++ b/src/OS/unsupported/Makefile-Darwin @@ -1,4 +1,5 @@ # Exim: OS-specific make file for Darwin (Mac OS X). +# SPDX-License-Identifier: GPL-2.0-or-later CC=cc diff --git a/src/OS/unsupported/Makefile-DragonFly b/src/OS/unsupported/Makefile-DragonFly index c49c59ffc..9d845f13f 100644 --- a/src/OS/unsupported/Makefile-DragonFly +++ b/src/OS/unsupported/Makefile-DragonFly @@ -1,4 +1,6 @@ # Exim: OS-specific make file for DragonFly +# SPDX-License-Identifier: GPL-2.0-or-later +# # There's no setting of CFLAGS here, to allow the system default # for "make" to be the default. diff --git a/src/OS/unsupported/Makefile-GNUkFreeBSD b/src/OS/unsupported/Makefile-GNUkFreeBSD index 801928143..b5ed325d1 100644 --- a/src/OS/unsupported/Makefile-GNUkFreeBSD +++ b/src/OS/unsupported/Makefile-GNUkFreeBSD @@ -1,4 +1,5 @@ # Exim: OS-specific make file for GNU and variants. +# SPDX-License-Identifier: GPL-2.0-or-later HAVE_ICONV=yes diff --git a/src/OS/unsupported/Makefile-GNUkNetBSD b/src/OS/unsupported/Makefile-GNUkNetBSD index 801928143..b5ed325d1 100644 --- a/src/OS/unsupported/Makefile-GNUkNetBSD +++ b/src/OS/unsupported/Makefile-GNUkNetBSD @@ -1,4 +1,5 @@ # Exim: OS-specific make file for GNU and variants. +# SPDX-License-Identifier: GPL-2.0-or-later HAVE_ICONV=yes diff --git a/src/OS/unsupported/Makefile-HI-OSF b/src/OS/unsupported/Makefile-HI-OSF index da3d487a4..66aac36b9 100644 --- a/src/OS/unsupported/Makefile-HI-OSF +++ b/src/OS/unsupported/Makefile-HI-OSF @@ -1,4 +1,5 @@ # Exim: OS-specific make file for HI-OSF/1-MJ and HI-UX/MPP +# SPDX-License-Identifier: GPL-2.0-or-later CC=cc CFLAGS=-O diff --git a/src/OS/unsupported/Makefile-HI-UX b/src/OS/unsupported/Makefile-HI-UX index 870ee8460..c1f4fe51a 100644 --- a/src/OS/unsupported/Makefile-HI-UX +++ b/src/OS/unsupported/Makefile-HI-UX @@ -1,4 +1,5 @@ # Exim: OS-specific make file for HI-UX +# SPDX-License-Identifier: GPL-2.0-or-later CC=cc -Aa -D_HIUX_SOURCE HAVE_SETRESUID=YES diff --git a/src/OS/unsupported/Makefile-HP-UX b/src/OS/unsupported/Makefile-HP-UX index ea35144bb..7e6cd9dba 100644 --- a/src/OS/unsupported/Makefile-HP-UX +++ b/src/OS/unsupported/Makefile-HP-UX @@ -1,4 +1,5 @@ # Exim: OS-specific make file for HP-UX later than 9 +# SPDX-License-Identifier: GPL-2.0-or-later # HP ANSI C compiler #CC=cc diff --git a/src/OS/unsupported/Makefile-HP-UX-9 b/src/OS/unsupported/Makefile-HP-UX-9 index 153000944..ad28d4fdb 100644 --- a/src/OS/unsupported/Makefile-HP-UX-9 +++ b/src/OS/unsupported/Makefile-HP-UX-9 @@ -1,4 +1,5 @@ # Exim: OS-specific make file for HP-UX 9 +# SPDX-License-Identifier: GPL-2.0-or-later CFLAGS=-O BASENAME_COMMAND=/bin/basename diff --git a/src/OS/unsupported/Makefile-IRIX b/src/OS/unsupported/Makefile-IRIX index 7b9578339..a9925956b 100644 --- a/src/OS/unsupported/Makefile-IRIX +++ b/src/OS/unsupported/Makefile-IRIX @@ -1,4 +1,5 @@ # Exim: OS-specific make file for IRIX +# SPDX-License-Identifier: GPL-2.0-or-later HAVE_ICONV=yes BASENAME_COMMAND=/sbin/basename diff --git a/src/OS/unsupported/Makefile-IRIX6 b/src/OS/unsupported/Makefile-IRIX6 index be0113846..6140649d1 100644 --- a/src/OS/unsupported/Makefile-IRIX6 +++ b/src/OS/unsupported/Makefile-IRIX6 @@ -1,4 +1,5 @@ # Exim: OS-specific make file for IRIX6 on 64-bit systems +# SPDX-License-Identifier: GPL-2.0-or-later HAVE_ICONV=yes HOSTNAME_COMMAND=/usr/bsd/hostname diff --git a/src/OS/unsupported/Makefile-IRIX632 b/src/OS/unsupported/Makefile-IRIX632 index b567fc64e..2b19463ff 100644 --- a/src/OS/unsupported/Makefile-IRIX632 +++ b/src/OS/unsupported/Makefile-IRIX632 @@ -1,4 +1,6 @@ # Exim: OS-specific make file for IRIX 6 on 32-bit systems. +# SPDX-License-Identifier: GPL-2.0-or-later + # There seems to be some variation. The commented settings show # some alternatives. diff --git a/src/OS/unsupported/Makefile-IRIX65 b/src/OS/unsupported/Makefile-IRIX65 index 50e77450a..4ad77a66f 100644 --- a/src/OS/unsupported/Makefile-IRIX65 +++ b/src/OS/unsupported/Makefile-IRIX65 @@ -1,4 +1,5 @@ # Exim: OS-specific make file for IRIX 6.5 +# SPDX-License-Identifier: GPL-2.0-or-later HAVE_ICONV=yes HOSTNAME_COMMAND=/usr/bsd/hostname diff --git a/src/OS/unsupported/Makefile-NetBSD b/src/OS/unsupported/Makefile-NetBSD index 35d03a24a..6792da216 100644 --- a/src/OS/unsupported/Makefile-NetBSD +++ b/src/OS/unsupported/Makefile-NetBSD @@ -1,4 +1,5 @@ # Exim: OS-specific make file for NetBSD (ELF object format) +# SPDX-License-Identifier: GPL-2.0-or-later CHOWN_COMMAND=/usr/sbin/chown CHMOD_COMMAND=/bin/chmod diff --git a/src/OS/unsupported/Makefile-NetBSD-a.out b/src/OS/unsupported/Makefile-NetBSD-a.out index e210efdda..5bee773ae 100644 --- a/src/OS/unsupported/Makefile-NetBSD-a.out +++ b/src/OS/unsupported/Makefile-NetBSD-a.out @@ -1,4 +1,5 @@ # Exim: OS-specific make file for NetBSD (a.out/COFF object format) +# SPDX-License-Identifier: GPL-2.0-or-later CHOWN_COMMAND=/usr/sbin/chown CHMOD_COMMAND=/bin/chmod diff --git a/src/OS/unsupported/Makefile-OSF1 b/src/OS/unsupported/Makefile-OSF1 index 811ca07a9..73814cec6 100644 --- a/src/OS/unsupported/Makefile-OSF1 +++ b/src/OS/unsupported/Makefile-OSF1 @@ -1,4 +1,5 @@ # Exim: OS-specific make file for OSF1 +# SPDX-License-Identifier: GPL-2.0-or-later CFLAGS=-O LIBS=-liconv -lm diff --git a/src/OS/unsupported/Makefile-OpenUNIX b/src/OS/unsupported/Makefile-OpenUNIX index e4d726158..dbab2b557 100644 --- a/src/OS/unsupported/Makefile-OpenUNIX +++ b/src/OS/unsupported/Makefile-OpenUNIX @@ -1,4 +1,5 @@ # Exim: OS-specific make file for OpenUNIX +# SPDX-License-Identifier: GPL-2.0-or-later CC=/usr/bin/cc CFLAGS=-O -I/usr/local/include diff --git a/src/OS/unsupported/Makefile-QNX b/src/OS/unsupported/Makefile-QNX index 3cf81c41b..7d957b27c 100644 --- a/src/OS/unsupported/Makefile-QNX +++ b/src/OS/unsupported/Makefile-QNX @@ -1,4 +1,5 @@ # Exim: OS-specific makefile for QNX +# SPDX-License-Identifier: GPL-2.0-or-later BASENAME_COMMAND=/bin/basename MAKE_SHELL=/usr/bin/bash diff --git a/src/OS/unsupported/Makefile-SCO b/src/OS/unsupported/Makefile-SCO index baa61d80b..c936ec536 100644 --- a/src/OS/unsupported/Makefile-SCO +++ b/src/OS/unsupported/Makefile-SCO @@ -1,4 +1,5 @@ # Exim: OS-specific make file for SCO +# SPDX-License-Identifier: GPL-2.0-or-later # It was reported that some versions of gcc (e.g. 2.8.1) require this to be # CFLAGS=-melf diff --git a/src/OS/unsupported/Makefile-SCO_SV b/src/OS/unsupported/Makefile-SCO_SV index 249b81a0f..9baf1c470 100644 --- a/src/OS/unsupported/Makefile-SCO_SV +++ b/src/OS/unsupported/Makefile-SCO_SV @@ -1,6 +1,7 @@ # Exim: OS-specific make file for SCO_SV release 5 (tested on 5.0.5 & 5.0.5) # (see the UNIX_SV files for SCO 4.2) # Supplied by: Tony Earnshaw +# SPDX-License-Identifier: GPL-2.0-or-later # Note that 'gcc -melf -m486' applies to gcc 2.7.2 and higher; # 2.7.1 and SCO's SDK need '-belf'. diff --git a/src/OS/unsupported/Makefile-SunOS4 b/src/OS/unsupported/Makefile-SunOS4 index c876998e5..0dec44b65 100644 --- a/src/OS/unsupported/Makefile-SunOS4 +++ b/src/OS/unsupported/Makefile-SunOS4 @@ -1,4 +1,5 @@ # Exim: OS-specific make file for SunOS4 +# SPDX-License-Identifier: GPL-2.0-or-later CFLAGS=-O diff --git a/src/OS/unsupported/Makefile-SunOS5-hal b/src/OS/unsupported/Makefile-SunOS5-hal index 05ea893af..05759f369 100644 --- a/src/OS/unsupported/Makefile-SunOS5-hal +++ b/src/OS/unsupported/Makefile-SunOS5-hal @@ -1,4 +1,5 @@ # Exim: OS-specific make file for SunOS5 on a HAL +# SPDX-License-Identifier: GPL-2.0-or-later # Note: The HAL runs a standard SunOS5 except that it has a 64 bit C # compiler called hcc. To make things work pass the -KV7 flag to force diff --git a/src/OS/unsupported/Makefile-ULTRIX b/src/OS/unsupported/Makefile-ULTRIX index 9e912b3cb..00a95b950 100644 --- a/src/OS/unsupported/Makefile-ULTRIX +++ b/src/OS/unsupported/Makefile-ULTRIX @@ -1,4 +1,5 @@ # Exim: OS-specific make file for Ultrix +# SPDX-License-Identifier: GPL-2.0-or-later MAKE_SHELL=/usr/bin/sh5 diff --git a/src/OS/unsupported/Makefile-UNIX_SV b/src/OS/unsupported/Makefile-UNIX_SV index bfcfae1c0..5ea8567a5 100644 --- a/src/OS/unsupported/Makefile-UNIX_SV +++ b/src/OS/unsupported/Makefile-UNIX_SV @@ -1,4 +1,5 @@ # Exim: OS-specific make file for SCO SVR4.2MP (and maybe Unixware) +# SPDX-License-Identifier: GPL-2.0-or-later # # *** Note that for SCO 5 the configuration file is called SCO_SV, # *** and that Unixware7 has its own configuration. This is an old diff --git a/src/OS/unsupported/Makefile-USG b/src/OS/unsupported/Makefile-USG index 753a2d7e8..69a31bc15 100644 --- a/src/OS/unsupported/Makefile-USG +++ b/src/OS/unsupported/Makefile-USG @@ -1,4 +1,5 @@ # Exim: OS-specific make file for Unixware 2.x +# SPDX-License-Identifier: GPL-2.0-or-later # # Note that Unixware does not include db/dbm/ndbm with their standard compiler # (it is available with /usr/ucb/cc, but that has bugs of its own). You diff --git a/src/OS/unsupported/Makefile-Unixware7 b/src/OS/unsupported/Makefile-Unixware7 index 88a883801..0dc1afb1a 100644 --- a/src/OS/unsupported/Makefile-Unixware7 +++ b/src/OS/unsupported/Makefile-Unixware7 @@ -1,5 +1,6 @@ # Exim: OS-specific make file for Unixware7 # Based on information from James FitzGibbon +# SPDX-License-Identifier: GPL-2.0-or-later # If you want to use libbind, you need to # add -I/usr/local/bind/include to CFLAGS diff --git a/src/OS/unsupported/Makefile-mips b/src/OS/unsupported/Makefile-mips index ff3313937..d48adb411 100644 --- a/src/OS/unsupported/Makefile-mips +++ b/src/OS/unsupported/Makefile-mips @@ -1,4 +1,5 @@ # Exim: OS-specific make file for RiscOS4bsd +# SPDX-License-Identifier: GPL-2.0-or-later HOSTNAME_COMMAND=/usr/ucb/hostname EXIT_FAILURE=1 diff --git a/src/OS/unsupported/os.c-BSDI b/src/OS/unsupported/os.c-BSDI index 03a7a1cef..42160046b 100644 --- a/src/OS/unsupported/os.c-BSDI +++ b/src/OS/unsupported/os.c-BSDI @@ -3,6 +3,7 @@ *************************************************/ /* Copyright (c) 2016 Heiko Schlittermann */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* See the file NOTICE for conditions of use and distribution. */ /* BSDI-specific code. This is concatenated onto the generic diff --git a/src/OS/unsupported/os.c-HI-OSF b/src/OS/unsupported/os.c-HI-OSF index 5e3d336f2..56db57fd5 100644 --- a/src/OS/unsupported/os.c-HI-OSF +++ b/src/OS/unsupported/os.c-HI-OSF @@ -3,6 +3,7 @@ *************************************************/ /* Copyright (c) University of Cambridge 2001 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* See the file NOTICE for conditions of use and distribution. */ /* HI-OSF-specific code. This is concatenated onto the generic diff --git a/src/OS/unsupported/os.c-HP-UX b/src/OS/unsupported/os.c-HP-UX index fdd8708a2..1178b782f 100644 --- a/src/OS/unsupported/os.c-HP-UX +++ b/src/OS/unsupported/os.c-HP-UX @@ -4,6 +4,7 @@ /* Copyright (c) University of Cambridge 2016 */ /* Copyright (c) Jeremy Harris 2016 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* See the file NOTICE for conditions of use and distribution. */ /* HP-UX-specific code. This is concatenated onto the generic diff --git a/src/OS/unsupported/os.c-IRIX b/src/OS/unsupported/os.c-IRIX index 19172389b..1019901dc 100644 --- a/src/OS/unsupported/os.c-IRIX +++ b/src/OS/unsupported/os.c-IRIX @@ -4,6 +4,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 2001 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* See the file NOTICE for conditions of use and distribution. */ /* Irix-specific code. This is concatenated onto the generic src/os.c file. diff --git a/src/OS/unsupported/os.c-IRIX6 b/src/OS/unsupported/os.c-IRIX6 index 19172389b..1019901dc 100644 --- a/src/OS/unsupported/os.c-IRIX6 +++ b/src/OS/unsupported/os.c-IRIX6 @@ -4,6 +4,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 2001 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* See the file NOTICE for conditions of use and distribution. */ /* Irix-specific code. This is concatenated onto the generic src/os.c file. diff --git a/src/OS/unsupported/os.c-IRIX632 b/src/OS/unsupported/os.c-IRIX632 index 19172389b..1019901dc 100644 --- a/src/OS/unsupported/os.c-IRIX632 +++ b/src/OS/unsupported/os.c-IRIX632 @@ -4,6 +4,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 2001 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* See the file NOTICE for conditions of use and distribution. */ /* Irix-specific code. This is concatenated onto the generic src/os.c file. diff --git a/src/OS/unsupported/os.c-IRIX65 b/src/OS/unsupported/os.c-IRIX65 index 19172389b..1019901dc 100644 --- a/src/OS/unsupported/os.c-IRIX65 +++ b/src/OS/unsupported/os.c-IRIX65 @@ -4,6 +4,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 2001 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* See the file NOTICE for conditions of use and distribution. */ /* Irix-specific code. This is concatenated onto the generic src/os.c file. diff --git a/src/OS/unsupported/os.c-OSF1 b/src/OS/unsupported/os.c-OSF1 index ad91b63d1..4dc700f79 100644 --- a/src/OS/unsupported/os.c-OSF1 +++ b/src/OS/unsupported/os.c-OSF1 @@ -3,6 +3,7 @@ *************************************************/ /* Copyright (c) University of Cambridge 2001 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* See the file NOTICE for conditions of use and distribution. */ /* OSF1-specific code. This is concatenated onto the generic src/os.c file. diff --git a/src/OS/unsupported/os.c-cygwin b/src/OS/unsupported/os.c-cygwin index 5ca05a8db..56085b857 100644 --- a/src/OS/unsupported/os.c-cygwin +++ b/src/OS/unsupported/os.c-cygwin @@ -1,6 +1,7 @@ /************************************************* * Exim - an Internet mail transport agent * *************************************************/ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Cygwin-specific code. December 2002. Updated Jan 2015. This is prefixed to the src/os.c file. diff --git a/src/OS/unsupported/os.h-AIX b/src/OS/unsupported/os.h-AIX index 5cd4501a4..d70b907f5 100644 --- a/src/OS/unsupported/os.h-AIX +++ b/src/OS/unsupported/os.h-AIX @@ -1,4 +1,6 @@ /* Exim: OS-specific C header file for AIX */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + /* Written by Nick Waterman */ /* Modified by Philip Hazel with data from Niels Provos diff --git a/src/OS/unsupported/os.h-BSDI b/src/OS/unsupported/os.h-BSDI index a1705ec95..0a5dd93e6 100644 --- a/src/OS/unsupported/os.h-BSDI +++ b/src/OS/unsupported/os.h-BSDI @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for BSDI */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define HAVE_BSD_GETLOADAVG #define HAVE_SETCLASSRESOURCES diff --git a/src/OS/unsupported/os.h-DGUX b/src/OS/unsupported/os.h-DGUX index 9040f0e72..a4918b649 100644 --- a/src/OS/unsupported/os.h-DGUX +++ b/src/OS/unsupported/os.h-DGUX @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for DGUX */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Written by Ken Bailey (K.Bailey@rbgkew.org.uk) Feb 1998 */ /* on dgux R4.11MU04 generic AViiON mc88100 */ diff --git a/src/OS/unsupported/os.h-Darwin b/src/OS/unsupported/os.h-Darwin index a85e92f05..8cec8a90f 100644 --- a/src/OS/unsupported/os.h-Darwin +++ b/src/OS/unsupported/os.h-Darwin @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for Darwin (Mac OS X) */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* #define CRYPT_H */ /* Apparently this isn't needed */ diff --git a/src/OS/unsupported/os.h-DragonFly b/src/OS/unsupported/os.h-DragonFly index 4c2f1d508..122957f8d 100644 --- a/src/OS/unsupported/os.h-DragonFly +++ b/src/OS/unsupported/os.h-DragonFly @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for DragonFly */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define HAVE_BSD_GETLOADAVG #define HAVE_MMAP diff --git a/src/OS/unsupported/os.h-GNUkFreeBSD b/src/OS/unsupported/os.h-GNUkFreeBSD index ab3503131..9326951c2 100644 --- a/src/OS/unsupported/os.h-GNUkFreeBSD +++ b/src/OS/unsupported/os.h-GNUkFreeBSD @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for GNU/kFreeBSD */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define CRYPT_H #define GLIBC_IP_OPTIONS diff --git a/src/OS/unsupported/os.h-GNUkNetBSD b/src/OS/unsupported/os.h-GNUkNetBSD index bc3bc259d..f255b383d 100644 --- a/src/OS/unsupported/os.h-GNUkNetBSD +++ b/src/OS/unsupported/os.h-GNUkNetBSD @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for GNU/kNetBSD */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define CRYPT_H #define GLIBC_IP_OPTIONS diff --git a/src/OS/unsupported/os.h-HI-OSF b/src/OS/unsupported/os.h-HI-OSF index 0f50fb660..9dc59cbcd 100644 --- a/src/OS/unsupported/os.h-HI-OSF +++ b/src/OS/unsupported/os.h-HI-OSF @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for HI-OSF/1-MJ and HI-UX/MPP */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define HAVE_SYS_MOUNT_H diff --git a/src/OS/unsupported/os.h-HI-UX b/src/OS/unsupported/os.h-HI-UX index f3df9638c..8a83ce0b0 100644 --- a/src/OS/unsupported/os.h-HI-UX +++ b/src/OS/unsupported/os.h-HI-UX @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for HI-UX */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define LOAD_AVG_NEEDS_ROOT #define HAVE_DEV_KMEM diff --git a/src/OS/unsupported/os.h-HP-UX b/src/OS/unsupported/os.h-HP-UX index 4998734f6..e74c254f6 100644 --- a/src/OS/unsupported/os.h-HP-UX +++ b/src/OS/unsupported/os.h-HP-UX @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for HP-UX versions greater than 9 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define EXIM_SOCKLEN_T size_t diff --git a/src/OS/unsupported/os.h-HP-UX-9 b/src/OS/unsupported/os.h-HP-UX-9 index 5a260d607..cb967a001 100644 --- a/src/OS/unsupported/os.h-HP-UX-9 +++ b/src/OS/unsupported/os.h-HP-UX-9 @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for HP-UX version 9 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define LOAD_AVG_NEEDS_ROOT #define HAVE_DEV_KMEM diff --git a/src/OS/unsupported/os.h-IRIX b/src/OS/unsupported/os.h-IRIX index 1d4bf46ba..ceff6ea05 100644 --- a/src/OS/unsupported/os.h-IRIX +++ b/src/OS/unsupported/os.h-IRIX @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for IRIX */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define DN_EXPAND_ARG4_TYPE u_char * diff --git a/src/OS/unsupported/os.h-IRIX6 b/src/OS/unsupported/os.h-IRIX6 index bf3076713..59bf6c46e 100644 --- a/src/OS/unsupported/os.h-IRIX6 +++ b/src/OS/unsupported/os.h-IRIX6 @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for IRIX */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define CRYPT_H #define LOAD_AVG_NEEDS_ROOT diff --git a/src/OS/unsupported/os.h-IRIX632 b/src/OS/unsupported/os.h-IRIX632 index 90f1c582c..8c171147b 100644 --- a/src/OS/unsupported/os.h-IRIX632 +++ b/src/OS/unsupported/os.h-IRIX632 @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for IRIX */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define CRYPT_H #define DN_EXPAND_ARG4_TYPE u_char * diff --git a/src/OS/unsupported/os.h-IRIX65 b/src/OS/unsupported/os.h-IRIX65 index 4b248fe36..381acbd57 100644 --- a/src/OS/unsupported/os.h-IRIX65 +++ b/src/OS/unsupported/os.h-IRIX65 @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for IRIX 6.5 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define CRYPT_H #define LOAD_AVG_NEEDS_ROOT diff --git a/src/OS/unsupported/os.h-NetBSD b/src/OS/unsupported/os.h-NetBSD index 0b9fc03f0..a8e32107a 100644 --- a/src/OS/unsupported/os.h-NetBSD +++ b/src/OS/unsupported/os.h-NetBSD @@ -1,5 +1,6 @@ /* Exim: OS-specific C header file for NetBSD */ /* Copyright (c) The Exim Maintainers 2021 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define HAVE_BSD_GETLOADAVG #define HAVE_GETIFADDRS diff --git a/src/OS/unsupported/os.h-NetBSD-a.out b/src/OS/unsupported/os.h-NetBSD-a.out index 29a8feeff..59eb79241 100644 --- a/src/OS/unsupported/os.h-NetBSD-a.out +++ b/src/OS/unsupported/os.h-NetBSD-a.out @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for NetBSD (a.out binary format) */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../OS/os.h-NetBSD" /* Same as for ELF format */ diff --git a/src/OS/unsupported/os.h-OSF1 b/src/OS/unsupported/os.h-OSF1 index 6b5fa4973..4c985e8f2 100644 --- a/src/OS/unsupported/os.h-OSF1 +++ b/src/OS/unsupported/os.h-OSF1 @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for OSF1 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define HAVE_SYS_MOUNT_H #define HAVE_GETIPNODEBYNAME 1 diff --git a/src/OS/unsupported/os.h-OpenUNIX b/src/OS/unsupported/os.h-OpenUNIX index 67d1063b0..e8fae863a 100644 --- a/src/OS/unsupported/os.h-OpenUNIX +++ b/src/OS/unsupported/os.h-OpenUNIX @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for OpenUNIX */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define NO_SYSEXITS diff --git a/src/OS/unsupported/os.h-QNX b/src/OS/unsupported/os.h-QNX index 798f799ed..e5b04826c 100644 --- a/src/OS/unsupported/os.h-QNX +++ b/src/OS/unsupported/os.h-QNX @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for QNX */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Modified for QNX 6.2.0 with diffs from Samuli Tuomola. */ #include diff --git a/src/OS/unsupported/os.h-SCO b/src/OS/unsupported/os.h-SCO index e5e915ed0..99585ae46 100644 --- a/src/OS/unsupported/os.h-SCO +++ b/src/OS/unsupported/os.h-SCO @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for SCO */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define DN_EXPAND_ARG4_TYPE u_char * diff --git a/src/OS/unsupported/os.h-SCO_SV b/src/OS/unsupported/os.h-SCO_SV index 0ca29f74a..724753e29 100644 --- a/src/OS/unsupported/os.h-SCO_SV +++ b/src/OS/unsupported/os.h-SCO_SV @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for SCO_SV */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define LOAD_AVG_NEEDS_ROOT #define HAVE_DEV_KMEM diff --git a/src/OS/unsupported/os.h-SunOS4 b/src/OS/unsupported/os.h-SunOS4 index 65556204c..d12acc05c 100644 --- a/src/OS/unsupported/os.h-SunOS4 +++ b/src/OS/unsupported/os.h-SunOS4 @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for SunOS4 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define LOAD_AVG_NEEDS_ROOT #define HAVE_DEV_KMEM diff --git a/src/OS/unsupported/os.h-SunOS5-hal b/src/OS/unsupported/os.h-SunOS5-hal index cd9e877a9..1a1e43f95 100644 --- a/src/OS/unsupported/os.h-SunOS5-hal +++ b/src/OS/unsupported/os.h-SunOS5-hal @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for SunOS5 on HAL */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define HAVE_MMAP diff --git a/src/OS/unsupported/os.h-ULTRIX b/src/OS/unsupported/os.h-ULTRIX index 08db5aec8..83b0fc4aa 100644 --- a/src/OS/unsupported/os.h-ULTRIX +++ b/src/OS/unsupported/os.h-ULTRIX @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for Ultrix */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Well, it *does* have statfs(), but its structure is called something different, all the members have different names, and the function returns diff --git a/src/OS/unsupported/os.h-UNIX_SV b/src/OS/unsupported/os.h-UNIX_SV index 4943a07de..275475322 100644 --- a/src/OS/unsupported/os.h-UNIX_SV +++ b/src/OS/unsupported/os.h-UNIX_SV @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for SCO SVR4.2 (and maybe Unixware) */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /** *** Note that for SCO 5 the configuration file is called SCO_SV, diff --git a/src/OS/unsupported/os.h-USG b/src/OS/unsupported/os.h-USG index e76922067..597c52f9d 100644 --- a/src/OS/unsupported/os.h-USG +++ b/src/OS/unsupported/os.h-USG @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for Unixware 2.x */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define NO_SYSEXITS diff --git a/src/OS/unsupported/os.h-Unixware7 b/src/OS/unsupported/os.h-Unixware7 index 4d3ed42f2..e8f8c3210 100644 --- a/src/OS/unsupported/os.h-Unixware7 +++ b/src/OS/unsupported/os.h-Unixware7 @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for Unixware 7 */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define NO_SYSEXITS diff --git a/src/OS/unsupported/os.h-cygwin b/src/OS/unsupported/os.h-cygwin index 6ef59e0cc..7f81c074f 100644 --- a/src/OS/unsupported/os.h-cygwin +++ b/src/OS/unsupported/os.h-cygwin @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for Cygwin */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This code was supplied by Pierre A. Humblet December 2002. Updated Jan 2015. */ diff --git a/src/OS/unsupported/os.h-mips b/src/OS/unsupported/os.h-mips index 325e3a134..39e0019cb 100644 --- a/src/OS/unsupported/os.h-mips +++ b/src/OS/unsupported/os.h-mips @@ -1,4 +1,5 @@ /* Exim: OS-specific C header file for RiscOS4bsd */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define LOAD_AVG_NEEDS_ROOT #define HAVE_DEV_KMEM diff --git a/src/README b/src/README index d9379f7f8..e335cc22e 100644 --- a/src/README +++ b/src/README @@ -2,6 +2,7 @@ THE EXIM MAIL TRANSFER AGENT VERSION 4 -------------------------------------- Copyright (c) 1995 - 2018 University of Cambridge. +SPDX-License-Identifier: GPL-2.0-or-later See the file NOTICE for conditions of use and distribution. There is a book about Exim by Philip Hazel called "The Exim SMTP Mail Server", diff --git a/src/exim_monitor/EDITME b/src/exim_monitor/EDITME index a70b7cb61..1b6320c8f 100644 --- a/src/exim_monitor/EDITME +++ b/src/exim_monitor/EDITME @@ -2,6 +2,9 @@ # The Exim Monitor # ################################################## +# Copyright (c) The Exim Maintainers 2022 +# SPDX-License-Identifier: GPL-2.0-or-later + # This is the template for the Exim monitor's main build-time configuration # file. It contains settings that are independent of any operating system. It # should be edited and then saved to a file called Local/eximon.conf before diff --git a/src/exim_monitor/em_StripChart.c b/src/exim_monitor/em_StripChart.c index 3eb98e4fb..a974790d3 100644 --- a/src/exim_monitor/em_StripChart.c +++ b/src/exim_monitor/em_StripChart.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /*********************************************************** Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts, and the Massachusetts Institute of Technology, Cambridge, Massachusetts. diff --git a/src/exim_monitor/em_TextPop.c b/src/exim_monitor/em_TextPop.c index 0e87cb580..0360f852b 100644 --- a/src/exim_monitor/em_TextPop.c +++ b/src/exim_monitor/em_TextPop.c @@ -2,7 +2,7 @@ Copyright (c) The Exim Maintainers 2022 Copyright 1989 by the Massachusetts Institute of Technology, Cambridge, Massachusetts. -SPDX-License-Identifier: GPL-2.0-only +SPDX-License-Identifier: GPL-2.0-or-later All Rights Reserved diff --git a/src/exim_monitor/em_globals.c b/src/exim_monitor/em_globals.c index 2943065b4..cf9b1075e 100644 --- a/src/exim_monitor/em_globals.c +++ b/src/exim_monitor/em_globals.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "em_hdr.h" diff --git a/src/exim_monitor/em_hdr.h b/src/exim_monitor/em_hdr.h index 76970c997..61f390d2c 100644 --- a/src/exim_monitor/em_hdr.h +++ b/src/exim_monitor/em_hdr.h @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This is the general header file for all the modules that comprise diff --git a/src/exim_monitor/em_init.c b/src/exim_monitor/em_init.c index e1f53fbba..b9289715d 100644 --- a/src/exim_monitor/em_init.c +++ b/src/exim_monitor/em_init.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This module contains code to initialize things from the environment and the arguments. */ diff --git a/src/exim_monitor/em_log.c b/src/exim_monitor/em_log.c index d625056c5..55925d786 100644 --- a/src/exim_monitor/em_log.c +++ b/src/exim_monitor/em_log.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainters 2021 - 2022 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This module contains code for scanning the main log, extracting information from it, and displaying a "tail". */ diff --git a/src/exim_monitor/em_main.c b/src/exim_monitor/em_main.c index 919cde632..50b8cd8bd 100644 --- a/src/exim_monitor/em_main.c +++ b/src/exim_monitor/em_main.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "em_hdr.h" diff --git a/src/exim_monitor/em_menu.c b/src/exim_monitor/em_menu.c index afcd31540..e4db84915 100644 --- a/src/exim_monitor/em_menu.c +++ b/src/exim_monitor/em_menu.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "em_hdr.h" diff --git a/src/exim_monitor/em_queue.c b/src/exim_monitor/em_queue.c index d9cfad38a..5eb44648a 100644 --- a/src/exim_monitor/em_queue.c +++ b/src/exim_monitor/em_queue.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "em_hdr.h" diff --git a/src/exim_monitor/em_strip.c b/src/exim_monitor/em_strip.c index cfafe9257..21e5739db 100644 --- a/src/exim_monitor/em_strip.c +++ b/src/exim_monitor/em_strip.c @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "em_hdr.h" diff --git a/src/exim_monitor/em_text.c b/src/exim_monitor/em_text.c index 1077353e0..4d03570fd 100644 --- a/src/exim_monitor/em_text.c +++ b/src/exim_monitor/em_text.c @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "em_hdr.h" diff --git a/src/exim_monitor/em_version.c b/src/exim_monitor/em_version.c index c5d4d62d8..4c562925c 100644 --- a/src/exim_monitor/em_version.c +++ b/src/exim_monitor/em_version.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define EM_VERSION_C diff --git a/src/exim_monitor/em_xs.c b/src/exim_monitor/em_xs.c index dd19c7f43..ac36b8f58 100644 --- a/src/exim_monitor/em_xs.c +++ b/src/exim_monitor/em_xs.c @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge, 1995 - 2016 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This file contains a number of subroutines that are in effect just alternative packaging for calls to various X functions that diff --git a/src/scripts/Configure b/src/scripts/Configure index 6c340ee25..4c68b5940 100755 --- a/src/scripts/Configure +++ b/src/scripts/Configure @@ -2,6 +2,9 @@ # A script to be called to run all the other configuring scripts manually. +# Copyright (c) The Exim Maintainters 2022 +# SPDX-License-Identifier: GPL-2.0-or-later + scripts/Configure-Makefile scripts/Configure-os.h scripts/Configure-os.c diff --git a/src/scripts/Configure-Makefile b/src/scripts/Configure-Makefile index ed77b6a8f..dc5015f6f 100755 --- a/src/scripts/Configure-Makefile +++ b/src/scripts/Configure-Makefile @@ -1,13 +1,15 @@ #! /bin/sh -LC_ALL=C -export LC_ALL - # Shell script to build Makefile in a build directory. It must be called # from inside the directory. It does its own checking of when to rebuild; it # just got too horrendous to get it right in "make", because of the optionally # existing configuration files. # # Copyright (c) The Exim Maintainers 1995 - 2021 +# SPDX-License-Identifier: GPL-2.0-or-later + + +LC_ALL=C +export LC_ALL # First off, get the OS type, and check that there is a make file for it. diff --git a/src/scripts/Configure-config.h b/src/scripts/Configure-config.h index c750f64f1..6744755f7 100755 --- a/src/scripts/Configure-config.h +++ b/src/scripts/Configure-config.h @@ -1,5 +1,6 @@ #! /bin/sh -# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) The Exim Maintainters 2022 +# SPDX-License-Identifier: GPL-2.0-or-later # Build the config.h file, using the buildconfig program, first ensuring that # it exists. diff --git a/src/scripts/Configure-eximon b/src/scripts/Configure-eximon index 0aca3b338..00076154d 100755 --- a/src/scripts/Configure-eximon +++ b/src/scripts/Configure-eximon @@ -1,5 +1,8 @@ #! /bin/sh +# Copyright (c) The Exim Maintainters 2022 +# SPDX-License-Identifier: GPL-2.0-or-later + # Shell script to build the configurable part of the Exim monitor's start-up # script. This is built from various configuration files. The final part is # added in the Makefile, using various macros that are available at that stage. diff --git a/src/scripts/Configure-os.c b/src/scripts/Configure-os.c index ab40ad183..7e9f14421 100755 --- a/src/scripts/Configure-os.c +++ b/src/scripts/Configure-os.c @@ -1,5 +1,7 @@ #! /bin/sh -# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) The Exim Maintainters 2022 +# SPDX-License-Identifier: GPL-2.0-or-later + # Shell script to build os.c. There doesn't have to be an OS-specific os.c # file, but if there is, it gets copied at the start of os.c. The basic src diff --git a/src/scripts/Configure-os.h b/src/scripts/Configure-os.h index f95a61a5d..dca0b9a3f 100755 --- a/src/scripts/Configure-os.h +++ b/src/scripts/Configure-os.h @@ -1,5 +1,7 @@ #! /bin/sh -# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) The Exim Maintainters 2022 +# SPDX-License-Identifier: GPL-2.0-or-later + # Shell script to create a link to the appropriate OS-specific header file. diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks index 471b3a369..6e0b65f5d 100755 --- a/src/scripts/MakeLinks +++ b/src/scripts/MakeLinks @@ -4,6 +4,7 @@ # specific build directory. It should be run from within that directory. # # Copyright (c) The Exim Maintainers 1995 - 2022 +# SPDX-License-Identifier: GPL-2.0-or-later test ! -d ../src && \ echo "*** $0 should be run in a system-specific subdirectory." && \ diff --git a/src/scripts/arch-type b/src/scripts/arch-type index f5fe8ebd3..0218c0233 100755 --- a/src/scripts/arch-type +++ b/src/scripts/arch-type @@ -1,7 +1,9 @@ #! /bin/sh - # Shell script to determine the architecture type. +# Copyright (c) The Exim Maintainters 2022 +# SPDX-License-Identifier: GPL-2.0-or-later + # If EXIM_ARCHTYPE is set, use it. This allows a manual override. case "$EXIM_ARCHTYPE" in ?*) arch="$EXIM_ARCHTYPE";; esac diff --git a/src/scripts/exim_install b/src/scripts/exim_install index 1e8805024..827841ffc 100755 --- a/src/scripts/exim_install +++ b/src/scripts/exim_install @@ -1,5 +1,8 @@ #! /bin/sh +# Copyright (c) The Exim Maintainters 2022 +# SPDX-License-Identifier: GPL-2.0-or-later + # Script to install Exim binaries in BIN_DIRECTORY, which is defined in # the local Makefile. It expects to be run in a build directory. It needs # to be run as root in order to make exim setuid to root. If exim runs setuid diff --git a/src/scripts/lookups-Makefile b/src/scripts/lookups-Makefile index ed8b0cb5c..8dcac585b 100755 --- a/src/scripts/lookups-Makefile +++ b/src/scripts/lookups-Makefile @@ -1,6 +1,7 @@ #! /bin/sh # Copyright (c) The Exim Maintainers 1995 - 2021 +# SPDX-License-Identifier: GPL-2.0-or-later # We turn the configure-built build-$foo/lookups/Makefile.predynamic into Makefile diff --git a/src/scripts/newer b/src/scripts/newer index 24c09e8f0..3bd65fb41 100755 --- a/src/scripts/newer +++ b/src/scripts/newer @@ -5,6 +5,9 @@ # if the second does not exist, the answer is "yes"; # otherwise their ages are compared using "find". +# Copyright (c) The Exim Maintainters 2022 +# SPDX-License-Identifier: GPL-2.0-or-later + if [ $# -ne 2 ]; then echo "*** Two file names needed for 'newer' ***" exit 2; diff --git a/src/scripts/os-type b/src/scripts/os-type index a188c4a56..1fcd37bc3 100755 --- a/src/scripts/os-type +++ b/src/scripts/os-type @@ -1,5 +1,8 @@ #! /bin/sh +# Copyright (c) The Exim Maintainters 2022 +# SPDX-License-Identifier: GPL-2.0-or-later + # Shell script to determine the operating system type. Some of the heuristics # herein have accumulated over the years and may not strictly be needed now, # but they are left in under the principle of "If it ain't broke, don't fix diff --git a/src/scripts/reversion b/src/scripts/reversion index b932224f5..6fd0782a4 100755 --- a/src/scripts/reversion +++ b/src/scripts/reversion @@ -1,5 +1,6 @@ #!/bin/sh # Copyright (c) The Exim Maintainers 1995 - 2021 +# SPDX-License-Identifier: GPL-2.0-or-later set -e LC_ALL=C diff --git a/src/scripts/source_checks b/src/scripts/source_checks index 918a6f8eb..8620b27f5 100644 --- a/src/scripts/source_checks +++ b/src/scripts/source_checks @@ -1,4 +1,6 @@ #!/bin/sh +# Copyright (c) The Exim Maintainters 2022 +# SPDX-License-Identifier: GPL-2.0-or-later cd src; diff --git a/src/src/EDITME b/src/src/EDITME index 28d3d538e..625df18f5 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -1,6 +1,8 @@ ################################################## # The Exim mail transport agent # ################################################## +# Copyright (c) The Exim Maintainers 2022 +# SPDX-License-Identifier: GPL-2.0-or-later # This is the template for Exim's main build-time configuration file. It # contains settings that are independent of any operating system. These are diff --git a/src/src/acl.c b/src/src/acl.c index 92af9991f..143890668 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Code for handling Access Control Lists (ACLs) */ diff --git a/src/src/arc.c b/src/src/arc.c index e3a2b3dad..30a66320e 100644 --- a/src/src/arc.c +++ b/src/src/arc.c @@ -5,7 +5,7 @@ Copyright (c) Jeremy Harris 2018 - 2020 Copyright (c) The Exim Maintainers 2021 - 2022 License: GPL - SPDX-License-Identifier: GPL-2.0-only + SPDX-License-Identifier: GPL-2.0-or-later */ #include "exim.h" diff --git a/src/src/auths/auth-spa.c b/src/src/auths/auth-spa.c index 32276c3f3..bcf88c84d 100644 --- a/src/src/auths/auth-spa.c +++ b/src/src/auths/auth-spa.c @@ -10,7 +10,7 @@ * Samba project (by Andrew Tridgell, Jeremy Allison, and others). * * Copyright (c) The Exim Maintainers 2021 - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-License-Identifier: GPL-2.0-or-later * Tom Kistner provided additional code, adding spa_build_auth_challenge() to * support server authentication mode. diff --git a/src/src/auths/auth-spa.h b/src/src/auths/auth-spa.h index df250d7a6..db93891ab 100644 --- a/src/src/auths/auth-spa.h +++ b/src/src/auths/auth-spa.h @@ -9,7 +9,7 @@ * All the code used here was torn by Marc Prud'hommeaux out of the * Samba project (by Andrew Tridgell, Jeremy Allison, and others). */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* December 2004: The spa_base64_to_bits() function has no length checking in it. I have added a check. PH */ diff --git a/src/src/auths/call_pam.c b/src/src/auths/call_pam.c index 483b083be..3ff15711d 100644 --- a/src/src/auths/call_pam.c +++ b/src/src/auths/call_pam.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/auths/call_pwcheck.c b/src/src/auths/call_pwcheck.c index 436b1a8de..88f708f62 100644 --- a/src/src/auths/call_pwcheck.c +++ b/src/src/auths/call_pwcheck.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This module contains interface functions to the two Cyrus authentication daemons. The original one was "pwcheck", which gives its name to the source diff --git a/src/src/auths/call_radius.c b/src/src/auths/call_radius.c index f4d7a9569..65882c108 100644 --- a/src/src/auths/call_radius.c +++ b/src/src/auths/call_radius.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2016 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This file was originally supplied by Ian Kirk. The libradius support came from Alex Kiernan. */ diff --git a/src/src/auths/check_serv_cond.c b/src/src/auths/check_serv_cond.c index 033d2026b..5201d2177 100644 --- a/src/src/auths/check_serv_cond.c +++ b/src/src/auths/check_serv_cond.c @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/auths/cram_md5.c b/src/src/auths/cram_md5.c index 60128b83e..280b5293a 100644 --- a/src/src/auths/cram_md5.c +++ b/src/src/auths/cram_md5.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* The stand-alone version just tests the algorithm. We have to drag diff --git a/src/src/auths/cram_md5.h b/src/src/auths/cram_md5.h index 25470a61b..984bc14c7 100644 --- a/src/src/auths/cram_md5.h +++ b/src/src/auths/cram_md5.h @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Private structure for the private options. */ diff --git a/src/src/auths/cyrus_sasl.c b/src/src/auths/cyrus_sasl.c index 4fe257ba7..b5d2d1d3b 100644 --- a/src/src/auths/cyrus_sasl.c +++ b/src/src/auths/cyrus_sasl.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This code was originally contributed by Matthew Byng-Maddick */ diff --git a/src/src/auths/cyrus_sasl.h b/src/src/auths/cyrus_sasl.h index 144ac5c80..05071b6e5 100644 --- a/src/src/auths/cyrus_sasl.h +++ b/src/src/auths/cyrus_sasl.h @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Copyright (c) A L Digital Ltd 2004 */ diff --git a/src/src/auths/dovecot.h b/src/src/auths/dovecot.h index b5eaf4f16..74c451930 100644 --- a/src/src/auths/dovecot.h +++ b/src/src/auths/dovecot.h @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* Copyright (c) The Exim Maintainters 2020 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Private structure for the private options. */ diff --git a/src/src/auths/external.c b/src/src/auths/external.c index 736c33982..078aad0fa 100644 --- a/src/src/auths/external.c +++ b/src/src/auths/external.c @@ -4,7 +4,7 @@ /* Copyright (c) Jeremy Harris 2019-2020 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This file provides an Exim authenticator driver for a server to verify a client SSL certificate, using the EXTERNAL diff --git a/src/src/auths/external.h b/src/src/auths/external.h index 9abb46a6b..0a9b0b50e 100644 --- a/src/src/auths/external.h +++ b/src/src/auths/external.h @@ -4,7 +4,7 @@ /* Copyright (c) Jeremy Harris 2019 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Private structure for the private options. */ diff --git a/src/src/auths/get_data.c b/src/src/auths/get_data.c index 0c85d2651..caf4cfdb8 100644 --- a/src/src/auths/get_data.c +++ b/src/src/auths/get_data.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/auths/get_no64_data.c b/src/src/auths/get_no64_data.c index 76f421473..e2cadfbc6 100644 --- a/src/src/auths/get_no64_data.c +++ b/src/src/auths/get_no64_data.c @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/auths/gsasl_exim.c b/src/src/auths/gsasl_exim.c index aac9c84e6..2c39d0f21 100644 --- a/src/src/auths/gsasl_exim.c +++ b/src/src/auths/gsasl_exim.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2019 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Copyright (c) Twitter Inc 2012 Author: Phil Pennock */ diff --git a/src/src/auths/gsasl_exim.h b/src/src/auths/gsasl_exim.h index 691d7d706..a56535710 100644 --- a/src/src/auths/gsasl_exim.h +++ b/src/src/auths/gsasl_exim.h @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2019 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Copyright (c) Twitter Inc 2012 */ diff --git a/src/src/auths/heimdal_gssapi.c b/src/src/auths/heimdal_gssapi.c index 12267e3fc..1336d0fab 100644 --- a/src/src/auths/heimdal_gssapi.c +++ b/src/src/auths/heimdal_gssapi.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Copyright (c) Twitter Inc 2012 Author: Phil Pennock */ diff --git a/src/src/auths/heimdal_gssapi.h b/src/src/auths/heimdal_gssapi.h index 031b580ef..6c9b24298 100644 --- a/src/src/auths/heimdal_gssapi.h +++ b/src/src/auths/heimdal_gssapi.h @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 1995 - 2012 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Copyright (c) Twitter Inc 2012 Author: Phil Pennock */ diff --git a/src/src/auths/plaintext.c b/src/src/auths/plaintext.c index 61be5867e..6692a676e 100644 --- a/src/src/auths/plaintext.c +++ b/src/src/auths/plaintext.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" #include "plaintext.h" diff --git a/src/src/auths/plaintext.h b/src/src/auths/plaintext.h index 49862ff9b..fdf0feb93 100644 --- a/src/src/auths/plaintext.h +++ b/src/src/auths/plaintext.h @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Private structure for the private options. */ diff --git a/src/src/auths/pwcheck.c b/src/src/auths/pwcheck.c index aff5ed39c..bf305832f 100644 --- a/src/src/auths/pwcheck.c +++ b/src/src/auths/pwcheck.c @@ -4,7 +4,7 @@ * $Id: checkpw.c,v 1.49 2002/03/07 19:14:04 ken3 Exp $ */ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (c) 2001 Carnegie Mellon University. All rights reserved. * diff --git a/src/src/auths/pwcheck.h b/src/src/auths/pwcheck.h index 4247b4e8b..4c1d71d92 100644 --- a/src/src/auths/pwcheck.h +++ b/src/src/auths/pwcheck.h @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This file provides support for authentication via the Cyrus SASL pwcheck daemon (whence its name) and the newer saslauthd daemon. */ diff --git a/src/src/auths/spa.c b/src/src/auths/spa.c index c5e6d83d5..222ccea86 100644 --- a/src/src/auths/spa.c +++ b/src/src/auths/spa.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This file, which provides support for Microsoft's Secure Password Authentication, was contributed by Marc Prud'hommeaux. Tom Kistner added SPA diff --git a/src/src/auths/spa.h b/src/src/auths/spa.h index 4321971d9..625a252d6 100644 --- a/src/src/auths/spa.h +++ b/src/src/auths/spa.h @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This file, which provides support for Microsoft's Secure Password Authentication, was contributed by Marc Prud'hommeaux. */ diff --git a/src/src/auths/tls.c b/src/src/auths/tls.c index d3ca8f796..72ad56c4e 100644 --- a/src/src/auths/tls.c +++ b/src/src/auths/tls.c @@ -4,7 +4,7 @@ /* Copyright (c) Jeremy Harris 1995 - 2020 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This file provides an Exim authenticator driver for a server to verify a client SSL certificate diff --git a/src/src/auths/tls.h b/src/src/auths/tls.h index 24e116464..472a3e260 100644 --- a/src/src/auths/tls.h +++ b/src/src/auths/tls.h @@ -4,7 +4,7 @@ /* Copyright (c) Jeremy Harris 2015 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Private structure for the private options. */ diff --git a/src/src/auths/xtextdecode.c b/src/src/auths/xtextdecode.c index 39c1f2b60..b6a927194 100644 --- a/src/src/auths/xtextdecode.c +++ b/src/src/auths/xtextdecode.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/auths/xtextencode.c b/src/src/auths/xtextencode.c index 89f12f1f1..c08288831 100644 --- a/src/src/auths/xtextencode.c +++ b/src/src/auths/xtextencode.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/base64.c b/src/src/base64.c index 14e8cfffe..e9ac41a55 100644 --- a/src/src/base64.c +++ b/src/src/base64.c @@ -4,7 +4,7 @@ /* Copyright (c) Tom Kistner 2004, 2015 */ /* License: GPL */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ diff --git a/src/src/blob.h b/src/src/blob.h index 95a987f0a..72b7fe564 100644 --- a/src/src/blob.h +++ b/src/src/blob.h @@ -2,7 +2,7 @@ * Blob - a general pointer/size item for a memory chunk * * Copyright (C) 2016 Exim maintainers - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef BLOB_H /* entire file */ diff --git a/src/src/bmi_spam.c b/src/src/bmi_spam.c index 140bec48f..03e8defa6 100644 --- a/src/src/bmi_spam.c +++ b/src/src/bmi_spam.c @@ -6,7 +6,7 @@ Copyright (c) Tom Kistner 2004 License: GPL */ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "exim.h" #ifdef EXPERIMENTAL_BRIGHTMAIL diff --git a/src/src/bmi_spam.h b/src/src/bmi_spam.h index a9833b200..bb1c859a9 100644 --- a/src/src/bmi_spam.h +++ b/src/src/bmi_spam.h @@ -5,7 +5,7 @@ /* Code for calling Brightmail AntiSpam. Copyright (c) Tom Kistner 2004 License: GPL */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #ifdef EXPERIMENTAL_BRIGHTMAIL diff --git a/src/src/buildconfig.c b/src/src/buildconfig.c index 710b53550..8f37e508a 100644 --- a/src/src/buildconfig.c +++ b/src/src/buildconfig.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /************************************************* diff --git a/src/src/child.c b/src/src/child.c index b94e814a1..359b791e8 100644 --- a/src/src/child.c +++ b/src/src/child.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "exim.h" diff --git a/src/src/daemon.c b/src/src/daemon.c index 0afc7ca86..be008c3d4 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions concerned with running Exim as a daemon */ diff --git a/src/src/dane.c b/src/src/dane.c index f2ad22481..a12e16238 100644 --- a/src/src/dane.c +++ b/src/src/dane.c @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2012, 2014 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This module provides DANE (RFC6659) support for Exim. See also the draft RFC for DANE-over-SMTP, "SMTP security via opportunistic DANE TLS" diff --git a/src/src/dbfn.c b/src/src/dbfn.c index f932520f9..3c51162a4 100644 --- a/src/src/dbfn.c +++ b/src/src/dbfn.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "exim.h" diff --git a/src/src/dbfunctions.h b/src/src/dbfunctions.h index 93e1d3405..1f0dec1f7 100644 --- a/src/src/dbfunctions.h +++ b/src/src/dbfunctions.h @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 1995 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef DBFUNCTIONS_H #define DBFUNCTIONS_H diff --git a/src/src/dcc.c b/src/src/dcc.c index d8e22b30c..8986dedde 100644 --- a/src/src/dcc.c +++ b/src/src/dcc.c @@ -8,7 +8,7 @@ * See the file NOTICE for conditions of use and distribution. * * Copyright (c) The Exim Maintainers 2015 - 2022 - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-License-Identifier: GPL-2.0-or-later */ /* Code for calling dccifd. Called from acl.c. */ diff --git a/src/src/debug.c b/src/src/debug.c index 38e8f8001..44ad763e1 100644 --- a/src/src/debug.c +++ b/src/src/debug.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2015 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "exim.h" diff --git a/src/src/deliver.c b/src/src/deliver.c index 719fa9d93..c4fce4602 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* The main code for delivering a message. */ diff --git a/src/src/directory.c b/src/src/directory.c index c3b341bbb..94303db0b 100644 --- a/src/src/directory.c +++ b/src/src/directory.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2010 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "exim.h" diff --git a/src/src/dkim.c b/src/src/dkim.c index 9b6e14a3f..0a8ab6fb3 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge, 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Code for DKIM support. Other DKIM relevant code is in receive.c, transport.c and transports/smtp.c */ diff --git a/src/src/dkim.h b/src/src/dkim.h index 61d83a9df..915c6c739 100644 --- a/src/src/dkim.h +++ b/src/src/dkim.h @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge, 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ void dkim_exim_init(void); gstring * dkim_exim_sign(int, off_t, uschar *, struct ob_dkim *, const uschar **); diff --git a/src/src/dkim_transport.c b/src/src/dkim_transport.c index 142f4552a..c127d5b73 100644 --- a/src/src/dkim_transport.c +++ b/src/src/dkim_transport.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Transport shim for dkim signing */ diff --git a/src/src/dmarc.c b/src/src/dmarc.c index c0313bf5c..118720750 100644 --- a/src/src/dmarc.c +++ b/src/src/dmarc.c @@ -5,7 +5,7 @@ Copyright (c) The Exim Maintainers 2019 - 2022 Copyright (c) Todd Lyons 2012 - 2014 License: GPL */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Portions Copyright (c) 2012, 2013, The Trusted Domain Project; All rights reserved, licensed for use per LICENSE.opendmarc. */ diff --git a/src/src/dmarc.h b/src/src/dmarc.h index f71f1fd5a..86d3b1e1c 100644 --- a/src/src/dmarc.h +++ b/src/src/dmarc.h @@ -6,7 +6,7 @@ Copyright (c) The Exim Maintainers 2021 - 2022 Copyright (c) Todd Lyons 2012 - 2014 License: GPL */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Portions Copyright (c) 2012, 2013, The Trusted Domain Project; All rights reserved, licensed for use per LICENSE.opendmarc. */ diff --git a/src/src/dns.c b/src/src/dns.c index 8106fb688..4e01d8661 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions for interfacing with the DNS. */ diff --git a/src/src/dnsbl.c b/src/src/dnsbl.c index af80f6be1..1172d6183 100644 --- a/src/src/dnsbl.c +++ b/src/src/dnsbl.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions concerned with dnsbls */ diff --git a/src/src/drtables.c b/src/src/drtables.c index a6cbede89..cf7c4e0b1 100644 --- a/src/src/drtables.c +++ b/src/src/drtables.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "exim.h" diff --git a/src/src/dummies.c b/src/src/dummies.c index a72767ccd..df8ef6d5b 100644 --- a/src/src/dummies.c +++ b/src/src/dummies.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This file is not part of the main Exim code. There are little bits of test code for some of Exim's modules, and when they are used, the module they are diff --git a/src/src/enq.c b/src/src/enq.c index 054aeaed8..43f53a585 100644 --- a/src/src/enq.c +++ b/src/src/enq.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions concerned with serialization. */ diff --git a/src/src/environment.c b/src/src/environment.c index 06f804e69..68adf3c0c 100644 --- a/src/src/environment.c +++ b/src/src/environment.c @@ -5,7 +5,7 @@ /* Copyright (c) Heiko Schlittermann 2016 * hs@schlittermann.de * See the file NOTICE for conditions of use and distribution. - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-License-Identifier: GPL-2.0-or-later */ #include "exim.h" diff --git a/src/src/exim.c b/src/src/exim.c index 62413e367..35f4ae4f7 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* The main function: entry point, initialization, and high-level control. diff --git a/src/src/exim.h b/src/src/exim.h index 24fedcced..ccf14f0fd 100644 --- a/src/src/exim.h +++ b/src/src/exim.h @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Source files for exim all #include this header, which drags in everything diff --git a/src/src/exim_dbmbuild.c b/src/src/exim_dbmbuild.c index bb04adc02..0ea71795f 100644 --- a/src/src/exim_dbmbuild.c +++ b/src/src/exim_dbmbuild.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* A small freestanding program to build dbm databases from serial input. For diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index 669f7098d..f16570d86 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This single source file is used to compile three utility programs for diff --git a/src/src/exim_lock.c b/src/src/exim_lock.c index 1a54a92ad..363c1bc71 100644 --- a/src/src/exim_lock.c +++ b/src/src/exim_lock.c @@ -11,7 +11,7 @@ Default is -fcntl -lockfile. Argument: the name of the lock file Copyright (c) The Exim Maintainers 2016 - 2021 -SPDX-License-Identifier: GPL-2.0-only +SPDX-License-Identifier: GPL-2.0-or-later */ #include "os.h" diff --git a/src/src/expand.c b/src/src/expand.c index 050f01297..657cf3cb9 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions for handling string expansion. */ diff --git a/src/src/filter.c b/src/src/filter.c index 82a9122c6..530d772b3 100644 --- a/src/src/filter.c +++ b/src/src/filter.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Code for mail filtering functions. */ diff --git a/src/src/filtertest.c b/src/src/filtertest.c index 8bab65e78..2426f7feb 100644 --- a/src/src/filtertest.c +++ b/src/src/filtertest.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Code for the filter test function. */ diff --git a/src/src/functions.h b/src/src/functions.h index be1fae00d..a2c8976e8 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Prototypes for functions that appear in various modules. Gathered together diff --git a/src/src/globals.c b/src/src/globals.c index 429952edc..e5b72592f 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* All the global variables are defined together in this one module, so that they are easy to find. */ diff --git a/src/src/globals.h b/src/src/globals.h index 48d93a1c3..5aae73fba 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Almost all the global variables are defined together in this one header, so that they are easy to find. However, those that are visible during the diff --git a/src/src/hash.c b/src/src/hash.c index 2b71463f1..10af1b43d 100644 --- a/src/src/hash.c +++ b/src/src/hash.c @@ -3,7 +3,7 @@ * * Copyright (c) The Exim Maintainers 2010 - 2022 * Copyright (c) University of Cambridge 1995 - 2009 - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-License-Identifier: GPL-2.0-or-later * * Hash interface functions */ diff --git a/src/src/hash.h b/src/src/hash.h index c94e53129..788c9f0ad 100644 --- a/src/src/hash.h +++ b/src/src/hash.h @@ -1,7 +1,7 @@ /* * Exim - an Internet mail transport agent * Copyright (c) The Exim Maintainers 1995 - 2022 - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-License-Identifier: GPL-2.0-or-later * * Hash interface functions */ diff --git a/src/src/header.c b/src/src/header.c index 2a8fbfe64..a4dd6e72e 100644 --- a/src/src/header.c +++ b/src/src/header.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2016 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "exim.h" diff --git a/src/src/hintsdb.h b/src/src/hintsdb.h index e79e6bd49..2d7199eda 100644 --- a/src/src/hintsdb.h +++ b/src/src/hintsdb.h @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This header file contains macro definitions so that a variety of DBM libraries can be used by Exim. Nigel Metheringham provided the original set for diff --git a/src/src/hintsdb_structs.h b/src/src/hintsdb_structs.h index 27cd9edf9..0e5853b2b 100644 --- a/src/src/hintsdb_structs.h +++ b/src/src/hintsdb_structs.h @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This header file contains the definitions of the structures used in the various hints databases are also kept in this file, which is used by the diff --git a/src/src/host.c b/src/src/host.c index 874e19a08..ecdc6d681 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions for finding hosts, either by gethostbyname(), gethostbyaddr(), or directly via the DNS. When IPv6 is supported, getipnodebyname() and diff --git a/src/src/host_address.c b/src/src/host_address.c index 28a8a685f..4c13f0bb7 100644 --- a/src/src/host_address.c +++ b/src/src/host_address.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "exim.h" diff --git a/src/src/imap_utf7.c b/src/src/imap_utf7.c index 75ecb1a4b..1c09db621 100644 --- a/src/src/imap_utf7.c +++ b/src/src/imap_utf7.c @@ -1,6 +1,6 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "exim.h" diff --git a/src/src/ip.c b/src/src/ip.c index 2ac2b267d..b50130be3 100644 --- a/src/src/ip.c +++ b/src/src/ip.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions for doing things with sockets. With the advent of IPv6 this has got messier, so that it's worth pulling out the code into separate functions diff --git a/src/src/local_scan.c b/src/src/local_scan.c index 85ecba8cb..da44cb7e6 100644 --- a/src/src/local_scan.c +++ b/src/src/local_scan.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /****************************************************************************** diff --git a/src/src/local_scan.h b/src/src/local_scan.h index 2eabc5929..69b3c6cdb 100644 --- a/src/src/local_scan.h +++ b/src/src/local_scan.h @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2020 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This file is the header that is the only Exim header to be included in the source for the local_scan.c() function. It contains definitions that are made diff --git a/src/src/log.c b/src/src/log.c index f1d435ce4..6c483216a 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions for writing log files. The code for maintaining datestamped log files was originally contributed by Tony Sheen. */ diff --git a/src/src/lookupapi.h b/src/src/lookupapi.h index cec8f976b..62c8c0524 100644 --- a/src/src/lookupapi.h +++ b/src/src/lookupapi.h @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* The "type" field in each item is a set of bit flags: diff --git a/src/src/lookups/dbmdb.c b/src/src/lookups/dbmdb.c index c99e948b5..aa930e654 100644 --- a/src/src/lookups/dbmdb.c +++ b/src/src/lookups/dbmdb.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" #include "lf_functions.h" diff --git a/src/src/lookups/dnsdb.c b/src/src/lookups/dnsdb.c index 4f43cf674..5482cd9d1 100644 --- a/src/src/lookups/dnsdb.c +++ b/src/src/lookups/dnsdb.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" #include "lf_functions.h" diff --git a/src/src/lookups/dsearch.c b/src/src/lookups/dsearch.c index 6cae0dafb..74439bfc8 100644 --- a/src/src/lookups/dsearch.c +++ b/src/src/lookups/dsearch.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* The idea for this code came from Matthew Byng-Maddick, but his original has been heavily reworked a lot for Exim 4 (and it now uses stat() (more precisely: diff --git a/src/src/lookups/ibase.c b/src/src/lookups/ibase.c index d42e490c4..7e4973e01 100644 --- a/src/src/lookups/ibase.c +++ b/src/src/lookups/ibase.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* The code in this module was contributed by Ard Biesheuvel. */ diff --git a/src/src/lookups/json.c b/src/src/lookups/json.c index b1e5fb742..43575cacf 100644 --- a/src/src/lookups/json.c +++ b/src/src/lookups/json.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) Jeremy Harris 2019 - 2020 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" #include "lf_functions.h" diff --git a/src/src/lookups/ldap.c b/src/src/lookups/ldap.c index 17c431e5c..feeea0e41 100644 --- a/src/src/lookups/ldap.c +++ b/src/src/lookups/ldap.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Many thanks to Stuart Lynne for contributing the original code for this driver. Further contributions from Michael Haardt, Brian Candler, Barry diff --git a/src/src/lookups/ldap.h b/src/src/lookups/ldap.h index 30228aebe..2ce62fc05 100644 --- a/src/src/lookups/ldap.h +++ b/src/src/lookups/ldap.h @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Header for eldapauth_find */ diff --git a/src/src/lookups/lf_check_file.c b/src/src/lookups/lf_check_file.c index 1649f9f83..5c74816ef 100644 --- a/src/src/lookups/lf_check_file.c +++ b/src/src/lookups/lf_check_file.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/lookups/lf_functions.h b/src/src/lookups/lf_functions.h index b83b9652a..b7acbb5a9 100644 --- a/src/src/lookups/lf_functions.h +++ b/src/src/lookups/lf_functions.h @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Header for the functions that are shared by the lookups */ diff --git a/src/src/lookups/lf_quote.c b/src/src/lookups/lf_quote.c index 816fe01e2..72e37bfb4 100644 --- a/src/src/lookups/lf_quote.c +++ b/src/src/lookups/lf_quote.c @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/lookups/lf_sqlperform.c b/src/src/lookups/lf_sqlperform.c index cf4b9cd0b..ecb0a3221 100644 --- a/src/src/lookups/lf_sqlperform.c +++ b/src/src/lookups/lf_sqlperform.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/lookups/lmdb.c b/src/src/lookups/lmdb.c index ccfdbe453..3a3eebcba 100644 --- a/src/src/lookups/lmdb.c +++ b/src/src/lookups/lmdb.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 2016 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/lookups/lsearch.c b/src/src/lookups/lsearch.c index da73ae8bf..f668f60f2 100644 --- a/src/src/lookups/lsearch.c +++ b/src/src/lookups/lsearch.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" #include "lf_functions.h" diff --git a/src/src/lookups/mysql.c b/src/src/lookups/mysql.c index a8dae4ade..7e0343233 100644 --- a/src/src/lookups/mysql.c +++ b/src/src/lookups/mysql.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Thanks to Paul Kelly for contributing the original code for these functions. */ diff --git a/src/src/lookups/nis.c b/src/src/lookups/nis.c index e7c124757..6a08ebc37 100644 --- a/src/src/lookups/nis.c +++ b/src/src/lookups/nis.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" #include "lf_functions.h" diff --git a/src/src/lookups/nisplus.c b/src/src/lookups/nisplus.c index 78c79563f..3f89c7f81 100644 --- a/src/src/lookups/nisplus.c +++ b/src/src/lookups/nisplus.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" #include "lf_functions.h" diff --git a/src/src/lookups/oracle.c b/src/src/lookups/oracle.c index 3f3868fe9..d0604c245 100644 --- a/src/src/lookups/oracle.c +++ b/src/src/lookups/oracle.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Interface to an Oracle database. This code was originally supplied by Paul Kelly, but I have hacked it around for various reasons, and tried to add diff --git a/src/src/lookups/passwd.c b/src/src/lookups/passwd.c index 7df1a5aaf..f185a9091 100644 --- a/src/src/lookups/passwd.c +++ b/src/src/lookups/passwd.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/lookups/pgsql.c b/src/src/lookups/pgsql.c index 4fcd0e3ed..1583378d5 100644 --- a/src/src/lookups/pgsql.c +++ b/src/src/lookups/pgsql.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Thanks to Petr Cech for contributing the original code for these functions. Thanks to Joachim Wieland for the initial patch for the Unix domain diff --git a/src/src/lookups/readsock.c b/src/src/lookups/readsock.c index 7c7b9cfa8..b1ea42c7f 100644 --- a/src/src/lookups/readsock.c +++ b/src/src/lookups/readsock.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) Jeremy Harris 2020 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" #include "lf_functions.h" diff --git a/src/src/lookups/redis.c b/src/src/lookups/redis.c index c7fcb66f0..7b680f086 100644 --- a/src/src/lookups/redis.c +++ b/src/src/lookups/redis.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/lookups/sqlite.c b/src/src/lookups/sqlite.c index b9a735e18..6c7af4225 100644 --- a/src/src/lookups/sqlite.c +++ b/src/src/lookups/sqlite.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" #include "lf_functions.h" diff --git a/src/src/lookups/testdb.c b/src/src/lookups/testdb.c index f94150b68..8de7090b7 100644 --- a/src/src/lookups/testdb.c +++ b/src/src/lookups/testdb.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" #include "lf_functions.h" diff --git a/src/src/lookups/whoson.c b/src/src/lookups/whoson.c index cd236787e..cd6c7e85c 100644 --- a/src/src/lookups/whoson.c +++ b/src/src/lookups/whoson.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This code originally came from Robert Wal. */ diff --git a/src/src/lss.c b/src/src/lss.c index 0d20c07b6..e6ec1d6d1 100644 --- a/src/src/lss.c +++ b/src/src/lss.c @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Support functions for calling from local_scan(). These are mostly just wrappers for various internal functions. */ diff --git a/src/src/macro_predef.c b/src/src/macro_predef.c index fe814500d..618c914dc 100644 --- a/src/src/macro_predef.c +++ b/src/src/macro_predef.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) Jeremy Harris 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Create a static data structure with the predefined macros, to be included in the main Exim build */ diff --git a/src/src/macro_predef.h b/src/src/macro_predef.h index d3bc5c074..8b7b375c6 100644 --- a/src/src/macro_predef.h +++ b/src/src/macro_predef.h @@ -5,7 +5,7 @@ /* Copyright (c) Jeremy Harris 2017 - 2018 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Global functions */ diff --git a/src/src/macros.h b/src/src/macros.h index db1c34b65..243c1e5a0 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* These two macros make it possible to obtain the result of macro-expanding diff --git a/src/src/malware.c b/src/src/malware.c index 96514e276..f36c46b0f 100644 --- a/src/src/malware.c +++ b/src/src/malware.c @@ -6,7 +6,7 @@ * Copyright (c) The Exim Maintainers 2015 - 2022 * Copyright (c) Tom Kistner 2003 - 2015 * License: GPL - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-License-Identifier: GPL-2.0-or-later */ /* Code for calling virus (malware) scanners. Called from acl.c. */ diff --git a/src/src/match.c b/src/src/match.c index a877aef3b..15209f84a 100644 --- a/src/src/match.c +++ b/src/src/match.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions for matching strings */ diff --git a/src/src/md5.c b/src/src/md5.c index e6df32c7a..1d54ab4f1 100644 --- a/src/src/md5.c +++ b/src/src/md5.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef STAND_ALONE #include "exim.h" diff --git a/src/src/mime.c b/src/src/mime.c index bebe9bf44..7c3a33d62 100644 --- a/src/src/mime.c +++ b/src/src/mime.c @@ -6,7 +6,7 @@ * Copyright (c) The Exim Maintainers 2015 - 2022 * Copyright (c) Tom Kistner 2004 - 2015 * License: GPL - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-License-Identifier: GPL-2.0-or-later */ #include "exim.h" diff --git a/src/src/mime.h b/src/src/mime.h index 48bbd81de..d436bcacb 100644 --- a/src/src/mime.h +++ b/src/src/mime.h @@ -5,7 +5,7 @@ /* Copyright (c) Tom Kistner 2004, 2015 * License: GPL * Copyright (c) The Exim Maintainers 2016 - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-License-Identifier: GPL-2.0-or-later */ #ifdef WITH_CONTENT_SCAN diff --git a/src/src/moan.c b/src/src/moan.c index 387359458..ebfd440f6 100644 --- a/src/src/moan.c +++ b/src/src/moan.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions for sending messages to sender or to mailmaster. */ diff --git a/src/src/mytypes.h b/src/src/mytypes.h index 06426af7a..954f683c8 100644 --- a/src/src/mytypes.h +++ b/src/src/mytypes.h @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This header file contains type definitions and macros that I use as diff --git a/src/src/os.c b/src/src/os.c index 4edb2ca98..fc29f1766 100644 --- a/src/src/os.c +++ b/src/src/os.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #ifdef STAND_ALONE # include diff --git a/src/src/osfunctions.h b/src/src/osfunctions.h index 454e9f77f..f88210943 100644 --- a/src/src/osfunctions.h +++ b/src/src/osfunctions.h @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2016 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Prototypes for os-specific functions. For utilities, we don't need the one that uses a type that isn't defined for them. */ diff --git a/src/src/parse.c b/src/src/parse.c index 93b12bc77..53d660869 100644 --- a/src/src/parse.c +++ b/src/src/parse.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions for parsing addresses */ diff --git a/src/src/pdkim/crypt_ver.h b/src/src/pdkim/crypt_ver.h index 367527e39..56ae236c1 100644 --- a/src/src/pdkim/crypt_ver.h +++ b/src/src/pdkim/crypt_ver.h @@ -4,7 +4,7 @@ /* Copyright (c) Jeremy Harris 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Signing and hashing routine selection for PDKIM */ diff --git a/src/src/pdkim/pdkim_hash.h b/src/src/pdkim/pdkim_hash.h index e470299a4..d56e3ce34 100644 --- a/src/src/pdkim/pdkim_hash.h +++ b/src/src/pdkim/pdkim_hash.h @@ -2,7 +2,7 @@ * PDKIM - a RFC4871 (DKIM) implementation * * Copyright (C) 1995 - 2018 Exim maintainers - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-License-Identifier: GPL-2.0-or-later * * Hash interface functions */ diff --git a/src/src/pdkim/signing.c b/src/src/pdkim/signing.c index 8876f617c..07737ab41 100644 --- a/src/src/pdkim/signing.c +++ b/src/src/pdkim/signing.c @@ -1,7 +1,7 @@ /* * PDKIM - a RFC4871 (DKIM) implementation * Copyright (c) The Exim Maintainers 1995 - 2022 - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-License-Identifier: GPL-2.0-or-later * * signing/verification interface */ diff --git a/src/src/pdkim/signing.h b/src/src/pdkim/signing.h index 880342b6f..7760ce73f 100644 --- a/src/src/pdkim/signing.h +++ b/src/src/pdkim/signing.h @@ -2,7 +2,7 @@ * PDKIM - a RFC4871 (DKIM) implementation * * Copyright (C) 1995 - 2020 Exim maintainers - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-License-Identifier: GPL-2.0-or-later * * RSA signing/verification interface */ diff --git a/src/src/perl.c b/src/src/perl.c index b5c3f882e..2a10452d3 100644 --- a/src/src/perl.c +++ b/src/src/perl.c @@ -4,7 +4,7 @@ /* Copyright (c) The Exim Maintainers 1999 - 2022 */ /* Copyright (c) 1998 Malcolm Beattie */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Modified by PH to get rid of the "na" usage, March 1999. Modified further by PH for general tidying for Exim 4. diff --git a/src/src/priv.c b/src/src/priv.c index c818b5b78..3a100cd9e 100644 --- a/src/src/priv.c +++ b/src/src/priv.c @@ -1,5 +1,5 @@ /* Copyright (c) The Exim Maintainers 2022 * -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "exim.h" #include diff --git a/src/src/queue.c b/src/src/queue.c index fd84d303f..f86e24b42 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions that operate on the input queue. */ diff --git a/src/src/rda.c b/src/src/rda.c index 1bc322e34..9c2aa5022 100644 --- a/src/src/rda.c +++ b/src/src/rda.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This module contains code for extracting addresses from a forwarding list (from an alias or forward file) or by running the filter interpreter. It may do diff --git a/src/src/readconf.c b/src/src/readconf.c index b0d688772..48b648bb2 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions for reading the configuration file, and for displaying overall configuration values. Thanks to Brian Candler for the original diff --git a/src/src/receive.c b/src/src/receive.c index d9d0757ef..9bf834aaf 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Code for receiving a message and setting up spool files. */ diff --git a/src/src/regex.c b/src/src/regex.c index eefba8ecf..757243e7f 100644 --- a/src/src/regex.c +++ b/src/src/regex.c @@ -6,7 +6,7 @@ * Copyright (c) The Exim Maintainers 2016 - 2022 * Copyright (c) Tom Kistner 2003-2015 * License: GPL - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-License-Identifier: GPL-2.0-or-later */ /* Code for matching regular expressions against headers and body. diff --git a/src/src/regex_cache.c b/src/src/regex_cache.c index e4771aab9..a9b482174 100644 --- a/src/src/regex_cache.c +++ b/src/src/regex_cache.c @@ -5,7 +5,7 @@ /* * Copyright (c) The Exim Maintainers 2022 * License: GPL - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-License-Identifier: GPL-2.0-or-later */ /* Caching layers for compiled REs. There is a local layer in the process, diff --git a/src/src/retry.c b/src/src/retry.c index e1490266c..f073af665 100644 --- a/src/src/retry.c +++ b/src/src/retry.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions concerned with retrying unsuccessful deliveries. */ diff --git a/src/src/rewrite.c b/src/src/rewrite.c index 040525ec5..c6a7b9fa5 100644 --- a/src/src/rewrite.c +++ b/src/src/rewrite.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions concerned with rewriting headers */ diff --git a/src/src/rfc2047.c b/src/src/rfc2047.c index af8993695..c40518a5d 100644 --- a/src/src/rfc2047.c +++ b/src/src/rfc2047.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This file contains a function for decoding message header lines that may contain encoded "words" according to the rules described in diff --git a/src/src/route.c b/src/src/route.c index 7e6e4eb69..82d51bc68 100644 --- a/src/src/route.c +++ b/src/src/route.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions concerned with routing, and the list of generic router options. */ diff --git a/src/src/routers/accept.c b/src/src/routers/accept.c index ab02716eb..63c8c22e4 100644 --- a/src/src/routers/accept.c +++ b/src/src/routers/accept.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/routers/accept.h b/src/src/routers/accept.h index e025179ee..ca768b26e 100644 --- a/src/src/routers/accept.h +++ b/src/src/routers/accept.h @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Private structure for the private options (there aren't any). */ diff --git a/src/src/routers/dnslookup.c b/src/src/routers/dnslookup.c index 0b1abeb8e..d27757c7e 100644 --- a/src/src/routers/dnslookup.c +++ b/src/src/routers/dnslookup.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" #include "rf_functions.h" diff --git a/src/src/routers/dnslookup.h b/src/src/routers/dnslookup.h index 0272db300..ce1daf3c0 100644 --- a/src/src/routers/dnslookup.h +++ b/src/src/routers/dnslookup.h @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Private structure for the private options. */ diff --git a/src/src/routers/ipliteral.c b/src/src/routers/ipliteral.c index cfbf276ff..1297b97f2 100644 --- a/src/src/routers/ipliteral.c +++ b/src/src/routers/ipliteral.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/routers/ipliteral.h b/src/src/routers/ipliteral.h index f7e99b9c2..7db24cfd1 100644 --- a/src/src/routers/ipliteral.h +++ b/src/src/routers/ipliteral.h @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Private structure for the private options. Some compilers do not like empty diff --git a/src/src/routers/iplookup.c b/src/src/routers/iplookup.c index 970ec7704..7faaea0cd 100644 --- a/src/src/routers/iplookup.c +++ b/src/src/routers/iplookup.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/routers/iplookup.h b/src/src/routers/iplookup.h index 3b7295574..ccce2eef5 100644 --- a/src/src/routers/iplookup.h +++ b/src/src/routers/iplookup.h @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Private structure for the private options. */ diff --git a/src/src/routers/manualroute.c b/src/src/routers/manualroute.c index 6a500d09a..160c866b9 100644 --- a/src/src/routers/manualroute.c +++ b/src/src/routers/manualroute.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/routers/manualroute.h b/src/src/routers/manualroute.h index e27d90953..1201c8362 100644 --- a/src/src/routers/manualroute.h +++ b/src/src/routers/manualroute.h @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Header for the manualroute router */ diff --git a/src/src/routers/queryprogram.c b/src/src/routers/queryprogram.c index 51b7b7551..51fdad229 100644 --- a/src/src/routers/queryprogram.c +++ b/src/src/routers/queryprogram.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" #include "rf_functions.h" diff --git a/src/src/routers/queryprogram.h b/src/src/routers/queryprogram.h index 148846fae..f2cff1dab 100644 --- a/src/src/routers/queryprogram.h +++ b/src/src/routers/queryprogram.h @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Private structure for the private options. */ diff --git a/src/src/routers/redirect.c b/src/src/routers/redirect.c index 6126555fe..6a17c2f8d 100644 --- a/src/src/routers/redirect.c +++ b/src/src/routers/redirect.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/routers/redirect.h b/src/src/routers/redirect.h index 76d02f261..8aeb892cb 100644 --- a/src/src/routers/redirect.h +++ b/src/src/routers/redirect.h @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Header for the redirect router */ diff --git a/src/src/routers/rf_change_domain.c b/src/src/routers/rf_change_domain.c index 4f8d47299..144fabbcd 100644 --- a/src/src/routers/rf_change_domain.c +++ b/src/src/routers/rf_change_domain.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/routers/rf_expand_data.c b/src/src/routers/rf_expand_data.c index 5fbf0ac78..9892567f0 100644 --- a/src/src/routers/rf_expand_data.c +++ b/src/src/routers/rf_expand_data.c @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/routers/rf_functions.h b/src/src/routers/rf_functions.h index d8fca68c5..91ccfb132 100644 --- a/src/src/routers/rf_functions.h +++ b/src/src/routers/rf_functions.h @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Header for the functions that are shared by the routers */ diff --git a/src/src/routers/rf_get_errors_address.c b/src/src/routers/rf_get_errors_address.c index 1189fd9e0..f70bdf25e 100644 --- a/src/src/routers/rf_get_errors_address.c +++ b/src/src/routers/rf_get_errors_address.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" #include "rf_functions.h" diff --git a/src/src/routers/rf_get_munge_headers.c b/src/src/routers/rf_get_munge_headers.c index be86a4e0e..58b5bc7ad 100644 --- a/src/src/routers/rf_get_munge_headers.c +++ b/src/src/routers/rf_get_munge_headers.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" #include "rf_functions.h" diff --git a/src/src/routers/rf_get_transport.c b/src/src/routers/rf_get_transport.c index 0c1fa8173..d54e3c296 100644 --- a/src/src/routers/rf_get_transport.c +++ b/src/src/routers/rf_get_transport.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" #include "rf_functions.h" diff --git a/src/src/routers/rf_get_ugid.c b/src/src/routers/rf_get_ugid.c index 15144beb1..cefe527a5 100644 --- a/src/src/routers/rf_get_ugid.c +++ b/src/src/routers/rf_get_ugid.c @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" #include "rf_functions.h" diff --git a/src/src/routers/rf_lookup_hostlist.c b/src/src/routers/rf_lookup_hostlist.c index affd70b6e..f10ff5921 100644 --- a/src/src/routers/rf_lookup_hostlist.c +++ b/src/src/routers/rf_lookup_hostlist.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/routers/rf_queue_add.c b/src/src/routers/rf_queue_add.c index 49dd83117..8d94f5725 100644 --- a/src/src/routers/rf_queue_add.c +++ b/src/src/routers/rf_queue_add.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" #include "rf_functions.h" diff --git a/src/src/routers/rf_self_action.c b/src/src/routers/rf_self_action.c index 7cc592039..73d07db40 100644 --- a/src/src/routers/rf_self_action.c +++ b/src/src/routers/rf_self_action.c @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/routers/rf_set_ugid.c b/src/src/routers/rf_set_ugid.c index 65f5200a2..ac63222b3 100644 --- a/src/src/routers/rf_set_ugid.c +++ b/src/src/routers/rf_set_ugid.c @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" #include "rf_functions.h" diff --git a/src/src/search.c b/src/src/search.c index 32099ab9e..2b6e5d37f 100644 --- a/src/src/search.c +++ b/src/src/search.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* A set of functions to search databases in various formats. An open database is represented by a void * value which is returned from a lookup- diff --git a/src/src/setenv.c b/src/src/setenv.c index 877fe388f..a5f056aee 100644 --- a/src/src/setenv.c +++ b/src/src/setenv.c @@ -6,7 +6,7 @@ * Copyright (c) Jeremy Harris 2015 - 2016 * Copyright (c) The Exim Maintainers 2016 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This module provides (un)setenv routines for those environments lacking them in libraries. It is #include'd by OS/os.c-foo files. */ diff --git a/src/src/sha_ver.h b/src/src/sha_ver.h index 8385c1377..0d57db84f 100644 --- a/src/src/sha_ver.h +++ b/src/src/sha_ver.h @@ -4,7 +4,7 @@ /* Copyright (c) Jeremy Harris 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* SHA routine selection */ diff --git a/src/src/sieve.c b/src/src/sieve.c index 033a9734a..0b347e48d 100644 --- a/src/src/sieve.c +++ b/src/src/sieve.c @@ -6,7 +6,7 @@ * Copyright (c) The Exim Maintainers 2016 - 2022 * Copyright (c) Michael Haardt 2003 - 2015 * See the file NOTICE for conditions of use and distribution. - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-License-Identifier: GPL-2.0-or-later */ /* This code was contributed by Michael Haardt. */ diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 3c6339c82..9b60702c1 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions for handling an incoming SMTP call. */ diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index db39dcab5..e705965ba 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* A number of functions for driving outgoing SMTP calls. */ diff --git a/src/src/spam.c b/src/src/spam.c index f6575c293..401fdb269 100644 --- a/src/src/spam.c +++ b/src/src/spam.c @@ -6,7 +6,7 @@ * Copyright (c) The Exim Maintainers 2016 - 2022 * Copyright (c) Tom Kistner 2003 - 2015 * License: GPL - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-License-Identifier: GPL-2.0-or-later */ /* Code for calling spamassassin's spamd. Called from acl.c. */ diff --git a/src/src/spam.h b/src/src/spam.h index c410198d7..0362e49dd 100644 --- a/src/src/spam.h +++ b/src/src/spam.h @@ -5,7 +5,7 @@ /* Copyright (c) Tom Kistner 2003 - 2015 */ /* Copyright (c) The Exim Maintainers 2021 */ /* License: GPL */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* spam defines */ diff --git a/src/src/spf.c b/src/src/spf.c index a94bc9677..6f0917a9c 100644 --- a/src/src/spf.c +++ b/src/src/spf.c @@ -6,7 +6,7 @@ Copyright (c) The Exim Maintainers 2015 - 2022 Copyright (c) Tom Kistner 2004 - 2014 License: GPL - SPDX-License-Identifier: GPL-2.0-only + SPDX-License-Identifier: GPL-2.0-or-later */ /* Code for calling spf checks via libspf-alt. Called from acl.c. */ diff --git a/src/src/spf.h b/src/src/spf.h index 8fb7b04cb..76c7522bd 100644 --- a/src/src/spf.h +++ b/src/src/spf.h @@ -6,7 +6,7 @@ Copyright (c) The Exim Maintainers 2016 - 2022 Copyright (c) Tom Kistner 2004 License: GPL - SPDX-License-Identifier: GPL-2.0-only + SPDX-License-Identifier: GPL-2.0-or-later */ #ifdef SUPPORT_SPF diff --git a/src/src/spool_in.c b/src/src/spool_in.c index 82d11bf65..6d6651f57 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions for reading spool files. When compiling for a utility (eximon), not all are needed, and some functionality can be cut out. */ diff --git a/src/src/spool_mbox.c b/src/src/spool_mbox.c index d9767ec3c..7ea565a58 100644 --- a/src/src/spool_mbox.c +++ b/src/src/spool_mbox.c @@ -5,7 +5,7 @@ /* Copyright (c) Tom Kistner 2003 - 2015 * License: GPL * Copyright (c) The Exim Maintainers 2016 - 2021 - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-License-Identifier: GPL-2.0-or-later */ /* Code for setting up a MBOX style spool file inside a /scan/ diff --git a/src/src/spool_out.c b/src/src/spool_out.c index cb409c641..9c5e8eb33 100644 --- a/src/src/spool_out.c +++ b/src/src/spool_out.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions for writing spool files, and moving them about. */ diff --git a/src/src/std-crypto.c b/src/src/std-crypto.c index c1eef6c35..29efa6997 100644 --- a/src/src/std-crypto.c +++ b/src/src/std-crypto.c @@ -7,7 +7,7 @@ * But almost everything here is fixed published constants from RFCs, so also: * Copyright (C) The Internet Society (2003) * Copyright (C) The IETF Trust (2008) - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-License-Identifier: GPL-2.0-or-later * * Most of the text in RFC referencing comments is copy/paste from RFC, * as is undoubtedly the intention. diff --git a/src/src/store.c b/src/src/store.c index 449fb4ead..9e4536eae 100644 --- a/src/src/store.c +++ b/src/src/store.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim maintainers 2019 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Exim gets and frees all its store through these functions. In the original implementation there was a lot of mallocing and freeing of small bits of store. diff --git a/src/src/store.h b/src/src/store.h index 47ed963cd..834457aaa 100644 --- a/src/src/store.h +++ b/src/src/store.h @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Header for Exim's memory allocation functions */ diff --git a/src/src/string.c b/src/src/string.c index ff4180b2b..2cb419517 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Miscellaneous string-handling functions. Some are not required for utilities and tests, and are cut out by the COMPILE_UTILITY macro. */ diff --git a/src/src/structs.h b/src/src/structs.h index 1cb69236e..e1d93a943 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Definitions of various structures. In addition, those that are visible for diff --git a/src/src/tls-cipher-stdname.c b/src/src/tls-cipher-stdname.c index fe442a050..ef3de99cb 100644 --- a/src/src/tls-cipher-stdname.c +++ b/src/src/tls-cipher-stdname.c @@ -4,7 +4,7 @@ /* Copyright (c) Jeremy Harris 2019 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Translate an IETF TLS ciphersuite code to an IETF ciphersuite name, for use when the TLS library do not provide such names. diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index c98760202..703a0a4ca 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -6,7 +6,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) Phil Pennock 2012 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This file provides TLS/SSL support for Exim using the GnuTLS library, one of the available supported implementations. This file is #included into diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 052d8161d..2b3f02712 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2019 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Portions Copyright (c) The OpenSSL Project 1999 */ diff --git a/src/src/tls.c b/src/src/tls.c index 9e20b5bca..4a23aaae9 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This module provides TLS (aka SSL) support for Exim. The code for OpenSSL is based on a patch that was originally contributed by Steve Haslam. It was diff --git a/src/src/tlscert-gnu.c b/src/src/tlscert-gnu.c index 8840d5cfd..a3f6d4434 100644 --- a/src/src/tlscert-gnu.c +++ b/src/src/tlscert-gnu.c @@ -4,7 +4,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) Jeremy Harris 2014 - 2018 */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This file provides TLS/SSL support for Exim using the GnuTLS library, one of the available supported implementations. This file is #included into diff --git a/src/src/tlscert-openssl.c b/src/src/tlscert-openssl.c index a4c3d19fa..32177ea81 100644 --- a/src/src/tlscert-openssl.c +++ b/src/src/tlscert-openssl.c @@ -4,7 +4,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) Jeremy Harris 2014 - 2019 */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* This module provides TLS (aka SSL) support for Exim using the OpenSSL library. It is #included into the tls.c file when that library is used. diff --git a/src/src/tod.c b/src/src/tod.c index ac4ed35b9..364703d53 100644 --- a/src/src/tod.c +++ b/src/src/tod.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* A function for returning the time of day in various formats */ diff --git a/src/src/transport.c b/src/src/transport.c index ff2e0b1d4..d04ea516a 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* General functions concerned with transportation, and generic options for all transports. */ diff --git a/src/src/transports/appendfile.c b/src/src/transports/appendfile.c index 18badde86..c39c07c9f 100644 --- a/src/src/transports/appendfile.c +++ b/src/src/transports/appendfile.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2020 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/transports/appendfile.h b/src/src/transports/appendfile.h index 108114905..c3b7c59c4 100644 --- a/src/src/transports/appendfile.h +++ b/src/src/transports/appendfile.h @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Private structure for the private options. */ diff --git a/src/src/transports/autoreply.c b/src/src/transports/autoreply.c index eff1a3792..fa884cec4 100644 --- a/src/src/transports/autoreply.c +++ b/src/src/transports/autoreply.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/transports/autoreply.h b/src/src/transports/autoreply.h index 5f278a51e..83061719c 100644 --- a/src/src/transports/autoreply.h +++ b/src/src/transports/autoreply.h @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Private structure for the private options. */ diff --git a/src/src/transports/lmtp.c b/src/src/transports/lmtp.c index c734c3a26..e04c991ab 100644 --- a/src/src/transports/lmtp.c +++ b/src/src/transports/lmtp.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/transports/lmtp.h b/src/src/transports/lmtp.h index 71fd77f26..fe51b18cc 100644 --- a/src/src/transports/lmtp.h +++ b/src/src/transports/lmtp.h @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Private structure for the private options. */ diff --git a/src/src/transports/pipe.c b/src/src/transports/pipe.c index b8103e2f6..c3547eefe 100644 --- a/src/src/transports/pipe.c +++ b/src/src/transports/pipe.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" diff --git a/src/src/transports/pipe.h b/src/src/transports/pipe.h index 416569608..be5ec3424 100644 --- a/src/src/transports/pipe.h +++ b/src/src/transports/pipe.h @@ -4,7 +4,7 @@ /* Copyright (c) University of Cambridge 1995 - 2014 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Private structure for the private options. */ diff --git a/src/src/transports/queuefile.c b/src/src/transports/queuefile.c index 3a2bae22f..b6511133c 100644 --- a/src/src/transports/queuefile.c +++ b/src/src/transports/queuefile.c @@ -6,7 +6,7 @@ /* Copyright (c) University of Cambridge 2016 */ /* Copyright (c) The Exim Maintainers 1995 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/src/src/transports/queuefile.h b/src/src/transports/queuefile.h index 22759a7f7..f5362515b 100644 --- a/src/src/transports/queuefile.h +++ b/src/src/transports/queuefile.h @@ -5,7 +5,7 @@ /* Copyright (c) Andrew Colin Kissa 2016 */ /* Copyright (c) University of Cambridge 2016 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Private structure for the private options. */ diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 2d2db1b10..1183fa478 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "../exim.h" #include "smtp.h" diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index 8793ece4f..cb1d72625 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #define DELIVER_BUFFER_SIZE 4096 diff --git a/src/src/transports/smtp_socks.c b/src/src/transports/smtp_socks.c index 353a69618..22ee74bd8 100644 --- a/src/src/transports/smtp_socks.c +++ b/src/src/transports/smtp_socks.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) Jeremy Harris 2015 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* SOCKS version 5 proxy, client-mode */ diff --git a/src/src/transports/tf_maildir.c b/src/src/transports/tf_maildir.c index 925b8fac0..df932b13e 100644 --- a/src/src/transports/tf_maildir.c +++ b/src/src/transports/tf_maildir.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions in support of the use of maildirsize files for handling quotas in maildir directories. Some of the rules are a bit baroque: diff --git a/src/src/transports/tf_maildir.h b/src/src/transports/tf_maildir.h index d314c3203..9f2f0e3f8 100644 --- a/src/src/transports/tf_maildir.h +++ b/src/src/transports/tf_maildir.h @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* Copyright (c) The Exim Maintainers 2021 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Header file for the functions that are used to support the use of maildirsize files for quota handling in maildir directories. */ diff --git a/src/src/tree.c b/src/src/tree.c index e340d986a..13fc28cc2 100644 --- a/src/src/tree.c +++ b/src/src/tree.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2021 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2015 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions for maintaining binary balanced trees and some associated functions as well. */ diff --git a/src/src/utf8.c b/src/src/utf8.c index 6604727ff..c05853838 100644 --- a/src/src/utf8.c +++ b/src/src/utf8.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2022 */ /* Copyright (c) Jeremy Harris 2015 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "exim.h" diff --git a/src/src/verify.c b/src/src/verify.c index 3a8914e38..0ca096130 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -5,7 +5,7 @@ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions concerned with verifying things. The original code for callout caching was contributed by Kevin Fleming (but I hacked it around a bit). */ diff --git a/src/src/version.c b/src/src/version.c index cbaaef24b..2637ee94a 100644 --- a/src/src/version.c +++ b/src/src/version.c @@ -5,7 +5,7 @@ /* Copyright (c) University of Cambridge 1995 - 2009 */ /* Copyright (c) The Exim Maintainers 2010 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Function for setting up the version string. */ diff --git a/src/util/chunking_fixqueue_finalnewlines.pl b/src/util/chunking_fixqueue_finalnewlines.pl index b8ab17bd7..832b6a71f 100755 --- a/src/util/chunking_fixqueue_finalnewlines.pl +++ b/src/util/chunking_fixqueue_finalnewlines.pl @@ -1,5 +1,6 @@ #!/usr/bin/env perl -# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) The Exim Maintainers 2022 +# SPDX-License-Identifier: GPL-2.0-or-later use warnings; use strict; diff --git a/src/util/cramtest.pl b/src/util/cramtest.pl index a6124628b..9fc797ba2 100755 --- a/src/util/cramtest.pl +++ b/src/util/cramtest.pl @@ -1,5 +1,6 @@ #!/usr/bin/perl -# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) The Exim Maintainers 2022 +# SPDX-License-Identifier: GPL-2.0-or-later # This script is contributed by Vadim Vygonets to aid in debugging CRAM-MD5 # authentication. diff --git a/src/util/gen_pkcs3.c b/src/util/gen_pkcs3.c index 3ef3110de..c5a4453cb 100644 --- a/src/util/gen_pkcs3.c +++ b/src/util/gen_pkcs3.c @@ -2,7 +2,7 @@ * Copyright (c) The Exim Maintainers 2021 * This is distributed as part of Exim and licensed under the GPL. * See the file "NOTICE" for more details. - * SPDX-License-Identifier: GPL-2.0-only + * SPDX-License-Identifier: GPL-2.0-or-later */ /* Build with: diff --git a/src/util/logargs.sh b/src/util/logargs.sh index 814228d10..782ff8566 100755 --- a/src/util/logargs.sh +++ b/src/util/logargs.sh @@ -1,5 +1,6 @@ #! /bin/sh -# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) The Exim Maintainers 2022 +# SPDX-License-Identifier: GPL-2.0-or-later # This script can be interposed between a calling program and another # program, in order to log the arguments which are being used. This can diff --git a/src/util/mkcdb.pl b/src/util/mkcdb.pl index e2b5931e7..3a438182c 100755 --- a/src/util/mkcdb.pl +++ b/src/util/mkcdb.pl @@ -1,5 +1,6 @@ #!/usr/bin/perl -wT -# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) The Exim Maintainers 2022 +# SPDX-License-Identifier: GPL-2.0-or-later # # Create cdb file from flat alias file. DPC: 15/10/98. # Args: source (may be relative or absolute) diff --git a/src/util/ocsp_fetch.pl b/src/util/ocsp_fetch.pl index 2afbdbdac..9255311a4 100755 --- a/src/util/ocsp_fetch.pl +++ b/src/util/ocsp_fetch.pl @@ -1,7 +1,7 @@ #!/usr/bin/perl # Copyright (C) 2012 Wizards Internet Ltd # License GPLv2: GNU GPL version 2 -# SPDX-License-Identifier: GPL-2.0-only +# SPDX-License-Identifier: GPL-2.0-or-later use strict; BEGIN { pop @INC if $INC[-1] eq '.' }; diff --git a/src/util/proxy_protocol_client.pl b/src/util/proxy_protocol_client.pl index 8b4311b64..8253fc4ac 100644 --- a/src/util/proxy_protocol_client.pl +++ b/src/util/proxy_protocol_client.pl @@ -3,7 +3,7 @@ # Copyright (C) 2014 Todd Lyons # License GPLv2: GNU GPL version 2 # -# SPDX-License-Identifier: GPL-2.0-only +# SPDX-License-Identifier: GPL-2.0-or-later # # This script emulates a proxy which uses Proxy Protocol to communicate # to a backend server. It should be run from an IP which is configured diff --git a/src/util/ratelimit.pl b/src/util/ratelimit.pl index 2ceeebbfa..ce54fe6c3 100644 --- a/src/util/ratelimit.pl +++ b/src/util/ratelimit.pl @@ -1,5 +1,6 @@ #!/usr/bin/perl -wT -# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) The Exim Maintainers 2022 +# SPDX-License-Identifier: GPL-2.0-or-later use strict; diff --git a/src/util/renew-opendmarc-tlds.sh b/src/util/renew-opendmarc-tlds.sh index 583dc0e40..d626aac37 100755 --- a/src/util/renew-opendmarc-tlds.sh +++ b/src/util/renew-opendmarc-tlds.sh @@ -1,5 +1,6 @@ #!/bin/sh -eu -# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) The Exim Maintainers 2022 +# SPDX-License-Identifier: GPL-2.0-or-later # # Short version of this script: # curl -f -o /var/cache/exim/opendmarc.tlds https://publicsuffix.org/list/public_suffix_list.dat diff --git a/src/util/unknownuser.sh b/src/util/unknownuser.sh index 01e617978..a57a84bba 100755 --- a/src/util/unknownuser.sh +++ b/src/util/unknownuser.sh @@ -1,5 +1,6 @@ #! /bin/sh -# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) The Exim Maintainers 2022 +# SPDX-License-Identifier: GPL-2.0-or-later # This is a sample script for demonstrating how to handle unknown users in # a more friendly way than just returning a "user unknown" error. It can commit f5730918ef684baafbd9e606a1d4eb06914563cc Author: Jeremy Harris Date: Mon Aug 15 20:41:56 2022 +0100 tidying diff --git a/src/src/lookups/lf_check_file.c b/src/src/lookups/lf_check_file.c index 5c74816ef..c4c05e44d 100644 --- a/src/src/lookups/lf_check_file.c +++ b/src/src/lookups/lf_check_file.c @@ -45,8 +45,7 @@ lf_check_file(int fd, const uschar * filename, int s_type, int modemask, { struct stat statbuf; -if ((fd >= 0 && fstat(fd, &statbuf) != 0) || - (fd < 0 && Ustat(filename, &statbuf) != 0)) +if ((fd < 0 ? Ustat(filename, &statbuf) : fstat(fd, &statbuf)) != 0) { int save_errno = errno; *errmsg = string_sprintf("%s: stat failed", filename); @@ -80,7 +79,7 @@ if ((statbuf.st_mode & modemask) != 0) return +1; } -if (owners != NULL) +if (owners) { BOOL uid_ok = FALSE; for (int i = 1; i <= (int)owners[0]; i++) @@ -94,7 +93,7 @@ if (owners != NULL) } } -if (owngroups != NULL) +if (owngroups) { BOOL gid_ok = FALSE; for (int i = 1; i <= (int)owngroups[0]; i++) diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 703a0a4ca..69387a3a7 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -2743,25 +2743,25 @@ exim_gnutls_state_st * state = gnutls_session_get_ptr(session); if ((cert_list = gnutls_certificate_get_peers(session, &cert_list_size))) while (cert_list_size--) - { - if ((rc = import_cert(&cert_list[cert_list_size], &crt)) != GNUTLS_E_SUCCESS) { - DEBUG(D_tls) debug_printf("TLS: peer cert problem: depth %d: %s\n", - cert_list_size, gnutls_strerror(rc)); - break; - } + if ((rc = import_cert(&cert_list[cert_list_size], &crt)) != GNUTLS_E_SUCCESS) + { + DEBUG(D_tls) debug_printf("TLS: peer cert problem: depth %d: %s\n", + cert_list_size, gnutls_strerror(rc)); + break; + } - state->tlsp->peercert = crt; - if ((yield = event_raise(state->event_action, - US"tls:cert", string_sprintf("%d", cert_list_size), &errno))) - { - log_write(0, LOG_MAIN, - "SSL verify denied by event-action: depth=%d: %s", - cert_list_size, yield); - return 1; /* reject */ + state->tlsp->peercert = crt; + if ((yield = event_raise(state->event_action, + US"tls:cert", string_sprintf("%d", cert_list_size), &errno))) + { + log_write(0, LOG_MAIN, + "SSL verify denied by event-action: depth=%d: %s", + cert_list_size, yield); + return 1; /* reject */ + } + state->tlsp->peercert = NULL; } - state->tlsp->peercert = NULL; - } return 0; } diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 2b3f02712..eabe34f31 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2530,6 +2530,8 @@ if (!(bs = OCSP_response_get1_basic(rsp))) DEBUG(D_tls) bp = BIO_new(BIO_s_mem()); /* Use the CA & chain that verified the server cert to verify the stapled info */ + /*XXX could we do an event here, for observability of ocsp? What reasonable data could we give access to? */ + /* Dates would be a start. Do we need another opaque variable type, as for certs, plus an extract expansion? */ { /* If this routine is not available, we've avoided [in tls_client_start()] diff --git a/src/src/verify.c b/src/src/verify.c index 0ca096130..125df8d91 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -1056,6 +1056,8 @@ no_conn: HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of transport filter\n"); } #ifndef DISABLE_DKIM + /* DKIM signing needs to add a header after seeing the whole body, so we cannot just copy + body bytes to the outbound as they are received, which is the intent of cutthrough. */ if (ob->dkim.dkim_domain) { cutthrough.delivery= FALSE; commit 340e5f5781abb5388c41fa326d6e1cabf7ba96ff Author: Jeremy Harris Date: Tue Nov 29 15:02:01 2022 +0000 Tweak debug output diff --git a/src/src/retry.c b/src/src/retry.c index f073af665..a34bf80ca 100644 --- a/src/src/retry.c +++ b/src/src/retry.c @@ -135,7 +135,7 @@ dbdata_retry * host_retry_record, * message_retry_record; *retry_host_key = *retry_message_key = NULL; -DEBUG(D_transport|D_retry) debug_printf("checking status of %s\n", host->name); +DEBUG(D_transport|D_retry) debug_printf("checking retry status of %s\n", host->name); /* Do nothing if status already set; otherwise initialize status as usable. */ commit fc37f2acaaa440c5265dc01fd693d8f5406f5cf9 Author: Jeremy Harris Date: Tue Nov 29 15:55:05 2022 +0000 Add predefined macros for expansions diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index 4e1f61a26..a290b90b0 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -111,8 +111,8 @@ MACRO_HSRC = macro_predef.h os.h globals.h config.h macros.h \ routers/redirect.h OBJ_MACRO = macro_predef.o \ - macro-globals.o macro-readconf.o macro-route.o macro-transport.o macro-drtables.o \ - macro-acl.o macro-tls.o \ + macro-globals.o macro-readconf.o macro-expand.o macro-route.o \ + macro-transport.o macro-drtables.o macro-acl.o macro-tls.o \ macro-appendfile.o macro-autoreply.o macro-lmtp.o macro-pipe.o macro-queuefile.o \ macro-smtp.o macro-accept.o macro-dnslookup.o macro-ipliteral.o macro-iplookup.o \ macro-manualroute.o macro-queryprogram.o macro-redirect.o \ @@ -131,6 +131,9 @@ macro-globals.o : globals.c macro-readconf.o : readconf.c @echo "$(CC) -DMACRO_PREDEF readconf.c" $(FE)$(CC) -c $(CFLAGS) -DMACRO_PREDEF $(INCLUDE) -o $@ readconf.c +macro-expand.o : expand.c + @echo "$(CC) -DMACRO_PREDEF expand.c" + $(FE)$(CC) -c $(CFLAGS) -DMACRO_PREDEF $(INCLUDE) -o $@ expand.c macro-route.o : route.c @echo "$(CC) -DMACRO_PREDEF route.c" $(FE)$(CC) -c $(CFLAGS) -DMACRO_PREDEF $(INCLUDE) -o $@ route.c diff --git a/src/src/expand.c b/src/src/expand.c index 657cf3cb9..6def3c102 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -13,6 +13,10 @@ #include "exim.h" +#ifdef MACRO_PREDEF +# include "macro_predef.h" +#endif + typedef unsigned esi_flags; #define ESI_NOFLAGS 0 #define ESI_BRACE_ENDS BIT(0) /* expansion should stop at } */ @@ -831,6 +835,76 @@ static var_entry var_table[] = { }; static int var_table_size = nelem(var_table); + +#ifdef MACRO_PREDEF + +/* dummies */ +uschar * fn_arc_domains(void) {return NULL;} +uschar * fn_hdrs_added(void) {return NULL;} +uschar * fn_queue_size(void) {return NULL;} +uschar * fn_recipients(void) {return NULL;} +uschar * sender_helo_verified_boolstr(void) {return NULL;} +uschar * smtp_cmd_hist(void) {return NULL;} + + + +static void +expansion_items(void) +{ +uschar buf[64]; +for (int i = 0; i < nelem(item_table); i++) + { + spf(buf, sizeof(buf), CUS"_EXP_ITEM_%T", item_table[i]); + builtin_macro_create(buf); + } +} +static void +expansion_operators(void) +{ +uschar buf[64]; +for (int i = 0; i < nelem(op_table_underscore); i++) + { + spf(buf, sizeof(buf), CUS"_EXP_OP_%T", op_table_underscore[i]); + builtin_macro_create(buf); + } +for (int i = 0; i < nelem(op_table_main); i++) + { + spf(buf, sizeof(buf), CUS"_EXP_OP_%T", op_table_main[i]); + builtin_macro_create(buf); + } +} +static void +expansion_conditions(void) +{ +uschar buf[64]; +for (int i = 0; i < nelem(cond_table); i++) + { + spf(buf, sizeof(buf), CUS"_EXP_COND_%T", cond_table[i]); + builtin_macro_create(buf); + } +} +static void +expansion_variables(void) +{ +uschar buf[64]; +for (int i = 0; i < nelem(var_table); i++) + { + spf(buf, sizeof(buf), CUS"_EXP_VAR_%T", var_table[i].name); + builtin_macro_create(buf); + } +} + +void +expansions(void) +{ +expansion_items(); +expansion_operators(); +expansion_conditions(); +expansion_variables(); +} + +#else /*!MACRO_PREDEF*/ + static uschar var_buffer[256]; static BOOL malformed_header; @@ -8861,8 +8935,9 @@ search_tidyup(); return 0; } -#endif +#endif /*STAND_ALONE*/ +#endif /*!MACRO_PREDEF*/ /* vi: aw ai sw=2 */ /* End of expand.c */ diff --git a/src/src/macro_predef.c b/src/src/macro_predef.c index 618c914dc..0053cb245 100644 --- a/src/src/macro_predef.c +++ b/src/src/macro_predef.c @@ -338,6 +338,7 @@ printf("#include \"exim.h\"\n"); features(); exp_features(); options(); +expansions(); params(); printf("macro_item * macros = &p%d;\n", mp_index-1); diff --git a/src/src/macro_predef.h b/src/src/macro_predef.h index 8b7b375c6..94f68dfa3 100644 --- a/src/src/macro_predef.h +++ b/src/src/macro_predef.h @@ -22,6 +22,7 @@ extern void options_routers(void); extern void options_transports(void); extern void options_auths(void); extern void options_logging(void); +extern void expansions(void); extern void params_dkim(void); #ifndef DISABLE_TLS extern void options_tls(void); commit 2484a8253df7795a45fb5b4aff6df0bf0e4d56e5 Author: Jeremy Harris Date: Tue Nov 29 14:34:25 2022 +0000 Add variable $sender_helo_verified diff --git a/src/src/acl.c b/src/src/acl.c index 143890668..8e1d92457 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -1643,6 +1643,30 @@ return period; +static BOOL +sender_helo_verified_internal(void) +{ +/* We can test the result of optional HELO verification that might have +occurred earlier. If not, we can attempt the verification now. */ + +if (!f.helo_verified && !f.helo_verify_failed) smtp_verify_helo(); +return f.helo_verified; +} + +static int +sender_helo_verified_cond(void) +{ +return sender_helo_verified_internal() ? OK : FAIL; +} + +uschar * +sender_helo_verified_boolstr(void) +{ +return sender_helo_verified_internal() ? US"yes" : US"no"; +} + + + /* This function implements the "verify" condition. It is called when encountered in any ACL, because some tests are almost always permitted. Some just don't make sense, and always fail (for example, an attempt to test a host @@ -1739,11 +1763,7 @@ switch(vp->value) return FAIL; case VERIFY_HELO: - /* We can test the result of optional HELO verification that might have - occurred earlier. If not, we can attempt the verification now. */ - - if (!f.helo_verified && !f.helo_verify_failed) smtp_verify_helo(); - return f.helo_verified ? OK : FAIL; + return sender_helo_verified_cond(); case VERIFY_CSA: /* Do Client SMTP Authorization checks in a separate function, and turn the diff --git a/src/src/expand.c b/src/src/expand.c index 6def3c102..57ad76f77 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -715,6 +715,7 @@ static var_entry var_table[] = { { "sender_fullhost", vtype_stringptr, &sender_fullhost }, { "sender_helo_dnssec", vtype_bool, &sender_helo_dnssec }, { "sender_helo_name", vtype_stringptr, &sender_helo_name }, + { "sender_helo_verified",vtype_string_func, (void *) &sender_helo_verified_boolstr }, { "sender_host_address", vtype_stringptr, &sender_host_address }, { "sender_host_authenticated",vtype_stringptr, &sender_host_authenticated }, { "sender_host_dnssec", vtype_bool, &sender_host_dnssec }, diff --git a/src/src/functions.h b/src/src/functions.h index a2c8976e8..3ca346c04 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -489,6 +489,7 @@ extern int search_findtype_partial(const uschar *, int *, const uschar **, i int *, const uschar **); extern void *search_open(const uschar *, int, int, uid_t *, gid_t *); extern void search_tidyup(void); +extern uschar *sender_helo_verified_boolstr(void); extern void set_process_info(const char *, ...) PRINTF_FUNCTION(1,2); extern void sha1_end(hctx *, const uschar *, int, uschar *); extern void sha1_mid(hctx *, const uschar *); commit e8297f953ed9c8e42f1b406b5ecad4ccdd9d95d3 Author: Jeremy Harris Date: Sat Dec 3 15:16:48 2022 +0000 tidying diff --git a/src/src/log.c b/src/src/log.c index 6c483216a..a01e9c59f 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -1544,6 +1544,7 @@ else DEBUG(D_deliver) void debug_logging_stop(BOOL kill) { +debug_printf("debug terminated by %s\n", kill ? "kill" : "stop"); debug_pretrigger_discard(); if (!debug_file || !debuglog_name[0]) return; diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index eabe34f31..db77a1274 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -3999,7 +3999,7 @@ if (tlsp->host_resumable) tls_error(US"set ex_data", host, NULL, errstr); return FALSE; } - debug_printf("tls_exdata_idx %d cbinfo %p\n", tls_exdata_idx, client_static_state); + /* debug_printf("tls_exdata_idx %d cbinfo %p\n", tls_exdata_idx, client_static_state); */ } tlsp->resumption = RESUME_SUPPORTED; diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 1183fa478..ed5f83b3e 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -990,7 +990,7 @@ return FALSE; /* Return an auths bitmap for the set of AUTH methods offered by the server -which match our authenticators. */ +which match our client-side authenticators. */ static unsigned short study_ehlo_auths(smtp_context * sx) @@ -1016,7 +1016,7 @@ for (au = auths, authnum = 0; au; au = au->next, authnum++) if (au->client) } DEBUG(D_transport) - debug_printf("server offers %s AUTH, methods '%s', bitmap 0x%04x\n", + debug_printf("server offers %s AUTH, methods '%s', usable-bitmap 0x%04x\n", tls_out.active.sock >= 0 ? "crypted" : "plaintext", names, authbits); if (tls_out.active.sock >= 0) commit 0da41dc541d0f2536f9d2afc7188e9dfb97b0c09 Author: Jeremy Harris Date: Sat Dec 3 17:50:17 2022 +0000 compiler quietening diff --git a/src/src/lookups/ldap.c b/src/src/lookups/ldap.c index feeea0e41..ba77c98c2 100644 --- a/src/src/lookups/ldap.c +++ b/src/src/lookups/ldap.c @@ -1470,27 +1470,20 @@ t = quoted = store_get_quoted(len + count + 1, s, idx); /* Handle plain quote_ldap */ if (!dn) - { - while ((c = *s++)) - { + for (; c = *s++; *t++ = c) if (!isalnum(c)) { if (Ustrchr(LDAP_QUOTE, c) != NULL) { sprintf(CS t, "%%5C%02X", c); /* e.g. * => %5C2A */ t += 5; - continue; } - if (Ustrchr(URL_NONQUOTE, c) == NULL) /* e.g. ] => %5D */ + else if (Ustrchr(URL_NONQUOTE, c) == NULL) /* e.g. ] => %5D */ { sprintf(CS t, "%%%02X", c); t += 3; - continue; } } - *t++ = c; /* unquoted character */ - } - } /* Handle quote_ldap_dn */ @@ -1520,8 +1513,8 @@ else { if (Ustrchr(LDAP_DN_QUOTE, c) != NULL) { - Ustrncpy(t, US"%5C", 3); /* insert \ where needed */ - t += 3; /* fall through to check URL */ + Ustrcpy(t, US"%5C"); /* insert \ where needed */ + t += 3; /* fall through to check URL */ } if (Ustrchr(URL_NONQUOTE, c) == NULL) /* e.g. ] => %5D */ { @@ -1535,9 +1528,9 @@ else /* Handle the trailing spaces */ - while (*ss++ != 0) + while (*ss++) { - Ustrncpy(t, US"%5C%20", 6); + Ustrcpy(t, US"%5C%20"); t += 6; } } commit 44b6e099b76f403a55e77650821f8a69e9d2682e Author: Jeremy Harris Date: Sat Dec 3 23:13:53 2022 +0000 Fix ${run } arg parsing Broken-by: cfe6acff2ddc diff --git a/src/src/expand.c b/src/src/expand.c index 57ad76f77..7bb2e4274 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -5614,7 +5614,7 @@ while (*s) const uschar * arg, ** argv; BOOL late_expand = TRUE; - if ((expand_forbid & RDO_RUN) != 0) + if (expand_forbid & RDO_RUN) { expand_string_message = US"running a command is not permitted"; goto EXPAND_FAILED; @@ -5645,13 +5645,20 @@ while (*s) s++; if (late_expand) /* this is the default case */ - { /*{*/ - int n = Ustrcspn(s, "}"); + { + int n; + const uschar * t; + /* Locate the end of the args */ + (void) expand_string_internal(s, + ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | ESI_SKIPPING, &t, NULL, NULL); + n = t - s; arg = flags & ESI_SKIPPING ? NULL : string_copyn(s, n); s += n; } else { + DEBUG(D_expand) + debug_printf_indent("args string for ${run} expand before split\n"); if (!(arg = expand_string_internal(s, ESI_BRACE_ENDS | ESI_HONOR_DOLLAR | flags, &s, &resetok, NULL))) goto EXPAND_FAILED; diff --git a/src/src/transport.c b/src/src/transport.c index d04ea516a..d6cedf911 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -2190,6 +2190,8 @@ if (expand_arguments) for (int i = 0; argv[i]; i++) { + DEBUG(D_expand) debug_printf_indent("arg %d\n", i); + /* Handle special fudge for passing an address list */ if (addr && @@ -2364,7 +2366,7 @@ if (expand_arguments) return FALSE; } - if ( f.running_in_test_harness && is_tainted(expanded_arg) + if ( f.running_in_test_harness && is_tainted(expanded_arg) && Ustrcmp(etext, "queryprogram router") == 0) { /* hack, would be good to not need it */ DEBUG(D_transport) commit 4243a209fd9499f30bebd58ceaa2d0d9845407ae Author: Jeremy Harris Date: Sat Dec 10 10:47:05 2022 +0000 Move connect ACL before TLS-on-connect diff --git a/src/src/EDITME b/src/src/EDITME index 625df18f5..4fcaeda5b 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -401,7 +401,7 @@ TRANSPORT_SMTP=yes # For Redis you need to have hiredis installed on your system # (https://github.com/redis/hiredis). # Depending on where it is installed you may have to edit the CFLAGS -# (often += -I/usr/local/include) and LDFLAGS (-lhiredis) lines. +# (often += -I/usr/local/include) and LOOKUP_LIBS (-lhiredis) lines. # If your system has pkg-config then the _INCLUDE/_LIBS setting can be # handled for you automatically by also defining the _PC variable to reference diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 9b60702c1..b161f362d 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2505,6 +2505,22 @@ else DEBUG(D_receive) #endif +static void +log_connect_tls_drop(const uschar * what, const uschar * log_msg) +{ +gstring * g = s_tlslog(NULL); +uschar * tls = string_from_gstring(g); + +log_write(L_connection_reject, + log_reject_target, "%s%s%s dropped by %s%s%s", + LOGGING(dnssec) && sender_host_dnssec ? US" DS" : US"", + host_and_ident(TRUE), + tls ? tls : US"", + what, + log_msg ? US": " : US"", log_msg); +} + + /************************************************* * Start an SMTP session * *************************************************/ @@ -2857,7 +2873,10 @@ if (!f.sender_host_unknown) { log_write(L_connection_reject, LOG_MAIN|LOG_REJECT, "refused connection " "from %s (host_reject_connection)", host_and_ident(FALSE)); - smtp_printf("554 SMTP service not available\r\n", FALSE); +#ifndef DISABLE_TLS + if (!tls_in.on_connect) +#endif + smtp_printf("554 SMTP service not available\r\n", FALSE); return FALSE; } @@ -2983,18 +3002,6 @@ if (check_proxy_protocol_host()) setup_proxy_protocol_host(); #endif -/* Start up TLS if tls_on_connect is set. This is for supporting the legacy -smtps port for use with older style SSL MTAs. */ - -#ifndef DISABLE_TLS -if (tls_in.on_connect) - { - if (tls_server_start(&user_msg) != OK) - return smtp_log_tls_fail(user_msg); - cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; - } -#endif - /* Run the connect ACL if it exists */ user_msg = NULL; @@ -3004,11 +3011,28 @@ if (acl_smtp_connect) if ((rc = acl_check(ACL_WHERE_CONNECT, NULL, acl_smtp_connect, &user_msg, &log_msg)) != OK) { - (void) smtp_handle_acl_fail(ACL_WHERE_CONNECT, rc, user_msg, log_msg); +#ifndef DISABLE_TLS + if (tls_in.on_connect) + log_connect_tls_drop(US"'connect' ACL", log_msg); + else +#endif + (void) smtp_handle_acl_fail(ACL_WHERE_CONNECT, rc, user_msg, log_msg); return FALSE; } } +/* Start up TLS if tls_on_connect is set. This is for supporting the legacy +smtps port for use with older style SSL MTAs. */ + +#ifndef DISABLE_TLS +if (tls_in.on_connect) + { + if (tls_server_start(&user_msg) != OK) + return smtp_log_tls_fail(user_msg); + cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; + } +#endif + /* Output the initial message for a two-way SMTP connection. It may contain newlines, which then cause a multi-line response to be given. */ commit 4f7a93c27e3d43b44c42d3fc503f03b9b42ca622 Author: Jeremy Harris Date: Sat Dec 10 16:19:16 2022 +0000 Allow a forced-fail for banner expansion to close connection without panic-log diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index b161f362d..6c043d434 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -3040,13 +3040,7 @@ code = US"220"; /* Default status code */ esc = US""; /* Default extended status code */ esclen = 0; /* Length of esc */ -if (!user_msg) - { - if (!(s = expand_string(smtp_banner))) - log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Expansion of \"%s\" (smtp_banner) " - "failed: %s", smtp_banner, expand_string_message); - } -else +if (user_msg) { int codelen = 3; s = user_msg; @@ -3057,6 +3051,17 @@ else esclen = codelen - 4; } } +else if (!(s = expand_string(smtp_banner))) + { + log_write(0, f.expand_string_forcedfail ? LOG_MAIN : LOG_MAIN|LOG_PANIC_DIE, + "Expansion of \"%s\" (smtp_banner) failed: %s", + smtp_banner, expand_string_message); + /* for force-fail */ +#ifndef DISABLE_TLS + if (tls_in.on_connect) tls_close(NULL, TLS_SHUTDOWN_WAIT); +#endif + return FALSE; + } /* Remove any terminating newlines; might as well remove trailing space too */ commit 520ef00f56cea3d35688bf4e13599a6e37ba275f Author: Jeremy Harris Date: Sun Dec 11 15:14:54 2022 +0000 TLS: Fix handling for server cert/key file SNI re-expansion forced-fail diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 69387a3a7..f8cc34406 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -398,7 +398,8 @@ tls_error_gnu(exim_gnutls_state_st * state, const uschar *prefix, int err, { return tls_error(prefix, state && err == GNUTLS_E_FATAL_ALERT_RECEIVED - ? US gnutls_alert_get_name(gnutls_alert_get(state->session)) + ? string_sprintf("rxd alert: %s", + US gnutls_alert_get_name(gnutls_alert_get(state->session))) : US gnutls_strerror(err), state ? state->host : NULL, errstr); @@ -1293,7 +1294,7 @@ while (cfile = string_nextinlist(&clist, &csep, NULL, 0)) if (!(kfile = string_nextinlist(&klist, &ksep, NULL, 0))) return tls_error(US"cert/key setup: out of keys", NULL, NULL, errstr); - else if ((rc = tls_add_certfile(state, NULL, cfile, kfile, errstr)) > 0) + else if ((rc = tls_add_certfile(state, NULL, cfile, kfile, errstr)) != OK) return rc; else { @@ -1810,8 +1811,13 @@ D-H generation. */ if (!state->lib_state.conn_certs) { - if (!Expand_check_tlsvar(tls_certificate, errstr)) + if ( !Expand_check_tlsvar(tls_certificate, errstr) + || f.expand_string_forcedfail) + { + if (f.expand_string_forcedfail) + *errstr = US"expansion of tls_certificate failed"; return DEFER; + } /* certificate is mandatory in server, optional in client */ @@ -1823,8 +1829,14 @@ if (!state->lib_state.conn_certs) else DEBUG(D_tls) debug_printf("TLS: no client certificate specified; okay\n"); - if (state->tls_privatekey && !Expand_check_tlsvar(tls_privatekey, errstr)) + if ( state->tls_privatekey && !Expand_check_tlsvar(tls_privatekey, errstr) + || f.expand_string_forcedfail + ) + { + if (f.expand_string_forcedfail) + *errstr = US"expansion of tls_privatekey failed"; return DEFER; + } /* tls_privatekey is optional, defaulting to same file as certificate */ @@ -1866,7 +1878,11 @@ if (!state->lib_state.conn_certs) tls_ocsp_file, #endif errstr) - ) ) return rc; + ) ) + { + DEBUG(D_tls) debug_printf("load-cert: '%s'\n", *errstr); + return rc; + } } } else @@ -2710,11 +2726,12 @@ if ((rc = tls_expand_session_files(state, &dummy_errstr)) != OK) { /* If the setup of certs/etc failed before handshake, TLS would not have been offered. The best we can do now is abort. */ - return GNUTLS_E_APPLICATION_ERROR_MIN; + DEBUG(D_tls) debug_printf("expansion for SNI-dependent session files failed\n"); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; } rc = tls_set_remaining_x509(state, &dummy_errstr); -if (rc != OK) return GNUTLS_E_APPLICATION_ERROR_MIN; +if (rc != OK) return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; return 0; } diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index db77a1274..3b060cc9c 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -1553,8 +1553,13 @@ else ) ) reexpand_tls_files_for_sni = TRUE; - if (!expand_check(state->certificate, US"tls_certificate", &expanded, errstr)) + if ( !expand_check(state->certificate, US"tls_certificate", &expanded, errstr) + || f.expand_string_forcedfail) + { + if (f.expand_string_forcedfail) + *errstr = US"expansion of tls_certificate failed"; return DEFER; + } if (expanded) if (state->is_server) @@ -1622,9 +1627,14 @@ else if ((err = tls_add_certfile(sctx, state, expanded, errstr))) return err; - if ( state->privatekey - && !expand_check(state->privatekey, US"tls_privatekey", &expanded, errstr)) + if ( state->privatekey + && !expand_check(state->privatekey, US"tls_privatekey", &expanded, errstr) + || f.expand_string_forcedfail) + { + if (f.expand_string_forcedfail) + *errstr = US"expansion of tls_privatekey failed"; return DEFER; + } /* If expansion was forced to fail, key_expanded will be NULL. If the result of the expansion is an empty string, ignore it also, and assume the private @@ -2201,13 +2211,13 @@ per https://www.openssl.org/docs/manmaster/man3/SSL_client_hello_cb_fn.html #ifdef EXIM_HAVE_OPENSSL_TLSEXT static int -tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg) +tls_servername_cb(SSL * s, int * ad ARG_UNUSED, void * arg) { -const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); -exim_openssl_state_st *state = (exim_openssl_state_st *) arg; +const char * servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); +exim_openssl_state_st * state = (exim_openssl_state_st *) arg; int rc; int old_pool = store_pool; -uschar * dummy_errstr; +uschar * errstr; if (!servername) return SSL_TLSEXT_ERR_OK; @@ -2227,7 +2237,7 @@ if (!reexpand_tls_files_for_sni) not confident that memcpy wouldn't break some internal reference counting. Especially since there's a references struct member, which would be off. */ -if (lib_ctx_new(&server_sni, NULL, &dummy_errstr) != OK) +if (lib_ctx_new(&server_sni, NULL, &errstr) != OK) goto bad; /* Not sure how many of these are actually needed, since SSL object @@ -2247,8 +2257,8 @@ already exists. Might even need this selfsame callback, for reneg? */ SSL_CTX_set_tlsext_servername_arg(server_sni, state); } -if ( !init_dh(server_sni, state->dhparam, &dummy_errstr) - || !init_ecdh(server_sni, &dummy_errstr) +if ( !init_dh(server_sni, state->dhparam, &errstr) + || !init_ecdh(server_sni, &errstr) ) goto bad; @@ -2267,7 +2277,7 @@ if (state->u_ocsp.server.file) { uschar * v_certs = tls_verify_certificates; if ((rc = setup_certs(server_sni, &v_certs, tls_crl, NULL, - &dummy_errstr)) != OK) + &errstr)) != OK) goto bad; if (v_certs && *v_certs) @@ -2276,14 +2286,16 @@ if (state->u_ocsp.server.file) /* do this after setup_certs, because this can require the certs for verifying OCSP information. */ -if ((rc = tls_expand_session_files(server_sni, state, &dummy_errstr)) != OK) +if ((rc = tls_expand_session_files(server_sni, state, &errstr)) != OK) goto bad; DEBUG(D_tls) debug_printf("Switching SSL context.\n"); SSL_set_SSL_CTX(s, server_sni); return SSL_TLSEXT_ERR_OK; -bad: return SSL_TLSEXT_ERR_ALERT_FATAL; +bad: + log_write(0, LOG_MAIN|LOG_PANIC, "%s", errstr); + return SSL_TLSEXT_ERR_ALERT_FATAL; } #endif /* EXIM_HAVE_OPENSSL_TLSEXT */ commit 60b8e1d8c24d1ab487134d8b5fb1e8523f786c33 Author: Jeremy Harris Date: Sat Nov 12 20:13:32 2022 +0000 tidying diff --git a/src/src/daemon.c b/src/src/daemon.c index be008c3d4..05ef3bfdd 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -1258,10 +1258,14 @@ if (sz >= sizeof(buf)) return FALSE; #ifdef notdef debug_printf("addrlen %d\n", msg.msg_namelen); #endif -DEBUG(D_queue_run) debug_printf("%s from addr '%s%.*s'\n", __FUNCTION__, - *sa_un.sun_path ? "" : "@", - (int)msg.msg_namelen - (*sa_un.sun_path ? 0 : 1), - sa_un.sun_path + (*sa_un.sun_path ? 0 : 1)); +DEBUG(D_queue_run) + if (msg.msg_namelen > 0) + debug_printf("%s from addr '%s%.*s'\n", __FUNCTION__, + *sa_un.sun_path ? "" : "@", + (int)msg.msg_namelen - (*sa_un.sun_path ? 0 : 1), + sa_un.sun_path + (*sa_un.sun_path ? 0 : 1)); + else + debug_printf("%s (from unknown addr)\n", __FUNCTION__); /* Refuse to handle the item unless the peer has good credentials */ #ifdef SCM_CREDENTIALS diff --git a/src/src/expand.c b/src/src/expand.c index 7bb2e4274..62b4a1890 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -4853,11 +4853,11 @@ while (*s) switch(read_subs(sub_arg, nelem(sub_arg), 1, &s, flags, TRUE, name, &resetok, NULL)) { + case -1: continue; /* If skipping, we don't actually do anything */ case 1: goto EXPAND_FAILED_CURLY; case 2: case 3: goto EXPAND_FAILED; } - /*XXX no skipping-optimisation? */ yield = string_append(yield, 3, US"Authentication-Results: ", sub_arg[0], US"; none"); @@ -4944,7 +4944,6 @@ while (*s) case 2: case 3: goto EXPAND_FAILED; } - /*XXX no skipping-optimisation? */ if (!sub_arg[1]) /* One argument */ { @@ -5439,15 +5438,12 @@ while (*s) switch(read_subs(sub_arg, 2, 1, &s, flags, TRUE, name, &resetok, NULL)) { + case -1: continue; /* If skipping, we don't actually do anything */ case 1: goto EXPAND_FAILED_CURLY; case 2: case 3: goto EXPAND_FAILED; } - /* If skipping, we don't actually do anything */ - - if (flags & ESI_SKIPPING) continue; - /* Open the file and read it */ if (!(f = Ufopen(sub_arg[0], "rb"))) @@ -6327,6 +6323,7 @@ while (*s) save_expand_strings(save_expand_nstring, save_expand_nlength); /* Read the field & list arguments */ + /*XXX Could we use read_subs here (and get better efficiency for skipping)? */ for (int i = 0; i < 2; i++) { commit 419ad98ed05e32ccbf1a05549984017e8f0bd79a Author: Jeremy Harris Date: Tue Dec 13 13:38:53 2022 +0000 GnuTLS: fix cert loading Broken-by: 520ef00f56ce diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index f8cc34406..729fb5879 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -379,7 +379,7 @@ Argument: the connected host if setting up a client errstr pointer to returned error string -Returns: OK/DEFER/FAIL +Returns: DEFER/FAIL */ static int @@ -392,6 +392,7 @@ return host ? FAIL : DEFER; } +/* Returns: DEFER/FAIL */ static int tls_error_gnu(exim_gnutls_state_st * state, const uschar *prefix, int err, uschar ** errstr) @@ -1271,6 +1272,7 @@ DEBUG(D_tls) debug_printf("TLS: basic cred init, %s\n", server ? "server" : "client"); } +/* Returns OK/DEFER/FAIL */ static int creds_load_server_certs(exim_gnutls_state_st * state, const uschar * cert, const uschar * pkey, const uschar * ocsp, uschar ** errstr) @@ -1294,7 +1296,7 @@ while (cfile = string_nextinlist(&clist, &csep, NULL, 0)) if (!(kfile = string_nextinlist(&klist, &ksep, NULL, 0))) return tls_error(US"cert/key setup: out of keys", NULL, NULL, errstr); - else if ((rc = tls_add_certfile(state, NULL, cfile, kfile, errstr)) != OK) + else if ((rc = tls_add_certfile(state, NULL, cfile, kfile, errstr)) > OK) return rc; else { @@ -1372,7 +1374,7 @@ while (cfile = string_nextinlist(&clist, &csep, NULL, 0)) } #endif /* DISABLE_OCSP */ } -return 0; +return OK; } static int @@ -1382,7 +1384,7 @@ creds_load_client_certs(exim_gnutls_state_st * state, const host_item * host, int rc = tls_add_certfile(state, host, cert, pkey, errstr); if (rc > 0) return rc; DEBUG(D_tls) debug_printf("TLS: cert/key registered\n"); -return 0; +return OK; } static int commit 85f4056d71b45977bf269c7e595386647538d14b Author: Jeremy Harris Date: Tue Dec 13 15:46:01 2022 +0000 ACL: Permit the "encrypted" condition to be used in a HELO/EHLO ACL diff --git a/src/src/acl.c b/src/src/acl.c index 8e1d92457..74b59b0fe 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -223,7 +223,7 @@ static condition_def conditions[] = { }, [ACLC_ENCRYPTED] = { US"encrypted", FALSE, FALSE, ACL_BIT_NOTSMTP | ACL_BIT_NOTSMTP_START | - ACL_BIT_HELO, + ACL_BIT_CONNECT }, [ACLC_ENDPASS] = { US"endpass", TRUE, TRUE, 0 }, commit 5fd353d843a09c53c6767cbc89671a751b719555 Author: Jeremy Harris Date: Sat Dec 17 16:20:19 2022 +0000 Add template lines for alternate DBM libraries, in the teplate Makefile diff --git a/src/src/EDITME b/src/src/EDITME index 4fcaeda5b..6344561d6 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -647,6 +647,29 @@ DISABLE_MAL_MKS=yes # understand these issues, go with the defaults, which are used by many sites. +#------------------------------------------------------------------------------ +# Which DBM library to use. If you do not specify a specific here, you get +# the platform default. Uncomment the pair of lines as preferred. +# Note: when changing an installation from one DB type to another all the +# hints-DB files, in spool/db, should be removed. + +# gdbm in native mode +# USE_GDBM = yes +# DBMLIB = -lgdbm + +# gdbm in Berkeley-DB compatibility mode +# USE_NDBM = yes +# DBMLIB = -lgdbm -lgdbm_compat + +# tdb +# USE_TDB = yes +# DBMLIB = -ltdb + +# Berkeley DB +# USE_DB = yes +# DBMLIB = -ldb + + #------------------------------------------------------------------------------ # Although Exim is normally a setuid program, owned by root, it refuses to run # local deliveries as root by default. There is a runtime option called commit 1ed24e36e279c922d3366f6c3144570cc5f54d7a Author: Jeremy Harris Date: Mon Dec 19 21:09:17 2022 +0000 Fix logging of max-size log line Broken-by: d12746bc15d8 diff --git a/src/src/log.c b/src/src/log.c index a01e9c59f..d11b933f9 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -806,7 +806,7 @@ log_write(unsigned int selector, int flags, const char *format, ...) { int paniclogfd; ssize_t written_len; -gstring gs = { .size = LOG_BUFFER_SIZE-1, .ptr = 0, .s = log_buffer }; +gstring gs = { .size = LOG_BUFFER_SIZE-2, .ptr = 0, .s = log_buffer }; gstring * g; va_list ap; @@ -952,11 +952,10 @@ DEBUG(D_any|D_v) } va_end(ap); - g->size = LOG_BUFFER_SIZE; g = string_catn(g, US"\n", 1); debug_printf("%s", string_from_gstring(g)); - gs.size = LOG_BUFFER_SIZE-1; /* Having used the buffer for debug output, */ + gs.size = LOG_BUFFER_SIZE-2; /* Having used the buffer for debug output, */ gs.ptr = 0; /* reset it for the real use. */ gs.s = log_buffer; } @@ -1038,6 +1037,8 @@ if ( flags & LOG_RECIPIENTS } } +/* actual size, now we are placing the newline (and space for NUL) */ +gs.size = LOG_BUFFER_SIZE; g = string_catn(g, US"\n", 1); string_from_gstring(g); commit d439520cf2ccd61b0a2190bb331b1dded18547b8 Author: Jeremy Harris Date: Tue Dec 20 14:38:26 2022 +0000 cppcheck sliencing diff --git a/src/src/acl.c b/src/src/acl.c index 74b59b0fe..5ab674776 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -2550,6 +2550,7 @@ else switch(mode) anchor = NULL; /* silence an "unused" complaint */ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "internal ACL error: unknown ratelimit mode %d", mode); + /*NOTREACHED*/ break; } diff --git a/src/src/deliver.c b/src/src/deliver.c index c4fce4602..c5e00eaef 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -342,13 +342,7 @@ if (Ustrstr(filename, US"/../")) for (int i = 2; i > 0; i--) { int fd = Uopen(filename, -#ifdef O_CLOEXEC - O_CLOEXEC | -#endif -#ifdef O_NOFOLLOW - O_NOFOLLOW | -#endif - O_WRONLY|O_APPEND|O_CREAT, mode); + EXIM_CLOEXEC | EXIM_NOFOLLOW | O_WRONLY|O_APPEND|O_CREAT, mode); if (fd >= 0) { /* Set the close-on-exec flag and change the owner to the exim uid/gid (this @@ -4705,17 +4699,13 @@ all pipes, so I do not see a reason to use non-blocking IO here { uschar * fname = spool_fname(US"input", message_subdir, message_id, US"-D"); - if ((deliver_datafile = Uopen(fname, -#ifdef O_CLOEXEC - O_CLOEXEC | -#endif - O_RDWR | O_APPEND, 0)) < 0) + if ( (deliver_datafile = Uopen(fname, EXIM_CLOEXEC | O_RDWR | O_APPEND, 0)) + < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Failed to reopen %s for remote " "parallel delivery: %s", fname, strerror(errno)); } - /* Set the close-on-exec flag */ -#ifndef O_CLOEXEC +#ifndef O_CLOEXEC /* Set the close-on-exec flag */ (void)fcntl(deliver_datafile, F_SETFD, fcntl(deliver_datafile, F_GETFD) | FD_CLOEXEC); #endif @@ -5749,14 +5739,8 @@ Otherwise it might be needed again. */ uschar * fname = spool_fname(US"input", message_subdir, id, US"-J"); FILE * jread; - if ( (journal_fd = Uopen(fname, O_RDWR|O_APPEND -#ifdef O_CLOEXEC - | O_CLOEXEC -#endif -#ifdef O_NOFOLLOW - | O_NOFOLLOW -#endif - , SPOOL_MODE)) >= 0 + if ( (journal_fd = Uopen(fname, + O_RDWR|O_APPEND | EXIM_CLOEXEC | EXIM_NOFOLLOW, SPOOL_MODE)) >= 0 && lseek(journal_fd, 0, SEEK_SET) == 0 && (jread = fdopen(journal_fd, "rb")) ) @@ -7154,10 +7138,7 @@ if (addr_local || addr_remote) uschar * fname = spool_fname(US"input", message_subdir, id, US"-J"); if ((journal_fd = Uopen(fname, -#ifdef O_CLOEXEC - O_CLOEXEC | -#endif - O_WRONLY|O_APPEND|O_CREAT|O_EXCL, SPOOL_MODE)) < 0) + EXIM_CLOEXEC | O_WRONLY|O_APPEND|O_CREAT|O_EXCL, SPOOL_MODE)) < 0) { log_write(0, LOG_MAIN|LOG_PANIC, "Couldn't open journal file %s: %s", fname, strerror(errno)); diff --git a/src/src/exim.c b/src/src/exim.c index 35f4ae4f7..dc082f392 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -841,6 +841,7 @@ exim_fail(const char * fmt, ...) va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); +va_end(ap); exit(EXIT_FAILURE); } @@ -1224,13 +1225,11 @@ DEBUG(D_any) #if defined(__clang__) g = string_fmt_append(g, "Compiler: CLang [%s]\n", __clang_version__); #elif defined(__GNUC__) - g = string_fmt_append(g, "Compiler: GCC [%s]\n", # ifdef __VERSION__ - __VERSION__ + g = string_fmt_append(g, "Compiler: GCC [%s]\n", __VERSION__); # else - "? unknown version ?" + g = string_fmt_append(g, "Compiler: GCC [%s]\n", "? unknown version ?"; # endif - ); #else g = string_cat(g, US"Compiler: \n"); #endif diff --git a/src/src/hash.c b/src/src/hash.c index 10af1b43d..95860fc50 100644 --- a/src/src/hash.c +++ b/src/src/hash.c @@ -408,7 +408,7 @@ Returns: nothing */ static void -native_sha1_end(sha1 *base, const uschar *text, int length, uschar *digest) +native_sha1_end(sha1 * base, const uschar * text, int length, uschar * digest) { uschar work[64]; @@ -426,7 +426,7 @@ out to 64, process it, and then set up the final chunk as 56 bytes of padding. If it has less than 56 bytes, we pad it out to 56 bytes as the final chunk. */ -memcpy(work, text, length); +if (length) memcpy(work, text, length); work[length] = 0x80; if (length > 55) diff --git a/src/src/host.c b/src/src/host.c index ecdc6d681..8d53eb3de 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -912,7 +912,7 @@ Returns: the number of ints used */ int -host_aton(const uschar *address, int *bin) +host_aton(const uschar * address, int * bin) { int x[4]; int v4offset = 0; @@ -924,13 +924,10 @@ supported. */ if (Ustrchr(address, ':') != NULL) { - const uschar *p = address; - const uschar *component[8]; + const uschar * p = address; + const uschar * component[8]; BOOL ipv4_ends = FALSE; - int ci = 0; - int nulloffset = 0; - int v6count = 8; - int i; + int ci = 0, nulloffset = 0, v6count = 8, i; /* If the address starts with a colon, it will start with two colons. Just lose the first one, which will leave a null first component. */ @@ -942,7 +939,7 @@ if (Ustrchr(address, ':') != NULL) overlooked; to guard against that happening again, check here and crash if there are too many components. */ - while (*p != 0 && *p != '%') + while (*p && *p != '%') { int len = Ustrcspn(p, ":%"); if (len == 0) nulloffset = ci; diff --git a/src/src/macros.h b/src/src/macros.h index 243c1e5a0..a631877a1 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -1125,4 +1125,15 @@ typedef unsigned mcs_flags; #define MCS_AT_SPECIAL BIT(2) /* recognize @, @[], etc. */ #define MCS_CACHEABLE BIT(3) /* no dynamic expansions used for pattern */ +/* Flags for open() */ +#ifdef O_CLOEXEC +# define EXIM_CLOEXEC O_CLOEXEC +#else +# define EXIM_CLOEXEC 0 +#endif +#ifdef O_NOFOLLOW +# define EXIM_NOFOLLOW O_NOFOLLOW +#else +# define EXIM_NOFOLLOW 0 +#endif /* End of macros.h */ diff --git a/src/src/os.c b/src/src/os.c index fc29f1766..87a336935 100644 --- a/src/src/os.c +++ b/src/src/os.c @@ -11,6 +11,8 @@ # include # include # include +#else +# define DEBUG(x) if (debug_selector & (x)) #endif #ifndef CS @@ -50,9 +52,9 @@ sigemptyset(&(act.sa_mask)); act.sa_flags = SA_RESTART; sigaction(sig, &act, NULL); -#ifdef STAND_ALONE +# ifdef STAND_ALONE printf("Used SA_RESTART\n"); -#endif +# endif /* SunOS4 and Ultrix default to non-interruptable signals, with SV_INTERRUPT for making them interruptable. This seems to be a dying fashion. */ @@ -60,9 +62,9 @@ for making them interruptable. This seems to be a dying fashion. */ #elif defined SV_INTERRUPT signal(sig, handler); -#ifdef STAND_ALONE +# ifdef STAND_ALONE printf("Used default signal()\n"); -#endif +# endif /* If neither SA_RESTART nor SV_INTERRUPT is available we don't know how to @@ -71,9 +73,9 @@ set up a restarting signal, so simply suppress the facility. */ #else signal(sig, SIG_IGN); -#ifdef STAND_ALONE +# ifdef STAND_ALONE printf("Used SIG_IGN\n"); -#endif +# endif #endif } @@ -361,9 +363,9 @@ here as there is the -hal variant, and other systems might follow this road one day. */ #if !defined(OS_LOAD_AVERAGE) && defined(HAVE_KSTAT) -#define OS_LOAD_AVERAGE +# define OS_LOAD_AVERAGE -#include +# include int os_getloadavg(void) @@ -397,7 +399,7 @@ return avg; #if !defined(OS_LOAD_AVERAGE) && defined(HAVE_DEV_KMEM) #define OS_LOAD_AVERAGE -#include +# include static int avg_kd = -1; static long avg_offset; @@ -481,7 +483,7 @@ Returns: a chain of ip_address_items, each pointing to a textual #ifdef HAVE_GETIFADDRS -#include +# include ip_address_item * os_common_find_running_interfaces(void) @@ -630,13 +632,13 @@ what we want to know. */ if ((vs = socket(FAMILY, SOCK_DGRAM, 0)) < 0) { - #if HAVE_IPV6 +#if HAVE_IPV6 DEBUG(D_interface) debug_printf("Unable to create IPv6 socket to find interface addresses:\n " "error %d %s\nTrying for an IPv4 socket\n", errno, strerror(errno)); vs = socket(AF_INET, SOCK_DGRAM, 0); if (vs < 0) - #endif +#endif log_write(0, LOG_PANIC_DIE, "Unable to create IPv4 socket to find interface " "addresses: %d %s", errno, strerror(errno)); } @@ -816,7 +818,7 @@ programmer creates their own structs. */ #if !defined(OS_GET_DNS_RESOLVER_RES) && !defined(COMPILE_UTILITY) -#include +# include /* confirmed that res_state is typedef'd as a struct* on BSD and Linux, will find out how unportable it is on other OSes, but most resolver implementations diff --git a/src/src/retry.c b/src/src/retry.c index a34bf80ca..1897c782f 100644 --- a/src/src/retry.c +++ b/src/src/retry.c @@ -518,8 +518,8 @@ Returns: nothing */ void -retry_update(address_item **addr_defer, address_item **addr_failed, - address_item **addr_succeed) +retry_update(address_item ** addr_defer, address_item ** addr_failed, + address_item ** addr_succeed) { open_db dbblock; open_db *dbm_file = NULL; @@ -533,11 +533,10 @@ to the failed chain if they have timed out. */ for (int i = 0; i < 3; i++) { - address_item *endaddr, *addr; - address_item *last_first = NULL; - address_item **paddr = i==0 ? addr_succeed : - i==1 ? addr_failed : addr_defer; - address_item **saved_paddr = NULL; + address_item * endaddr, *addr; + address_item * last_first = NULL; + address_item ** paddr = i==0 ? addr_succeed : i==1 ? addr_failed : addr_defer; + address_item ** saved_paddr = NULL; DEBUG(D_retry) debug_printf("%s addresses:\n", i == 0 ? "Succeeded" : i == 1 ? "Failed" : "Deferred"); diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 6c043d434..1cfcc0404 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2680,32 +2680,32 @@ if (!f.sender_host_unknown) #if !HAVE_IPV6 && !defined(NO_IP_OPTIONS) - #ifdef GLIBC_IP_OPTIONS - #if (!defined __GLIBC__) || (__GLIBC__ < 2) - #define OPTSTYLE 1 - #else - #define OPTSTYLE 2 - #endif - #elif defined DARWIN_IP_OPTIONS - #define OPTSTYLE 2 - #else - #define OPTSTYLE 3 - #endif +# ifdef GLIBC_IP_OPTIONS +# if (!defined __GLIBC__) || (__GLIBC__ < 2) +# define OPTSTYLE 1 +# else +# define OPTSTYLE 2 +# endif +# elif defined DARWIN_IP_OPTIONS +# define OPTSTYLE 2 +# else +# define OPTSTYLE 3 +# endif if (!host_checking && !f.sender_host_notsocket) { - #if OPTSTYLE == 1 +# if OPTSTYLE == 1 EXIM_SOCKLEN_T optlen = sizeof(struct ip_options) + MAX_IPOPTLEN; struct ip_options *ipopt = store_get(optlen, GET_UNTAINTED); - #elif OPTSTYLE == 2 +# elif OPTSTYLE == 2 struct ip_opts ipoptblock; struct ip_opts *ipopt = &ipoptblock; EXIM_SOCKLEN_T optlen = sizeof(ipoptblock); - #else +# else struct ipoption ipoptblock; struct ipoption *ipopt = &ipoptblock; EXIM_SOCKLEN_T optlen = sizeof(ipoptblock); - #endif +# endif /* Occasional genuine failures of getsockopt() have been seen - for example, "reset by peer". Therefore, just log and give up on this @@ -2735,19 +2735,19 @@ if (!f.sender_host_unknown) else if (optlen > 0) { - uschar *p = big_buffer; - uschar *pend = big_buffer + big_buffer_size; - uschar *adptr; + uschar * p = big_buffer; + uschar * pend = big_buffer + big_buffer_size; + uschar * adptr; int optcount; struct in_addr addr; - #if OPTSTYLE == 1 - uschar *optstart = US (ipopt->__data); - #elif OPTSTYLE == 2 - uschar *optstart = US (ipopt->ip_opts); - #else - uschar *optstart = US (ipopt->ipopt_list); - #endif +# if OPTSTYLE == 1 + uschar * optstart = US (ipopt->__data); +# elif OPTSTYLE == 2 + uschar * optstart = US (ipopt->ip_opts); +# else + uschar * optstart = US (ipopt->ipopt_list); +# endif DEBUG(D_receive) debug_printf("IP options exist\n"); @@ -2758,59 +2758,65 @@ if (!f.sender_host_unknown) switch (*opt) { case IPOPT_EOL: - opt = NULL; - break; + opt = NULL; + break; case IPOPT_NOP: - opt++; - break; + opt++; + break; case IPOPT_SSRR: case IPOPT_LSRR: - if (!string_format(p, pend-p, " %s [@%s", - (*opt == IPOPT_SSRR)? "SSRR" : "LSRR", - #if OPTSTYLE == 1 - inet_ntoa(*((struct in_addr *)(&(ipopt->faddr)))))) - #elif OPTSTYLE == 2 - inet_ntoa(ipopt->ip_dst))) - #else - inet_ntoa(ipopt->ipopt_dst))) - #endif - { - opt = NULL; - break; - } + if (! +# if OPTSTYLE == 1 + string_format(p, pend-p, " %s [@%s", + (*opt == IPOPT_SSRR)? "SSRR" : "LSRR", + inet_ntoa(*((struct in_addr *)(&(ipopt->faddr))))) +# elif OPTSTYLE == 2 + string_format(p, pend-p, " %s [@%s", + (*opt == IPOPT_SSRR)? "SSRR" : "LSRR", + inet_ntoa(ipopt->ip_dst)) +# else + string_format(p, pend-p, " %s [@%s", + (*opt == IPOPT_SSRR)? "SSRR" : "LSRR", + inet_ntoa(ipopt->ipopt_dst)) +# endif + ) + { + opt = NULL; + break; + } - p += Ustrlen(p); - optcount = (opt[1] - 3) / sizeof(struct in_addr); - adptr = opt + 3; - while (optcount-- > 0) - { - memcpy(&addr, adptr, sizeof(addr)); - if (!string_format(p, pend - p - 1, "%s%s", - (optcount == 0)? ":" : "@", inet_ntoa(addr))) - { - opt = NULL; - break; - } - p += Ustrlen(p); - adptr += sizeof(struct in_addr); - } - *p++ = ']'; - opt += opt[1]; - break; + p += Ustrlen(p); + optcount = (opt[1] - 3) / sizeof(struct in_addr); + adptr = opt + 3; + while (optcount-- > 0) + { + memcpy(&addr, adptr, sizeof(addr)); + if (!string_format(p, pend - p - 1, "%s%s", + (optcount == 0)? ":" : "@", inet_ntoa(addr))) + { + opt = NULL; + break; + } + p += Ustrlen(p); + adptr += sizeof(struct in_addr); + } + *p++ = ']'; + opt += opt[1]; + break; default: - { - if (pend - p < 4 + 3*opt[1]) { opt = NULL; break; } - Ustrcat(p, "[ "); - p += 2; - for (int i = 0; i < opt[1]; i++) - p += sprintf(CS p, "%2.2x ", opt[i]); - *p++ = ']'; - } - opt += opt[1]; - break; + { + if (pend - p < 4 + 3*opt[1]) { opt = NULL; break; } + Ustrcat(p, "[ "); + p += 2; + for (int i = 0; i < opt[1]; i++) + p += sprintf(CS p, "%2.2x ", opt[i]); + *p++ = ']'; + } + opt += opt[1]; + break; } *p = 0; diff --git a/src/src/spool_in.c b/src/src/spool_in.c index 6d6651f57..e785f695b 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -64,13 +64,7 @@ for (int i = 0; i < 2; i++) * No -D file inside the spool area should be a symlink. */ if ((fd = Uopen(fname, -#ifdef O_CLOEXEC - O_CLOEXEC | -#endif -#ifdef O_NOFOLLOW - O_NOFOLLOW | -#endif - O_RDWR | O_APPEND, 0)) >= 0) + EXIM_CLOEXEC | EXIM_NOFOLLOW | O_RDWR | O_APPEND, 0)) >= 0) break; save_errno = errno; if (errno == ENOENT) diff --git a/src/src/string.c b/src/src/string.c index 2cb419517..b30673c04 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -1783,7 +1783,7 @@ while (fgets(CS buffer, sizeof(buffer), stdin) != NULL) int llflag = 0; int n = 0; int count; - int countset = 0; + BOOL countset = FASE; uschar format[256]; uschar outbuf[256]; uschar *s; @@ -1825,7 +1825,7 @@ while (fgets(CS buffer, sizeof(buffer), stdin) != NULL) else if (Ustrcmp(ss, "*") == 0) { args[n++] = (void *)(&count); - countset = 1; + countset = TRUE; } else commit c7bec9723a721f566c67df0a526e4de18b723659 Author: Jeremy Harris Date: Sun Dec 18 18:44:54 2022 +0000 Rework "compiler quietening" This partially reverts commit 0da41dc541d0f2536f9d2afc7188e9dfb97b0c09. diff --git a/src/src/lookups/ldap.c b/src/src/lookups/ldap.c index ba77c98c2..b2ad3bbbc 100644 --- a/src/src/lookups/ldap.c +++ b/src/src/lookups/ldap.c @@ -1470,20 +1470,27 @@ t = quoted = store_get_quoted(len + count + 1, s, idx); /* Handle plain quote_ldap */ if (!dn) - for (; c = *s++; *t++ = c) + { + while ((c = *s++)) + { if (!isalnum(c)) { if (Ustrchr(LDAP_QUOTE, c) != NULL) { sprintf(CS t, "%%5C%02X", c); /* e.g. * => %5C2A */ t += 5; + continue; } - else if (Ustrchr(URL_NONQUOTE, c) == NULL) /* e.g. ] => %5D */ + if (Ustrchr(URL_NONQUOTE, c) == NULL) /* e.g. ] => %5D */ { sprintf(CS t, "%%%02X", c); t += 3; + continue; } } + *t++ = c; /* unquoted character */ + } + } /* Handle quote_ldap_dn */ @@ -1513,7 +1520,7 @@ else { if (Ustrchr(LDAP_DN_QUOTE, c) != NULL) { - Ustrcpy(t, US"%5C"); /* insert \ where needed */ + memcpy(t, US"%5C", 3); /* insert \ where needed */ t += 3; /* fall through to check URL */ } if (Ustrchr(URL_NONQUOTE, c) == NULL) /* e.g. ] => %5D */ @@ -1530,7 +1537,7 @@ else while (*ss++) { - Ustrcpy(t, US"%5C%20"); + memcpy(t, US"%5C%20", 6); t += 6; } } commit 0762e1a4d6de4b7b0206314302297c9dd6d7ae73 Author: Jeremy Harris Date: Fri Dec 23 18:02:25 2022 +0000 Expand max_rcpt option on smtp transport. Bug 2946 diff --git a/src/src/deliver.c b/src/src/deliver.c index c5e00eaef..ca31df587 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -4290,10 +4290,14 @@ So look out for the place it gets used. } /* Get the maximum it can handle in one envelope, with zero meaning - unlimited, which is forced for the MUA wrapper case. */ - - address_count_max = tp->max_addresses; - if (address_count_max == 0 || mua_wrapper) address_count_max = 999999; + unlimited, which is forced for the MUA wrapper case and if the + value could vary depending on the messages. + For those, we only split (below) by (tpt,dest,erraddr,hdrs) and rely on the + transport splitting further by max_rcp. So we potentially lose some + parallellism. */ + + address_count_max = mua_wrapper || Ustrchr(tp->max_addresses, '$') + ? UNLIMITED_ADDRS : expand_max_rcpt(tp->max_addresses); /************************************************************************/ diff --git a/src/src/functions.h b/src/src/functions.h index 3ca346c04..1817144ea 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -1307,6 +1307,14 @@ debug_printf("cmdlog: '%s'\n", client_cmd_log ? client_cmd_log->s : US"(unset)") +static inline int +expand_max_rcpt(const uschar * str_max_rcpt) +{ +const uschar * s = expand_cstring(str_max_rcpt); +int res; +return !s || !*s || (res = Uatoi(s)) == 0 ? UNLIMITED_ADDRS : res; +} + # endif /* !COMPILE_UTILITY */ /******************************************************************************/ @@ -1314,6 +1322,6 @@ debug_printf("cmdlog: '%s'\n", client_cmd_log ? client_cmd_log->s : US"(unset)") #endif /* _FUNCTIONS_H_ */ -/* vi: aw +/* vi: aw ai sw=2 */ /* End of functions.h */ diff --git a/src/src/globals.c b/src/src/globals.c index e5b72592f..efe34902a 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -1592,7 +1592,7 @@ transport_instance transport_defaults = { /* All non-mentioned elements zero/NULL/FALSE */ .batch_max = 1, .multi_domain = TRUE, - .max_addresses = 100, + .max_addresses = US"100", .connection_max_messages = 500, .uid = (uid_t)(-1), .gid = (gid_t)(-1), diff --git a/src/src/macros.h b/src/src/macros.h index a631877a1..585067fc9 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -1136,4 +1136,8 @@ typedef unsigned mcs_flags; #else # define EXIM_NOFOLLOW 0 #endif + +/* A big number for (effectively) unlimited envelope addresses */ +#define UNLIMITED_ADDRS 999999 + /* End of macros.h */ diff --git a/src/src/structs.h b/src/src/structs.h index e1d93a943..eae66e88d 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -183,7 +183,7 @@ typedef struct transport_instance { uschar *expand_multi_domain; /* ) */ BOOL multi_domain; /* ) */ BOOL overrides_hosts; /* ) Used only for remote transports */ - int max_addresses; /* ) */ + uschar *max_addresses; /* ) */ int connection_max_messages;/* ) */ /**************************************/ BOOL deliver_as_creator; /* Used only by pipe at present */ diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index ed5f83b3e..c5951832b 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -114,7 +114,7 @@ optionlist smtp_transport_options[] = { { "interface", opt_stringptr, LOFF(interface) }, { "keepalive", opt_bool, LOFF(keepalive) }, { "lmtp_ignore_quota", opt_bool, LOFF(lmtp_ignore_quota) }, - { "max_rcpt", opt_int | opt_public, + { "max_rcpt", opt_stringptr | opt_public, OPT_OFF(transport_instance, max_addresses) }, { "message_linelength_limit", opt_int, LOFF(message_linelength_limit) }, { "multi_domain", opt_expand_bool | opt_public, @@ -2121,8 +2121,9 @@ sx->dane_required = verify_check_given_host(CUSS &ob->hosts_require_dane, sx->conn_args.host) == OK; #endif -if ((sx->max_mail = sx->conn_args.tblock->connection_max_messages) == 0) sx->max_mail = 999999; -if ((sx->max_rcpt = sx->conn_args.tblock->max_addresses) == 0) sx->max_rcpt = 999999; +if ((sx->max_mail = sx->conn_args.tblock->connection_max_messages) == 0) + sx->max_mail = UNLIMITED_ADDRS; +sx->max_rcpt = expand_max_rcpt(sx->conn_args.tblock->max_addresses); sx->igquotstr = US""; if (!sx->helo_data) sx->helo_data = ob->helo_data; @@ -2819,8 +2820,9 @@ if (tls_out.active.sock >= 0) #ifdef EXPERIMMENTAL_ESMTP_LIMITS /* As we are about to send another EHLO, forget any LIMITS received so far. */ sx->peer_limit_mail = sx->peer_limit_rcpt = sx->peer_limit_rcptdom = 0; - if ((sx->max_mail = sx->conn_args.tblock->connection_max_message) == 0) sx->max_mail = 999999; - if ((sx->max_rcpt = sx->conn_args.tblock->max_addresses) == 0) sx->max_rcpt = 999999; + if ((sx->max_mail = sx->conn_args.tblock->connection_max_message) == 0) + sx->max_mail = UNLIMITED_ADDRS; + sx->max_rcpt = expand_max_rcpt(sx->conn_args.tblock->max_addresses); sx->single_rcpt_domain = FALSE; #endif commit 2b7e98456504911562b1b5aca7fa94492bbe5204 Author: Jeremy Harris Date: Sun Dec 25 21:05:42 2022 +0000 FreeBSD: fix notifier socket use diff --git a/src/src/daemon.c b/src/src/daemon.c index 05ef3bfdd..d3fec42ee 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -45,6 +45,9 @@ static smtp_slot *smtp_slots = NULL; static BOOL write_pid = TRUE; +#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS +static uschar * notifier_socket_name; +#endif /************************************************* @@ -129,15 +132,14 @@ if (smtp_out) smtp_printf("421 %s\r\n", FALSE, smtp_msg); /************************************************* *************************************************/ -#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS static void unlink_notifier_socket(void) { -uschar * s = expand_string(notifier_socket); -DEBUG(D_any) debug_printf("unlinking notifier socket %s\n", s); -Uunlink(s); -} +#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS +DEBUG(D_any) debug_printf("unlinking notifier socket %s\n", notifier_socket_name); +Uunlink(notifier_socket_name); #endif +} static void @@ -148,9 +150,6 @@ if (daemon_notifier_fd >= 0) { (void) close(daemon_notifier_fd); daemon_notifier_fd = -1; -#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS - unlink_notifier_socket(); -#endif } for (int i = 0; i < listen_socket_count; i++) (void) close(fd_polls[i].fd); @@ -1105,9 +1104,7 @@ if (daemon_notifier_fd >= 0) { close(daemon_notifier_fd); daemon_notifier_fd = -1; -#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS unlink_notifier_socket(); -#endif } if (f.running_in_test_harness || write_pid) @@ -1143,7 +1140,7 @@ return offsetof(struct sockaddr_un, sun_path) + 1 #else *sname = string_sprintf("%s/p_%d", spool_directory, getpid()); return offsetof(struct sockaddr_un, sun_path) - + snprintf(sup->sun_path, sizeof(sup->sun_path), "%s", sname); + + snprintf(sup->sun_path, sizeof(sup->sun_path), "%s", CS *sname); #endif } @@ -1154,11 +1151,12 @@ daemon_notifier_sockname(struct sockaddr_un * sup) sup->sun_path[0] = 0; /* Abstract local socket addr - Linux-specific? */ return offsetof(struct sockaddr_un, sun_path) + 1 + snprintf(sup->sun_path+1, sizeof(sup->sun_path)-1, "%s", - expand_string(notifier_socket)); + CS expand_string(notifier_socket)); #else +notifier_socket_name = expand_string(notifier_socket); return offsetof(struct sockaddr_un, sun_path) + snprintf(sup->sun_path, sizeof(sup->sun_path), "%s", - expand_string(notifier_socket)); + CS notifier_socket_name); #endif } @@ -1260,10 +1258,17 @@ debug_printf("addrlen %d\n", msg.msg_namelen); #endif DEBUG(D_queue_run) if (msg.msg_namelen > 0) - debug_printf("%s from addr '%s%.*s'\n", __FUNCTION__, - *sa_un.sun_path ? "" : "@", - (int)msg.msg_namelen - (*sa_un.sun_path ? 0 : 1), - sa_un.sun_path + (*sa_un.sun_path ? 0 : 1)); + { + BOOL abstract = !*sa_un.sun_path; + char * name = sa_un.sun_path + (abstract ? 1 : 0); + int namelen = (int)msg.msg_namelen - abstract ? 1 : 0; + if (*name) + debug_printf("%s from addr '%s%.*s'\n", __FUNCTION__, + abstract ? "@" : "", + namelen, name); + else + debug_printf("%s (from unknown addr)\n", __FUNCTION__); + } else debug_printf("%s (from unknown addr)\n", __FUNCTION__); @@ -2673,6 +2678,7 @@ for (;;) log_write(0, LOG_MAIN, "pid %d: SIGHUP received: re-exec daemon", getpid()); close_daemon_sockets(daemon_notifier_fd, fd_polls, listen_socket_count); + unlink_notifier_socket(); ALARM_CLR(0); signal(SIGHUP, SIG_IGN); sighup_argv[0] = exim_path; commit 1d38781da934809e6ce0b8c3718c4b3bccdfe1d2 Author: Jeremy Harris Date: Wed Dec 28 19:39:06 2022 +0000 Fix recursion on dns_again_means_nonexist. Bug 2911 diff --git a/src/src/dns.c b/src/src/dns.c index 4e01d8661..2355409ec 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -802,6 +802,7 @@ dns_basic_lookup(dns_answer * dnsa, const uschar * name, int type) int rc; #ifndef STAND_ALONE const uschar * save_domain; +static BOOL try_again_recursion = FALSE; #endif /* DNS lookup failures of any kind are cached in a tree. This is mainly so that @@ -906,11 +907,22 @@ if (dnsa->answerlen < 0) switch (h_errno) /* Cut this out for various test programs */ #ifndef STAND_ALONE + if (try_again_recursion) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "dns_again_means_nonexist recursion seen for %s (assuming nonexist)", + name); + return dns_fail_return(name, type, dns_expire_from_soa(dnsa, type), DNS_NOMATCH); + } + + try_again_recursion = TRUE; save_domain = deliver_domain; deliver_domain = string_copy(name); /* set $domain */ rc = match_isinlist(name, CUSS &dns_again_means_nonexist, 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL); deliver_domain = save_domain; + try_again_recursion = FALSE; + if (rc != OK) { DEBUG(D_dns) debug_printf("returning DNS_AGAIN\n"); commit 6fc54bd18aa7a51e11dce5a905e754cedb526230 Author: Jeremy Harris Date: Thu Dec 29 00:50:50 2022 +0000 Debug: list searching diff --git a/src/src/match.c b/src/src/match.c index 15209f84a..91a49c0f0 100644 --- a/src/src/match.c +++ b/src/src/match.c @@ -430,9 +430,9 @@ int yield = OK; unsigned int * original_cache_bits = *cache_ptr; BOOL include_unknown = FALSE, ignore_unknown = FALSE, include_defer = FALSE, ignore_defer = FALSE; -const uschar *list; -uschar *sss; -uschar *ot = NULL; +const uschar * list; +uschar * sss; +uschar * ot = NULL; BOOL textonly_re; /* Save time by not scanning for the option name when we don't need it. */ @@ -514,6 +514,11 @@ HDEBUG(D_any) if (!ot) gstring_release_unused(g); ot = string_from_gstring(g); } +HDEBUG(D_lists) + { + debug_printf_indent("%s\n", ot); + expand_level++; + } /* Now scan the list and process each item in turn, until one of them matches, or we hit an error. */ @@ -522,6 +527,8 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) { uschar * ss = sss; + HDEBUG(D_lists) debug_printf_indent("list element: %s\n", ss); + /* Address lists may contain +caseful, to restore caseful matching of the local part. We have to know the layout of the control block, unfortunately. The lower cased address is in a temporary buffer, so we just copy the local @@ -605,14 +612,15 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) { if (*ss == '+' && anchorptr) { - int bits = 0; - int offset = 0; - int shift = 0; - unsigned int *use_cache_bits = original_cache_bits; - uschar *cached = US""; - namedlist_block *nb; + int bits = 0, offset = 0, shift = 0; + unsigned int * use_cache_bits = original_cache_bits; + uschar * cached = US""; + namedlist_block * nb; tree_node * t; + DEBUG(D_lists) + { debug_printf_indent(" start sublist %s\n", ss+1); expand_level += 2; } + if (!(t = tree_search(*anchorptr, ss+1))) { log_write(0, LOG_MAIN|LOG_PANIC, "unknown named%s list \"%s\"", @@ -621,7 +629,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) type == MCL_ADDRESS ? " address" : type == MCL_LOCALPART ? " local part" : "", ss); - return DEFER; + goto DEFER_RETURN; } nb = t->data.ptr; @@ -645,8 +653,12 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) if (bits == 0) { - switch (match_check_list(&(nb->string), 0, anchorptr, &use_cache_bits, - func, arg, type, name, valueptr)) + int res = match_check_list(&(nb->string), 0, anchorptr, &use_cache_bits, + func, arg, type, name, valueptr); + DEBUG(D_lists) + { expand_level -= 2; debug_printf_indent(" end sublist %s\n", ss+1); } + + switch (res) { case OK: bits = 1; break; case FAIL: bits = 3; break; @@ -695,8 +707,12 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) else { - DEBUG(D_lists) debug_printf_indent("cached %s match for %s\n", - (bits & (-bits)) == bits ? "yes" : "no", ss); + DEBUG(D_lists) + { + expand_level -= 2; + debug_printf_indent("cached %s match for %s\n", + (bits & (-bits)) == bits ? "yes" : "no", ss); + } cached = US" - cached"; if (valueptr) @@ -720,7 +736,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) { HDEBUG(D_lists) debug_printf_indent("%s %s (matched \"%s\"%s)\n", ot, yield == OK ? "yes" : "no", sss, cached); - return yield; + goto YIELD_RETURN; } } @@ -734,7 +750,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) case OK: HDEBUG(D_lists) debug_printf_indent("%s %s (matched \"%s\")\n", ot, (yield == OK)? "yes" : "no", sss); - return yield; + goto YIELD_RETURN; case DEFER: if (!error) @@ -852,7 +868,8 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) Copy it to allocated memory now we know it matched. */ if (valueptr) *valueptr = string_copy(ss); - return file_yield; + yield = file_yield; + goto YIELD_RETURN; case DEFER: if (!error) @@ -864,12 +881,10 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) break; } (void)fclose(f); - if (include_defer) - { - log_write(0, LOG_MAIN, "%s: accepted by +include_defer", error); - return OK; - } - goto DEFER_RETURN; + if (!include_defer) + goto DEFER_RETURN; + log_write(0, LOG_MAIN, "%s: accepted by +include_defer", error); + goto OK_RETURN; case ERROR: /* host name lookup failed - this can only */ if (ignore_unknown) /* be for an incoming host (not outgoing) */ @@ -886,10 +901,10 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) { if (LOGGING(unknown_in_list)) log_write(0, LOG_MAIN, "list matching forced to fail: %s", error); - return FAIL; + goto FAIL_RETURN; } log_write(0, LOG_MAIN, "%s: accepted by +include_unknown", error); - return OK; + goto OK_RETURN; } } } @@ -905,14 +920,33 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0))) /* End of list reached: if the last item was negated yield OK, else FAIL. */ HDEBUG(D_lists) - debug_printf_indent("%s %s (end of list)\n", ot, yield == OK ? "no":"yes"); -return yield == OK ? FAIL : OK; + HDEBUG(D_lists) + { + expand_level--; + debug_printf_indent("%s %s (end of list)\n", ot, yield == OK ? "no":"yes"); + } + return yield == OK ? FAIL : OK; /* Something deferred */ DEFER_RETURN: -HDEBUG(D_lists) debug_printf("%s list match deferred for %s\n", ot, sss); -return DEFER; + HDEBUG(D_lists) + { + expand_level--; + debug_printf_indent("%s list match deferred for %s\n", ot, sss); + } + return DEFER; + +FAIL_RETURN: + yield = FAIL; + goto YIELD_RETURN; + +OK_RETURN: + yield = OK; + +YIELD_RETURN: + HDEBUG(D_lists) expand_level--; + return yield; } commit caf28f95ce0a617b075cf66e24a0e4b0b8aaf18e Author: Jeremy Harris Date: Thu Dec 29 17:14:31 2022 +0000 Debug: quieten environment-cleaning diff --git a/src/src/environment.c b/src/src/environment.c index 68adf3c0c..b05b1aefd 100644 --- a/src/src/environment.c +++ b/src/src/environment.c @@ -4,6 +4,7 @@ /* Copyright (c) Heiko Schlittermann 2016 * hs@schlittermann.de + * Copyright (c) The Exim Maintainers 2022 * See the file NOTICE for conditions of use and distribution. * SPDX-License-Identifier: GPL-2.0-or-later */ @@ -25,10 +26,7 @@ Returns: TRUE if successful BOOL cleanup_environment() { -int old_pool = store_pool; -store_pool = POOL_PERM; /* Need perm memory for any created env vars */ - -if (!keep_environment || *keep_environment == '\0') +if (!keep_environment || !*keep_environment) { /* From: https://github.com/dovecot/core/blob/master/src/lib/env-util.c#L55 Try to clear the environment. @@ -43,6 +41,11 @@ if (!keep_environment || *keep_environment == '\0') else if (Ustrcmp(keep_environment, "*") != 0) { rmark reset_point = store_mark(); + unsigned deb = debug_selector; + BOOL hc = host_checking; + debug_selector = 0; /* quieten this clearout */ + host_checking = FALSE; + if (environ) for (uschar ** p = USS environ; *p; /* see below */) { /* It's considered broken if we do not find the '=', according to @@ -54,31 +57,42 @@ else if (Ustrcmp(keep_environment, "*") != 0) { uschar * name = string_copyn(*p, eqp - *p); - if (OK != match_isinlist(name, CUSS &keep_environment, - 0, NULL, NULL, MCL_NOEXPAND, FALSE, NULL)) - if (os_unsetenv(name) < 0) return FALSE; - else p = USS environ; /* RESTART from the beginning */ - else p++; + if (match_isinlist(name, CUSS &keep_environment, + 0, NULL, NULL, MCL_NOEXPAND, FALSE, NULL) == OK) + p++; /* next */ + else if (os_unsetenv(name) == 0) + p = USS environ; /* RESTART from the beginning */ + else + { debug_selector = deb; host_checking = hc; return FALSE; } } } + debug_selector = deb; + host_checking = hc; store_reset(reset_point); } +DEBUG(D_expand) + { + debug_printf("environment after trimming:\n"); + if (environ) for (uschar ** p = USS environ; *p; p++) + debug_printf(" %s\n", *p); + } if (add_environment) { - uschar * p; int sep = 0; const uschar * envlist = add_environment; + int old_pool = store_pool; + store_pool = POOL_PERM; /* Need perm memory for any created env vars */ - while ((p = string_nextinlist(&envlist, &sep, NULL, 0))) + for (const uschar * p; p = string_nextinlist(&envlist, &sep, NULL, 0); ) { DEBUG(D_expand) debug_printf("adding %s\n", p); putenv(CS p); } + store_pool = old_pool; } #ifndef DISABLE_TLS tls_clean_env(); #endif -store_pool = old_pool; return TRUE; } commit 5281dce92f37ab268bfa781e384d64dc5947203f Author: Jeremy Harris Date: Fri Dec 30 18:53:51 2022 +0000 FreeBSD: fix listener-socket backlog monitoring diff --git a/src/src/daemon.c b/src/src/daemon.c index d3fec42ee..05d94b188 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -2553,7 +2553,19 @@ for (;;) if (p->revents & POLLIN) { EXIM_SOCKLEN_T alen = sizeof(accepted); -#ifdef TCP_INFO +#if defined(__FreeBSD__) && defined(SO_LISTENQLEN) + int backlog; + socklen_t blen = sizeof(backlog); + + if ( smtp_backlog_monitor > 0 + && getsockopt(p->fd, SOL_SOCKET, SO_LISTENQLEN, &backlog, &blen) == 0) + { + DEBUG(D_interface) + debug_printf("listen fd %d queue curr %d\n", p->fd, backlog); + smtp_listen_backlog = backlog; + } + +#elif defined(TCP_INFO) && defined(EXIM_HAVE_TCPI_UNACKED) struct tcp_info ti; socklen_t tlen = sizeof(ti); @@ -2563,15 +2575,9 @@ for (;;) if ( smtp_backlog_monitor > 0 && getsockopt(p->fd, IPPROTO_TCP, TCP_INFO, &ti, &tlen) == 0) { -# ifdef EXIM_HAVE_TCPI_UNACKED DEBUG(D_interface) debug_printf("listen fd %d queue max %u curr %u\n", p->fd, ti.tcpi_sacked, ti.tcpi_unacked); smtp_listen_backlog = ti.tcpi_unacked; -# elif defined(__FreeBSD__) /* This does not work. Investigate kernel sourcecode. */ - DEBUG(D_interface) debug_printf("listen fd %d queue max %u curr %u\n", - p->fd, ti.__tcpi_sacked, ti.__tcpi_unacked); - smtp_listen_backlog = ti.__tcpi_unacked; -# endif } #endif p->revents = 0; commit 57d70161718e02927a22d6a3481803b72035ac46 Author: Jeremy Harris Date: Sat Dec 31 13:37:17 2022 +0000 Close server smtp socket explicitly on connect ACL "drop" diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 1cfcc0404..6880e3c09 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -3565,6 +3565,19 @@ problem, because we get here only if some other ACL has issued "drop", and in that case, *its* custom messages will have been used above. */ smtp_notquit_exit(US"acl-drop", NULL, NULL); + +/* An overenthusiastic fail2ban/iptables implimentation has been seen to result +in the TCP conn staying open, and retrying, despite this process exiting. A +malicious client could possibly do the same, tying up server netowrking +resources. Close the socket explicitly to try to avoid that (there's a note in +the Linux socket(7) manpage, SO_LINGER para, to the effect that exim() without +close() results in the socket always lingering). */ + +(void) poll_one_fd(fileno(smtp_in), POLLIN, 200); +DEBUG(D_any) debug_printf_indent("SMTP(close)>>\n"); +(void) fclose(smtp_in); +(void) fclose(smtp_out); + return 2; } commit 313dcd5968cd8a02995322fa771f4d56b9f15e49 Author: Jeremy Harris Date: Sat Dec 31 18:32:37 2022 +0000 Testsuite: longer timing for OpenSSL resumption tests diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 3b060cc9c..ae0986aac 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -155,6 +155,7 @@ change this guard and punt the issue for a while longer. */ # endif #endif +#define TESTSUITE_TICKET_LIFE 10 /* seconds */ /************************************************* * OpenSSL option parse * *************************************************/ @@ -2044,7 +2045,7 @@ if (exim_tk.name[0]) exim_tk_old = exim_tk; } -if (f.running_in_test_harness) ssl_session_timeout = 6; +if (f.running_in_test_harness) ssl_session_timeout = TESTSUITE_TICKET_LIFE; DEBUG(D_tls) debug_printf("OpenSSL: %s STEK\n", exim_tk.name[0] ? "rotating" : "creating"); if (RAND_bytes(exim_tk.aes_key, sizeof(exim_tk.aes_key)) <= 0) return; @@ -3908,16 +3909,17 @@ if (tlsp->host_resumable) #ifdef EXIM_HAVE_SESSION_TICKET SSL_SESSION_get_ticket_lifetime_hint(ss); #else /* Use, fairly arbitrilarily, what we as server would */ - f.running_in_test_harness ? 6 : ssl_session_timeout; + f.running_in_test_harness ? TESTSUITE_TICKET_LIFE : ssl_session_timeout; #endif - if (lifetime + dt->time_stamp < time(NULL)) + time_t now = time(NULL), expires = lifetime + dt->time_stamp; + if (expires < now) { - DEBUG(D_tls) debug_printf("session expired\n"); + DEBUG(D_tls) debug_printf("session expired (by " TIME_T_FMT "s from %lus)\n", now - expires, lifetime); dbfn_delete(dbm_file, tlsp->resume_index); } else if (SSL_set_session(ssl, ss)) { - DEBUG(D_tls) debug_printf("good session\n"); + DEBUG(D_tls) debug_printf("good session (" TIME_T_FMT "s left of %lus)\n", expires - now, lifetime); tlsp->resumption |= RESUME_CLIENT_SUGGESTED; tlsp->verify_override = dt->verify_override; tlsp->ocsp = dt->ocsp; commit ca4014de81e6aa367aa0a54c49b4c3d4b137814c Author: Jeremy Harris Date: Sun Jan 1 12:18:38 2023 +0000 OpenSSL: fix tls_eccurve setting explicit curve/group. Bug 2954 diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index ae0986aac..4d0f99ea9 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -676,12 +676,12 @@ if (dh_bitsize <= tls_dh_max_bits) } else DEBUG(D_tls) - debug_printf("Diffie-Hellman initialized from %s with %d-bit prime\n", + debug_printf(" Diffie-Hellman initialized from %s with %d-bit prime\n", dhexpanded ? dhexpanded : US"default", dh_bitsize); } else DEBUG(D_tls) - debug_printf("dhparams '%s' %d bits, is > tls_dh_max_bits limit of %d\n", + debug_printf(" dhparams '%s' %d bits, is > tls_dh_max_bits limit of %d\n", dhexpanded ? dhexpanded : US"default", dh_bitsize, tls_dh_max_bits); #if OPENSSL_VERSION_NUMBER < 0x30000000L @@ -731,19 +731,27 @@ return TRUE; #else uschar * exp_curve; -int nid; -BOOL rv; +int nid, rc; # ifndef EXIM_HAVE_ECDH DEBUG(D_tls) - debug_printf("No OpenSSL API to define ECDH parameters, skipping\n"); + debug_printf(" No OpenSSL API to define ECDH parameters, skipping\n"); return TRUE; # else if (!expand_check(tls_eccurve, US"tls_eccurve", &exp_curve, errstr)) return FALSE; + +/* Is the option deliberately empty? */ + if (!exp_curve || !*exp_curve) + { +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + DEBUG(D_tls) debug_printf( " ECDH OpenSSL 1.0.2+: clearing curves list\n"); + (void) SSL_CTX_set1_curves(sctx, &nid, 0); +#endif return TRUE; + } /* "auto" needs to be handled carefully. * OpenSSL < 1.0.2: we do not select anything, but fallback to prime256v1 @@ -756,23 +764,22 @@ if (Ustrcmp(exp_curve, "auto") == 0) { #if OPENSSL_VERSION_NUMBER < 0x10002000L DEBUG(D_tls) debug_printf( - "ECDH OpenSSL < 1.0.2: temp key parameter settings: overriding \"auto\" with \"prime256v1\"\n"); + " ECDH OpenSSL < 1.0.2: temp key parameter settings: overriding \"auto\" with \"prime256v1\"\n"); exp_curve = US"prime256v1"; #else # if defined SSL_CTRL_SET_ECDH_AUTO DEBUG(D_tls) debug_printf( - "ECDH OpenSSL 1.0.2+: temp key parameter settings: autoselection\n"); + " ECDH OpenSSL 1.0.2+: temp key parameter settings: autoselection\n"); SSL_CTX_set_ecdh_auto(sctx, 1); return TRUE; # else DEBUG(D_tls) debug_printf( - "ECDH OpenSSL 1.1.0+: temp key parameter settings: default selection\n"); + " ECDH OpenSSL 1.1.0+: temp key parameter settings: library default selection\n"); return TRUE; # endif #endif } -DEBUG(D_tls) debug_printf("ECDH: curve '%s'\n", exp_curve); if ( (nid = OBJ_sn2nid (CCS exp_curve)) == NID_undef # ifdef EXIM_HAVE_OPENSSL_EC_NIST2NID && (nid = EC_curve_nist2nid(CCS exp_curve)) == NID_undef @@ -796,23 +803,23 @@ if ( (nid = OBJ_sn2nid (CCS exp_curve)) == NID_undef /* The "tmp" in the name here refers to setting a temporary key not to the stability of the interface. */ - if ((rv = SSL_CTX_set_tmp_ecdh(sctx, ecdh) == 0)) + if ((rc = SSL_CTX_set_tmp_ecdh(sctx, ecdh) == 0)) tls_error(string_sprintf("Error enabling '%s' curve", exp_curve), NULL, NULL, errstr); else - DEBUG(D_tls) debug_printf("ECDH: enabled '%s' curve\n", exp_curve); + DEBUG(D_tls) debug_printf(" ECDH: enabled '%s' curve\n", exp_curve); EC_KEY_free(ecdh); } #else /* v 3.0.0 + */ -if ((rv = SSL_CTX_set1_groups(sctx, &nid, 1)) == 0) +if ((rc = SSL_CTX_set1_groups(sctx, &nid, 1)) == 0) tls_error(string_sprintf("Error enabling '%s' group", exp_curve), NULL, NULL, errstr); else - DEBUG(D_tls) debug_printf("ECDH: enabled '%s' group\n", exp_curve); + DEBUG(D_tls) debug_printf(" ECDH: enabled '%s' group\n", exp_curve); #endif -return !rv; +return !!rc; # endif /*EXIM_HAVE_ECDH*/ #endif /*OPENSSL_NO_ECDH*/ @@ -1746,7 +1753,7 @@ state_server.lib_state.lib_ctx = ctx; if (opt_unset_or_noexpand(tls_dhparam)) { - DEBUG(D_tls) debug_printf("TLS: preloading DH params for server\n"); + DEBUG(D_tls) debug_printf("TLS: preloading DH params '%s' for server\n", tls_dhparam); if (init_dh(ctx, tls_dhparam, &dummy_errstr)) state_server.lib_state.dh = TRUE; } @@ -1754,7 +1761,7 @@ else DEBUG(D_tls) debug_printf("TLS: not preloading DH params for server\n"); if (opt_unset_or_noexpand(tls_eccurve)) { - DEBUG(D_tls) debug_printf("TLS: preloading ECDH curve for server\n"); + DEBUG(D_tls) debug_printf("TLS: preloading ECDH curve '%s' for server\n", tls_eccurve); if (init_ecdh(ctx, &dummy_errstr)) state_server.lib_state.ecdh = TRUE; } commit 7fa5764c203f2f4a900898a79ed02d674075313f Author: Jeremy Harris Date: Mon Jan 2 15:04:14 2023 +0000 OpenSSL: Fix tls_eccurve on earlier versions than 3.0.0. Bug 2954 Broken-by: ca4014de81e6 diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 4d0f99ea9..e063d29bd 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -786,8 +786,9 @@ if ( (nid = OBJ_sn2nid (CCS exp_curve)) == NID_undef # endif ) { - tls_error(string_sprintf("Unknown curve name tls_eccurve '%s'", exp_curve), - NULL, NULL, errstr); + uschar * s = string_sprintf("Unknown curve name tls_eccurve '%s'", exp_curve); + DEBUG(D_tls) debug_printf("TLS error '%s'\n", s); + if (errstr) *errstr = s; return FALSE; } @@ -803,7 +804,7 @@ if ( (nid = OBJ_sn2nid (CCS exp_curve)) == NID_undef /* The "tmp" in the name here refers to setting a temporary key not to the stability of the interface. */ - if ((rc = SSL_CTX_set_tmp_ecdh(sctx, ecdh) == 0)) + if ((rc = SSL_CTX_set_tmp_ecdh(sctx, ecdh)) == 0) tls_error(string_sprintf("Error enabling '%s' curve", exp_curve), NULL, NULL, errstr); else DEBUG(D_tls) debug_printf(" ECDH: enabled '%s' curve\n", exp_curve); commit e1aca33756f73c22b00a98d40ce2be8ed94464b1 Author: Jeremy Harris Date: Thu Jan 5 13:03:37 2023 +0000 OpenSSL: log conns rejected for bad ALPN, with the offered value Unfortunately, no way to do this under GnuTLS diff --git a/src/src/match.c b/src/src/match.c index 91a49c0f0..07070362d 100644 --- a/src/src/match.c +++ b/src/src/match.c @@ -968,6 +968,7 @@ Arguments: s string to search for listptr ptr to ptr to colon separated list of patterns, or NULL sep a separator value for the list (see string_nextinlist()) + or zero for auto anchorptr ptr to tree for named items, or NULL if no named items cache_bits ptr to cache_bits for ditto, or NULL if not caching type MCL_DOMAIN when matching a domain list diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 729fb5879..b47fabf1d 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -1119,21 +1119,28 @@ switch (tls_id) /* The format of "data" here doesn't seem to be documented, but appears to be a 2-byte field with a (redundant, given the "size" arg) total length then a sequence of one-byte size then string (not nul-term) names. The - latter is as described in OpenSSL documentation. */ + latter is as described in OpenSSL documentation. + Note that we do not get called for a match_fail, making it hard to log + a single bad ALPN being offered (the common case). */ + { + gstring * g = NULL; DEBUG(D_tls) debug_printf("Seen ALPN extension from client (s=%u):", size); for (const uschar * s = data+2; s-data < size-1; s += *s + 1) { server_seen_alpn++; + g = string_append_listele_n(g, ':', s+1, *s); DEBUG(D_tls) debug_printf(" '%.*s'", (int)*s, s+1); } DEBUG(D_tls) debug_printf("\n"); if (server_seen_alpn > 1) { + log_write(0, LOG_MAIN, "TLS ALPN (%s) rejected", string_from_gstring(g)); DEBUG(D_tls) debug_printf("TLS: too many ALPNs presented in handshake\n"); return GNUTLS_E_NO_APPLICATION_PROTOCOL; } break; + } #endif } return 0; diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index e063d29bd..513ba0d3a 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2324,6 +2324,8 @@ static int tls_server_alpn_cb(SSL *ssl, const uschar ** out, uschar * outlen, const uschar * in, unsigned int inlen, void * arg) { +gstring * g = NULL; + server_seen_alpn = TRUE; DEBUG(D_tls) { @@ -2354,12 +2356,19 @@ if ( inlen > 1 /* at least one name */ } } -/* More than one name from clilent, or name did not match our list. */ +/* More than one name from client, or name did not match our list. */ /* This will be fatal to the TLS conn; would be nice to kill TCP also. Maybe as an option in future; for now leave control to the config (must-tls). */ -DEBUG(D_tls) debug_printf("TLS ALPN rejected\n"); +for (int pos = 0, siz; pos < inlen; pos += siz+1) + { + siz = in[pos]; + if (pos + 1 + siz > inlen) siz = inlen - pos - 1; + g = string_append_listele_n(g, ':', in + pos + 1, siz); + } +log_write(0, LOG_MAIN, "TLS ALPN (%s) rejected", string_from_gstring(g)); +gstring_release_unused(g); return SSL_TLSEXT_ERR_ALERT_FATAL; } #endif /* EXIM_HAVE_ALPN */ commit 30520c8f87fcf660ed99a2344cae7f9787f7bc89 Author: Jeremy Harris Date: Thu Jan 5 18:39:51 2023 +0000 DANE: do not check dns_again_means_nonexist for TLSA results of TRY_AGAIN diff --git a/src/src/dns.c b/src/src/dns.c index 2355409ec..d39b4b590 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -907,21 +907,30 @@ if (dnsa->answerlen < 0) switch (h_errno) /* Cut this out for various test programs */ #ifndef STAND_ALONE - if (try_again_recursion) + /* Permitting dns_again_means nonexist for TLSA lookups breaks the + doewngrade resistance of dane, so avoid for those. */ + + if (type == T_TLSA) + rc = FAIL; + else { - log_write(0, LOG_MAIN|LOG_PANIC, - "dns_again_means_nonexist recursion seen for %s (assuming nonexist)", - name); - return dns_fail_return(name, type, dns_expire_from_soa(dnsa, type), DNS_NOMATCH); - } + if (try_again_recursion) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "dns_again_means_nonexist recursion seen for %s" + " (assuming nonexist)", name); + return dns_fail_return(name, type, dns_expire_from_soa(dnsa, type), + DNS_NOMATCH); + } - try_again_recursion = TRUE; - save_domain = deliver_domain; - deliver_domain = string_copy(name); /* set $domain */ - rc = match_isinlist(name, CUSS &dns_again_means_nonexist, 0, - &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL); - deliver_domain = save_domain; - try_again_recursion = FALSE; + try_again_recursion = TRUE; + save_domain = deliver_domain; + deliver_domain = string_copy(name); /* set $domain */ + rc = match_isinlist(name, CUSS &dns_again_means_nonexist, 0, + &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL); + deliver_domain = save_domain; + try_again_recursion = FALSE; + } if (rc != OK) { commit 31c546c4d0c3baf1b1e0ab292b4d096cffe64c34 Author: Jeremy Harris Date: Fri Jan 6 20:50:23 2023 +0000 Debug: show received Proxy Protocol bytes diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 6880e3c09..5d8ffd3d0 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -1187,6 +1187,16 @@ errno = EOVERFLOW; return -1; } + +static void +proxy_debug(uschar * buf, unsigned start, unsigned end) +{ +debug_printf("PROXY<<"); +while (start < end) debug_printf(" %02x", buf[start++]); +debug_printf("\n"); +} + + /************************************************* * Setup host for proxy protocol * *************************************************/ @@ -1263,11 +1273,11 @@ So to safely handle v1 and v2, with client-sent-first supported correctly, we have to do a minimum of 3 read calls, not 1. Eww. */ -#define PROXY_INITIAL_READ 14 -#define PROXY_V2_HEADER_SIZE 16 -#if PROXY_INITIAL_READ > PROXY_V2_HEADER_SIZE -# error Code bug in sizes of data to read for proxy usage -#endif +# define PROXY_INITIAL_READ 14 +# define PROXY_V2_HEADER_SIZE 16 +# if PROXY_INITIAL_READ > PROXY_V2_HEADER_SIZE +# error Code bug in sizes of data to read for proxy usage +# endif int get_ok = 0; int size, ret; @@ -1287,11 +1297,11 @@ do "safe". Can't take it all because TLS-on-connect clients follow immediately with TLS handshake. */ ret = read(fd, &hdr, PROXY_INITIAL_READ); - } - while (ret == -1 && errno == EINTR && !had_command_timeout); + } while (ret == -1 && errno == EINTR && !had_command_timeout); if (ret == -1) goto proxyfail; +DEBUG(D_receive) proxy_debug(US &hdr, 0, ret); /* For v2, handle reading the length, and then the rest. */ if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0)) @@ -1299,6 +1309,8 @@ if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0)) int retmore; uint8_t ver; + DEBUG(D_receive) debug_printf("v2\n"); + /* First get the length fields. */ do { @@ -1306,6 +1318,8 @@ if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0)) } while (retmore == -1 && errno == EINTR && !had_command_timeout); if (retmore == -1) goto proxyfail; + DEBUG(D_receive) proxy_debug(US &hdr, ret, ret + retmore); + ret += retmore; ver = (hdr.v2.ver_cmd & 0xf0) >> 4; @@ -1343,6 +1357,7 @@ if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0)) } while (retmore == -1 && errno == EINTR && !had_command_timeout); if (retmore == -1) goto proxyfail; + DEBUG(D_receive) proxy_debug(US &hdr, ret, ret + retmore); ret += retmore; DEBUG(D_receive) debug_printf("PROXYv2: have %d/%d required octets\n", ret, size); } while (ret < size); @@ -1588,7 +1603,7 @@ bad: ALARM(0); return; } -#endif +#endif /*SUPPORT_PROXY*/ /************************************************* * Read one command line * commit 42f1855e94bd87f98bc6c74255be53ed6d805ba6 Author: Jeremy Harris Date: Sat Jan 7 00:17:08 2023 +0000 OpenSSL: tls_eccurves list support. Bug 2955 diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 513ba0d3a..96be7c4a2 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -122,6 +122,7 @@ change this guard and punt the issue for a while longer. */ # define EXIM_HAVE_OPENSSL_CIPHER_STD_NAME # define EXIM_HAVE_EXP_CHNL_BNGNG # define EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_SIGNER +# define EXIM_HAVE_OPENSSL_SET1_GROUPS # else # define OPENSSL_BAD_SRVR_OURCERT # endif @@ -700,6 +701,41 @@ return TRUE; * Initialize for ECDH * *************************************************/ +/* "auto" needs to be handled carefully. +OpenSSL < 1.0.2: we do not select anything, but fallback to prime256v1 +OpenSSL < 1.1.0: we have to call SSL_CTX_set_ecdh_auto + (openssl/ssl.h defines SSL_CTRL_SET_ECDH_AUTO) +OpenSSL >= 1.1.0: we do not set anything, the libray does autoselection + https://github.com/openssl/openssl/commit/fe6ef2472db933f01b59cad82aa925736935984b + +*/ + +static uschar * +init_ecdh_auto(SSL_CTX * ctx) +{ +#if OPENSSL_VERSION_NUMBER < 0x10002000L +DEBUG(D_tls) debug_printf( + " ECDH OpenSSL < 1.0.2: temp key parameter settings: overriding \"auto\" with \"prime256v1\"\n"); +return US"prime256v1"; + +#else +# if defined SSL_CTRL_SET_ECDH_AUTO + +DEBUG(D_tls) debug_printf( + " ECDH OpenSSL 1.0.2+: temp key parameter settings: autoselection\n"); +SSL_CTX_set_ecdh_auto(sctx, 1); +return NULL; + +# else + +DEBUG(D_tls) debug_printf( + " ECDH OpenSSL 1.1.0+: temp key parameter settings: library default selection\n"); +return NULL; + +# endif +#endif +} + /* Load parameters for ECDH encryption. Server only. For now, we stick to NIST P-256 because: it's simple and easy to configure; @@ -730,72 +766,76 @@ init_ecdh(SSL_CTX * sctx, uschar ** errstr) return TRUE; #else -uschar * exp_curve; -int nid, rc; - # ifndef EXIM_HAVE_ECDH DEBUG(D_tls) debug_printf(" No OpenSSL API to define ECDH parameters, skipping\n"); return TRUE; # else +uschar * exp_curve; +int ngroups, rc, sep; +const uschar * curves_list, * curve; +# ifdef EXIM_HAVE_OPENSSL_SET1_GROUPS +int nids[16]; +# else +int nids[1]; +# endif + if (!expand_check(tls_eccurve, US"tls_eccurve", &exp_curve, errstr)) return FALSE; /* Is the option deliberately empty? */ if (!exp_curve || !*exp_curve) - { -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - DEBUG(D_tls) debug_printf( " ECDH OpenSSL 1.0.2+: clearing curves list\n"); - (void) SSL_CTX_set1_curves(sctx, &nid, 0); -#endif return TRUE; - } -/* "auto" needs to be handled carefully. - * OpenSSL < 1.0.2: we do not select anything, but fallback to prime256v1 - * OpenSSL < 1.1.0: we have to call SSL_CTX_set_ecdh_auto - * (openssl/ssl.h defines SSL_CTRL_SET_ECDH_AUTO) - * OpenSSL >= 1.1.0: we do not set anything, the libray does autoselection - * https://github.com/openssl/openssl/commit/fe6ef2472db933f01b59cad82aa925736935984b - */ -if (Ustrcmp(exp_curve, "auto") == 0) - { -#if OPENSSL_VERSION_NUMBER < 0x10002000L - DEBUG(D_tls) debug_printf( - " ECDH OpenSSL < 1.0.2: temp key parameter settings: overriding \"auto\" with \"prime256v1\"\n"); - exp_curve = US"prime256v1"; -#else -# if defined SSL_CTRL_SET_ECDH_AUTO - DEBUG(D_tls) debug_printf( - " ECDH OpenSSL 1.0.2+: temp key parameter settings: autoselection\n"); - SSL_CTX_set_ecdh_auto(sctx, 1); - return TRUE; -# else - DEBUG(D_tls) debug_printf( - " ECDH OpenSSL 1.1.0+: temp key parameter settings: library default selection\n"); - return TRUE; -# endif -#endif - } +/* Limit the list to hardwired array size. Drop out if any element is "suto". */ -if ( (nid = OBJ_sn2nid (CCS exp_curve)) == NID_undef -# ifdef EXIM_HAVE_OPENSSL_EC_NIST2NID - && (nid = EC_curve_nist2nid(CCS exp_curve)) == NID_undef -# endif - ) - { - uschar * s = string_sprintf("Unknown curve name tls_eccurve '%s'", exp_curve); - DEBUG(D_tls) debug_printf("TLS error '%s'\n", s); - if (errstr) *errstr = s; - return FALSE; - } +curves_list = exp_curve; +sep = 0; +for (ngroups = 0; + ngroups < nelem(nids) + && (curve = string_nextinlist(&curves_list, &sep, NULL, 0)); + ) + if (Ustrcmp(curve, "auto") == 0) + { + DEBUG(D_tls) if (ngroups > 0) + debug_printf(" tls_eccurve 'auto' item takes precedence\n"); + if ((exp_curve = init_ecdh_auto(sctx))) break; /* have a curve name to set */ + return TRUE; /* all done */ + } + else + ngroups++; -# if OPENSSL_VERSION_NUMBER < 0x30000000L +/* Translate to NIDs */ + +curves_list = exp_curve; +for (ngroups = 0; curve = string_nextinlist(&curves_list, &sep, NULL, 0); + ngroups++) + if ( (nids[ngroups] = OBJ_sn2nid (CCS curve)) == NID_undef +# ifdef EXIM_HAVE_OPENSSL_EC_NIST2NID + && (nids[ngroups] = EC_curve_nist2nid(CCS curve)) == NID_undef +# endif + ) + { + uschar * s = string_sprintf("Unknown curve name in tls_eccurve '%s'", curve); + DEBUG(D_tls) debug_printf("TLS error: %s\n", s); + if (errstr) *errstr = s; + return FALSE; + } + +# ifdef EXIM_HAVE_OPENSSL_SET1_GROUPS +/* Set the groups */ + +if ((rc = SSL_CTX_set1_groups(sctx, nids, ngroups)) == 0) + tls_error(string_sprintf("Error enabling '%s' group(s)", exp_curve), NULL, NULL, errstr); +else + DEBUG(D_tls) debug_printf(" ECDH: enabled '%s' group(s)\n", exp_curve); + +# else /* Cannot handle a list; only 1 element nids array */ { EC_KEY * ecdh; - if (!(ecdh = EC_KEY_new_by_curve_name(nid))) + if (!(ecdh = EC_KEY_new_by_curve_name(nids[0]))) { tls_error(US"Unable to create ec curve", NULL, NULL, errstr); return FALSE; @@ -810,15 +850,7 @@ if ( (nid = OBJ_sn2nid (CCS exp_curve)) == NID_undef DEBUG(D_tls) debug_printf(" ECDH: enabled '%s' curve\n", exp_curve); EC_KEY_free(ecdh); } - -#else /* v 3.0.0 + */ - -if ((rc = SSL_CTX_set1_groups(sctx, &nid, 1)) == 0) - tls_error(string_sprintf("Error enabling '%s' group", exp_curve), NULL, NULL, errstr); -else - DEBUG(D_tls) debug_printf(" ECDH: enabled '%s' group\n", exp_curve); - -#endif +# endif /*!EXIM_HAVE_OPENSSL_SET1_GROUPS*/ return !!rc; diff --git a/src/src/tls.c b/src/src/tls.c index 4a23aaae9..f7be5293d 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -106,7 +106,8 @@ Returns: TRUE if OK; result may still be NULL after forced failure */ static BOOL -expand_check(const uschar *s, const uschar *name, uschar **result, uschar ** errstr) +expand_check(const uschar * s, const uschar * name, + uschar ** result, uschar ** errstr) { if (!s) *result = NULL; commit fb09cfc3f2b667aa09deef8a0f9933a2e710be8f Author: Jeremy Harris Date: Sat Jan 7 16:15:46 2023 +0000 OpenSSL: fix build for pre-1.1.0 Broken-by: 42f1855e94bd diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 96be7c4a2..10b5f2aa5 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -711,7 +711,7 @@ OpenSSL >= 1.1.0: we do not set anything, the libray does autoselection */ static uschar * -init_ecdh_auto(SSL_CTX * ctx) +init_ecdh_auto(SSL_CTX * sctx) { #if OPENSSL_VERSION_NUMBER < 0x10002000L DEBUG(D_tls) debug_printf( commit d23b36cd691de33b709eae04f6f100272a8081eb Author: Jeremy Harris Date: Sun Jan 15 22:08:48 2023 +0000 tidying diff --git a/src/src/expand.c b/src/src/expand.c index 62b4a1890..2949579c5 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -23,16 +23,18 @@ typedef unsigned esi_flags; #define ESI_HONOR_DOLLAR BIT(1) /* $ is meaningfull */ #define ESI_SKIPPING BIT(2) /* value will not be needed */ +#ifdef STAND_ALONE +# ifndef SUPPORT_CRYPTEQ +# define SUPPORT_CRYPTEQ +# endif +#else + /* Recursively called function */ static uschar *expand_string_internal(const uschar *, esi_flags, const uschar **, BOOL *, BOOL *); static int_eximarith_t expanded_string_integer(const uschar *, BOOL); -#ifdef STAND_ALONE -# ifndef SUPPORT_CRYPTEQ -# define SUPPORT_CRYPTEQ -# endif -#endif +#endif /*!STAND_ALONE*/ #ifdef LOOKUP_LDAP # include "lookups/ldap.h" @@ -835,8 +837,6 @@ static var_entry var_table[] = { { "warnmsg_recipients", vtype_stringptr, &warnmsg_recipients } }; -static int var_table_size = nelem(var_table); - #ifdef MACRO_PREDEF /* dummies */ @@ -1278,7 +1278,7 @@ static var_entry * find_var_ent(uschar * name) { int first = 0; -int last = var_table_size; +int last = nelem(var_table); while (last > first) { @@ -8806,7 +8806,7 @@ for (int i = 0; i < REGEX_VARS; i++) if (regex_vars[i]) #endif /* check known-name variables */ -for (var_entry * v = var_table; v < var_table + var_table_size; v++) +for (var_entry * v = var_table; v < var_table + nelem(var_table); v++) if (v->type == vtype_stringptr) assert_variable_notin(US v->name, *(USS v->value), &e); diff --git a/src/src/filter.c b/src/src/filter.c index 530d772b3..d878acb8f 100644 --- a/src/src/filter.c +++ b/src/src/filter.c @@ -671,8 +671,8 @@ for (;;) { // if (toplevel) *saveptr = 0; // else - if (!toplevel) - *error_pointer = string_sprintf("missing \")\" at end of " + if (!toplevel) + *error_pointer = string_sprintf("missing \")\" at end of " "condition near line %d of filter file", line_number); break; } diff --git a/src/src/priv.c b/src/src/priv.c index 3a100cd9e..9305f8b45 100644 --- a/src/src/priv.c +++ b/src/src/priv.c @@ -1,4 +1,4 @@ -/* Copyright (c) The Exim Maintainers 2022 * +/* Copyright (c) The Exim Maintainers 2022 */ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "exim.h" diff --git a/src/src/regex_cache.c b/src/src/regex_cache.c index a9b482174..1ca3c96d5 100644 --- a/src/src/regex_cache.c +++ b/src/src/regex_cache.c @@ -239,7 +239,7 @@ regex_at_daemon(const uschar * reqbuf) { const re_req * req = (const re_req *)reqbuf; uschar * errstr; -const pcre2_code * cre; +const pcre2_code * cre = NULL; if (regex_cachesize >= REGEX_CACHESIZE_LIMIT) errstr = US"regex cache size limit reached"; diff --git a/src/src/spool_in.c b/src/src/spool_in.c index e785f695b..1291197de 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -496,15 +496,18 @@ for (;;) if (*var == '(') /* marker for quoted value */ { uschar * s; - int idx; for (s = ++var; *s != ')'; ) s++; #ifndef COMPILE_UTILITY - if ((idx = search_findtype(var, s - var)) < 0) { - DEBUG(D_any) debug_printf("Unrecognised quoter %.*s\n", (int)(s - var), var+1); - goto SPOOL_FORMAT_ERROR; + int idx; + if ((idx = search_findtype(var, s - var)) < 0) + { + DEBUG(D_any) + debug_printf("Unrecognised quoter %.*s\n", (int)(s - var), var+1); + goto SPOOL_FORMAT_ERROR; + } + proto_mem = store_get_quoted(1, GET_TAINTED, idx); } - proto_mem = store_get_quoted(1, GET_TAINTED, idx); #endif /* COMPILE_UTILITY */ var = s + 1; } diff --git a/src/src/tls.c b/src/src/tls.c index f7be5293d..ba7c2de38 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -26,11 +26,6 @@ functions from the OpenSSL or GNU TLS libraries. */ #endif -/* Forward decl. */ -static void tls_client_resmption_key(tls_support *, smtp_connect_args *, - smtp_transport_options_block *); - - #if defined(MACRO_PREDEF) && !defined(DISABLE_TLS) # include "macro_predef.h" # ifdef USE_GNUTLS @@ -459,6 +454,10 @@ tzset(); /************************************************* * Many functions are package-specific * *************************************************/ +/* Forward decl. */ +static void tls_client_resmption_key(tls_support *, smtp_connect_args *, + smtp_transport_options_block *); + #ifdef USE_GNUTLS # include "tls-gnu.c" commit aae673d7db4b26e8c4a8cc3d59fe94de4c47ba16 Author: Jeremy Harris Date: Wed Jan 18 11:34:07 2023 +0000 Logging: Add "D=" to more connection closure log lines. Bug 2434 diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 5d8ffd3d0..04b20d27c 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -333,7 +333,7 @@ Returns: nothing */ static void -incomplete_transaction_log(uschar *what) +incomplete_transaction_log(uschar * what) { if (!sender_address /* No transaction in progress */ || !LOGGING(smtp_incomplete_transaction)) @@ -355,13 +355,21 @@ log_write(L_smtp_incomplete_transaction, LOG_MAIN|LOG_SENDER|LOG_RECIPIENTS, +static void +log_close_event(const uschar * reason) +{ +log_write(L_smtp_connection, LOG_MAIN, "%s D=%s closed %s", + smtp_get_connection_info(), string_timesince(&smtp_connection_start), reason); +} + void smtp_command_timeout_exit(void) { log_write(L_lost_incoming_connection, - LOG_MAIN, "SMTP command timeout on%s connection from %s", - tls_in.active.sock >= 0 ? " TLS" : "", host_and_ident(FALSE)); + LOG_MAIN, "SMTP command timeout on%s connection from %s D=%s", + tls_in.active.sock >= 0 ? " TLS" : "", host_and_ident(FALSE), + string_timesince(&smtp_connection_start)); if (smtp_batched_input) moan_smtp_batch(NULL, "421 SMTP command timeout"); /* Does not return */ smtp_notquit_exit(US"command-timeout", US"421", @@ -373,7 +381,7 @@ exim_exit(EXIT_FAILURE); void smtp_command_sigterm_exit(void) { -log_write(0, LOG_MAIN, "%s closed after SIGTERM", smtp_get_connection_info()); +log_close_event(US"after SIGTERM"); if (smtp_batched_input) moan_smtp_batch(NULL, "421 SIGTERM received"); /* Does not return */ smtp_notquit_exit(US"signal-exit", US"421", @@ -384,9 +392,10 @@ exim_exit(EXIT_FAILURE); void smtp_data_timeout_exit(void) { -log_write(L_lost_incoming_connection, - LOG_MAIN, "SMTP data timeout (message abandoned) on connection from %s F=<%s>", - sender_fullhost ? sender_fullhost : US"local process", sender_address); +log_write(L_lost_incoming_connection, LOG_MAIN, + "SMTP data timeout (message abandoned) on connection from %s F=<%s> D=%s", + sender_fullhost ? sender_fullhost : US"local process", sender_address, + string_timesince(&smtp_connection_start)); receive_bomb_out(US"data-timeout", US"SMTP incoming data timeout"); /* Does not return */ } @@ -394,8 +403,7 @@ receive_bomb_out(US"data-timeout", US"SMTP incoming data timeout"); void smtp_data_sigint_exit(void) { -log_write(0, LOG_MAIN, "%s closed after %s", - smtp_get_connection_info(), had_data_sigint == SIGTERM ? "SIGTERM":"SIGINT"); +log_close_event(had_data_sigint == SIGTERM ? US"SIGTERM":US"SIGINT"); receive_bomb_out(US"signal-exit", US"Service not available - SIGTERM or SIGINT received"); /* Does not return */ @@ -3572,8 +3580,7 @@ if (log_reject_target != 0) if (!drop) return 0; -log_write(L_smtp_connection, LOG_MAIN, "%s closed by DROP in ACL", - smtp_get_connection_info()); +log_close_event(US"by DROP in ACL"); /* Run the not-quit ACL, but without any custom messages. This should not be a problem, because we get here only if some other ACL has issued "drop", and @@ -3999,16 +4006,14 @@ else tls_close(NULL, TLS_SHUTDOWN_NOWAIT); # endif -log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT", - smtp_get_connection_info()); +log_close_event(US"by QUIT"); #else # ifndef DISABLE_TLS tls_close(NULL, TLS_SHUTDOWN_WAIT); # endif -log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT", - smtp_get_connection_info()); +log_close_event(US"by QUIT"); /* Pause, hoping client will FIN first so that they get the TIME_WAIT. The socket should become readble (though with no data) */ @@ -5763,8 +5768,7 @@ while (done <= 0) while (done <= 0) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED)) { case EOF_CMD: - log_write(L_smtp_connection, LOG_MAIN, "%s closed by EOF", - smtp_get_connection_info()); + log_close_event(US"by EOF"); smtp_notquit_exit(US"tls-failed", NULL, NULL); done = 2; break; @@ -5786,8 +5790,7 @@ while (done <= 0) smtp_respond(US"221", 3, TRUE, user_msg); else smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname); - log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT", - smtp_get_connection_info()); + log_close_event(US"by QUIT"); done = 2; break; commit f108ec74c278b14363dc9ad1274596f284d21868 Author: Jeremy Harris Date: Fri Jan 27 10:31:11 2023 +0000 Support use-but-not-create of notifier socket diff --git a/src/src/daemon.c b/src/src/daemon.c index 05d94b188..c3ab735d0 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -1169,7 +1169,7 @@ const uschar * where; struct sockaddr_un sa_un = {.sun_family = AF_UNIX}; ssize_t len; -if (!notifier_socket || !*notifier_socket) +if (!f.notifier_socket_en) { DEBUG(D_any) debug_printf("-oY used so not creating notifier socket\n"); return; @@ -1180,6 +1180,11 @@ if (override_local_interfaces && !override_pid_file_path) debug_printf("-oX used without -oP so not creating notifier socket\n"); return; } +if (!notifier_socket || !*notifier_socket) + { + DEBUG(D_any) debug_printf("no name for notifier socket\n"); + return; + } DEBUG(D_any) debug_printf("creating notifier socket\n"); diff --git a/src/src/exim.c b/src/src/exim.c index dc082f392..9072ffd54 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -3450,7 +3450,7 @@ on the second character (the one after '-'), to save some effort. */ case 'Y': if (*argrest) badarg = TRUE; - else notifier_socket = NULL; + else f.notifier_socket_en = FALSE; break; /* Unknown -o argument */ diff --git a/src/src/globals.c b/src/src/globals.c index efe34902a..7af345465 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -280,6 +280,7 @@ struct global_flags f = .no_mbox_unspool = FALSE, #endif .no_multiline_responses = FALSE, + .notifier_socket_en = TRUE, .parse_allow_group = FALSE, .parse_found_group = FALSE, diff --git a/src/src/globals.h b/src/src/globals.h index 5aae73fba..f2e147670 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -246,6 +246,7 @@ extern struct global_flags { BOOL no_mbox_unspool :1; /* don't unlink files in /scan directory */ #endif BOOL no_multiline_responses :1; /* For broken clients */ + BOOL notifier_socket_en :1; /* Permit create of notifier socket */ BOOL parse_allow_group :1; /* Allow group syntax */ BOOL parse_found_group :1; /* In the middle of a group */ commit dbb05434c6730ae4a13d5879f6df652d93cd6019 Author: Jeremy Harris Date: Fri Jan 27 20:02:58 2023 +0000 Docs: add note on daemon shutdown diff --git a/src/src/exim.c b/src/src/exim.c index 9072ffd54..c5de167c6 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1923,6 +1923,7 @@ signal(SIGSEGV, segv_handler); /* log faults */ /* If running in a dockerized environment, the TERM signal is only delegated to the PID 1 if we request it by setting an signal handler */ + if (getpid() == 1) signal(SIGTERM, term_handler); /* SIGHUP is used to get the daemon to reconfigure. It gets set as appropriate commit 36afd696f0000b50e9c14ad61c96b46fb3d68cd0 Author: Jeremy Harris Date: Sat Jan 28 17:05:43 2023 +0000 refactor diff --git a/src/src/daemon.c b/src/src/daemon.c index c3ab735d0..7666626ed 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -1348,6 +1348,184 @@ return FALSE; +static void +daemon_inetd_wtimeout(time_t last_connection_time) +{ +time_t resignal_interval = inetd_wait_timeout; + +if (last_connection_time == (time_t)0) + { + DEBUG(D_any) + debug_printf("inetd wait timeout expired, but still not seen first message, ignoring\n"); + } +else + { + time_t now = time(NULL); + if (now == (time_t)-1) + { + DEBUG(D_any) debug_printf("failed to get time: %s\n", strerror(errno)); + } + else if ((now - last_connection_time) >= inetd_wait_timeout) + { + DEBUG(D_any) + debug_printf("inetd wait timeout %d expired, ending daemon\n", + inetd_wait_timeout); + log_write(0, LOG_MAIN, "exim %s daemon terminating, inetd wait timeout reached.\n", + version_string); + daemon_die(); /* Does not return */ + } + else + resignal_interval -= (now - last_connection_time); + } + +sigalrm_seen = FALSE; +ALARM(resignal_interval); +} + + +static void +daemon_qrun(int local_queue_run_max, struct pollfd * fd_polls, int listen_socket_count) +{ +DEBUG(D_any) debug_printf("%s received\n", +#ifndef DISABLE_QUEUE_RAMP + *queuerun_msgid ? "qrun notification" : +#endif + "SIGALRM"); + +/* Do a full queue run in a child process, if required, unless we already +have enough queue runners on the go. If we are not running as root, a +re-exec is required. */ + +if ( queue_interval > 0 + && (local_queue_run_max <= 0 || queue_run_count < local_queue_run_max)) + { +pid_t pid; + + if ((pid = exim_fork(US"queue-runner")) == 0) + { + /* Disable debugging if it's required only for the daemon process. We + leave the above message, because it ties up with the "child ended" + debugging messages. */ + + if (f.debug_daemon) debug_selector = 0; + + /* Close any open listening sockets in the child */ + + close_daemon_sockets(daemon_notifier_fd, + fd_polls, listen_socket_count); + + /* Reset SIGHUP and SIGCHLD in the child in both cases. */ + + signal(SIGHUP, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGINT, SIG_DFL); + + /* Re-exec if privilege has been given up, unless deliver_drop_ + privilege is set. Reset SIGALRM before exec(). */ + + if (geteuid() != root_uid && !deliver_drop_privilege) + { + uschar opt[8]; + uschar *p = opt; + uschar *extra[7]; + int extracount = 1; + + signal(SIGALRM, SIG_DFL); + *p++ = '-'; + *p++ = 'q'; + if ( f.queue_2stage +#ifndef DISABLE_QUEUE_RAMP + && !*queuerun_msgid +#endif + ) *p++ = 'q'; + if (f.queue_run_first_delivery) *p++ = 'i'; + if (f.queue_run_force) *p++ = 'f'; + if (f.deliver_force_thaw) *p++ = 'f'; + if (f.queue_run_local) *p++ = 'l'; + *p = 0; + extra[0] = *queue_name + ? string_sprintf("%sG%s", opt, queue_name) : opt; + +#ifndef DISABLE_QUEUE_RAMP + if (*queuerun_msgid) + { + log_write(0, LOG_MAIN, "notify triggered queue run"); + extra[extracount++] = queuerun_msgid; /* Trigger only the */ + extra[extracount++] = queuerun_msgid; /* one message */ + } +#endif + + /* If -R or -S were on the original command line, ensure they get + passed on. */ + + if (deliver_selectstring) + { + extra[extracount++] = f.deliver_selectstring_regex ? US"-Rr" : US"-R"; + extra[extracount++] = deliver_selectstring; + } + + if (deliver_selectstring_sender) + { + extra[extracount++] = f.deliver_selectstring_sender_regex + ? US"-Sr" : US"-S"; + extra[extracount++] = deliver_selectstring_sender; + } + + /* Overlay this process with a new execution. */ + + (void)child_exec_exim(CEE_EXEC_PANIC, FALSE, NULL, FALSE, extracount, + extra[0], extra[1], extra[2], extra[3], extra[4], extra[5], extra[6]); + + /* Control never returns here. */ + } + + /* No need to re-exec; SIGALRM remains set to the default handler */ + +#ifndef DISABLE_QUEUE_RAMP + if (*queuerun_msgid) + { + log_write(0, LOG_MAIN, "notify triggered queue run"); + f.queue_2stage = FALSE; + queue_run(queuerun_msgid, queuerun_msgid, FALSE); + } + else +#endif + queue_run(NULL, NULL, FALSE); + exim_underbar_exit(EXIT_SUCCESS); + } + + if (pid < 0) + { + log_write(0, LOG_MAIN|LOG_PANIC, "daemon: fork of queue-runner " + "process failed: %s", strerror(errno)); + log_close_all(); + } + else + { + for (int i = 0; i < local_queue_run_max; ++i) + if (queue_pid_slots[i] <= 0) + { + queue_pid_slots[i] = pid; + queue_run_count++; + break; + } + DEBUG(D_any) debug_printf("%d queue-runner process%s running\n", + queue_run_count, queue_run_count == 1 ? "" : "es"); + } + } + +/* Reset the alarm clock */ + +sigalrm_seen = FALSE; +#ifndef DISABLE_QUEUE_RAMP +if (*queuerun_msgid) + *queuerun_msgid = 0; +else +#endif + ALARM(queue_interval); +} + /************************************************* * Exim Daemon Mainline * *************************************************/ @@ -2267,8 +2445,6 @@ report_time_since(×tamp_startup, US"daemon loop start"); /* testcase 0022 * for (;;) { - pid_t pid; - if (sigterm_seen) daemon_die(); /* Does not return */ @@ -2279,186 +2455,10 @@ for (;;) The other option is that we have an inetd wait timeout specified to -bw. */ if (sigalrm_seen) - { if (inetd_wait_timeout > 0) - { - time_t resignal_interval = inetd_wait_timeout; - - if (last_connection_time == (time_t)0) - { - DEBUG(D_any) - debug_printf("inetd wait timeout expired, but still not seen first message, ignoring\n"); - } - else - { - time_t now = time(NULL); - if (now == (time_t)-1) - { - DEBUG(D_any) debug_printf("failed to get time: %s\n", strerror(errno)); - } - else - { - if ((now - last_connection_time) >= inetd_wait_timeout) - { - DEBUG(D_any) - debug_printf("inetd wait timeout %d expired, ending daemon\n", - inetd_wait_timeout); - log_write(0, LOG_MAIN, "exim %s daemon terminating, inetd wait timeout reached.\n", - version_string); - exit(EXIT_SUCCESS); - } - else - { - resignal_interval -= (now - last_connection_time); - } - } - } - - sigalrm_seen = FALSE; - ALARM(resignal_interval); - } - + daemon_inetd_wtimeout(last_connection_time); /* Might not return */ else - { - DEBUG(D_any) debug_printf("%s received\n", -#ifndef DISABLE_QUEUE_RAMP - *queuerun_msgid ? "qrun notification" : -#endif - "SIGALRM"); - - /* Do a full queue run in a child process, if required, unless we already - have enough queue runners on the go. If we are not running as root, a - re-exec is required. */ - - if ( queue_interval > 0 - && (local_queue_run_max <= 0 || queue_run_count < local_queue_run_max)) - { - if ((pid = exim_fork(US"queue-runner")) == 0) - { - /* Disable debugging if it's required only for the daemon process. We - leave the above message, because it ties up with the "child ended" - debugging messages. */ - - if (f.debug_daemon) debug_selector = 0; - - /* Close any open listening sockets in the child */ - - close_daemon_sockets(daemon_notifier_fd, - fd_polls, listen_socket_count); - - /* Reset SIGHUP and SIGCHLD in the child in both cases. */ - - signal(SIGHUP, SIG_DFL); - signal(SIGCHLD, SIG_DFL); - signal(SIGTERM, SIG_DFL); - signal(SIGINT, SIG_DFL); - - /* Re-exec if privilege has been given up, unless deliver_drop_ - privilege is set. Reset SIGALRM before exec(). */ - - if (geteuid() != root_uid && !deliver_drop_privilege) - { - uschar opt[8]; - uschar *p = opt; - uschar *extra[7]; - int extracount = 1; - - signal(SIGALRM, SIG_DFL); - *p++ = '-'; - *p++ = 'q'; - if ( f.queue_2stage -#ifndef DISABLE_QUEUE_RAMP - && !*queuerun_msgid -#endif - ) *p++ = 'q'; - if (f.queue_run_first_delivery) *p++ = 'i'; - if (f.queue_run_force) *p++ = 'f'; - if (f.deliver_force_thaw) *p++ = 'f'; - if (f.queue_run_local) *p++ = 'l'; - *p = 0; - extra[0] = *queue_name - ? string_sprintf("%sG%s", opt, queue_name) : opt; - -#ifndef DISABLE_QUEUE_RAMP - if (*queuerun_msgid) - { - log_write(0, LOG_MAIN, "notify triggered queue run"); - extra[extracount++] = queuerun_msgid; /* Trigger only the */ - extra[extracount++] = queuerun_msgid; /* one message */ - } -#endif - - /* If -R or -S were on the original command line, ensure they get - passed on. */ - - if (deliver_selectstring) - { - extra[extracount++] = f.deliver_selectstring_regex ? US"-Rr" : US"-R"; - extra[extracount++] = deliver_selectstring; - } - - if (deliver_selectstring_sender) - { - extra[extracount++] = f.deliver_selectstring_sender_regex - ? US"-Sr" : US"-S"; - extra[extracount++] = deliver_selectstring_sender; - } - - /* Overlay this process with a new execution. */ - - (void)child_exec_exim(CEE_EXEC_PANIC, FALSE, NULL, FALSE, extracount, - extra[0], extra[1], extra[2], extra[3], extra[4], extra[5], extra[6]); - - /* Control never returns here. */ - } - - /* No need to re-exec; SIGALRM remains set to the default handler */ - -#ifndef DISABLE_QUEUE_RAMP - if (*queuerun_msgid) - { - log_write(0, LOG_MAIN, "notify triggered queue run"); - f.queue_2stage = FALSE; - queue_run(queuerun_msgid, queuerun_msgid, FALSE); - } - else -#endif - queue_run(NULL, NULL, FALSE); - exim_underbar_exit(EXIT_SUCCESS); - } - - if (pid < 0) - { - log_write(0, LOG_MAIN|LOG_PANIC, "daemon: fork of queue-runner " - "process failed: %s", strerror(errno)); - log_close_all(); - } - else - { - for (int i = 0; i < local_queue_run_max; ++i) - if (queue_pid_slots[i] <= 0) - { - queue_pid_slots[i] = pid; - queue_run_count++; - break; - } - DEBUG(D_any) debug_printf("%d queue-runner process%s running\n", - queue_run_count, queue_run_count == 1 ? "" : "es"); - } - } - - /* Reset the alarm clock */ - - sigalrm_seen = FALSE; -#ifndef DISABLE_QUEUE_RAMP - if (*queuerun_msgid) - *queuerun_msgid = 0; - else -#endif - ALARM(queue_interval); - } - - } /* sigalrm_seen */ + daemon_qrun(local_queue_run_max, fd_polls, listen_socket_count); /* Sleep till a connection happens if listening, and handle the connection if commit 9ee30919f807678b0bc9f675dcfa73225b486574 Author: Jeremy Harris Date: Sun Jan 29 21:31:27 2023 +0000 Debug: include variable content as expansion interim item diff --git a/src/src/expand.c b/src/src/expand.c index 2949579c5..10f009ce2 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -4733,6 +4733,7 @@ while (*s) reset in the middle of the buffer will make it inaccessible. */ len = Ustrlen(value); + DEBUG(D_expand) debug_expansion_interim(US"value", value, len, !!(flags & ESI_SKIPPING)); if (!yield && newsize != 0) { yield = g; @@ -4751,7 +4752,10 @@ while (*s) int n; s = read_cnumber(&n, s); if (n >= 0 && n <= expand_nmax) + { + DEBUG(D_expand) debug_expansion_interim(US"value", expand_nstring[n], expand_nlength[n], !!(flags & ESI_SKIPPING)); yield = string_catn(yield, expand_nstring[n], expand_nlength[n]); + } continue; } @@ -4776,7 +4780,10 @@ while (*s) goto EXPAND_FAILED; } if (n >= 0 && n <= expand_nmax) + { + DEBUG(D_expand) debug_expansion_interim(US"value", expand_nstring[n], expand_nlength[n], !!(flags & ESI_SKIPPING)); yield = string_catn(yield, expand_nstring[n], expand_nlength[n]); + } continue; } commit 04e5caa9a7e84b2afca642d28096d988cb6802e7 Author: Jeremy Harris Date: Mon Jan 30 13:31:40 2023 +0000 Testsuite: dovecot suthenticator testing diff --git a/src/src/auths/check_serv_cond.c b/src/src/auths/check_serv_cond.c index 5201d2177..1b0430ab1 100644 --- a/src/src/auths/check_serv_cond.c +++ b/src/src/auths/check_serv_cond.c @@ -31,9 +31,9 @@ Returns: */ int -auth_check_serv_cond(auth_instance *ablock) +auth_check_serv_cond(auth_instance * ablock) { - return auth_check_some_cond(ablock, +return auth_check_some_cond(ablock, US"server_condition", ablock->server_condition, OK); } @@ -58,10 +58,10 @@ Returns: */ int -auth_check_some_cond(auth_instance *ablock, - uschar *label, uschar *condition, int unset) +auth_check_some_cond(auth_instance * ablock, + uschar * label, uschar * condition, int unset) { -uschar *cond; +uschar * cond; HDEBUG(D_auth) { diff --git a/src/src/auths/dovecot.c b/src/src/auths/dovecot.c index ed56ab8cd..85d029c9c 100644 --- a/src/src/auths/dovecot.c +++ b/src/src/auths/dovecot.c @@ -97,13 +97,15 @@ static int socket_buffer_left; enable consistency checks to be done, or anything else that needs to be set up. */ -void auth_dovecot_init(auth_instance *ablock) +void +auth_dovecot_init(auth_instance * ablock) { -auth_dovecot_options_block *ob = +auth_dovecot_options_block * ob = (auth_dovecot_options_block *)(ablock->options_block); if (!ablock->public_name) ablock->public_name = ablock->name; if (ob->server_socket) ablock->server = TRUE; +else DEBUG(D_auth) debug_printf("Dovecot auth driver: no server_socket for %s\n", ablock->public_name); ablock->client = FALSE; } @@ -304,16 +306,14 @@ auth_defer_msg = US"authentication socket protocol error"; socket_buffer_left = 0; /* Global, used to read more than a line but return by line */ for (;;) { -debug_printf("%s %d\n", __FUNCTION__, __LINE__); if (!dc_gets(buffer, sizeof(buffer), &cctx)) OUT("authentication socket read error or premature eof"); -debug_printf("%s %d\n", __FUNCTION__, __LINE__); p = buffer + Ustrlen(buffer) - 1; if (*p != '\n') OUT("authentication socket protocol line too long"); *p = '\0'; - HDEBUG(D_auth) debug_printf("received: '%s'\n", buffer); + HDEBUG(D_auth) debug_printf(" DOVECOT<< '%s'\n", buffer); nargs = strcut(buffer, args, nelem(args)); @@ -423,12 +423,12 @@ if (( HDEBUG(D_auth) debug_printf("error sending auth_command: %s\n", strerror(errno)); -HDEBUG(D_auth) debug_printf("sent: '%s'\n", auth_command); +HDEBUG(D_auth) debug_printf(" DOVECOT>> '%s'\n", auth_command); while (1) { - uschar *temp; - uschar *auth_id_pre = NULL; + uschar * temp; + uschar * auth_id_pre = NULL; if (!dc_gets(buffer, sizeof(buffer), &cctx)) { @@ -437,7 +437,7 @@ while (1) } buffer[Ustrlen(buffer) - 1] = 0; - HDEBUG(D_auth) debug_printf("received: '%s'\n", buffer); + HDEBUG(D_auth) debug_printf(" DOVECOT<< '%s'\n", buffer); nargs = strcut(buffer, args, nelem(args)); HDEBUG(D_auth) debug_strcut(args, nargs, nelem(args)); @@ -471,6 +471,8 @@ while (1) #endif write(cctx.sock, temp, Ustrlen(temp))) < 0) OUT("authentication socket write error"); + + HDEBUG(D_auth) debug_printf(" DOVECOT>> '%s'\n", temp); break; case 'F': @@ -524,7 +526,10 @@ if (cctx.sock >= 0) close(cctx.sock); /* Expand server_condition as an authorization check */ -return ret == OK ? auth_check_serv_cond(ablock) : ret; +if (ret == OK) ret = auth_check_serv_cond(ablock); + +HDEBUG(D_auth) debug_printf("dovecot auth ret: %s\n", rc_names[ret]); +return ret; } diff --git a/src/src/auths/plaintext.c b/src/src/auths/plaintext.c index 6692a676e..1392b369f 100644 --- a/src/src/auths/plaintext.c +++ b/src/src/auths/plaintext.c @@ -62,9 +62,9 @@ auth_plaintext_init(auth_instance *ablock) { auth_plaintext_options_block *ob = (auth_plaintext_options_block *)(ablock->options_block); -if (ablock->public_name == NULL) ablock->public_name = ablock->name; -if (ablock->server_condition != NULL) ablock->server = TRUE; -if (ob->client_send != NULL) ablock->client = TRUE; +if (!ablock->public_name) ablock->public_name = ablock->name; +if (ablock->server_condition) ablock->server = TRUE; +if (ob->client_send) ablock->client = TRUE; } commit 70069b65a39a7ba73a36fbd95371ff03cde1eb23 Author: Jeremy Harris Date: Thu Feb 2 20:00:35 2023 +0000 Fix crash in expansions Broken-by: 1058096b8c53 diff --git a/src/src/expand.c b/src/src/expand.c index 10f009ce2..a7e6e4fb3 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -4747,7 +4747,7 @@ while (*s) continue; } - if (isdigit(*s)) + if (isdigit(*s)) /* A $ variable */ { int n; s = read_cnumber(&n, s); @@ -7165,6 +7165,7 @@ NOT_ITEM: ; /* Deal specially with operators that might take a certificate variable as we do not want to do the usual expansion. For most, expand the string.*/ + switch(c) { #ifndef DISABLE_TLS @@ -7213,7 +7214,7 @@ NOT_ITEM: ; to the main loop top. */ { - int start = yield->ptr; + unsigned expansion_start = gstring_length(yield); switch(c) { case EOP_BASE32: @@ -8275,8 +8276,8 @@ NOT_ITEM: ; DEBUG(D_expand) { - const uschar * s = yield->s + start; - int i = yield->ptr - start; + const uschar * s = yield->s + expansion_start; + int i = gstring_length(yield) - expansion_start; BOOL tainted = is_tainted(s); DEBUG(D_noutf8) commit 63deec8a3ba77fcabf405d9c30fdd65a8b909526 Author: Jeremy Harris Date: Sun Feb 5 16:04:14 2023 +0000 More abstraction of the gstring API diff --git a/src/src/acl.c b/src/src/acl.c index 5ab674776..17d6c68da 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -1092,7 +1092,7 @@ for (header_line * h = acl_added_headers; h; h = h->next) g = string_append_listele_n(g, '\n', h->text, i); } -return g ? g->s : NULL; +return string_from_gstring(g); } diff --git a/src/src/arc.c b/src/src/arc.c index 30a66320e..ef44672f8 100644 --- a/src/src/arc.c +++ b/src/src/arc.c @@ -261,8 +261,7 @@ while ((c = *s)) if (c != ' ' && c != '\t' && c != '\n' && c != '\r') g = string_catn(g, s, 1); if (!g) return US"no b= value"; - al->b.data = string_from_gstring(g); - al->b.len = g->ptr; + al->b.len = len_string_from_gstring(g, &al->b.data); gstring_release_unused(g); bend = s; break; @@ -278,8 +277,7 @@ while ((c = *s)) if (c != ' ' && c != '\t' && c != '\n' && c != '\r') g = string_catn(g, s, 1); if (!g) return US"no bh= value"; - al->bh.data = string_from_gstring(g); - al->bh.len = g->ptr; + al->bh.len = len_string_from_gstring(g, &al->bh.data); gstring_release_unused(g); break; default: @@ -1308,7 +1306,7 @@ header_line * h = (header_line *)(al+1); /* Construct the to-be-signed AMS pseudo-header: everything but the sig. */ -ams_off = g->ptr; +ams_off = gstring_length(g); g = string_fmt_append(g, "%s i=%d; a=rsa-sha256; c=relaxed; d=%s; s=%s", ARC_HDR_AMS, instance, identity, selector); /*XXX hardwired a= */ if (options & ARC_SIGN_OPT_TSTAMP) @@ -1352,7 +1350,7 @@ for(col = 3; rheaders; rheaders = rheaders->prev) /* Lose the last colon from the h= list */ -if (g->s[g->ptr - 1] == ':') g->ptr--; +gstring_trim_trailing(g, ':'); g = string_catn(g, US";\r\n\tb=;", 7); @@ -1370,7 +1368,7 @@ if (!arc_sig_from_pseudoheader(hdata, hashtype, privkey, &sig, US"AMS")) /* Lose the trailing semicolon from the psuedo-header, and append the signature (folded over lines) and termination to complete it. */ -g->ptr--; +gstring_trim(g, 1); g = arc_sign_append_sig(g, &sig); h->slen = g->ptr - ams_off; @@ -1548,7 +1546,7 @@ into the copies. static const uschar * arc_header_sign_feed(gstring * g) { -uschar * s = string_copyn(g->s, g->ptr); +uschar * s = string_copy_from_gstring(g); headers_rlist = arc_rlist_entry(headers_rlist, s, g->ptr); return arc_try_header(&arc_sign_ctx, headers_rlist->h, TRUE); } @@ -1772,10 +1770,9 @@ if (strncmpic(ARC_HDR_AMS, g->s, ARC_HDRLEN_AMS) != 0) return US"not AMS"; DEBUG(D_receive) debug_printf("ARC: spotted AMS header\n"); /* Parse the AMS header */ -h.next = NULL; -h.slen = g->size; -h.text = g->s; memset(&al, 0, sizeof(arc_line)); +h.next = NULL; +h.slen = len_string_from_gstring(g, &h.text); if ((errstr = arc_parse_line(&al, &h, ARC_HDRLEN_AMS, FALSE))) { DEBUG(D_acl) if (errstr) debug_printf("ARC: %s\n", errstr); @@ -1857,7 +1854,8 @@ for (as = arc_verify_ctx.arcset_chain, inst = 1; as; as = as->next, inst++) else g = string_catn(g, US":", 1); } -return g ? g->s : US""; +if (!g) return US""; +return string_from_gstring(g); } @@ -1870,7 +1868,7 @@ if (arc_state) { arc_line * highest_ams; int start = 0; /* Compiler quietening */ - DEBUG(D_acl) start = g->ptr; + DEBUG(D_acl) start = gstring_length(g); g = string_append(g, 2, US";\n\tarc=", arc_state); if (arc_received_instance > 0) @@ -1890,7 +1888,7 @@ if (arc_state) else if (arc_state_reason) g = string_append(g, 3, US" (", arc_state_reason, US")"); DEBUG(D_acl) debug_printf("ARC: authres '%.*s'\n", - g->ptr - start - 3, g->s + start + 3); + gstring_length(g) - start - 3, g->s + start + 3); } else DEBUG(D_acl) debug_printf("ARC: no authres\n"); diff --git a/src/src/deliver.c b/src/src/deliver.c index ca31df587..084a048c9 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -1045,7 +1045,7 @@ splitting is done; in those cases use the original field. */ else { uschar * cmp; - int off = g->ptr; /* start of the "full address" */ + int off = gstring_length(g); /* start of the "full address" */ if (addr->local_part) { @@ -1338,23 +1338,25 @@ if (LOGGING(deliver_time)) if (addr->message) g = string_append(g, 2, US": ", addr->message); -(void) string_from_gstring(g); + { + const uschar * s = string_from_gstring(g); -/* Log the deferment in the message log, but don't clutter it -up with retry-time defers after the first delivery attempt. */ + /* Log the deferment in the message log, but don't clutter it + up with retry-time defers after the first delivery attempt. */ -if (f.deliver_firsttime || addr->basic_errno > ERRNO_RETRY_BASE) - deliver_msglog("%s %s\n", now, g->s); + if (f.deliver_firsttime || addr->basic_errno > ERRNO_RETRY_BASE) + deliver_msglog("%s %s\n", now, s); -/* Write the main log and reset the store. -For errors of the type "retry time not reached" (also remotes skipped -on queue run), logging is controlled by L_retry_defer. Note that this kind -of error number is negative, and all the retry ones are less than any -others. */ + /* Write the main log and reset the store. + For errors of the type "retry time not reached" (also remotes skipped + on queue run), logging is controlled by L_retry_defer. Note that this kind + of error number is negative, and all the retry ones are less than any + others. */ -log_write(addr->basic_errno <= ERRNO_RETRY_BASE ? L_retry_defer : 0, logflags, - "== %s", g->s); + log_write(addr->basic_errno <= ERRNO_RETRY_BASE ? L_retry_defer : 0, logflags, + "== %s", s); + } store_reset(reset_point); return; @@ -1417,17 +1419,19 @@ if (addr->message) if (LOGGING(deliver_time)) g = string_append(g, 2, US" DT=", string_timediff(&addr->delivery_time)); -(void) string_from_gstring(g); - /* Do the logging. For the message log, "routing failed" for those cases, just to make it clearer. */ -if (driver_kind) - deliver_msglog("%s %s failed for %s\n", now, driver_kind, g->s); -else - deliver_msglog("%s %s\n", now, g->s); + { + const uschar * s = string_from_gstring(g); + + if (driver_kind) + deliver_msglog("%s %s failed for %s\n", now, driver_kind, s); + else + deliver_msglog("%s %s\n", now, s); -log_write(0, LOG_MAIN, "** %s", g->s); + log_write(0, LOG_MAIN, "** %s", s); + } store_reset(reset_point); return; diff --git a/src/src/dkim.c b/src/src/dkim.c index 0a8ab6fb3..4c19f752f 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -83,7 +83,7 @@ for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); return string_from_gstring(g); } - g->ptr = 0; /* overwrite previous record */ + gstring_reset(g); /* overwrite previous record */ } bad: @@ -822,7 +822,7 @@ authres_dkim(gstring * g) { int start = 0; /* compiler quietening */ -DEBUG(D_acl) start = g->ptr; +DEBUG(D_acl) start = gstring_length(g); for (pdkim_signature * sig = dkim_signatures; sig; sig = sig->next) { @@ -884,7 +884,7 @@ for (pdkim_signature * sig = dkim_signatures; sig; sig = sig->next) } DEBUG(D_acl) - if (g->ptr == start) + if (gstring_length(g) == start) debug_printf("DKIM: no authres\n"); else debug_printf("DKIM: authres '%.*s'\n", g->ptr - start - 3, g->s + start + 3); diff --git a/src/src/exim.c b/src/src/exim.c index c5de167c6..dcc71ea45 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -203,15 +203,16 @@ Returns: nothing */ void -set_process_info(const char *format, ...) +set_process_info(const char * format, ...) { gstring gs = { .size = PROCESS_INFO_SIZE - 2, .ptr = 0, .s = process_info }; gstring * g; int len; +uschar * s; va_list ap; g = string_fmt_append(&gs, "%5d ", (int)getpid()); -len = g->ptr; +len = gstring_length(g); va_start(ap, format); if (!string_vformat(g, 0, format, ap)) { @@ -219,8 +220,7 @@ if (!string_vformat(g, 0, format, ap)) g = string_cat(&gs, US"**** string overflowed buffer ****"); } g = string_catn(g, US"\n", 1); -string_from_gstring(g); -process_info_len = g->ptr; +process_info_len = len_string_from_gstring(g, &s); DEBUG(D_process_info) debug_printf("set_process_info: %s", process_info); va_end(ap); } @@ -1501,10 +1501,10 @@ for (int i = 0;; i++) #endif /* g can only be NULL if ss==p */ - if (ss == p || g->s[g->ptr-1] != '\\') /* not continuation; done */ + if (ss == p || gstring_last_char(g) != '\\') /* not continuation; done */ break; - --g->ptr; /* drop the \ */ + gstring_trim(g, 1); /* drop the \ */ } if (had_input) return g ? string_from_gstring(g) : US""; diff --git a/src/src/expand.c b/src/src/expand.c index a7e6e4fb3..1daf10044 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1667,12 +1667,13 @@ Returns: NULL if the header does not exist, else a pointer to a new */ static uschar * -find_header(uschar *name, int *newsize, unsigned flags, const uschar *charset) +find_header(uschar * name, int * newsize, unsigned flags, const uschar * charset) { BOOL found = !name; int len = name ? Ustrlen(name) : 0; BOOL comma = FALSE; gstring * g = NULL; +uschar * rawhdr; for (header_line * h = header_list; h; h = h->next) if (h->type != htype_old && h->text) /* NULL => Received: placeholder */ @@ -1735,8 +1736,9 @@ if (!g) return US""; /* That's all we do for raw header expansion. */ *newsize = g->size; +rawhdr = string_from_gstring(g); if (flags & FH_WANT_RAW) - return string_from_gstring(g); + return rawhdr; /* Otherwise do RFC 2047 decoding, translating the charset if requested. The rfc2047_decode2() function can return an error with decoded data if the @@ -1744,12 +1746,12 @@ charset translation fails. If decoding fails, it returns NULL. */ else { - uschar * error, * decoded = rfc2047_decode2(string_from_gstring(g), + uschar * error, * decoded = rfc2047_decode2(rawhdr, check_rfc2047_length, charset, '?', NULL, newsize, &error); if (error) DEBUG(D_any) debug_printf("*** error in RFC 2047 decoding: %s\n" - " input was: %s\n", error, g->s); - return decoded ? decoded : string_from_gstring(g); + " input was: %s\n", error, rawhdr); + return decoded ? decoded : rawhdr; } } @@ -1814,7 +1816,7 @@ for (int i = 0; i < recipients_count; i++) s = recipients_list[i].address; g = string_append2_listele_n(g, US", ", s, Ustrlen(s)); } -return g ? g->s : NULL; +return string_from_gstring(g); } @@ -4804,7 +4806,7 @@ while (*s) skipping, but "break" otherwise so we get debug output for the item expansion. */ { - int start = gstring_length(yield); + int expansion_start = gstring_length(yield); switch(item_type) { /* Call an ACL from an expansion. We feed data in via $acl_arg1 - $acl_arg9. @@ -4868,7 +4870,7 @@ while (*s) yield = string_append(yield, 3, US"Authentication-Results: ", sub_arg[0], US"; none"); - yield->ptr -= 6; + yield->ptr -= 6; /* ignore tha ": none" for now */ yield = authres_local(yield, sub_arg[0]); yield = authres_iprev(yield); @@ -5785,7 +5787,7 @@ while (*s) if (o2m >= 0) for (; oldptr < yield->ptr; oldptr++) { - uschar *m = Ustrrchr(sub[1], yield->s[oldptr]); + uschar * m = Ustrrchr(sub[1], yield->s[oldptr]); if (m) { int o = m - sub[1]; @@ -7107,7 +7109,7 @@ while (*s) it was for good reason */ if (quoted) yield = string_catn(yield, US"\"", 1); - yield = string_catn(yield, g->s, g->ptr); + yield = gstring_append(yield, g); if (quoted) yield = string_catn(yield, US"\"", 1); /* @$original_domain */ @@ -7126,10 +7128,11 @@ while (*s) } /* EITEM_* switch */ /*NOTREACHED*/ - DEBUG(D_expand) - if (yield && (start > 0 || *s)) /* only if not the sole expansion of the line */ + DEBUG(D_expand) /* only if not the sole expansion of the line */ + if (yield && (expansion_start > 0 || *s)) debug_expansion_interim(US"item-res", - yield->s + start, yield->ptr - start, !!(flags & ESI_SKIPPING)); + yield->s + expansion_start, yield->ptr - expansion_start, + !!(flags & ESI_SKIPPING)); continue; NOT_ITEM: ; @@ -7219,11 +7222,11 @@ NOT_ITEM: ; { case EOP_BASE32: { - uschar *t; + uschar * t; unsigned long int n = Ustrtoul(sub, &t, 10); gstring * g = NULL; - if (*t != 0) + if (*t) { expand_string_message = string_sprintf("argument for base32 " "operator is \"%s\", which is not a decimal number", sub); @@ -7832,16 +7835,19 @@ NOT_ITEM: ; case EOP_UTF8CLEAN: { - int seq_len = 0, index = 0; - int bytes_left = 0; + int seq_len = 0, index = 0, bytes_left = 0, complete; long codepoint = -1; - int complete; uschar seq_buff[4]; /* accumulate utf-8 here */ /* Manually track tainting, as we deal in individual chars below */ - if (!yield->s || !yield->ptr) + if (!yield) + yield = string_get_tainted(Ustrlen(sub), sub); + else if (!yield->s || !yield->ptr) + { yield->s = store_get(yield->size = Ustrlen(sub), sub); + gstring_reset(yield); + } else if (is_incompatible(yield->s, sub)) gstring_rebuffer(yield, sub); @@ -7967,7 +7973,7 @@ NOT_ITEM: ; goto EXPAND_FAILED; } yield = string_cat(yield, s); - DEBUG(D_expand) debug_printf_indent("yield: '%s'\n", yield->s); + DEBUG(D_expand) debug_printf_indent("yield: '%s'\n", string_from_gstring(yield)); break; } @@ -8276,7 +8282,8 @@ NOT_ITEM: ; DEBUG(D_expand) { - const uschar * s = yield->s + expansion_start; + const uschar * res = string_from_gstring(yield); + const uschar * s = res + expansion_start; int i = gstring_length(yield) - expansion_start; BOOL tainted = is_tainted(s); @@ -8286,7 +8293,7 @@ NOT_ITEM: ; if (tainted) { debug_printf_indent("%s \\__", flags & ESI_SKIPPING ? "| " : " "); - debug_print_taint(yield->s); + debug_print_taint(res); } } else @@ -8299,7 +8306,7 @@ NOT_ITEM: ; debug_printf_indent("%s", flags & ESI_SKIPPING ? UTF8_VERT " " : " " UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ); - debug_print_taint(yield->s); + debug_print_taint(res); } } } @@ -8374,58 +8381,62 @@ if (flags & ESI_BRACE_ENDS && !*s) added to the string. If so, set up an empty string. Add a terminating zero. If left != NULL, return a pointer to the terminator. */ -if (!yield) - yield = string_get(1); -(void) string_from_gstring(yield); -if (left) *left = s; + { + uschar * res; -/* Any stacking store that was used above the final string is no longer needed. -In many cases the final string will be the first one that was got and so there -will be optimal store usage. */ + if (!yield) + yield = string_get(1); + res = string_from_gstring(yield); + if (left) *left = s; -if (resetok) gstring_release_unused(yield); -else if (resetok_p) *resetok_p = FALSE; + /* Any stacking store that was used above the final string is no longer needed. + In many cases the final string will be the first one that was got and so there + will be optimal store usage. */ -DEBUG(D_expand) - { - BOOL tainted = is_tainted(yield->s); - DEBUG(D_noutf8) + if (resetok) gstring_release_unused(yield); + else if (resetok_p) *resetok_p = FALSE; + + DEBUG(D_expand) { - debug_printf_indent("|--expanding: %.*s\n", (int)(s - string), string); - debug_printf_indent("%sresult: %s\n", - flags & ESI_SKIPPING ? "|-----" : "\\_____", yield->s); - if (tainted) + BOOL tainted = is_tainted(res); + DEBUG(D_noutf8) { - debug_printf_indent("%s \\__", flags & ESI_SKIPPING ? "| " : " "); - debug_print_taint(yield->s); + debug_printf_indent("|--expanding: %.*s\n", (int)(s - string), string); + debug_printf_indent("%sresult: %s\n", + flags & ESI_SKIPPING ? "|-----" : "\\_____", res); + if (tainted) + { + debug_printf_indent("%s \\__", flags & ESI_SKIPPING ? "| " : " "); + debug_print_taint(res); + } + if (flags & ESI_SKIPPING) + debug_printf_indent("\\___skipping: result is not used\n"); } - if (flags & ESI_SKIPPING) - debug_printf_indent("\\___skipping: result is not used\n"); - } - else - { - debug_printf_indent(UTF8_VERT_RIGHT UTF8_HORIZ UTF8_HORIZ - "expanding: %.*s\n", - (int)(s - string), string); - debug_printf_indent("%s" UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ - "result: %s\n", - flags & ESI_SKIPPING ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT, - yield->s); - if (tainted) + else { - debug_printf_indent("%s", - flags & ESI_SKIPPING - ? UTF8_VERT " " : " " UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ); - debug_print_taint(yield->s); + debug_printf_indent(UTF8_VERT_RIGHT UTF8_HORIZ UTF8_HORIZ + "expanding: %.*s\n", + (int)(s - string), string); + debug_printf_indent("%s" UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ + "result: %s\n", + flags & ESI_SKIPPING ? UTF8_VERT_RIGHT : UTF8_UP_RIGHT, + res); + if (tainted) + { + debug_printf_indent("%s", + flags & ESI_SKIPPING + ? UTF8_VERT " " : " " UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ); + debug_print_taint(res); + } + if (flags & ESI_SKIPPING) + debug_printf_indent(UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ + "skipping: result is not used\n"); } - if (flags & ESI_SKIPPING) - debug_printf_indent(UTF8_UP_RIGHT UTF8_HORIZ UTF8_HORIZ UTF8_HORIZ - "skipping: result is not used\n"); } - } -if (textonly_p) *textonly_p = textonly; -expand_level--; -return yield->s; + if (textonly_p) *textonly_p = textonly; + expand_level--; + return res; + } /* This is the failure exit: easiest to program with a goto. We still need to update the pointer to the terminator, for cases of nested calls with "fail". diff --git a/src/src/functions.h b/src/src/functions.h index 1817144ea..961db2dc0 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -963,12 +963,58 @@ g->s[g->ptr] = '\0'; return g->s; } +static inline int +len_string_from_gstring(gstring * g, uschar ** sp) +{ +if (g) + { + *sp = g->s; + g->s[g->ptr] = '\0'; + return g->ptr; + } +else + { + *sp = NULL; + return 0; + } +} + +static inline uschar * +string_copy_from_gstring(gstring * g) +{ +return g ? string_copyn(g->s, g->ptr) : NULL; +} + static inline unsigned gstring_length(const gstring * g) { return g ? (unsigned)g->ptr : 0; } +static inline uschar +gstring_last_char(gstring * g) +{ +return g->s[g->ptr-1]; +} + +static inline void +gstring_trim(gstring * g, unsigned amount) +{ +g->ptr -= amount; +} + +static inline void +gstring_trim_trailing(gstring * g, uschar c) +{ +if (gstring_last_char(g) == c) gstring_trim(g, 1); +} + +static inline void +gstring_reset(gstring * g) +{ +g->ptr = 0; +} + #define gstring_release_unused(g) \ gstring_release_unused_trc(g, __FUNCTION__, __LINE__) @@ -1014,6 +1060,13 @@ memcpy(s, g->s, g->ptr); g->s = s; } +/* Append one gstring to another */ +static inline gstring * +gstring_append(gstring * dest, gstring * item) +{ +return string_catn(dest, item->s, item->ptr); +} + # ifndef COMPILE_UTILITY /******************************************************************************/ diff --git a/src/src/imap_utf7.c b/src/src/imap_utf7.c index 1c09db621..6c9b5c179 100644 --- a/src/src/imap_utf7.c +++ b/src/src/imap_utf7.c @@ -200,9 +200,7 @@ iconv_close(icd); #endif yield = string_catn(yield, outbuf, outptr - outbuf); - -if (yield->s[yield->ptr-1] == '.') - yield->ptr--; +gstring_trim_trailing(yield, '.'); return string_from_gstring(yield); } diff --git a/src/src/log.c b/src/src/log.c index d11b933f9..08ece6158 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -684,11 +684,11 @@ Returns: length actually written, persisting an errno from write() */ ssize_t -write_to_fd_buf(int fd, const uschar *buf, size_t length) +write_to_fd_buf(int fd, const uschar * buf, size_t length) { ssize_t wrote; size_t total_written = 0; -const uschar *p = buf; +const uschar * p = buf; size_t left = length; while (1) @@ -711,6 +711,12 @@ while (1) return total_written; } +static inline ssize_t +write_gstring_to_fd_buf(int fd, const gstring * g) +{ +return write_to_fd_buf(fd, g->s, g->ptr); +} + static void @@ -1113,7 +1119,7 @@ if ( flags & LOG_MAIN /* Failing to write to the log is disastrous */ - written_len = write_to_fd_buf(mainlogfd, g->s, g->ptr); + written_len = write_gstring_to_fd_buf(mainlogfd, g); if (written_len != g->ptr) { log_write_failed(US"main log", g->ptr, written_len); @@ -1172,8 +1178,8 @@ if (flags & LOG_REJECT) g = g2; else /* Buffer is full; truncate */ { - g->ptr -= 100; /* For message and separator */ - if (g->s[g->ptr-1] == '\n') g->ptr--; + gstring_trim(g, 100); /* For message and separator */ + gstring_trim_trailing(g, '\n'); g = string_cat(g, US"\n*** truncated ***\n"); break; } @@ -1228,7 +1234,7 @@ if (flags & LOG_REJECT) if (fstat(rejectlogfd, &statbuf) >= 0) rejectlog_inode = statbuf.st_ino; } - written_len = write_to_fd_buf(rejectlogfd, g->s, g->ptr); + written_len = write_gstring_to_fd_buf(rejectlogfd, g); if (written_len != g->ptr) { log_write_failed(US"reject log", g->ptr, written_len); @@ -1263,7 +1269,7 @@ if (flags & LOG_PANIC) if (panic_save_buffer) (void) write(paniclogfd, panic_save_buffer, Ustrlen(panic_save_buffer)); - written_len = write_to_fd_buf(paniclogfd, g->s, g->ptr); + written_len = write_gstring_to_fd_buf(paniclogfd, g); if (written_len != g->ptr) { int save_errno = errno; diff --git a/src/src/lookups/dnsdb.c b/src/src/lookups/dnsdb.c index 5482cd9d1..1563eda56 100644 --- a/src/src/lookups/dnsdb.c +++ b/src/src/lookups/dnsdb.c @@ -136,15 +136,12 @@ dnsdb_find(void * handle, const uschar * filename, const uschar * keystring, { int rc; int sep = 0; -int defer_mode = PASS; -int dnssec_mode = PASS; -int save_retrans = dns_retrans; -int save_retry = dns_retry; +int defer_mode = PASS, dnssec_mode = PASS; +int save_retrans = dns_retrans, save_retry = dns_retry; int type; int failrc = FAIL; -const uschar *outsep = CUS"\n"; -const uschar *outsep2 = NULL; -uschar *equals, *domain, *found; +const uschar * outsep = CUS"\n", * outsep2 = NULL; +uschar * equals, * domain, * found; dns_answer * dnsa = store_get_dns_answer(); dns_scan dnss; @@ -385,10 +382,7 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0))) if (type == T_A || type == T_AAAA || type == T_ADDRESSES) { for (dns_address * da = dns_address_from_rr(dnsa, rr); da; da = da->next) - { - if (yield->ptr) yield = string_catn(yield, outsep, 1); - yield = string_cat(yield, da->address); - } + yield = string_append_listele(yield, *outsep, da->address); continue; } @@ -399,21 +393,17 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0))) if (type == T_TXT || type == T_SPF) { - if (outsep2 == NULL) /* output only the first item of data */ + if (!outsep2) /* output only the first item of data */ yield = string_catn(yield, US (rr->data+1), (rr->data)[0]); else - { - /* output all items */ - int data_offset = 0; - while (data_offset < rr->size) + for (unsigned data_offset = 0; data_offset < rr->size; ) { - uschar chunk_len = (rr->data)[data_offset++]; - if (outsep2[0] != '\0' && data_offset != 1) + uschar chunk_len = (rr->data)[data_offset]; + if (*outsep2 && data_offset != 0) yield = string_catn(yield, outsep2, 1); - yield = string_catn(yield, US ((rr->data)+data_offset), chunk_len); + yield = string_catn(yield, US ((rr->data) + ++data_offset), chunk_len); data_offset += chunk_len; } - } } else if (type == T_TLSA) { diff --git a/src/src/lookups/ldap.c b/src/src/lookups/ldap.c index b2ad3bbbc..82d6954ff 100644 --- a/src/src/lookups/ldap.c +++ b/src/src/lookups/ldap.c @@ -326,17 +326,19 @@ if (!lcp) g = string_catn(NULL, ldap_url, init_ptr - ldap_url); g = string_fmt_append(g, "//%s:%d/", shost, port); } - string_from_gstring(g); /* Call ldap_initialize() and check the result */ + { + const uschar * s = string_from_gstring(g); - DEBUG(D_lookup) debug_printf_indent("ldap_initialize with URL %s\n", g->s); - if ((rc = ldap_initialize(&ld, CS g->s)) != LDAP_SUCCESS) - { - *errmsg = string_sprintf("ldap_initialize: (error %d) URL \"%s\"\n", - rc, g->s); - goto RETURN_ERROR; - } + DEBUG(D_lookup) debug_printf_indent("ldap_initialize with URL %s\n", s); + if ((rc = ldap_initialize(&ld, CS s)) != LDAP_SUCCESS) + { + *errmsg = string_sprintf("ldap_initialize: (error %d) URL \"%s\"\n", + rc, s); + goto RETURN_ERROR; + } + } store_reset(reset_point); /* Might as well save memory when we can */ diff --git a/src/src/mime.c b/src/src/mime.c index 7c3a33d62..d4c26540a 100644 --- a/src/src/mime.c +++ b/src/src/mime.c @@ -755,7 +755,7 @@ while(1) int result = 0; /* must find first free sequential filename */ - for (gstring * g = string_get(64); result != -1; g->ptr = 0) + for (gstring * g = string_get(64); result != -1; gstring_reset(g)) { struct stat mystat; g = string_fmt_append(g, diff --git a/src/src/parse.c b/src/src/parse.c index 53d660869..ead8751ae 100644 --- a/src/src/parse.c +++ b/src/src/parse.c @@ -875,26 +875,26 @@ Returns: pointer to the original string, if no quoting needed, or */ const uschar * -parse_quote_2047(const uschar *string, int len, const uschar *charset, +parse_quote_2047(const uschar * string, int len, const uschar * charset, BOOL fold) { const uschar * s = string; -int hlen, l; +int hlen, line_off; BOOL coded = FALSE; BOOL first_byte = FALSE; gstring * g = - string_fmt_append(NULL, "=?%s?Q?", charset ? charset : US"iso-8859-1"); + string_fmt_append(NULL, "=?%s?Q?%n", charset ? charset : US"iso-8859-1", &hlen); -hlen = l = g->ptr; +line_off = hlen; for (s = string; len > 0; s++, len--) { int ch = *s; - if (g->ptr - l > 67 && !first_byte) + if (g->ptr - line_off > 67 && !first_byte) { g = fold ? string_catn(g, US"?=\n ", 4) : string_catn(g, US"?= ", 3); - l = g->ptr; + line_off = g->ptr; g = string_catn(g, g->s, hlen); } diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index eb26b3864..c8f180a58 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -957,9 +957,8 @@ return; static int pdkim_header_complete(pdkim_ctx * ctx) { -if ( (ctx->cur_header->ptr > 1) && - (ctx->cur_header->s[ctx->cur_header->ptr-1] == '\r') ) - --ctx->cur_header->ptr; +if (ctx->cur_header->ptr > 1) + gstring_trim_trailing(ctx->cur_header, '\r'); (void) string_from_gstring(ctx->cur_header); #ifdef EXPERIMENTAL_ARC diff --git a/src/src/readconf.c b/src/src/readconf.c index 48b648bb2..6dba11ca1 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -3219,7 +3219,7 @@ if (config_file) g = string_cat(NULL, buf); /* If the dir does not end with a "/", append one */ - if (g->s[g->ptr-1] != '/') + if (gstring_last_char(g) != '/') g = string_catn(g, US"/", 1); /* If the config file contains a "/", extract the directory part */ diff --git a/src/src/receive.c b/src/src/receive.c index 9bf834aaf..77665d89f 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -4214,7 +4214,8 @@ if (message_logs && !blackholed_by) } else { - uschar *now = tod_stamp(tod_log); + uschar * now = tod_stamp(tod_log); + /* Drop the initial "<= " */ fprintf(message_log, "%s Received from %s\n", now, g->s+3); if (f.deliver_freeze) fprintf(message_log, "%s frozen by %s\n", now, frozen_by); @@ -4266,7 +4267,7 @@ if ( smtp_input && sender_host_address && !f.sender_host_notsocket /* Re-use the log line workspace */ - g->ptr = 0; + gstring_reset(g); g = string_cat(g, US"SMTP connection lost after final dot"); g = add_host_info_for_log(g); log_write(0, LOG_MAIN, "%s", string_from_gstring(g)); diff --git a/src/src/rfc2047.c b/src/src/rfc2047.c index c40518a5d..d5e33b9b1 100644 --- a/src/src/rfc2047.c +++ b/src/src/rfc2047.c @@ -192,9 +192,9 @@ rfc2047_decode2(uschar *string, BOOL lencheck, const uschar *target, { int size = Ustrlen(string); size_t dlen; -uschar *dptr; -gstring *yield; -uschar *mimeword, *q1, *q2, *endword; +uschar * dptr; +gstring * yield; +uschar * mimeword, * q1, * q2, * endword; *error = NULL; mimeword = decode_mimeword(string, lencheck, &q1, &q2, &endword, &dlen, &dptr); @@ -210,17 +210,14 @@ building the result as we go. The result may be longer than the input if it is translated into a multibyte code such as UTF-8. That's why we use the dynamic string building code. */ -yield = store_get(sizeof(gstring) + ++size, string); -yield->size = size; -yield->ptr = 0; -yield->s = US(yield + 1); +yield = string_get_tainted(++size, string); while (mimeword) { - #if HAVE_ICONV +#if HAVE_ICONV iconv_t icd = (iconv_t)(-1); - #endif +#endif if (mimeword != string) yield = string_catn(yield, string, mimeword - string); @@ -233,7 +230,7 @@ while (mimeword) of long strings - the RFC puts limits on the length, but it's best to be robust. */ - #if HAVE_ICONV +#if HAVE_ICONV *q1 = 0; if (target && strcmpic(target, mimeword+2) != 0) if ((icd = iconv_open(CS target, CS(mimeword+2))) == (iconv_t)-1) @@ -241,14 +238,14 @@ while (mimeword) target, mimeword+2, strerror(errno), (errno == EINVAL)? " (maybe unsupported conversion)" : ""); *q1 = '?'; - #endif +#endif while (dlen > 0) { uschar *tptr = NULL; /* Stops compiler warning */ int tlen = -1; - #if HAVE_ICONV +#if HAVE_ICONV uschar tbuffer[256]; uschar *outptr = tbuffer; size_t outleft = sizeof(tbuffer); @@ -281,7 +278,7 @@ while (mimeword) } } - #endif +#endif /* No charset translation is happening or there was a translation error; just set up the original as the string to be added, and mark it all used. @@ -305,9 +302,9 @@ while (mimeword) yield = string_catn(yield, tptr, tlen); } - #if HAVE_ICONV +#if HAVE_ICONV if (icd != (iconv_t)(-1)) iconv_close(icd); - #endif +#endif /* Update string past the MIME word; skip any white space if the next thing is another MIME word. */ diff --git a/src/src/sieve.c b/src/src/sieve.c index 0b347e48d..4793d5756 100644 --- a/src/src/sieve.c +++ b/src/src/sieve.c @@ -439,8 +439,7 @@ if (*uri && *uri!='?') { gstring * g = string_catn(NULL, start, uri-start); - to.character = string_from_gstring(g); - to.length = g->ptr; + to.length = len_string_from_gstring(g, &to.character); if (uri_decode(&to)==-1) { filter->errmsg=US"Invalid URI encoding"; @@ -472,8 +471,7 @@ if (*uri=='?') { gstring * g = string_catn(NULL, start, uri-start); - hname.character = string_from_gstring(g); - hname.length = g->ptr; + hname.length = len_string_from_gstring(g, &hname.character); if (uri_decode(&hname)==-1) { filter->errmsg=US"Invalid URI encoding"; @@ -494,8 +492,7 @@ if (*uri=='?') { gstring * g = string_catn(NULL, start, uri-start); - hname.character = string_from_gstring(g); - hname.length = g->ptr; + hname.length = len_string_from_gstring(g, &hname.character); if (uri_decode(&hvalue)==-1) { filter->errmsg=US"Invalid URI encoding"; @@ -541,8 +538,7 @@ if (*uri=='?') g = string_catn(g, hvalue.character, hvalue.length); g = string_catn(g, CUS "\n", 1); - header->character = string_from_gstring(g); - header->length = g->ptr; + hname.length = len_string_from_gstring(g, &hname.character); } } if (*uri=='&') ++uri; @@ -1482,10 +1478,7 @@ if (*filter->pc=='"') /* quoted string */ ++filter->pc; if (g) - { - data->character = string_from_gstring(g); - data->length = g->ptr; - } + data->length = len_string_from_gstring(g, &data->character); else data->character = US"\0"; /* that way, there will be at least one character allocated */ @@ -1569,10 +1562,7 @@ else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */ #endif { if (g) - { - data->character = string_from_gstring(g); - data->length = g->ptr; - } + data->length = len_string_from_gstring(g, &data->character); else data->character = US"\0"; /* that way, there will be at least one character allocated */ @@ -3303,7 +3293,7 @@ while (*filter->pc) if (subject.length==-1) { - uschar *subject_def; + uschar * subject_def; subject_def = expand_string(US"${if def:header_subject {true}{false}}"); if (subject_def && Ustrcmp(subject_def,"true")==0) @@ -3312,13 +3302,12 @@ while (*filter->pc) expand_header(&subject,&str_subject); g = string_catn(g, subject.character, subject.length); - subject.character = string_from_gstring(g); - subject.length = g->ptr; + subject.length = len_string_from_gstring(g, &subject.character); } else { - subject.character=US"Automated reply"; - subject.length=Ustrlen(subject.character); + subject.character = US"Automated reply"; + subject.length = Ustrlen(subject.character); } } diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 04b20d27c..a13af867a 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -4508,7 +4508,7 @@ while (done <= 0) if (fl.esmtp) { - g->s[3] = '-'; + g->s[3] = '-'; /* overwrite the space after the SMTP response code */ /* I'm not entirely happy with this, as an MTA is supposed to check that it has enough room to accept a message of maximum size before @@ -4639,9 +4639,9 @@ while (done <= 0) first = FALSE; fl.auth_advertised = TRUE; } - saveptr = g->ptr; + saveptr = gstring_length(g); g = string_catn(g, US" ", 1); - g = string_cat (g, au->public_name); + g = string_cat(g, au->public_name); while (++saveptr < g->ptr) g->s[saveptr] = toupper(g->s[saveptr]); au->advertised = TRUE; } @@ -4704,25 +4704,29 @@ while (done <= 0) /* Terminate the string (for debug), write it, and note that HELO/EHLO has been seen. */ + { + uschar * ehlo_resp; + int len = len_string_from_gstring(g, &ehlo_resp); #ifndef DISABLE_TLS - if (tls_in.active.sock >= 0) - (void)tls_write(NULL, g->s, g->ptr, + if (tls_in.active.sock >= 0) + (void) tls_write(NULL, ehlo_resp, len, # ifndef DISABLE_PIPE_CONNECT - fl.pipe_connect_acceptable && pipeline_connect_sends()); + fl.pipe_connect_acceptable && pipeline_connect_sends()); # else - FALSE); + FALSE); # endif - else + else #endif - (void) fwrite(g->s, 1, g->ptr, smtp_out); - - DEBUG(D_receive) for (const uschar * t, * s = string_from_gstring(g); - s && (t = Ustrchr(s, '\r')); - s = t + 2) /* \r\n */ - debug_printf("%s %.*s\n", - s == g->s ? "SMTP>>" : " ", - (int)(t - s), s); - fl.helo_seen = TRUE; + (void) fwrite(ehlo_resp, 1, len, smtp_out); + + DEBUG(D_receive) for (const uschar * t, * s = ehlo_resp; + s && (t = Ustrchr(s, '\r')); + s = t + 2) /* \r\n */ + debug_printf("%s %.*s\n", + s == g->s ? "SMTP>>" : " ", + (int)(t - s), s); + fl.helo_seen = TRUE; + } /* Reset the protocol and the state, abandoning any previous message. */ received_protocol = diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index b47fabf1d..f3f70d2e0 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -2302,7 +2302,7 @@ old_pool = store_pool; for (s++; (c = *s) && c != ')'; s++) g = string_catn(g, s, 1); - tlsp->ver = string_copyn(g->s, g->ptr); + tlsp->ver = string_copy_from_gstring(g); for (uschar * p = US tlsp->ver; *p; p++) if (*p == '-') { *p = '\0'; break; } /* TLS1.0-PKIX -> TLS1.0 */ commit 1e835086d1592bdfbcd8577133965b78470840ac Author: Jeremy Harris Date: Mon Feb 13 11:34:38 2023 +0000 Named queues: support multiple queue-runners from single daemon diff --git a/src/src/daemon.c b/src/src/daemon.c index 7666626ed..b0533c28f 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -3,7 +3,7 @@ *************************************************/ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ -/* Copyright (c) University of Cambridge 1995 - 2018 */ +/* Copyright (c) University of Cambridge 1995 - 2023 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ @@ -16,17 +16,20 @@ /* Structure for holding data for each SMTP connection */ typedef struct smtp_slot { - pid_t pid; /* pid of the spawned reception process */ - uschar *host_address; /* address of the client host */ + pid_t pid; /* pid of the spawned reception process */ + uschar * host_address; /* address of the client host */ } smtp_slot; +typedef struct runner_slot { + pid_t pid; /* pid of spawned queue-runner process */ + const uschar *queue_name; /* pointer to the name in the qrunner struct */ +} runner_slot; + /* An empty slot for initializing (Standard C does not allow constructor expressions in assignments except as initializers in declarations). */ static smtp_slot empty_smtp_slot = { .pid = 0, .host_address = NULL }; - - /************************************************* * Local static variables * *************************************************/ @@ -39,9 +42,11 @@ static int accept_retry_count = 0; static int accept_retry_errno; static BOOL accept_retry_select_failed; -static int queue_run_count = 0; -static pid_t *queue_pid_slots = NULL; -static smtp_slot *smtp_slots = NULL; +static int queue_run_count = 0; /* current runners */ + +static unsigned queue_runner_slot_count = 0; +static runner_slot * queue_runner_slots = NULL; +static smtp_slot * smtp_slots = NULL; static BOOL write_pid = TRUE; @@ -920,19 +925,30 @@ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) /* If it wasn't an accepting process, see if it was a queue-runner process that we are tracking. */ - if (queue_pid_slots) - { - int max = atoi(CS expand_string(queue_run_max)); - for (int i = 0; i < max; i++) - if (queue_pid_slots[i] == pid) + if (queue_runner_slots) + for (unsigned i = 0; i < queue_runner_slot_count; i++) + { + runner_slot * r = queue_runner_slots + i; + if (r->pid == pid) { - queue_pid_slots[i] = 0; + r->pid = 0; /* free up the slot */ + if (--queue_run_count < 0) queue_run_count = 0; DEBUG(D_any) debug_printf("%d queue-runner process%s now running\n", - queue_run_count, (queue_run_count == 1)? "" : "es"); + queue_run_count, queue_run_count == 1 ? "" : "es"); + + for (qrunner ** p = &qrunners, * q = qrunners; q; p = &q->next, q = *p) + if (q->name == r->queue_name) + { + if (q->interval) /* a periodic queue run */ + q->run_count--; + else /* a one-time run */ + *p = q->next; /* drop this qrunner */ + break; + } break; } - } + } } } @@ -1232,7 +1248,11 @@ bad: } +/* Data for notifier-triggered queue runs */ + static uschar queuerun_msgid[MESSAGE_ID_LENGTH+1]; +static const uschar * queuerun_msg_qname; + /* The notifier socket has something to read. Pull the message from it, decode and do the action. @@ -1320,7 +1340,12 @@ switch (buf[0]) /* this should be a message_id */ DEBUG(D_queue_run) debug_printf("%s: qrunner trigger: %s\n", __FUNCTION__, buf+1); + memcpy(queuerun_msgid, buf+1, MESSAGE_ID_LENGTH+1); + + for (qrunner * q = qrunners; q; q = q->next) + if (Ustrcmp(q->name, buf+1+MESSAGE_ID_LENGTH+1) == 0) + { queuerun_msg_qname = q->name; break; } return TRUE; #endif @@ -1383,7 +1408,45 @@ ALARM(resignal_interval); } -static void +/* Re-sort the qrunners list, and return the shortest interval. +That could be negatime. +The next-tick times should have been updated by any runs initiated, +though will not be when the global limit on runners was reached. + +Unlikely to have many queues, so insertion-sort. +*/ + +static int +next_qrunner_interval(void) +{ +qrunner * sorted = NULL; +for (qrunner * q = qrunners, * next; q; q = next) + { + next = q->next; + q->next = NULL; + if (sorted) + { + qrunner ** p = &sorted; + for (qrunner * qq; qq = *p; p = &(qq->next)) + if ( q->next_tick < qq->next_tick + || q->next_tick == qq->next_tick && q->interval < qq->interval + ) + { + *p = q; + q->next = qq; + goto INSERTED; + } + *p = q; + INSERTED: ; + } + else + sorted = q; + } +qrunners = sorted; +return qrunners ? qrunners->next_tick - time(NULL) : 0; +} + +static int daemon_qrun(int local_queue_run_max, struct pollfd * fd_polls, int listen_socket_count) { DEBUG(D_any) debug_printf("%s received\n", @@ -1392,140 +1455,207 @@ DEBUG(D_any) debug_printf("%s received\n", #endif "SIGALRM"); -/* Do a full queue run in a child process, if required, unless we already -have enough queue runners on the go. If we are not running as root, a -re-exec is required. */ - -if ( queue_interval > 0 - && (local_queue_run_max <= 0 || queue_run_count < local_queue_run_max)) - { -pid_t pid; +/* Do a full queue run in a child process, if required, unless we already have +enough queue runners on the go. If we are not running as root, a re-exec is +required. In the calling process, restart the alamr timer for the next run. */ - if ((pid = exim_fork(US"queue-runner")) == 0) +if (is_multiple_qrun()) + if (local_queue_run_max <= 0 || queue_run_count < local_queue_run_max) { - /* Disable debugging if it's required only for the daemon process. We - leave the above message, because it ties up with the "child ended" - debugging messages. */ + qrunner * q = NULL; + +#ifndef DISABLE_QUEUE_RAMP + if (*queuerun_msgid) /* See if we can start another runner for this queue */ + { + for (qrunner * qq = qrunners; qq; qq = qq->next) + if (qq->name == queuerun_msg_qname) + { + q = qq->run_count < qq->run_max ? qq : NULL; + break; + } + } + else +#endif + /* In order of run priority, find the first queue for which we can start + a runner */ - if (f.debug_daemon) debug_selector = 0; + for (q = qrunners; q; q = q->next) + if (q->run_count < q->run_max) break; + + if (q) + { + pid_t pid; - /* Close any open listening sockets in the child */ + /* Bump this queue's next-tick by it's interval */ - close_daemon_sockets(daemon_notifier_fd, - fd_polls, listen_socket_count); + if (q->interval) + { + time_t now = time(NULL); + do ; while ((q->next_tick += q->interval) <= now); + } - /* Reset SIGHUP and SIGCHLD in the child in both cases. */ + if ((pid = exim_fork(US"queue-runner")) == 0) + { + /* Disable debugging if it's required only for the daemon process. We + leave the above message, because it ties up with the "child ended" + debugging messages. */ - signal(SIGHUP, SIG_DFL); - signal(SIGCHLD, SIG_DFL); - signal(SIGTERM, SIG_DFL); - signal(SIGINT, SIG_DFL); + if (f.debug_daemon) debug_selector = 0; - /* Re-exec if privilege has been given up, unless deliver_drop_ - privilege is set. Reset SIGALRM before exec(). */ + /* Close any open listening sockets in the child */ - if (geteuid() != root_uid && !deliver_drop_privilege) - { - uschar opt[8]; - uschar *p = opt; - uschar *extra[7]; - int extracount = 1; - - signal(SIGALRM, SIG_DFL); - *p++ = '-'; - *p++ = 'q'; - if ( f.queue_2stage + close_daemon_sockets(daemon_notifier_fd, + fd_polls, listen_socket_count); + + /* Reset SIGHUP and SIGCHLD in the child in both cases. */ + + signal(SIGHUP, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGINT, SIG_DFL); + + /* Re-exec if privilege has been given up, unless deliver_drop_ + privilege is set. Reset SIGALRM before exec(). */ + + if (geteuid() != root_uid && !deliver_drop_privilege) + { + uschar opt[8]; + uschar *p = opt; + uschar *extra[7]; + int extracount = 1; + + signal(SIGALRM, SIG_DFL); + queue_name = US""; + + *p++ = '-'; + *p++ = 'q'; + if ( q->queue_2stage #ifndef DISABLE_QUEUE_RAMP - && !*queuerun_msgid + && !*queuerun_msgid #endif - ) *p++ = 'q'; - if (f.queue_run_first_delivery) *p++ = 'i'; - if (f.queue_run_force) *p++ = 'f'; - if (f.deliver_force_thaw) *p++ = 'f'; - if (f.queue_run_local) *p++ = 'l'; - *p = 0; - extra[0] = *queue_name - ? string_sprintf("%sG%s", opt, queue_name) : opt; + ) *p++ = 'q'; + if (q->queue_run_first_delivery) *p++ = 'i'; + if (q->queue_run_force) *p++ = 'f'; + if (q->deliver_force_thaw) *p++ = 'f'; + if (q->queue_run_local) *p++ = 'l'; + *p = 0; + + extra[0] = q->name + ? string_sprintf("%sG%s", opt, q->name) : opt; #ifndef DISABLE_QUEUE_RAMP - if (*queuerun_msgid) - { - log_write(0, LOG_MAIN, "notify triggered queue run"); - extra[extracount++] = queuerun_msgid; /* Trigger only the */ - extra[extracount++] = queuerun_msgid; /* one message */ - } + if (*queuerun_msgid) + { + log_write(0, LOG_MAIN, "notify triggered queue run"); + extra[extracount++] = queuerun_msgid; /* Trigger only the */ + extra[extracount++] = queuerun_msgid; /* one message */ + } #endif - /* If -R or -S were on the original command line, ensure they get - passed on. */ + /* If -R or -S were on the original command line, ensure they get + passed on. */ - if (deliver_selectstring) - { - extra[extracount++] = f.deliver_selectstring_regex ? US"-Rr" : US"-R"; - extra[extracount++] = deliver_selectstring; - } + if (deliver_selectstring) + { + extra[extracount++] = f.deliver_selectstring_regex ? US"-Rr" : US"-R"; + extra[extracount++] = deliver_selectstring; + } - if (deliver_selectstring_sender) - { - extra[extracount++] = f.deliver_selectstring_sender_regex - ? US"-Sr" : US"-S"; - extra[extracount++] = deliver_selectstring_sender; - } + if (deliver_selectstring_sender) + { + extra[extracount++] = f.deliver_selectstring_sender_regex + ? US"-Sr" : US"-S"; + extra[extracount++] = deliver_selectstring_sender; + } - /* Overlay this process with a new execution. */ + /* Overlay this process with a new execution. */ - (void)child_exec_exim(CEE_EXEC_PANIC, FALSE, NULL, FALSE, extracount, - extra[0], extra[1], extra[2], extra[3], extra[4], extra[5], extra[6]); + (void)child_exec_exim(CEE_EXEC_PANIC, FALSE, NULL, FALSE, extracount, + extra[0], extra[1], extra[2], extra[3], extra[4], extra[5], extra[6]); - /* Control never returns here. */ - } + /* Control never returns here. */ + } - /* No need to re-exec; SIGALRM remains set to the default handler */ + /* No need to re-exec; SIGALRM remains set to the default handler */ #ifndef DISABLE_QUEUE_RAMP - if (*queuerun_msgid) - { - log_write(0, LOG_MAIN, "notify triggered queue run"); - f.queue_2stage = FALSE; - queue_run(queuerun_msgid, queuerun_msgid, FALSE); - } - else + if (*queuerun_msgid) + { + log_write(0, LOG_MAIN, "notify triggered queue run"); + f.queue_2stage = FALSE; + queue_run(q, queuerun_msgid, queuerun_msgid, FALSE); + } + else #endif - queue_run(NULL, NULL, FALSE); - exim_underbar_exit(EXIT_SUCCESS); - } + queue_run(q, NULL, NULL, FALSE); + exim_underbar_exit(EXIT_SUCCESS); + } - if (pid < 0) - { - log_write(0, LOG_MAIN|LOG_PANIC, "daemon: fork of queue-runner " - "process failed: %s", strerror(errno)); - log_close_all(); - } - else - { - for (int i = 0; i < local_queue_run_max; ++i) - if (queue_pid_slots[i] <= 0) + if (pid < 0) { - queue_pid_slots[i] = pid; - queue_run_count++; - break; + log_write(0, LOG_MAIN|LOG_PANIC, "daemon: fork of queue-runner " + "process failed: %s", strerror(errno)); + log_close_all(); } - DEBUG(D_any) debug_printf("%d queue-runner process%s running\n", - queue_run_count, queue_run_count == 1 ? "" : "es"); + else + { + for (int i = 0; i < local_queue_run_max; ++i) + if (queue_runner_slots[i].pid <= 0) + { + queue_runner_slots[i].pid = pid; + queue_runner_slots[i].queue_name = q->name; + q->run_count++; + queue_run_count++; + break; + } + DEBUG(D_any) debug_printf("%d queue-runner process%s running\n", + queue_run_count, queue_run_count == 1 ? "" : "es"); + } + } } - } - -/* Reset the alarm clock */ sigalrm_seen = FALSE; #ifndef DISABLE_QUEUE_RAMP -if (*queuerun_msgid) +if (*queuerun_msgid) /* it was a fast-ramp kick */ *queuerun_msgid = 0; -else +else /* periodic or one-time queue run */ #endif - ALARM(queue_interval); + { /* Impose a minimum 1s tick, even when a run was outstanding */ + int interval = next_qrunner_interval(); + if (interval <= 0) interval = 1; + + if (qrunners) /* there are still periodic qrunners */ + { + ALARM(interval); + return interval; + } + } +return 0; } + + + +const uschar * +describe_queue_runners(void) +{ +gstring * g = NULL; + +if (!is_multiple_qrun()) return US"no queue runs"; + +for (qrunner * q = qrunners; q; q = q->next) + { + g = string_catn(g, US"-q", 2); + if (q->name) g = string_append(g, 3, US"G", q->name, US"/"); + g = string_cat(g, readconf_printtime(q->interval)); + g = string_catn(g, US" ", 1); + } +gstring_trim(g, 1); +gstring_release_unused(g); +return string_from_gstring(g); +} + + /************************************************* * Exim Daemon Mainline * *************************************************/ @@ -1557,7 +1687,32 @@ struct pollfd * fd_polls, * tls_watch_poll = NULL, * dnotify_poll = NULL; int listen_socket_count = 0, poll_fd_count; ip_address_item * addresses = NULL; time_t last_connection_time = (time_t)0; -int local_queue_run_max = atoi(CS expand_string(queue_run_max)); +int local_queue_run_max = 0; +BOOL queue_run_max_has_dollar; + +if (is_multiple_qrun()) + + /* Nuber of runner-tracking structs needed: If the option queue_run_max has + no expandable elements then it is the overall maximum; else we assume it + depends on the queue name, and add them up to get the maximum. + Evaluate both that and the individual limits. */ + + if (Ustrchr(queue_run_max, '$') != NULL) + { + for (qrunner * q = qrunners; q; q = q->next) + { + queue_name = q->name; + local_queue_run_max += + (q->run_max = atoi(CS expand_string(queue_run_max))); + } + queue_name = US""; + } + else + { + local_queue_run_max = atoi(CS expand_string(queue_run_max)); + for (qrunner * q = qrunners; q; q = q->next) + q->run_max = local_queue_run_max; + } process_purpose = US"daemon"; @@ -2216,10 +2371,11 @@ originator_login = (pw = getpwuid(exim_uid)) /* Get somewhere to keep the list of queue-runner pids if we are keeping track of them (and also if we are doing queue runs). */ -if (queue_interval > 0 && local_queue_run_max > 0) +if (is_multiple_qrun() && local_queue_run_max > 0) { - queue_pid_slots = store_get(local_queue_run_max * sizeof(pid_t), GET_UNTAINTED); - for (int i = 0; i < local_queue_run_max; i++) queue_pid_slots[i] = 0; + queue_runner_slot_count = local_queue_run_max; + queue_runner_slots = store_get(local_queue_run_max * sizeof(runner_slot), GET_UNTAINTED); + memset(queue_runner_slots, 0, local_queue_run_max * sizeof(runner_slot)); } /* Set up the handler for termination of child processes, and the one @@ -2233,9 +2389,12 @@ os_non_restarting_signal(SIGTERM, main_sigterm_handler); os_non_restarting_signal(SIGINT, main_sigterm_handler); /* If we are to run the queue periodically, pretend the alarm has just gone -off. This will cause the first queue-runner to get kicked off straight away. */ +off. This will cause the first queue-runner to get kicked off straight away. +Get an initial sort of the list of queues, to prioritize the initial q-runs */ -sigalrm_seen = (queue_interval > 0); + +if ((sigalrm_seen = is_multiple_qrun())) + (void) next_qrunner_interval(); /* Log the start up of a daemon - at least one of listening or queue running must be set up. */ @@ -2264,20 +2423,16 @@ else if (f.daemon_listen) int smtps_ports = 0; ip_address_item * ipa; uschar * p; - uschar * qinfo = queue_interval > 0 - ? string_sprintf("-q%s%s", - f.queue_2stage ? "q" : "", readconf_printtime(queue_interval)) - : US"no queue runs"; + const uschar * qinfo = describe_queue_runners(); /* Build a list of listening addresses in big_buffer, but limit it to 10 items. The style is for backwards compatibility. - It is now possible to have some ports listening for SMTPS (the old, - deprecated protocol that starts TLS without using STARTTLS), and others - listening for standard SMTP. Keep their listings separate. */ + It is possible to have some ports listening for SMTPS (as opposed to TLS + startted by STARTTLS), and others listening for standard SMTP. Keep their + listings separate. */ for (int j = 0, i; j < 2; j++) - { for (i = 0, ipa = addresses; i < 10 && ipa; i++, ipa = ipa->next) { /* First time round, look for SMTP ports; second time round, look for @@ -2315,7 +2470,7 @@ else if (f.daemon_listen) && Ustrcmp(ipa->address, i2->address) == 0 ) { /* found; append port to list */ - for (p = i2->log; *p; ) p++; /* end of existing string */ + for (p = i2->log; *p; ) p++; /* end of existing string { */ if (*--p == '}') *p = '\0'; /* drop EOL */ while (isdigit(*--p)) ; /* char before port */ @@ -2331,7 +2486,6 @@ else if (f.daemon_listen) } } } - } p = big_buffer; for (int j = 0, i; j < 2; j++) @@ -2367,11 +2521,9 @@ else if (f.daemon_listen) version_string, qinfo, big_buffer); } -else +else /* no listening sockets, only queue-runs */ { - uschar * s = *queue_name - ? string_sprintf("-qG%s/%s", queue_name, readconf_printtime(queue_interval)) - : string_sprintf("-q%s", readconf_printtime(queue_interval)); + const uschar * s = describe_queue_runners(); log_write(0, LOG_MAIN, "exim %s daemon started: pid=%d, %s, not listening for SMTP", version_string, getpid(), s); @@ -2445,6 +2597,8 @@ report_time_since(×tamp_startup, US"daemon loop start"); /* testcase 0022 * for (;;) { + int nolisten_sleep = 60; + if (sigterm_seen) daemon_die(); /* Does not return */ @@ -2458,7 +2612,8 @@ for (;;) if (inetd_wait_timeout > 0) daemon_inetd_wtimeout(last_connection_time); /* Might not return */ else - daemon_qrun(local_queue_run_max, fd_polls, listen_socket_count); + nolisten_sleep = + daemon_qrun(local_queue_run_max, fd_polls, listen_socket_count); /* Sleep till a connection happens if listening, and handle the connection if @@ -2663,7 +2818,7 @@ for (;;) else { struct pollfd p; - poll(&p, 0, queue_interval * 1000); + poll(&p, 0, nolisten_sleep * 1000); handle_ending_processes(); } diff --git a/src/src/exim.c b/src/src/exim.c index dcc71ea45..9cba8d51e 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1687,6 +1687,33 @@ else +/************************************************* +* Queue-runner operations * +*************************************************/ + +/* Prefix a new qrunner descriptor to the qrunners list */ + +static qrunner * +alloc_qrunner(void) +{ +qrunner * q = qrunners; +qrunners = store_get(sizeof(qrunner), GET_UNTAINTED); +memset(qrunners, 0, sizeof(qrunner)); /* default queue, zero interval */ +qrunners->next = q; +qrunners->next_tick = time(NULL); /* run right away */ +return qrunners; +} + +static qrunner * +alloc_onetime_qrunner(void) +{ +qrunners = store_get_perm(sizeof(qrunner), GET_UNTAINTED); +memset(qrunners, 0, sizeof(qrunner)); /* default queue, zero interval */ +qrunners->next_tick = time(NULL); /* run right away */ +qrunners->run_max = 1; +} + + /************************************************* * Entry point and high-level code * *************************************************/ @@ -2051,7 +2078,7 @@ this is a smail convention. */ if ((namelen == 4 && Ustrcmp(argv[0], "runq") == 0) || (namelen > 4 && Ustrncmp(argv[0] + namelen - 5, "/runq", 5) == 0)) { - queue_interval = 0; + alloc_onetime_qrunner(); receiving_message = FALSE; called_as = US"-runq"; } @@ -2110,7 +2137,7 @@ on the second character (the one after '-'), to save some effort. */ BOOL badarg = FALSE; uschar * arg = argv[i]; uschar * argrest; - int switchchar; + uschar switchchar; /* An argument not starting with '-' is the start of a recipients list; break out of the options-scanning loop. */ @@ -3504,87 +3531,100 @@ on the second character (the one after '-'), to save some effort. */ } break; + /* -q: set up queue runs */ case 'q': - receiving_message = FALSE; - if (queue_interval >= 0) - exim_fail("exim: -q specified more than once\n"); - - /* -qq...: Do queue runs in a 2-stage manner */ - - if (*argrest == 'q') { - f.queue_2stage = TRUE; - argrest++; - } + BOOL two_stage, first_del, force, thaw = FALSE, local; - /* -qi...: Do only first (initial) deliveries */ + receiving_message = FALSE; - if (*argrest == 'i') - { - f.queue_run_first_delivery = TRUE; - argrest++; - } + /* -qq...: Do queue runs in a 2-stage manner */ - /* -qf...: Run the queue, forcing deliveries - -qff..: Ditto, forcing thawing as well */ + if ((two_stage = *argrest == 'q')) + argrest++; - if (*argrest == 'f') - { - f.queue_run_force = TRUE; - if (*++argrest == 'f') - { - f.deliver_force_thaw = TRUE; - argrest++; - } - } + /* -qi...: Do only first (initial) deliveries */ - /* -q[f][f]l...: Run the queue only on local deliveries */ + if ((first_del = *argrest == 'i')) + argrest++; - if (*argrest == 'l') - { - f.queue_run_local = TRUE; - argrest++; - } + /* -qf...: Run the queue, forcing deliveries + -qff..: Ditto, forcing thawing as well */ - /* -q[f][f][l][G]... Work on the named queue */ + if ((force = *argrest == 'f')) + if ((thaw = *++argrest == 'f')) + argrest++; - if (*argrest == 'G') - { - int i; - for (argrest++, i = 0; argrest[i] && argrest[i] != '/'; ) i++; - exim_len_fail_toolong(i, EXIM_DRIVERNAME_MAX, "-q*G"); - queue_name = string_copyn(argrest, i); - argrest += i; - if (*argrest == '/') argrest++; - } + /* -q[f][f]l...: Run the queue only on local deliveries */ + + if ((local = *argrest == 'l')) + argrest++; - /* -q[f][f][l][G]: Run the queue, optionally forced, optionally local - only, optionally named, optionally starting from a given message id. */ + /* -q[f][f][l][G]... Work on the named queue */ - if (!(list_queue || count_queue)) - if ( !*argrest - && (i + 1 >= argc || argv[i+1][0] == '-' || mac_ismsgid(argv[i+1]))) + if (*argrest == 'G') { - queue_interval = 0; - if (i+1 < argc && mac_ismsgid(argv[i+1])) - start_queue_run_id = string_copy_taint(argv[++i], GET_TAINTED); - if (i+1 < argc && mac_ismsgid(argv[i+1])) - stop_queue_run_id = string_copy_taint(argv[++i], GET_TAINTED); + int i; + for (argrest++, i = 0; argrest[i] && argrest[i] != '/'; ) i++; + exim_len_fail_toolong(i, EXIM_DRIVERNAME_MAX, "-q*G"); + queue_name = string_copyn(argrest, i); + argrest += i; + if (*argrest == '/') argrest++; } - /* -q[f][f][l][G/]: Run the queue at regular intervals, optionally - forced, optionally local only, optionally named. */ + /* -q[f][f][l][G]: Run the queue, optionally forced, optionally local + only, optionally named, optionally starting from a given message id. */ - else if ((queue_interval = readconf_readtime(*argrest ? argrest : argv[++i], - 0, FALSE)) <= 0) - exim_fail("exim: bad time value %s: abandoned\n", argv[i]); - break; + if (!(list_queue || count_queue)) + { + qrunner * q; + + if ( !*argrest + && (i + 1 >= argc || argv[i+1][0] == '-' || mac_ismsgid(argv[i+1]))) + { + q = alloc_onetime_qrunner(); + if (i+1 < argc && mac_ismsgid(argv[i+1])) + start_queue_run_id = string_copy_taint(argv[++i], GET_TAINTED); + if (i+1 < argc && mac_ismsgid(argv[i+1])) + stop_queue_run_id = string_copy_taint(argv[++i], GET_TAINTED); + } + + /* -q[f][f][l][G/]: Run the queue at regular intervals, optionally + forced, optionally local only, optionally named. */ + + else + { + int intvl = readconf_readtime(*argrest ? argrest : argv[++i], 0, FALSE); + if (intvl <= 0) + exim_fail("exim: bad time value %s: abandoned\n", argv[i]); + + for (qrunner * qq = qrunners; qq; qq = qq->next) + if ( queue_name && qq->name && Ustrcmp(queue_name, qq->name) == 0 + || !queue_name && !qq->name) + exim_fail("exim: queue-runner specified more than once\n"); + + q = alloc_qrunner(); + q->interval = intvl; + } + + q->name = *queue_name ? queue_name : NULL; /* will be NULL for the default queue */ + q->queue_run_force = force; + q->deliver_force_thaw = thaw; + q->queue_run_first_delivery = first_del; + q->queue_run_local = local; + q->queue_2stage = two_stage; + } + + break; + } case 'R': /* Synonymous with -qR... */ + case 'S': /* Synonymous with -qS... */ { - const uschar *tainted_selectstr; + const uschar * tainted_selectstr; + uschar * s; receiving_message = FALSE; @@ -3594,20 +3634,28 @@ on the second character (the one after '-'), to save some effort. */ -Rrf: Regex and force -Rrff: Regex and force and thaw + -S...: Like -R but works on sender. + in all cases provided there are no further characters in this argument. */ + alloc_onetime_qrunner(); + qrunners->queue_2stage = f.queue_2stage; if (*argrest) for (int i = 0; i < nelem(rsopts); i++) if (Ustrcmp(argrest, rsopts[i]) == 0) { - if (i != 2) f.queue_run_force = TRUE; - if (i >= 2) f.deliver_selectstring_regex = TRUE; - if (i == 1 || i == 4) f.deliver_force_thaw = TRUE; + if (i != 2) qrunners->queue_run_force = TRUE; + if (i >= 2) + if (switchchar == 'R') + f.deliver_selectstring_regex = TRUE; + else + f.deliver_selectstring_sender_regex = TRUE; + if (i == 1 || i == 4) qrunners->deliver_force_thaw = TRUE; argrest += Ustrlen(rsopts[i]); } - /* -R: Set string to match in addresses for forced queue run to + /* -R or -S: Set string to match in addresses for forced queue run to pick out particular messages. */ /* Avoid attacks from people providing very long strings, and do so before @@ -3617,58 +3665,22 @@ on the second character (the one after '-'), to save some effort. */ else if (i+1 < argc) tainted_selectstr = argv[++i]; else - exim_fail("exim: string expected after -R\n"); - deliver_selectstring = string_copy_taint( + exim_fail("exim: string expected after %s\n", switchchar == 'R' ? "-R" : "-S"); + + s = string_copy_taint( exim_str_fail_toolong(tainted_selectstr, EXIM_EMAILADDR_MAX, "-R"), GET_TAINTED); - } - break; - - /* -r: an obsolete synonym for -f (see above) */ - - - /* -S: Like -R but works on sender. */ - - case 'S': /* Synonymous with -qS... */ - { - const uschar *tainted_selectstr; - - receiving_message = FALSE; - - /* -Sf: As -S (below) but force all deliveries, - -Sff: Ditto, but also thaw all frozen messages, - -Sr: String is regex - -Srf: Regex and force - -Srff: Regex and force and thaw - - in all cases provided there are no further characters in this - argument. */ - - if (*argrest) - for (int i = 0; i < nelem(rsopts); i++) - if (Ustrcmp(argrest, rsopts[i]) == 0) - { - if (i != 2) f.queue_run_force = TRUE; - if (i >= 2) f.deliver_selectstring_sender_regex = TRUE; - if (i == 1 || i == 4) f.deliver_force_thaw = TRUE; - argrest += Ustrlen(rsopts[i]); - } - - /* -S: Set string to match in addresses for forced queue run to - pick out particular messages. */ - if (*argrest) - tainted_selectstr = argrest; - else if (i+1 < argc) - tainted_selectstr = argv[++i]; + if (switchchar == 'R') + deliver_selectstring = s; else - exim_fail("exim: string expected after -S\n"); - deliver_selectstring_sender = string_copy_taint( - exim_str_fail_toolong(tainted_selectstr, EXIM_EMAILADDR_MAX, "-S"), - GET_TAINTED); + deliver_selectstring_sender = s; } break; + + /* -r: an obsolete synonym for -f (see above) */ + /* -Tqt is an option that is exclusively for use by the testing suite. It is not recognized in other circumstances. It allows for the setting up of explicit "queue times" so that various warning/retry things can be @@ -3777,9 +3789,8 @@ on the second character (the one after '-'), to save some effort. */ /* If -R or -S have been specified without -q, assume a single queue run. */ - if ( (deliver_selectstring || deliver_selectstring_sender) - && queue_interval < 0) - queue_interval = 0; + if ((deliver_selectstring || deliver_selectstring_sender) && !qrunners) + alloc_onetime_qrunner(); END_ARG: @@ -3791,22 +3802,22 @@ if (usage_wanted) exim_usage(called_as); /* Arguments have been processed. Check for incompatibilities. */ if ( ( (smtp_input || extract_recipients || recipients_arg < argc) - && ( f.daemon_listen || queue_interval >= 0 || bi_option + && ( f.daemon_listen || qrunners || bi_option || test_retry_arg >= 0 || test_rewrite_arg >= 0 || filter_test != FTEST_NONE || msg_action_arg > 0 && !one_msg_action ) ) || ( msg_action_arg > 0 - && ( f.daemon_listen || queue_interval > 0 || list_options + && ( f.daemon_listen || is_multiple_qrun() || list_options || checking && msg_action != MSG_LOAD || bi_option || test_retry_arg >= 0 || test_rewrite_arg >= 0 ) ) - || ( (f.daemon_listen || queue_interval > 0) + || ( (f.daemon_listen || is_multiple_qrun()) && ( sender_address || list_options || list_queue || checking || bi_option ) ) - || f.daemon_listen && queue_interval == 0 - || f.inetd_wait_mode && queue_interval >= 0 + || f.daemon_listen && is_onetime_qrun() + || f.inetd_wait_mode && qrunners || ( list_options && ( checking || smtp_input || extract_recipients || filter_test != FTEST_NONE || bi_option @@ -3822,7 +3833,7 @@ if ( ( (smtp_input || extract_recipients || recipients_arg < argc) || ( smtp_input && (sender_address || filter_test != FTEST_NONE || extract_recipients) ) - || deliver_selectstring && queue_interval < 0 + || deliver_selectstring && !qrunners || msg_action == MSG_LOAD && (!expansion_test || expansion_test_message) ) exim_fail("exim: incompatible command-line options or arguments\n"); @@ -4444,7 +4455,7 @@ if (!f.admin_user) if ( deliver_give_up || f.daemon_listen || malware_test_file || count_queue && queue_list_requires_admin || list_queue && queue_list_requires_admin - || queue_interval >= 0 && prod_requires_admin + || qrunners && prod_requires_admin || queue_name_dest && prod_requires_admin || debugset && !f.running_in_test_harness ) @@ -4460,7 +4471,7 @@ regression testing. */ if ( real_uid != root_uid && real_uid != exim_uid && ( continue_hostname || ( f.dont_deliver - && (queue_interval >= 0 || f.daemon_listen || msg_action_arg > 0) + && (qrunners || f.daemon_listen || msg_action_arg > 0) ) ) && !f.running_in_test_harness ) @@ -4577,11 +4588,11 @@ to the state Exim usually runs in. */ if ( !unprivileged /* originally had root AND */ && !removed_privilege /* still got root AND */ && !f.daemon_listen /* not starting the daemon */ - && queue_interval <= 0 /* (either kind of daemon) */ + && (!qrunners || is_onetime_qrun()) /* (either kind of daemon) */ && ( /* AND EITHER */ deliver_drop_privilege /* requested unprivileged */ || ( /* OR */ - queue_interval < 0 /* not running the queue */ + !qrunners /* not running the queue */ && ( msg_action_arg < 0 /* and */ || msg_action != MSG_DELIVER /* not delivering */ ) /* and */ @@ -4696,7 +4707,7 @@ if (msg_action_arg > 0 && msg_action != MSG_DELIVER && msg_action != MSG_LOAD) } /* We used to set up here to skip reading the ACL section, on - (msg_action_arg > 0 || (queue_interval == 0 && !f.daemon_listen) + (msg_action_arg > 0 || (is_onetime_qrun() && !f.daemon_listen) Now, since the intro of the ${acl } expansion, ACL definitions may be needed in transports so we lost the optimisation. */ @@ -4947,18 +4958,9 @@ if (msg_action_arg > 0 && msg_action != MSG_LOAD) /* If only a single queue run is requested, without SMTP listening, we can just turn into a queue runner, with an optional starting message id. */ -if (queue_interval == 0 && !f.daemon_listen) +if (is_onetime_qrun() && !f.daemon_listen) { - DEBUG(D_queue_run) debug_printf("Single queue run%s%s%s%s\n", - start_queue_run_id ? US" starting at " : US"", - start_queue_run_id ? start_queue_run_id: US"", - stop_queue_run_id ? US" stopping at " : US"", - stop_queue_run_id ? stop_queue_run_id : US""); - if (*queue_name) - set_process_info("running the '%s' queue (single queue run)", queue_name); - else - set_process_info("running the queue (single queue run)"); - queue_run(start_queue_run_id, stop_queue_run_id, FALSE); + single_queue_run(qrunners, start_queue_run_id, stop_queue_run_id); exim_exit(EXIT_SUCCESS); } @@ -5083,7 +5085,7 @@ returns. We leave this till here so that the originator_ fields are available for incoming messages via the daemon. The daemon cannot be run in mua_wrapper mode. */ -if (f.daemon_listen || f.inetd_wait_mode || queue_interval > 0) +if (f.daemon_listen || f.inetd_wait_mode || is_multiple_qrun()) { if (mua_wrapper) { diff --git a/src/src/functions.h b/src/src/functions.h index 961db2dc0..37f0a57bc 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -411,7 +411,7 @@ extern void queue_list(int, uschar **, int); #ifndef DISABLE_QUEUE_RAMP extern void queue_notify_daemon(const uschar * hostname); #endif -extern void queue_run(uschar *, uschar *, BOOL); +extern void queue_run(qrunner *, uschar *, uschar *, BOOL); extern int random_number(int); extern const uschar *rc_to_string(int); @@ -498,6 +498,7 @@ extern int sieve_interpret(const uschar *, int, const uschar *, const uschar *, const uschar *, const uschar *, address_item **, uschar **); extern void sigalrm_handler(int); +extern void single_queue_run(qrunner *, uschar *, uschar *); extern int smtp_boundsock(smtp_connect_args *); extern void smtp_closedown(uschar *); extern void smtp_command_timeout_exit(void) NORETURN; @@ -1368,6 +1369,22 @@ int res; return !s || !*s || (res = Uatoi(s)) == 0 ? UNLIMITED_ADDRS : res; } +/******************************************************************************/ +/* Queue-runner operations */ + +static inline BOOL +is_onetime_qrun(void) +{ +return qrunners && !qrunners->next && qrunners->interval == 0; +} + +static inline BOOL +is_multiple_qrun(void) +{ +return qrunners && (qrunners->interval > 0 || qrunners->next); +} + + # endif /* !COMPILE_UTILITY */ /******************************************************************************/ diff --git a/src/src/globals.c b/src/src/globals.c index 7af345465..a4b2c6a9c 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -291,8 +291,6 @@ struct global_flags f = .queue_2stage = FALSE, .queue_only_policy = FALSE, - .queue_run_first_delivery = FALSE, - .queue_run_force = FALSE, .queue_run_local = FALSE, .queue_running = FALSE, .queue_smtp = FALSE, @@ -1248,6 +1246,8 @@ uschar *prvscheck_keynum = NULL; uschar *prvscheck_result = NULL; +qrunner *qrunners = NULL; + const uschar *qualify_domain_recipient = NULL; uschar *qualify_domain_sender = NULL; uschar *queue_domains = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index f2e147670..914e2d0f9 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -257,8 +257,6 @@ extern struct global_flags { BOOL queue_2stage :1; /* Run queue in 2-stage manner */ BOOL queue_only_policy :1; /* ACL or local_scan wants queue_only */ - BOOL queue_run_first_delivery :1; /* If TRUE, first deliveries only */ - BOOL queue_run_force :1; /* TRUE to force during queue run */ BOOL queue_run_local :1; /* Local deliveries only in queue run */ BOOL queue_running :1; /* TRUE for queue running process and */ BOOL queue_smtp :1; /* Disable all immediate SMTP (-odqs)*/ @@ -837,6 +835,8 @@ extern uschar *prvscheck_address; /* Set during prvscheck expansion item */ extern uschar *prvscheck_keynum; /* Set during prvscheck expansion item */ extern uschar *prvscheck_result; /* Set during prvscheck expansion item */ +extern qrunner *qrunners; /* tracking data for queues */ + extern const uschar *qualify_domain_recipient; /* Domain to qualify recipients with */ extern uschar *qualify_domain_sender; /* Domain to qualify senders with */ extern uschar *queue_domains; /* Queue these domains */ diff --git a/src/src/macros.h b/src/src/macros.h index 585067fc9..3b0293b97 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -1113,9 +1113,9 @@ should not be one active. */ #define NOTIFIER_SOCKET_NAME "exim_daemon_notify" /* Notify message types */ -#define NOTIFY_MSG_QRUN 1 -#define NOTIFY_QUEUE_SIZE_REQ 2 -#define NOTIFY_REGEX 3 +#define NOTIFY_MSG_QRUN 1 /* 2stage qrun fast-ramp trigger */ +#define NOTIFY_QUEUE_SIZE_REQ 2 /* obtain current queue count */ +#define NOTIFY_REGEX 3 /* an RE for caching */ /* Flags for match_check_string() */ typedef unsigned mcs_flags; diff --git a/src/src/queue.c b/src/src/queue.c index f86e24b42..d01cde655 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -325,8 +325,8 @@ previous lexically lesser one if the given stop message doesn't exist. Because a queue run can take some time, stat each file before forking, in case it has been delivered in the meantime by some other means. -The global variables queue_run_force and queue_run_local may be set to cause -forced deliveries or local-only deliveries, respectively. +The qrun descriptor variables queue_run_force and queue_run_local may be set to +cause forced deliveries or local-only deliveries, respectively. If deliver_selectstring[_sender] is not NULL, skip messages whose recipients do not contain the string. As this option is typically used when a machine comes @@ -339,6 +339,7 @@ is set so that routing is done for all messages. Thus in the second run those that are routed to the same host should go down the same SMTP connection. Arguments: + q queue-runner descriptor start_id message id to start at, or NULL for all stop_id message id to end at, or NULL for all recurse TRUE if recursing for 2-stage run @@ -347,10 +348,10 @@ Returns: nothing */ void -queue_run(uschar *start_id, uschar *stop_id, BOOL recurse) +queue_run(qrunner * q, uschar * start_id, uschar * stop_id, BOOL recurse) { -BOOL force_delivery = f.queue_run_force || deliver_selectstring != NULL || - deliver_selectstring_sender != NULL; +BOOL force_delivery = q->queue_run_force + || deliver_selectstring || deliver_selectstring_sender; const pcre2_code *selectstring_regex = NULL; const pcre2_code *selectstring_regex_sender = NULL; uschar *log_detail = NULL; @@ -363,6 +364,13 @@ BOOL single_id = FALSE; report_time_since(×tamp_startup, US"queue_run start"); #endif +/* Copy the legacy globals from the newer per-qrunner-desc */ + +queue_name = q->name ? q->name : US""; +f.queue_2stage = q->queue_2stage; +f.deliver_force_thaw = q->deliver_force_thaw; +f.queue_run_local = q->queue_run_local; + /* Cancel any specific queue domains. Turn off the flag that causes SMTP deliveries not to happen, unless doing a 2-stage queue run, when the SMTP flag gets set. Save the queue_runner's pid and the flag that indicates any @@ -371,7 +379,7 @@ on TCP/IP channels have queue_run_pid set, but not queue_running. */ queue_domains = NULL; queue_smtp_domains = NULL; -f.queue_smtp = f.queue_2stage; +f.queue_smtp = q->queue_2stage; queue_run_pid = getpid(); f.queue_running = TRUE; @@ -383,11 +391,11 @@ if (!recurse) uschar extras[8]; uschar *p = extras; - if (f.queue_2stage) *p++ = 'q'; - if (f.queue_run_first_delivery) *p++ = 'i'; - if (f.queue_run_force) *p++ = 'f'; - if (f.deliver_force_thaw) *p++ = 'f'; - if (f.queue_run_local) *p++ = 'l'; + if (q->queue_2stage) *p++ = 'q'; + if (q->queue_run_first_delivery) *p++ = 'i'; + if (q->queue_run_force) *p++ = 'f'; + if (q->deliver_force_thaw) *p++ = 'f'; + if (q->queue_run_local) *p++ = 'l'; *p = 0; p = big_buffer; @@ -399,25 +407,25 @@ if (!recurse) if (deliver_selectstring) { snprintf(CS p, big_buffer_size - (p - big_buffer), " -R%s %s", - f.deliver_selectstring_regex? "r" : "", deliver_selectstring); + f.deliver_selectstring_regex ? "r" : "", deliver_selectstring); p += Ustrlen(CCS p); } if (deliver_selectstring_sender) { snprintf(CS p, big_buffer_size - (p - big_buffer), " -S%s %s", - f.deliver_selectstring_sender_regex? "r" : "", deliver_selectstring_sender); + f.deliver_selectstring_sender_regex ? "r" : "", deliver_selectstring_sender); p += Ustrlen(CCS p); } log_detail = string_copy(big_buffer); - if (*queue_name) + if (q->name) log_write(L_queue_run, LOG_MAIN, "Start '%s' queue run: %s", - queue_name, log_detail); + q->name, log_detail); else log_write(L_queue_run, LOG_MAIN, "Start queue run: %s", log_detail); - single_id = start_id && stop_id && !f.queue_2stage + single_id = start_id && stop_id && !q->queue_2stage && Ustrcmp(start_id, stop_id) == 0; } @@ -474,7 +482,7 @@ for (int i = queue_run_in_order ? -1 : 0; /* Unless deliveries are forced, if deliver_queue_load_max is non-negative, check that the load average is low enough to permit deliveries. */ - if (!f.queue_run_force && deliver_queue_load_max >= 0) + if (!q->queue_run_force && deliver_queue_load_max >= 0) if ((load_average = os_getloadavg()) > deliver_queue_load_max) { log_write(L_queue_run, LOG_MAIN, "Abandon queue run: %s (load %.2f, max %.2f)", @@ -492,7 +500,7 @@ for (int i = queue_run_in_order ? -1 : 0; /* If initial of a 2-phase run, maintain a set of child procs to get disk parallelism */ - if (f.queue_2stage && !queue_run_in_order) + if (q->queue_2stage && !queue_run_in_order) { int i; if (qpid[f.running_in_test_harness ? 0 : nelem(qpid) - 1]) @@ -530,7 +538,7 @@ for (int i = queue_run_in_order ? -1 : 0; message when many are not going to be delivered. */ if (deliver_selectstring || deliver_selectstring_sender || - f.queue_run_first_delivery) + q->queue_run_first_delivery) { BOOL wanted = TRUE; BOOL orig_dont_deliver = f.dont_deliver; @@ -548,7 +556,7 @@ for (int i = queue_run_in_order ? -1 : 0; header file, we might as well do the freeze test now, and save forking another process. */ - if (f.deliver_freeze && !f.deliver_force_thaw) + if (f.deliver_freeze && !q->deliver_force_thaw) { log_write(L_skip_delivery, LOG_MAIN, "Message is frozen"); wanted = FALSE; @@ -556,7 +564,7 @@ for (int i = queue_run_in_order ? -1 : 0; /* Check first_delivery in the case when there are no message logs. */ - else if (f.queue_run_first_delivery && !f.deliver_firsttime) + else if (q->queue_run_first_delivery && !f.deliver_firsttime) { DEBUG(D_queue_run) debug_printf("%s: not first delivery\n", fq->text); wanted = FALSE; @@ -688,7 +696,7 @@ single_item_retry: /* A zero return means a delivery was attempted; turn off the force flag for any subsequent calls unless queue_force is set. */ - if (!(status & 0xffff)) force_delivery = f.queue_run_force; + if (!(status & 0xffff)) force_delivery = q->queue_run_force; /* If the process crashed, tell somebody */ @@ -723,13 +731,13 @@ single_item_retry: set_process_info("running queue"); /* If initial of a 2-phase run, we are a child - so just exit */ - if (f.queue_2stage && !queue_run_in_order) + if (q->queue_2stage && !queue_run_in_order) exim_exit(EXIT_SUCCESS); /* If we are in the test harness, and this is not the first of a 2-stage queue run, update fudged queue times. */ - if (f.running_in_test_harness && !f.queue_2stage) + if (f.running_in_test_harness && !q->queue_2stage) { uschar * fqtnext = Ustrchr(fudged_queue_times, '/'); if (fqtnext) fudged_queue_times = fqtnext + 1; @@ -740,7 +748,7 @@ single_item_retry: go_around: /* If initial of a 2-phase run, we are a child - so just exit */ - if (f.queue_2stage && !queue_run_in_order) + if (q->queue_2stage && !queue_run_in_order) exim_exit(EXIT_SUCCESS); } /* End loop for list of messages */ @@ -767,7 +775,7 @@ single_item_retry: /* If queue_2stage is true, we do it all again, with the 2stage flag turned off. */ -if (f.queue_2stage) +if (q->queue_2stage) { /* wait for last children */ @@ -782,22 +790,40 @@ if (f.queue_2stage) #ifdef MEASURE_TIMING report_time_since(×tamp_startup, US"queue_run 1st phase done"); #endif - f.queue_2stage = FALSE; - queue_run(start_id, stop_id, TRUE); + q->queue_2stage = f.queue_2stage = FALSE; + queue_run(q, start_id, stop_id, TRUE); } /* At top level, log the end of the run. */ if (!recurse) - if (*queue_name) + if (q->name) log_write(L_queue_run, LOG_MAIN, "End '%s' queue run: %s", - queue_name, log_detail); + q->name, log_detail); else log_write(L_queue_run, LOG_MAIN, "End queue run: %s", log_detail); } +void +single_queue_run(qrunner * q, uschar * start_id, uschar * stop_id) +{ +DEBUG(D_queue_run) debug_printf("Single queue run%s%s%s%s\n", + start_id ? US" starting at " : US"", + start_id ? start_id: US"", + stop_id ? US" stopping at " : US"", + stop_id ? stop_id : US""); + +if (*queue_name) + set_process_info("running the '%s' queue (single queue run)", queue_name); +else + set_process_info("running the queue (single queue run)"); +queue_run(q, start_id, stop_id, FALSE); +} + + + /************************************************ * Count messages on the queue * @@ -1552,20 +1578,22 @@ if (s) void queue_notify_daemon(const uschar * msgid) { -uschar buf[MESSAGE_ID_LENGTH + 2]; +int bsize = 1 + MESSAGE_ID_LENGTH + 1 + Ustrlen(queue_name) + 1; +uschar * buf = store_get(bsize, GET_UNTAINTED); int fd; DEBUG(D_queue_run) debug_printf("%s: %s\n", __FUNCTION__, msgid); buf[0] = NOTIFY_MSG_QRUN; memcpy(buf+1, msgid, MESSAGE_ID_LENGTH+1); +Ustrcpy(buf+1+MESSAGE_ID_LENGTH+1, queue_name); if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) >= 0) { struct sockaddr_un sa_un = {.sun_family = AF_UNIX}; ssize_t len = daemon_notifier_sockname(&sa_un); - if (sendto(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sa_un, (socklen_t)len) < 0) + if (sendto(fd, buf, bsize, 0, (struct sockaddr *)&sa_un, (socklen_t)len) < 0) DEBUG(D_queue_run) debug_printf("%s: sendto %s\n", __FUNCTION__, strerror(errno)); close(fd); diff --git a/src/src/structs.h b/src/src/structs.h index eae66e88d..3f237fce5 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -958,4 +958,22 @@ struct ob_dkim { #endif }; + +/* per-queue-runner info */ +typedef struct qrunner { + struct qrunner * next; /* list sorted by next tick */ + + uschar * name; /* NULL for the default queue */ + unsigned interval; /* tick rate, seconds */ + time_t next_tick; /* next run should, or should have, start(ed) */ + unsigned run_max; /* concurrent queue runner limit */ + unsigned run_count; /* current runners */ + + BOOL queue_run_force :1; + BOOL deliver_force_thaw :1; + BOOL queue_run_first_delivery :1; + BOOL queue_run_local :1; + BOOL queue_2stage :1; +} qrunner; + /* End of structs.h */ commit 2ea09d783ec32eea87d0592ac941e8849d780f9d Author: Jeremy Harris Date: Tue Feb 14 11:56:40 2023 +0000 Fix onetime qrunner alloc Broken-by: 1e835086d159 diff --git a/src/src/exim.c b/src/src/exim.c index 9cba8d51e..8d13bd478 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1711,6 +1711,7 @@ qrunners = store_get_perm(sizeof(qrunner), GET_UNTAINTED); memset(qrunners, 0, sizeof(qrunner)); /* default queue, zero interval */ qrunners->next_tick = time(NULL); /* run right away */ qrunners->run_max = 1; +return qrunners; } commit 73d6e13f9b0cc4f708210372c59893950b3f7097 Author: Jeremy Harris Date: Tue Feb 14 11:57:27 2023 +0000 tidying diff --git a/src/src/exim_dbmbuild.c b/src/src/exim_dbmbuild.c index 0ea71795f..43628234d 100644 --- a/src/src/exim_dbmbuild.c +++ b/src/src/exim_dbmbuild.c @@ -52,6 +52,9 @@ void store_release_above_3(void *ptr, const char *func, int linenumber) { } gstring * +string_catn(gstring * g, const uschar * s, int count) +{ return NULL; } +gstring * string_vformat_trc(gstring * g, const uschar * func, unsigned line, unsigned size_limit, unsigned flags, const char *format, va_list ap) { return NULL; } diff --git a/src/src/expand.c b/src/src/expand.c index 1daf10044..baf7134cd 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -478,8 +478,8 @@ typedef struct { int *length; } alblock; -static uschar * fn_recipients(void); typedef uschar * stringptr_fn_t(void); +static uschar * fn_recipients(void); static uschar * fn_queue_size(void); /* This table must be kept in alphabetical order. */ @@ -685,7 +685,7 @@ static var_entry var_table[] = { { "qualify_domain", vtype_stringptr, &qualify_domain_sender }, { "qualify_recipient", vtype_stringptr, &qualify_domain_recipient }, { "queue_name", vtype_stringptr, &queue_name }, - { "queue_size", vtype_string_func, &fn_queue_size }, + { "queue_size", vtype_string_func, (void *) &fn_queue_size }, { "rcpt_count", vtype_int, &rcpt_count }, { "rcpt_defer_count", vtype_int, &rcpt_defer_count }, { "rcpt_fail_count", vtype_int, &rcpt_fail_count }, diff --git a/src/src/globals.c b/src/src/globals.c index a4b2c6a9c..78b225fbc 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -1326,7 +1326,7 @@ const pcre2_code *regex_EARLY_PIPE = NULL; int regex_cachesize = 0; const pcre2_code *regex_ismsgid = NULL; const pcre2_code *regex_smtp_code = NULL; -const uschar *regex_vars[REGEX_VARS] = { 0 };; +const uschar *regex_vars[REGEX_VARS] = { NULL }; #ifdef WHITELIST_D_MACROS const pcre2_code *regex_whitelisted_macro = NULL; #endif commit 7b5fe03f9c6c2a322dc385ab78b60ccfe1fe33fe Author: Andrew Aitchison Date: Sun Feb 12 11:28:49 2023 +0000 Utility: exim_msgdate diff --git a/src/ACKNOWLEDGMENTS b/src/ACKNOWLEDGMENTS index 22e9909c0..c318d3fea 100644 --- a/src/ACKNOWLEDGMENTS +++ b/src/ACKNOWLEDGMENTS @@ -357,6 +357,7 @@ David Woodhouse Dynamic modules. Security. Contributors ------------ Andrew Aitchison Spotted cmdline AV scanner regression with -bmalware + exim_msgdate Simon Arlott Code for outbound SSL-on-connect Patch implementing %M datestamping in log filenames Patch restoring SIGPIPE handler for child_open_uid diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index a290b90b0..29c037401 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -249,7 +249,8 @@ utils: $(EXIM_MONITOR) exicyclog exinext exiwhat \ exigrep eximstats exipick exiqgrep exiqsumm \ transport-filter.pl convert4r3 convert4r4 \ exim_checkaccess \ - exim_dbmbuild exim_dumpdb exim_fixdb exim_tidydb exim_lock + exim_dbmbuild exim_dumpdb exim_fixdb exim_tidydb exim_lock \ + exim_msgdate # Targets for special-purpose configuration header builders @@ -383,6 +384,23 @@ exigrep: config ../src/exigrep.src @chmod a+x exigrep @echo ">>> exigrep script built" +exim_msgdate: config ../src/exim_msgdate.src + @rm -f exim_msgdate + @. ./version.sh && sed \ + -e "s?PROCESSED_FLAG?This file has been so processed.?"\ + -e "/^[ \t]*# /p" \ + -e "/^[ \t]*# /d" \ + -e "s?BIN_DIRECTORY?$(BIN_DIRECTORY)?" \ + -e "s?PERL_COMMAND?$(PERL_COMMAND)?" \ + -e "s?BASE_62?$${BASE_62:-62}?" \ + -e "s?CONFIGURE_FILE\"?$(CONFIGURE_FILE)\"?" \ + -e "s?EXIM_RELEASE_VERSION?$${EXIM_RELEASE_VERSION}?" \ + -e "s?EXIM_VARIANT_VERSION?$${EXIM_VARIANT_VERSION}?" \ + ../src/exim_msgdate.src > exim_msgdate-t + @mv exim_msgdate-t exim_msgdate + @chmod a+x exim_msgdate + @echo ">>> exim_msgdate script built" + eximstats: config ../src/eximstats.src @rm -f eximstats @. ./version.sh && sed \ diff --git a/src/scripts/exim_install b/src/scripts/exim_install index 827841ffc..e6857adaf 100755 --- a/src/scripts/exim_install +++ b/src/scripts/exim_install @@ -198,7 +198,7 @@ else set exim${EXE} ${exim_monitor} exim_dumpdb${EXE} exim_fixdb${EXE} \ exim_tidydb${EXE} exinext exiwhat exim_dbmbuild${EXE} exicyclog \ exigrep eximstats exipick exiqgrep exiqsumm exim_lock${EXE} \ - exim_checkaccess + exim_checkaccess exim_msgdate fi echo $com "" diff --git a/src/src/exim_msgdate.src b/src/src/exim_msgdate.src new file mode 100755 index 000000000..e5c357bca --- /dev/null +++ b/src/src/exim_msgdate.src @@ -0,0 +1,579 @@ +#!PERL_COMMAND -WT +# +# Utility to convert an exim message-id to a human readable form +# +# https://bugs.exim.org/show_bug.cgi?id=2956 +# Written by Andrew C Aitchison +# +# Copyright (c) 2023 The Exim Maintainers 2023 +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Portions taken from exicyclog.src, which is +# Copyright (c) University of Cambridge, 1995 - 2015 +# See the file NOTICE for conditions of use and distribution. + +# https://bugs.exim.org/show_bug.cgi?id=2956 +# https://exim.org/exim-html-current/doc/html/spec_html/ch-how_exim_receives_and_delivers_mail.html#SECTmessiden + +# Except when they appear in comments, the following placeholders in this +# source are replaced when it is turned into a runnable script: +# +# BASE_62 +# BIN_DIRECTORY +# CONFIGURE_FILE +# PERL_COMMAND +# EXIM_RELEASE_VERSION +# EXIM_VARIANT_VERSION +# +# PROCESSED_FLAG + +use strict; +use File::Basename; +use Getopt::Long; +use Pod::Usage; + +use constant { TRUE => 1, FALSE => 0 }; + +if (defined $ENV{TZ}) { + my $zonefile = "/usr/share/zoneinfo/$ENV{TZ}"; + if (defined $ENV{TZDIR}) { + if (-d $ENV{TZDIR}) { + $zonefile="$ENV{TZDIR}/$ENV{TZ}"; + } else { + warn "No directory TZDIR=$ENV{TZDIR}\n" + } + } + warn "Cannot read timezone file $zonefile (from TZDIR/TZ)\n\t'man tzset' may help.\n" + unless -r $zonefile; +} + +my $localhost_number; # An Exim config value + +my $p_name = basename $0; +my $p_version = "20230203.0"; +my $p_cp = < \$prefix_pattern, + # "--|\/" => \$long_prefix_pattern, + + "b=i" => \$optbase, + "base=i" => \$optbase, + "b36" => \$optbase36, + "base36" => \$optbase36, + "b62" => \$optbase62, + "base62" => \$optbase62, + + "localhost_number=s" => \$localhost_number, # cf "local" + + "unix" => \$optunix, + "u" => \$optunix, + "GMT" => \$optgmt, + "UTC" => \$optgmt, + "zulu" => \$optgmt, + "local" => \$optlocal, # cf "localhost_number" + "l" => \$optlocal, # cf "localhost_number" + + "pid" => \$optpid, + + # exim args given by the test harness + "C=s" => \$optconfigfile, + "dexim_path=s" => \$opteximpath, + + "debug" => \$debug, + "nodebug" => \$nodebug, + "no-debug" => \$nodebug, + + 'help' => sub { pod2usage(-exit => 0) }, + 'man' => sub { + pod2usage( + -exit => 0, + -verbose => 2, + -noperldoc => system('perldoc -V 2>/dev/null 1>&2') + ); + }, +) or pod2usage; +# die("Error in command line arguments\n"); + +$debug = undef if $nodebug; + + +if ($debug) { + warn "$0 ", join(" ", @ARGV), "\n"; + warn "C=$optconfigfile\n" if defined $optconfigfile; + warn "dexim_path=$opteximpath\n" if defined $opteximpath; +} + +unless ($optgmt || $optunix || $optlocal) { + $optlocal = TRUE; +} + +if (defined($optbase36) && defined($optbase62)) { + die "cannot be base36 and base62\n"; +} + +if (defined $optbase36) { + $optbase = 36; +} +if (defined $optbase62) { + $optbase = 62; +} +if (defined $optbase) { + if ($optbase =~ 62) { + $optbase = 62; + } elsif ($optbase =~ 36) { + $optbase = 36; + } else { + warn "\toptbase36=$optbase36\n" if defined $optbase36; + warn "\toptbase62=$optbase62\n"if defined $optbase62; + die "unknown base option $optbase\n"; + } +} + +# Some Operating Systems have case-insensitive file systems +# (at least by default). +# This limits the characters available for the message-id +# and hence the base Exim uses to encode numbers. +# +# We use Perl's idea of the operating system. +# Should we instead use the script "scripts/os-type" which comes with Exim ? +my $defaultbase; +if ($^O =~ /darwin|cygwin/i) { # darwin aka MacOS X + $defaultbase = 36; +} else { + $defaultbase = 62; +} + +if ("BASE_62" != $defaultbase and !defined $optbase) { + die "base_62 mismatch: OS implies $defaultbase but config has BASE_62\n"; +} + +my $base=$defaultbase; +$base = $optbase if $optbase; + +my $base62_chars = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; +my $base36_chars="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +my $base_chars; +if ($base == 62) { + $base_chars = $base62_chars; +} else { + $base_chars = $base36_chars; +} + +# We use this to decode both base62 and base36 +sub decode62($) { + #warn "decode62(", join(",", @_), ")\n"; + my ($text) = @_; + unless ($text =~ /^[$base_chars]+$/) { + die "$text is not base $base\n"; + } + my $n=0; + foreach my $tt (split //, $text) { + $n = $n * $base + index($base_chars, $tt); + } + #warn "$text -> $n\n"; + return $n; +} # decode62 + +sub get_configfilename() +{ + if (defined $optconfigfile) { + if ( -r $optconfigfile ) { + warn "using config $optconfigfile\n" if $debug; + return $optconfigfile; + } else { + die "cannot read $optconfigfile\n"; + } + } + + # See if this installation is using the esoteric "USE_EUID" feature of + # Exim, in which it uses the effective user id as a suffix for the + # configuration file name. In order for this to work, exim_msgdate + # must be run under the appropriate euid. + my $euid = ""; + if ("CONFIGURE_FILE_USE_EUID" eq "yes" ) { + $euid=`id -u`; + } + + # See if this installation is using the esoteric "USE_NODE" + # feature of Exim, in which it uses the host's name as a suffix + # for the configuration file name. + my $hostsuffix=""; + if ("CONFIGURE_FILE_USE_NODE" eq "yes") { + $hostsuffix=`uname -n`; + } + + # Now find the configuration file name. + # This has got complicated because the CONFIGURE_FILE value may now + # be a list of files. The one that is used is the first one that + # exists. Mimic the code in readconf.c by testing first for the + # suffixed file in each case. + + my $config=""; + my $baseconfig; + foreach $baseconfig (split /:/, "CONFIGURE_FILE") { + chomp $baseconfig; + if (-f "$baseconfig$euid$hostsuffix" ) { + $config="$baseconfig$euid$hostsuffix"; + } elsif (-f "$baseconfig$euid" ) { + $config="$baseconfig$euid"; + } elsif (-f "$baseconfig$hostsuffix" ) { + $config="$baseconfig$hostsuffix"; + } elsif (-f "$baseconfig" ) { + $config="$baseconfig"; + } + last if $config; + } + unless ($config) { + die "No config file found\n"; + } + + return $config; +} # sub get_configfilename + + +if ($debug) { + warn "before reading configfiles:\n"; + if (defined $localhost_number) { + warn "localhost_number=$localhost_number\n"; + } else { + warn "localhost_number unset\n"; + } +} + +if (defined $localhost_number) { + if ($localhost_number eq "none") { + $localhost_number = undef; + } +} else { + my $config = get_configfilename(); + warn "Reading config $config to find localhost_number\n" if $debug; + + if (-r $config) { + # This does not do any expansions or lookups, + # so could be end up with a different value for localhost_number + # from the one that exim finds. + open(CONFIG, "<", $config) or + die "cannot open config $config :$!\n"; + + while() { + if (/^\s*localhost_number\s*=\s*(\d+)\s*$/) { + $localhost_number = $1; + } + } + close CONFIG or die "cannot close config $config: $!\n"; + warn "$config gives localhost_number $localhost_number\n" + if $debug and defined $localhost_number; + } else { + # This way we get the expanded value for localhost_number + # directly from exim, but we have to guess which exim binary ... + # On Debian and Ubuntu, /usr/sbin/exim is a link to exim4 so is OK. + # + # Even if given on command line, we cannot use $opteximpath + # since it is the full path to this script, + # or $config since it is tainted. + # + warn "running system exim -bP localhost_number\n" if $debug; + my $exim_bP_localhost_number = `/usr/sbin/exim -bP localhost_number`; + if ($exim_bP_localhost_number =~ /^localhost_number\s*=\s*(\d*)/) { + $localhost_number = $1; + } + warn "exim_bP_localhost_number $exim_bP_localhost_number gives localhost_number $localhost_number\n" + if $debug and defined $localhost_number; + } +} + +if (defined $localhost_number) { + die "localhost_number > 16\n" + if $localhost_number > 16; + die "localhost_number > 10\n" + if $localhost_number > 10 && ($base != 62); +} + +if ($debug) { + if (defined $localhost_number) { + warn "localhost_number=$localhost_number\n"; + } else { + warn "localhost_number unset\n"; + } +} + +sub unpack_time($$) { + my ($seconds, $fractions) = @_; + # warn "encoded: seconds: $seconds fractions: $fractions\n"; + $seconds = decode62($seconds); + $fractions = decode62($fractions) if $fractions; + my $id_resolution; + if (defined $localhost_number && $localhost_number ne "none") { + print "localhost_number $localhost_number\n" if $debug; + if ($base != 62) { + # MacOS/Darwin and Cygwin + $id_resolution = 100; + } else { + # Standard UNIX etc. + $id_resolution = 200; + } + $fractions -= $localhost_number * $id_resolution; + } else { + if ($base != 62) { + # MacOS/Darwin and Cygwin + $id_resolution = 1000; + } else { + # Standard UNIX etc. + $id_resolution = 2000; + } + } + while ($fractions > $id_resolution) { + $seconds++; + $fractions -= $id_resolution; + } + while ($fractions < -1e-7) { + $seconds--; + $fractions += $id_resolution; + } + # $seconds += $fractions / $id_resolution; + + # warn "decoded: seconds: $seconds, fractions: $fractions/$id_resolution\n"; + + return ($seconds, $fractions / $id_resolution); +} # sub unpack_time($$) + +sub print_time($$$$$$) +{ + my ($seconds, $decimal, $unix, $zulu, $localtm, $pid) = @_; + + if ($debug) { + my $ounix = defined($unix) ? $unix : "undef"; + my $ozulu = defined($zulu) ? $zulu : "undef"; + my $olocal = defined($localtm) ? $localtm : "undef"; + my $opid = defined($pid) ? $pid : "undef"; + warn "print_time($seconds, $decimal, $ounix, $ozulu, $olocal, $opid)\n" + } + + my $pidstring = ""; + $pidstring = "\tpid $pid" if defined $pid; + + my $decimalstring = ""; + # if ($decimal>0) + { + $decimalstring = sprintf(".%6.6d", 1000000*$decimal); + } + my $secondsstring; + unless (defined $unix or defined $zulu or defined $localtm) { + warn "No time type requested. Reporting UNIX time\n"; + $unix = TRUE; + } + if (defined $unix) { + $secondsstring = $seconds; + print "$secondsstring$decimalstring$pidstring\n"; + } + if (defined $zulu) { + $secondsstring = strftime("%F %T", gmtime($seconds)); + print "$secondsstring$decimalstring$pidstring\n"; + } + if (defined $localtm) { + $secondsstring = strftime("%F %T%%s %Z%%s\n", localtime($seconds)); + # print "secondstring $secondsstring\n" if $debug; + printf($secondsstring, $decimalstring, $pidstring); + } + +} # sub print_time($$$$$$) + +foreach my $msgid (@ARGV) { + my ($seconds, $pid, $fractions, $decimal); + + if ($msgid =~ + /(^|[\s<])E?([a-zA-Z0-9]{6})-([a-zA-Z0-9]{6})-([a-zA-Z0-9]{2})/) + { + # Should take either the log form of timestamp, + # the Message-ID: header form with the leading 'E', ... + ($seconds, $pid, $fractions) = ($2, $3, $4); + ($seconds, $decimal) = unpack_time($seconds, $fractions); + $pid = decode62($pid); + #warn "$seconds, $pid, $fractions\n"; + } elsif ($msgid =~ /(^|[^0-9A-Za-z])([a-zA-Z0-9]{6})$/) { + # ... or just the timecode section before the first '-' + ($seconds, $pid, $decimal) = (decode62($2), undef, 0); + } else { + warn "$msgid not parsed\n"; + next; + } + + if ($debug) { + print "msgid: $msgid\n"; + my $ogmt = defined($optgmt) ? $optgmt : "undef"; + my $ounix = defined($optunix) ? $optunix : "undef"; + my $olocal = defined($optlocal) ? $optlocal : "undef"; + my $opid = defined($optpid) ? $optpid : "undef"; + print "print_time($seconds, $decimal, $ounix, $ogmt, $olocal, $opid)\n"; + } + $pid = undef unless $optpid; + print_time($seconds, $decimal, $optunix, $optgmt, $optlocal, $pid); +} + +=head1 NAME + + exim_msgdate - Utility to convert an exim message-id to a human readable date+time + +=head1 SYNOPSIS + +B [ -u|--unix | --GMT | --z|-Zulu | --UTC | -l|--local ] + [ --base 36 | --base 62 | --base36 | --base62 | --b36 | --b62 ] + [ --pid ] [ --debug ] [ --localhost_number ] + [ -c c ] + exim-message-id [ | exim-message-id ...] + +B --help|--man + +=head1 DESCRIPTION + +B is a tool which converts an exim message-id to a human +readable form, usuall just the date+time, but with the I<--pid> option +the process id as well. + +=head1 Message IDs: + +Three exim message ID formats are recognized. +In each case the 'X's are taken from the base (see below) which depends upon the platform. + +=over 4 + +=item XXXXXX-XXXXXX-XX + +found in the exim logfile, + +=item EXXXXXX-XXXXXX-XX + +found in the Message-Id header, + +=item XXXXXX + +just the first six characters of the message id. + +=back + +=head1 OPTIONS + +=head2 Time Zones and Unix Time + +=over 4 + +=item B<-u | --unix> + +Display time as seconds since 1 Jan 1970, the Unix Epoch. + +=item B<--GMT> B<-u|--UTC> B<-z|--zulu> + +Display time in GMT/UTC - we assume these are the same. +Zulu time is another name for GMT. + +=item B<-l | --local> + +Display time in the local time-zone. + +Do not confuse this with the L<--localhost_number|/--localhost_number-n> option. + +=back + +The default is the local timezone. + +=head2 User Assistance Options + +=over 4 + +=item B<--help> + +A brief list of the options + +=item B<--man> + +A more detailed manual for B + +=item B<--debug> + +Information about what went wrong, mostly for developers. + +=back + +=head2 Specialized Options + +=over 4 + +=item B<--base> n | B<--base36> | B<--base62> + +The message-id is usually encoded in base-62 (0-9A-Za-z), +but on systems with case-insensitive file systems, such as MacOS and Cygwin, +base-36 (0-9A-Z) is used instead. +The installation script should have set the default appropriately, +but these options allow the default base to be overridden. + +The default matches C; in this installation it is base-BASE_62. + +=item B<--pid> + +Report the process id as well as the date and time in the message-id. + +=item B<--localhost_number> n + +If the Exim configuration option B has been set, +the third and final section of the message-id will include this and +the timer resolution will change (see the Exim Spec. for details). +C reads the Exim config file (see L<--C|/C-full-path-to-exim-configuration-file>) to find this value, +but it can be overridden with this option. + +The value is an integer between 0 and 16, or the value "none" which +means there is no localhost_number. + +Do not confuse this with the L<--local|/l---local> option, which displays times + in the local timezone. + +=item B<--C> B + +This overrides the usual exim search path. +We set C from the exim configfile. + +=item B<-dexim_path> + +The test test harness passes the full path of the C binary, +or here the C being tested. Not currently used. + +=back + +=head1 SEE ALSO: + +L + +L + +=cut commit e95b96eb1dc35bae278f237f9d65734305dba6b2 Author: Jeremy Harris Date: Thu Feb 16 19:02:42 2023 +0000 Solaris: more build-fixing Broken-by: 1e835086d159 See-also: 73d6e13f9b0c diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index f16570d86..b2f5f6028 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -61,6 +61,9 @@ uschar * readconf_printtime(int t) { return NULL; } gstring * +string_catn(gstring * g, const uschar * s, int count) +{ return NULL; } +gstring * string_vformat_trc(gstring * g, const uschar * func, unsigned line, unsigned size_limit, unsigned flags, const char *format, va_list ap) { return NULL; } commit 3b06efc10bd99da03c446e85839c2933b3e309b0 Author: Jeremy Harris Date: Tue Feb 21 20:58:22 2023 +0000 Include address declared malformed, in error message diff --git a/src/src/verify.c b/src/src/verify.c index 125df8d91..5c0a3e408 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -2958,7 +2958,7 @@ dots). */ for (t = ss; isdigit(*t) || *t == '.'; ) t++; if (!*t || (*t == '/' && t != ss)) { - *error = US"malformed IPv4 address or address mask"; + *error = string_sprintf("malformed IPv4 address or address mask: %.*s", (int)(t - ss), ss); return ERROR; } commit 63874787bdc51535a040baa38be3ff07c97f0bdc Author: Jeremy Harris Date: Thu Feb 23 13:39:14 2023 +0000 Split out separate fn for bounce-message send diff --git a/src/src/deliver.c b/src/src/deliver.c index 084a048c9..efb85348c 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -38,8 +38,8 @@ enum { RECIP_ACCEPT, RECIP_IGNORE, RECIP_DEFER, /* Mutually recursive functions for marking addresses done. */ -static void child_done(address_item *, uschar *); -static void address_done(address_item *, uschar *); +static void child_done(address_item *, const uschar *); +static void address_done(address_item *, const uschar *); /* Table for turning base-62 numbers into binary */ @@ -663,7 +663,7 @@ Returns: nothing */ static void -address_done(address_item *addr, uschar *now) +address_done(address_item * addr, const uschar * now) { update_spool = TRUE; /* Ensure spool gets updated */ @@ -720,7 +720,7 @@ Returns: nothing */ static void -child_done(address_item *addr, uschar *now) +child_done(address_item * addr, const uschar * now) { while (addr->parent) { @@ -1458,12 +1458,12 @@ Returns: nothing */ static void -post_process_one(address_item *addr, int result, int logflags, int driver_type, +post_process_one(address_item * addr, int result, int logflags, int driver_type, int logchar) { -uschar *now = tod_stamp(tod_log); -uschar *driver_kind = NULL; -uschar *driver_name = NULL; +uschar * now = tod_stamp(tod_log); +uschar * driver_kind = NULL; +uschar * driver_name = NULL; DEBUG(D_deliver) debug_printf("post-process %s (%d)\n", addr->address, result); @@ -2316,7 +2316,7 @@ if ((pid = exim_fork(US"delivery-local")) == 0) if (addr->transport->setup) switch((addr->transport->setup)(addr->transport, addr, NULL, uid, gid, - &(addr->message))) + &addr->message)) { case DEFER: addr->transport_return = DEFER; @@ -3481,7 +3481,7 @@ while (!done) guarantee it won't be split in the pipe. */ #ifndef DISABLE_TLS - case 'X': + case 'X': /* TLS details */ if (!addr) goto ADDR_MISMATCH; /* Below, in 'A' handler */ switch (*subid) { @@ -3565,7 +3565,7 @@ while (!done) DEBUG(D_deliver) debug_printf("DSN read: addr->dsn_aware = %d\n", addr->dsn_aware); break; - case 'A': + case 'A': /* Per-address info */ if (!addr) { ADDR_MISMATCH: @@ -3609,7 +3609,7 @@ while (!done) break; #endif - case '0': + case '0': /* results of trying to send to this address */ DEBUG(D_deliver) debug_printf("A0 %s tret %d\n", addr->address, *ptr); addr->transport_return = *ptr++; addr->special_action = *ptr++; @@ -3758,7 +3758,7 @@ Returns: nothing */ static void -remote_post_process(address_item *addr, int logflags, uschar *msg, +remote_post_process(address_item * addr, int logflags, uschar * msg, BOOL fallback) { /* If any host addresses were found to be unusable, add them to the unusable @@ -3773,7 +3773,7 @@ into the special_action field for each successful delivery. */ while (addr) { - address_item *next = addr->next; + address_item * next = addr->next; /* If msg == NULL (normal processing) and the result is DEFER and we are processing the main hosts and there are fallback hosts available, put the @@ -4098,7 +4098,7 @@ par_reduce(int max, BOOL fallback) { while (parcount > max) { - address_item *doneaddr = par_wait(); + address_item * doneaddr = par_wait(); if (!doneaddr) { log_write(0, LOG_MAIN|LOG_PANIC, @@ -5552,6 +5552,447 @@ else if (!(fp = Ufopen(s, "rb"))) return fp; } +/************************************************* +* Send a bounce message * +*************************************************/ + +/* Find the error address for the first address, then send a message that +includes all failed addresses that have the same error address. Note the +bounce_recipient is a global so that it can be accessed by $bounce_recipient +while creating a customized error message. */ + +static void +send_bounce_message(time_t now, const uschar * logtod) +{ +pid_t pid; +int fd; + +if (!(bounce_recipient = addr_failed->prop.errors_address)) + bounce_recipient = sender_address; + +/* Make a subprocess to send a message, using its stdin */ + +if ((pid = child_open_exim(&fd, US"bounce-message")) < 0) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Process %d (parent %d) failed to " + "create child process to send failure message: %s", getpid(), + getppid(), strerror(errno)); + +/* Creation of child succeeded */ + +else + { + int ch, rc, filecount = 0, rcount = 0; + uschar * bcc, * emf_text; + FILE * fp = fdopen(fd, "wb"), * emf = NULL; + BOOL to_sender = strcmpic(sender_address, bounce_recipient) == 0; + int max = (bounce_return_size_limit/DELIVER_IN_BUFFER_SIZE + 1) * + DELIVER_IN_BUFFER_SIZE; + uschar * bound, * dsnlimitmsg, * dsnnotifyhdr; + int topt; + address_item ** paddr; + address_item * msgchain = NULL, ** pmsgchain = &msgchain; + address_item * handled_addr = NULL; + + DEBUG(D_deliver) + debug_printf("sending error message to: %s\n", bounce_recipient); + + /* Scan the addresses for all that have the same errors address, removing + them from the addr_failed chain, and putting them on msgchain. */ + + paddr = &addr_failed; + for (address_item * addr = addr_failed; addr; addr = *paddr) + if (Ustrcmp(bounce_recipient, addr->prop.errors_address + ? addr->prop.errors_address : sender_address) == 0) + { /* The same - dechain */ + *paddr = addr->next; + *pmsgchain = addr; + addr->next = NULL; + pmsgchain = &addr->next; + } + else + paddr = &addr->next; /* Not the same; skip */ + + /* Include X-Failed-Recipients: for automatic interpretation, but do + not let any one header line get too long. We do this by starting a + new header every 50 recipients. Omit any addresses for which the + "hide_child" flag is set. */ + + for (address_item * addr = msgchain; addr; addr = addr->next) + { + if (testflag(addr, af_hide_child)) continue; + if (rcount >= 50) + { + fprintf(fp, "\n"); + rcount = 0; + } + fprintf(fp, "%s%s", + rcount++ == 0 + ? "X-Failed-Recipients: " + : ",\n ", + testflag(addr, af_pfr) && addr->parent + ? string_printing(addr->parent->address) + : string_printing(addr->address)); + } + if (rcount > 0) fprintf(fp, "\n"); + + /* Output the standard headers */ + + if (errors_reply_to) + fprintf(fp, "Reply-To: %s\n", errors_reply_to); + fprintf(fp, "Auto-Submitted: auto-replied\n"); + moan_write_from(fp); + fprintf(fp, "To: %s\n", bounce_recipient); + moan_write_references(fp, NULL); + + /* generate boundary string and output MIME-Headers */ + bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); + + fprintf(fp, "Content-Type: multipart/report;" + " report-type=delivery-status; boundary=%s\n" + "MIME-Version: 1.0\n", + bound); + + /* Open a template file if one is provided. Log failure to open, but + carry on - default texts will be used. */ + + if (bounce_message_file) + emf = expand_open(bounce_message_file, + US"bounce_message_file", US"error"); + + /* Quietly copy to configured additional addresses if required. */ + + if ((bcc = moan_check_errorcopy(bounce_recipient))) + fprintf(fp, "Bcc: %s\n", bcc); + + /* The texts for the message can be read from a template file; if there + isn't one, or if it is too short, built-in texts are used. The first + emf text is a Subject: and any other headers. */ + + if ((emf_text = next_emf(emf, US"header"))) + fprintf(fp, "%s\n", emf_text); + else + fprintf(fp, "Subject: Mail delivery failed%s\n\n", + to_sender? ": returning message to sender" : ""); + + /* output human readable part as text/plain section */ + fprintf(fp, "--%s\n" + "Content-type: text/plain; charset=us-ascii\n\n", + bound); + + if ((emf_text = next_emf(emf, US"intro"))) + fprintf(fp, "%s", CS emf_text); + else + { + fprintf(fp, +/* This message has been reworded several times. It seems to be confusing to +somebody, however it is worded. I have retreated to the original, simple +wording. */ +"This message was created automatically by mail delivery software.\n"); + + if (bounce_message_text) + fprintf(fp, "%s", CS bounce_message_text); + if (to_sender) + fprintf(fp, +"\nA message that you sent could not be delivered to one or more of its\n" +"recipients. This is a permanent error. The following address(es) failed:\n"); + else + fprintf(fp, +"\nA message sent by\n\n <%s>\n\n" +"could not be delivered to one or more of its recipients. The following\n" +"address(es) failed:\n", sender_address); + } + fputc('\n', fp); + + /* Process the addresses, leaving them on the msgchain if they have a + file name for a return message. (There has already been a check in + post_process_one() for the existence of data in the message file.) A TRUE + return from print_address_information() means that the address is not + hidden. */ + + paddr = &msgchain; + for (address_item * addr = msgchain; addr; addr = *paddr) + { + if (print_address_information(addr, fp, US" ", US"\n ", US"")) + print_address_error(addr, fp, US""); + + /* End the final line for the address */ + + fputc('\n', fp); + + /* Leave on msgchain if there's a return file. */ + + if (addr->return_file >= 0) + { + paddr = &(addr->next); + filecount++; + } + + /* Else save so that we can tick off the recipient when the + message is sent. */ + + else + { + *paddr = addr->next; + addr->next = handled_addr; + handled_addr = addr; + } + } + + fputc('\n', fp); + + /* Get the next text, whether we need it or not, so as to be + positioned for the one after. */ + + emf_text = next_emf(emf, US"generated text"); + + /* If there were any file messages passed by the local transports, + include them in the message. Then put the address on the handled chain. + In the case of a batch of addresses that were all sent to the same + transport, the return_file field in all of them will contain the same + fd, and the return_filename field in the *last* one will be set (to the + name of the file). */ + + if (msgchain) + { + address_item * nextaddr; + + if (emf_text) + fprintf(fp, "%s", CS emf_text); + else + fprintf(fp, + "The following text was generated during the delivery " + "attempt%s:\n", (filecount > 1)? "s" : ""); + + for (address_item * addr = msgchain; addr; addr = nextaddr) + { + FILE *fm; + address_item *topaddr = addr; + + /* List all the addresses that relate to this file */ + + fputc('\n', fp); + while(addr) /* Insurance */ + { + print_address_information(addr, fp, US"------ ", US"\n ", + US" ------\n"); + if (addr->return_filename) break; + addr = addr->next; + } + fputc('\n', fp); + + /* Now copy the file */ + + if (!(fm = Ufopen(addr->return_filename, "rb"))) + fprintf(fp, " +++ Exim error... failed to open text file: %s\n", + strerror(errno)); + else + { + while ((ch = fgetc(fm)) != EOF) fputc(ch, fp); + (void)fclose(fm); + } + Uunlink(addr->return_filename); + + /* Can now add to handled chain, first fishing off the next + address on the msgchain. */ + + nextaddr = addr->next; + addr->next = handled_addr; + handled_addr = topaddr; + } + fputc('\n', fp); + } + + /* output machine readable part */ +#ifdef SUPPORT_I18N + if (message_smtputf8) + fprintf(fp, "--%s\n" + "Content-type: message/global-delivery-status\n\n" + "Reporting-MTA: dns; %s\n", + bound, smtp_active_hostname); + else +#endif + fprintf(fp, "--%s\n" + "Content-type: message/delivery-status\n\n" + "Reporting-MTA: dns; %s\n", + bound, smtp_active_hostname); + + if (dsn_envid) + { + /* must be decoded from xtext: see RFC 3461:6.3a */ + uschar *xdec_envid; + if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0) + fprintf(fp, "Original-Envelope-ID: %s\n", dsn_envid); + else + fprintf(fp, "X-Original-Envelope-ID: error decoding xtext formatted ENVID\n"); + } + fputc('\n', fp); + + for (address_item * addr = handled_addr; addr; addr = addr->next) + { + host_item * hu; + + print_dsn_addr_action(fp, addr, US"failed", US"5.0.0"); + + if ((hu = addr->host_used) && hu->name) + { + fprintf(fp, "Remote-MTA: dns; %s\n", hu->name); +#ifdef EXPERIMENTAL_DSN_INFO + { + const uschar * s; + if (hu->address) + { + uschar * p = hu->port == 25 + ? US"" : string_sprintf(":%d", hu->port); + fprintf(fp, "Remote-MTA: X-ip; [%s]%s\n", hu->address, p); + } + if ((s = addr->smtp_greeting) && *s) + fprintf(fp, "X-Remote-MTA-smtp-greeting: X-str; %.900s\n", s); + if ((s = addr->helo_response) && *s) + fprintf(fp, "X-Remote-MTA-helo-response: X-str; %.900s\n", s); + if ((s = addr->message) && *s) + fprintf(fp, "X-Exim-Diagnostic: X-str; %.900s\n", s); + } +#endif + print_dsn_diagnostic_code(addr, fp); + } + fputc('\n', fp); + } + + /* Now copy the message, trying to give an intelligible comment if + it is too long for it all to be copied. The limit isn't strictly + applied because of the buffering. There is, however, an option + to suppress copying altogether. */ + + emf_text = next_emf(emf, US"copy"); + + /* add message body + we ignore the intro text from template and add + the text for bounce_return_size_limit at the end. + + bounce_return_message is ignored + in case RET= is defined we honor these values + otherwise bounce_return_body is honored. + + bounce_return_size_limit is always honored. + */ + + fprintf(fp, "--%s\n", bound); + + dsnlimitmsg = US"X-Exim-DSN-Information: Due to administrative limits only headers are returned"; + dsnnotifyhdr = NULL; + topt = topt_add_return_path; + + /* RET=HDRS? top priority */ + if (dsn_ret == dsn_ret_hdrs) + topt |= topt_no_body; + else + { + struct stat statbuf; + + /* no full body return at all? */ + if (!bounce_return_body) + { + topt |= topt_no_body; + /* add header if we overrule RET=FULL */ + if (dsn_ret == dsn_ret_full) + dsnnotifyhdr = dsnlimitmsg; + } + /* line length limited... return headers only if oversize */ + /* size limited ... return headers only if limit reached */ + else if ( max_received_linelength > bounce_return_linesize_limit + || ( bounce_return_size_limit > 0 + && fstat(deliver_datafile, &statbuf) == 0 + && statbuf.st_size > max + ) ) + { + topt |= topt_no_body; + dsnnotifyhdr = dsnlimitmsg; + } + } + +#ifdef SUPPORT_I18N + if (message_smtputf8) + fputs(topt & topt_no_body ? "Content-type: message/global-headers\n\n" + : "Content-type: message/global\n\n", + fp); + else +#endif + fputs(topt & topt_no_body ? "Content-type: text/rfc822-headers\n\n" + : "Content-type: message/rfc822\n\n", + fp); + + fflush(fp); + transport_filter_argv = NULL; /* Just in case */ + return_path = sender_address; /* In case not previously set */ + { /* Dummy transport for headers add */ + transport_ctx tctx = {{0}}; + transport_instance tb = {0}; + + tctx.u.fd = fileno(fp); + tctx.tblock = &tb; + tctx.options = topt; + tb.add_headers = dsnnotifyhdr; + + /*XXX no checking for failure! buggy! */ + transport_write_message(&tctx, 0); + } + fflush(fp); + + /* we never add the final text. close the file */ + if (emf) + (void)fclose(emf); + + fprintf(fp, "\n--%s--\n", bound); + + /* Close the file, which should send an EOF to the child process + that is receiving the message. Wait for it to finish. */ + + (void)fclose(fp); + rc = child_close(pid, 0); /* Waits for child to close, no timeout */ + + /* If the process failed, there was some disaster in setting up the + error message. Unless the message is very old, ensure that addr_defer + is non-null, which will have the effect of leaving the message on the + spool. The failed addresses will get tried again next time. However, we + don't really want this to happen too often, so freeze the message unless + there are some genuine deferred addresses to try. To do this we have + to call spool_write_header() here, because with no genuine deferred + addresses the normal code below doesn't get run. */ + + if (rc != 0) + { + uschar *s = US""; + if (now - received_time.tv_sec < retry_maximum_timeout && !addr_defer) + { + addr_defer = (address_item *)(+1); + f.deliver_freeze = TRUE; + deliver_frozen_at = time(NULL); + /* Panic-dies on error */ + (void)spool_write_header(message_id, SW_DELIVERING, NULL); + s = US" (frozen)"; + } + deliver_msglog("Process failed (%d) when writing error message " + "to %s%s", rc, bounce_recipient, s); + log_write(0, LOG_MAIN, "Process failed (%d) when writing error message " + "to %s%s", rc, bounce_recipient, s); + } + + /* The message succeeded. Ensure that the recipients that failed are + now marked finished with on the spool and their parents updated. */ + + else + { + for (address_item * addr = handled_addr; addr; addr = addr->next) + { + address_done(addr, logtod); + child_done(addr, logtod); + } + /* Panic-dies on error */ + (void)spool_write_header(message_id, SW_DELIVERING, NULL); + } + } +} + /************************************************* * Deliver one message * *************************************************/ @@ -6394,7 +6835,7 @@ deliver_out_buffer = store_malloc(DELIVER_OUT_BUFFER_SIZE); f.header_rewritten = FALSE; /* No headers rewritten yet */ while (addr_new) /* Loop until all addresses dealt with */ { - address_item *addr, *parent; + address_item * addr, * parent; /* Failure to open the retry database is treated the same as if it does not exist. In both cases, dbm_file is NULL. */ @@ -7449,7 +7890,7 @@ if (addr_senddsn) if (dsn_envid) { /* must be decoded from xtext: see RFC 3461:6.3a */ - uschar *xdec_envid; + uschar * xdec_envid; if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0) fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid); else @@ -7501,14 +7942,8 @@ requirements. */ while (addr_failed) { - pid_t pid; - int fd; - uschar *logtod = tod_stamp(tod_log); - address_item *addr; - address_item *handled_addr = NULL; - address_item **paddr; - address_item *msgchain = NULL; - address_item **pmsgchain = &msgchain; + const uschar * logtod = tod_stamp(tod_log); + address_item * addr; /* There are weird cases when logging is disabled in the transport. However, there may not be a transport (address failed by a router). */ @@ -7578,439 +8013,10 @@ while (addr_failed) /* Otherwise, handle the sending of a message. Find the error address for the first address, then send a message that includes all failed addresses - that have the same error address. Note the bounce_recipient is a global so - that it can be accessed by $bounce_recipient while creating a customized - error message. */ + that have the same error address. */ else - { - if (!(bounce_recipient = addr_failed->prop.errors_address)) - bounce_recipient = sender_address; - - /* Make a subprocess to send a message */ - - if ((pid = child_open_exim(&fd, US"bounce-message")) < 0) - log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Process %d (parent %d) failed to " - "create child process to send failure message: %s", getpid(), - getppid(), strerror(errno)); - - /* Creation of child succeeded */ - - else - { - int ch, rc; - int filecount = 0; - int rcount = 0; - uschar *bcc, *emf_text; - FILE * fp = fdopen(fd, "wb"); - FILE * emf = NULL; - BOOL to_sender = strcmpic(sender_address, bounce_recipient) == 0; - int max = (bounce_return_size_limit/DELIVER_IN_BUFFER_SIZE + 1) * - DELIVER_IN_BUFFER_SIZE; - uschar * bound; - uschar *dsnlimitmsg; - uschar *dsnnotifyhdr; - int topt; - - DEBUG(D_deliver) - debug_printf("sending error message to: %s\n", bounce_recipient); - - /* Scan the addresses for all that have the same errors address, removing - them from the addr_failed chain, and putting them on msgchain. */ - - paddr = &addr_failed; - for (addr = addr_failed; addr; addr = *paddr) - if (Ustrcmp(bounce_recipient, addr->prop.errors_address - ? addr->prop.errors_address : sender_address) == 0) - { /* The same - dechain */ - *paddr = addr->next; - *pmsgchain = addr; - addr->next = NULL; - pmsgchain = &(addr->next); - } - else - paddr = &addr->next; /* Not the same; skip */ - - /* Include X-Failed-Recipients: for automatic interpretation, but do - not let any one header line get too long. We do this by starting a - new header every 50 recipients. Omit any addresses for which the - "hide_child" flag is set. */ - - for (addr = msgchain; addr; addr = addr->next) - { - if (testflag(addr, af_hide_child)) continue; - if (rcount >= 50) - { - fprintf(fp, "\n"); - rcount = 0; - } - fprintf(fp, "%s%s", - rcount++ == 0 - ? "X-Failed-Recipients: " - : ",\n ", - testflag(addr, af_pfr) && addr->parent - ? string_printing(addr->parent->address) - : string_printing(addr->address)); - } - if (rcount > 0) fprintf(fp, "\n"); - - /* Output the standard headers */ - - if (errors_reply_to) - fprintf(fp, "Reply-To: %s\n", errors_reply_to); - fprintf(fp, "Auto-Submitted: auto-replied\n"); - moan_write_from(fp); - fprintf(fp, "To: %s\n", bounce_recipient); - moan_write_references(fp, NULL); - - /* generate boundary string and output MIME-Headers */ - bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); - - fprintf(fp, "Content-Type: multipart/report;" - " report-type=delivery-status; boundary=%s\n" - "MIME-Version: 1.0\n", - bound); - - /* Open a template file if one is provided. Log failure to open, but - carry on - default texts will be used. */ - - if (bounce_message_file) - emf = expand_open(bounce_message_file, - US"bounce_message_file", US"error"); - - /* Quietly copy to configured additional addresses if required. */ - - if ((bcc = moan_check_errorcopy(bounce_recipient))) - fprintf(fp, "Bcc: %s\n", bcc); - - /* The texts for the message can be read from a template file; if there - isn't one, or if it is too short, built-in texts are used. The first - emf text is a Subject: and any other headers. */ - - if ((emf_text = next_emf(emf, US"header"))) - fprintf(fp, "%s\n", emf_text); - else - fprintf(fp, "Subject: Mail delivery failed%s\n\n", - to_sender? ": returning message to sender" : ""); - - /* output human readable part as text/plain section */ - fprintf(fp, "--%s\n" - "Content-type: text/plain; charset=us-ascii\n\n", - bound); - - if ((emf_text = next_emf(emf, US"intro"))) - fprintf(fp, "%s", CS emf_text); - else - { - fprintf(fp, -/* This message has been reworded several times. It seems to be confusing to -somebody, however it is worded. I have retreated to the original, simple -wording. */ -"This message was created automatically by mail delivery software.\n"); - - if (bounce_message_text) - fprintf(fp, "%s", CS bounce_message_text); - if (to_sender) - fprintf(fp, -"\nA message that you sent could not be delivered to one or more of its\n" -"recipients. This is a permanent error. The following address(es) failed:\n"); - else - fprintf(fp, -"\nA message sent by\n\n <%s>\n\n" -"could not be delivered to one or more of its recipients. The following\n" -"address(es) failed:\n", sender_address); - } - fputc('\n', fp); - - /* Process the addresses, leaving them on the msgchain if they have a - file name for a return message. (There has already been a check in - post_process_one() for the existence of data in the message file.) A TRUE - return from print_address_information() means that the address is not - hidden. */ - - paddr = &msgchain; - for (addr = msgchain; addr; addr = *paddr) - { - if (print_address_information(addr, fp, US" ", US"\n ", US"")) - print_address_error(addr, fp, US""); - - /* End the final line for the address */ - - fputc('\n', fp); - - /* Leave on msgchain if there's a return file. */ - - if (addr->return_file >= 0) - { - paddr = &(addr->next); - filecount++; - } - - /* Else save so that we can tick off the recipient when the - message is sent. */ - - else - { - *paddr = addr->next; - addr->next = handled_addr; - handled_addr = addr; - } - } - - fputc('\n', fp); - - /* Get the next text, whether we need it or not, so as to be - positioned for the one after. */ - - emf_text = next_emf(emf, US"generated text"); - - /* If there were any file messages passed by the local transports, - include them in the message. Then put the address on the handled chain. - In the case of a batch of addresses that were all sent to the same - transport, the return_file field in all of them will contain the same - fd, and the return_filename field in the *last* one will be set (to the - name of the file). */ - - if (msgchain) - { - address_item *nextaddr; - - if (emf_text) - fprintf(fp, "%s", CS emf_text); - else - fprintf(fp, - "The following text was generated during the delivery " - "attempt%s:\n", (filecount > 1)? "s" : ""); - - for (addr = msgchain; addr; addr = nextaddr) - { - FILE *fm; - address_item *topaddr = addr; - - /* List all the addresses that relate to this file */ - - fputc('\n', fp); - while(addr) /* Insurance */ - { - print_address_information(addr, fp, US"------ ", US"\n ", - US" ------\n"); - if (addr->return_filename) break; - addr = addr->next; - } - fputc('\n', fp); - - /* Now copy the file */ - - if (!(fm = Ufopen(addr->return_filename, "rb"))) - fprintf(fp, " +++ Exim error... failed to open text file: %s\n", - strerror(errno)); - else - { - while ((ch = fgetc(fm)) != EOF) fputc(ch, fp); - (void)fclose(fm); - } - Uunlink(addr->return_filename); - - /* Can now add to handled chain, first fishing off the next - address on the msgchain. */ - - nextaddr = addr->next; - addr->next = handled_addr; - handled_addr = topaddr; - } - fputc('\n', fp); - } - - /* output machine readable part */ -#ifdef SUPPORT_I18N - if (message_smtputf8) - fprintf(fp, "--%s\n" - "Content-type: message/global-delivery-status\n\n" - "Reporting-MTA: dns; %s\n", - bound, smtp_active_hostname); - else -#endif - fprintf(fp, "--%s\n" - "Content-type: message/delivery-status\n\n" - "Reporting-MTA: dns; %s\n", - bound, smtp_active_hostname); - - if (dsn_envid) - { - /* must be decoded from xtext: see RFC 3461:6.3a */ - uschar *xdec_envid; - if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0) - fprintf(fp, "Original-Envelope-ID: %s\n", dsn_envid); - else - fprintf(fp, "X-Original-Envelope-ID: error decoding xtext formatted ENVID\n"); - } - fputc('\n', fp); - - for (addr = handled_addr; addr; addr = addr->next) - { - host_item * hu; - - print_dsn_addr_action(fp, addr, US"failed", US"5.0.0"); - - if ((hu = addr->host_used) && hu->name) - { - fprintf(fp, "Remote-MTA: dns; %s\n", hu->name); -#ifdef EXPERIMENTAL_DSN_INFO - { - const uschar * s; - if (hu->address) - { - uschar * p = hu->port == 25 - ? US"" : string_sprintf(":%d", hu->port); - fprintf(fp, "Remote-MTA: X-ip; [%s]%s\n", hu->address, p); - } - if ((s = addr->smtp_greeting) && *s) - fprintf(fp, "X-Remote-MTA-smtp-greeting: X-str; %.900s\n", s); - if ((s = addr->helo_response) && *s) - fprintf(fp, "X-Remote-MTA-helo-response: X-str; %.900s\n", s); - if ((s = addr->message) && *s) - fprintf(fp, "X-Exim-Diagnostic: X-str; %.900s\n", s); - } -#endif - print_dsn_diagnostic_code(addr, fp); - } - fputc('\n', fp); - } - - /* Now copy the message, trying to give an intelligible comment if - it is too long for it all to be copied. The limit isn't strictly - applied because of the buffering. There is, however, an option - to suppress copying altogether. */ - - emf_text = next_emf(emf, US"copy"); - - /* add message body - we ignore the intro text from template and add - the text for bounce_return_size_limit at the end. - - bounce_return_message is ignored - in case RET= is defined we honor these values - otherwise bounce_return_body is honored. - - bounce_return_size_limit is always honored. - */ - - fprintf(fp, "--%s\n", bound); - - dsnlimitmsg = US"X-Exim-DSN-Information: Due to administrative limits only headers are returned"; - dsnnotifyhdr = NULL; - topt = topt_add_return_path; - - /* RET=HDRS? top priority */ - if (dsn_ret == dsn_ret_hdrs) - topt |= topt_no_body; - else - { - struct stat statbuf; - - /* no full body return at all? */ - if (!bounce_return_body) - { - topt |= topt_no_body; - /* add header if we overrule RET=FULL */ - if (dsn_ret == dsn_ret_full) - dsnnotifyhdr = dsnlimitmsg; - } - /* line length limited... return headers only if oversize */ - /* size limited ... return headers only if limit reached */ - else if ( max_received_linelength > bounce_return_linesize_limit - || ( bounce_return_size_limit > 0 - && fstat(deliver_datafile, &statbuf) == 0 - && statbuf.st_size > max - ) ) - { - topt |= topt_no_body; - dsnnotifyhdr = dsnlimitmsg; - } - } - -#ifdef SUPPORT_I18N - if (message_smtputf8) - fputs(topt & topt_no_body ? "Content-type: message/global-headers\n\n" - : "Content-type: message/global\n\n", - fp); - else -#endif - fputs(topt & topt_no_body ? "Content-type: text/rfc822-headers\n\n" - : "Content-type: message/rfc822\n\n", - fp); - - fflush(fp); - transport_filter_argv = NULL; /* Just in case */ - return_path = sender_address; /* In case not previously set */ - { /* Dummy transport for headers add */ - transport_ctx tctx = {{0}}; - transport_instance tb = {0}; - - tctx.u.fd = fileno(fp); - tctx.tblock = &tb; - tctx.options = topt; - tb.add_headers = dsnnotifyhdr; - - /*XXX no checking for failure! buggy! */ - transport_write_message(&tctx, 0); - } - fflush(fp); - - /* we never add the final text. close the file */ - if (emf) - (void)fclose(emf); - - fprintf(fp, "\n--%s--\n", bound); - - /* Close the file, which should send an EOF to the child process - that is receiving the message. Wait for it to finish. */ - - (void)fclose(fp); - rc = child_close(pid, 0); /* Waits for child to close, no timeout */ - - /* If the process failed, there was some disaster in setting up the - error message. Unless the message is very old, ensure that addr_defer - is non-null, which will have the effect of leaving the message on the - spool. The failed addresses will get tried again next time. However, we - don't really want this to happen too often, so freeze the message unless - there are some genuine deferred addresses to try. To do this we have - to call spool_write_header() here, because with no genuine deferred - addresses the normal code below doesn't get run. */ - - if (rc != 0) - { - uschar *s = US""; - if (now - received_time.tv_sec < retry_maximum_timeout && !addr_defer) - { - addr_defer = (address_item *)(+1); - f.deliver_freeze = TRUE; - deliver_frozen_at = time(NULL); - /* Panic-dies on error */ - (void)spool_write_header(message_id, SW_DELIVERING, NULL); - s = US" (frozen)"; - } - deliver_msglog("Process failed (%d) when writing error message " - "to %s%s", rc, bounce_recipient, s); - log_write(0, LOG_MAIN, "Process failed (%d) when writing error message " - "to %s%s", rc, bounce_recipient, s); - } - - /* The message succeeded. Ensure that the recipients that failed are - now marked finished with on the spool and their parents updated. */ - - else - { - for (addr = handled_addr; addr; addr = addr->next) - { - address_done(addr, logtod); - child_done(addr, logtod); - } - /* Panic-dies on error */ - (void)spool_write_header(message_id, SW_DELIVERING, NULL); - } - } - } + send_bounce_message(now, logtod); } f.disable_logging = FALSE; /* In case left set */ commit e6841985b1abe753ea39f12665444234344cdc15 Author: Jeremy Harris Date: Thu Feb 23 14:17:29 2023 +0000 Split out separate fn for delay-warning-message send diff --git a/src/src/deliver.c b/src/src/deliver.c index efb85348c..c86c4bb4b 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -5993,6 +5993,181 @@ wording. */ } } +/************************************************* +* Send a warning message * +*************************************************/ +/* Return: boolean success */ + +static BOOL +send_warning_message(const uschar * recipients, int queue_time, int show_time) +{ +int fd; +pid_t pid = child_open_exim(&fd, US"delay-warning-message"); +FILE * wmf = NULL, * f = fdopen(fd, "wb"); +uschar * wmf_text, * bound; +transport_ctx tctx = {{0}}; + + +if (pid <= 0) return FALSE; + +if (warn_message_file) + wmf = expand_open(warn_message_file, + US"warn_message_file", US"warning"); + +warnmsg_recipients = recipients; +warnmsg_delay = queue_time < 120*60 + ? string_sprintf("%d minutes", show_time/60) + : string_sprintf("%d hours", show_time/3600); + +if (errors_reply_to) + fprintf(f, "Reply-To: %s\n", errors_reply_to); +fprintf(f, "Auto-Submitted: auto-replied\n"); +moan_write_from(f); +fprintf(f, "To: %s\n", recipients); +moan_write_references(f, NULL); + +/* generated boundary string and output MIME-Headers */ +bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); + +fprintf(f, "Content-Type: multipart/report;" + " report-type=delivery-status; boundary=%s\n" + "MIME-Version: 1.0\n", + bound); + +if ((wmf_text = next_emf(wmf, US"header"))) + fprintf(f, "%s\n", wmf_text); +else + fprintf(f, "Subject: Warning: message %s delayed %s\n\n", + message_id, warnmsg_delay); + +/* output human readable part as text/plain section */ +fprintf(f, "--%s\n" + "Content-type: text/plain; charset=us-ascii\n\n", + bound); + +if ((wmf_text = next_emf(wmf, US"intro"))) + fprintf(f, "%s", CS wmf_text); +else + { + fprintf(f, +"This message was created automatically by mail delivery software.\n"); + + if (Ustrcmp(recipients, sender_address) == 0) + fprintf(f, +"A message that you sent has not yet been delivered to one or more of its\n" +"recipients after more than "); + + else + fprintf(f, +"A message sent by\n\n <%s>\n\n" +"has not yet been delivered to one or more of its recipients after more than \n", + sender_address); + + fprintf(f, "%s on the queue on %s.\n\n" + "The message identifier is: %s\n", + warnmsg_delay, primary_hostname, message_id); + + for (header_line * h = header_list; h; h = h->next) + if (strncmpic(h->text, US"Subject:", 8) == 0) + fprintf(f, "The subject of the message is: %s", h->text + 9); + else if (strncmpic(h->text, US"Date:", 5) == 0) + fprintf(f, "The date of the message is: %s", h->text + 6); + fputc('\n', f); + + fprintf(f, "The address%s to which the message has not yet been " + "delivered %s:\n", + !addr_defer->next ? "" : "es", + !addr_defer->next ? "is": "are"); + } + +/* List the addresses, with error information if allowed */ + +fputc('\n', f); +for (address_item * addr = addr_defer; addr; addr = addr->next) + { + if (print_address_information(addr, f, US" ", US"\n ", US"")) + print_address_error(addr, f, US"Delay reason: "); + fputc('\n', f); + } +fputc('\n', f); + +/* Final text */ + +if (wmf) + { + if ((wmf_text = next_emf(wmf, US"final"))) + fprintf(f, "%s", CS wmf_text); + (void)fclose(wmf); + } +else + { + fprintf(f, +"No action is required on your part. Delivery attempts will continue for\n" +"some time, and this warning may be repeated at intervals if the message\n" +"remains undelivered. Eventually the mail delivery software will give up,\n" +"and when that happens, the message will be returned to you.\n"); + } + +/* output machine readable part */ +fprintf(f, "\n--%s\n" + "Content-type: message/delivery-status\n\n" + "Reporting-MTA: dns; %s\n", + bound, + smtp_active_hostname); + + +if (dsn_envid) + { + /* must be decoded from xtext: see RFC 3461:6.3a */ + uschar *xdec_envid; + if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0) + fprintf(f,"Original-Envelope-ID: %s\n", dsn_envid); + else + fprintf(f,"X-Original-Envelope-ID: error decoding xtext formatted ENVID\n"); + } +fputc('\n', f); + +for (address_item * addr = addr_defer; addr; addr = addr->next) + { + host_item * hu; + + print_dsn_addr_action(f, addr, US"delayed", US"4.0.0"); + + if ((hu = addr->host_used) && hu->name) + { + fprintf(f, "Remote-MTA: dns; %s\n", hu->name); + print_dsn_diagnostic_code(addr, f); + } + fputc('\n', f); + } + +fprintf(f, "--%s\n" + "Content-type: text/rfc822-headers\n\n", + bound); + +fflush(f); +/* header only as required by RFC. only failure DSN needs to honor RET=FULL */ +tctx.u.fd = fileno(f); +tctx.options = topt_add_return_path | topt_no_body; +transport_filter_argv = NULL; /* Just in case */ +return_path = sender_address; /* In case not previously set */ + +/* Write the original email out */ +/*XXX no checking for failure! buggy! */ +transport_write_message(&tctx, 0); +fflush(f); + +fprintf(f,"\n--%s--\n", bound); + +fflush(f); + +/* Close and wait for child process to complete, without a timeout. +If there's an error, don't update the count. */ + +(void)fclose(f); +return child_close(pid, 0) == 0; +} + /************************************************* * Deliver one message * *************************************************/ @@ -8115,7 +8290,7 @@ was set just to keep the message on the spool, so there is nothing to do here. else if (addr_defer != (address_item *)(+1)) { - uschar *recipients = US""; + uschar * recipients = US""; BOOL want_warning_msg = FALSE; deliver_domain = testflag(addr_defer, af_pfr) @@ -8123,7 +8298,7 @@ else if (addr_defer != (address_item *)(+1)) for (address_item * addr = addr_defer; addr; addr = addr->next) { - address_item *otaddr; + address_item * otaddr; if (addr->basic_errno > ERRNO_WARN_BASE) want_warning_msg = TRUE; @@ -8245,181 +8420,11 @@ else if (addr_defer != (address_item *)(+1)) have been. */ if (warning_count < count) - { - header_line *h; - int fd; - pid_t pid = child_open_exim(&fd, US"delay-warning-message"); - - if (pid > 0) - { - uschar * wmf_text; - FILE * wmf = NULL; - FILE * f = fdopen(fd, "wb"); - uschar * bound; - transport_ctx tctx = {{0}}; - - if (warn_message_file) - wmf = expand_open(warn_message_file, - US"warn_message_file", US"warning"); - - warnmsg_recipients = recipients; - warnmsg_delay = queue_time < 120*60 - ? string_sprintf("%d minutes", show_time/60) - : string_sprintf("%d hours", show_time/3600); - - if (errors_reply_to) - fprintf(f, "Reply-To: %s\n", errors_reply_to); - fprintf(f, "Auto-Submitted: auto-replied\n"); - moan_write_from(f); - fprintf(f, "To: %s\n", recipients); - moan_write_references(f, NULL); - - /* generated boundary string and output MIME-Headers */ - bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); - - fprintf(f, "Content-Type: multipart/report;" - " report-type=delivery-status; boundary=%s\n" - "MIME-Version: 1.0\n", - bound); - - if ((wmf_text = next_emf(wmf, US"header"))) - fprintf(f, "%s\n", wmf_text); - else - fprintf(f, "Subject: Warning: message %s delayed %s\n\n", - message_id, warnmsg_delay); - - /* output human readable part as text/plain section */ - fprintf(f, "--%s\n" - "Content-type: text/plain; charset=us-ascii\n\n", - bound); - - if ((wmf_text = next_emf(wmf, US"intro"))) - fprintf(f, "%s", CS wmf_text); - else - { - fprintf(f, -"This message was created automatically by mail delivery software.\n"); - - if (Ustrcmp(recipients, sender_address) == 0) - fprintf(f, -"A message that you sent has not yet been delivered to one or more of its\n" -"recipients after more than "); - - else - fprintf(f, -"A message sent by\n\n <%s>\n\n" -"has not yet been delivered to one or more of its recipients after more than \n", - sender_address); - - fprintf(f, "%s on the queue on %s.\n\n" - "The message identifier is: %s\n", - warnmsg_delay, primary_hostname, message_id); - - for (h = header_list; h; h = h->next) - if (strncmpic(h->text, US"Subject:", 8) == 0) - fprintf(f, "The subject of the message is: %s", h->text + 9); - else if (strncmpic(h->text, US"Date:", 5) == 0) - fprintf(f, "The date of the message is: %s", h->text + 6); - fputc('\n', f); - - fprintf(f, "The address%s to which the message has not yet been " - "delivered %s:\n", - !addr_defer->next ? "" : "es", - !addr_defer->next ? "is": "are"); - } - - /* List the addresses, with error information if allowed */ - - fputc('\n', f); - for (address_item * addr = addr_defer; addr; addr = addr->next) - { - if (print_address_information(addr, f, US" ", US"\n ", US"")) - print_address_error(addr, f, US"Delay reason: "); - fputc('\n', f); - } - fputc('\n', f); - - /* Final text */ - - if (wmf) - { - if ((wmf_text = next_emf(wmf, US"final"))) - fprintf(f, "%s", CS wmf_text); - (void)fclose(wmf); - } - else - { - fprintf(f, -"No action is required on your part. Delivery attempts will continue for\n" -"some time, and this warning may be repeated at intervals if the message\n" -"remains undelivered. Eventually the mail delivery software will give up,\n" -"and when that happens, the message will be returned to you.\n"); - } - - /* output machine readable part */ - fprintf(f, "\n--%s\n" - "Content-type: message/delivery-status\n\n" - "Reporting-MTA: dns; %s\n", - bound, - smtp_active_hostname); - - - if (dsn_envid) - { - /* must be decoded from xtext: see RFC 3461:6.3a */ - uschar *xdec_envid; - if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0) - fprintf(f,"Original-Envelope-ID: %s\n", dsn_envid); - else - fprintf(f,"X-Original-Envelope-ID: error decoding xtext formatted ENVID\n"); - } - fputc('\n', f); - - for (address_item * addr = addr_defer; addr; addr = addr->next) - { - host_item * hu; - - print_dsn_addr_action(f, addr, US"delayed", US"4.0.0"); - - if ((hu = addr->host_used) && hu->name) - { - fprintf(f, "Remote-MTA: dns; %s\n", hu->name); - print_dsn_diagnostic_code(addr, f); - } - fputc('\n', f); - } - - fprintf(f, "--%s\n" - "Content-type: text/rfc822-headers\n\n", - bound); - - fflush(f); - /* header only as required by RFC. only failure DSN needs to honor RET=FULL */ - tctx.u.fd = fileno(f); - tctx.options = topt_add_return_path | topt_no_body; - transport_filter_argv = NULL; /* Just in case */ - return_path = sender_address; /* In case not previously set */ - - /* Write the original email out */ - /*XXX no checking for failure! buggy! */ - transport_write_message(&tctx, 0); - fflush(f); - - fprintf(f,"\n--%s--\n", bound); - - fflush(f); - - /* Close and wait for child process to complete, without a timeout. - If there's an error, don't update the count. */ - - (void)fclose(f); - if (child_close(pid, 0) == 0) - { - warning_count = count; - update_spool = TRUE; /* Ensure spool rewritten */ - } - } - } + if (send_warning_message(recipients, queue_time, show_time)) + { + warning_count = count; + update_spool = TRUE; /* Ensure spool rewritten */ + } } /* Clear deliver_domain */ diff --git a/src/src/globals.c b/src/src/globals.c index 78b225fbc..c6bacc02f 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -1657,8 +1657,8 @@ uschar *version_string = US"?"; uschar *warn_message_file = NULL; int warning_count = 0; -uschar *warnmsg_delay = NULL; -uschar *warnmsg_recipients = NULL; +const uschar *warnmsg_delay = NULL; +const uschar *warnmsg_recipients = NULL; /* End of globals.c */ diff --git a/src/src/globals.h b/src/src/globals.h index 914e2d0f9..81d052fd5 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -1114,8 +1114,8 @@ extern uschar *uucp_from_pattern; /* For recognizing "From " lines */ extern uschar *uucp_from_sender; /* For building the sender */ extern uschar *warn_message_file; /* Template for warning messages */ -extern uschar *warnmsg_delay; /* String form of delay time */ -extern uschar *warnmsg_recipients; /* Recipients of warning message */ +extern const uschar *warnmsg_delay; /* String form of delay time */ +extern const uschar *warnmsg_recipients; /* Recipients of warning message */ extern BOOL write_rejectlog; /* Control of reject logging */ extern uschar *verify_mode; /* Running a router in verify mode */ commit e603a342100ed93b09a152b6150e65f77abe5b0d Author: Jeremy Harris Date: Thu Feb 23 14:33:05 2023 +0000 Split out separate fn for success-DSN send diff --git a/src/src/deliver.c b/src/src/deliver.c index c86c4bb4b..993b24231 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -68,7 +68,6 @@ static address_item *addr_new = NULL; static address_item *addr_remote = NULL; static address_item *addr_route = NULL; static address_item *addr_succeed = NULL; -static address_item *addr_senddsn = NULL; static FILE *message_log = NULL; static BOOL update_spool; @@ -6168,6 +6167,162 @@ If there's an error, don't update the count. */ return child_close(pid, 0) == 0; } +/************************************************* +* Send a success-DSN * +*************************************************/ + +static void +maybe_send_dsn(void) +{ +address_item * addr_senddsn = NULL; + +for (address_item * a = addr_succeed; a; a = a->next) + { + /* af_ignore_error not honored here. it's not an error */ + DEBUG(D_deliver) debug_printf("DSN: processing router : %s\n" + "DSN: processing successful delivery address: %s\n" + "DSN: Sender_address: %s\n" + "DSN: orcpt: %s flags: 0x%x\n" + "DSN: envid: %s ret: %d\n" + "DSN: Final recipient: %s\n" + "DSN: Remote SMTP server supports DSN: %d\n", + a->router ? a->router->name : US"(unknown)", + a->address, + sender_address, + a->dsn_orcpt ? a->dsn_orcpt : US"NULL", + a->dsn_flags, + dsn_envid ? dsn_envid : US"NULL", dsn_ret, + a->address, + a->dsn_aware + ); + + /* send report if next hop not DSN aware or a router flagged "last DSN hop" + and a report was requested */ + + if ( (a->dsn_aware != dsn_support_yes || a->dsn_flags & rf_dsnlasthop) + && a->dsn_flags & rf_notify_success + ) + { + /* copy and relink address_item and send report with all of them at once later */ + address_item * addr_next = addr_senddsn; + addr_senddsn = store_get(sizeof(address_item), GET_UNTAINTED); + *addr_senddsn = *a; + addr_senddsn->next = addr_next; + } + else + DEBUG(D_deliver) debug_printf("DSN: not sending DSN success message\n"); + } + +if (addr_senddsn) + { /* create exim process to send message */ + int fd; + pid_t pid = child_open_exim(&fd, US"DSN"); + + DEBUG(D_deliver) debug_printf("DSN: child_open_exim returns: %d\n", pid); + + if (pid < 0) /* Creation of child failed */ + { + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Process %d (parent %d) failed to " + "create child process to send success-dsn message: %s", getpid(), + getppid(), strerror(errno)); + + DEBUG(D_deliver) debug_printf("DSN: child_open_exim failed\n"); + } + else /* Creation of child succeeded */ + { + FILE * f = fdopen(fd, "wb"); + /* header only as required by RFC. only failure DSN needs to honor RET=FULL */ + uschar * bound; + transport_ctx tctx = {{0}}; + + DEBUG(D_deliver) + debug_printf("sending success-dsn to: %s\n", sender_address); + + /* build unique id for MIME boundary */ + bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); + DEBUG(D_deliver) debug_printf("DSN: MIME boundary: %s\n", bound); + + if (errors_reply_to) + fprintf(f, "Reply-To: %s\n", errors_reply_to); + + moan_write_from(f); + fprintf(f, "Auto-Submitted: auto-generated\n" + "To: %s\n" + "Subject: Delivery Status Notification\n", + sender_address); + moan_write_references(f, NULL); + fprintf(f, "Content-Type: multipart/report;" + " report-type=delivery-status; boundary=%s\n" + "MIME-Version: 1.0\n\n" + + "--%s\n" + "Content-type: text/plain; charset=us-ascii\n\n" + + "This message was created automatically by mail delivery software.\n" + " ----- The following addresses had successful delivery notifications -----\n", + bound, bound); + + for (address_item * a = addr_senddsn; a; a = a->next) + fprintf(f, "<%s> (relayed %s)\n\n", + a->address, + a->dsn_flags & rf_dsnlasthop ? "via non DSN router" + : a->dsn_aware == dsn_support_no ? "to non-DSN-aware mailer" + : "via non \"Remote SMTP\" router" + ); + + fprintf(f, "--%s\n" + "Content-type: message/delivery-status\n\n" + "Reporting-MTA: dns; %s\n", + bound, smtp_active_hostname); + + if (dsn_envid) + { /* must be decoded from xtext: see RFC 3461:6.3a */ + uschar * xdec_envid; + if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0) + fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid); + else + fprintf(f, "X-Original-Envelope-ID: error decoding xtext formatted ENVID\n"); + } + fputc('\n', f); + + for (address_item * a = addr_senddsn; a; a = a->next) + { + host_item * hu; + + print_dsn_addr_action(f, a, US"delivered", US"2.0.0"); + + if ((hu = a->host_used) && hu->name) + fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; 250 Ok\n\n", + hu->name); + else + fprintf(f, "Diagnostic-Code: X-Exim; relayed via non %s router\n\n", + a->dsn_flags & rf_dsnlasthop ? "DSN" : "SMTP"); + } + + fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", bound); + + fflush(f); + transport_filter_argv = NULL; /* Just in case */ + return_path = sender_address; /* In case not previously set */ + + /* Write the original email out */ + + tctx.u.fd = fd; + tctx.options = topt_add_return_path | topt_no_body; + /*XXX hmm, FALSE(fail) retval ignored. + Could error for any number of reasons, and they are not handled. */ + transport_write_message(&tctx, 0); + fflush(f); + + fprintf(f,"\n--%s--\n", bound); + + fflush(f); + fclose(f); + (void) child_close(pid, 0); /* Waits for child to close, no timeout */ + } + } +} + /************************************************* * Deliver one message * *************************************************/ @@ -7959,156 +8114,8 @@ else if (!f.dont_deliver) retry_update(&addr_defer, &addr_failed, &addr_succeed); /* Send DSN for successful messages if requested */ -addr_senddsn = NULL; -for (address_item * a = addr_succeed; a; a = a->next) - { - /* af_ignore_error not honored here. it's not an error */ - DEBUG(D_deliver) debug_printf("DSN: processing router : %s\n" - "DSN: processing successful delivery address: %s\n" - "DSN: Sender_address: %s\n" - "DSN: orcpt: %s flags: 0x%x\n" - "DSN: envid: %s ret: %d\n" - "DSN: Final recipient: %s\n" - "DSN: Remote SMTP server supports DSN: %d\n", - a->router ? a->router->name : US"(unknown)", - a->address, - sender_address, - a->dsn_orcpt ? a->dsn_orcpt : US"NULL", - a->dsn_flags, - dsn_envid ? dsn_envid : US"NULL", dsn_ret, - a->address, - a->dsn_aware - ); - - /* send report if next hop not DSN aware or a router flagged "last DSN hop" - and a report was requested */ - - if ( (a->dsn_aware != dsn_support_yes || a->dsn_flags & rf_dsnlasthop) - && a->dsn_flags & rf_notify_success - ) - { - /* copy and relink address_item and send report with all of them at once later */ - address_item * addr_next = addr_senddsn; - addr_senddsn = store_get(sizeof(address_item), GET_UNTAINTED); - *addr_senddsn = *a; - addr_senddsn->next = addr_next; - } - else - DEBUG(D_deliver) debug_printf("DSN: not sending DSN success message\n"); - } - -if (addr_senddsn) - { - pid_t pid; - int fd; - - /* create exim process to send message */ - pid = child_open_exim(&fd, US"DSN"); - - DEBUG(D_deliver) debug_printf("DSN: child_open_exim returns: %d\n", pid); - - if (pid < 0) /* Creation of child failed */ - { - log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Process %d (parent %d) failed to " - "create child process to send success-dsn message: %s", getpid(), - getppid(), strerror(errno)); - - DEBUG(D_deliver) debug_printf("DSN: child_open_exim failed\n"); - } - else /* Creation of child succeeded */ - { - FILE * f = fdopen(fd, "wb"); - /* header only as required by RFC. only failure DSN needs to honor RET=FULL */ - uschar * bound; - transport_ctx tctx = {{0}}; - - DEBUG(D_deliver) - debug_printf("sending success-dsn to: %s\n", sender_address); - - /* build unique id for MIME boundary */ - bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); - DEBUG(D_deliver) debug_printf("DSN: MIME boundary: %s\n", bound); - - if (errors_reply_to) - fprintf(f, "Reply-To: %s\n", errors_reply_to); - - moan_write_from(f); - fprintf(f, "Auto-Submitted: auto-generated\n" - "To: %s\n" - "Subject: Delivery Status Notification\n", - sender_address); - moan_write_references(f, NULL); - fprintf(f, "Content-Type: multipart/report;" - " report-type=delivery-status; boundary=%s\n" - "MIME-Version: 1.0\n\n" - - "--%s\n" - "Content-type: text/plain; charset=us-ascii\n\n" - - "This message was created automatically by mail delivery software.\n" - " ----- The following addresses had successful delivery notifications -----\n", - bound, bound); - - for (address_item * a = addr_senddsn; a; a = a->next) - fprintf(f, "<%s> (relayed %s)\n\n", - a->address, - a->dsn_flags & rf_dsnlasthop ? "via non DSN router" - : a->dsn_aware == dsn_support_no ? "to non-DSN-aware mailer" - : "via non \"Remote SMTP\" router" - ); - - fprintf(f, "--%s\n" - "Content-type: message/delivery-status\n\n" - "Reporting-MTA: dns; %s\n", - bound, smtp_active_hostname); - - if (dsn_envid) - { /* must be decoded from xtext: see RFC 3461:6.3a */ - uschar * xdec_envid; - if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0) - fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid); - else - fprintf(f, "X-Original-Envelope-ID: error decoding xtext formatted ENVID\n"); - } - fputc('\n', f); - - for (address_item * a = addr_senddsn; a; a = a->next) - { - host_item * hu; - - print_dsn_addr_action(f, a, US"delivered", US"2.0.0"); - - if ((hu = a->host_used) && hu->name) - fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; 250 Ok\n\n", - hu->name); - else - fprintf(f, "Diagnostic-Code: X-Exim; relayed via non %s router\n\n", - a->dsn_flags & rf_dsnlasthop ? "DSN" : "SMTP"); - } - - fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", bound); - - fflush(f); - transport_filter_argv = NULL; /* Just in case */ - return_path = sender_address; /* In case not previously set */ - - /* Write the original email out */ - - tctx.u.fd = fd; - tctx.options = topt_add_return_path | topt_no_body; - /*XXX hmm, FALSE(fail) retval ignored. - Could error for any number of reasons, and they are not handled. */ - transport_write_message(&tctx, 0); - fflush(f); - - fprintf(f,"\n--%s--\n", bound); - - fflush(f); - fclose(f); - rc = child_close(pid, 0); /* Waits for child to close, no timeout */ - } - } +maybe_send_dsn(); /* If any addresses failed, we must send a message to somebody, unless af_ignore_error is set, in which case no action is taken. It is possible for @@ -8449,27 +8456,23 @@ else if (addr_defer != (address_item *)(+1)) if (f.deliver_freeze) { - if (freeze_tell && freeze_tell[0] != 0 && !f.local_error_message) + if (freeze_tell && *freeze_tell && !f.local_error_message) { - uschar *s = string_copy(frozen_info); - uschar *ss = Ustrstr(s, " by the system filter: "); + uschar * s = string_copy(frozen_info); + uschar * ss = Ustrstr(s, " by the system filter: "); - if (ss != NULL) + if (ss) { ss[21] = '.'; ss[22] = '\n'; } - ss = s; - while (*ss != 0) - { + for (ss = s; *ss; ) if (*ss == '\\' && ss[1] == 'n') - { - *ss++ = ' '; - *ss++ = '\n'; - } - else ss++; - } + { *ss++ = ' '; *ss++ = '\n'; } + else + ss++; + moan_tell_someone(freeze_tell, addr_defer, US"Message frozen", "Message %s has been frozen%s.\nThe sender is <%s>.\n", message_id, s, sender_address); commit 7c60296900bdff369ffd2bf54eecfe6097b997a4 Author: Jeremy Harris Date: Sat Feb 25 15:50:58 2023 +0000 Linewrap long lines in bounce bodies. Bug 2979 diff --git a/src/src/deliver.c b/src/src/deliver.c index 993b24231..d0e6d1c2e 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -5400,19 +5400,18 @@ uschar * s = testflag(addr, af_pass_message) ? addr->message : NULL; unsigned cnt; /* af_pass_message and addr->message set ? print remote host answer */ -if (s) - { - DEBUG(D_deliver) - debug_printf("DSN Diagnostic-Code: addr->message = %s\n", addr->message); +if (!s) + return; - /* search first ": ". we assume to find the remote-MTA answer there */ - if (!(s = Ustrstr(addr->message, ": "))) - return; /* not found, bail out */ - s += 2; /* skip ": " */ - cnt = fprintf(f, "Diagnostic-Code: smtp; "); - } -/* no message available. do nothing */ -else return; +DEBUG(D_deliver) + debug_printf("DSN Diagnostic-Code: addr->message = %s\n", addr->message); + +/* search first ": ". we assume to find the remote-MTA answer there */ +if (!(s = Ustrstr(addr->message, ": "))) + return; /* not found, bail out */ + +s += 2; /* skip ": " */ +cnt = fprintf(f, "Diagnostic-Code: smtp; "); while (*s) { @@ -5551,6 +5550,82 @@ else if (!(fp = Ufopen(s, "rb"))) return fp; } + +/* Output the given header and string, converting either +the sequence "\n" or a real newline into newline plus space. +If that still takes us past column 78, look for the last space +and split there too. +Append a newline if string did not have one. +Limit to about 1024 chars total. */ + +static void +dsn_put_wrapped(FILE * fp, const uschar * header, const uschar * s) +{ +const uschar * t; +int llen = fprintf(fp, "%s", CS header), sleft = Ustrlen(s); +int remain = 1022 - llen; + +if (*s && remain > 0) + { + for(;;) + { + unsigned ltail; /* source chars to skip */ + + /* Chop at a newline, or end of string */ + + if ((t = Ustrchr(s, '\\')) && t[1] == 'n') + ltail = 2; + else if ((t = Ustrchr(s, '\n'))) + ltail = 1; + else + { + t = s + sleft; + ltail = 0; + } + + /* If that is too long, search backward for a space */ + + if ((llen + t - s) > 78) + { + const uschar * u; + for (u = s + 78 - llen; u > s + 10; --u) if (*u == ' ') break; + if (u > s + 10) + { /* found a space to linebreak at */ + llen = u - s; + remain -= fprintf(fp, "%.*s", (int)llen, s); + s += ++llen; /* skip the space also */ + } + else if (llen < 78) + { /* just linebreak at 78 */ + llen = 78 - llen; + remain -= fprintf(fp, "%.*s", llen, s); + s += llen; + } + else /* header rather long */ + llen = 0; + } + else + { + llen = t - s; + remain -= fprintf(fp, "%.*s", llen, s); + s = t + ltail; + } + + sleft -= llen; + remain -= 2; + if (!*s || remain <= 0) + break; + fputs("\n ", fp); + llen = 1; /* one for the leading space output above */ + } + + if (s[-1] != '\n') fputs("\n", fp); + } +else + fputs("\n", fp); +} + + /************************************************* * Send a bounce message * *************************************************/ @@ -5818,7 +5893,7 @@ wording. */ if (dsn_envid) { /* must be decoded from xtext: see RFC 3461:6.3a */ - uschar *xdec_envid; + uschar * xdec_envid; if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0) fprintf(fp, "Original-Envelope-ID: %s\n", dsn_envid); else @@ -5845,11 +5920,11 @@ wording. */ fprintf(fp, "Remote-MTA: X-ip; [%s]%s\n", hu->address, p); } if ((s = addr->smtp_greeting) && *s) - fprintf(fp, "X-Remote-MTA-smtp-greeting: X-str; %.900s\n", s); + dsn_put_wrapped(fp, US"X-Remote-MTA-smtp-greeting: X-str; ", s); if ((s = addr->helo_response) && *s) - fprintf(fp, "X-Remote-MTA-helo-response: X-str; %.900s\n", s); + dsn_put_wrapped(fp, US"X-Remote-MTA-helo-response: X-str; ", s); if ((s = addr->message) && *s) - fprintf(fp, "X-Exim-Diagnostic: X-str; %.900s\n", s); + dsn_put_wrapped(fp, US"X-Exim-Diagnostic: X-str; ", s); } #endif print_dsn_diagnostic_code(addr, fp); commit bd0f95ded48f560cb1f9f8b808e1abaabeb4d4ec Author: Andrew Aitchison Date: Sat Mar 4 17:23:09 2023 +0000 exim_msgdate: more options, better perl version compatibility diff --git a/src/src/exim_msgdate.src b/src/src/exim_msgdate.src index e5c357bca..c591f306e 100755 --- a/src/src/exim_msgdate.src +++ b/src/src/exim_msgdate.src @@ -1,4 +1,4 @@ -#!PERL_COMMAND -WT +#!PERL_COMMAND -T # # Utility to convert an exim message-id to a human readable form # @@ -27,6 +27,11 @@ # # PROCESSED_FLAG +# These match runtest +use v5.10.1; +use warnings; +use if $^V >= v5.19.11, experimental => 'smartmatch'; + use strict; use File::Basename; use Getopt::Long; @@ -48,6 +53,7 @@ if (defined $ENV{TZ}) { } my $localhost_number; # An Exim config value +my $nolocalhost_number; my $p_name = basename $0; my $p_version = "20230203.0"; @@ -56,20 +62,13 @@ my $p_cp = < \$optbase62, "localhost_number=s" => \$localhost_number, # cf "local" + "nolocalhost_number" => \$nolocalhost_number, + "no-localhost_number" => \$nolocalhost_number, + "no_localhost_number" => \$nolocalhost_number, "unix" => \$optunix, "u" => \$optunix, @@ -122,6 +124,11 @@ GetOptions ( -noperldoc => system('perldoc -V 2>/dev/null 1>&2') ); }, + 'version' => sub { + print basename($0), ": $0\n"; + print "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n"; + print "perl(runtime): $]\n"; + }, ) or pod2usage; # die("Error in command line arguments\n"); @@ -270,13 +277,27 @@ if ($debug) { } else { warn "localhost_number unset\n"; } + if (defined $nolocalhost_number) { + warn "nolocalhost_number=$nolocalhost_number\n"; + } else { + warn "nolocalhost_number unset\n"; + } } if (defined $localhost_number) { if ($localhost_number eq "none") { $localhost_number = undef; + $nolocalhost_number = TRUE; + } else { + if ($nolocalhost_number) { + die "aborting: localhost_number and nolocalhost_number both set\n "; + } + $nolocalhost_number = FALSE; } -} else { +} + +unless (defined $nolocalhost_number) { + warn "Looking for config file\n" if $debug; my $config = get_configfilename(); warn "Reading config $config to find localhost_number\n" if $debug; @@ -296,6 +317,9 @@ if (defined $localhost_number) { warn "$config gives localhost_number $localhost_number\n" if $debug and defined $localhost_number; } else { + if ($debug) { + warn "cannot read config file $config\n"; + } # This way we get the expanded value for localhost_number # directly from exim, but we have to guess which exim binary ... # On Debian and Ubuntu, /usr/sbin/exim is a link to exim4 so is OK. @@ -315,10 +339,18 @@ if (defined $localhost_number) { } if (defined $localhost_number) { - die "localhost_number > 16\n" - if $localhost_number > 16; - die "localhost_number > 10\n" - if $localhost_number > 10 && ($base != 62); + if ($localhost_number =~ /\D/) { + die "localhost_number must be a number >=0\n"; + } elsif ($localhost_number =~ /^\d*$/) { + die "localhost_number > 16\n" + if $localhost_number > 16; + die "localhost_number > 10\n" + if $localhost_number > 10 && ($base != 62); + } else { + warn "clearing localhost_number - was $localhost_number\n"; + undef $localhost_number; + $nolocalhost_number=TRUE; + } } if ($debug) { commit c5722bc5a8df130f4733ca3c4398ae51cc7842e0 Author: Andrew Aitchison Date: Sun Mar 5 12:47:17 2023 +0000 exim_msgdate: version output does not require an exim config diff --git a/src/src/exim_msgdate.src b/src/src/exim_msgdate.src index c591f306e..bfb5bc81e 100755 --- a/src/src/exim_msgdate.src +++ b/src/src/exim_msgdate.src @@ -56,7 +56,7 @@ my $localhost_number; # An Exim config value my $nolocalhost_number; my $p_name = basename $0; -my $p_version = "20230203.0"; +my $p_version = "20230304.0"; my $p_cp = < sub { - print basename($0), ": $0\n"; - print "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n"; + print basename($0), ": $p_version $0\n"; + print "exim build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n"; print "perl(runtime): $]\n"; + exit 0; }, ) or pod2usage; # die("Error in command line arguments\n"); commit cfeaa009b9b407d8b404438afbf4c5d85d61e8c1 Author: Jeremy Harris Date: Sat Mar 4 22:55:01 2023 +0000 Truncate overlong lines in DSNs. Bug 2979 diff --git a/src/src/deliver.c b/src/src/deliver.c index d0e6d1c2e..9b77b3619 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -5370,6 +5370,11 @@ while (*s) fprintf(f, "\n "); /* sic (because space follows) */ count = 0; } + else if (count > 254) /* arbitrary limit */ + { + fprintf(f, "[truncated]"); + do s++; while (*s && !(*s == '\\' && s[1] == '\n')); + } } } commit ca8410e981982edd16bcc8689e09c2c15d8267e7 Author: Jeremy Harris Date: Sun Mar 5 00:40:49 2023 +0000 Fix crash in queue-ramp Broken-by: 1e835086d159 diff --git a/src/src/daemon.c b/src/src/daemon.c index b0533c28f..caed44bb3 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -1344,7 +1344,10 @@ switch (buf[0]) memcpy(queuerun_msgid, buf+1, MESSAGE_ID_LENGTH+1); for (qrunner * q = qrunners; q; q = q->next) - if (Ustrcmp(q->name, buf+1+MESSAGE_ID_LENGTH+1) == 0) + if (q->name + ? Ustrcmp(q->name, buf+1+MESSAGE_ID_LENGTH+1) == 0 + : !buf[1+MESSAGE_ID_LENGTH+1] + ) { queuerun_msg_qname = q->name; break; } return TRUE; #endif commit a6d90e094d4e2a84d18859cf2005d10c2020e2d4 Author: Jeremy Harris Date: Sat Mar 11 17:48:28 2023 +0000 Header-wrap expansion. Bug 2843 diff --git a/src/src/deliver.c b/src/src/deliver.c index 9b77b3619..e2994b116 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2023 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ @@ -5566,69 +5566,14 @@ Limit to about 1024 chars total. */ static void dsn_put_wrapped(FILE * fp, const uschar * header, const uschar * s) { -const uschar * t; -int llen = fprintf(fp, "%s", CS header), sleft = Ustrlen(s); -int remain = 1022 - llen; +gstring * g = string_cat(NULL, header); -if (*s && remain > 0) - { - for(;;) - { - unsigned ltail; /* source chars to skip */ - - /* Chop at a newline, or end of string */ - - if ((t = Ustrchr(s, '\\')) && t[1] == 'n') - ltail = 2; - else if ((t = Ustrchr(s, '\n'))) - ltail = 1; - else - { - t = s + sleft; - ltail = 0; - } - - /* If that is too long, search backward for a space */ - - if ((llen + t - s) > 78) - { - const uschar * u; - for (u = s + 78 - llen; u > s + 10; --u) if (*u == ' ') break; - if (u > s + 10) - { /* found a space to linebreak at */ - llen = u - s; - remain -= fprintf(fp, "%.*s", (int)llen, s); - s += ++llen; /* skip the space also */ - } - else if (llen < 78) - { /* just linebreak at 78 */ - llen = 78 - llen; - remain -= fprintf(fp, "%.*s", llen, s); - s += llen; - } - else /* header rather long */ - llen = 0; - } - else - { - llen = t - s; - remain -= fprintf(fp, "%.*s", llen, s); - s = t + ltail; - } +g = string_cat(g, s); +gstring_release_unused(g); +fprintf(fp, "%s\n", wrap_header(string_from_gstring(g), 79, 1023, US" ", 1)); +} - sleft -= llen; - remain -= 2; - if (!*s || remain <= 0) - break; - fputs("\n ", fp); - llen = 1; /* one for the leading space output above */ - } - if (s[-1] != '\n') fputs("\n", fp); - } -else - fputs("\n", fp); -} /************************************************* diff --git a/src/src/expand.c b/src/src/expand.c index baf7134cd..6dcd45062 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2023 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ @@ -237,6 +237,7 @@ static uschar *op_table_main[] = { US"expand", US"h", US"hash", + US"headerwrap", US"hex2b64", US"hexquote", US"ipv6denorm", @@ -284,6 +285,7 @@ enum { EOP_EXPAND, EOP_H, EOP_HASH, + EOP_HEADERWRAP, EOP_HEX2B64, EOP_HEXQUOTE, EOP_IPV6DENORM, @@ -7262,7 +7264,7 @@ NOT_ITEM: ; { uschar *t; unsigned long int n = Ustrtoul(sub, &t, 10); - if (*t != 0) + if (*t) { expand_string_message = string_sprintf("argument for base62 " "operator is \"%s\", which is not a decimal number", sub); @@ -7278,7 +7280,7 @@ NOT_ITEM: ; { uschar *tt = sub; unsigned long int n = 0; - while (*tt != 0) + while (*tt) { uschar *t = Ustrchr(base62_chars, *tt++); if (!t) @@ -7428,6 +7430,29 @@ NOT_ITEM: ; goto EXPAND_FAILED; #endif + /* Line-wrap a string as if it is a header line */ + + case EOP_HEADERWRAP: + { + unsigned col = 80, lim = 998; + uschar * s; + + if (arg) + { + const uschar * list = arg; + int sep = '_'; + if ((s = string_nextinlist(&list, &sep, NULL, 0))) + { + col = atoi(CS s); + if ((s = string_nextinlist(&list, &sep, NULL, 0))) + lim = atoi(CS s); + } + } + if ((s = wrap_header(sub, col, lim, US"\t", 8))) + yield = string_cat(yield, s); + } + break; + /* Convert hex encoding to base64 encoding */ case EOP_HEX2B64: diff --git a/src/src/functions.h b/src/src/functions.h index 37f0a57bc..5fbb426ec 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2023 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ @@ -678,6 +678,7 @@ extern void version_init(void); extern BOOL write_chunk(transport_ctx *, uschar *, int); extern ssize_t write_to_fd_buf(int, const uschar *, size_t); +extern uschar *wrap_header(const uschar *, unsigned, unsigned, const uschar *, unsigned); /******************************************************************************/ diff --git a/src/src/header.c b/src/src/header.c index a4dd6e72e..e2b3d8a9c 100644 --- a/src/src/header.c +++ b/src/src/header.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2023 */ /* Copyright (c) University of Cambridge 1995 - 2016 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ @@ -466,4 +466,85 @@ va_end(ap); return !cond; } + + +/* Wrap and truncate a string for use as a header. +Convert either the sequence "\n" or a real newline into newline plus indent. +If that still takes us past the column limit, look for the last space +and split there too. +Limit to the given max total char count. + +Return: string or NULL */ + +uschar * +wrap_header(const uschar * s, unsigned cols, unsigned maxchars, + const uschar * indent, unsigned indent_cols) +{ +gstring * g = NULL; + +if (maxchars == 0) maxchars = INT_MAX; +if (cols == 0) cols = INT_MAX; + +if (s && *s) + { + int sleft = Ustrlen(s); + for(unsigned llen = 0; ; llen = indent_cols) + { + const uschar * t; + unsigned ltail = 0, glen; + + if ((t = Ustrchr(s, '\\')) && t[1] == 'n') + ltail = 2; + else if ((t = Ustrchr(s, '\n'))) + ltail = 1; + else + t = s + sleft; + + if ((llen + t - s) > cols) /* more than a linesworth of s */ + { /* look backward for whitespace */ + for (const uschar * u = s + cols - llen; u > s + 10; --u) if (isspace(*u)) + { + llen = u - s; + while (u > s+1 && isspace(u[-1])) --u; /* find start of whitespace */ + g = string_catn(g, s, u - s); + s += ++llen; /* skip the space */ + while (*s && isspace(*s)) /* and any trailing */ + s++, llen++; + goto LDONE; + } + /* no whitespace */ + if (llen < cols) + { /* just linebreak at 80 */ + llen = cols - llen; + g = string_catn(g, s, llen); + s += llen; + } + else + llen = 0; + LDONE: + } + else /* rest of s fits in line */ + { + llen = t - s; + g = string_catn(g, s, llen); + s = t + ltail; + } + + if (!*s) + break; /* no trailing linebreak */ + if ((glen = gstring_length(g)) >= maxchars) + { + gstring_trim(g, glen - maxchars); + break; /* no trailing linebreak */ + } + sleft -= llen; + g = string_catn(g, US"\n", 1); + g = string_catn(g, indent, 1); + } + } +gstring_release_unused(g); +return string_from_gstring(g); +} + + /* End of header.c */ commit ecc7ae95e36be550fa4b47de2d8dfc7115eac9cb Author: Jeremy Harris Date: Sat Mar 11 17:57:29 2023 +0000 Compiler quietening Broken-by: a6d90e094d4e diff --git a/src/src/header.c b/src/src/header.c index e2b3d8a9c..d5f1dcd6b 100644 --- a/src/src/header.c +++ b/src/src/header.c @@ -521,7 +521,7 @@ if (s && *s) } else llen = 0; - LDONE: + LDONE: ; } else /* rest of s fits in line */ { commit 1d904e0470fed2e5c7a867f63d39ee44dbe80a2a Author: Jeremy Harris Date: Sun Mar 12 19:02:31 2023 +0000 Compiler quietening. Bug 2983 diff --git a/src/src/local_scan.h b/src/src/local_scan.h index 69b3c6cdb..c88994442 100644 --- a/src/src/local_scan.h +++ b/src/src/local_scan.h @@ -133,7 +133,7 @@ typedef struct { union { void * value; long offset; - void (* fn)(); + void (* fn)(const uschar *, const uschar *, unsigned); } v; } optionlist; #define OPT_OFF(s, field) {.offset = offsetof(s, field)} diff --git a/src/src/readconf.c b/src/src/readconf.c index 6dba11ca1..3b26e87d5 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -2356,11 +2356,8 @@ switch (type) } case opt_func: - { - void (*fn)() = ol->v.fn; - fn(name, s, 0); + ol->v.fn(name, s, 0); break; - } } return TRUE; @@ -2384,11 +2381,7 @@ readconf_printtime(int t) int s, m, h, d, w; uschar *p = time_buffer; -if (t < 0) - { - *p++ = '-'; - t = -t; - } +if (t < 0) *p++ = '-', t = -t; s = t % 60; t /= 60; commit 8ff2ba119ba654e9238f157f94bf10ed640ed877 Author: Jeremy Harris Date: Sun Mar 12 20:57:40 2023 +0000 Cmdine option for only IDs of queue diff --git a/src/src/exim.c b/src/src/exim.c index 8d13bd478..c16beb1af 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1745,7 +1745,7 @@ int filter_sfd = -1; int filter_ufd = -1; int group_count; int i, rv; -int list_queue_option = 0; +int list_queue_option = QL_BASIC; int msg_action = 0; int msg_action_arg = -1; int namelen = argv[0] ? Ustrlen(argv[0]) : 0; @@ -2388,11 +2388,9 @@ on the second character (the one after '-'), to save some effort. */ } if (*argrest == 'r') - { - list_queue_option = 8; - argrest++; - } - else list_queue_option = 0; + list_queue_option = QL_UNSORTED, argrest++; + else + list_queue_option = QL_BASIC; list_queue = TRUE; @@ -2402,11 +2400,15 @@ on the second character (the one after '-'), to save some effort. */ /* -bpu: List the contents of the mail queue, top-level undelivered */ - else if (Ustrcmp(argrest, "u") == 0) list_queue_option += 1; + else if (Ustrcmp(argrest, "u") == 0) list_queue_option |= QL_UNDELIVERED_ONLY; /* -bpa: List the contents of the mail queue, including all delivered */ - else if (Ustrcmp(argrest, "a") == 0) list_queue_option += 2; + else if (Ustrcmp(argrest, "a") == 0) list_queue_option |= QL_PLUS_GENERATED; + + /* -bpi: List only message IDs */ + + else if (Ustrcmp(argrest, "i") == 0) list_queue_option |= QL_MSGID_ONLY; /* Unknown after -bp[r] */ diff --git a/src/src/macros.h b/src/src/macros.h index 3b0293b97..9f3a7b06a 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -1140,4 +1140,11 @@ typedef unsigned mcs_flags; /* A big number for (effectively) unlimited envelope addresses */ #define UNLIMITED_ADDRS 999999 +/* Flags for queue_list() */ +#define QL_BASIC 0 +#define QL_UNDELIVERED_ONLY 1 +#define QL_PLUS_GENERATED 2 +#define QL_MSGID_ONLY 3 +#define QL_UNSORTED 8 + /* End of macros.h */ diff --git a/src/src/queue.c b/src/src/queue.c index d01cde655..b6e7907d7 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -913,7 +913,7 @@ Returns: nothing */ void -queue_list(int option, uschar **list, int count) +queue_list(int option, uschar ** list, int count) { int subcount; int now = (int)time(NULL); @@ -942,21 +942,25 @@ if (count > 0) else qf = queue_get_spool_list( - -1, /* entire queue */ - subdirs, /* for holding sub list */ - &subcount, /* for subcount */ - option >= 8, /* randomize if required */ - NULL); /* don't just count */ + -1, /* entire queue */ + subdirs, /* for holding sub list */ + &subcount, /* for subcount */ + option >= QL_UNSORTED, /* randomize if required */ + NULL); /* don't just count */ -if (option >= 8) option -= 8; +option &= ~QL_UNSORTED; /* Now scan the chain and print information, resetting store used each time. */ -for (; - qf && (reset_point = store_mark()); - spool_clear_header_globals(), store_reset(reset_point), qf = qf->next - ) +if (option == QL_MSGID_ONLY) /* Print only the message IDs from the chain */ + for (; qf; qf = qf->next) + fprintf(stdout, "%.*s\n", MESSAGE_ID_LENGTH, qf->text); + +else for (; + qf && (reset_point = store_mark()); + spool_clear_header_globals(), store_reset(reset_point), qf = qf->next + ) { int rc, save_errno; int size = 0; @@ -1010,8 +1014,8 @@ for (; } } - fprintf(stdout, "%s ", string_format_size(size, big_buffer)); - for (int i = 0; i < 16; i++) fputc(qf->text[i], stdout); + fprintf(stdout, "%s %.*s", + string_format_size(size, big_buffer), MESSAGE_ID_LENGTH, qf->text); if (env_read && sender_address) { @@ -1048,14 +1052,14 @@ for (; { for (int i = 0; i < recipients_count; i++) { - tree_node *delivered = + tree_node * delivered = tree_search(tree_nonrecipients, recipients_list[i].address); - if (!delivered || option != 1) + if (!delivered || option != QL_UNDELIVERED_ONLY) printf(" %s %s\n", delivered ? "D" : " ", recipients_list[i].address); if (delivered) delivered->data.val = TRUE; } - if (option == 2 && tree_nonrecipients) + if (option == QL_PLUS_GENERATED && tree_nonrecipients) queue_list_extras(tree_nonrecipients); printf("\n"); } commit 6fdf76d0eae42ce4507fe317f095572100c5d6b8 Author: Jeremy Harris Date: Mon Mar 13 00:43:01 2023 +0000 SNI for ${readsocket } diff --git a/src/src/functions.h b/src/src/functions.h index 5fbb426ec..896122a69 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -54,6 +54,8 @@ extern uschar * tls_cert_fprt_sha256(void *); extern void tls_clean_env(void); extern BOOL tls_client_start(client_conn_ctx *, smtp_connect_args *, void *, tls_support *, uschar **); +extern BOOL tls_client_adjunct_start(host_item *, client_conn_ctx *, + const uschar *, uschar **); extern void tls_client_creds_reload(BOOL); extern void tls_close(void *, int); diff --git a/src/src/lookups/readsock.c b/src/src/lookups/readsock.c index b1ea42c7f..a3f87108a 100644 --- a/src/src/lookups/readsock.c +++ b/src/src/lookups/readsock.c @@ -13,7 +13,7 @@ static int internal_readsock_open(client_conn_ctx * cctx, const uschar * sspec, - int timeout, BOOL do_tls, uschar ** errmsg) + int timeout, uschar * do_tls, uschar ** errmsg) { const uschar * server_name; host_item host; @@ -116,27 +116,8 @@ else #ifndef DISABLE_TLS if (do_tls) - { - union sockaddr_46 interface_sock; - EXIM_SOCKLEN_T size = sizeof(interface_sock); - smtp_connect_args conn_args = {.host = &host }; - tls_support tls_dummy = { .sni = NULL }; - uschar * errstr; - - if (getsockname(cctx->sock, (struct sockaddr *) &interface_sock, &size) == 0) - conn_args.sending_ip_address = host_ntoa(-1, &interface_sock, NULL, NULL); - else - { - *errmsg = string_sprintf("getsockname failed: %s", strerror(errno)); + if (!tls_client_adjunct_start(&host, cctx, do_tls, errmsg)) goto bad; - } - - if (!tls_client_start(cctx, &conn_args, NULL, &tls_dummy, &errstr)) - { - *errmsg = string_sprintf("TLS connect failed: %s", errstr); - goto bad; - } - } #endif DEBUG(D_expand|D_lookup) debug_printf_indent(" connected to socket %s\n", sspec); @@ -187,8 +168,8 @@ client_conn_ctx * cctx = handle; int sep = ','; struct { BOOL do_shutdown:1; - BOOL do_tls:1; BOOL cache:1; + uschar * do_tls; /* NULL, empty-string, or SNI */ } lf = {.do_shutdown = TRUE}; uschar * eol = NULL; int timeout = 5; @@ -207,8 +188,10 @@ if (opts) for (uschar * s; s = string_nextinlist(&opts, &sep, NULL, 0); ) else if (Ustrncmp(s, "shutdown=", 9) == 0) lf.do_shutdown = Ustrcmp(s + 9, "no") != 0; #ifndef DISABLE_TLS - else if (Ustrncmp(s, "tls=", 4) == 0 && Ustrcmp(s + 4, US"no") != 0) - lf.do_tls = TRUE; + else if (Ustrncmp(s, "tls=", 4) == 0 && Ustrcmp(s + 4, US"no") != 0 && !lf.do_tls) + lf.do_tls = US""; + else if (Ustrncmp(s, "sni=", 4) == 0) + lf.do_tls = s + 4; #endif else if (Ustrncmp(s, "eol=", 4) == 0) eol = string_unprinting(s + 4); diff --git a/src/src/tls.c b/src/src/tls.c index ba7c2de38..825313a9a 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -852,6 +852,57 @@ DEBUG(D_tls) debug_printf("TLS: resume session index %s\n", tlsp->resume_index); #endif } + + +/* Start TLS as a client for an ajunct connection, eg. readsocket +Return boolean success. +*/ + +BOOL +tls_client_adjunct_start(host_item * host, client_conn_ctx * cctx, + const uschar * sni, uschar ** errmsg) +{ +union sockaddr_46 interface_sock; +EXIM_SOCKLEN_T size = sizeof(interface_sock); +smtp_connect_args conn_args = {.host = host }; +tls_support tls_dummy = { .sni = NULL }; +uschar * errstr; + +if (getsockname(cctx->sock, (struct sockaddr *) &interface_sock, &size) == 0) + conn_args.sending_ip_address = host_ntoa(-1, &interface_sock, NULL, NULL); +else + { + *errmsg = string_sprintf("getsockname failed: %s", strerror(errno)); + return FALSE; + } + +/* To handle SNI we need to emulate more of a real transport because the +base tls code assumes that is where the SNI string lives. */ + +if (*sni) + { + transport_instance * tb; + smtp_transport_options_block * ob; + + conn_args.tblock = tb = store_get(sizeof(*tb), GET_UNTAINTED); + memset(tb, 0, sizeof(*tb)); + + tb->options_block = ob = store_get(sizeof(*ob), GET_UNTAINTED); + memcpy(ob, &smtp_transport_option_defaults, sizeof(*ob)); + + ob->tls_sni = sni; + } + +if (!tls_client_start(cctx, &conn_args, NULL, &tls_dummy, &errstr)) + { + *errmsg = string_sprintf("TLS connect failed: %s", errstr); + return FALSE; + } +return TRUE; +} + + + #endif /*!DISABLE_TLS*/ #endif /*!MACRO_PREDEF*/ commit 7ce1ced40351c3cd5982d37ff4ccdb02afd82365 Author: Andrew Aitchison Date: Mon Mar 13 15:59:23 2023 +0000 Compiler quietening. Bug 2983 diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 10b5f2aa5..9d0ab2fdf 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -1001,7 +1001,7 @@ Returns: nothing */ static void -info_callback(SSL * s, int where, int ret) +info_callback(const SSL * s, int where, int ret) { DEBUG(D_tls) { @@ -1750,13 +1750,13 @@ level. */ DEBUG(D_tls) { - SSL_CTX_set_info_callback(ctx, (void (*)())info_callback); + SSL_CTX_set_info_callback(ctx, info_callback); #if defined(EXIM_HAVE_OPESSL_TRACE) && !defined(OPENSSL_NO_SSL_TRACE) /* this needs a debug build of OpenSSL */ - SSL_CTX_set_msg_callback(ctx, (void (*)())SSL_trace); + SSL_CTX_set_msg_callback(ctx, SSL_trace); #endif #ifdef OPENSSL_HAVE_KEYLOG_CB - SSL_CTX_set_keylog_callback(ctx, (void (*)())keylog_callback); + SSL_CTX_set_keylog_callback(ctx, keylog_callback); #endif } commit 3607e3e00236f6039b765882edd0200dff6a31fc Author: Jeremy Harris Date: Thu Mar 16 15:49:59 2023 +0000 Limit expanded References header to RFC max size. Bug 2827 diff --git a/src/src/deliver.c b/src/src/deliver.c index e2994b116..57a435eeb 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -6178,6 +6178,7 @@ return_path = sender_address; /* In case not previously set */ /* Write the original email out */ /*XXX no checking for failure! buggy! */ +/*XXX overlong headers in the original become overlong body lines here*/ transport_write_message(&tctx, 0); fflush(f); diff --git a/src/src/moan.c b/src/src/moan.c index ebfd440f6..9c30c8edd 100644 --- a/src/src/moan.c +++ b/src/src/moan.c @@ -80,11 +80,17 @@ if (!h) /* We limit the total length of references. Although there is no fixed limit, some systems do not like headers growing beyond recognition. Keep the first message ID for the thread root and the last few for -the position inside the thread, up to a maximum of 12 altogether. */ +the position inside the thread, up to a maximum of 12 altogether. +Also apply the max line length limit from RFC 2822 2.1.1 + +XXX preferably we would get any limit from the outbound transport, +passed in here for a limit value. +*/ if (h || message_id) { - fprintf(fp, "References:"); + unsigned use = fprintf(fp, "References:"); + if (message_id) use += Ustrlen(message_id) + 1; if (h) { const uschar * s; @@ -95,14 +101,27 @@ if (h || message_id) s = Ustrchr(h->text, ':') + 1; f.parse_allow_group = FALSE; while (*s && (s = parse_message_id(s, &id, &error))) - if (reference_count == nelem(referenced_ids)) - { - memmove(referenced_ids + 1, referenced_ids + 2, - sizeof(referenced_ids) - 2*sizeof(uschar *)); - referenced_ids[reference_count - 1] = id; - } + { + unsigned this = Ustrlen(id); + if ( reference_count == nelem(referenced_ids) + || use + this + reference_count > 998 + ) + { + if (reference_count > 1) + { + /* drop position 1 and shuffle down */ + use -= Ustrlen(referenced_ids + 1); + memmove(referenced_ids + 1, referenced_ids + 2, + sizeof(referenced_ids) - 2*sizeof(*referenced_ids)); + + /* append new one */ + referenced_ids[reference_count - 1] = id; + } + } else referenced_ids[reference_count++] = id; + use += this; + } for (int i = 0; i < reference_count; ++i) fprintf(fp, " %s", referenced_ids[i]); commit 35d78f064b4e9f3ec28481e5e842c33a68171721 Author: Jeremy Harris Date: Thu Mar 16 19:35:48 2023 +0000 Fix long headers going into DSN bodies. Bug 1760 diff --git a/src/src/deliver.c b/src/src/deliver.c index 57a435eeb..f3a406990 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -5954,7 +5954,7 @@ wording. */ tctx.u.fd = fileno(fp); tctx.tblock = &tb; - tctx.options = topt; + tctx.options = topt | topt_truncate_headers; tb.add_headers = dsnnotifyhdr; /*XXX no checking for failure! buggy! */ @@ -6172,13 +6172,12 @@ fprintf(f, "--%s\n" fflush(f); /* header only as required by RFC. only failure DSN needs to honor RET=FULL */ tctx.u.fd = fileno(f); -tctx.options = topt_add_return_path | topt_no_body; +tctx.options = topt_add_return_path | topt_truncate_headers | topt_no_body; transport_filter_argv = NULL; /* Just in case */ return_path = sender_address; /* In case not previously set */ /* Write the original email out */ /*XXX no checking for failure! buggy! */ -/*XXX overlong headers in the original become overlong body lines here*/ transport_write_message(&tctx, 0); fflush(f); @@ -6334,7 +6333,7 @@ if (addr_senddsn) /* Write the original email out */ tctx.u.fd = fd; - tctx.options = topt_add_return_path | topt_no_body; + tctx.options = topt_add_return_path | topt_truncate_headers | topt_no_body; /*XXX hmm, FALSE(fail) retval ignored. Could error for any number of reasons, and they are not handled. */ transport_write_message(&tctx, 0); diff --git a/src/src/macros.h b/src/src/macros.h index 9f3a7b06a..73c6ac2c6 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -868,19 +868,20 @@ enum { /* Options for transport_write_message */ -#define topt_add_return_path 0x0001 -#define topt_add_delivery_date 0x0002 -#define topt_add_envelope_to 0x0004 -#define topt_escape_headers 0x0008 /* Apply escape check to headers */ -#define topt_use_crlf 0x0010 /* Terminate lines with CRLF */ -#define topt_no_headers 0x0020 /* Omit headers */ -#define topt_no_body 0x0040 /* Omit body */ -#define topt_end_dot 0x0080 /* Send terminating dot line */ -#define topt_no_flush 0x0100 /* more data expected after message (eg QUIT) */ -#define topt_use_bdat 0x0200 /* prepend chunks with RFC3030 BDAT header */ -#define topt_output_string 0x0400 /* create string rather than write to fd */ -#define topt_continuation 0x0800 /* do not reset buffer */ -#define topt_not_socket 0x1000 /* cannot do socket-only syscalls */ +#define topt_add_return_path BIT(0) +#define topt_add_delivery_date BIT(1) +#define topt_add_envelope_to BIT(2) +#define topt_escape_headers BIT(3) /* Apply escape check to headers */ +#define topt_truncate_headers BIT(4) /* Truncate header lines at 998 chars */ +#define topt_use_crlf BIT(5) /* Terminate lines with CRLF */ +#define topt_no_headers BIT(6) /* Omit headers */ +#define topt_no_body BIT(7) /* Omit body */ +#define topt_end_dot BIT(8) /* Send terminating dot line */ +#define topt_no_flush BIT(9) /* more data expected after message (eg QUIT) */ +#define topt_use_bdat BIT(10) /* prepend chunks with RFC3030 BDAT header */ +#define topt_output_string BIT(11) /* create string rather than write to fd */ +#define topt_continuation BIT(12) /* do not reset buffer */ +#define topt_not_socket BIT(13) /* cannot do socket-only syscalls */ /* Options for smtp_write_command */ diff --git a/src/src/transport.c b/src/src/transport.c index d6cedf911..80ba1eece 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -706,7 +706,7 @@ BOOL transport_headers_send(transport_ctx * tctx, BOOL (*sendfn)(transport_ctx * tctx, uschar * s, int len)) { -const uschar *list; +const uschar * list; transport_instance * tblock = tctx ? tctx->tblock : NULL; address_item * addr = tctx ? tctx->addr : NULL; @@ -761,15 +761,18 @@ for (header_line * h = header_list; h; h = h->next) if (h->type != htype_old) if (include_header) { + int len; if (tblock && tblock->rewrite_rules) { rmark reset_point = store_mark(); - header_line *hh; + header_line * hh; if ((hh = rewrite_header(h, NULL, NULL, tblock->rewrite_rules, tblock->rewrite_existflags, FALSE))) { - if (!sendfn(tctx, hh->text, hh->slen)) return FALSE; + len = hh->slen; + if (tctx->options & topt_truncate_headers && len > 998) len = 998; + if (!sendfn(tctx, hh->text, len)) return FALSE; store_reset(reset_point); continue; /* With the next header line */ } @@ -777,7 +780,9 @@ for (header_line * h = header_list; h; h = h->next) if (h->type != htype_old) /* Either no rewriting rules, or it didn't get rewritten */ - if (!sendfn(tctx, h->text, h->slen)) return FALSE; + len = h->slen; + if (tctx->options & topt_truncate_headers && len > 998) len = 998; + if (!sendfn(tctx, h->text, len)) return FALSE; } /* Header removed */ commit 1745d310db8102be38db65401df57747e9beaf6a Author: Jeremy Harris Date: Fri Mar 17 18:39:46 2023 +0000 ACL: patterns for remove_headers. Bug 2985 diff --git a/src/src/header.c b/src/src/header.c index d5f1dcd6b..59a9a13b3 100644 --- a/src/src/header.c +++ b/src/src/header.c @@ -30,11 +30,12 @@ Returns: TRUE or FALSE */ BOOL -header_testname(header_line *h, const uschar *name, int len, BOOL notdel) +header_testname(const header_line * h, const uschar * name, int len, + BOOL notdel) { uschar *tt; if (h->type == '*' && notdel) return FALSE; -if (h->text == NULL || strncmpic(h->text, name, len) != 0) return FALSE; +if (!h->text || strncmpic(h->text, name, len) != 0) return FALSE; tt = h->text + len; while (*tt == ' ' || *tt == '\t') tt++; return *tt == ':'; @@ -46,11 +47,11 @@ return *tt == ':'; header_testname() above. */ BOOL -header_testname_incomplete(header_line *h, const uschar *name, +header_testname_incomplete(const header_line * h, const uschar * name, int len, BOOL notdel) { if (h->type == '*' && notdel) return FALSE; -if (h->text == NULL || strncmpic(h->text, name, len) != 0) return FALSE; +if (!h->text || strncmpic(h->text, name, len) != 0) return FALSE; return TRUE; } diff --git a/src/src/local_scan.h b/src/src/local_scan.h index c88994442..72f2ac47d 100644 --- a/src/src/local_scan.h +++ b/src/src/local_scan.h @@ -188,8 +188,8 @@ extern uschar *expand_string(uschar *); extern void header_add(int, const char *, ...); extern void header_add_at_position(BOOL, uschar *, BOOL, int, const char *, ...); extern void header_remove(int, const uschar *); -extern BOOL header_testname(header_line *, const uschar *, int, BOOL); -extern BOOL header_testname_incomplete(header_line *, const uschar *, int, BOOL); +extern BOOL header_testname(const header_line *, const uschar *, int, BOOL); +extern BOOL header_testname_incomplete(const header_line *, const uschar *, int, BOOL); extern void log_write(unsigned int, int, const char *format, ...) PRINTF_FUNCTION(3,4); extern int lss_b64decode(uschar *, uschar **); extern uschar *lss_b64encode(uschar *, int); diff --git a/src/src/receive.c b/src/src/receive.c index 77665d89f..94fa6d5de 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1230,9 +1230,9 @@ Returns: nothing */ static void -add_acl_headers(int where, uschar *acl_name) +add_acl_headers(int where, uschar * acl_name) { -header_line *last_received = NULL; +header_line * last_received = NULL; switch(where) { @@ -1254,15 +1254,22 @@ if (acl_removed_headers) for (header_line * h = header_list; h; h = h->next) if (h->type != htype_old) { - const uschar * list = acl_removed_headers; + const uschar * list = acl_removed_headers, * s; int sep = ':'; /* This is specified as a colon-separated list */ - uschar *s; + /* If a list element has a leading '^' then it is an RE for + the whole header, else just a header name. */ while ((s = string_nextinlist(&list, &sep, NULL, 0))) - if (header_testname(h, s, Ustrlen(s), FALSE)) + if ( ( *s == '^' + && regex_match( + regex_must_compile(s, MCS_CACHEABLE, FALSE), + h->text, h->slen, NULL) + ) + || header_testname(h, s, Ustrlen(s), FALSE) + ) { h->type = htype_old; - DEBUG(D_receive|D_acl) debug_printf_indent(" %s", h->text); + DEBUG(D_receive|D_acl) debug_printf_indent(" %s", h->text); } } acl_removed_headers = NULL; commit 3e6d406e8ae9681a8cc1b404e7f5d1bd6d65d201 Author: Andrew Aitchison Date: Sat Mar 18 15:36:21 2023 +0000 Docs: tweak standards status of port 465 diff --git a/src/src/configure.default b/src/src/configure.default index 3761dafbf..83380bdaa 100644 --- a/src/src/configure.default +++ b/src/src/configure.default @@ -183,11 +183,15 @@ tls_resumption_hosts = ${if inlist {$received_port}{587:465} {:}{*}} # In order to support roaming users who wish to send email from anywhere, # you may want to make Exim listen on other ports as well as port 25, in # case these users need to send email from a network that blocks port 25. -# The standard port for this purpose is port 587, the "message submission" -# port. See RFC 4409 for details. Microsoft MUAs cannot be configured to +# The standard ports for this purpose are: +# port 587, the "message submission" port - see RFC 4409 for details, +# and 465 the TLS-encrypted "submission" port, service name is "submissions", +# see RFC 8314. + +# Microsoft MUAs cannot be configured to # talk the message submission protocol correctly, so if you need to support -# them you should also allow TLS-on-connect on the traditional but -# non-standard port 465. +# them you should also allow TLS-on-connect on the traditional (and now +# standard) port 465. # daemon_smtp_ports = 25 : 465 : 587 # tls_on_connect_ports = 465 commit e08a679856effe96c0099d3516e6edec1f5616d6 Author: Jeremy Harris Date: Sun Mar 19 20:01:53 2023 +0000 Tidying diff --git a/src/src/macros.h b/src/src/macros.h index 73c6ac2c6..36ed185ed 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -817,9 +817,7 @@ local_scan.h */ #define DEBUG_FROM_CONFIG 0x0001 /* SMTP command identifiers for the smtp_connection_had field that records the -most recent SMTP commands. Must be kept in step with the list of names in -smtp_in.c that is used for creating the smtp_no_mail logging action. SCH_NONE -is "empty". */ +most recent SMTP commands. SCH_NONE is "empty". */ enum { SCH_NONE, SCH_AUTH, SCH_DATA, SCH_BDAT, SCH_EHLO, SCH_ETRN, SCH_EXPN, SCH_HELO, diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index a13af867a..5b2df7805 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -189,16 +189,22 @@ count of non-mail commands and possibly provoke an error. tls_auth is a pseudo-command, never expected in input. It is activated on TLS startup and looks for a tls authenticator. */ +enum { CL_RSET, CL_HELO, CL_EHLO, CL_AUTH, +#ifndef DISABLE_TLS + CL_STLS, CL_TLAU, +#endif +}; + static smtp_cmd_list cmd_list[] = { /* name len cmd has_arg is_mail_cmd */ - { "rset", sizeof("rset")-1, RSET_CMD, FALSE, FALSE }, /* First */ - { "helo", sizeof("helo")-1, HELO_CMD, TRUE, FALSE }, - { "ehlo", sizeof("ehlo")-1, EHLO_CMD, TRUE, FALSE }, - { "auth", sizeof("auth")-1, AUTH_CMD, TRUE, TRUE }, + [CL_RSET] = { "rset", sizeof("rset")-1, RSET_CMD, FALSE, FALSE }, /* First */ + [CL_HELO] = { "helo", sizeof("helo")-1, HELO_CMD, TRUE, FALSE }, + [CL_EHLO] = { "ehlo", sizeof("ehlo")-1, EHLO_CMD, TRUE, FALSE }, + [CL_AUTH] = { "auth", sizeof("auth")-1, AUTH_CMD, TRUE, TRUE }, #ifndef DISABLE_TLS - { "starttls", sizeof("starttls")-1, STARTTLS_CMD, FALSE, FALSE }, - { "tls_auth", 0, TLS_AUTH_CMD, FALSE, FALSE }, + [CL_STLS] = { "starttls", sizeof("starttls")-1, STARTTLS_CMD, FALSE, FALSE }, + [CL_TLAU] = { "tls_auth", 0, TLS_AUTH_CMD, FALSE, FALSE }, #endif /* If you change anything above here, also fix the definitions below. */ @@ -215,24 +221,27 @@ static smtp_cmd_list cmd_list[] = { { "help", sizeof("help")-1, HELP_CMD, TRUE, FALSE } }; -static smtp_cmd_list *cmd_list_end = - cmd_list + sizeof(cmd_list)/sizeof(smtp_cmd_list); - -#define CMD_LIST_RSET 0 -#define CMD_LIST_HELO 1 -#define CMD_LIST_EHLO 2 -#define CMD_LIST_AUTH 3 -#define CMD_LIST_STARTTLS 4 -#define CMD_LIST_TLS_AUTH 5 - -/* This list of names is used for performing the smtp_no_mail logging action. -It must be kept in step with the SCH_xxx enumerations. */ +/* This list of names is used for performing the smtp_no_mail logging action. */ uschar * smtp_names[] = { - US"NONE", US"AUTH", US"DATA", US"BDAT", US"EHLO", US"ETRN", US"EXPN", - US"HELO", US"HELP", US"MAIL", US"NOOP", US"QUIT", US"RCPT", US"RSET", - US"STARTTLS", US"VRFY" }; + [SCH_NONE] = US"NONE", + [SCH_AUTH] = US"AUTH", + [SCH_DATA] = US"DATA", + [SCH_BDAT] = US"BDAT", + [SCH_EHLO] = US"EHLO", + [SCH_ETRN] = US"ETRN", + [SCH_EXPN] = US"EXPN", + [SCH_HELO] = US"HELO", + [SCH_HELP] = US"HELP", + [SCH_MAIL] = US"MAIL", + [SCH_NOOP] = US"NOOP", + [SCH_QUIT] = US"QUIT", + [SCH_RCPT] = US"RCPT", + [SCH_RSET] = US"RSET", + [SCH_STARTTLS] = US"STARTTLS", + [SCH_VRFY] = US"VRFY", + }; static uschar *protocols_local[] = { US"local-smtp", /* HELO */ @@ -1685,7 +1694,7 @@ if (hadnull) return BADCHAR_CMD; to the start of the actual data characters. Check for SMTP synchronization if required. */ -for (smtp_cmd_list * p = cmd_list; p < cmd_list_end; p++) +for (smtp_cmd_list * p = cmd_list; p < cmd_list + nelem(cmd_list); p++) { #ifdef SUPPORT_PROXY /* Only allow QUIT command if Proxy Protocol parsing failed */ @@ -3058,7 +3067,7 @@ if (tls_in.on_connect) { if (tls_server_start(&user_msg) != OK) return smtp_log_tls_fail(user_msg); - cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; + cmd_list[CL_TLAU].is_mail_cmd = TRUE; } #endif @@ -4029,7 +4038,7 @@ smtp_rset_handler(void) HAD(SCH_RSET); incomplete_transaction_log(US"RSET"); smtp_printf("250 Reset OK\r\n", FALSE); -cmd_list[CMD_LIST_RSET].is_mail_cmd = FALSE; +cmd_list[CL_RSET].is_mail_cmd = FALSE; if (chunking_state > CHUNKING_OFFERED) chunking_state = CHUNKING_OFFERED; } @@ -4091,11 +4100,11 @@ message_ended = END_NOTSTARTED; chunking_state = f.chunking_offered ? CHUNKING_OFFERED : CHUNKING_NOT_OFFERED; -cmd_list[CMD_LIST_RSET].is_mail_cmd = TRUE; -cmd_list[CMD_LIST_HELO].is_mail_cmd = TRUE; -cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE; +cmd_list[CL_RSET].is_mail_cmd = TRUE; +cmd_list[CL_HELO].is_mail_cmd = TRUE; +cmd_list[CL_EHLO].is_mail_cmd = TRUE; #ifndef DISABLE_TLS -cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = TRUE; +cmd_list[CL_STLS].is_mail_cmd = TRUE; #endif if (lwr_receive_getc != NULL) @@ -4151,10 +4160,10 @@ while (done <= 0) if ( tls_in.active.sock >= 0 && tls_in.peercert && tls_in.certificate_verified - && cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd + && cmd_list[CL_TLAU].is_mail_cmd ) { - cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = FALSE; + cmd_list[CL_TLAU].is_mail_cmd = FALSE; for (auth_instance * au = auths; au; au = au->next) if (strcmpic(US"tls", au->driver_name) == 0) @@ -4214,7 +4223,7 @@ while (done <= 0) case AUTH_CMD: HAD(SCH_AUTH); authentication_failed = TRUE; - cmd_list[CMD_LIST_AUTH].is_mail_cmd = FALSE; + cmd_list[CL_AUTH].is_mail_cmd = FALSE; if (!fl.auth_advertised && !f.allow_auth_unadvertised) { @@ -4336,8 +4345,8 @@ while (done <= 0) fl.esmtp = TRUE; HELO_EHLO: /* Common code for HELO and EHLO */ - cmd_list[CMD_LIST_HELO].is_mail_cmd = FALSE; - cmd_list[CMD_LIST_EHLO].is_mail_cmd = FALSE; + cmd_list[CL_HELO].is_mail_cmd = FALSE; + cmd_list[CL_EHLO].is_mail_cmd = FALSE; /* Reject the HELO if its argument was invalid or non-existent. A successful check causes the argument to be saved in malloc store. */ @@ -5685,7 +5694,7 @@ while (done <= 0) cancel_cutthrough_connection(TRUE, US"STARTTLS received"); reset_point = smtp_reset(reset_point); toomany = FALSE; - cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = FALSE; + cmd_list[CL_STLS].is_mail_cmd = FALSE; /* There's an attack where more data is read in past the STARTTLS command before TLS is negotiated, then assumed to be part of the secure session @@ -5726,9 +5735,9 @@ while (done <= 0) { if (!tls_remember_esmtp) fl.helo_seen = fl.esmtp = fl.auth_advertised = f.smtp_in_pipelining_advertised = FALSE; - cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE; - cmd_list[CMD_LIST_AUTH].is_mail_cmd = TRUE; - cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE; + cmd_list[CL_EHLO].is_mail_cmd = TRUE; + cmd_list[CL_AUTH].is_mail_cmd = TRUE; + cmd_list[CL_TLAU].is_mail_cmd = TRUE; if (sender_helo_name) { sender_helo_name = NULL; @@ -5838,23 +5847,19 @@ while (done <= 0) case HELP_CMD: HAD(SCH_HELP); - smtp_printf("214-Commands supported:\r\n", TRUE); - { - uschar buffer[256]; - buffer[0] = 0; - Ustrcat(buffer, US" AUTH"); - #ifndef DISABLE_TLS - if (tls_in.active.sock < 0 && - verify_check_host(&tls_advertise_hosts) != FAIL) - Ustrcat(buffer, US" STARTTLS"); - #endif - Ustrcat(buffer, US" HELO EHLO MAIL RCPT DATA BDAT"); - Ustrcat(buffer, US" NOOP QUIT RSET HELP"); - if (acl_smtp_etrn) Ustrcat(buffer, US" ETRN"); - if (acl_smtp_expn) Ustrcat(buffer, US" EXPN"); - if (acl_smtp_vrfy) Ustrcat(buffer, US" VRFY"); - smtp_printf("214%s\r\n", FALSE, buffer); - } + smtp_printf("214-Commands supported:\r\n214", TRUE); + smtp_printf(" AUTH", TRUE); +#ifndef DISABLE_TLS + if (tls_in.active.sock < 0 && + verify_check_host(&tls_advertise_hosts) != FAIL) + smtp_printf(" STARTTLS", TRUE); +#endif + smtp_printf(" HELO EHLO MAIL RCPT DATA BDAT", TRUE); + smtp_printf(" NOOP QUIT RSET HELP", TRUE); + if (acl_smtp_etrn) smtp_printf(" ETRN", TRUE); + if (acl_smtp_expn) smtp_printf(" EXPN", TRUE); + if (acl_smtp_vrfy) smtp_printf(" VRFY", TRUE); + smtp_printf("\r\n", FALSE); break; commit df0dc54a7666ef64b8a6681ab7b50a4836905203 Author: Jeremy Harris Date: Tue Mar 21 20:02:18 2023 +0000 Move Proxy-Protocol impl to separate srcfile diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index 29c037401..d00ab9404 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -508,7 +508,7 @@ OBJ_EXIM = acl.o base64.o child.o crypt16.o daemon.o dbfn.o debug.o deliver.o \ directory.o dns.o drtables.o enq.o exim.o expand.o filter.o \ filtertest.o globals.o dkim.o dkim_transport.o dnsbl.o hash.o \ header.o host.o host_address.o ip.o log.o lss.o match.o md5.o moan.o \ - os.o parse.o priv.o queue.o \ + os.o parse.o priv.o proxy.o queue.o \ rda.o readconf.o receive.o retry.o rewrite.o rfc2047.o regex_cache.o \ route.o search.o sieve.o smtp_in.o smtp_out.o spool_in.o spool_out.o \ std-crypto.o store.o string.o tls.o tod.o transport.o tree.o verify.o \ @@ -824,6 +824,7 @@ moan.o: $(HDRS) moan.c os.o: $(HDRS) $(OS_C_INCLUDES) os.c parse.o: $(HDRS) parse.c priv.o: $(HDRS) priv.c +proxy.o: $(HDRS) proxy.c queue.o: $(HDRS) queue.c rda.o: $(HDRS) rda.c readconf.o: $(HDRS) readconf.c diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks index 6e0b65f5d..af6138063 100755 --- a/src/scripts/MakeLinks +++ b/src/scripts/MakeLinks @@ -104,7 +104,7 @@ for f in blob.h dbfunctions.h exim.h functions.h globals.h \ deliver.c directory.c dns.c dnsbl.c drtables.c dummies.c enq.c exim.c \ exim_dbmbuild.c exim_dbutil.c exim_lock.c expand.c filter.c filtertest.c \ globals.c hash.c header.c host.c host_address.c ip.c log.c lss.c match.c md5.c moan.c \ - parse.c perl.c priv.c queue.c rda.c readconf.c receive.c retry.c rewrite.c \ + parse.c perl.c priv.c proxy.c queue.c rda.c readconf.c receive.c retry.c rewrite.c \ regex_cache.c rfc2047.c route.c search.c setenv.c environment.c \ sieve.c smtp_in.c smtp_out.c spool_in.c spool_out.c std-crypto.c store.c \ string.c tls.c tlscert-gnu.c tlscert-openssl.c tls-cipher-stdname.c \ diff --git a/src/src/functions.h b/src/src/functions.h index 896122a69..76392f304 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -400,9 +400,9 @@ extern const uschar *parse_quote_2047(const uschar *, int, const uschar *, extern const uschar *parse_date_time(const uschar *str, time_t *t); extern void priv_drop_temp(const uid_t, const gid_t); extern void priv_restore(void); -extern int vaguely_random_number(int); -#ifndef DISABLE_TLS -extern int vaguely_random_number_fallback(int); +#ifdef SUPPORT_PROXY +extern BOOL proxy_protocol_host(void); +extern void proxy_protocol_setup(void); #endif extern BOOL queue_action(uschar *, int, uschar **, int, int); @@ -658,6 +658,10 @@ extern void unspool_mbox(void); extern gstring *utf8_version_report(gstring *); #endif +extern int vaguely_random_number(int); +#ifndef DISABLE_TLS +extern int vaguely_random_number_fallback(int); +#endif extern int verify_address(address_item *, FILE *, int, int, int, int, uschar *, uschar *, BOOL *); extern int verify_check_dnsbl(int, const uschar **, uschar **); diff --git a/src/src/globals.c b/src/src/globals.c index c6bacc02f..539bae00e 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -387,7 +387,7 @@ BOOL mua_wrapper = FALSE; BOOL preserve_message_logs = FALSE; BOOL print_topbitchars = FALSE; BOOL prod_requires_admin = TRUE; -#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) +#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) || defined(EXPERIMETAL_XCLIENT) BOOL proxy_session = FALSE; #endif diff --git a/src/src/globals.h b/src/src/globals.h index 81d052fd5..e216b9208 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -821,7 +821,7 @@ extern uschar *process_log_path; /* Alternate path */ extern const uschar *process_purpose; /* for debug output */ extern BOOL prod_requires_admin; /* TRUE if prodding requires admin */ -#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) +#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) || defined(EXPERIMENTAL_XCLIENT) extern uschar *hosts_proxy; /* Hostlist which (require) use proxy protocol */ extern uschar *proxy_external_address; /* IP of remote interface of proxy */ extern int proxy_external_port; /* Port on remote interface of proxy */ diff --git a/src/src/proxy.c b/src/src/proxy.c new file mode 100644 index 000000000..fbce11163 --- /dev/null +++ b/src/src/proxy.c @@ -0,0 +1,529 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) The Exim Maintainers 2020 - 2023 */ +/* Copyright (c) University of Cambridge 1995 - 2018 */ +/* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/************************************************ +* Proxy-Protocol support * +************************************************/ + +#include "exim.h" + +#ifdef SUPPORT_PROXY +/************************************************* +* Check if host is required proxy host * +*************************************************/ +/* The function determines if inbound host will be a regular smtp host +or if it is configured that it must use Proxy Protocol. A local +connection cannot. + +Arguments: none +Returns: boolean for Proxy Protocol needed +*/ + +BOOL +proxy_protocol_host(void) +{ +int rc; + +if ( sender_host_address + && (rc = verify_check_this_host(CUSS &hosts_proxy, NULL, NULL, + sender_host_address, NULL)) == OK) + { + DEBUG(D_receive) + debug_printf("Detected proxy protocol configured host\n"); + proxy_session = TRUE; + } +return proxy_session; +} + + +/************************************************* +* Read data until newline or end of buffer * +*************************************************/ +/* While SMTP is server-speaks-first, TLS is client-speaks-first, so we can't +read an entire buffer and assume there will be nothing past a proxy protocol +header. Our approach normally is to use stdio, but again that relies upon +"STARTTLS\r\n" and a server response before the client starts TLS handshake, or +reading _nothing_ before client TLS handshake. So we don't want to use the +usual buffering reads which may read enough to block TLS starting. + +So unfortunately we're down to "read one byte at a time, with a syscall each, +and expect a little overhead", for all proxy-opened connections which are v1, +just to handle the TLS-on-connect case. Since SSL functions wrap the +underlying fd, we can't assume that we can feed them any already-read content. + +We need to know where to read to, the max capacity, and we'll read until we +get a CR and one more character. Let the caller scream if it's CR+!LF. + +Return the amount read. +*/ + +static int +swallow_until_crlf(int fd, uschar *base, int already, int capacity) +{ +uschar *to = base + already; +uschar *cr; +int have = 0; +int ret; +int last = 0; + +/* For "PROXY UNKNOWN\r\n" we, at time of writing, expect to have read +up through the \r; for the _normal_ case, we haven't yet seen the \r. */ + +cr = memchr(base, '\r', already); +if (cr != NULL) + { + if ((cr - base) < already - 1) + { + /* \r and presumed \n already within what we have; probably not + actually proxy protocol, but abort cleanly. */ + return 0; + } + /* \r is last character read, just need one more. */ + last = 1; + } + +while (capacity > 0) + { + do { ret = read(fd, to, 1); } while (ret == -1 && errno == EINTR && !had_command_timeout); + if (ret == -1) + return -1; + have++; + if (last) + return have; + if (*to == '\r') + last = 1; + capacity--; + to++; + } + +/* reached end without having room for a final newline, abort */ +errno = EOVERFLOW; +return -1; +} + + +static void +proxy_debug(uschar * buf, unsigned start, unsigned end) +{ +debug_printf("PROXY<<"); +while (start < end) debug_printf(" %02x", buf[start++]); +debug_printf("\n"); +} + + +/************************************************* +* Setup host for proxy protocol * +*************************************************/ +/* The function configures the connection based on a header from the +inbound host to use Proxy Protocol. The specification is very exact +so exit with an error if do not find the exact required pieces. This +includes an incorrect number of spaces separating args. + +Arguments: none +Returns: Boolean success +*/ + +void +proxy_protocol_setup(void) +{ +union { + struct { + uschar line[108]; + } v1; + struct { + uschar sig[12]; + uint8_t ver_cmd; + uint8_t fam; + uint16_t len; + union { + struct { /* TCP/UDP over IPv4, len = 12 */ + uint32_t src_addr; + uint32_t dst_addr; + uint16_t src_port; + uint16_t dst_port; + } ip4; + struct { /* TCP/UDP over IPv6, len = 36 */ + uint8_t src_addr[16]; + uint8_t dst_addr[16]; + uint16_t src_port; + uint16_t dst_port; + } ip6; + struct { /* AF_UNIX sockets, len = 216 */ + uschar src_addr[108]; + uschar dst_addr[108]; + } unx; + } addr; + } v2; +} hdr; + +/* Temp variables used in PPv2 address:port parsing */ +uint16_t tmpport; +char tmpip[INET_ADDRSTRLEN]; +struct sockaddr_in tmpaddr; +char tmpip6[INET6_ADDRSTRLEN]; +struct sockaddr_in6 tmpaddr6; + +/* We can't read "all data until end" because while SMTP is +server-speaks-first, the TLS handshake is client-speaks-first, so for +TLS-on-connect ports the proxy protocol header will usually be immediately +followed by a TLS handshake, and with N TLS libraries, we can't reliably +reinject data for reading by those. So instead we first read "enough to be +safely read within the header, and figure out how much more to read". +For v1 we will later read to the end-of-line, for v2 we will read based upon +the stated length. + +The v2 sig is 12 octets, and another 4 gets us the length, so we know how much +data is needed total. For v1, where the line looks like: +PROXY TCPn L3src L3dest SrcPort DestPort \r\n + +However, for v1 there's also `PROXY UNKNOWN\r\n` which is only 15 octets. +We seem to support that. So, if we read 14 octets then we can tell if we're +v2 or v1. If we're v1, we can continue reading as normal. + +If we're v2, we can't slurp up the entire header. We need the length in the +15th & 16th octets, then to read everything after that. + +So to safely handle v1 and v2, with client-sent-first supported correctly, +we have to do a minimum of 3 read calls, not 1. Eww. +*/ + +# define PROXY_INITIAL_READ 14 +# define PROXY_V2_HEADER_SIZE 16 +# if PROXY_INITIAL_READ > PROXY_V2_HEADER_SIZE +# error Code bug in sizes of data to read for proxy usage +# endif + +int get_ok = 0; +int size, ret; +int fd = fileno(smtp_in); +const char v2sig[12] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"; +uschar * iptype; /* To display debug info */ +socklen_t vslen = sizeof(struct timeval); +BOOL yield = FALSE; + +ALARM(proxy_protocol_timeout); + +do + { + /* The inbound host was declared to be a Proxy Protocol host, so + don't do a PEEK into the data, actually slurp up enough to be + "safe". Can't take it all because TLS-on-connect clients follow + immediately with TLS handshake. */ + ret = read(fd, &hdr, PROXY_INITIAL_READ); + } while (ret == -1 && errno == EINTR && !had_command_timeout); + +if (ret == -1) + goto proxyfail; +DEBUG(D_receive) proxy_debug(US &hdr, 0, ret); + +/* For v2, handle reading the length, and then the rest. */ +if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0)) + { + int retmore; + uint8_t ver; + + DEBUG(D_receive) debug_printf("v2\n"); + + /* First get the length fields. */ + do + { + retmore = read(fd, (uschar*)&hdr + ret, PROXY_V2_HEADER_SIZE - PROXY_INITIAL_READ); + } while (retmore == -1 && errno == EINTR && !had_command_timeout); + if (retmore == -1) + goto proxyfail; + DEBUG(D_receive) proxy_debug(US &hdr, ret, ret + retmore); + + ret += retmore; + + ver = (hdr.v2.ver_cmd & 0xf0) >> 4; + + /* May 2014: haproxy combined the version and command into one byte to + allow two full bytes for the length field in order to proxy SSL + connections. SSL Proxy is not supported in this version of Exim, but + must still separate values here. */ + + if (ver != 0x02) + { + DEBUG(D_receive) debug_printf("Invalid Proxy Protocol version: %d\n", ver); + goto proxyfail; + } + + /* The v2 header will always be 16 bytes per the spec. */ + size = 16 + ntohs(hdr.v2.len); + DEBUG(D_receive) debug_printf("Detected PROXYv2 header, size %d (limit %d)\n", + size, (int)sizeof(hdr)); + + /* We should now have 16 octets (PROXY_V2_HEADER_SIZE), and we know the total + amount that we need. Double-check that the size is not unreasonable, then + get the rest. */ + if (size > sizeof(hdr)) + { + DEBUG(D_receive) debug_printf("PROXYv2 header size unreasonably large; security attack?\n"); + goto proxyfail; + } + + do + { + do + { + retmore = read(fd, (uschar*)&hdr + ret, size-ret); + } while (retmore == -1 && errno == EINTR && !had_command_timeout); + if (retmore == -1) + goto proxyfail; + DEBUG(D_receive) proxy_debug(US &hdr, ret, ret + retmore); + ret += retmore; + DEBUG(D_receive) debug_printf("PROXYv2: have %d/%d required octets\n", ret, size); + } while (ret < size); + + } /* end scope for getting rest of data for v2 */ + +/* At this point: if PROXYv2, we've read the exact size required for all data; +if PROXYv1 then we've read "less than required for any valid line" and should +read the rest". */ + +if (ret >= 16 && memcmp(&hdr.v2, v2sig, 12) == 0) + { + uint8_t cmd = (hdr.v2.ver_cmd & 0x0f); + + switch (cmd) + { + case 0x01: /* PROXY command */ + switch (hdr.v2.fam) + { + case 0x11: /* TCPv4 address type */ + iptype = US"IPv4"; + tmpaddr.sin_addr.s_addr = hdr.v2.addr.ip4.src_addr; + inet_ntop(AF_INET, &tmpaddr.sin_addr, CS &tmpip, sizeof(tmpip)); + if (!string_is_ip_address(US tmpip, NULL)) + { + DEBUG(D_receive) debug_printf("Invalid %s source IP\n", iptype); + goto proxyfail; + } + proxy_local_address = sender_host_address; + sender_host_address = string_copy(US tmpip); + tmpport = ntohs(hdr.v2.addr.ip4.src_port); + proxy_local_port = sender_host_port; + sender_host_port = tmpport; + /* Save dest ip/port */ + tmpaddr.sin_addr.s_addr = hdr.v2.addr.ip4.dst_addr; + inet_ntop(AF_INET, &tmpaddr.sin_addr, CS &tmpip, sizeof(tmpip)); + if (!string_is_ip_address(US tmpip, NULL)) + { + DEBUG(D_receive) debug_printf("Invalid %s dest port\n", iptype); + goto proxyfail; + } + proxy_external_address = string_copy(US tmpip); + tmpport = ntohs(hdr.v2.addr.ip4.dst_port); + proxy_external_port = tmpport; + goto done; + case 0x21: /* TCPv6 address type */ + iptype = US"IPv6"; + memmove(tmpaddr6.sin6_addr.s6_addr, hdr.v2.addr.ip6.src_addr, 16); + inet_ntop(AF_INET6, &tmpaddr6.sin6_addr, CS &tmpip6, sizeof(tmpip6)); + if (!string_is_ip_address(US tmpip6, NULL)) + { + DEBUG(D_receive) debug_printf("Invalid %s source IP\n", iptype); + goto proxyfail; + } + proxy_local_address = sender_host_address; + sender_host_address = string_copy(US tmpip6); + tmpport = ntohs(hdr.v2.addr.ip6.src_port); + proxy_local_port = sender_host_port; + sender_host_port = tmpport; + /* Save dest ip/port */ + memmove(tmpaddr6.sin6_addr.s6_addr, hdr.v2.addr.ip6.dst_addr, 16); + inet_ntop(AF_INET6, &tmpaddr6.sin6_addr, CS &tmpip6, sizeof(tmpip6)); + if (!string_is_ip_address(US tmpip6, NULL)) + { + DEBUG(D_receive) debug_printf("Invalid %s dest port\n", iptype); + goto proxyfail; + } + proxy_external_address = string_copy(US tmpip6); + tmpport = ntohs(hdr.v2.addr.ip6.dst_port); + proxy_external_port = tmpport; + goto done; + default: + DEBUG(D_receive) + debug_printf("Unsupported PROXYv2 connection type: 0x%02x\n", + hdr.v2.fam); + goto proxyfail; + } + /* Unsupported protocol, keep local connection address */ + break; + case 0x00: /* LOCAL command */ + /* Keep local connection address for LOCAL */ + iptype = US"local"; + break; + default: + DEBUG(D_receive) + debug_printf("Unsupported PROXYv2 command: 0x%x\n", cmd); + goto proxyfail; + } + } +else if (ret >= 8 && memcmp(hdr.v1.line, "PROXY", 5) == 0) + { + uschar *p; + uschar *end; + uschar *sp; /* Utility variables follow */ + int tmp_port; + int r2; + char *endc; + + /* get the rest of the line */ + r2 = swallow_until_crlf(fd, (uschar*)&hdr, ret, sizeof(hdr)-ret); + if (r2 == -1) + goto proxyfail; + ret += r2; + + p = string_copy(hdr.v1.line); + end = memchr(p, '\r', ret - 1); + + if (!end || (end == (uschar*)&hdr + ret) || end[1] != '\n') + { + DEBUG(D_receive) debug_printf("Partial or invalid PROXY header\n"); + goto proxyfail; + } + *end = '\0'; /* Terminate the string */ + size = end + 2 - p; /* Skip header + CRLF */ + DEBUG(D_receive) debug_printf("Detected PROXYv1 header\n"); + DEBUG(D_receive) debug_printf("Bytes read not within PROXY header: %d\n", ret - size); + /* Step through the string looking for the required fields. Ensure + strict adherence to required formatting, exit for any error. */ + p += 5; + if (!isspace(*(p++))) + { + DEBUG(D_receive) debug_printf("Missing space after PROXY command\n"); + goto proxyfail; + } + if (!Ustrncmp(p, CCS"TCP4", 4)) + iptype = US"IPv4"; + else if (!Ustrncmp(p,CCS"TCP6", 4)) + iptype = US"IPv6"; + else if (!Ustrncmp(p,CCS"UNKNOWN", 7)) + { + iptype = US"Unknown"; + goto done; + } + else + { + DEBUG(D_receive) debug_printf("Invalid TCP type\n"); + goto proxyfail; + } + + p += Ustrlen(iptype); + if (!isspace(*(p++))) + { + DEBUG(D_receive) debug_printf("Missing space after TCP4/6 command\n"); + goto proxyfail; + } + /* Find the end of the arg */ + if ((sp = Ustrchr(p, ' ')) == NULL) + { + DEBUG(D_receive) + debug_printf("Did not find proxied src %s\n", iptype); + goto proxyfail; + } + *sp = '\0'; + if(!string_is_ip_address(p, NULL)) + { + DEBUG(D_receive) + debug_printf("Proxied src arg is not an %s address\n", iptype); + goto proxyfail; + } + proxy_local_address = sender_host_address; + sender_host_address = p; + p = sp + 1; + if ((sp = Ustrchr(p, ' ')) == NULL) + { + DEBUG(D_receive) + debug_printf("Did not find proxy dest %s\n", iptype); + goto proxyfail; + } + *sp = '\0'; + if(!string_is_ip_address(p, NULL)) + { + DEBUG(D_receive) + debug_printf("Proxy dest arg is not an %s address\n", iptype); + goto proxyfail; + } + proxy_external_address = p; + p = sp + 1; + if ((sp = Ustrchr(p, ' ')) == NULL) + { + DEBUG(D_receive) debug_printf("Did not find proxied src port\n"); + goto proxyfail; + } + *sp = '\0'; + tmp_port = strtol(CCS p, &endc, 10); + if (*endc || tmp_port == 0) + { + DEBUG(D_receive) + debug_printf("Proxied src port '%s' not an integer\n", p); + goto proxyfail; + } + proxy_local_port = sender_host_port; + sender_host_port = tmp_port; + p = sp + 1; + if ((sp = Ustrchr(p, '\0')) == NULL) + { + DEBUG(D_receive) debug_printf("Did not find proxy dest port\n"); + goto proxyfail; + } + tmp_port = strtol(CCS p, &endc, 10); + if (*endc || tmp_port == 0) + { + DEBUG(D_receive) + debug_printf("Proxy dest port '%s' not an integer\n", p); + goto proxyfail; + } + proxy_external_port = tmp_port; + /* Already checked for /r /n above. Good V1 header received. */ + } +else + { + /* Wrong protocol */ + DEBUG(D_receive) debug_printf("Invalid proxy protocol version negotiation\n"); + (void) swallow_until_crlf(fd, (uschar*)&hdr, ret, sizeof(hdr)-ret); + goto proxyfail; + } + +done: + DEBUG(D_receive) + debug_printf("Valid %s sender from Proxy Protocol header\n", iptype); + yield = proxy_session; + +/* Don't flush any potential buffer contents. Any input on proxyfail +should cause a synchronization failure */ + +proxyfail: + DEBUG(D_receive) if (had_command_timeout) + debug_printf("Timeout while reading proxy header\n"); + +bad: + if (yield) + { + sender_host_name = NULL; + (void) host_name_lookup(); + host_build_sender_fullhost(); + } + else + { + f.proxy_session_failed = TRUE; + DEBUG(D_receive) + debug_printf("Failure to extract proxied host, only QUIT allowed\n"); + } + +ALARM(0); +return; +} +#endif /*SUPPORT_PROXY*/ + +/* vi: aw ai sw=2 +*/ +/* End of proxy.c */ diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 5b2df7805..7a45772ce 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -1110,518 +1110,6 @@ had_command_sigterm = sig; -#ifdef SUPPORT_PROXY -/************************************************* -* Check if host is required proxy host * -*************************************************/ -/* The function determines if inbound host will be a regular smtp host -or if it is configured that it must use Proxy Protocol. A local -connection cannot. - -Arguments: none -Returns: bool -*/ - -static BOOL -check_proxy_protocol_host() -{ -int rc; - -if ( sender_host_address - && (rc = verify_check_this_host(CUSS &hosts_proxy, NULL, NULL, - sender_host_address, NULL)) == OK) - { - DEBUG(D_receive) - debug_printf("Detected proxy protocol configured host\n"); - proxy_session = TRUE; - } -return proxy_session; -} - - -/************************************************* -* Read data until newline or end of buffer * -*************************************************/ -/* While SMTP is server-speaks-first, TLS is client-speaks-first, so we can't -read an entire buffer and assume there will be nothing past a proxy protocol -header. Our approach normally is to use stdio, but again that relies upon -"STARTTLS\r\n" and a server response before the client starts TLS handshake, or -reading _nothing_ before client TLS handshake. So we don't want to use the -usual buffering reads which may read enough to block TLS starting. - -So unfortunately we're down to "read one byte at a time, with a syscall each, -and expect a little overhead", for all proxy-opened connections which are v1, -just to handle the TLS-on-connect case. Since SSL functions wrap the -underlying fd, we can't assume that we can feed them any already-read content. - -We need to know where to read to, the max capacity, and we'll read until we -get a CR and one more character. Let the caller scream if it's CR+!LF. - -Return the amount read. -*/ - -static int -swallow_until_crlf(int fd, uschar *base, int already, int capacity) -{ -uschar *to = base + already; -uschar *cr; -int have = 0; -int ret; -int last = 0; - -/* For "PROXY UNKNOWN\r\n" we, at time of writing, expect to have read -up through the \r; for the _normal_ case, we haven't yet seen the \r. */ - -cr = memchr(base, '\r', already); -if (cr != NULL) - { - if ((cr - base) < already - 1) - { - /* \r and presumed \n already within what we have; probably not - actually proxy protocol, but abort cleanly. */ - return 0; - } - /* \r is last character read, just need one more. */ - last = 1; - } - -while (capacity > 0) - { - do { ret = read(fd, to, 1); } while (ret == -1 && errno == EINTR && !had_command_timeout); - if (ret == -1) - return -1; - have++; - if (last) - return have; - if (*to == '\r') - last = 1; - capacity--; - to++; - } - -/* reached end without having room for a final newline, abort */ -errno = EOVERFLOW; -return -1; -} - - -static void -proxy_debug(uschar * buf, unsigned start, unsigned end) -{ -debug_printf("PROXY<<"); -while (start < end) debug_printf(" %02x", buf[start++]); -debug_printf("\n"); -} - - -/************************************************* -* Setup host for proxy protocol * -*************************************************/ -/* The function configures the connection based on a header from the -inbound host to use Proxy Protocol. The specification is very exact -so exit with an error if do not find the exact required pieces. This -includes an incorrect number of spaces separating args. - -Arguments: none -Returns: Boolean success -*/ - -static void -setup_proxy_protocol_host() -{ -union { - struct { - uschar line[108]; - } v1; - struct { - uschar sig[12]; - uint8_t ver_cmd; - uint8_t fam; - uint16_t len; - union { - struct { /* TCP/UDP over IPv4, len = 12 */ - uint32_t src_addr; - uint32_t dst_addr; - uint16_t src_port; - uint16_t dst_port; - } ip4; - struct { /* TCP/UDP over IPv6, len = 36 */ - uint8_t src_addr[16]; - uint8_t dst_addr[16]; - uint16_t src_port; - uint16_t dst_port; - } ip6; - struct { /* AF_UNIX sockets, len = 216 */ - uschar src_addr[108]; - uschar dst_addr[108]; - } unx; - } addr; - } v2; -} hdr; - -/* Temp variables used in PPv2 address:port parsing */ -uint16_t tmpport; -char tmpip[INET_ADDRSTRLEN]; -struct sockaddr_in tmpaddr; -char tmpip6[INET6_ADDRSTRLEN]; -struct sockaddr_in6 tmpaddr6; - -/* We can't read "all data until end" because while SMTP is -server-speaks-first, the TLS handshake is client-speaks-first, so for -TLS-on-connect ports the proxy protocol header will usually be immediately -followed by a TLS handshake, and with N TLS libraries, we can't reliably -reinject data for reading by those. So instead we first read "enough to be -safely read within the header, and figure out how much more to read". -For v1 we will later read to the end-of-line, for v2 we will read based upon -the stated length. - -The v2 sig is 12 octets, and another 4 gets us the length, so we know how much -data is needed total. For v1, where the line looks like: -PROXY TCPn L3src L3dest SrcPort DestPort \r\n - -However, for v1 there's also `PROXY UNKNOWN\r\n` which is only 15 octets. -We seem to support that. So, if we read 14 octets then we can tell if we're -v2 or v1. If we're v1, we can continue reading as normal. - -If we're v2, we can't slurp up the entire header. We need the length in the -15th & 16th octets, then to read everything after that. - -So to safely handle v1 and v2, with client-sent-first supported correctly, -we have to do a minimum of 3 read calls, not 1. Eww. -*/ - -# define PROXY_INITIAL_READ 14 -# define PROXY_V2_HEADER_SIZE 16 -# if PROXY_INITIAL_READ > PROXY_V2_HEADER_SIZE -# error Code bug in sizes of data to read for proxy usage -# endif - -int get_ok = 0; -int size, ret; -int fd = fileno(smtp_in); -const char v2sig[12] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"; -uschar * iptype; /* To display debug info */ -socklen_t vslen = sizeof(struct timeval); -BOOL yield = FALSE; - -os_non_restarting_signal(SIGALRM, command_timeout_handler); -ALARM(proxy_protocol_timeout); - -do - { - /* The inbound host was declared to be a Proxy Protocol host, so - don't do a PEEK into the data, actually slurp up enough to be - "safe". Can't take it all because TLS-on-connect clients follow - immediately with TLS handshake. */ - ret = read(fd, &hdr, PROXY_INITIAL_READ); - } while (ret == -1 && errno == EINTR && !had_command_timeout); - -if (ret == -1) - goto proxyfail; -DEBUG(D_receive) proxy_debug(US &hdr, 0, ret); - -/* For v2, handle reading the length, and then the rest. */ -if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0)) - { - int retmore; - uint8_t ver; - - DEBUG(D_receive) debug_printf("v2\n"); - - /* First get the length fields. */ - do - { - retmore = read(fd, (uschar*)&hdr + ret, PROXY_V2_HEADER_SIZE - PROXY_INITIAL_READ); - } while (retmore == -1 && errno == EINTR && !had_command_timeout); - if (retmore == -1) - goto proxyfail; - DEBUG(D_receive) proxy_debug(US &hdr, ret, ret + retmore); - - ret += retmore; - - ver = (hdr.v2.ver_cmd & 0xf0) >> 4; - - /* May 2014: haproxy combined the version and command into one byte to - allow two full bytes for the length field in order to proxy SSL - connections. SSL Proxy is not supported in this version of Exim, but - must still separate values here. */ - - if (ver != 0x02) - { - DEBUG(D_receive) debug_printf("Invalid Proxy Protocol version: %d\n", ver); - goto proxyfail; - } - - /* The v2 header will always be 16 bytes per the spec. */ - size = 16 + ntohs(hdr.v2.len); - DEBUG(D_receive) debug_printf("Detected PROXYv2 header, size %d (limit %d)\n", - size, (int)sizeof(hdr)); - - /* We should now have 16 octets (PROXY_V2_HEADER_SIZE), and we know the total - amount that we need. Double-check that the size is not unreasonable, then - get the rest. */ - if (size > sizeof(hdr)) - { - DEBUG(D_receive) debug_printf("PROXYv2 header size unreasonably large; security attack?\n"); - goto proxyfail; - } - - do - { - do - { - retmore = read(fd, (uschar*)&hdr + ret, size-ret); - } while (retmore == -1 && errno == EINTR && !had_command_timeout); - if (retmore == -1) - goto proxyfail; - DEBUG(D_receive) proxy_debug(US &hdr, ret, ret + retmore); - ret += retmore; - DEBUG(D_receive) debug_printf("PROXYv2: have %d/%d required octets\n", ret, size); - } while (ret < size); - - } /* end scope for getting rest of data for v2 */ - -/* At this point: if PROXYv2, we've read the exact size required for all data; -if PROXYv1 then we've read "less than required for any valid line" and should -read the rest". */ - -if (ret >= 16 && memcmp(&hdr.v2, v2sig, 12) == 0) - { - uint8_t cmd = (hdr.v2.ver_cmd & 0x0f); - - switch (cmd) - { - case 0x01: /* PROXY command */ - switch (hdr.v2.fam) - { - case 0x11: /* TCPv4 address type */ - iptype = US"IPv4"; - tmpaddr.sin_addr.s_addr = hdr.v2.addr.ip4.src_addr; - inet_ntop(AF_INET, &tmpaddr.sin_addr, CS &tmpip, sizeof(tmpip)); - if (!string_is_ip_address(US tmpip, NULL)) - { - DEBUG(D_receive) debug_printf("Invalid %s source IP\n", iptype); - goto proxyfail; - } - proxy_local_address = sender_host_address; - sender_host_address = string_copy(US tmpip); - tmpport = ntohs(hdr.v2.addr.ip4.src_port); - proxy_local_port = sender_host_port; - sender_host_port = tmpport; - /* Save dest ip/port */ - tmpaddr.sin_addr.s_addr = hdr.v2.addr.ip4.dst_addr; - inet_ntop(AF_INET, &tmpaddr.sin_addr, CS &tmpip, sizeof(tmpip)); - if (!string_is_ip_address(US tmpip, NULL)) - { - DEBUG(D_receive) debug_printf("Invalid %s dest port\n", iptype); - goto proxyfail; - } - proxy_external_address = string_copy(US tmpip); - tmpport = ntohs(hdr.v2.addr.ip4.dst_port); - proxy_external_port = tmpport; - goto done; - case 0x21: /* TCPv6 address type */ - iptype = US"IPv6"; - memmove(tmpaddr6.sin6_addr.s6_addr, hdr.v2.addr.ip6.src_addr, 16); - inet_ntop(AF_INET6, &tmpaddr6.sin6_addr, CS &tmpip6, sizeof(tmpip6)); - if (!string_is_ip_address(US tmpip6, NULL)) - { - DEBUG(D_receive) debug_printf("Invalid %s source IP\n", iptype); - goto proxyfail; - } - proxy_local_address = sender_host_address; - sender_host_address = string_copy(US tmpip6); - tmpport = ntohs(hdr.v2.addr.ip6.src_port); - proxy_local_port = sender_host_port; - sender_host_port = tmpport; - /* Save dest ip/port */ - memmove(tmpaddr6.sin6_addr.s6_addr, hdr.v2.addr.ip6.dst_addr, 16); - inet_ntop(AF_INET6, &tmpaddr6.sin6_addr, CS &tmpip6, sizeof(tmpip6)); - if (!string_is_ip_address(US tmpip6, NULL)) - { - DEBUG(D_receive) debug_printf("Invalid %s dest port\n", iptype); - goto proxyfail; - } - proxy_external_address = string_copy(US tmpip6); - tmpport = ntohs(hdr.v2.addr.ip6.dst_port); - proxy_external_port = tmpport; - goto done; - default: - DEBUG(D_receive) - debug_printf("Unsupported PROXYv2 connection type: 0x%02x\n", - hdr.v2.fam); - goto proxyfail; - } - /* Unsupported protocol, keep local connection address */ - break; - case 0x00: /* LOCAL command */ - /* Keep local connection address for LOCAL */ - iptype = US"local"; - break; - default: - DEBUG(D_receive) - debug_printf("Unsupported PROXYv2 command: 0x%x\n", cmd); - goto proxyfail; - } - } -else if (ret >= 8 && memcmp(hdr.v1.line, "PROXY", 5) == 0) - { - uschar *p; - uschar *end; - uschar *sp; /* Utility variables follow */ - int tmp_port; - int r2; - char *endc; - - /* get the rest of the line */ - r2 = swallow_until_crlf(fd, (uschar*)&hdr, ret, sizeof(hdr)-ret); - if (r2 == -1) - goto proxyfail; - ret += r2; - - p = string_copy(hdr.v1.line); - end = memchr(p, '\r', ret - 1); - - if (!end || (end == (uschar*)&hdr + ret) || end[1] != '\n') - { - DEBUG(D_receive) debug_printf("Partial or invalid PROXY header\n"); - goto proxyfail; - } - *end = '\0'; /* Terminate the string */ - size = end + 2 - p; /* Skip header + CRLF */ - DEBUG(D_receive) debug_printf("Detected PROXYv1 header\n"); - DEBUG(D_receive) debug_printf("Bytes read not within PROXY header: %d\n", ret - size); - /* Step through the string looking for the required fields. Ensure - strict adherence to required formatting, exit for any error. */ - p += 5; - if (!isspace(*(p++))) - { - DEBUG(D_receive) debug_printf("Missing space after PROXY command\n"); - goto proxyfail; - } - if (!Ustrncmp(p, CCS"TCP4", 4)) - iptype = US"IPv4"; - else if (!Ustrncmp(p,CCS"TCP6", 4)) - iptype = US"IPv6"; - else if (!Ustrncmp(p,CCS"UNKNOWN", 7)) - { - iptype = US"Unknown"; - goto done; - } - else - { - DEBUG(D_receive) debug_printf("Invalid TCP type\n"); - goto proxyfail; - } - - p += Ustrlen(iptype); - if (!isspace(*(p++))) - { - DEBUG(D_receive) debug_printf("Missing space after TCP4/6 command\n"); - goto proxyfail; - } - /* Find the end of the arg */ - if ((sp = Ustrchr(p, ' ')) == NULL) - { - DEBUG(D_receive) - debug_printf("Did not find proxied src %s\n", iptype); - goto proxyfail; - } - *sp = '\0'; - if(!string_is_ip_address(p, NULL)) - { - DEBUG(D_receive) - debug_printf("Proxied src arg is not an %s address\n", iptype); - goto proxyfail; - } - proxy_local_address = sender_host_address; - sender_host_address = p; - p = sp + 1; - if ((sp = Ustrchr(p, ' ')) == NULL) - { - DEBUG(D_receive) - debug_printf("Did not find proxy dest %s\n", iptype); - goto proxyfail; - } - *sp = '\0'; - if(!string_is_ip_address(p, NULL)) - { - DEBUG(D_receive) - debug_printf("Proxy dest arg is not an %s address\n", iptype); - goto proxyfail; - } - proxy_external_address = p; - p = sp + 1; - if ((sp = Ustrchr(p, ' ')) == NULL) - { - DEBUG(D_receive) debug_printf("Did not find proxied src port\n"); - goto proxyfail; - } - *sp = '\0'; - tmp_port = strtol(CCS p, &endc, 10); - if (*endc || tmp_port == 0) - { - DEBUG(D_receive) - debug_printf("Proxied src port '%s' not an integer\n", p); - goto proxyfail; - } - proxy_local_port = sender_host_port; - sender_host_port = tmp_port; - p = sp + 1; - if ((sp = Ustrchr(p, '\0')) == NULL) - { - DEBUG(D_receive) debug_printf("Did not find proxy dest port\n"); - goto proxyfail; - } - tmp_port = strtol(CCS p, &endc, 10); - if (*endc || tmp_port == 0) - { - DEBUG(D_receive) - debug_printf("Proxy dest port '%s' not an integer\n", p); - goto proxyfail; - } - proxy_external_port = tmp_port; - /* Already checked for /r /n above. Good V1 header received. */ - } -else - { - /* Wrong protocol */ - DEBUG(D_receive) debug_printf("Invalid proxy protocol version negotiation\n"); - (void) swallow_until_crlf(fd, (uschar*)&hdr, ret, sizeof(hdr)-ret); - goto proxyfail; - } - -done: - DEBUG(D_receive) - debug_printf("Valid %s sender from Proxy Protocol header\n", iptype); - yield = proxy_session; - -/* Don't flush any potential buffer contents. Any input on proxyfail -should cause a synchronization failure */ - -proxyfail: - DEBUG(D_receive) if (had_command_timeout) - debug_printf("Timeout while reading proxy header\n"); - -bad: - if (yield) - { - sender_host_name = NULL; - (void) host_name_lookup(); - host_build_sender_fullhost(); - } - else - { - f.proxy_session_failed = TRUE; - DEBUG(D_receive) - debug_printf("Failure to extract proxied host, only QUIT allowed\n"); - } - -ALARM(0); -return; -} -#endif /*SUPPORT_PROXY*/ - /************************************************* * Read one command line * *************************************************/ @@ -3030,14 +2518,20 @@ if (!f.sender_host_unknown) if (smtp_batched_input) return TRUE; +#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) || defined(EXPERIMETAL_XCLIENT) +proxy_session = FALSE; +#endif + +#ifdef SUPPORT_PROXY /* If valid Proxy Protocol source is connecting, set up session. Failure will not allow any SMTP function other than QUIT. */ -#ifdef SUPPORT_PROXY -proxy_session = FALSE; f.proxy_session_failed = FALSE; -if (check_proxy_protocol_host()) - setup_proxy_protocol_host(); +if (proxy_protocol_host()) + { + os_non_restarting_signal(SIGALRM, command_timeout_handler); + proxy_protocol_setup(); + } #endif /* Run the connect ACL if it exists */ commit 24cda181fb88542cf38db2beae5d0ddb37f59c5c Author: Jeremy Harris Date: Sat Mar 25 23:21:15 2023 +0000 Experimental_XCLIENT. Bug 2702 diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index d00ab9404..71aee4d93 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -497,7 +497,8 @@ OBJ_EXPERIMENTAL = arc.o \ dmarc.o \ imap_utf7.o \ spf.o \ - utf8.o + utf8.o \ + xclient.o # Targets for final binaries; the main one has a build number which is # updated each time. We don't bother with that for the auxiliaries. @@ -873,6 +874,7 @@ dmarc.o: $(HDRS) pdkim/pdkim.h dmarc.h dmarc.c imap_utf7.o: $(HDRS) imap_utf7.c spf.o: $(HDRS) spf.h spf.c utf8.o: $(HDRS) utf8.c +xclient.o: $(HDRS) xclient.c # The module containing tables of available lookups, routers, auths, and # transports must be rebuilt if any of them are. However, because the makefiles diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks index af6138063..0694af4c0 100755 --- a/src/scripts/MakeLinks +++ b/src/scripts/MakeLinks @@ -125,7 +125,7 @@ done # EXPERIMENTAL_* for f in arc.c bmi_spam.c bmi_spam.h dcc.c dcc.h dane.c dane-openssl.c \ - danessl.h imap_utf7.c spf.c spf.h srs.c srs.h utf8.c + danessl.h imap_utf7.c spf.c spf.h srs.c srs.h utf8.c xclient.c do ln -s ../src/$f $f done diff --git a/src/src/auths/xtextdecode.c b/src/src/auths/xtextdecode.c index b6a927194..edd2282d0 100644 --- a/src/src/auths/xtextdecode.c +++ b/src/src/auths/xtextdecode.c @@ -32,9 +32,9 @@ Returns: the number of bytes in the result, excluding the final zero; */ int -auth_xtextdecode(uschar *code, uschar **ptr) +auth_xtextdecode(uschar * code, uschar ** ptr) { -register int x; +int x; uschar * result = store_get(Ustrlen(code) + 1, code); *ptr = result; diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index 221705224..fb5fe3603 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -211,6 +211,7 @@ Do not put spaces between # and the 'define'. #define EXPERIMENTAL_DSN_INFO #define EXPERIMENTAL_ESMTP_LIMITS #define EXPERIMENTAL_QUEUEFILE +#define EXPERIMENTAL_XCLIENT /* For developers */ diff --git a/src/src/exim.c b/src/src/exim.c index c16beb1af..06863347d 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1132,6 +1132,9 @@ g = string_cat(g, US"Support for:"); #ifdef EXPERIMENTAL_QUEUEFILE g = string_cat(g, US" Experimental_QUEUEFILE"); #endif +#ifdef EXPERIMENTAL_XCLIENT + g = string_cat(g, US" Experimental_XCLIENT"); +#endif g = string_cat(g, US"\n"); g = string_cat(g, US"Lookups (built-in):"); diff --git a/src/src/functions.h b/src/src/functions.h index 76392f304..aa5057a83 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -686,6 +686,11 @@ extern BOOL write_chunk(transport_ctx *, uschar *, int); extern ssize_t write_to_fd_buf(int, const uschar *, size_t); extern uschar *wrap_header(const uschar *, unsigned, unsigned, const uschar *, unsigned); +#ifdef EXPERIMENTAL_XCLIENT +extern uschar * xclient_smtp_command(uschar *, int *, BOOL *); +extern gstring * xclient_smtp_advertise_str(gstring *); +#endif + /******************************************************************************/ /* Predicate: if an address is in a tainted pool. diff --git a/src/src/globals.c b/src/src/globals.c index 539bae00e..9f4053937 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -995,11 +995,18 @@ uschar *host_lookup_msg = US""; int host_number = 0; uschar *host_number_string = NULL; uschar *host_reject_connection = NULL; -tree_node *hostlist_anchor = NULL; -int hostlist_count = 0; +uschar *hosts_connection_nolog = NULL; +#ifdef SUPPORT_PROXY +uschar *hosts_proxy = NULL; +#endif uschar *hosts_treat_as_local = NULL; uschar *hosts_require_helo = US"*"; -uschar *hosts_connection_nolog = NULL; +#ifdef EXPERIMENTAL_XCLIENT +uschar *hosts_xclient = NULL; +#endif +tree_node *hostlist_anchor = NULL; +int hostlist_count = 0; + int ignore_bounce_errors_after = 10*7*24*60*60; /* 10 weeks */ uschar *ignore_fromline_hosts = NULL; @@ -1232,8 +1239,7 @@ int process_info_len = 0; uschar *process_log_path = NULL; const uschar *process_purpose = US"fresh-exec"; -#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) -uschar *hosts_proxy = NULL; +#if defined(SUPPORT_PROXY) || defined(SUPPORT_SOCKS) || defined(EXPERIMENTAL_XCLIENT) uschar *proxy_external_address = NULL; int proxy_external_port = 0; uschar *proxy_local_address = NULL; @@ -1660,5 +1666,4 @@ int warning_count = 0; const uschar *warnmsg_delay = NULL; const uschar *warnmsg_recipients = NULL; - /* End of globals.c */ diff --git a/src/src/globals.h b/src/src/globals.h index e216b9208..3a5513382 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -661,12 +661,16 @@ extern uschar *host_lookup_order; /* Order of host lookup types */ extern uschar *host_lookup_msg; /* Text for why it failed */ extern int host_number; /* For sharing spools */ extern uschar *host_number_string; /* For expanding */ -extern uschar *hosts_require_helo; /* check for HELO/EHLO before MAIL */ extern uschar *host_reject_connection; /* Reject these hosts */ -extern tree_node *hostlist_anchor; /* Tree of defined host lists */ -extern int hostlist_count; /* Number defined */ extern uschar *hosts_connection_nolog; /* Limits the logging option */ +extern uschar *hosts_require_helo; /* check for HELO/EHLO before MAIL */ extern uschar *hosts_treat_as_local; /* For routing */ +#ifdef EXPERIMENTAL_XCLIENT +extern uschar *hosts_xclient; /* Allow XCLIENT command for specified hosts */ +#endif +extern tree_node *hostlist_anchor; /* Tree of defined host lists */ +extern int hostlist_count; /* Number defined */ + extern int ignore_bounce_errors_after; /* Keep them for this time. */ extern BOOL ignore_fromline_local; /* Local SMTP ignore fromline */ @@ -828,7 +832,8 @@ extern int proxy_external_port; /* Port on remote interface of proxy */ extern uschar *proxy_local_address; /* IP of local interface of proxy */ extern int proxy_local_port; /* Port on local interface of proxy */ extern int proxy_protocol_timeout; /* Timeout for proxy negotiation */ -extern BOOL proxy_session; /* TRUE if receiving mail from valid proxy */ +extern BOOL proxy_session; /* TRUE if receiving mail from valid proxy + or sending via one */ #endif extern uschar *prvscheck_address; /* Set during prvscheck expansion item */ diff --git a/src/src/host.c b/src/src/host.c index 8d53eb3de..136ee8953 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -824,9 +824,9 @@ Returns: pointer to character string */ uschar * -host_ntoa(int type, const void *arg, uschar *buffer, int *portptr) +host_ntoa(int type, const void * arg, uschar * buffer, int * portptr) { -uschar *yield; +uschar * yield; /* The new world. It is annoying that we have to fish out the address from different places in the block, depending on what kind of address it is. It diff --git a/src/src/macro_predef.c b/src/src/macro_predef.c index 0053cb245..8fade68ca 100644 --- a/src/src/macro_predef.c +++ b/src/src/macro_predef.c @@ -205,6 +205,9 @@ due to conflicts with other common macros. */ #ifndef DISABLE_TLS_RESUME builtin_macro_create(US"_HAVE_TLS_RESUME"); #endif +#ifdef EXPERIMENTAL_XCLIENT + builtin_macro_create(US"_HAVE_XCLIENT"); +#endif #ifdef LOOKUP_LSEARCH builtin_macro_create(US"_HAVE_LOOKUP_LSEARCH"); diff --git a/src/src/macros.h b/src/src/macros.h index 36ed185ed..c55276332 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -822,7 +822,11 @@ most recent SMTP commands. SCH_NONE is "empty". */ enum { SCH_NONE, SCH_AUTH, SCH_DATA, SCH_BDAT, SCH_EHLO, SCH_ETRN, SCH_EXPN, SCH_HELO, SCH_HELP, SCH_MAIL, SCH_NOOP, SCH_QUIT, SCH_RCPT, SCH_RSET, SCH_STARTTLS, - SCH_VRFY }; + SCH_VRFY, +#ifdef EXPERIMENTAL_XCLIENT + SCH_XCLIENT, +#endif + }; /* Returns from host_find_by{name,dns}() */ diff --git a/src/src/readconf.c b/src/src/readconf.c index 3b26e87d5..7d48f085d 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -187,6 +187,9 @@ static optionlist optionlist_config[] = { #endif { "hosts_require_helo", opt_stringptr, {&hosts_require_helo} }, { "hosts_treat_as_local", opt_stringptr, {&hosts_treat_as_local} }, +#ifdef EXPERIMENTAL_XCLIENT + { "hosts_xclient", opt_stringptr, {&hosts_xclient} }, +#endif #ifdef LOOKUP_IBASE { "ibase_servers", opt_stringptr, {&ibase_servers} }, #endif @@ -399,7 +402,7 @@ static optionlist optionlist_config[] = { { "uucp_from_pattern", opt_stringptr, {&uucp_from_pattern} }, { "uucp_from_sender", opt_stringptr, {&uucp_from_sender} }, { "warn_message_file", opt_stringptr, {&warn_message_file} }, - { "write_rejectlog", opt_bool, {&write_rejectlog} } + { "write_rejectlog", opt_bool, {&write_rejectlog} }, }; #ifndef MACRO_PREDEF diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 7a45772ce..6f4ad9495 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -75,6 +75,9 @@ enum { ETRN_CMD, /* This by analogy with TURN from the RFC */ STARTTLS_CMD, /* Required by the STARTTLS RFC */ TLS_AUTH_CMD, /* auto-command at start of SSL */ +#ifdef EXPERIMENTAL_XCLIENT + XCLIENT_CMD, /* per xlexkiro implementation */ +#endif /* This is a dummy to identify the non-sync commands when pipelining */ @@ -189,14 +192,22 @@ count of non-mail commands and possibly provoke an error. tls_auth is a pseudo-command, never expected in input. It is activated on TLS startup and looks for a tls authenticator. */ -enum { CL_RSET, CL_HELO, CL_EHLO, CL_AUTH, +enum { + CL_RSET = 0, + CL_HELO, + CL_EHLO, + CL_AUTH, #ifndef DISABLE_TLS - CL_STLS, CL_TLAU, + CL_STLS, + CL_TLAU, +#endif +#ifdef EXPERIMENTAL_XCLIENT + CL_XCLI, #endif }; static smtp_cmd_list cmd_list[] = { - /* name len cmd has_arg is_mail_cmd */ + /* name len cmd has_arg is_mail_cmd */ [CL_RSET] = { "rset", sizeof("rset")-1, RSET_CMD, FALSE, FALSE }, /* First */ [CL_HELO] = { "helo", sizeof("helo")-1, HELO_CMD, TRUE, FALSE }, @@ -206,8 +217,9 @@ static smtp_cmd_list cmd_list[] = { [CL_STLS] = { "starttls", sizeof("starttls")-1, STARTTLS_CMD, FALSE, FALSE }, [CL_TLAU] = { "tls_auth", 0, TLS_AUTH_CMD, FALSE, FALSE }, #endif - -/* If you change anything above here, also fix the definitions below. */ +#ifdef EXPERIMENTAL_XCLIENT + [CL_XCLI] = { "xclient", sizeof("xclient")-1, XCLIENT_CMD, TRUE, FALSE }, +#endif { "mail from:", sizeof("mail from:")-1, MAIL_CMD, TRUE, TRUE }, { "rcpt to:", sizeof("rcpt to:")-1, RCPT_CMD, TRUE, TRUE }, @@ -241,6 +253,9 @@ uschar * smtp_names[] = [SCH_RSET] = US"RSET", [SCH_STARTTLS] = US"STARTTLS", [SCH_VRFY] = US"VRFY", +#ifdef EXPERIMENTAL_XCLIENT + [SCH_XCLIENT] = US"XCLIENT", +#endif }; static uschar *protocols_local[] = { @@ -1260,6 +1275,7 @@ return OTHER_CMD; + /************************************************* * Forced closedown of call * *************************************************/ @@ -1774,7 +1790,6 @@ while (done <= 0) bsmtp_transaction_linecount = receive_linecount; break; - /* The MAIL FROM command requires an address as an operand. All we do here is to parse it for syntactic correctness. The form "<>" is a special case which converts into an empty string. The start/end @@ -4178,7 +4193,13 @@ while (done <= 0) fl.tls_advertised = TRUE; } #endif - +#ifdef EXPERIMENTAL_XCLIENT + if (proxy_session || verify_check_host(&hosts_xclient) != FAIL) + { + g = string_catn(g, smtp_code, 3); + g = xclient_smtp_advertise_str(g); + } +#endif #ifndef DISABLE_PRDR /* Per Recipient Data Response, draft by Eric A. Hall extending RFC */ if (prdr_enable) @@ -4244,6 +4265,41 @@ while (done <= 0) toomany = FALSE; break; /* HELO/EHLO */ +#ifdef EXPERIMENTAL_XCLIENT + case XCLIENT_CMD: + { + BOOL fatal = fl.helo_seen; + uschar * errmsg; + int resp; + + HAD(SCH_XCLIENT); + smtp_mailcmd_count++; + + if ((errmsg = xclient_smtp_command(smtp_cmd_data, &resp, &fatal))) + if (fatal) + done = synprot_error(L_smtp_syntax_error, resp, NULL, errmsg); + else + { + smtp_printf("%d %s\r\n", FALSE, resp, errmsg); + log_write(0, LOG_MAIN|LOG_REJECT, "rejected XCLIENT from %s: %s", + host_and_ident(FALSE), errmsg); + } + else + { + fl.helo_seen = FALSE; /* Require another EHLO */ + smtp_code = string_sprintf("%d", resp); + + /*XXX unclear in spec. if this needs to be an ESMTP banner, + nor whether we get the original client's HELO after (or a proxy fake). + We require that we do; the following HELO/EHLO handling will set + sender_helo_name as normal. */ + + smtp_printf("%s XCLIENT success\r\n", FALSE, smtp_code); + } + break; /* XCLIENT */ + } +#endif + /* The MAIL command requires an address as an operand. All we do here is to parse it for syntactic correctness. The form "<>" is @@ -5353,6 +5409,10 @@ while (done <= 0) if (acl_smtp_etrn) smtp_printf(" ETRN", TRUE); if (acl_smtp_expn) smtp_printf(" EXPN", TRUE); if (acl_smtp_vrfy) smtp_printf(" VRFY", TRUE); +#ifdef EXPERIMENTAL_XCLIENT + if (proxy_session || verify_check_host(&hosts_xclient) != FAIL) + smtp_printf(" XCLIENT", TRUE); +#endif smtp_printf("\r\n", FALSE); break; diff --git a/src/src/xclient.c b/src/src/xclient.c new file mode 100644 index 000000000..2a8be9b0e --- /dev/null +++ b/src/src/xclient.c @@ -0,0 +1,299 @@ +/************************************************* +* Exim - an Internet mail transport agent * +*************************************************/ + +/* Copyright (c) The Exim Maintainers 2023 */ +/* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "exim.h" + +#ifdef EXPERIMENTAL_XCLIENT + +/* From https://www.postfix.org/XCLIENT_README.html I infer two generations of +protocol. The more recent one obviates the utility of the HELO attribute, since +it mandates the proxy always sending a HELO/EHLO smtp command following (a +successful) XCLIENT command, and that will carry a NELO name (which we assume, +though it isn't specified, will be the actual one presented to the proxy by the +possibly-new client). The same applies to the PROTO attribute. */ + +# define XCLIENT_V2 + +enum xclient_cmd_e { + XCLIENT_CMD_UNKNOWN, + XCLIENT_CMD_ADDR, + XCLIENT_CMD_NAME, + XCLIENT_CMD_PORT, + XCLIENT_CMD_LOGIN, + XCLIENT_CMD_DESTADDR, + XCLIENT_CMD_DESTPORT, +# ifdef XCLIENT_V1 + XCLIENT_CMD_HELO, + XCLIENT_CMD_PROTO, +# endif +}; + +struct xclient_cmd { + const uschar * str; + unsigned len; +} xclient_cmds[] = { + [XCLIENT_CMD_UNKNOWN] = { NULL }, + [XCLIENT_CMD_ADDR] = { US"ADDR", 4 }, + [XCLIENT_CMD_NAME] = { US"NAME", 4 }, + [XCLIENT_CMD_PORT] = { US"PORT", 4 }, + [XCLIENT_CMD_LOGIN] = { US"LOGIN", 5 }, + [XCLIENT_CMD_DESTADDR] = { US"DESTADDR", 8 }, + [XCLIENT_CMD_DESTPORT] = { US"DESTPORT", 8 }, +# ifdef XCLIENT_V1 + [XCLIENT_CMD_HELO] = { US"HELO", 4 }, + [XCLIENT_CMD_PROTO] = { US"PROTO", 5 }, +# endif +}; + +/************************************************* +* XCLIENT proxy implementation * +*************************************************/ + +/* Arguments: + code points to the coded string + end points to the end of coded string + ptr where to put the pointer to the result, which is in + dynamic store +Returns: the number of bytes in the result, excluding the final zero; + -1 if the input is malformed +*/ + +static int +xclient_xtextdecode(uschar * code, uschar * end, uschar ** ptr) +{ +return auth_xtextdecode(string_copyn(code, end-code), ptr); +} + +/************************************************* +* Check XCLIENT line and set sender_address * +*************************************************/ + + +/* Check the format of a XCLIENT line. +Arguments: + s the data portion of the line (already past any white space) + resp result: smtp respose code + flag input: helo seen output: fail is fatal + +Return: NULL on success, or error message +*/ + +# define XCLIENT_UNAVAIL US"[UNAVAILABLE]" +# define XCLIENT_TEMPUNAVAIL US"[TEMPUNAVAIL]" + +uschar * +xclient_smtp_command(uschar * s, int * resp, BOOL * flag) +{ +uschar * word = s; +enum { + XCLIENT_READ_COMMAND = 0, + XCLIENT_READ_VALUE, + XCLIENT_SKIP_SPACES +} state = XCLIENT_SKIP_SPACES; +enum xclient_cmd_e cmd; + +if ( !flag + && verify_check_host(&hosts_require_helo) == OK) + { + *resp = 503; + *flag = FALSE; + return US"no HELO/EHLO given"; + } + +/* If already in a proxy session, do not re-check permission. +Strictly we should avoid doing this for a Proxy-Protocol +session to avoid mixups. */ + +if(!proxy_session && verify_check_host(&hosts_xclient) == FAIL) + { + *resp = 550; + *flag = TRUE; + return US"XCLIENT command used when not advertised"; + } + +if (sender_address) + { + *resp = 503; + *flag = FALSE; + return US"mail transaction in progress"; + } + +if (!*word) + { + s = US"XCLIENT must have at least one operand"; + goto fatal_501; + } + +for (state = XCLIENT_SKIP_SPACES; *s; ) + switch (state) + { + case XCLIENT_READ_COMMAND: + { + int len; + + word = s; + while (*s && *s != '=') s++; + len = s - word; + if (!*s) + { + s = string_sprintf("XCLIENT: missing value for parameter '%.*s'", + len, word); + goto fatal_501; + } + + DEBUG(D_transport) debug_printf(" XCLIENT: cmd %.*s\n", len, word); + cmd = XCLIENT_CMD_UNKNOWN; + for (struct xclient_cmd * x = xclient_cmds + 1; + x < xclient_cmds + nelem(xclient_cmds); x++) + if (len == x->len && strncmpic(word, x->str, len) == 0) + { + cmd = x - xclient_cmds; + break; + } + if (cmd == XCLIENT_CMD_UNKNOWN) + { + s = string_sprintf("XCLIENT: unrecognised parameter '%.*s'", + len, word); + goto fatal_501; + } + state = XCLIENT_READ_VALUE; + } + break; + + case XCLIENT_READ_VALUE: + { + int old_pool = store_pool; + int len; + uschar * val; + + word = ++s; /* skip the = */ + while (*s && !isspace(*s)) s++; + len = s - word; + + DEBUG(D_transport) debug_printf(" XCLIENT: \tvalue %.*s\n", len, word); + if (len == 0) + { s = US"XCLIENT: zero-length value for param"; goto fatal_501; } + + if ( len == 13 + && ( strncmpic(word, XCLIENT_UNAVAIL, 13) == 0 + || strncmpic(word, XCLIENT_TEMPUNAVAIL, 13) == 0 + ) ) + val = NULL; + + else if ((len = xclient_xtextdecode(word, s, &val)) == -1) + { + s = string_sprintf("failed xtext decode for XCLIENT: '%.*s'", len, word); + goto fatal_501; + } + + store_pool = POOL_PERM; + switch (cmd) + { + case XCLIENT_CMD_ADDR: + proxy_local_address = sender_host_address; + sender_host_address = val ? string_copyn(val, len) : NULL; + break; + case XCLIENT_CMD_NAME: + sender_host_name = val ? string_copyn(val, len) : NULL; + break; + case XCLIENT_CMD_PORT: + proxy_local_port = sender_host_port; + sender_host_port = val ? Uatoi(val) : 0; + break; + case XCLIENT_CMD_DESTADDR: + proxy_external_address = val ? string_copyn(val, len) : NULL; + break; + case XCLIENT_CMD_DESTPORT: + proxy_external_port = val ? Uatoi(val) : 0; + break; + + case XCLIENT_CMD_LOGIN: + if (val) + { + authenticated_id = string_copyn(val, len); + sender_host_authenticated = US"xclient"; + authentication_failed = FALSE; + } + else + { + authenticated_id = NULL; + sender_host_authenticated = NULL; + } + break; + +# ifdef XCLIENT_V1 + case XCLIENT_CMD_HELO: + sender_helo_name = val ? string_copyn(val, len) : NULL; + break; + case XCLIENT_CMD_PROTO: + if (!val) + { store_pool = old_pool; s = US"missing proto for XCLIENT"; goto fatal_501; } + else if (len == 4 && strncmpic(val, US"SMTP", 4) == 0) + *esmtpflag = FALSE; /* function arg */ + else if (len == 5 && strncmpic(val, US"ESMTP", 5) == 0) + *esmtpflag = TRUE; + else + { store_pool = old_pool; s = US"bad proto for XCLIENT"; goto fatal_501; } + break; +# endif + } + store_pool = old_pool; + state = XCLIENT_SKIP_SPACES; + break; + } + + case XCLIENT_SKIP_SPACES: + while (*s && isspace (*s)) s++; + state = XCLIENT_READ_COMMAND; + break; + + default: + s = US"unhandled XCLIENT parameter type"; + goto fatal_501; + } + +if (!proxy_local_address) + { s = US"missing ADDR for XCLIENT"; goto fatal_501; } +if (!proxy_local_port) + { s = US"missing PORT for XCLIENT"; goto fatal_501; } +if (state != XCLIENT_SKIP_SPACES) + { s = US"bad state parsing XCLIENT parameters"; goto fatal_501; } + +host_build_sender_fullhost(); +proxy_session = TRUE; +*resp = 220; +return NULL; + +fatal_501: + *flag = TRUE; + *resp = 501; + return s; +} + +# undef XCLIENT_UNAVAIL +# undef XCLIENT_TEMPUNAVAIL + + +gstring * +xclient_smtp_advertise_str(gstring * g) +{ +g = string_catn(g, US"-XCLIENT ", 8); +for (int i = 1; i < nelem(xclient_cmds); i++) + { + g = string_catn(g, US" ", 1); + g = string_cat(g, xclient_cmds[i].str); + } +return string_catn(g, US"\r\n", 2); +} + + +#endif /*EXPERIMENTAL_XCLIENT*/ + +/* vi: aw ai sw=2 +*/ +/* End of xclient.c */ commit a31b8b2c91b548cc11f9e482315beb2373211b5e Author: Jeremy Harris Date: Sun Mar 26 16:17:16 2023 +0100 Make $router_name usable from transport diff --git a/src/src/deliver.c b/src/src/deliver.c index f3a406990..ad045c8cc 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -2371,7 +2371,9 @@ if ((pid = exim_fork(US"delivery-local")) == 0) addr->local_part, tp->name); /* Setting these globals in the subprocess means we need never clear them */ - transport_name = addr->transport->name; + + transport_name = tp->name; + if (addr->router) router_name = addr->router->name; driver_srcfile = tp->srcfile; driver_srcline = tp->srcline; @@ -4663,7 +4665,9 @@ all pipes, so I do not see a reason to use non-blocking IO here host_item *h; /* Setting these globals in the subprocess means we need never clear them */ - transport_name = addr->transport->name; + + transport_name = tp->name; + if (addr->router) router_name = addr->router->name; driver_srcfile = tp->srcfile; driver_srcline = tp->srcline; commit e093f239f9d0c6e5f590d9979bcbc3e60c540edf Author: Jeremy Harris Date: Wed Mar 29 13:14:14 2023 +0100 tidying diff --git a/src/src/EDITME b/src/src/EDITME index 6344561d6..ac323fe18 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -634,6 +634,9 @@ DISABLE_MAL_MKS=yes # Uncomment the following line to add queuefile transport support # EXPERIMENTAL_QUEUEFILE=yes +# +# Uncomment the following line to add XCLIENT support +# EXPERIMENTAL_XCLIENT=yes ############################################################################### # THESE ARE THINGS YOU MIGHT WANT TO SPECIFY # commit d5939cf05037d4a70ca43ec4d436c2e699530444 Author: Jeremy Harris Date: Sat Apr 1 19:48:15 2023 +0100 Docs: options after "run" of ${run } may not have space. Bug 2932 diff --git a/src/src/expand.c b/src/src/expand.c index 6dcd45062..9f80439cb 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -5630,7 +5630,6 @@ while (*s) /* Handle options to the "run" */ while (*s == ',') - { if (Ustrncmp(++s, "preexpand", 9) == 0) { late_expand = FALSE; s += 9; } else @@ -5641,7 +5640,6 @@ while (*s) (int)(t-s), s); goto EXPAND_FAILED; } - } Uskip_whitespace(&s); if (*s != '{') /*}*/ commit 8d960c19a447e105a4375c2cbcd0c9493622b6a2 Author: Jeremy Harris Date: Sat Apr 1 21:15:24 2023 +0100 Cutthrough: fix error message for unexpected response from onward connection. Bug 2912 Only affects debug. diff --git a/src/src/verify.c b/src/src/verify.c index 5c0a3e408..228f63020 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -3,7 +3,7 @@ *************************************************/ /* Copyright (c) The Exim Maintainers 2020 - 2022 */ -/* Copyright (c) University of Cambridge 1995 - 2018 */ +/* Copyright (c) University of Cambridge 1995 - 2023 */ /* See the file NOTICE for conditions of use and distribution. */ /* SPDX-License-Identifier: GPL-2.0-or-later */ @@ -1329,7 +1329,13 @@ cutthrough_data_puts(US"\r\n", 2); } -/* Get and check response from cutthrough target */ +/* Get and check response from cutthrough target. +Used for +- nonfirst RCPT +- predata +- data finaldot +- cutthrough conn close +*/ static uschar cutthrough_response(client_conn_ctx * cctx, char expect, uschar ** copy, int timeout) { @@ -1343,7 +1349,7 @@ sx.inblock.ptr = inbuffer; sx.inblock.ptrend = inbuffer; sx.inblock.cctx = cctx; if(!smtp_read_response(&sx, responsebuffer, sizeof(responsebuffer), expect, timeout)) - cancel_cutthrough_connection(TRUE, US"target timeout on read"); + cancel_cutthrough_connection(TRUE, US"unexpected response to smtp command"); if(copy) { commit f1bf269876f4e32b074d271168edc2af64e1c7a6 Author: Jeremy Harris Date: Sat Apr 1 21:47:20 2023 +0100 Expansions: Fix ${readsocket } to do nicer TLS close diff --git a/src/src/lookups/readsock.c b/src/src/lookups/readsock.c index a3f87108a..73cc02813 100644 --- a/src/src/lookups/readsock.c +++ b/src/src/lookups/readsock.c @@ -275,6 +275,10 @@ if (!lf.cache) *do_cache = 0; out: +#ifndef DISABLE_TLS +if (cctx->tls_ctx) tls_close(cctx->tls_ctx, TLS_SHUTDOWN_NOWAIT); +#endif + (void) close(cctx->sock); cctx->sock = -1; return ret; @@ -294,7 +298,7 @@ readsock_close(void * handle) client_conn_ctx * cctx = handle; if (cctx->sock < 0) return; #ifndef DISABLE_TLS -if (cctx->tls_ctx) tls_close(cctx->tls_ctx, TRUE); +if (cctx->tls_ctx) tls_close(cctx->tls_ctx, TLS_SHUTDOWN_NOWAIT); #endif close(cctx->sock); cctx->sock = -1; commit 12e1cfcb1649e2ea213c2965adf8479f1cff06f7 Author: Jeremy Harris Date: Sun Apr 2 22:08:40 2023 +0100 tidying diff --git a/src/src/daemon.c b/src/src/daemon.c index caed44bb3..8ceeac3a6 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -1691,7 +1691,6 @@ int listen_socket_count = 0, poll_fd_count; ip_address_item * addresses = NULL; time_t last_connection_time = (time_t)0; int local_queue_run_max = 0; -BOOL queue_run_max_has_dollar; if (is_multiple_qrun()) diff --git a/src/src/expand.c b/src/src/expand.c index 9f80439cb..fe0fd1469 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -27,13 +27,6 @@ typedef unsigned esi_flags; # ifndef SUPPORT_CRYPTEQ # define SUPPORT_CRYPTEQ # endif -#else - -/* Recursively called function */ - -static uschar *expand_string_internal(const uschar *, esi_flags, const uschar **, BOOL *, BOOL *); -static int_eximarith_t expanded_string_integer(const uschar *, BOOL); - #endif /*!STAND_ALONE*/ #ifdef LOOKUP_LDAP @@ -942,6 +935,10 @@ static uschar *mtable_sticky[] = #define FH_WANT_RAW BIT(1) #define FH_WANT_LIST BIT(2) +/* Recursively called function */ +static uschar *expand_string_internal(const uschar *, esi_flags, const uschar **, BOOL *, BOOL *); +static int_eximarith_t expanded_string_integer(const uschar *, BOOL); + /************************************************* * Tables for UTF-8 support * diff --git a/src/src/regex_cache.c b/src/src/regex_cache.c index 1ca3c96d5..91ca8ca02 100644 --- a/src/src/regex_cache.c +++ b/src/src/regex_cache.c @@ -92,11 +92,9 @@ return node ? node->data.ptr : NULL; static void regex_to_cache(const uschar * key, BOOL caseless, const pcre2_code * cre) { -PCRE2_SIZE srelen; -uschar * sre; -tree_node * node; -node = store_get(sizeof(tree_node) + Ustrlen(key) + 1, key); /* we are called with STORE_PERM */ +/* we are called with STORE_PERM */ +tree_node * node = store_get(sizeof(tree_node) + Ustrlen(key) + 1, key); Ustrcpy(node->name, key); node->data.ptr = (void *)cre; commit 51f9c07cd341c9c1a09b3816df988c6f44477c99 Author: Jeremy Harris Date: Tue Apr 11 11:59:08 2023 +0100 Fix ${srs_encode ..} for mod-1024 day zero diff --git a/src/src/expand.c b/src/src/expand.c index fe0fd1469..26df25795 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -3523,7 +3523,7 @@ switch(cond_type = identify_operator(&s, &opname)) /* Match the given local_part against the SRS-encoded pattern */ - re = regex_must_compile(US"^(?i)SRS0=([^=]+)=([A-Z2-7]+)=([^=]*)=(.*)$", + re = regex_must_compile(US"^(?i)SRS0=([^=]+)=([A-Z2-7]{2})=([^=]*)=(.*)$", MCS_CASELESS | MCS_CACHEABLE, FALSE); md = pcre2_match_data_create(4+1, pcre_gen_ctx); if (pcre2_match(re, sub[0], PCRE2_ZERO_TERMINATED, 0, PCRE_EOPT, @@ -7061,13 +7061,11 @@ while (*s) { struct timeval now; unsigned long i; - gstring * h = NULL; gettimeofday(&now, NULL); - for (unsigned long i = (now.tv_sec / 86400) & 0x3ff; i; i >>= 5) - h = string_catn(h, &base32_chars[i & 0x1f], 1); - if (h) while (h->ptr > 0) - g = string_catn(g, &h->s[--h->ptr], 1); + i = (now.tv_sec / 86400) & 0x3ff; + g = string_catn(g, &base32_chars[i >> 5], 1); + g = string_catn(g, &base32_chars[i & 0x1f], 1); } g = string_catn(g, US"=", 1); commit 37688315a566d2bfaeae040ee1cbaae3102efced Author: Jeremy Harris Date: Sat Apr 22 00:21:25 2023 +0100 Docs: clarify TLS cert name verification diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index f3f70d2e0..76176a64e 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -2620,7 +2620,7 @@ else ) { DEBUG(D_tls) - debug_printf("TLS certificate verification failed: cert name mismatch\n"); + debug_printf("TLS certificate verification failed: cert name mismatch (per GnuTLS)\n"); if (state->verify_requirement >= VERIFY_REQUIRED) goto badcert; return TRUE; diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 9d0ab2fdf..cd715cc18 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -1192,6 +1192,8 @@ else uschar * name; int rc; while ((name = string_nextinlist(&list, &sep, NULL, 0))) + { + DEBUG(D_tls|D_lookup) debug_printf_indent("%s suitable for cert, per OpenSSL?", name); if ((rc = X509_check_host(cert, CCS name, 0, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS | X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS, @@ -1203,8 +1205,11 @@ else tlsp == &tls_out ? deliver_host_address : sender_host_address); name = NULL; } + DEBUG(D_tls|D_lookup) debug_printf_indent(" yes\n"); break; } + else DEBUG(D_tls|D_lookup) debug_printf_indent(" no\n"); + } if (!name) #else if (!tls_is_name_for_cert(verify_cert_hostnames, cert)) diff --git a/src/src/tls.c b/src/src/tls.c index 825313a9a..8f4344c6c 100644 --- a/src/src/tls.c +++ b/src/src/tls.c @@ -670,21 +670,24 @@ Returns: BOOL tls_is_name_for_cert(const uschar * namelist, void * cert) { -uschar * altnames = tls_cert_subject_altname(cert, US"dns"); -uschar * subjdn; -uschar * certname; +uschar * altnames, * subjdn, * certname, * cmpname; int cmp_sep = 0; -uschar * cmpname; if ((altnames = tls_cert_subject_altname(cert, US"dns"))) { int alt_sep = '\n'; + DEBUG(D_tls|D_lookup) debug_printf_indent("cert has SAN\n"); while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0))) { const uschar * an = altnames; + DEBUG(D_tls|D_lookup) debug_printf_indent(" %s in SANs?", cmpname); while ((certname = string_nextinlist(&an, &alt_sep, NULL, 0))) if (is_name_match(cmpname, certname)) + { + DEBUG(D_tls|D_lookup) debug_printf_indent(" yes (matched %s)\n", certname); return TRUE; + } + DEBUG(D_tls|D_lookup) debug_printf_indent(" no (end of SAN list)\n"); } } @@ -696,13 +699,18 @@ else if ((subjdn = tls_cert_subject(cert, NULL))) while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0))) { const uschar * sn = subjdn; + DEBUG(D_tls|D_lookup) debug_printf_indent(" %s in SN?", cmpname); while ((certname = string_nextinlist(&sn, &sn_sep, NULL, 0))) if ( *certname++ == 'C' && *certname++ == 'N' && *certname++ == '=' && is_name_match(cmpname, certname) ) + { + DEBUG(D_tls|D_lookup) debug_printf_indent(" yes (matched %s)\n", certname); return TRUE; + } + DEBUG(D_tls|D_lookup) debug_printf_indent(" no (end of CN)\n"); } } return FALSE; commit 7533e17a427d6ae51bba9af028b0d9496f487caf Author: Jeremy Harris Date: Sun Apr 23 22:24:30 2023 +0100 tidying diff --git a/src/src/daemon.c b/src/src/daemon.c index 8ceeac3a6..ea7db0f25 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -564,7 +564,7 @@ if (pid == 0) smtp_log_no_mail(); /* Log no mail if configured */ exim_underbar_exit(EXIT_SUCCESS); } - if (message_id[0] == 0) continue; /* No message was accepted */ + if (!message_id[0]) continue; /* No message was accepted */ } else /* bad smtp_setup_msg() */ { diff --git a/src/src/filter.c b/src/src/filter.c index d878acb8f..fc6970f23 100644 --- a/src/src/filter.c +++ b/src/src/filter.c @@ -1463,22 +1463,21 @@ switch (c->type) testing and verification. */ case cond_errormsg: - yield = message_id[0] != 0 && - (sender_address == NULL || sender_address[0] == 0); + yield = message_id[0] && (!sender_address || !*sender_address); break; /* Only FALSE if a message is actually being processed; TRUE for address and filter testing and verification. */ case cond_firsttime: - yield = filter_test != FTEST_NONE || message_id[0] == 0 || f.deliver_firsttime; + yield = filter_test != FTEST_NONE || !message_id[0] || f.deliver_firsttime; break; /* Only TRUE if a message is actually being processed; FALSE for address testing and verification. */ case cond_manualthaw: - yield = message_id[0] != 0 && f.deliver_manual_thaw; + yield = message_id[0] && f.deliver_manual_thaw; break; /* The foranyaddress condition loops through a list of addresses */ diff --git a/src/src/host.c b/src/src/host.c index 136ee8953..9c66e9aac 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -2078,11 +2078,11 @@ so we pass that back. */ if (!host->address) { uschar *msg = - #ifndef STAND_ALONE +#ifndef STAND_ALONE !message_id[0] && smtp_in ? string_sprintf("no IP address found for host %s (during %s)", host->name, smtp_get_connection_info()) : - #endif +#endif string_sprintf("no IP address found for host %s", host->name); HDEBUG(D_host_lookup) debug_printf("%s\n", msg); diff --git a/src/src/log.c b/src/src/log.c index 08ece6158..54d2b8027 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -996,7 +996,7 @@ if (LOGGING(pid)) if (!syslog_pid) pid_position[1] = g->ptr; /* … and end+1 of the PID */ } -if (f.really_exim && message_id[0] != 0) +if (f.really_exim && message_id[0]) g = string_fmt_append(g, "%s ", message_id); if (flags & LOG_CONFIG) diff --git a/src/src/receive.c b/src/src/receive.c index 94fa6d5de..19f8962c6 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -3143,9 +3143,8 @@ if (cutthrough.cctx.sock >= 0 && cutthrough.delivery) sender_address, sender_fullhost ? "H=" : "", sender_fullhost ? sender_fullhost : US"", sender_ident ? "U=" : "", sender_ident ? sender_ident : US""); - message_id[0] = 0; /* Indicate no message accepted */ smtp_reply = US"550 Too many \"Received\" headers - suspected mail loop"; - goto TIDYUP; /* Skip to end of function */ + goto NOT_ACCEPTED; /* Skip to end of function */ } received_header_gen(); add_acl_headers(ACL_WHERE_RCPT, US"MAIL or RCPT"); @@ -3242,12 +3241,12 @@ if (!ferror(spool_data_file) && !(receive_feof)() && message_ended != END_DOT) case END_EOF: if (smtp_input) { - Uunlink(spool_name); /* Lose data file when closed */ + Uunlink(spool_name); /* Lose data file when closed */ cancel_cutthrough_connection(TRUE, US"sender closed connection"); - message_id[0] = 0; /* Indicate no message accepted */ + message_id[0] = 0; /* Indicate no message_accepted */ smtp_reply = handle_lost_connection(US""); smtp_yield = FALSE; - goto TIDYUP; /* Skip to end of function */ + goto TIDYUP; /* Skip to end of function */ } break; @@ -3272,8 +3271,7 @@ if (!ferror(spool_data_file) && !(receive_feof)() && message_ended != END_DOT) if (smtp_input) { smtp_reply = US"552 Message size exceeds maximum permitted"; - message_id[0] = 0; /* Indicate no message accepted */ - goto TIDYUP; /* Skip to end of function */ + goto NOT_ACCEPTED; /* Skip to end of function */ } else { @@ -3291,8 +3289,7 @@ if (!ferror(spool_data_file) && !(receive_feof)() && message_ended != END_DOT) Uunlink(spool_name); /* Lose the data file when closed */ cancel_cutthrough_connection(TRUE, US"sender protocol error"); smtp_reply = US""; /* Response already sent */ - message_id[0] = 0; /* Indicate no message accepted */ - goto TIDYUP; /* Skip to end of function */ + goto NOT_ACCEPTED; /* Skip to end of function */ } } @@ -3333,8 +3330,7 @@ if (fflush(spool_data_file) == EOF || ferror(spool_data_file) || smtp_reply = US"451 Error while writing spool file"; receive_swallow_smtp(); } - message_id[0] = 0; /* Indicate no message accepted */ - goto TIDYUP; /* Skip to end of function */ + goto NOT_ACCEPTED; /* Skip to end of function */ } else @@ -3567,8 +3563,7 @@ else if (smtp_handle_acl_fail(ACL_WHERE_DKIM, rc, user_msg, log_msg) != 0) smtp_yield = FALSE; /* No more messages after dropped connection */ smtp_reply = US""; /* Indicate reply already sent */ - message_id[0] = 0; /* Indicate no message accepted */ - goto TIDYUP; /* Skip to end of function */ + goto NOT_ACCEPTED; /* Skip to end of function */ } } else @@ -3649,10 +3644,7 @@ else ? US"accepted" : US"accepted for some recipients"); if (recipients_count == 0) - { - message_id[0] = 0; /* Indicate no message accepted */ - goto TIDYUP; - } + goto NOT_ACCEPTED; } else prdr_requested = FALSE; @@ -3686,8 +3678,7 @@ else if (smtp_handle_acl_fail(ACL_WHERE_DATA, rc, user_msg, log_msg) != 0) smtp_yield = FALSE; /* No more messages after dropped connection */ smtp_reply = US""; /* Indicate reply already sent */ - message_id[0] = 0; /* Indicate no message accepted */ - goto TIDYUP; /* Skip to end of function */ + goto NOT_ACCEPTED; /* Skip to end of function */ } } } @@ -3925,9 +3916,8 @@ else if (!smtp_batched_input) { smtp_respond(smtp_code, 3, TRUE, errmsg); - message_id[0] = 0; /* Indicate no message accepted */ smtp_reply = US""; /* Indicate reply already sent */ - goto TIDYUP; /* Skip to end of function */ + goto NOT_ACCEPTED; /* Skip to end of function */ } else moan_smtp_batch(NULL, "%s %s", smtp_code, errmsg); @@ -4004,8 +3994,7 @@ else if (smtp_input) { smtp_reply = US"451 Error in writing spool file"; - message_id[0] = 0; /* Indicate no message accepted */ - goto TIDYUP; + goto NOT_ACCEPTED; } else { @@ -4035,8 +4024,7 @@ if (fflush(spool_data_file)) if (smtp_input) { smtp_reply = US"451 Error in writing spool file"; - message_id[0] = 0; /* Indicate no message accepted */ - goto TIDYUP; + goto NOT_ACCEPTED; } else { @@ -4386,6 +4374,11 @@ if this happens? We can at least log it; if it is observed on some platform then we can think about properly declaring the message not-received. */ +goto TIDYUP; + +NOT_ACCEPTED: +message_id[0] = 0; /* Indicate no message accepted */ + TIDYUP: process_info[process_info_len] = 0; /* Remove message id */ if (spool_data_file && cutthrough_done == NOT_TRIED) diff --git a/src/src/routers/rf_self_action.c b/src/src/routers/rf_self_action.c index 73d07db40..e5da4cb91 100644 --- a/src/src/routers/rf_self_action.c +++ b/src/src/routers/rf_self_action.c @@ -74,7 +74,7 @@ switch (code) and where it has come from. Otherwise, during message delivery, the normal logging for the address will be sufficient. */ - if (message_id[0] == 0) + if (!message_id[0]) if (sender_fullhost) log_write(0, LOG_MAIN, "%s: %s (while verifying <%s> from host %s)", msg, addr->domain, addr->address, sender_fullhost); commit a8786a66feb3c003c74551399b345b1634cc6739 Author: Jeremy Harris Date: Thu May 4 15:41:46 2023 +0100 Fix variable initialisation in smtp transport. Bug 2996 diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index c5951832b..c72028ce9 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -4973,7 +4973,7 @@ smtp_transport_closedown(transport_instance *tblock) { smtp_transport_options_block * ob = SOB tblock->options_block; client_conn_ctx cctx; -smtp_context sx; +smtp_context sx = {0}; uschar buffer[256]; uschar inbuffer[4096]; uschar outbuffer[16]; commit 138860785cc96f077f052043d46f697abbf87947 Author: Heiko Schlittermann (HS12-RIPE) Date: Tue May 9 16:49:30 2023 +0200 fix the list URL commit 4b8cdd1571a4635ff2951306102dc22f7c8af7f3 Author: Heiko Schlittermann (HS12-RIPE) Date: Tue Jun 6 20:03:53 2023 +0200 fix typo in debug output diff --git a/src/src/expand.c b/src/src/expand.c index 26df25795..3428179d8 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -5382,18 +5382,18 @@ while (*s) if (iexpire >= inow) { prvscheck_result = US"1"; - DEBUG(D_expand) debug_printf_indent("prvscheck: success, $pvrs_result set to 1\n"); + DEBUG(D_expand) debug_printf_indent("prvscheck: success, $prvscheck_result set to 1\n"); } else { prvscheck_result = NULL; - DEBUG(D_expand) debug_printf_indent("prvscheck: signature expired, $pvrs_result unset\n"); + DEBUG(D_expand) debug_printf_indent("prvscheck: signature expired, $prvscheck_result unset\n"); } } else { prvscheck_result = NULL; - DEBUG(D_expand) debug_printf_indent("prvscheck: hash failure, $pvrs_result unset\n"); + DEBUG(D_expand) debug_printf_indent("prvscheck: hash failure, $prvscheck_result unset\n"); } /* Now expand the final argument. We leave this till now so that commit 821be597760473fb60a45c3ad2b2659987b376cf Author: Jeremy Harris Date: Fri Jun 3 15:34:03 2022 +0100 SRS: fix mismerge Broken-by: 9f1a75f126 diff --git a/src/src/expand.c b/src/src/expand.c index 3428179d8..45172b1ed 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -7047,6 +7047,7 @@ while (*s) case 2: case 3: goto EXPAND_FAILED; } + if (flags & ESI_SKIPPING) continue; if (sub[1] && *(sub[1])) { commit 3b6774c818ba09749c2168cd0705c18d01b572ec Author: Jeremy Harris Date: Sat Jun 10 18:18:01 2023 +0100 DMARC: use growable-strings diff --git a/src/src/acl.c b/src/src/acl.c index 17d6c68da..ab991ef41 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -3773,8 +3773,9 @@ for (; cb; cb = cb->next) if (!f.dmarc_has_been_checked) dmarc_process(); f.dmarc_has_been_checked = TRUE; + /* used long way of dmarc_exim_expand_query() in case we need more - * view into the process in the future. */ + view into the process in the future. */ rc = match_isinlist(dmarc_exim_expand_query(DMARC_VERIFY_STATUS), &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL); break; diff --git a/src/src/dmarc.c b/src/src/dmarc.c index 118720750..555e3a72b 100644 --- a/src/src/dmarc.c +++ b/src/src/dmarc.c @@ -246,8 +246,8 @@ dmarc_write_history_file() int history_file_fd; ssize_t written_len; int tmp_ans; -u_char **rua; /* aggregate report addressees */ -uschar *history_buffer = NULL; +u_char ** rua; /* aggregate report addressees */ +gstring * g; if (!dmarc_history_file) { @@ -264,43 +264,40 @@ if (history_file_fd < 0) } /* Generate the contents of the history file */ -history_buffer = string_sprintf( +g = string_fmt_append(NULL, "job %s\nreporter %s\nreceived %ld\nipaddr %s\nfrom %s\nmfrom %s\n", message_id, primary_hostname, time(NULL), sender_host_address, header_from_sender, expand_string(US"$sender_address_domain")); if (spf_response) - history_buffer = string_sprintf("%sspf %d\n", history_buffer, dmarc_spf_ares_result); - /* history_buffer = string_sprintf("%sspf -1\n", history_buffer); */ + g = string_fmt_append(s, "spf %d\n", dmarc_spf_ares_result); -history_buffer = string_sprintf( - "%s%spdomain %s\npolicy %d\n", - history_buffer, dkim_history_buffer, dmarc_used_domain, dmarc_policy); +g = string_fmt_append(g, "%spdomain %s\npolicy %d\n", + dkim_history_buffer, dmarc_used_domain, dmarc_policy); if ((rua = opendmarc_policy_fetch_rua(dmarc_pctx, NULL, 0, 1))) for (tmp_ans = 0; rua[tmp_ans]; tmp_ans++) - history_buffer = string_sprintf("%srua %s\n", history_buffer, rua[tmp_ans]); + g = string_fmd_append(g, "rua %s\n", rua[tmp_ans]); else - history_buffer = string_sprintf("%srua -\n", history_buffer); + g = string_fmtappend(g, "rua -\n"); opendmarc_policy_fetch_pct(dmarc_pctx, &tmp_ans); -history_buffer = string_sprintf("%spct %d\n", history_buffer, tmp_ans); +g = atring_fmt_append(g, "pct %d\n", tmp_ans); opendmarc_policy_fetch_adkim(dmarc_pctx, &tmp_ans); -history_buffer = string_sprintf("%sadkim %d\n", history_buffer, tmp_ans); +g = atring_fmt_append(g, "adkim %d\n", tmp_ans); opendmarc_policy_fetch_aspf(dmarc_pctx, &tmp_ans); -history_buffer = string_sprintf("%saspf %d\n", history_buffer, tmp_ans); +g = atring_fmt_append(g, "aspf %d\n", tmp_ans); opendmarc_policy_fetch_p(dmarc_pctx, &tmp_ans); -history_buffer = string_sprintf("%sp %d\n", history_buffer, tmp_ans); +g = atring_fmt_append(g, "p %d\n", tmp_ans); opendmarc_policy_fetch_sp(dmarc_pctx, &tmp_ans); -history_buffer = string_sprintf("%ssp %d\n", history_buffer, tmp_ans); +g = atring_fmt_append(g, "sp %d\n", tmp_ans); -history_buffer = string_sprintf( - "%salign_dkim %d\nalign_spf %d\naction %d\n", - history_buffer, da, sa, action); +g = atring_fmt_append(g, "align_dkim %d\nalign_spf %d\naction %d\n", + da, sa, action); /* Write the contents to the history file */ DEBUG(D_receive) @@ -309,13 +306,13 @@ DEBUG(D_receive) if (host_checking || f.running_in_test_harness) { DEBUG(D_receive) - debug_printf("DMARC history data for debugging:\n%s", history_buffer); + debug_printf("DMARC history data for debugging:\n%s", string_from_gstring(g)); } else { written_len = write_to_fd_buf(history_file_fd, - history_buffer, - Ustrlen(history_buffer)); + g->s, + gstring_length(g)); if (written_len == 0) { log_write(0, LOG_MAIN|LOG_PANIC, "failure to write to DMARC history file: %s", @@ -341,18 +338,18 @@ int tmp_ans, c; pdkim_signature * sig = dkim_signatures; uschar * rr; BOOL has_dmarc_record = TRUE; -u_char **ruf; /* forensic report addressees, if called for */ +u_char ** ruf; /* forensic report addressees, if called for */ /* ACLs have "control=dmarc_disable_verify" */ if (f.dmarc_disable_verify) return OK; /* Store the header From: sender domain for this part of DMARC. - * If there is no from_header struct, then it's likely this message - * is locally generated and relying on fixups to add it. Just skip - * the entire DMARC system if we can't find a From: header....or if - * there was a previous error. - */ +If there is no from_header struct, then it's likely this message +is locally generated and relying on fixups to add it. Just skip +the entire DMARC system if we can't find a From: header....or if +there was a previous error. */ + if (!from_header) { DEBUG(D_receive) debug_printf("DMARC: no From: header\n"); @@ -374,7 +371,8 @@ else if (!dmarc_abort) *p = saveend; /* The opendmarc library extracts the domain from the email address, but - * only try to store it if it's not empty. Otherwise, skip out of DMARC. */ + only try to store it if it's not empty. Otherwise, skip out of DMARC. */ + if (!header_from_sender || (strcmp( CCS header_from_sender, "") == 0)) dmarc_abort = TRUE; libdm_status = dmarc_abort @@ -390,17 +388,21 @@ else if (!dmarc_abort) } /* Skip DMARC if connection is SMTP Auth. Temporarily, admin should - * instead do this in the ACLs. */ +instead do this in the ACLs. */ + if (!dmarc_abort && !sender_host_authenticated) { uschar * dmarc_domain; + gstring * g = NULL; /* Use the envelope sender domain for this part of DMARC */ + spf_sender_domain = expand_string(US"$sender_address_domain"); if (!spf_response) { /* No spf data means null envelope sender so generate a domain name - * from the sender_helo_name */ + from the sender_helo_name */ + if (!spf_sender_domain) { spf_sender_domain = sender_helo_name; @@ -448,9 +450,9 @@ if (!dmarc_abort && !sender_host_authenticated) } /* Now we cycle through the dkim signature results and put into - * the opendmarc context, further building the DMARC reply. */ - dkim_history_buffer = US""; - while (sig) + the opendmarc context, further building the DMARC reply. */ + + for(pdkim_signature * sig = dkim_signatures; sig; sig = sig->next) { int dkim_result, dkim_ares_result, vs, ves; @@ -461,8 +463,9 @@ if (!dmarc_abort && !sender_host_authenticated) vs == PDKIM_VERIFY_INVALID ? DMARC_POLICY_DKIM_OUTCOME_TMPFAIL : DMARC_POLICY_DKIM_OUTCOME_NONE; libdm_status = opendmarc_policy_store_dkim(dmarc_pctx, US sig->domain, + /* The opendmarc project broke its API in a way we can't detect * easily. - * The EDITME provides a DMARC_API variable */ +The EDITME provides a DMARC_API variable */ #if DMARC_API >= 100400 sig->selector, #endif @@ -485,10 +488,9 @@ if (!dmarc_abort && !sender_host_authenticated) ves == PDKIM_VERIFY_INVALID_PUBKEY_IMPORT ? ARES_RESULT_PERMERROR : ARES_RESULT_UNKNOWN : ARES_RESULT_UNKNOWN; - dkim_history_buffer = string_sprintf("%sdkim %s %d\n", dkim_history_buffer, - sig->domain, dkim_ares_result); - sig = sig->next; + g = string_fmt_append(g, "dkim %s %d\n", sig->domain, dkim_ares_result); } + dkim_history_buffer = string_from_gstring(g); /* Look up DMARC policy record in DNS. We do this explicitly, rather than letting the dmarc library do it with opendmarc_policy_query_dmarc(), so that commit cfd47d9e77d3ec3cccbf9fb0ff6672c3b83b8fe3 Author: Jeremy Harris Date: Fri Jun 2 00:17:00 2023 +0100 Logging: for callout errors likely to be config problems, include the transport in the log line diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index e705965ba..02f1fa438 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -305,7 +305,7 @@ if (sc->interface) ) { HDEBUG(D_transport|D_acl|D_v) - debug_printf_indent("unable to bind outgoing SMTP call to %s: %s", sc->interface, + debug_printf_indent("unable to bind outgoing SMTP call to %s: %s\n", sc->interface, strerror(errno)); close(sock); return -1; diff --git a/src/src/verify.c b/src/src/verify.c index 228f63020..c420fcac1 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -709,6 +709,30 @@ tls_retry_connection: if (yield != OK) { errno = addr->basic_errno; + + /* For certain errors we want specifically to log the transport name, + for ease of fixing config errors. Slightly ugly doing it here, but we want + to not leak that also in the SMTP response. */ + switch (errno) + { + case EPROTOTYPE: + case ENOPROTOOPT: + case EPROTONOSUPPORT: + case ESOCKTNOSUPPORT: + case EOPNOTSUPP: + case EPFNOSUPPORT: + case EAFNOSUPPORT: + case EADDRINUSE: + case EADDRNOTAVAIL: + case ENETDOWN: + case ENETUNREACH: + log_write(0, LOG_MAIN|LOG_PANIC, + "%s verify %s (making calloout connection): T=%s %s", + options & vopt_is_recipient ? "sender" : "recipient", + yield == FAIL ? "fail" : "defer", + transport_name, strerror(errno)); + } + transport_name = NULL; deliver_host = deliver_host_address = NULL; deliver_domain = save_deliver_domain; commit 3531d1a756c5a72dfc825fbfdc7184cd017a7f39 Author: Jeremy Harris Date: Tue Jun 20 19:38:58 2023 +0100 TLS: log input for pipelining violation diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 6f4ad9495..cd759df7b 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -5630,27 +5630,27 @@ while (done <= 0) case BADSYN_CMD: SYNC_FAILURE: - if (smtp_inend >= smtp_inbuffer + IN_BUFFER_SIZE) - smtp_inend = smtp_inbuffer + IN_BUFFER_SIZE - 1; - c = smtp_inend - smtp_inptr; - if (c > 150) c = 150; /* limit logged amount */ - smtp_inptr[c] = 0; - incomplete_transaction_log(US"sync failure"); - log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol synchronization error " - "(next input sent too soon: pipelining was%s advertised): " - "rejected \"%s\" %s next input=\"%s\"", - f.smtp_in_pipelining_advertised ? "" : " not", - smtp_cmd_buffer, host_and_ident(TRUE), - string_printing(smtp_inptr)); - smtp_notquit_exit(US"synchronization-error", US"554", - US"SMTP synchronization error"); - done = 1; /* Pretend eof - drops connection */ - break; + { + unsigned nchars = 150; + uschar * buf = receive_getbuf(&nchars); /* destructive read */ + buf[nchars] = '\0'; + incomplete_transaction_log(US"sync failure"); + log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol synchronization error " + "(next input sent too soon: pipelining was%s advertised): " + "rejected \"%s\" %s next input=\"%s\" (%u bytes)", + f.smtp_in_pipelining_advertised ? "" : " not", + smtp_cmd_buffer, host_and_ident(TRUE), + string_printing(buf), nchars); + smtp_notquit_exit(US"synchronization-error", US"554", + US"SMTP synchronization error"); + done = 1; /* Pretend eof - drops connection */ + break; + } case TOO_MANY_NONMAIL_CMD: s = smtp_cmd_buffer; - while (*s != 0 && !isspace(*s)) s++; + while (*s && !isspace(*s)) s++; incomplete_transaction_log(US"too many non-mail commands"); log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many " "nonmail commands (last was \"%.*s\")", host_and_ident(FALSE), commit 4d108e7777e9b8e5fb212c31812fef61529cd414 Author: Jeremy Harris Date: Mon Jun 12 22:13:46 2023 +0100 Cancel early-pipe on an observed advertising change diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index c72028ce9..24ee577a2 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1115,7 +1115,10 @@ if (pending_EHLO) write_ehlo_cache_entry(sx); } else + { invalidate_ehlo_cache_entry(sx); + sx->early_pipe_active = FALSE; /* cancel further early-pipe on this conn */ + } return OK; /* just carry on */ } commit 7d39ccdbb100b10143ef1bd7451cbbf9c3779160 Author: Jeremy Harris Date: Wed Jun 14 12:33:21 2023 +0100 Debug old ehlo-resp record date, on noting mismatch diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 24ee577a2..926e77df4 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -931,6 +931,16 @@ if ( sx->early_pipe_active && (dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE))) { uschar * ehlo_resp_key = ehlo_cache_key(sx); + HDEBUG(D_transport) + { + dbdata_ehlo_resp * er; + + if (!(er = dbfn_read_enforce_length(dbm_file, ehlo_resp_key, sizeof(dbdata_ehlo_resp)))) + debug_printf("no ehlo-resp record!\n"); + else + debug_printf("ehlo-resp record is %d seconds old\n", time(NULL) - er->time_stamp); + } + dbfn_delete(dbm_file, ehlo_resp_key); dbfn_close(dbm_file); } commit 46a36afae41f63de654269c8a0b7cf5852a85a14 Author: Jeremy Harris Date: Thu Jun 29 23:21:21 2023 +0100 New format for exim_message_id diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index 71aee4d93..c01e911ce 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -249,8 +249,8 @@ utils: $(EXIM_MONITOR) exicyclog exinext exiwhat \ exigrep eximstats exipick exiqgrep exiqsumm \ transport-filter.pl convert4r3 convert4r4 \ exim_checkaccess \ - exim_dbmbuild exim_dumpdb exim_fixdb exim_tidydb exim_lock \ - exim_msgdate + exim_dbmbuild exim_dumpdb exim_fixdb exim_tidydb \ + exim_lock exim_msgdate exim_id_update # Targets for special-purpose configuration header builders @@ -451,6 +451,19 @@ exipick: config ../src/exipick.src @chmod a+x exipick @echo ">>> exipick script built" +exim_id_update: config ../src/exim_id_update.src + @rm -f exim_id_update + @. ./version.sh && sed \ + -e "s?PERL_COMMAND?$(PERL_COMMAND)?" \ + -e "s?SPOOL_DIRECTORY?$(SPOOL_DIRECTORY)?" \ + -e "s?BIN_DIRECTORY?$(BIN_DIRECTORY)?" \ + -e "s?EXIM_RELEASE_VERSION?$${EXIM_RELEASE_VERSION}?" \ + -e "s?EXIM_VARIANT_VERSION?$${EXIM_VARIANT_VERSION}?" \ + ../src/exim_id_update.src > exim_id_update-t + @mv exim_id_update-t exim_id_update + @chmod a+x exim_id_update + @echo ">>> exim_id_update script built" + transport-filter.pl: config ../src/transport-filter.src @rm -f transport-filter.pl @. ./version.sh && sed \ diff --git a/src/exim_monitor/em_menu.c b/src/exim_monitor/em_menu.c index e4db84915..926dbd95b 100644 --- a/src/exim_monitor/em_menu.c +++ b/src/exim_monitor/em_menu.c @@ -825,7 +825,7 @@ while (p > 0 && s[p+11] == ' ') /* Now pointing at first character of a main line. */ -Ustrncpy(message_id, s+p+11, MESSAGE_ID_LENGTH); +Ustrncpy(message_id, s+p+11, MESSAGE_ID_LENGTH); /*III*/ message_id[MESSAGE_ID_LENGTH] = 0; /* Highlight the line being menued, and save its parameters so that it diff --git a/src/exim_monitor/em_queue.c b/src/exim_monitor/em_queue.c index 5eb44648a..accc93652 100644 --- a/src/exim_monitor/em_queue.c +++ b/src/exim_monitor/em_queue.c @@ -154,7 +154,7 @@ return node; *************************************************/ static queue_item * -set_up(uschar *name, int dir_char) +set_up(uschar * name, int dir_char) { int i, rc, save_errno; struct stat statdata; @@ -271,15 +271,15 @@ sender_address = NULL; snprintf(CS buffer, sizeof(buffer), "%s/input/%s/%s/%s-D", spool_directory, queue_name, message_subdir, name); if (Ustat(buffer, &statdata) == 0) - q->size = message_size + statdata.st_size - SPOOL_DATA_START_OFFSET + 1; + q->size = message_size + statdata.st_size - spool_data_start_offset(name) + 1; /* Scan and process the recipients list, skipping any that have already been delivered, and removing visible names. */ -if (recipients_list != NULL) +if (recipients_list) for (i = 0; i < recipients_count; i++) { - uschar *r = recipients_list[i].address; + uschar * r = recipients_list[i].address; if (tree_search(tree_nonrecipients, r) == NULL) { if ((p = strstric(r+1, qualify_domain, FALSE)) != NULL && diff --git a/src/scripts/exim_install b/src/scripts/exim_install index e6857adaf..90eb09661 100755 --- a/src/scripts/exim_install +++ b/src/scripts/exim_install @@ -198,7 +198,7 @@ else set exim${EXE} ${exim_monitor} exim_dumpdb${EXE} exim_fixdb${EXE} \ exim_tidydb${EXE} exinext exiwhat exim_dbmbuild${EXE} exicyclog \ exigrep eximstats exipick exiqgrep exiqsumm exim_lock${EXE} \ - exim_checkaccess exim_msgdate + exim_checkaccess exim_msgdate exim_id_update fi echo $com "" diff --git a/src/src/dcc.c b/src/src/dcc.c index 8986dedde..98f978fa9 100644 --- a/src/src/dcc.c +++ b/src/src/dcc.c @@ -272,7 +272,7 @@ if (flushbuffer(sockfd, sendbuf) != 0) /* now send the body */ DEBUG(D_acl) debug_printf("DCC: ***********************************\nDCC: Writing body:\n"); -(void)fseek(data_file, SPOOL_DATA_START_OFFSET, SEEK_SET); +(void)fseek(data_file, spool_data_start_offset(message_id), SEEK_SET); gstring filebuf = { .size = big_buffer_size, .ptr = 0, .s = big_buffer }; diff --git a/src/src/deliver.c b/src/src/deliver.c index ad045c8cc..47368a860 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -6392,7 +6392,7 @@ Returns: When the global variable mua_wrapper is FALSE: */ int -deliver_message(uschar *id, BOOL forced, BOOL give_up) +deliver_message(uschar * id, BOOL forced, BOOL give_up) { int i, rc; int final_yield = DELIVER_ATTEMPTED_NORMAL; @@ -6478,7 +6478,7 @@ opening the data file, message_subdir gets set. */ if ((deliver_datafile = spool_open_datafile(id)) < 0) return continue_closedown(); /* yields DELIVER_NOT_ATTEMPTED */ -/* The value of message_size at this point has been set to the data length, +/* tHe value of message_size at this point has been set to the data length, plus one for the blank line that notionally precedes the data. */ /* Now read the contents of the header file, which will set up the headers in @@ -6511,8 +6511,8 @@ give up; if the message has been around for sufficiently long, remove it. */ if (rc != spool_read_hdrerror) { received_time.tv_sec = received_time.tv_usec = 0; - /*XXX subsec precision?*/ - for (i = 0; i < 6; i++) + /*III subsec precision?*/ + for (i = 0; i < MESSAGE_ID_TIME_LEN; i++) received_time.tv_sec = received_time.tv_sec * BASE_62 + tab62[id[i] - '0']; } diff --git a/src/src/dkim_transport.c b/src/src/dkim_transport.c index c127d5b73..5b79b4b76 100644 --- a/src/src/dkim_transport.c +++ b/src/src/dkim_transport.c @@ -160,8 +160,8 @@ arc_sign_init(); in wireformat. */ dkim->dot_stuffed = f.spool_file_wireformat; -if (!(dkim_signature = dkim_exim_sign(deliver_datafile, SPOOL_DATA_START_OFFSET, - hdrs, dkim, &errstr))) +if (!(dkim_signature = dkim_exim_sign(deliver_datafile, + spool_data_start_offset(message_id), hdrs, dkim, &errstr))) if (!(rc = dkt_sign_fail(dkim, &errno))) { *err = errstr; diff --git a/src/src/exigrep.src b/src/src/exigrep.src index 2c414fd31..9eb9c454a 100644 --- a/src/src/exigrep.src +++ b/src/src/exigrep.src @@ -87,7 +87,7 @@ sub do_line if (!/^\d{4}-/o) { $_ =~ s/^.*? exim\b.*?: //o; } return unless - my($date,$id) = /^(\d{4}-\d\d-\d\d \d\d:\d\d:\d\d(?:\.\d+)? (?:[+-]\d{4} )?)(?:\[\d+\] )?(\w{6}\-\w{6}\-\w{2})?/o; + my($date,$id) = /^(\d{4}-\d\d-\d\d \d\d:\d\d:\d\d(?:\.\d+)? (?:[+-]\d{4} )?)(?:\[\d+\] )?(\w{6}\-\w{6}\-\w{2}|\w{6}-\w{11}-\w{4})?/o; # Handle the case when the log line belongs to a specific message. We save # lines for specific messages until the message is complete. Then either print @@ -128,7 +128,7 @@ sub do_line if (index($_, 'Completed') != -1 || index($_, 'SMTP data timeout') != -1 || (index($_, 'rejected') != -1 && - /^(\d{4}-\d\d-\d\d \d\d:\d\d:\d\d(?:\.\d+)? (?:[+-]\d{4} )?)(?:\[\d+\] )?\w{6}\-\w{6}\-\w{2} rejected/o)) + /^(\d{4}-\d\d-\d\d \d\d:\d\d:\d\d(?:\.\d+)? (?:[+-]\d{4} )?)(?:\[\d+\] )?(?:\w{6}\-\w{6}\-\w{2}|\w{6}-\w{11}-\w{4}) rejected/o)) { if ($queue_time != -1 && $saved{$id} =~ /^(\d{4}-\d\d-\d\d \d\d:\d\d:\d\d ([+-]\d{4} )?)/o) diff --git a/src/src/exim.c b/src/src/exim.c index 06863347d..94061f97d 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -2022,7 +2022,14 @@ this here, because the -M options check their arguments for syntactic validity using mac_ismsgid, which uses this. */ regex_ismsgid = - regex_must_compile(US"^(?:[^\\W_]{6}-){2}[^\\W_]{2}$", MCS_NOFLAGS, TRUE); + regex_must_compile(US"^(?:" + "[^\\W_]{" str(MESSAGE_ID_TIME_LEN) "}" + "-[^\\W_]{" str(MESSAGE_ID_PID_LEN) "}" + "-[^\\W_]{" str(MESSAGE_ID_SUBTIME_LEN) "}" + "|" + "(?:[^\\W_]{6}-){2}[^\\W_]{2}" /* old ID format */ + ")$", + MCS_NOFLAGS, TRUE); /* Precompile the regular expression that is used for matching an SMTP error code, possibly extended, at the start of an error message. Note that the diff --git a/src/src/exim_id_update.src b/src/src/exim_id_update.src new file mode 100644 index 000000000..28fff1c4d --- /dev/null +++ b/src/src/exim_id_update.src @@ -0,0 +1,224 @@ +#!PERL_COMMAND +# Copyright (c) 2023 The Exim Maintainers +# SPDX-License-Identifier: GPL-2.0-or-later +# See the file NOTICE for conditions of use and distribution. + +# Utility for one-time upgrage/downgrade between exim message-id formats, +# around the 4.97 transition + + +# This variables should be set by the building process +my $spool = 'SPOOL_DIRECTORY'; # may be overridden later + +use strict; +use warnings; +use Getopt::Std; +use File::Find; +use Fcntl; +use File::FcntlLock; +use IO::Handle; + + +my %opt; +my $mode_upgrade; +my $id; + +my $b62 = '[0-9A-Za-z]'; + +if ( !getopts('hud', \%opt) + || $opt{h} + || !$opt{u} && !$opt{d} + ) { + &help; exit 1; +} + +$spool = $ARGV[0] if ($ARGV[0]); +$mode_upgrade = $opt{u}; + +sub help(){ + print <<'EOF' +Utility for one-time down/upgrade of Exim message-id formats +in spool files. Only the filenames is first-line ID tag values +are affected; not message content such as Message-ID fields. +Only -H, -D and -J files are handled. + +Syntax: exim_id_update [-d | -u | -h] [spooldir] + + -d Downgrade mode + -h This help message + -u Upgrade mode + +Exactly one of -d or -u must be given. +The spool directory defaults to the build-time value, +or can be given as a command-line argument. +EOF +} + +# For downgrade mode: +# - Check exim not running +# - Wipe any wait-hints DBs, buy just removing the files. +# For all queue (main and named), with split-spool if needed, for each file identifiable +# as a spoolfile (name starts with an ID, ends with -H -D -J -K) +# XXX are there only subsets we can handle - eg. a -H + a -D ? +# mainline code sequence is -D (locks msg) -H ?-J +# mainline locking sequence (spool_open_datafile()) is +# - open -D +# - fnctl F_LOCK (amount = first line of file) + +# The -H and -D files contain the ID as their initial line. +# The -J file +# - records successful deliveries, as insurance vs. crashes +# - has lines with mail addresses +# The -K file +# - is a temp for DKIM'd delivery when a transport-filter is in use +# - contains the message that would have been put on the wire (except for encryption) +# - the transport, with tpt-filter, writes the file - and then reads it +# so as to generate the DKIM signature. Then it sends the message, with +# generated headers and reading the file again, down the wire. +# And then it deletes it. +# - unclear if we really want to rewrite these files, if we do see then +# Probably not. + +# - if old-format name: +# - lock old message +# - generate new files, in safe sequence +# - remove old files (do we need to archive?) +# + +# loop for default Q, named Qs +# loop for plain, split-spool +# loop over files +# if is -H, and -D exists +# +# create new ID string from old +# lock the old -D +# create new -D +# lock new -D +# create new -H +# +# if -J exists +# rename old -J to new -J +# +# remove old -H +# remove old -D +# unlock new -D +# + +chdir $spool or die "failed cd to $spool"; +find( sub { + do_file($_) + if ($_ =~ ($mode_upgrade ? "${b62}{6}-${b62}{6}-${b62}{2}-D" : "${b62}{6}-${b62}{11}-${b62}{4}-D") ); + }, + '.' ); +exit 0; + + +sub do_file { + my $old_dfile = shift; + my $old_prefix = $old_dfile; + my ($old_hfile , $new_prefix); + my ($d_old, $d_new); + my $line; + + $old_prefix =~ s/-D$//; + $old_hfile = $old_prefix . '-H'; + + # The -H file must also exist + return if (! -e $old_hfile); + + $new_prefix = $old_prefix; + if ($mode_upgrade) { + $new_prefix =~ s/^([^-]*)-([^-]*)-(.*)$/$1-00000$2-${3}00/; + } else { + $new_prefix =~ s/^([^-]*)-.....([^-]*)-(..)..$/$1-$2-${3}/; + } + + ####### create the new -D file + + open $d_old, '+<', $old_dfile + or die "Can't open file: $!\n"; + + # lock the old -D file + dfile_lock($d_old, $mode_upgrade ? 16 : 23); + # seek past the first line + <$d_old>; + + # create the new -D file + $d_new = f_create($new_prefix . '-D'); + + # lock the new -D file + dfile_lock($d_new, $mode_upgrade ? 23 : 16); + + # write the new message-id to the first line + print $d_new "$new_prefix-D\n"; + + # copy the rest of the -D file + while ($line = <$d_old>) { + print $d_new $line; + } + + ####### create the new -H file + + open my $h_old, '<', $old_hfile + or die "Can't open file: $!\n"; + <$h_old>; + + my $h_new = f_create($new_prefix . '-H'); + print $h_new "$new_prefix-H\n"; + while ($line = <$h_old>) { + print $h_new $line; + } + + ###### rename a journal file if it exists + + rename $old_prefix . '-J', $new_prefix . '-J' if (-e $old_prefix . '-J'); + + ###### tidy up + + close $h_old; + unlink $old_hfile or die "failed to remove $old_hfile"; + close $d_old; + unlink $old_dfile or die "failed to remove $old_dfile"; + + dfile_unlock($d_new, $mode_upgrade ? 23 : 16); + close $d_new; +} + + + +sub dfile_lock { + my $fh = shift; + my $nbytes = shift; + my $fs = new File::FcntlLock; + + $fs->l_type( F_WRLCK ); + $fs->l_whence( SEEK_CUR ); + $fs->l_start( 0 ); + $fs->l_len( $nbytes ); + + $fs->lock( $fh, F_SETLK ) + or die "Locking failed: " . $fs->error . "\n"; +} + +sub dfile_unlock { + my $fh = shift; + my $nbytes = shift; + my $fs = new File::FcntlLock; + + $fs->l_type( F_UNLCK ); + $fs->l_whence( SEEK_CUR ); + $fs->l_start( 0 ); + $fs->l_len( $nbytes ); + $fs->lock( $fh, F_SETLK ) + or die "Unlocking failed: " . $fs->error . "\n"; +} + +sub f_create { + my $filename = shift; + sysopen(my $fh, $filename, O_RDWR|O_CREAT|O_EXCL) + or die "Can't create $filename: $!"; + $fh->autoflush(1); + # + # TODO: chown, chgrp exim; chmod 0640 + return $fh; +} diff --git a/src/src/exim_msgdate.src b/src/src/exim_msgdate.src index bfb5bc81e..d68aa392b 100755 --- a/src/src/exim_msgdate.src +++ b/src/src/exim_msgdate.src @@ -56,7 +56,7 @@ my $localhost_number; # An Exim config value my $nolocalhost_number; my $p_name = basename $0; -my $p_version = "20230304.0"; +my $p_version = "20230501.0"; my $p_cp = < $id_resolution) { - $seconds++; - $fractions -= $id_resolution; - } - while ($fractions < -1e-7) { - $seconds--; - $fractions += $id_resolution; - } - # $seconds += $fractions / $id_resolution; + $fractions *= $id_resolution; + #warn "decoded: seconds: $seconds, fractions: $fractions"; + ($fractions < 1000000) or die "bad microsecond count: $fractions\n"; - # warn "decoded: seconds: $seconds, fractions: $fractions/$id_resolution\n"; - - return ($seconds, $fractions / $id_resolution); + return ($seconds, $fractions); } # sub unpack_time($$) sub print_time($$$$$$) @@ -420,7 +422,7 @@ sub print_time($$$$$$) my $decimalstring = ""; # if ($decimal>0) { - $decimalstring = sprintf(".%6.6d", 1000000*$decimal); + $decimalstring = sprintf(".%6.6d", $decimal); } my $secondsstring; unless (defined $unix or defined $zulu or defined $localtm) { @@ -447,17 +449,31 @@ foreach my $msgid (@ARGV) { my ($seconds, $pid, $fractions, $decimal); if ($msgid =~ - /(^|[\s<])E?([a-zA-Z0-9]{6})-([a-zA-Z0-9]{6})-([a-zA-Z0-9]{2})/) + /(?:(?:^|[\s<])E? + (?[a-zA-Z0-9]{6}) # new format + -(?[a-zA-Z0-9]{11}) + -(?[a-zA-Z0-9]{4}) + | + (?[a-zA-Z0-9]{6}) # old format + -(?[a-zA-Z0-9]{6}) + -(?[a-zA-Z0-9]{2}) + )/x) { +print "saw full mesgid\n" if $debug; + # Should take either the log form of timestamp, # the Message-ID: header form with the leading 'E', ... - ($seconds, $pid, $fractions) = ($2, $3, $4); - ($seconds, $decimal) = unpack_time($seconds, $fractions); - $pid = decode62($pid); - #warn "$seconds, $pid, $fractions\n"; - } elsif ($msgid =~ /(^|[^0-9A-Za-z])([a-zA-Z0-9]{6})$/) { + ($seconds, $decimal) = unpack_time($+{seconds}, $+{fractions}); + $pid = decode62($+{pid}); + #warn "$seconds, $pid, $+{fractions}\n"; + } elsif ($msgid =~ /(?:^|[^0-9A-Za-z]) + (? + [a-zA-Z0-9]{11} # new format + |[a-zA-Z0-9]{6} # old format + )$/x) { # ... or just the timecode section before the first '-' - ($seconds, $pid, $decimal) = (decode62($2), undef, 0); +print "saw just timecode\n" if $debug; + ($seconds, $pid, $decimal) = (decode62($+{seconds}), undef, 0); } else { warn "$msgid not parsed\n"; next; diff --git a/src/src/eximstats.src b/src/src/eximstats.src index 5e1a0847b..3b89e6bd7 100644 --- a/src/src/eximstats.src +++ b/src/src/eximstats.src @@ -1898,7 +1898,11 @@ sub generate_parser { next if ($length < 38 + $extra); } - $id = substr($_, 20 + $extra, 16); + # $id = substr($_, 20 + $extra, 16); # old ID was 16 chars + $id = substr($_, 20 + $extra, 23); # new IS is 23 chars + $id =~ s/(\S+).*/$1/; + $extra += length($id) - 16; + $flag = substr($_, 37 + $extra, 2); if ($flag !~ /^([<>=*-]+|SA)$/ && /rejected|refused|dropped/) { diff --git a/src/src/exipick.src b/src/src/exipick.src index a63133353..61527ca64 100644 --- a/src/src/exipick.src +++ b/src/src/exipick.src @@ -1,5 +1,6 @@ #!PERL_COMMAND # Copyright (c) 1995 - 2018 University of Cambridge. +# SPDX-License-Identifier: GPL-2.0-or-later # See the file NOTICE for conditions of use and distribution. diff --git a/src/src/exiqgrep.src b/src/src/exiqgrep.src index 0661c5792..74c29f7ef 100644 --- a/src/src/exiqgrep.src +++ b/src/src/exiqgrep.src @@ -3,7 +3,9 @@ # Utility for searching and displaying queue information. # Written by Matt Hubbard 15 August 2002 # -# Copyright (c) The Exim Maintainers 2021 - 2022 +# Copyright (c) The Exim Maintainers 2021 - 2023 +# SPDX-License-Identifier: GPL-2.0-or-later +# See the file NOTICE for conditions of use and distribution. # Except when they appear in comments, the following placeholders in this # source are replaced when it is turned into a runnable script: @@ -17,7 +19,7 @@ # Routine for extracting the UTC timestamp from message ID # lifted from eximstat utility -# Version 1.2 +# Version 1.3 use strict; BEGIN { pop @INC if $INC[-1] eq '.' }; @@ -109,18 +111,17 @@ sub collect() { chomp(); my $line = $_; #Should be 1st line of record, if not error. - if ($line =~ /^\s*(\w+)\s+((?:\d+(?:\.\d+)?[A-Z]?)?)\s*(\w{6}-\w{6}-\w{2})\s+(<.*?>)/) { - my $msg = $3; - $id{$msg}{age} = $1; - $id{$msg}{size} = $2; - $id{$msg}{from} = $4; + if ($line =~ /^\s*(?\w+) + \s+(?(?:\d+(?:\.\d+)?[A-Z]?)?) + \s*(?(?:\w{6}-\w{6}-\w{2}|\w{6}-\w{11}-\w{4})) # old, 2023 msgid formats + \s+(?<.*?>)/x) { + my $msg = $+{msgid}; + $id{$msg}{age} = $+{age}; + $id{$msg}{size} = $+{size}; + $id{$msg}{from} = $+{from}; $id{$msg}{birth} = &msg_utc($msg); $id{$msg}{ages} = time - $id{$msg}{birth}; - if ($line =~ /\*\*\* frozen \*\*\*$/) { - $id{$msg}{frozen} = 1; - } else { - $id{$msg}{frozen} = 0; - } + $id{$msg}{frozen} = ($line =~ /\*\*\* frozen \*\*\*$/) ? 1 : 0; while( =~ /\s+(.*?\@.*)$/) { push(@{$id{$msg}{rcpt}},$1); } diff --git a/src/src/exiqsumm.src b/src/src/exiqsumm.src index 67772f5e8..afb74fdcd 100644 --- a/src/src/exiqsumm.src +++ b/src/src/exiqsumm.src @@ -2,6 +2,11 @@ # Mail Queue Summary # Christoph Lameter, 21 May 1997 +# +# Copyright (c) The Exim Maintainers 2023 +# SPDX-License-Identifier: GPL-2.0-or-later +# See the file NOTICE for conditions of use and distribution. + # Modified by Philip Hazel, June 1997 # Bug fix: June 1998 by Philip Hazel # Message sizes not listed by -bp with K or M @@ -117,9 +122,12 @@ if (/^$/o || /^\s*D\s\S+/o) { next; } # If it's the first line of a message, pick out the data. Note: it may # have text after the final > (e.g. frozen) so don't insist that it ends >. -if (/^([\d\s]{2,3}\w)\s+(\S+)\s(\S+)\s\<(\S*)\>/o) +if (/^ (?[\d\s]{2,3}\w) + \s+ (?\S+) + \s (?\S+) + \s\< (?\S*) \>/ox) { - ($age,$size,$id,$src)=($1,$2,$3,$4); + ($age,$size,$id,$src)=($+{age},$+{size},$+{id},$+{src}); $src =~ s/([^\@]*)\@(.*?)$/$2/o; if (/\*\*\*\sfrozen\s\*\*\*/o) { $frozen=1; } else { $frozen=0; } if ($src eq "") { $bounce=1; $src="<>"; } else { $bounce=0; } diff --git a/src/src/expand.c b/src/src/expand.c index 45172b1ed..de00c7254 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -2037,7 +2037,8 @@ switch (vp->type) if (!*ss && deliver_datafile >= 0) /* Read body when needed */ { uschar * body; - off_t start_offset = SPOOL_DATA_START_OFFSET; + off_t start_offset_o = spool_data_start_offset(message_id); + off_t start_offset = start_offset_o; int len = message_body_visible; if (len > message_size) len = message_size; @@ -2049,8 +2050,8 @@ switch (vp->type) if (fstat(deliver_datafile, &statbuf) == 0) { start_offset = statbuf.st_size - len; - if (start_offset < SPOOL_DATA_START_OFFSET) - start_offset = SPOOL_DATA_START_OFFSET; + if (start_offset < start_offset_o) + start_offset = start_offset_o; } } if (lseek(deliver_datafile, start_offset, SEEK_SET) < 0) @@ -7264,7 +7265,7 @@ NOT_ITEM: ; "operator is \"%s\", which is not a decimal number", sub); goto EXPAND_FAILED; } - yield = string_cat(yield, string_base62(n)); + yield = string_cat(yield, string_base62_32(n)); /*XXX only handles 32b input range. Need variants? */ break; } diff --git a/src/src/functions.h b/src/src/functions.h index aa5057a83..b5829a54c 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -562,7 +562,8 @@ extern gstring *string_append(gstring *, int, ...) WARN_UNUSED_RESULT; extern gstring *string_append_listele(gstring *, uschar, const uschar *) WARN_UNUSED_RESULT; extern gstring *string_append_listele_n(gstring *, uschar, const uschar *, unsigned) WARN_UNUSED_RESULT; extern gstring *string_append2_listele_n(gstring *, const uschar *, const uschar *, unsigned) WARN_UNUSED_RESULT; -extern uschar *string_base62(unsigned long int); +extern uschar *string_base62_32(unsigned long int); +extern uschar *string_base62_64(unsigned long int); extern gstring *string_cat (gstring *, const uschar * ) WARN_UNUSED_RESULT; extern gstring *string_catn(gstring *, const uschar *, int) WARN_UNUSED_RESULT; extern int string_compare_by_pointer(const void *, const void *); @@ -1166,10 +1167,32 @@ set_subdir_str(uschar * subdir_str, const uschar * name, int search_sequence) { subdir_str[0] = split_spool_directory == (search_sequence == 0) - ? name[5] : '\0'; + ? name[MESSAGE_ID_TIME_LEN-1] : '\0'; subdir_str[1] = '\0'; } +/******************************************************************************/ +/* Message-ID format transition knowlege */ + +static inline BOOL +is_new_message_id(const uschar * id) +{ +return id[MESSAGE_ID_TIME_LEN + 1 + MESSAGE_ID_PID_LEN] == '-'; +} + +static inline BOOL +is_old_message_id(const uschar * id) +{ +return id[MESSAGE_ID_TIME_LEN + 1 + MESSAGE_ID_PID_LEN_OLD] == '-'; +} + +static inline unsigned +spool_data_start_offset(const uschar * id) +{ +if (is_old_message_id(id)) return SPOOL_DATA_START_OFFSET_OLD; +return SPOOL_DATA_START_OFFSET; +} + /******************************************************************************/ /* Time calculations */ diff --git a/src/src/local_scan.h b/src/src/local_scan.h index 72f2ac47d..c276b882d 100644 --- a/src/src/local_scan.h +++ b/src/src/local_scan.h @@ -109,12 +109,22 @@ enum { opt_stringptr, opt_int, opt_octint, opt_mkint, opt_Kint, opt_fixed, by exim. The external version for use in Received: strings has a leading 'E' added to ensure it starts with a letter. */ -#define MESSAGE_ID_LENGTH 16 +#define MESSAGE_ID_PID_LEN_OLD 6 +#define MESSAGE_ID_SUBTIME_LEN_OLD 2 + +/* tttttt-ppppppppppp-ssss */ +# define MESSAGE_ID_TIME_LEN 6 /*III could these be not-exposed to local_scan? */ +# define MESSAGE_ID_PID_LEN 11 +# define MESSAGE_ID_SUBTIME_LEN 4 + +#define MESSAGE_ID_LENGTH_OLD (MESSAGE_ID_TIME_LEN+1+MESSAGE_ID_PID_LEN_OLD+1+MESSAGE_ID_SUBTIME_LEN_OLD) +#define MESSAGE_ID_LENGTH (MESSAGE_ID_TIME_LEN+1+MESSAGE_ID_PID_LEN +1+MESSAGE_ID_SUBTIME_LEN) /* The offset to the start of the data in the data file - this allows for the name of the data file to be present in the first line. */ -#define SPOOL_DATA_START_OFFSET (MESSAGE_ID_LENGTH+3) +#define SPOOL_DATA_START_OFFSET_OLD (MESSAGE_ID_LENGTH_OLD+3) +#define SPOOL_DATA_START_OFFSET (MESSAGE_ID_LENGTH+3) /* Structure definitions that are documented as visible in the function. */ diff --git a/src/src/macros.h b/src/src/macros.h index c55276332..ed7a259aa 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -184,7 +184,8 @@ written on the spool, it gets read into big_buffer. */ /* The length of the base names of spool files, which consist of an internal message id with a trailing "-H" or "-D" added. */ -#define SPOOL_NAME_LENGTH (MESSAGE_ID_LENGTH+2) +#define SPOOL_NAME_LENGTH_OLD (MESSAGE_ID_LENGTH_OLD + 2) +#define SPOOL_NAME_LENGTH (MESSAGE_ID_LENGTH + 2) /* The maximum number of message ids to store in a waiting database record, and the max number of continuation records allowed. */ @@ -199,6 +200,8 @@ record, and the max number of continuation records allowed. */ /* Macros for trivial functions */ +#define xstr(x) #x +#define str(x) xstr(x) /* stringize, expanding macros in arg first */ #define mac_ismsgid(s) (regex_match(regex_ismsgid, (s), -1, NULL)) diff --git a/src/src/queue.c b/src/src/queue.c index b6e7907d7..fa4fc0aec 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -54,8 +54,13 @@ queue_filename **append = &first; while (a && b) { int d; - if ((d = Ustrncmp(a->text, b->text, 6)) == 0) - d = Ustrcmp(a->text + 14, b->text + 14); + if ((d = Ustrncmp(a->text, b->text, MESSAGE_ID_TIME_LEN)) == 0) + { + BOOL a_old = is_old_message_id(a->text), b_old = is_old_message_id(b->text); + /* Do not worry over the sub-second sorting wrt. old vs. new */ + d = Ustrcmp(a->text + (a_old ? 6+1+6+1 : MESSAGE_ID_TIME_LEN + 1 + MESSAGE_ID_PID_LEN + 1), + b->text + (b_old ? 6+1+6+1 : MESSAGE_ID_TIME_LEN + 1 + MESSAGE_ID_PID_LEN + 1)); + } if (d < 0) { *append = a; @@ -188,9 +193,9 @@ for (; i <= *subcount; i++) /* Now scan the directory. */ - for (struct dirent *ent; ent = readdir(dd); ) + for (struct dirent * ent; ent = readdir(dd); ) { - uschar *name = US ent->d_name; + uschar * name = US ent->d_name; int len = Ustrlen(name); /* Count entries */ @@ -209,14 +214,15 @@ for (; i <= *subcount; i++) /* Otherwise, if it is a header spool file, add it to the list */ - if (len == SPOOL_NAME_LENGTH && - Ustrcmp(name + SPOOL_NAME_LENGTH - 2, "-H") == 0) + if ( (len == SPOOL_NAME_LENGTH || len == SPOOL_NAME_LENGTH_OLD) + && Ustrcmp(name + len - 2, "-H") == 0 + ) if (pcount) (*pcount)++; else { queue_filename * next = - store_get(sizeof(queue_filename) + Ustrlen(name), name); + store_get(sizeof(queue_filename) + len, name); Ustrcpy(next->text, name); next->dir_uschar = subdirchar; @@ -657,8 +663,8 @@ for (int i = queue_run_in_order ? -1 : 0; /* Now deliver the message; get the id by cutting the -H off the file name. The return of the process is zero if a delivery was attempted. */ + fq->text[Ustrlen(fq->text)-2] = 0; set_process_info("running queue: %s", fq->text); - fq->text[SPOOL_NAME_LENGTH-2] = 0; #ifdef MEASURE_TIMING report_time_since(×tamp_startup, US"queue msg selected"); #endif @@ -990,7 +996,7 @@ else for (; that precedes the data. */ if (Ustat(fname, &statbuf) == 0) - size = message_size + statbuf.st_size - SPOOL_DATA_START_OFFSET + 1; + size = message_size + statbuf.st_size - spool_data_start_offset(qf->text) + 1; i = (now - received_time.tv_sec)/60; /* minutes on queue */ if (i > 90) { @@ -1087,7 +1093,8 @@ Returns: FALSE if there was any problem */ BOOL -queue_action(uschar *id, int action, uschar **argv, int argc, int recipients_arg) +queue_action(uschar * id, int action, uschar ** argv, int argc, + int recipients_arg) { BOOL yield = TRUE; BOOL removed = FALSE; @@ -1156,7 +1163,7 @@ if (action >= MSG_SHOW_BODY) } while((rc = read(fd, big_buffer, big_buffer_size)) > 0) - rc = write(fileno(stdout), big_buffer, rc); + rc = write(fileno(stdout), big_buffer, rc); /*XXX why not fwrite() ? */ (void)close(fd); return TRUE; @@ -1299,11 +1306,9 @@ switch(action) case MSG_REMOVE: { - uschar suffix[3]; + uschar suffix[3] = { [0]='-', [2]=0 }; - suffix[0] = '-'; - suffix[2] = 0; - message_subdir[0] = id[5]; + message_subdir[0] = id[MESSAGE_ID_TIME_LEN - 1]; for (int j = 0; j < 2; message_subdir[0] = 0, j++) { diff --git a/src/src/receive.c b/src/src/receive.c index 19f8962c6..acb3c40fc 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1693,7 +1693,9 @@ int error_rc = error_handling == ERRORS_SENDER int header_size = 256; int had_zero = 0; int prevlines_length = 0; -const int id_resolution = BASE_62 == 62 ? 5000 : 10000; +const int id_resolution = BASE_62 == 62 && !host_number_string ? 1 + : BASE_62 != 62 && host_number_string ? 4 + : 2; int ptr = 0; @@ -2693,41 +2695,37 @@ if (extract_recip) } /* Now build the unique message id. This has changed several times over the -lifetime of Exim. This description was rewritten for Exim 4.14 (February 2003). -Retaining all the history in the comment has become too unwieldy - read -previous release sources if you want it. - -The message ID has 3 parts: tttttt-pppppp-ss. Each part is a number in base 62. -The first part is the current time, in seconds. The second part is the current -pid. Both are large enough to hold 32-bit numbers in base 62. The third part -can hold a number in the range 0-3843. It used to be a computed sequence -number, but is now the fractional component of the current time in units of -1/2000 of a second (i.e. a value in the range 0-1999). After a message has been -received, Exim ensures that the timer has ticked at the appropriate level -before proceeding, to avoid duplication if the pid happened to be re-used -within the same time period. It seems likely that most messages will take at -least half a millisecond to be received, so no delay will normally be -necessary. At least for some time... +lifetime of Exim, and is changing for Exim 4.97. +The previous change was in about 2003. + +Detail for the pre-4.97 version is here in [square-brackets]. + +The message ID has 3 parts: tttttt-ppppppppppp-ssss (6, 11, 4 - total 23 with +the dashes). Each part is a number in base 62. +[ tttttt-pppppp-ss 6, 6, 2 => 16 ] -There is a modification when localhost_number is set. Formerly this was allowed -to be as large as 255. Now it is restricted to the range 0-16, and the final -component of the message id becomes (localhost_number * 200) + fractional time -in units of 1/200 of a second (i.e. a value in the range 0-3399). +The first part is the current time, in seconds. Six chars is enough until +year 3700 with case-sensitive filesystes, but will run out in 2038 on +case-insensitive ones (Cygwin, Darwin - where we have to use base-36. +Both of those are in the "unsupported" bucket, so ignore for now). -Some not-really-Unix operating systems use case-insensitive file names (Darwin, -Cygwin). For these, we have to use base 36 instead of base 62. Luckily, this -still allows the tttttt field to hold a large enough number to last for some -more decades, and the final two-digit field can hold numbers up to 1295, which -is enough for milliseconds (instead of 1/2000 of a second). +The second part is the current pid, and supports 64b [31b] PIDs. -However, the pppppp field cannot hold a 32-bit pid, but it can hold a 31-bit -pid, so it is probably safe because pids have to be positive. The -localhost_number is restricted to 0-10 for these hosts, and when it is set, the -final field becomes (localhost_number * 100) + fractional time in centiseconds. +The third part holds sub-second time, plus (when localhost_number is set) +the host number multiplied by a number large enough to keep it away from +the time portion. Host numbers are restricted to the range 0-16. +The time resolution is variously 1, 2 or 4 microseconds [0.5 or 1 ms] +depending on the use of localhost_nubmer and of case-insensitive filesystems. + +After a message has been received, Exim ensures that the timer has ticked at the +appropriate level before proceeding, to avoid duplication if the pid happened to +be re-used within the same time period. It seems likely that most messages will +take at least half a millisecond to be received, so no delay will normally be +necessary. At least for some time... -Note that string_base62() returns its data in a static storage block, so it -must be copied before calling string_base62() again. It always returns exactly -6 characters. +Note that string_base62_XX() returns its data in a static storage block, so it +must be copied before calling string_base62_XXX) again. It always returns exactly +11 (_64) or 6 (_32) characters. There doesn't seem to be anything in the RFC which requires a message id to start with a letter, but Smail was changed to ensure this. The external form of @@ -2740,27 +2738,35 @@ checking that a string is in this format must be updated in a corresponding way. It appears in the initializing code in exim.c. The macro MESSAGE_ID_LENGTH must also be changed to reflect the correct string length. The queue-sort code needs to know the layout. Then, of course, other programs that rely on the -message id format will need updating too. */ +message id format will need updating too (inc. at least exim_msgdate). */ -Ustrncpy(message_id, string_base62((long int)(message_id_tv.tv_sec)), 6); -message_id[6] = '-'; -Ustrncpy(message_id + 7, string_base62((long int)getpid()), 6); +Ustrncpy(message_id, string_base62_32((long int)(message_id_tv.tv_sec)), MESSAGE_ID_TIME_LEN); +message_id[MESSAGE_ID_TIME_LEN] = '-'; +Ustrncpy(message_id + MESSAGE_ID_TIME_LEN + 1, + string_base62_64((long int)getpid()), + MESSAGE_ID_PID_LEN + ); /* Deal with the case where the host number is set. The value of the number was checked when it was read, to ensure it isn't too big. */ if (host_number_string) - sprintf(CS(message_id + MESSAGE_ID_LENGTH - 3), "-%2s", - string_base62((long int)( - host_number * (1000000/id_resolution) + - message_id_tv.tv_usec/id_resolution)) + 4); + sprintf(CS(message_id + MESSAGE_ID_TIME_LEN + 1 + MESSAGE_ID_PID_LEN), + "-%" str(MESSAGE_ID_SUBTIME_LEN) "s", + string_base62_32((long int)( + host_number * (1000000/id_resolution) + + message_id_tv.tv_usec/id_resolution)) + + (6 - MESSAGE_ID_SUBTIME_LEN) + ); /* Host number not set: final field is just the fractional time at an appropriate resolution. */ else - sprintf(CS(message_id + MESSAGE_ID_LENGTH - 3), "-%2s", - string_base62((long int)(message_id_tv.tv_usec/id_resolution)) + 4); + sprintf(CS(message_id + MESSAGE_ID_TIME_LEN + 1 + MESSAGE_ID_PID_LEN), + "-%" str(MESSAGE_ID_SUBTIME_LEN) "s", + string_base62_32((long int)(message_id_tv.tv_usec/id_resolution)) + + (6 - MESSAGE_ID_SUBTIME_LEN)); /* Add the current message id onto the current process info string if it will fit. */ @@ -3191,7 +3197,7 @@ spool_data_file = fdopen(data_fd, "w+"); lock_data.l_type = F_WRLCK; lock_data.l_whence = SEEK_SET; lock_data.l_start = 0; -lock_data.l_len = SPOOL_DATA_START_OFFSET; +lock_data.l_len = spool_data_start_offset(message_id); if (fcntl(data_fd, F_SETLK, &lock_data) < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Cannot lock %s (%d): %s", spool_name, @@ -3275,7 +3281,7 @@ if (!ferror(spool_data_file) && !(receive_feof)() && message_ended != END_DOT) } else { - fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET); give_local_error(ERRMESS_TOOBIG, string_sprintf("message too big (max=%d)", thismessage_size_limit), US"message rejected: ", error_rc, spool_data_file, header_list); @@ -3335,7 +3341,7 @@ if (fflush(spool_data_file) == EOF || ferror(spool_data_file) || else { - fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET); give_local_error(ERRMESS_IOERR, msg, US"", error_rc, spool_data_file, header_list); /* Does not return */ @@ -3376,7 +3382,7 @@ if (extract_recip && (bad_addresses || recipients_count == 0)) log_write(0, LOG_MAIN|LOG_PANIC, "%s %s found in headers", message_id, bad_addresses ? "bad addresses" : "no recipients"); - fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET); /* If configured to send errors to the sender, but this fails, force a failure error code. We use a special one for no recipients so that it @@ -3443,7 +3449,7 @@ if (!received_header->text) /* Non-cutthrough case */ /* Set the value of message_body_size for the DATA ACL and for local_scan() */ message_body_size = (fstat(data_fd, &statbuf) == 0)? - statbuf.st_size - SPOOL_DATA_START_OFFSET : -1; + statbuf.st_size - spool_data_start_offset(message_id) : -1; /* If an ACL from any RCPT commands set up any warning headers to add, do so now, before running the DATA ACL. */ @@ -3452,7 +3458,7 @@ if (!received_header->text) /* Non-cutthrough case */ } else message_body_size = (fstat(data_fd, &statbuf) == 0)? - statbuf.st_size - SPOOL_DATA_START_OFFSET : -1; + statbuf.st_size - spool_data_start_offset(message_id) : -1; /* If an ACL is specified for checking things at this stage of reception of a message, run it, unless all the recipients were removed by "discard" in earlier @@ -3731,7 +3737,7 @@ else /* Does not return */ else { - fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET); give_local_error(ERRMESS_LOCAL_ACL, user_msg, US"message rejected by non-SMTP ACL: ", error_rc, spool_data_file, header_list); @@ -3763,7 +3769,7 @@ version supplied with Exim always accepts, but this is a hook for sysadmins to supply their own checking code. The local_scan() function is run even when all the recipients have been discarded. */ -lseek(data_fd, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); +lseek(data_fd, (long int)spool_data_start_offset(message_id), SEEK_SET); /* Arrange to catch crashes in local_scan(), so that the -D file gets deleted, and the incident gets logged. */ @@ -3924,7 +3930,7 @@ else /* Does not return */ else { - fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET); give_local_error(ERRMESS_LOCAL_SCAN, errmsg, US"message rejected by local scan code: ", error_rc, spool_data_file, header_list); @@ -3947,7 +3953,7 @@ f.deliver_firsttime = TRUE; #ifdef EXPERIMENTAL_BRIGHTMAIL if (bmi_run == 1) { /* rewind data file */ - lseek(data_fd, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + lseek(data_fd, (long int)spool_data_start_offset(message_id), SEEK_SET); bmi_verdicts = bmi_process_message(header_list, data_fd); } #endif @@ -3998,7 +4004,7 @@ else } else { - fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET); give_local_error(ERRMESS_IOERR, errmsg, US"", error_rc, spool_data_file, header_list); /* Does not return */ @@ -4028,7 +4034,7 @@ if (fflush(spool_data_file)) } else { - fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET); give_local_error(ERRMESS_IOERR, errmsg, US"", error_rc, spool_data_file, header_list); /* Does not return */ @@ -4036,7 +4042,7 @@ if (fflush(spool_data_file)) } fstat(data_fd, &statbuf); -msg_size += statbuf.st_size - SPOOL_DATA_START_OFFSET + 1; +msg_size += statbuf.st_size - spool_data_start_offset(message_id) + 1; /* Generate a "message received" log entry. We do this by building up a dynamic string as required. We log the arrival of a new message while the diff --git a/src/src/spool_in.c b/src/src/spool_in.c index 1291197de..1fcff954f 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -36,18 +36,18 @@ Side effect: message_subdir is set for the (possibly split) spool directory */ int -spool_open_datafile(uschar *id) +spool_open_datafile(uschar * id) { struct stat statbuf; flock_t lock_data; int fd; -/* If split_spool_directory is set, first look for the file in the appropriate -sub-directory of the input directory. If it is not found there, try the input -directory itself, to pick up leftovers from before the splitting. If split_ -spool_directory is not set, first look in the main input directory. If it is -not found there, try the split sub-directory, in case it is left over from a -splitting state. */ +/* If split_spool_directory is set (handled by set_subdir_str()), first look for +the file in the appropriate sub-directory of the input directory. If it is not +found there, try the input directory itself, to pick up leftovers from before +the splitting. If split_ spool_directory is not set, first look in the main +input directory. If it is not found there, try the split sub-directory, in case +it is left over from a splitting state. */ for (int i = 0; i < 2; i++) { @@ -59,10 +59,10 @@ for (int i = 0; i < 2; i++) DEBUG(D_deliver) debug_printf_indent("Trying spool file %s\n", fname); /* We protect against symlink attacks both in not propagating the - * file-descriptor to other processes as we exec, and also ensuring that we - * don't even open symlinks. - * No -D file inside the spool area should be a symlink. - */ + file-descriptor to other processes as we exec, and also ensuring that we + don't even open symlinks. + No -D file inside the spool area should be a symlink. */ + if ((fd = Uopen(fname, EXIM_CLOEXEC | EXIM_NOFOLLOW | O_RDWR | O_APPEND, 0)) >= 0) break; @@ -75,6 +75,11 @@ for (int i = 0; i < 2; i++) *queue_name ? US" Q=" : US"", *queue_name ? queue_name : US"", id); + else DEBUG(D_deliver) + debug_printf("Spool%s%s file %s-D not found\n", + *queue_name ? US" Q=" : US"", + *queue_name ? queue_name : US"", + id); } else log_write(0, LOG_MAIN, "Spool error for %s: %s", fname, strerror(errno)); @@ -97,7 +102,7 @@ what it locks. */ lock_data.l_type = F_WRLCK; lock_data.l_whence = SEEK_SET; lock_data.l_start = 0; -lock_data.l_len = SPOOL_DATA_START_OFFSET; +lock_data.l_len = spool_data_start_offset(id); if (fcntl(fd, F_SETLK, &lock_data) < 0) { @@ -114,7 +119,7 @@ in the count, but add one for the newline before the data. */ if (fstat(fd, &statbuf) == 0) { - message_body_size = statbuf.st_size - SPOOL_DATA_START_OFFSET; + message_body_size = statbuf.st_size - spool_data_start_offset(id); message_size = message_body_size + 1; } @@ -369,6 +374,7 @@ int n; int rcount = 0; long int uid, gid; BOOL inheader = FALSE; +const uschar * where; /* Reset all the global variables to their default values. However, there is one exception. DO NOT change the default value of dont_deliver, because it may @@ -400,9 +406,15 @@ DEBUG(D_deliver) debug_printf_indent("reading spool file %s\n", name); /* The first line of a spool file contains the message id followed by -H (i.e. the file name), in order to make the file self-identifying. */ +where = US"first line read"; if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) goto SPOOL_READ_ERROR; -if (Ustrlen(big_buffer) != MESSAGE_ID_LENGTH + 3 || - Ustrncmp(big_buffer, name, MESSAGE_ID_LENGTH + 2) != 0) +where = US"first line length"; +if ( ( Ustrlen(big_buffer) != MESSAGE_ID_LENGTH + 3 + && Ustrlen(big_buffer) != MESSAGE_ID_LENGTH_OLD + 3 + ) + || ( Ustrncmp(big_buffer, name, MESSAGE_ID_LENGTH + 2) != 0 + && Ustrncmp(big_buffer, name, MESSAGE_ID_LENGTH_OLD + 2) != 0 + ) ) goto SPOOL_FORMAT_ERROR; /* The next three lines in the header file are in a fixed format. The first @@ -412,20 +424,24 @@ negative uids and gids. The second contains the mail address of the message's sender, enclosed in <>. The third contains the time the message was received, and the number of warning messages for delivery delays that have been sent. */ +where = US"2nd line read"; if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) goto SPOOL_READ_ERROR; { uschar *p = big_buffer + Ustrlen(big_buffer); while (p > big_buffer && isspace(p[-1])) p--; *p = 0; + where = US"2nd line fmt 1"; if (!isdigit(p[-1])) goto SPOOL_FORMAT_ERROR; while (p > big_buffer && (isdigit(p[-1]) || '-' == p[-1])) p--; gid = Uatoi(p); + where = US"2nd line fmt 2"; if (p <= big_buffer || *(--p) != ' ') goto SPOOL_FORMAT_ERROR; *p = 0; if (!isdigit(p[-1])) goto SPOOL_FORMAT_ERROR; while (p > big_buffer && (isdigit(p[-1]) || '-' == p[-1])) p--; uid = Uatoi(p); + where = US"2nd line fmt 3"; if (p <= big_buffer || *(--p) != ' ') goto SPOOL_FORMAT_ERROR; *p = 0; } @@ -434,7 +450,7 @@ originator_login = string_copy(big_buffer); originator_uid = (uid_t)uid; originator_gid = (gid_t)gid; -/* envelope from */ +where = US"envelope from"; if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) goto SPOOL_READ_ERROR; n = Ustrlen(big_buffer); if (n < 3 || big_buffer[0] != '<' || big_buffer[n-2] != '>') @@ -444,7 +460,7 @@ sender_address = store_get(n-2, GET_TAINTED); Ustrncpy(sender_address, big_buffer+1, n-3); sender_address[n-3] = 0; -/* time */ +where = US"time"; if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) goto SPOOL_READ_ERROR; if (sscanf(CS big_buffer, TIME_T_FMT " %d", &received_time.tv_sec, &warning_count) != 2) goto SPOOL_FORMAT_ERROR; @@ -504,6 +520,7 @@ for (;;) { DEBUG(D_any) debug_printf("Unrecognised quoter %.*s\n", (int)(s - var), var+1); + where = NULL; goto SPOOL_FORMAT_ERROR; } proto_mem = store_get_quoted(1, GET_TAINTED, idx); @@ -529,7 +546,8 @@ for (;;) int count; tree_node *node; endptr = Ustrchr(var + 5, ' '); - if (endptr == NULL) goto SPOOL_FORMAT_ERROR; + where = US"-aclXn"; + if (!endptr) goto SPOOL_FORMAT_ERROR; name = string_sprintf("%c%.*s", var[3], (int)(endptr - var - 5), var + 5); if (sscanf(CS endptr, " %d", &count) != 1) goto SPOOL_FORMAT_ERROR; @@ -563,6 +581,7 @@ for (;;) unsigned index, count; uschar name[20]; /* Need plenty of space for %u format */ tree_node * node; + where = US"-acl (old)"; if ( sscanf(CS var + 4, "%u %u", &index, &count) != 2 || index >= 20 || count > 16384 /* arbitrary limit on variable size */ @@ -777,6 +796,7 @@ DEBUG(D_deliver) /* We now have the tree of addresses NOT to deliver to, or a line containing "XX", indicating no tree. */ +where = US"nondeliver"; if (Ustrncmp(big_buffer, "XX\n", 3) != 0 && !read_nonrecipients_tree(&tree_nonrecipients, fp, big_buffer, big_buffer_size)) goto SPOOL_FORMAT_ERROR; @@ -789,6 +809,7 @@ DEBUG(D_deliver) debug_print_tree("Non-recipients", tree_nonrecipients); buffer. It contains the count of recipients which follow on separate lines. Apply an arbitrary sanity check.*/ +where = US"rcpt cnt"; if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) goto SPOOL_READ_ERROR; if (sscanf(CS big_buffer, "%d", &rcount) != 1 || rcount > 16384) goto SPOOL_FORMAT_ERROR; @@ -804,6 +825,7 @@ recipients_list = store_get(rcount * sizeof(recipient_item), GET_UNTAINTED); the Coverity error on recipients_count */ /* coverity[tainted_data] */ +where = US"recipient"; for (recipients_count = 0; recipients_count < rcount; recipients_count++) { int nn; @@ -955,12 +977,13 @@ always, in order to check on the format of the file, but only create a header list if requested to do so. */ inheader = TRUE; +where = US"headers"; if (Ufgets(big_buffer, big_buffer_size, fp) == NULL) goto SPOOL_READ_ERROR; if (big_buffer[0] != '\n') goto SPOOL_FORMAT_ERROR; while ((n = fgetc(fp)) != EOF) { - header_line *h; + header_line * h; uschar flag[4]; int i; @@ -1038,7 +1061,8 @@ if (errno != 0) SPOOL_FORMAT_ERROR: #ifndef COMPILE_UTILITY -DEBUG(D_any) debug_printf("Format error in spool file %s\n", name); +DEBUG(D_any) debug_printf("Format error in spool file %s%s%s\n", name, + where ? ": " : "", where ? where : US""); #endif /* COMPILE_UTILITY */ fclose(fp); @@ -1057,15 +1081,14 @@ We assume that message_subdir is already set. uschar * spool_sender_from_msgid(const uschar * id) { -uschar * name = string_sprintf("%s-H", id); FILE * fp; int n; uschar * yield = NULL; -if (!(fp = Ufopen(spool_fname(US"input", message_subdir, name, US""), "rb"))) +if (!(fp = Ufopen(spool_fname(US"input", message_subdir, id, US"-H"), "rb"))) return NULL; -DEBUG(D_deliver) debug_printf_indent("reading spool file %s\n", name); +DEBUG(D_deliver) debug_printf_indent("reading spool file %s-H\n", id); /* Skip the line with the copy of the filename, then the line with login/uid/gid. Read the next line, which should be the envelope sender. diff --git a/src/src/spool_mbox.c b/src/src/spool_mbox.c index 7ea565a58..0a2a1d27d 100644 --- a/src/src/spool_mbox.c +++ b/src/src/spool_mbox.c @@ -99,6 +99,7 @@ if (!spool_mbox_ok) } /* End headers */ + if (fwrite("\n", 1, 1, mbox_file) != 1) { log_write(0, LOG_MAIN|LOG_PANIC, "Error/short write while writing \ @@ -109,20 +110,18 @@ if (!spool_mbox_ok) /* Copy body file. If the main receive still has it open then it is holding a lock, and we must not close it (which releases the lock), so just use the global file handle. */ + if (source_file_override) l_data_file = Ufopen(source_file_override, "rb"); else if (spool_data_file) l_data_file = spool_data_file; else - { - message_subdir[1] = '\0'; for (int i = 0; i < 2; i++) { set_subdir_str(message_subdir, message_id, i); temp_string = spool_fname(US"input", message_subdir, message_id, US"-D"); if ((l_data_file = Ufopen(temp_string, "rb"))) break; } - } if (!l_data_file) { @@ -141,7 +140,7 @@ if (!spool_mbox_ok) explicitly, because the one in the file is parted of the locked area. */ if (!source_file_override) - (void)fseek(l_data_file, SPOOL_DATA_START_OFFSET, SEEK_SET); + (void)fseek(l_data_file, spool_data_start_offset(message_id), SEEK_SET); do { diff --git a/src/src/spool_out.c b/src/src/spool_out.c index 9c5e8eb33..e0650bb36 100644 --- a/src/src/spool_out.c +++ b/src/src/spool_out.c @@ -143,7 +143,7 @@ be open and locked, thus preventing any other exim process from working on this message. Argument: - id the message id + id the message id (used for the eventual filename; the *content* uses the global. Unclear why.) where SW_RECEIVING, SW_DELIVERING, or SW_MODIFYING errmsg where to put an error message; if NULL, panic-die on error @@ -152,16 +152,13 @@ Returns: the size of the header texts on success; */ int -spool_write_header(uschar *id, int where, uschar **errmsg) +spool_write_header(uschar * id, int where, uschar ** errmsg) { -int fd; -int size_correction; +int fd, size_correction; FILE * fp; struct stat statbuf; -uschar * tname; uschar * fname; - -tname = spool_fname(US"input", message_subdir, US"hdr.", message_id); +uschar * tname = spool_fname(US"input", message_subdir, US"hdr.", message_id); if ((fd = spool_open_temp(tname)) < 0) return spool_write_error(where, errmsg, US"open", NULL, NULL); @@ -460,8 +457,8 @@ Returns: TRUE if all went well */ static BOOL -make_link(uschar *dir, uschar * dq, uschar *subdir, uschar *id, uschar *suffix, - uschar *from, uschar *to, BOOL noentok) +make_link(uschar * dir, uschar * dq, uschar * subdir, uschar * id, uschar * suffix, + uschar * from, uschar * to, BOOL noentok) { uschar * fname = spool_fname(string_sprintf("%s%s", from, dir), subdir, id, suffix); uschar * tname = spool_q_fname(string_sprintf("%s%s", to, dir), dq, subdir, id, suffix); @@ -497,7 +494,7 @@ Returns: TRUE if all went well */ static BOOL -break_link(uschar *dir, uschar *subdir, uschar *id, uschar *suffix, uschar *from, +break_link(uschar * dir, uschar * subdir, uschar * id, uschar * suffix, uschar * from, BOOL noentok) { uschar * fname = spool_fname(string_sprintf("%s%s", from, dir), subdir, id, suffix); @@ -531,7 +528,7 @@ Returns: TRUE if all is well */ BOOL -spool_move_message(uschar *id, uschar *subdir, uschar *from, uschar *to) +spool_move_message(uschar * id, uschar * subdir, uschar * from, uschar * to) { uschar * dest_qname = queue_name_dest ? queue_name_dest : queue_name; diff --git a/src/src/string.c b/src/src/string.c index b30673c04..0ea98d47d 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -190,26 +190,44 @@ return buffer; *************************************************/ /* Convert a long integer into an ASCII base 62 string. For Cygwin the value of -BASE_62 is actually 36. Always return exactly 6 characters plus zero, in a -static area. +BASE_62 is actually 36. Always return exactly 6 characters plus a NUL, in a +static area. This is enough for a 32b input, for 62 (for 64b we would want 11+nul); +but with 36 we lose half the input range of a 32b input. Argument: a long integer Returns: pointer to base 62 string */ uschar * -string_base62(unsigned long int value) +string_base62_32(unsigned long int value) { static uschar yield[7]; -uschar *p = yield + sizeof(yield) - 1; +uschar * p = yield + sizeof(yield) - 1; *p = 0; while (p > yield) { - *(--p) = base62_chars[value % BASE_62]; + *--p = base62_chars[value % BASE_62]; value /= BASE_62; } return yield; } + +uschar * +string_base62_64(unsigned long int value) +{ +static uschar yield[12]; +uschar * p = yield + sizeof(yield) - 1; +*p = '\0'; +while (p > yield) + if (value) + { + *--p = base62_chars[value % BASE_62]; + value /= BASE_62; + } + else + *--p = '0'; +return yield; +} #endif /* COMPILE_UTILITY */ diff --git a/src/src/transport.c b/src/src/transport.c index 80ba1eece..c125cc7c3 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -1043,7 +1043,7 @@ if (tctx->options & topt_use_bdat) if (!(tctx->options & topt_no_body)) { if ((fsize = lseek(deliver_datafile, 0, SEEK_END)) < 0) return FALSE; - fsize -= SPOOL_DATA_START_OFFSET; + fsize -= spool_data_start_offset(message_id); if (size_limit > 0 && fsize > size_limit) fsize = size_limit; size = hsize + fsize; @@ -1101,7 +1101,7 @@ if ( f.spool_file_wireformat ) { ssize_t copied = 0; - off_t offset = SPOOL_DATA_START_OFFSET; + off_t offset = spool_data_start_offset(message_id); /* Write out any header data in the buffer */ @@ -1139,7 +1139,7 @@ if (!(tctx->options & topt_no_body)) nl_check_length = abs(nl_check_length); nl_partial_match = 0; - if (lseek(deliver_datafile, SPOOL_DATA_START_OFFSET, SEEK_SET) < 0) + if (lseek(deliver_datafile, spool_data_start_offset(message_id), SEEK_SET) < 0) return FALSE; while ( (len = MIN(DELIVER_IN_BUFFER_SIZE, size)) > 0 && (len = read(deliver_datafile, deliver_in_buffer, len)) > 0) @@ -1497,12 +1497,19 @@ Returns: nothing */ void -transport_update_waiting(host_item *hostlist, uschar *tpname) +transport_update_waiting(host_item * hostlist, uschar * tpname) { const uschar *prevname = US""; open_db dbblock; open_db *dbm_file; +if (!is_new_message_id(message_id)) + { + DEBUG(D_transport) debug_printf("message_id %s is not new format; " + "skipping wait-%s database update\n", tpname); + return; + } + DEBUG(D_transport) debug_printf("updating wait-%s database\n", tpname); /* Open the database for this transport */ @@ -1517,7 +1524,7 @@ that the message id is in each host record. */ for (host_item * host = hostlist; host; host = host->next) { BOOL already = FALSE; - dbdata_wait *host_record; + dbdata_wait * host_record; int host_length; uschar buffer[256]; @@ -1543,8 +1550,27 @@ for (host_item * host = hostlist; host; host = host->next) for (uschar * s = host_record->text; s < host_record->text + host_length; s += MESSAGE_ID_LENGTH) + { + /* If any ID is seen which is not new-format, wipe the record and + any continuations */ + + if (!is_new_message_id(s)) + { + DEBUG(D_hints_lookup) + debug_printf_indent("NOTE: old or corrupt message-id found in wait=%.200s" + " hints DB; deleting records for %s\n", tpname, host->name); + + (void) dbfn_delete(dbm_file, host->name); + for (int i = host_record->sequence - 1; i >= 0; i--) + (void) dbfn_delete(dbm_file, + (sprintf(CS buffer, "%.200s:%d", host->name, i), buffer)); + + host_record->count = host_record->sequence = 0; + break; + } if (Ustrncmp(s, message_id, MESSAGE_ID_LENGTH) == 0) { already = TRUE; break; } + } /* If we haven't found this message in the main record, search any continuation records that exist. */ @@ -1652,13 +1678,14 @@ typedef struct msgq_s } msgq_t; BOOL -transport_check_waiting(const uschar *transport_name, const uschar *hostname, - int local_message_max, uschar *new_message_id, oicf oicf_func, void *oicf_data) +transport_check_waiting(const uschar * transport_name, const uschar * hostname, + int local_message_max, uschar * new_message_id, + oicf oicf_func, void * oicf_data) { -dbdata_wait *host_record; +dbdata_wait * host_record; int host_length; open_db dbblock; -open_db *dbm_file; +open_db * dbm_file; int i; struct stat statbuf; @@ -1735,6 +1762,22 @@ while (1) for (i = 0; i < host_record->count; ++i) { + /* If any ID is seen which is not new-format, wipe the record and + any continuations */ + + if (!is_new_message_id(host_record->text + (i * MESSAGE_ID_LENGTH))) + { + uschar buffer[256]; + DEBUG(D_hints_lookup) + debug_printf_indent("NOTE: old or corrupt message-id found in wait=%.200s" + " hints DB; deleting records for %s\n", transport_name, hostname); + (void) dbfn_delete(dbm_file, hostname); + for (int i = host_record->sequence - 1; i >= 0; i--) + (void) dbfn_delete(dbm_file, + (sprintf(CS buffer, "%.200s:%d", hostname, i), buffer)); + dbfn_close(dbm_file); + goto retfalse; + } msgq[i].bKeep = TRUE; Ustrncpy_nt(msgq[i].message_id, host_record->text + (i * MESSAGE_ID_LENGTH), @@ -1885,8 +1928,8 @@ return FALSE; /* Just the regain-root-privilege exec portion */ void -transport_do_pass_socket(const uschar *transport_name, const uschar *hostname, - const uschar *hostaddress, uschar *id, int socket_fd) +transport_do_pass_socket(const uschar * transport_name, const uschar * hostname, + const uschar * hostaddress, uschar * id, int socket_fd) { int i = 13; const uschar **argv; diff --git a/src/src/transports/appendfile.c b/src/src/transports/appendfile.c index c39c07c9f..e49f46be4 100644 --- a/src/src/transports/appendfile.c +++ b/src/src/transports/appendfile.c @@ -2512,9 +2512,9 @@ else else { - FILE *env_file; + FILE * env_file; mailstore_basename = string_sprintf("%s/%s-%s", path, message_id, - string_base62((long int)getpid())); + string_base62_64((long int)getpid())); DEBUG(D_transport) debug_printf("delivering in mailstore format in %s\n", path); commit 2b3ac4f13c815ad1b0d59165f2f441f8e4cc6b99 Author: Jeremy Harris Date: Thu Jul 6 21:43:50 2023 +0100 Explicit log of failiing bind addr & port diff --git a/src/src/deliver.c b/src/src/deliver.c index 47368a860..8f0f350d7 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -3353,8 +3353,8 @@ while (!done) pipeheader[PIPE_HEADER_SIZE] = '\0'; DEBUG(D_deliver) - debug_printf("got %ld bytes (pipeheader) from transport process %d\n", - (long) got, pid); + debug_printf("got %ld bytes (pipeheader) '%c' from transport process %d\n", + (long) got, *id, pid); { /* If we can't decode the pipeheader, the subprocess seems to have a @@ -3469,7 +3469,7 @@ while (!done) /* Put the amount of data written into the parlist block */ - case 'S': + case 'S': /* Size */ memcpy(&(p->transport_count), ptr, sizeof(transport_count)); ptr += sizeof(transport_count); break; @@ -3559,7 +3559,7 @@ while (!done) if (*subid > '1') setflag(addr, af_tcp_fastopen_data); break; - case 'D': + case 'D': /* DSN */ if (!addr) goto ADDR_MISMATCH; memcpy(&(addr->dsn_aware), ptr, sizeof(addr->dsn_aware)); ptr += sizeof(addr->dsn_aware); diff --git a/src/src/ip.c b/src/src/ip.c index b50130be3..6ff8fe626 100644 --- a/src/src/ip.c +++ b/src/src/ip.c @@ -161,7 +161,10 @@ ip_bind(int sock, int af, uschar *address, int port) { union sockaddr_46 sin; int s_len = ip_addr(&sin, af, address, port); -return bind(sock, (struct sockaddr *)&sin, s_len); +int rc = bind(sock, (struct sockaddr *)&sin, s_len); +if (rc < 0) + log_write(0, LOG_MAIN, "bind of [%s]:%d failed", address, port); +return rc; } commit 00392be0e7cfb5c6c6ce173ff31d81ab2a2e8779 Author: Jeremy Harris Date: Fri Jul 7 00:40:43 2023 +0100 Support gring as a first-class conversion specifier in internal string-formatting diff --git a/src/src/auths/heimdal_gssapi.c b/src/src/auths/heimdal_gssapi.c index 1336d0fab..7a74d5be5 100644 --- a/src/src/auths/heimdal_gssapi.c +++ b/src/src/auths/heimdal_gssapi.c @@ -565,9 +565,8 @@ do { if (!auth_defer_msg) auth_defer_msg = string_copy(US status_string.value); - HDEBUG(D_auth) debug_printf("heimdal %s: %.*s\n", - string_from_gstring(g), (int)status_string.length, - CS status_string.value); + HDEBUG(D_auth) debug_printf("heimdal %Y: %.*s\n", + g, (int)status_string.length, CS status_string.value); gss_release_buffer(&min_stat, &status_string); } while (msgcontext != 0); diff --git a/src/src/daemon.c b/src/src/daemon.c index ea7db0f25..f6867b882 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -254,8 +254,6 @@ if (LOGGING(incoming_interface)) whofrom = string_fmt_append(whofrom, " I=[%s]:%d", interface_address, interface_port); -(void) string_from_gstring(whofrom); /* Terminate the newly-built string */ - /* Check maximum number of connections. We do not check for reserved connections or unacceptable hosts here. That is done in the subprocess because it might take some time. */ @@ -267,8 +265,8 @@ if (smtp_accept_max > 0 && smtp_accept_count >= smtp_accept_max) smtp_printf("421 Too many concurrent SMTP connections; " "please try again later.\r\n", FALSE); log_write(L_connection_reject, - LOG_MAIN, "Connection from %s refused: too many connections", - whofrom->s); + LOG_MAIN, "Connection from %Y refused: too many connections", + whofrom); goto ERROR_RETURN; } @@ -286,8 +284,8 @@ if (smtp_load_reserve >= 0) (double)load_average/1000.0); smtp_printf("421 Too much load; please try again later.\r\n", FALSE); log_write(L_connection_reject, - LOG_MAIN, "Connection from %s refused: load average = %.2f", - whofrom->s, (double)load_average/1000.0); + LOG_MAIN, "Connection from %Y refused: load average = %.2f", + whofrom, (double)load_average/1000.0); goto ERROR_RETURN; } } @@ -307,7 +305,7 @@ if (smtp_accept_max_per_host) { if (!f.expand_string_forcedfail) log_write(0, LOG_MAIN|LOG_PANIC, "expansion of smtp_accept_max_per_host " - "failed for %s: %s", whofrom->s, expand_string_message); + "failed for %Y: %s", whofrom, expand_string_message); } /* For speed, interpret a decimal number inline here */ else @@ -317,7 +315,7 @@ if (smtp_accept_max_per_host) max_for_this_host = max_for_this_host * 10 + *s++ - '0'; if (*s) log_write(0, LOG_MAIN|LOG_PANIC, "expansion of smtp_accept_max_per_host " - "for %s contains non-digit: %s", whofrom->s, expanded); + "for %Y contains non-digit: %s", whofrom, expanded); } } @@ -355,8 +353,8 @@ if (max_for_this_host > 0 && smtp_accept_count >= max_for_this_host) smtp_printf("421 Too many concurrent SMTP connections " "from this IP address; please try again later.\r\n", FALSE); log_write(L_connection_reject, - LOG_MAIN, "Connection from %s refused: too many connections " - "from that IP address", whofrom->s); + LOG_MAIN, "Connection from %Y refused: too many connections " + "from that IP address", whofrom); search_tidyup(); goto ERROR_RETURN; } @@ -382,8 +380,8 @@ if (LOGGING(smtp_connection)) if (list && verify_check_host(&list) == OK) save_log_selector &= ~L_smtp_connection; else - log_write(L_smtp_connection, LOG_MAIN, "SMTP connection from %s " - "(TCP/IP connection count = %d)", whofrom->s, smtp_accept_count + 1); + log_write(L_smtp_connection, LOG_MAIN, "SMTP connection from %Y " + "(TCP/IP connection count = %d)", whofrom, smtp_accept_count + 1); } /* Now we can fork the accepting process; do a lookup tidy, just in case any diff --git a/src/src/dcc.c b/src/src/dcc.c index 98f978fa9..e7a932426 100644 --- a/src/src/dcc.c +++ b/src/src/dcc.c @@ -237,11 +237,11 @@ for (int i = 0; i < recipients_count; i++) } /* send a blank line between options and message */ dcc_headers = string_catn(dcc_headers, US"\n", 1); + /* Now we send the input buffer */ -(void) string_from_gstring(dcc_headers); DEBUG(D_acl) - debug_printf("DCC: ***********************************\nDCC: Sending options:\n%s" - "DCC: ***********************************\n", dcc_headers->s); + debug_printf("DCC: ***********************************\nDCC: Sending options:\n%Y" + "DCC: ***********************************\n", dcc_headers); if (flushbuffer(sockfd, dcc_headers) != 0) { (void)fclose(data_file); @@ -259,10 +259,9 @@ while((mail_headers=mail_headers->next)) /* a blank line separates header from body */ sendbuf = string_catn(sendbuf, US"\r\n", 2); -(void) string_from_gstring(sendbuf); gstring_release_unused(sendbuf); DEBUG(D_acl) - debug_printf("%sDCC: ***********************************\n", sendbuf->s); + debug_printf("%YDCC: ***********************************\n", sendbuf); if (flushbuffer(sockfd, sendbuf) != 0) { (void)fclose(data_file); @@ -449,23 +448,23 @@ dcc_header_str = string_catn(dcc_header_str, US"\n", 1); /* Now let's sum up what we've got. */ DEBUG(D_acl) debug_printf("\nDCC: --------------------------\nDCC: Overall result = %d\n" - "DCC: X-DCC header: %sReturn message: %s\nDCC: dcc_result: %s\n", - retval, dcc_header_str->s, dcc_return_text, dcc_result); + "DCC: X-DCC header: %YReturn message: %s\nDCC: dcc_result: %s\n", + retval, dcc_header_str, dcc_return_text, dcc_result); /* We only add the X-DCC header if it starts with X-DCC */ if(!(Ustrncmp(dcc_header_str->s, "X-DCC", 5))) { - dcc_header = dcc_header_str->s; + dcc_header = string_from_gstring(dcc_header_str); if(dcc_direct_add_header) { - header_add(' ' , "%s", dcc_header_str->s); + header_add(' ' , "%s", dcc_header); /* since the MIME ACL already writes the .eml file to disk without DCC Header we've to erase it */ unspool_mbox(); } } else DEBUG(D_acl) - debug_printf("DCC: Wrong format of the X-DCC header: %.*s\n", dcc_header_str->ptr, dcc_header_str->s); + debug_printf("DCC: Wrong format of the X-DCC header: %Y\n", dcc_header_str); /* check if we should add additional headers passed in acl_m_dcc_add_header */ if (dcc_direct_add_header) @@ -477,7 +476,7 @@ if (dcc_direct_add_header) dcc_xtra_hdrs = string_catn(dcc_xtra_hdrs, US"\n", 1); header_add(' ', "%s", string_from_gstring(dcc_xtra_hdrs)); DEBUG(D_acl) - debug_printf("DCC: adding additional headers in $acl_m_dcc_add_header: %.*s", dcc_xtra_hdrs->ptr, dcc_xtra_hdrs->s); + debug_printf("DCC: adding additional headers in $acl_m_dcc_add_header: %Y", dcc_xtra_hdrs); } } diff --git a/src/src/debug.c b/src/src/debug.c index 44ad763e1..dac738470 100644 --- a/src/src/debug.c +++ b/src/src/debug.c @@ -439,7 +439,7 @@ if (fstat(fd, &s) == 0 && (s.st_mode & S_IFMT) == S_IFSOCK) : string_fmt_append(g, " proto %d", val); } #endif - debug_printf_indent(" socket: %s\n", string_from_gstring(g)); + debug_printf_indent(" socket: %Y\n", g); } else debug_printf_indent(" fd st_mode 0%o\n", s.st_mode); diff --git a/src/src/deliver.c b/src/src/deliver.c index 8f0f350d7..bea38c5d1 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -723,7 +723,7 @@ child_done(address_item * addr, const uschar * now) { while (addr->parent) { - address_item *aa; + address_item * aa; addr = addr->parent; if (--addr->child_count > 0) return; /* Incomplete parent */ @@ -1278,7 +1278,7 @@ if (LOGGING(deliver_time)) /* string_cat() always leaves room for the terminator. Release the store we used to build the line after writing it. */ -log_write(0, flags, "%s", string_from_gstring(g)); +log_write(0, flags, "%Y", g); #ifndef DISABLE_EVENT if (!msg) msg_event_raise(US"msg:delivery", addr); @@ -1337,25 +1337,21 @@ if (LOGGING(deliver_time)) if (addr->message) g = string_append(g, 2, US": ", addr->message); - { - const uschar * s = string_from_gstring(g); +/* Log the deferment in the message log, but don't clutter it +up with retry-time defers after the first delivery attempt. */ - /* Log the deferment in the message log, but don't clutter it - up with retry-time defers after the first delivery attempt. */ +if (f.deliver_firsttime || addr->basic_errno > ERRNO_RETRY_BASE) + deliver_msglog("%s %.*s\n", now, g->ptr, g->s); - if (f.deliver_firsttime || addr->basic_errno > ERRNO_RETRY_BASE) - deliver_msglog("%s %s\n", now, s); +/* Write the main log and reset the store. +For errors of the type "retry time not reached" (also remotes skipped +on queue run), logging is controlled by L_retry_defer. Note that this kind +of error number is negative, and all the retry ones are less than any +others. */ - /* Write the main log and reset the store. - For errors of the type "retry time not reached" (also remotes skipped - on queue run), logging is controlled by L_retry_defer. Note that this kind - of error number is negative, and all the retry ones are less than any - others. */ - - log_write(addr->basic_errno <= ERRNO_RETRY_BASE ? L_retry_defer : 0, logflags, - "== %s", s); - } +log_write(addr->basic_errno <= ERRNO_RETRY_BASE ? L_retry_defer : 0, logflags, + "== %Y", g); store_reset(reset_point); return; @@ -1421,16 +1417,12 @@ if (LOGGING(deliver_time)) /* Do the logging. For the message log, "routing failed" for those cases, just to make it clearer. */ - { - const uschar * s = string_from_gstring(g); - - if (driver_kind) - deliver_msglog("%s %s failed for %s\n", now, driver_kind, s); - else - deliver_msglog("%s %s\n", now, s); +if (driver_kind) + deliver_msglog("%s %s failed for %.*s\n", now, driver_kind, g->ptr, g->s); +else + deliver_msglog("%s %.*s\n", now, g->ptr, g->s); - log_write(0, LOG_MAIN, "** %s", s); - } +log_write(0, LOG_MAIN, "** %Y", g); store_reset(reset_point); return; @@ -5989,7 +5981,7 @@ wording. */ if (rc != 0) { - uschar *s = US""; + uschar * s = US""; if (now - received_time.tv_sec < retry_maximum_timeout && !addr_defer) { addr_defer = (address_item *)(+1); diff --git a/src/src/dkim.c b/src/src/dkim.c index 4c19f752f..068b802e0 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -289,7 +289,7 @@ else break; } -log_write(0, LOG_MAIN, "%s", string_from_gstring(logmsg)); +log_write(0, LOG_MAIN, "%Y", logmsg); return; } diff --git a/src/src/dmarc.c b/src/src/dmarc.c index 555e3a72b..409d8a47d 100644 --- a/src/src/dmarc.c +++ b/src/src/dmarc.c @@ -306,7 +306,7 @@ DEBUG(D_receive) if (host_checking || f.running_in_test_harness) { DEBUG(D_receive) - debug_printf("DMARC history data for debugging:\n%s", string_from_gstring(g)); + debug_printf("DMARC history data for debugging:\n%Y", g); } else { diff --git a/src/src/expand.c b/src/src/expand.c index de00c7254..55c53957e 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -3957,10 +3957,9 @@ if (Ustrlen(key) > 64) hash_source = string_catn(NULL, key_num, 1); hash_source = string_catn(hash_source, daystamp, 3); hash_source = string_cat(hash_source, address); -(void) string_from_gstring(hash_source); DEBUG(D_expand) - debug_printf_indent("prvs: hash source is '%s'\n", hash_source->s); + debug_printf_indent("prvs: hash source is '%Y'\n", hash_source); memset(innerkey, 0x36, 64); memset(outerkey, 0x5c, 64); @@ -7993,7 +7992,7 @@ NOT_ITEM: ; goto EXPAND_FAILED; } yield = string_cat(yield, s); - DEBUG(D_expand) debug_printf_indent("yield: '%s'\n", string_from_gstring(yield)); + DEBUG(D_expand) debug_printf_indent("yield: '%Y'\n", yield); break; } diff --git a/src/src/header.c b/src/src/header.c index 59a9a13b3..97fa44b4e 100644 --- a/src/src/header.c +++ b/src/src/header.c @@ -110,7 +110,7 @@ gs.ptr = 0; if (!string_vformat(&gs, SVFMT_REBUFFER, format, ap)) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "string too long in header_add: " - "%.100s ...", string_from_gstring(&gs)); + "%.100Y ...", &gs); if (gs.s != buf) store_release_above(buf); gstring_release_unused(&gs); diff --git a/src/src/log.c b/src/src/log.c index 54d2b8027..fac577d5a 100644 --- a/src/src/log.c +++ b/src/src/log.c @@ -958,8 +958,7 @@ DEBUG(D_any|D_v) } va_end(ap); - g = string_catn(g, US"\n", 1); - debug_printf("%s", string_from_gstring(g)); + debug_printf("%Y\n", g); gs.size = LOG_BUFFER_SIZE-2; /* Having used the buffer for debug output, */ gs.ptr = 0; /* reset it for the real use. */ diff --git a/src/src/lookups/pgsql.c b/src/src/lookups/pgsql.c index 1583378d5..5d52f28b1 100644 --- a/src/src/lookups/pgsql.c +++ b/src/src/lookups/pgsql.c @@ -293,7 +293,7 @@ switch(PQresultStatus(pg_result)) result = string_cat(result, US PQcmdTuples(pg_result)); *do_cache = 0; DEBUG(D_lookup) debug_printf_indent("PGSQL: command does not return any data " - "but was successful. Rows affected: %s\n", string_from_gstring(result)); + "but was successful. Rows affected: %Y\n", result); break; case PGRES_TUPLES_OK: diff --git a/src/src/mime.c b/src/src/mime.c index d4c26540a..7d30b5462 100644 --- a/src/src/mime.c +++ b/src/src/mime.c @@ -489,7 +489,7 @@ while ((c = *fname)) val = string_catn(val, fname++, 1); val = string_catn(val, US"?=", 2); -*len = val->ptr; +*len = gstring_length(val); return string_from_gstring(val); } diff --git a/src/src/receive.c b/src/src/receive.c index acb3c40fc..0891a4a8c 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -3915,8 +3915,8 @@ else sender_address[0] == 0 ? US"<>" : sender_address); g = add_host_info_for_log(g); - log_write(0, LOG_MAIN|LOG_REJECT, "%s %srejected by local_scan(): %.256s", - string_from_gstring(g), istemp, string_printing(errmsg)); + log_write(0, LOG_MAIN|LOG_REJECT, "%Y %srejected by local_scan(): %.256s", + g, istemp, string_printing(errmsg)); if (smtp_input) if (!smtp_batched_input) @@ -4271,7 +4271,7 @@ if ( smtp_input && sender_host_address && !f.sender_host_notsocket gstring_reset(g); g = string_cat(g, US"SMTP connection lost after final dot"); g = add_host_info_for_log(g); - log_write(0, LOG_MAIN, "%s", string_from_gstring(g)); + log_write(0, LOG_MAIN, "%Y", g); /* Delete the files for this aborted message. */ @@ -4337,7 +4337,7 @@ if(!smtp_reply) log_write(0, LOG_MAIN | (LOGGING(received_recipients) ? LOG_RECIPIENTS : 0) | (LOGGING(received_sender) ? LOG_SENDER : 0), - "%s", g->s); + "%Y", g); /* Log any control actions taken by an ACL or local_scan(). */ diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index cd759df7b..e6f9808dd 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -2680,13 +2680,13 @@ if (!check_sync()) /* Now output the banner */ /*XXX the ehlo-resp code does its own tls/nontls bit. Maybe subroutine that? */ -smtp_printf("%s", +smtp_printf("%Y", #ifndef DISABLE_PIPE_CONNECT fl.pipe_connect_acceptable && pipeline_connect_sends(), #else FALSE, #endif - string_from_gstring(ss)); + ss); /* Attempt to see if we sent the banner before the last ACK of the 3-way handshake arrived. If so we must have managed a TFO. */ @@ -2735,9 +2735,9 @@ if (++synprot_error_count > smtp_max_synprot_errors) { yield = 1; log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many " - "syntax or protocol errors (last command was \"%s\", %s)", + "syntax or protocol errors (last command was \"%s\", %Y)", host_and_ident(FALSE), string_printing(smtp_cmd_buffer), - string_from_gstring(s_connhad_log(NULL)) + s_connhad_log(NULL) ); } @@ -3195,7 +3195,7 @@ if (code && defaultrespond) va_start(ap, defaultrespond); g = string_vformat(NULL, SVFMT_EXTEND|SVFMT_REBUFFER, CS defaultrespond, ap); va_end(ap); - smtp_printf("%s %s\r\n", FALSE, code, string_from_gstring(g)); + smtp_printf("%s %Y\r\n", FALSE, code, g); } mac_smtp_fflush(); } @@ -3872,9 +3872,9 @@ while (done <= 0) if (++synprot_error_count > smtp_max_synprot_errors) { log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many " - "syntax or protocol errors (last command was \"%s\", %s)", + "syntax or protocol errors (last command was \"%s\", %Y)", host_and_ident(FALSE), string_printing(smtp_cmd_buffer), - string_from_gstring(s_connhad_log(NULL)) + s_connhad_log(NULL) ); done = 1; } diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index 02f1fa438..7f477ed76 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -680,7 +680,6 @@ if (format) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing " "SMTP"); va_end(ap); - string_from_gstring(&gs); if (gs.ptr > outblock->buffersize) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong write_command in outgoing " diff --git a/src/src/string.c b/src/src/string.c index 0ea98d47d..854cf0d34 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -1327,6 +1327,11 @@ If the "extend" flag is false, the string passed in may not be NULL, will not be grown, and is usable in the original place after return. The return value can be NULL to signify overflow. +Field width: decimal digits, or * +Precision: dot, followed by decimal digits or * +Length modifiers: h L l ll z +Conversion specifiers: n d o u x X p f e E g G % c s S T Y D M + Returns the possibly-new (if copy for growth or taint-handling was needed) string, not nul-terminated. */ @@ -1571,6 +1576,14 @@ while (*fp) slen = string_datestamp_length; goto INSERT_STRING; + case 'Y': /* gstring pointer */ + { + gstring * zg = va_arg(ap, gstring *); + s = CS zg->s; + slen = zg->ptr; + goto INSERT_GSTRING; + } + case 's': case 'S': /* Forces *lower* case */ case 'T': /* Forces *upper* case */ @@ -1579,6 +1592,8 @@ while (*fp) if (!s) s = null; slen = Ustrlen(s); + INSERT_GSTRING: /* Coome to from %Y above */ + if (!(flags & SVFMT_TAINT_NOCHK) && is_incompatible(g->s, s)) if (flags & SVFMT_REBUFFER) { diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 76176a64e..c3e2d98e8 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -1135,7 +1135,7 @@ switch (tls_id) DEBUG(D_tls) debug_printf("\n"); if (server_seen_alpn > 1) { - log_write(0, LOG_MAIN, "TLS ALPN (%s) rejected", string_from_gstring(g)); + log_write(0, LOG_MAIN, "TLS ALPN (%Y) rejected", g); DEBUG(D_tls) debug_printf("TLS: too many ALPNs presented in handshake\n"); return GNUTLS_E_NO_APPLICATION_PROTOCOL; } diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index cd715cc18..22c8ea99a 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2404,7 +2404,7 @@ for (int pos = 0, siz; pos < inlen; pos += siz+1) if (pos + 1 + siz > inlen) siz = inlen - pos - 1; g = string_append_listele_n(g, ':', in + pos + 1, siz); } -log_write(0, LOG_MAIN, "TLS ALPN (%s) rejected", string_from_gstring(g)); +log_write(0, LOG_MAIN, "TLS ALPN (%Y) rejected", g); gstring_release_unused(g); return SSL_TLSEXT_ERR_ALERT_FATAL; } diff --git a/src/src/transports/lmtp.c b/src/src/transports/lmtp.c index e04c991ab..776c40e05 100644 --- a/src/src/transports/lmtp.c +++ b/src/src/transports/lmtp.c @@ -241,7 +241,7 @@ if (!string_vformat(&gs, SVFMT_TAINT_NOCHK, CS format, ap)) return FALSE; } va_end(ap); -DEBUG(D_transport|D_v) debug_printf(" LMTP>> %s", string_from_gstring(&gs)); +DEBUG(D_transport|D_v) debug_printf(" LMTP>> %Y", &gs); rc = write(fd, gs.s, gs.ptr); gs.ptr -= 2; string_from_gstring(&gs); /* remove \r\n for debug and error message */ if (rc > 0) return TRUE; diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 926e77df4..c502d7365 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -626,8 +626,8 @@ if (suffix) else message = string_fmt_append(message, " %s", exim_errstr(basic_errno)); -log_write(0, LOG_MAIN, "%s", string_from_gstring(message)); -deliver_msglog("%s %s\n", tod_stamp(tod_log), message->s); +log_write(0, LOG_MAIN, "%Y", message); +deliver_msglog("%s %.*s\n", tod_stamp(tod_log), message->ptr, message->s); } static void commit aae2bf28db36ab9133829dc33ea6ef886e8373c2 Author: Jeremy Harris Date: Sat Jul 8 17:59:20 2023 +0100 Fix json extract for strings carrying commas. Bug 3006 diff --git a/src/src/expand.c b/src/src/expand.c index 55c53957e..fea6501fe 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -2384,19 +2384,26 @@ static uschar * json_nextinlist(const uschar ** list) { unsigned array_depth = 0, object_depth = 0; +BOOL quoted = FALSE; const uschar * s = *list, * item; skip_whitespace(&s); for (item = s; - *s && (*s != ',' || array_depth != 0 || object_depth != 0); + *s && (*s != ',' || array_depth != 0 || object_depth != 0 || quoted); s++) - switch (*s) + if (!quoted) switch (*s) { case '[': array_depth++; break; case ']': array_depth--; break; case '{': object_depth++; break; case '}': object_depth--; break; + case '"': quoted = TRUE; + } + else switch(*s) + { + case '\\': s++; break; /* backslash protects one char */ + case '"': quoted = FALSE; break; } *list = *s ? s+1 : s; if (item == s) return NULL; commit b90406e36cfef4cf6aaf104c3a403f6745763b5b Author: Jeremy Harris Date: Sat Jul 15 16:12:58 2023 +0100 OpenSSL: add remote host info to log line for in-connection TLS error. Bug 3010 diff --git a/src/src/receive.c b/src/src/receive.c index 0891a4a8c..4271561d7 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -3911,8 +3911,7 @@ else break; } - g = string_append(NULL, 2, US"F=", - sender_address[0] == 0 ? US"<>" : sender_address); + g = string_append(NULL, 2, US"F=", *sender_address ? sender_address : US"<>"); g = add_host_info_for_log(g); log_write(0, LOG_MAIN|LOG_REJECT, "%Y %srejected by local_scan(): %.256s", @@ -4056,7 +4055,7 @@ g = string_get(256); g = string_append(g, 2, fake_response == FAIL ? US"(= " : US"<= ", - sender_address[0] == 0 ? US"<>" : sender_address); + *sender_address ? sender_address : US"<>"); if (message_reference) g = string_append(g, 2, US" R=", message_reference); diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 22c8ea99a..2e537a160 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -4532,10 +4532,15 @@ switch(error) /* Handle genuine errors */ case SSL_ERROR_SSL: + { + uschar * conn_info = smtp_get_connection_info(); + if (Ustrncmp(conn_info, US"SMTP ", 5) == 0) conn_info += 5; + /* I'd like to get separated H= here, but too hard for now */ ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring)); - log_write(0, LOG_MAIN, "TLS error (SSL_read): %s", ssl_errstring); + log_write(0, LOG_MAIN, "TLS error (SSL_read): on %s %s", conn_info, ssl_errstring); ssl_xfer_error = TRUE; return FALSE; + } default: DEBUG(D_tls) debug_printf("Got SSL error %d\n", error); commit 87a97abbfb57cb6583c330e09446c3e8549fd32f Author: Jeremy Harris Date: Sat Jul 15 20:24:01 2023 +0100 Testsuite: basic Sieve operations diff --git a/src/src/sieve.c b/src/src/sieve.c index 4793d5756..74d008568 100644 --- a/src/src/sieve.c +++ b/src/src/sieve.c @@ -831,23 +831,23 @@ int r=0; if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) { - debug_printf("String comparison (match "); + debug_printf_indent("String comparison (match "); switch (mt) { - case MATCH_IS: debug_printf(":is"); break; - case MATCH_CONTAINS: debug_printf(":contains"); break; - case MATCH_MATCHES: debug_printf(":matches"); break; + case MATCH_IS: debug_printf_indent(":is"); break; + case MATCH_CONTAINS: debug_printf_indent(":contains"); break; + case MATCH_MATCHES: debug_printf_indent(":matches"); break; } - debug_printf(", comparison \""); + debug_printf_indent(", comparison \""); switch (co) { - case COMP_OCTET: debug_printf("i;octet"); break; - case COMP_EN_ASCII_CASEMAP: debug_printf("en;ascii-casemap"); break; - case COMP_ASCII_NUMERIC: debug_printf("i;ascii-numeric"); break; + case COMP_OCTET: debug_printf_indent("i;octet"); break; + case COMP_EN_ASCII_CASEMAP: debug_printf_indent("en;ascii-casemap"); break; + case COMP_ASCII_NUMERIC: debug_printf_indent("i;ascii-numeric"); break; } - debug_printf("\"):\n"); - debug_printf(" Search = %s (%d chars)\n", needle->character,needle->length); - debug_printf(" Inside = %s (%d chars)\n", haystack->character,haystack->length); + debug_printf_indent("\"):\n"); + debug_printf_indent(" Search = %s (%d chars)\n", needle->character,needle->length); + debug_printf_indent(" Inside = %s (%d chars)\n", haystack->character,haystack->length); } switch (mt) { @@ -917,7 +917,7 @@ switch (mt) } if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) - debug_printf(" Result %s\n",r?"true":"false"); + debug_printf_indent(" Result %s\n",r?"true":"false"); return r; } @@ -1039,13 +1039,13 @@ for (new_addr = *generated; new_addr; new_addr = new_addr->next) ) { if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) - debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr); + debug_printf_indent("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr); return; } if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) - debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr); + debug_printf_indent("%s `%s'\n",file ? "fileinto" : "redirect", addr); new_addr = deliver_make_addr(addr,TRUE); if (file) @@ -2653,7 +2653,8 @@ Returns: 2 success by stop -1 syntax or execution error */ -static int parse_block(struct Sieve *filter, int exec, +static int +parse_block(struct Sieve *filter, int exec, address_item **generated) { int r; @@ -2745,7 +2746,7 @@ while (*filter->pc) if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) { - if (exec) debug_printf("if %s\n",cond?"true":"false"); + if (exec) debug_printf_indent("if %s\n",cond?"true":"false"); } m=parse_block(filter,exec ? cond : 0, generated); if (m==-1 || m==2) return m; @@ -2771,7 +2772,7 @@ while (*filter->pc) if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) { - if (exec) debug_printf("elsif %s\n",cond?"true":"false"); + if (exec) debug_printf_indent("elsif %s\n",cond?"true":"false"); } m=parse_block(filter,exec && unsuccessful ? cond : 0, generated); if (m==-1 || m==2) return m; @@ -3104,16 +3105,16 @@ while (*filter->pc) } } if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter) - debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS ""); + debug_printf_indent("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS ""); #endif } else if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter) - debug_printf("Repeated notification to `%s' ignored.\n",method.character); + debug_printf_indent("Repeated notification to `%s' ignored.\n",method.character); } else if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter) - debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n"); + debug_printf_indent("Ignoring notification, triggering message contains Auto-submitted: field.\n"); } } #endif @@ -3281,7 +3282,7 @@ while (*filter->pc) for (int i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]); if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) - debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest); + debug_printf_indent("Sieve: mail was personal, vacation file basename: %s\n", hexdigest); if (filter_test == FTEST_NONE) { @@ -3362,7 +3363,7 @@ while (*filter->pc) } } else if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) - debug_printf("Sieve: mail was not personal, vacation would ignore it\n"); + debug_printf_indent("Sieve: mail was not personal, vacation would ignore it\n"); } } else break; @@ -3554,7 +3555,8 @@ struct Sieve sieve; int r; uschar * msg; -DEBUG(D_route) debug_printf("Sieve: start of processing\n"); +DEBUG(D_route) debug_printf_indent("Sieve: start of processing\n"); +expand_level++; sieve.filter = filter; if (!vacation_directory) @@ -3611,9 +3613,10 @@ else #ifndef COMPILE_SYNTAX_CHECKER if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg); - else debug_printf("%s\n", msg); + else debug_printf_indent("%s\n", msg); #endif -DEBUG(D_route) debug_printf("Sieve: end of processing\n"); +expand_level--; +DEBUG(D_route) debug_printf_indent("Sieve: end of processing\n"); return r; } commit 9c254e6f5079a0df3e3df8cfaa0e917ac421498b Author: Jeremy Harris Date: Sat Jul 15 21:21:37 2023 +0100 Sieve filters: convert to gstring, massage coding style diff --git a/src/src/sieve.c b/src/src/sieve.c index 74d008568..dc9010936 100644 --- a/src/src/sieve.c +++ b/src/src/sieve.c @@ -54,39 +54,38 @@ /* Increase it if you want to match headers from buggy MUAs. */ #define MIMEWORD_LENGTH 75 -struct Sieve - { +struct Sieve { const uschar *filter; const uschar *pc; - int line; + int line; const uschar *errmsg; - int keep; - int require_envelope; - int require_fileinto; + int keep; + int require_envelope; + int require_fileinto; #ifdef ENCODED_CHARACTER - int require_encoded_character; + BOOL require_encoded_character; #endif #ifdef ENVELOPE_AUTH - int require_envelope_auth; + int require_envelope_auth; #endif #ifdef ENOTIFY - int require_enotify; + int require_enotify; struct Notification *notified; #endif const uschar *enotify_mailto_owner; #ifdef SUBADDRESS - int require_subaddress; + int require_subaddress; #endif #ifdef VACATION - int require_vacation; - int vacation_ran; + BOOL require_vacation; + BOOL vacation_ran; #endif const uschar *vacation_directory; const uschar *subaddress; const uschar *useraddress; - int require_copy; - int require_iascii_numeric; - }; + BOOL require_copy; + BOOL require_iascii_numeric; +}; enum Comparator { COMP_OCTET, COMP_EN_ASCII_CASEMAP, COMP_ASCII_NUMERIC }; enum MatchType { MATCH_IS, MATCH_CONTAINS, MATCH_MATCHES }; @@ -97,19 +96,12 @@ enum AddressPart { ADDRPART_LOCALPART, ADDRPART_DOMAIN, ADDRPART_ALL }; #endif enum RelOp { LT, LE, EQ, GE, GT, NE }; -struct String - { - uschar *character; - int length; - }; - -struct Notification - { - struct String method; - struct String importance; - struct String message; +struct Notification { + gstring method; + gstring importance; + gstring message; struct Notification *next; - }; +}; /* This should be a complete list of supported extensions, so that an external ManageSieve (RFC 5804) program can interrogate the current Exim binary for the @@ -143,78 +135,78 @@ const uschar *exim_sieve_extension_list[] = { NULL }; -static int eq_asciicase(const struct String *needle, const struct String *haystack, int match_prefix); +static int eq_asciicase(const gstring * needle, const gstring * haystack, BOOL match_prefix); static int parse_test(struct Sieve *filter, int *cond, int exec); static int parse_commands(struct Sieve *filter, int exec, address_item **generated); -static uschar str_from_c[]="From"; -static const struct String str_from={ str_from_c, 4 }; -static uschar str_to_c[]="To"; -static const struct String str_to={ str_to_c, 2 }; -static uschar str_cc_c[]="Cc"; -static const struct String str_cc={ str_cc_c, 2 }; -static uschar str_bcc_c[]="Bcc"; -static const struct String str_bcc={ str_bcc_c, 3 }; +static uschar str_from_c[] = "From"; +static const gstring str_from = { .s = str_from_c, .ptr = 4, .size = 5 }; +static uschar str_to_c[] = "To"; +static const gstring str_to = { .s = str_to_c, .ptr = 2, .size = 3 }; +static uschar str_cc_c[] = "Cc"; +static const gstring str_cc = { .s = str_cc_c, .ptr = 2, .size = 3 }; +static uschar str_bcc_c[] = "Bcc"; +static const gstring str_bcc = { .s = str_bcc_c, .ptr = 3, .size = 4 }; #ifdef ENVELOPE_AUTH -static uschar str_auth_c[]="auth"; -static const struct String str_auth={ str_auth_c, 4 }; +static uschar str_auth_c[] = "auth"; +static const gstring str_auth = { .s = str_auth_c, .ptr = 4, .size = 5 }; #endif -static uschar str_sender_c[]="Sender"; -static const struct String str_sender={ str_sender_c, 6 }; -static uschar str_resent_from_c[]="Resent-From"; -static const struct String str_resent_from={ str_resent_from_c, 11 }; -static uschar str_resent_to_c[]="Resent-To"; -static const struct String str_resent_to={ str_resent_to_c, 9 }; -static uschar str_fileinto_c[]="fileinto"; -static const struct String str_fileinto={ str_fileinto_c, 8 }; -static uschar str_envelope_c[]="envelope"; -static const struct String str_envelope={ str_envelope_c, 8 }; +static uschar str_sender_c[] = "Sender"; +static const gstring str_sender = { .s = str_sender_c, .ptr = 6, .size = 7 }; +static uschar str_resent_from_c[] = "Resent-From"; +static const gstring str_resent_from = { .s = str_resent_from_c, .ptr = 11, .size = 12 }; +static uschar str_resent_to_c[] = "Resent-To"; +static const gstring str_resent_to = { .s = str_resent_to_c, .ptr = 9, .size = 10 }; +static uschar str_fileinto_c[] = "fileinto"; +static const gstring str_fileinto = { .s = str_fileinto_c, .ptr = 8, .size = 9 }; +static uschar str_envelope_c[] = "envelope"; +static const gstring str_envelope = { .s = str_envelope_c, .ptr = 8, .size = 9 }; #ifdef ENCODED_CHARACTER -static uschar str_encoded_character_c[]="encoded-character"; -static const struct String str_encoded_character={ str_encoded_character_c, 17 }; +static uschar str_encoded_character_c[] = "encoded-character"; +static const gstring str_encoded_character = { .s = str_encoded_character_c, .ptr = 17, .size = 18 }; #endif #ifdef ENVELOPE_AUTH -static uschar str_envelope_auth_c[]="envelope-auth"; -static const struct String str_envelope_auth={ str_envelope_auth_c, 13 }; +static uschar str_envelope_auth_c[] = "envelope-auth"; +static const gstring str_envelope_auth = { .s = str_envelope_auth_c, .ptr = 13, .size = 14 }; #endif #ifdef ENOTIFY -static uschar str_enotify_c[]="enotify"; -static const struct String str_enotify={ str_enotify_c, 7 }; -static uschar str_online_c[]="online"; -static const struct String str_online={ str_online_c, 6 }; -static uschar str_maybe_c[]="maybe"; -static const struct String str_maybe={ str_maybe_c, 5 }; -static uschar str_auto_submitted_c[]="Auto-Submitted"; -static const struct String str_auto_submitted={ str_auto_submitted_c, 14 }; +static uschar str_enotify_c[] = "enotify"; +static const gstring str_enotify = { .s = str_enotify_c, .ptr = 7, .size = 8 }; +static uschar str_online_c[] = "online"; +static const gstring str_online = { .s = str_online_c, .ptr = 6, .size = 7 }; +static uschar str_maybe_c[] = "maybe"; +static const gstring str_maybe = { .s = str_maybe_c, .ptr = 5, .size = 6 }; +static uschar str_auto_submitted_c[] = "Auto-Submitted"; +static const gstring str_auto_submitted = { .s = str_auto_submitted_c, .ptr = 14, .size = 15 }; #endif #ifdef SUBADDRESS -static uschar str_subaddress_c[]="subaddress"; -static const struct String str_subaddress={ str_subaddress_c, 10 }; +static uschar str_subaddress_c[] = "subaddress"; +static const gstring str_subaddress = { .s = str_subaddress_c, .ptr = 10, .size = 11 }; #endif #ifdef VACATION -static uschar str_vacation_c[]="vacation"; -static const struct String str_vacation={ str_vacation_c, 8 }; -static uschar str_subject_c[]="Subject"; -static const struct String str_subject={ str_subject_c, 7 }; +static uschar str_vacation_c[] = "vacation"; +static const gstring str_vacation = { .s = str_vacation_c, .ptr = 8, .size = 9 }; +static uschar str_subject_c[] = "Subject"; +static const gstring str_subject = { .s = str_subject_c, .ptr = 7, .size = 8 }; #endif -static uschar str_copy_c[]="copy"; -static const struct String str_copy={ str_copy_c, 4 }; -static uschar str_iascii_casemap_c[]="i;ascii-casemap"; -static const struct String str_iascii_casemap={ str_iascii_casemap_c, 15 }; -static uschar str_enascii_casemap_c[]="en;ascii-casemap"; -static const struct String str_enascii_casemap={ str_enascii_casemap_c, 16 }; -static uschar str_ioctet_c[]="i;octet"; -static const struct String str_ioctet={ str_ioctet_c, 7 }; -static uschar str_iascii_numeric_c[]="i;ascii-numeric"; -static const struct String str_iascii_numeric={ str_iascii_numeric_c, 15 }; -static uschar str_comparator_iascii_casemap_c[]="comparator-i;ascii-casemap"; -static const struct String str_comparator_iascii_casemap={ str_comparator_iascii_casemap_c, 26 }; -static uschar str_comparator_enascii_casemap_c[]="comparator-en;ascii-casemap"; -static const struct String str_comparator_enascii_casemap={ str_comparator_enascii_casemap_c, 27 }; -static uschar str_comparator_ioctet_c[]="comparator-i;octet"; -static const struct String str_comparator_ioctet={ str_comparator_ioctet_c, 18 }; -static uschar str_comparator_iascii_numeric_c[]="comparator-i;ascii-numeric"; -static const struct String str_comparator_iascii_numeric={ str_comparator_iascii_numeric_c, 26 }; +static uschar str_copy_c[] = "copy"; +static const gstring str_copy = { .s = str_copy_c, .ptr = 4, .size = 5 }; +static uschar str_iascii_casemap_c[] = "i;ascii-casemap"; +static const gstring str_iascii_casemap = { .s = str_iascii_casemap_c, .ptr = 15, .size = 16 }; +static uschar str_enascii_casemap_c[] = "en;ascii-casemap"; +static const gstring str_enascii_casemap = { .s = str_enascii_casemap_c, .ptr = 16, .size = 17 }; +static uschar str_ioctet_c[] = "i;octet"; +static const gstring str_ioctet = { .s = str_ioctet_c, .ptr = 7, .size = 8 }; +static uschar str_iascii_numeric_c[] = "i;ascii-numeric"; +static const gstring str_iascii_numeric = { .s = str_iascii_numeric_c, .ptr = 15, .size = 16 }; +static uschar str_comparator_iascii_casemap_c[] = "comparator-i;ascii-casemap"; +static const gstring str_comparator_iascii_casemap = { .s = str_comparator_iascii_casemap_c, .ptr = 26, .size = 27 }; +static uschar str_comparator_enascii_casemap_c[] = "comparator-en;ascii-casemap"; +static const gstring str_comparator_enascii_casemap = { .s = str_comparator_enascii_casemap_c, .ptr = 27, .size = 28 }; +static uschar str_comparator_ioctet_c[] = "comparator-i;octet"; +static const gstring str_comparator_ioctet = { .s = str_comparator_ioctet_c, .ptr = 18, .size = 19 }; +static uschar str_comparator_iascii_numeric_c[] = "comparator-i;ascii-numeric"; +static const gstring str_comparator_iascii_numeric = { .s = str_comparator_iascii_numeric_c, .ptr = 26, .size = 27 }; /************************************************* @@ -224,84 +216,53 @@ static const struct String str_comparator_iascii_numeric={ str_comparator_iascii /* Arguments: src UTF-8 string - dst US-ASCII string Returns - dst + dst, allocated, a US-ASCII string */ -static struct String * -quoted_printable_encode(const struct String *src, struct String *dst) +static gstring * +quoted_printable_encode(const gstring * src) { -uschar *new = NULL; +gstring * dst = NULL; uschar ch; -size_t line; +size_t line = 0; -/* Two passes: one to count output allocation size, second -to do the encoding */ - -for (int pass = 0; pass <= 1; pass++) +for (const uschar * start = src->s, * end = start + src->ptr; + start < end; ++start) { - line=0; - if (pass==0) - dst->length=0; - else + ch = *start; + if (line >= 73) /* line length limit */ { - dst->character = store_get(dst->length+1, src->character); /* plus one for \0 */ - new=dst->character; + dst = string_catn(dst, US"=\n", 2); /* line split */ + line = 0; } - for (const uschar * start = src->character, * end = start + src->length; - start < end; ++start) + if ( (ch >= '!' && ch <= '<') + || (ch >= '>' && ch <= '~') + || ( (ch == '\t' || ch == ' ') + && start+2 < end && (start[1] != '\r' || start[2] != '\n') /* CRLF */ + ) + ) { - ch=*start; - if (line>=73) /* line length limit */ - { - if (pass==0) - dst->length+=2; - else - { - *new++='='; /* line split */ - *new++='\n'; - } - line=0; - } - if ( (ch>='!' && ch<='<') - || (ch>='>' && ch<='~') - || ( (ch=='\t' || ch==' ') - && start+2length; - else - *new++=*start; /* copy char */ - ++line; - } - else if (ch=='\r' && start+1length; - else - *new++='\n'; /* NL */ - line=0; - ++start; /* consume extra input char */ - } - else - { - if (pass==0) - dst->length+=3; - else - { /* encoded char */ - new += sprintf(CS new,"=%02X",ch); - } - line+=3; - } + dst = string_catn(dst, start, 1); /* copy char */ + ++line; + } + else if (ch == '\r' && start+1 < end && start[1] == '\n') /* CRLF */ + { + dst = string_catn(dst, US"\n", 1); /* NL */ + line = 0; + ++start; /* consume extra input char */ + } + else + { + dst = string_fmt_append(dst, "=%02X", ch); + line += 3; } } - *new='\0'; /* not included in length, but nice */ - return dst; + +(void) string_from_gstring(dst); +gstring_release_unused(dst); +return dst; } @@ -321,19 +282,20 @@ Returns -1 syntax error */ -int check_mail_address(struct Sieve *filter, const struct String *address) +int +check_mail_address(struct Sieve * filter, const gstring * address) { int start, end, domain; -uschar *error,*ss; +uschar * error, * ss; -if (address->length>0) +if (address->ptr > 0) { - ss = parse_extract_address(address->character, &error, &start, &end, &domain, + ss = parse_extract_address(address->s, &error, &start, &end, &domain, FALSE); if (!ss) { - filter->errmsg=string_sprintf("malformed address \"%s\" (%s)", - address->character, error); + filter->errmsg = string_sprintf("malformed address \"%s\" (%s)", + address->s, error); return -1; } else @@ -341,7 +303,7 @@ if (address->length>0) } else { - filter->errmsg=CUS "empty address"; + filter->errmsg = CUS "empty address"; return -1; } } @@ -356,34 +318,35 @@ Arguments: str URI encoded string Returns - 0 Decoding successful - -1 Encoding error + str is modified in place + TRUE Decoding successful + FALSE Encoding error */ #ifdef ENOTIFY -static int -uri_decode(struct String *str) +static BOOL +uri_decode(gstring * str) { -uschar *s,*t,*e; +uschar *s, *t, *e; -if (str->length==0) return 0; -for (s=str->character,t=s,e=s+str->length; sptr == 0) return TRUE; +for (t = s = str->s, e = s + str->ptr; s < e; ) + if (*s == '%') { - if (s+2length=t-str->character; -return 0; +*t = '\0'; +str->ptr = t - str->s; +return TRUE; } @@ -397,14 +360,14 @@ Parse mailto-URI. mailtoURI = "mailto:" [ to ] [ headers ] to = [ addr-spec *("%2C" addr-spec ) ] headers = "?" header *( "&" header ) - header = hname "=" hvalue + header = hname " = " hvalue hname = *urlc hvalue = *urlc Arguments: filter points to the Sieve filter including its state uri URI, excluding scheme - recipient + recipient list of recipients; prepnded to body Returns @@ -414,41 +377,36 @@ Returns */ static int -parse_mailto_uri(struct Sieve *filter, const uschar *uri, - string_item **recipient, struct String *header, struct String *subject, - struct String *body) +parse_mailto_uri(struct Sieve * filter, const uschar * uri, + string_item ** recipient, gstring * header, gstring * subject, + gstring * body) { -const uschar *start; -struct String to, hname; -struct String hvalue = {.character = NULL, .length = 0}; -string_item *new; +const uschar * start; -if (Ustrncmp(uri,"mailto:",7)) +if (Ustrncmp(uri, "mailto:", 7)) { - filter->errmsg=US "Unknown URI scheme"; + filter->errmsg = US "Unknown URI scheme"; return 0; } -uri+=7; -if (*uri && *uri!='?') +uri += 7; +if (*uri && *uri != '?') for (;;) { /* match to */ - for (start=uri; *uri && *uri!='?' && (*uri!='%' || *(uri+1)!='2' || tolower(*(uri+2))!='c'); ++uri); - if (uri>start) + for (start = uri; *uri && *uri != '?' && (*uri != '%' || uri[1] != '2' || tolower(uri[2]) != 'c'); ++uri); + if (uri > start) { - gstring * g = string_catn(NULL, start, uri-start); + gstring * to = string_catn(NULL, start, uri - start); + string_item * new; - to.length = len_string_from_gstring(g, &to.character); - if (uri_decode(&to)==-1) + if (!uri_decode(to)) { - filter->errmsg=US"Invalid URI encoding"; + filter->errmsg = US"Invalid URI encoding"; return -1; } new = store_get(sizeof(string_item), GET_UNTAINTED); - new->text = store_get(to.length+1, to.character); - if (to.length) memcpy(new->text, to.character, to.length); - new->text[to.length] = '\0'; + new->text = string_from_gstring(to); new->next = *recipient; *recipient = new; } @@ -457,97 +415,83 @@ if (*uri && *uri!='?') filter->errmsg = US"Missing addr-spec in URI"; return -1; } - if (*uri=='%') uri+=3; + if (*uri == '%') uri += 3; else break; } -if (*uri=='?') - { - ++uri; - for (;;) +if (*uri == '?') + for (uri++; ;) { + gstring * hname = string_get(0), * hvalue = NULL; + /* match hname */ - for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri); - if (uri>start) + for (start = uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(), %", *uri)); ++uri) ; + if (uri > start) { - gstring * g = string_catn(NULL, start, uri-start); + hname = string_catn(hname, start, uri-start); - hname.length = len_string_from_gstring(g, &hname.character); - if (uri_decode(&hname)==-1) + if (!uri_decode(hname)) { - filter->errmsg=US"Invalid URI encoding"; + filter->errmsg = US"Invalid URI encoding"; return -1; } } /* match = */ - if (*uri=='=') - ++uri; - else + if (*uri++ != '=') { - filter->errmsg=US"Missing equal after hname"; + filter->errmsg = US"Missing equal after hname"; return -1; } + /* match hvalue */ - for (start=uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(),%",*uri)); ++uri); - if (uri>start) + for (start = uri; *uri && (isalnum(*uri) || strchr("$-_.+!*'(), %", *uri)); ++uri) ; + if (uri > start) { - gstring * g = string_catn(NULL, start, uri-start); + hvalue = string_catn(NULL, start, uri-start); /*XXX this used to say "hname =" */ - hname.length = len_string_from_gstring(g, &hname.character); - if (uri_decode(&hvalue)==-1) + if (!uri_decode(hvalue)) { - filter->errmsg=US"Invalid URI encoding"; + filter->errmsg = US"Invalid URI encoding"; return -1; } } - if (hname.length==2 && strcmpic(hname.character, US"to")==0) + if (hname->ptr == 2 && strcmpic(hname->s, US"to") == 0) { - new=store_get(sizeof(string_item), GET_UNTAINTED); - new->text = store_get(hvalue.length+1, hvalue.character); - if (hvalue.length) memcpy(new->text, hvalue.character, hvalue.length); - new->text[hvalue.length]='\0'; - new->next=*recipient; - *recipient=new; + string_item * new = store_get(sizeof(string_item), GET_UNTAINTED); + new->text = string_from_gstring(hvalue); + new->next = *recipient; + *recipient = new; } - else if (hname.length==4 && strcmpic(hname.character, US"body")==0) - *body=hvalue; - else if (hname.length==7 && strcmpic(hname.character, US"subject")==0) - *subject=hvalue; + else if (hname->ptr == 4 && strcmpic(hname->s, US"body") == 0) + *body = *hvalue; + else if (hname->ptr == 7 && strcmpic(hname->s, US"subject") == 0) + *subject = *hvalue; else { - static struct String ignore[]= + static gstring ignore[] = { - {US"date",4}, - {US"from",4}, - {US"message-id",10}, - {US"received",8}, - {US"auto-submitted",14} + {.s = US"date", .ptr = 4, .size = 5}, + {.s = US"from", .ptr = 4, .size = 5}, + {.s = US"message-id", .ptr = 10, .size = 11}, + {.s = US"received", .ptr = 8, .size = 9}, + {.s = US"auto-submitted", .ptr = 14, .size = 15} }; - static struct String *end=ignore+sizeof(ignore)/sizeof(ignore[0]); - struct String *i; + static gstring * end = ignore + nelem(ignore); + gstring * i; - for (i=ignore; ilength==-1) header->length = 0; - - g = string_catn(NULL, header->character, header->length); - g = string_catn(g, hname.character, hname.length); - g = string_catn(g, CUS ": ", 2); - g = string_catn(g, hvalue.character, hvalue.length); - g = string_catn(g, CUS "\n", 1); - - hname.length = len_string_from_gstring(g, &hname.character); + hname = string_fmt_append(NULL, "%Y%Y: %Y\n", header, hname, hvalue); + (void) string_from_gstring(hname); + /*XXX we seem to do nothing with this new hname? */ } } - if (*uri=='&') ++uri; + if (*uri == '&') ++uri; else break; } - } if (*uri) { - filter->errmsg=US"Syntactically invalid URI"; + filter->errmsg = US"Syntactically invalid URI"; return -1; } return 1; @@ -563,35 +507,35 @@ return 1; Arguments: needle UTF-8 string to search ... haystack ... inside the haystack - match_prefix 1 to compare if needle is a prefix of haystack + match_prefix TRUE to compare if needle is a prefix of haystack Returns: 0 needle not found in haystack 1 needle found */ -static int eq_octet(const struct String *needle, - const struct String *haystack, int match_prefix) +static int +eq_octet(const gstring *needle, const gstring *haystack, BOOL match_prefix) { -size_t nl,hl; -const uschar *n,*h; +size_t nl, hl; +const uschar *n, *h; -nl=needle->length; -n=needle->character; -hl=haystack->length; -h=haystack->character; +nl = needle->ptr; +n = needle->s; +hl = haystack->ptr; +h = haystack->s; while (nl>0 && hl>0) { #if !HAVE_ICONV - if (*n&0x80) return 0; - if (*h&0x80) return 0; + if (*n & 0x80) return 0; + if (*h & 0x80) return 0; #endif - if (*n!=*h) return 0; + if (*n != *h) return 0; ++n; ++h; --nl; --hl; } -return (match_prefix ? nl==0 : nl==0 && hl==0); +return (match_prefix ? nl == 0 : nl == 0 && hl == 0); } @@ -603,39 +547,39 @@ return (match_prefix ? nl==0 : nl==0 && hl==0); Arguments: needle UTF-8 string to search ... haystack ... inside the haystack - match_prefix 1 to compare if needle is a prefix of haystack + match_prefix TRUE to compare if needle is a prefix of haystack Returns: 0 needle not found in haystack 1 needle found */ -static int eq_asciicase(const struct String *needle, - const struct String *haystack, int match_prefix) +static int +eq_asciicase(const gstring *needle, const gstring *haystack, BOOL match_prefix) { -size_t nl,hl; -const uschar *n,*h; -uschar nc,hc; - -nl=needle->length; -n=needle->character; -hl=haystack->length; -h=haystack->character; -while (nl>0 && hl>0) - { - nc=*n; - hc=*h; +size_t nl, hl; +const uschar *n, *h; +uschar nc, hc; + +nl = needle->ptr; +n = needle->s; +hl = haystack->ptr; +h = haystack->s; +while (nl > 0 && hl > 0) + { + nc = *n; + hc = *h; #if !HAVE_ICONV - if (nc&0x80) return 0; - if (hc&0x80) return 0; + if (nc & 0x80) return 0; + if (hc & 0x80) return 0; #endif /* tolower depends on the locale and only ASCII case must be insensitive */ - if ((nc>='A' && nc<='Z' ? nc|0x20 : nc) != (hc>='A' && hc<='Z' ? hc|0x20 : hc)) return 0; + if ((nc >= 'A' && nc <= 'Z' ? nc | 0x20 : nc) != (hc >= 'A' && hc <= 'Z' ? hc | 0x20 : hc)) return 0; ++n; ++h; --nl; --hl; } -return (match_prefix ? nl==0 : nl==0 && hl==0); +return (match_prefix ? nl == 0 : nl == 0 && hl == 0); } @@ -655,46 +599,46 @@ Returns: 0 needle not found in haystack -1 pattern error */ -static int eq_glob(const struct String *needle, - const struct String *haystack, int ascii_caseless, int match_octet) +static int +eq_glob(const gstring *needle, + const gstring *haystack, BOOL ascii_caseless, BOOL match_octet) { -const uschar *n,*h,*nend,*hend; -int may_advance=0; - -n=needle->character; -h=haystack->character; -nend=n+needle->length; -hend=h+haystack->length; -while (ns; +h = haystack->s; +nend = n+needle->ptr; +hend = h+haystack->ptr; +while (n < nend) + if (*n == '*') { ++n; - may_advance=1; + may_advance = 1; } else { - const uschar *npart,*hpart; + const uschar *npart, *hpart; /* Try to match a non-star part of the needle at the current */ /* position in the haystack. */ match_part: - npart=n; - hpart=h; - while (npart='A' && *npart<='Z' ? *npart|0x20 : *npart) != (*hpart>='A' && *hpart<='Z' ? *hpart|0x20 : *hpart)) - : *hpart!=*npart + ? ((*npart>= 'A' && *npart<= 'Z' ? *npart|0x20 : *npart) != (*hpart>= 'A' && *hpart<= 'Z' ? *hpart|0x20 : *hpart)) + : *hpart != *npart ) { if (may_advance) @@ -738,18 +682,17 @@ while (ncharacter; -aend=a->character+a->length; -bs=b->character; -bend=b->character+b->length; +as = a->s; +aend = a->s+a->ptr; +bs = b->s; +bend = b->s+b->ptr; -while (*as>='0' && *as<='9' && ascharacter; -while (*bs>='0' && *bs<='9' && bscharacter; +while (*as>= '0' && *as<= '9' && ass; +while (*bs>= '0' && *bs<= '9' && bss; -if (al && bl==0) cmp=-1; -else if (al==0 && bl==0) cmp=0; -else if (al==0 && bl) cmp=1; +if (al && bl == 0) cmp = -1; +else if (al == 0 && bl == 0) cmp = 0; +else if (al == 0 && bl) cmp = 1; else { - cmp=al-bl; - if (cmp==0) cmp=memcmp(a->character,b->character,al); + cmp = al-bl; + if (cmp == 0) cmp = memcmp(a->s, b->s, al); } switch (relop) { - case LT: return cmp<0; - case LE: return cmp<=0; - case EQ: return cmp==0; - case GE: return cmp>=0; - case GT: return cmp>0; - case NE: return cmp!=0; + case LT: return cmp < 0; + case LE: return cmp <= 0; + case EQ: return cmp == 0; + case GE: return cmp >= 0; + case GT: return cmp > 0; + case NE: return cmp != 0; } /*NOTREACHED*/ return -1; @@ -823,13 +766,14 @@ Returns: 0 needle not found in haystack -1 comparator does not offer matchtype */ -static int compare(struct Sieve *filter, const struct String *needle, const struct String *haystack, +static int +compare(struct Sieve * filter, const gstring * needle, const gstring * haystack, enum Comparator co, enum MatchType mt) { -int r=0; +int r = 0; -if ((filter_test != FTEST_NONE && debug_selector != 0) || - (debug_selector & D_filter) != 0) +if ( (filter_test != FTEST_NONE && debug_selector != 0) + || (debug_selector & D_filter) != 0) { debug_printf_indent("String comparison (match "); switch (mt) @@ -846,8 +790,8 @@ if ((filter_test != FTEST_NONE && debug_selector != 0) || case COMP_ASCII_NUMERIC: debug_printf_indent("i;ascii-numeric"); break; } debug_printf_indent("\"):\n"); - debug_printf_indent(" Search = %s (%d chars)\n", needle->character,needle->length); - debug_printf_indent(" Inside = %s (%d chars)\n", haystack->character,haystack->length); + debug_printf_indent(" Search = %s (%d chars)\n", needle->s, needle->ptr); + debug_printf_indent(" Inside = %s (%d chars)\n", haystack->s, haystack->ptr); } switch (mt) { @@ -855,38 +799,38 @@ switch (mt) switch (co) { case COMP_OCTET: - if (eq_octet(needle,haystack,0)) r=1; + if (eq_octet(needle, haystack, FALSE)) r = 1; break; case COMP_EN_ASCII_CASEMAP: - if (eq_asciicase(needle,haystack,0)) r=1; + if (eq_asciicase(needle, haystack, FALSE)) r = 1; break; case COMP_ASCII_NUMERIC: if (!filter->require_iascii_numeric) { - filter->errmsg=CUS "missing previous require \"comparator-i;ascii-numeric\";"; + filter->errmsg = CUS "missing previous require \"comparator-i;ascii-numeric\";"; return -1; } - if (eq_asciinumeric(needle,haystack,EQ)) r=1; + if (eq_asciinumeric(needle, haystack, EQ)) r = 1; break; } break; case MATCH_CONTAINS: { - struct String h; + gstring h; switch (co) { case COMP_OCTET: - for (h = *haystack; h.length; ++h.character,--h.length) - if (eq_octet(needle,&h,1)) { r=1; break; } + for (h = *haystack; h.ptr; ++h.s, --h.ptr) + if (eq_octet(needle, &h, TRUE)) { r = 1; break; } break; case COMP_EN_ASCII_CASEMAP: - for (h = *haystack; h.length; ++h.character, --h.length) - if (eq_asciicase(needle,&h,1)) { r=1; break; } + for (h = *haystack; h.ptr; ++h.s, --h.ptr) + if (eq_asciicase(needle, &h, TRUE)) { r = 1; break; } break; default: - filter->errmsg=CUS "comparator does not offer specified matchtype"; + filter->errmsg = CUS "comparator does not offer specified matchtype"; return -1; } break; @@ -896,28 +840,28 @@ switch (mt) switch (co) { case COMP_OCTET: - if ((r=eq_glob(needle,haystack,0,1))==-1) + if ((r = eq_glob(needle, haystack, FALSE, TRUE)) == -1) { - filter->errmsg=CUS "syntactically invalid pattern"; + filter->errmsg = CUS "syntactically invalid pattern"; return -1; } break; case COMP_EN_ASCII_CASEMAP: - if ((r=eq_glob(needle,haystack,1,1))==-1) + if ((r = eq_glob(needle, haystack, TRUE, TRUE)) == -1) { - filter->errmsg=CUS "syntactically invalid pattern"; + filter->errmsg = CUS "syntactically invalid pattern"; return -1; } break; default: - filter->errmsg=CUS "comparator does not offer specified matchtype"; + filter->errmsg = CUS "comparator does not offer specified matchtype"; return -1; } break; } if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) - debug_printf_indent(" Result %s\n",r?"true":"false"); + debug_printf_indent(" Result %s\n", r?"true":"false"); return r; } @@ -945,22 +889,21 @@ Returns: 0 string is not a valid header field 1 string is a value header field */ -static int is_header(const struct String *header) +static int +is_header(const gstring *header) { size_t l; const uschar *h; -l=header->length; -h=header->character; -if (l==0) return 0; +l = header->ptr; +h = header->s; +if (l == 0) return 0; while (l) { - if (((unsigned char)*h)<33 || ((unsigned char)*h)==':' || ((unsigned char)*h)==127) return 0; - else - { - ++h; - --l; - } + if (*h < 33 || *h == ':' || *h == 127) + return 0; + ++h; + --l; } return 1; } @@ -979,16 +922,13 @@ Returns: quoted string */ static const uschar * -quote(const struct String *header) +quote(const gstring * header) { gstring * quoted = NULL; size_t l; -const uschar *h; +const uschar * h; -l=header->length; -h=header->character; -while (l) - { +for (l = header->ptr, h = header->s; l; ++h, --l) switch (*h) { case '\0': @@ -1001,10 +941,7 @@ while (l) default: quoted = string_catn(quoted, h, 1); } - ++h; - --l; - } -quoted = string_catn(quoted, CUS "", 1); + return string_from_gstring(quoted); } @@ -1031,7 +968,7 @@ add_addr(address_item **generated, uschar *addr, int file, int maxage, int maxme address_item *new_addr; for (new_addr = *generated; new_addr; new_addr = new_addr->next) - if ( Ustrcmp(new_addr->address,addr) == 0 + if ( Ustrcmp(new_addr->address, addr) == 0 && ( !file || testflag(new_addr, af_pfr) || testflag(new_addr, af_file) @@ -1039,15 +976,15 @@ for (new_addr = *generated; new_addr; new_addr = new_addr->next) ) { if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) - debug_printf_indent("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr); + debug_printf_indent("Repeated %s `%s' ignored.\n", file ? "fileinto" : "redirect", addr); return; } if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) - debug_printf_indent("%s `%s'\n",file ? "fileinto" : "redirect", addr); + debug_printf_indent("%s `%s'\n", file ? "fileinto" : "redirect", addr); -new_addr = deliver_make_addr(addr,TRUE); +new_addr = deliver_make_addr(addr, TRUE); if (file) { setflag(new_addr, af_pfr); @@ -1077,27 +1014,27 @@ Returns: nothing The expanded string is empty in case there is no such header */ -static void expand_header(struct String *value, const struct String *header) +static void +expand_header(gstring * value, const gstring * header) { -uschar *s,*r,*t; +uschar *s, *r, *t; uschar *errmsg; -value->length=0; -value->character=(uschar*)0; +value->ptr = 0; +value->s = (uschar*)0; -t = r = s = expand_string(string_sprintf("$rheader_%s",quote(header))); +t = r = s = expand_string(string_sprintf("$rheader_%s", quote(header))); if (!t) return; -while (*r==' ' || *r=='\t') ++r; +while (*r == ' ' || *r == '\t') ++r; while (*r) - { - if (*r=='\n') + if (*r == '\n') ++r; else - *t++=*r++; - } -while (t>s && (*(t-1)==' ' || *(t-1)=='\t')) --t; -*t='\0'; -value->character=rfc2047_decode(s,check_rfc2047_length,US"utf-8",'\0',&value->length,&errmsg); + *t++ = *r++; + +while (t>s && (*(t-1) == ' ' || *(t-1) == '\t')) --t; +*t = '\0'; +value->s = rfc2047_decode(s, check_rfc2047_length, US"utf-8", '\0', &value->ptr, &errmsg); } @@ -1116,19 +1053,20 @@ Returns: 1 success -1 syntax error */ -static int parse_hashcomment(struct Sieve *filter) +static int +parse_hashcomment(struct Sieve * filter) { ++filter->pc; while (*filter->pc) { #ifdef RFC_EOL - if (*filter->pc=='\r' && *(filter->pc+1)=='\n') + if (*filter->pc == '\r' && (filter->pc)[1] == '\n') #else - if (*filter->pc=='\n') + if (*filter->pc == '\n') #endif { #ifdef RFC_EOL - filter->pc+=2; + filter->pc += 2; #else ++filter->pc; #endif @@ -1137,7 +1075,7 @@ while (*filter->pc) } else ++filter->pc; } -filter->errmsg=CUS "missing end of comment"; +filter->errmsg = CUS "missing end of comment"; return -1; } @@ -1157,20 +1095,21 @@ Returns: 1 success -1 syntax error */ -static int parse_comment(struct Sieve *filter) +static int +parse_comment(struct Sieve *filter) { - filter->pc+=2; - while (*filter->pc) - { - if (*filter->pc=='*' && *(filter->pc+1)=='/') +filter->pc += 2; +while (*filter->pc) + if (*filter->pc == '*' && (filter->pc)[1] == '/') { - filter->pc+=2; - return 1; + filter->pc += 2; + return 1; } - else ++filter->pc; - } - filter->errmsg=CUS "missing end of comment"; - return -1; + else + ++filter->pc; + +filter->errmsg = CUS "missing end of comment"; +return -1; } @@ -1189,31 +1128,32 @@ Returns: 1 success -1 syntax error */ -static int parse_white(struct Sieve *filter) +static int +parse_white(struct Sieve *filter) { while (*filter->pc) { - if (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc; + if (*filter->pc == ' ' || *filter->pc == '\t') ++filter->pc; #ifdef RFC_EOL - else if (*filter->pc=='\r' && *(filter->pc+1)=='\n') + else if (*filter->pc == '\r' && (filter->pc)[1] == '\n') #else - else if (*filter->pc=='\n') + else if (*filter->pc == '\n') #endif { #ifdef RFC_EOL - filter->pc+=2; + filter->pc += 2; #else ++filter->pc; #endif ++filter->line; } - else if (*filter->pc=='#') + else if (*filter->pc == '#') { - if (parse_hashcomment(filter)==-1) return -1; + if (parse_hashcomment(filter) == -1) return -1; } - else if (*filter->pc=='/' && *(filter->pc+1)=='*') + else if (*filter->pc == '/' && (filter->pc)[1] == '*') { - if (parse_comment(filter)==-1) return -1; + if (parse_comment(filter) == -1) return -1; } else break; } @@ -1238,32 +1178,33 @@ Arguments: dst points to the destination of the decoded octets, optionally to (uschar*)0 for checking only -Returns: >=0 number of decoded octets +Returns: >= 0 number of decoded octets -1 syntax error */ -static int hex_decode(uschar *src, uschar *end, uschar *dst) +static int +hex_decode(uschar *src, uschar *end, uschar *dst) { -int decoded=0; +int decoded = 0; -while (*src==' ' || *src=='\t' || *src=='\n') ++src; +while (*src == ' ' || *src == '\t' || *src == '\n') ++src; do { - int x,d,n; + int x, d, n; for (x = 0, d = 0; - d<2 && src='0' && n<='9' ? n-'0' : 10+(n-'a')) ,++d, ++src) ; - if (d==0) return -1; - if (dst) *dst++=x; + d<2 && src= '0' && n<= '9' ? n-'0' : 10+(n-'a')) , ++d, ++src) ; + if (d == 0) return -1; + if (dst) *dst++ = x; ++decoded; - if (src==end) return decoded; - if (*src==' ' || *src=='\t' || *src=='\n') - while (*src==' ' || *src=='\t' || *src=='\n') ++src; + if (src == end) return decoded; + if (*src == ' ' || *src == '\t' || *src == '\n') + while (*src == ' ' || *src == '\t' || *src == '\n') ++src; else return -1; } -while (src=0 number of decoded octets +Returns: >= 0 number of decoded octets -1 syntax error -2 semantic error (character range violation) */ @@ -1298,64 +1239,64 @@ Returns: >=0 number of decoded octets static int unicode_decode(uschar *src, uschar *end, uschar *dst) { -int decoded=0; +int decoded = 0; -while (*src==' ' || *src=='\t' || *src=='\n') ++src; +while (*src == ' ' || *src == '\t' || *src == '\n') ++src; do { uschar *hex_seq; - int c,d,n; + int c, d, n; unicode_hex: - for (hex_seq = src; src < end && *src=='0'; ) src++; + for (hex_seq = src; src < end && *src == '0'; ) src++; for (c = 0, d = 0; - d < 7 && src < end && isxdigit(n=tolower(*src)); - c=(c<<4)|(n>='0' && n<='9' ? n-'0' : 10+(n-'a')), ++d, ++src) ; + d < 7 && src < end && isxdigit(n = tolower(*src)); + c = (c<<4)|(n>= '0' && n<= '9' ? n-'0' : 10+(n-'a')), ++d, ++src) ; if (src == hex_seq) return -1; - if (d==7 || (!((c>=0 && c<=0xd7ff) || (c>=0xe000 && c<=0x10ffff)))) return -2; + if (d == 7 || (!((c >= 0 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0x10ffff)))) return -2; if (c<128) { - if (dst) *dst++=c; + if (dst) *dst++ = c; ++decoded; } - else if (c>=0x80 && c<=0x7ff) + else if (c>= 0x80 && c<= 0x7ff) { if (dst) { - *dst++=192+(c>>6); - *dst++=128+(c&0x3f); + *dst++ = 192+(c>>6); + *dst++ = 128+(c&0x3f); } - decoded+=2; + decoded += 2; } - else if (c>=0x800 && c<=0xffff) + else if (c>= 0x800 && c<= 0xffff) { if (dst) { - *dst++=224+(c>>12); - *dst++=128+((c>>6)&0x3f); - *dst++=128+(c&0x3f); + *dst++ = 224+(c>>12); + *dst++ = 128+((c>>6)&0x3f); + *dst++ = 128+(c&0x3f); } - decoded+=3; + decoded += 3; } - else if (c>=0x10000 && c<=0x1fffff) + else if (c>= 0x10000 && c<= 0x1fffff) { if (dst) { - *dst++=240+(c>>18); - *dst++=128+((c>>10)&0x3f); - *dst++=128+((c>>6)&0x3f); - *dst++=128+(c&0x3f); + *dst++ = 240+(c>>18); + *dst++ = 128+((c>>10)&0x3f); + *dst++ = 128+((c>>6)&0x3f); + *dst++ = 128+(c&0x3f); } - decoded+=4; + decoded += 4; } - if (*src==' ' || *src=='\t' || *src=='\n') + if (*src == ' ' || *src == '\t' || *src == '\n') { - while (*src==' ' || *src=='\t' || *src=='\n') ++src; - if (src==end) return decoded; + while (*src == ' ' || *src == '\t' || *src == '\n') ++src; + if (src == end) return decoded; goto unicode_hex; } } -while (srccharacter; -dst=src; -end=data->character+data->length; -while (srcs; +dst = src; +end = data->s+data->ptr; +while (src < end) { uschar *brace; if ( - strncmpic(src,US "${hex:",6)==0 - && (brace=Ustrchr(src+6,'}'))!=(uschar*)0 - && (hex_decode(src+6,brace,(uschar*)0))>=0 + strncmpic(src, US "${hex:", 6) == 0 + && (brace = Ustrchr(src+6, '}')) != (uschar*)0 + && (hex_decode(src+6, brace, (uschar*)0))>= 0 ) { - dst+=hex_decode(src+6,brace,dst); - src=brace+1; + dst += hex_decode(src+6, brace, dst); + src = brace+1; } else if ( - strncmpic(src,US "${unicode:",10)==0 - && (brace=Ustrchr(src+10,'}'))!=(uschar*)0 + strncmpic(src, US "${unicode:", 10) == 0 + && (brace = Ustrchr(src+10, '}')) != (uschar*)0 ) { - switch (unicode_decode(src+10,brace,(uschar*)0)) + switch (unicode_decode(src+10, brace, (uschar*)0)) { case -2: { - filter->errmsg=CUS "unicode character out of range"; + filter->errmsg = CUS "unicode character out of range"; return -1; } case -1: { - *dst++=*src++; + *dst++ = *src++; break; } default: { - dst+=unicode_decode(src+10,brace,dst); - src=brace+1; + dst += unicode_decode(src+10, brace, dst); + src = brace+1; } } } - else *dst++=*src++; + else *dst++ = *src++; } - data->length=dst-data->character; - *dst='\0'; + data->ptr = dst-data->s; + *dst = '\0'; return 1; } #endif @@ -1461,46 +1403,46 @@ Returns: 1 success */ static int -parse_string(struct Sieve *filter, struct String *data) +parse_string(struct Sieve *filter, gstring *data) { gstring * g = NULL; -data->length = 0; -data->character = NULL; +data->ptr = 0; +data->s = NULL; -if (*filter->pc=='"') /* quoted string */ +if (*filter->pc == '"') /* quoted string */ { ++filter->pc; while (*filter->pc) { - if (*filter->pc=='"') /* end of string */ + if (*filter->pc == '"') /* end of string */ { ++filter->pc; if (g) - data->length = len_string_from_gstring(g, &data->character); + data->ptr = len_string_from_gstring(g, &data->s); else - data->character = US"\0"; + data->s = US"\0"; /* that way, there will be at least one character allocated */ #ifdef ENCODED_CHARACTER - if (filter->require_encoded_character - && string_decode(filter,data)==-1) + if ( filter->require_encoded_character + && string_decode(filter, data) == -1) return -1; #endif return 1; } - else if (*filter->pc=='\\' && *(filter->pc+1)) /* quoted character */ + else if (*filter->pc == '\\' && (filter->pc)[1]) /* quoted character */ { g = string_catn(g, filter->pc+1, 1); - filter->pc+=2; + filter->pc += 2; } else /* regular character */ { #ifdef RFC_EOL - if (*filter->pc=='\r' && *(filter->pc+1)=='\n') ++filter->line; + if (*filter->pc == '\r' && (filter->pc)[1] == '\n') ++filter->line; #else - if (*filter->pc=='\n') + if (*filter->pc == '\n') { g = string_catn(g, US"\r", 1); ++filter->line; @@ -1510,26 +1452,26 @@ if (*filter->pc=='"') /* quoted string */ filter->pc++; } } - filter->errmsg=CUS "missing end of string"; + filter->errmsg = CUS "missing end of string"; return -1; } -else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */ +else if (Ustrncmp(filter->pc, CUS "text:", 5) == 0) /* multiline string */ { - filter->pc+=5; + filter->pc += 5; /* skip optional white space followed by hashed comment or CRLF */ - while (*filter->pc==' ' || *filter->pc=='\t') ++filter->pc; - if (*filter->pc=='#') + while (*filter->pc == ' ' || *filter->pc == '\t') ++filter->pc; + if (*filter->pc == '#') { - if (parse_hashcomment(filter)==-1) return -1; + if (parse_hashcomment(filter) == -1) return -1; } #ifdef RFC_EOL - else if (*filter->pc=='\r' && *(filter->pc+1)=='\n') + else if (*filter->pc == '\r' && (filter->pc)[1] == '\n') #else - else if (*filter->pc=='\n') + else if (*filter->pc == '\n') #endif { #ifdef RFC_EOL - filter->pc+=2; + filter->pc += 2; #else ++filter->pc; #endif @@ -1537,53 +1479,53 @@ else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */ } else { - filter->errmsg=CUS "syntax error"; + filter->errmsg = CUS "syntax error"; return -1; } while (*filter->pc) { #ifdef RFC_EOL - if (*filter->pc=='\r' && *(filter->pc+1)=='\n') /* end of line */ + if (*filter->pc == '\r' && (filter->pc)[1] == '\n') /* end of line */ #else - if (*filter->pc=='\n') /* end of line */ + if (*filter->pc == '\n') /* end of line */ #endif { g = string_catn(g, CUS "\r\n", 2); #ifdef RFC_EOL - filter->pc+=2; + filter->pc += 2; #else ++filter->pc; #endif ++filter->line; #ifdef RFC_EOL - if (*filter->pc=='.' && *(filter->pc+1)=='\r' && *(filter->pc+2)=='\n') /* end of string */ + if (*filter->pc == '.' && (filter->pc)[1] == '\r' && (filter->pc)[2] == '\n') /* end of string */ #else - if (*filter->pc=='.' && *(filter->pc+1)=='\n') /* end of string */ + if (*filter->pc == '.' && (filter->pc)[1] == '\n') /* end of string */ #endif { if (g) - data->length = len_string_from_gstring(g, &data->character); + data->ptr = len_string_from_gstring(g, &data->s); else - data->character = US"\0"; + data->s = US"\0"; /* that way, there will be at least one character allocated */ #ifdef RFC_EOL - filter->pc+=3; + filter->pc += 3; #else - filter->pc+=2; + filter->pc += 2; #endif ++filter->line; #ifdef ENCODED_CHARACTER - if (filter->require_encoded_character - && string_decode(filter,data)==-1) + if ( filter->require_encoded_character + && string_decode(filter, data) == -1) return -1; #endif return 1; } - else if (*filter->pc=='.' && *(filter->pc+1)=='.') /* remove dot stuffing */ + else if (*filter->pc == '.' && (filter->pc)[1] == '.') /* remove dot stuffing */ { g = string_catn(g, CUS ".", 1); - filter->pc+=2; + filter->pc += 2; } } else /* regular character */ @@ -1592,7 +1534,7 @@ else if (Ustrncmp(filter->pc,CUS "text:",5)==0) /* multiline string */ filter->pc++; } } - filter->errmsg=CUS "missing end of multi line string"; + filter->errmsg = CUS "missing end of multi line string"; return -1; } else return 0; @@ -1615,19 +1557,20 @@ Returns: 1 success 0 identifier not matched */ -static int parse_identifier(struct Sieve *filter, const uschar *id) +static int +parse_identifier(struct Sieve *filter, const uschar *id) { - size_t idlen=Ustrlen(id); +size_t idlen = Ustrlen(id); - if (strncmpic(US filter->pc,US id,idlen)==0) +if (strncmpic(US filter->pc, US id, idlen) == 0) { - uschar next=filter->pc[idlen]; + uschar next = filter->pc[idlen]; - if ((next>='A' && next<='Z') || (next>='a' && next<='z') || next=='_' || (next>='0' && next<='9')) return 0; - filter->pc+=idlen; - return 1; + if ((next>= 'A' && next<= 'Z') || (next>= 'a' && next<= 'z') || next == '_' || (next>= '0' && next<= '9')) return 0; + filter->pc += idlen; + return 1; } - else return 0; +else return 0; } @@ -1648,38 +1591,39 @@ Returns: 1 success -1 no string list found */ -static int parse_number(struct Sieve *filter, unsigned long *data) +static int +parse_number(struct Sieve *filter, unsigned long *data) { -unsigned long d,u; +unsigned long d, u; -if (*filter->pc>='0' && *filter->pc<='9') +if (*filter->pc>= '0' && *filter->pc<= '9') { uschar *e; - errno=0; - d=Ustrtoul(filter->pc,&e,10); - if (errno==ERANGE) + errno = 0; + d = Ustrtoul(filter->pc, &e, 10); + if (errno == ERANGE) { - filter->errmsg=CUstrerror(ERANGE); + filter->errmsg = CUstrerror(ERANGE); return -1; } - filter->pc=e; - u=1; - if (*filter->pc=='K') { u=1024; ++filter->pc; } - else if (*filter->pc=='M') { u=1024*1024; ++filter->pc; } - else if (*filter->pc=='G') { u=1024*1024*1024; ++filter->pc; } + filter->pc = e; + u = 1; + if (*filter->pc == 'K') { u = 1024; ++filter->pc; } + else if (*filter->pc == 'M') { u = 1024*1024; ++filter->pc; } + else if (*filter->pc == 'G') { u = 1024*1024*1024; ++filter->pc; } if (d>(ULONG_MAX/u)) { - filter->errmsg=CUstrerror(ERANGE); + filter->errmsg = CUstrerror(ERANGE); return -1; } - d*=u; - *data=d; + d *= u; + *data = d; return 1; } else { - filter->errmsg=CUS "missing number"; + filter->errmsg = CUS "missing number"; return -1; } } @@ -1691,7 +1635,7 @@ else /* Grammar: - string-list = "[" string *("," string) "]" / string + string-list = "[" string *(", " string) "]" / string Arguments: filter points to the Sieve filter including its state @@ -1702,85 +1646,85 @@ Returns: 1 success */ static int -parse_stringlist(struct Sieve *filter, struct String **data) +parse_stringlist(struct Sieve *filter, gstring **data) { -const uschar *orig=filter->pc; +const uschar *orig = filter->pc; int dataCapacity = 0; int dataLength = 0; -struct String *d = NULL; +gstring *d = NULL; int m; -if (*filter->pc=='[') /* string list */ +if (*filter->pc == '[') /* string list */ { ++filter->pc; for (;;) { - if (parse_white(filter)==-1) goto error; + if (parse_white(filter) == -1) goto error; if (dataLength+1 >= dataCapacity) /* increase buffer */ { - struct String *new; + gstring *new; dataCapacity = dataCapacity ? dataCapacity * 2 : 4; - new = store_get(sizeof(struct String) * dataCapacity, GET_UNTAINTED); + new = store_get(sizeof(gstring) * dataCapacity, GET_UNTAINTED); - if (d) memcpy(new,d,sizeof(struct String)*dataLength); + if (d) memcpy(new, d, sizeof(gstring)*dataLength); d = new; } - m=parse_string(filter,&d[dataLength]); - if (m==0) + m = parse_string(filter, &d[dataLength]); + if (m == 0) { - if (dataLength==0) break; + if (dataLength == 0) break; else { - filter->errmsg=CUS "missing string"; + filter->errmsg = CUS "missing string"; goto error; } } - else if (m==-1) goto error; + else if (m == -1) goto error; else ++dataLength; - if (parse_white(filter)==-1) goto error; - if (*filter->pc==',') ++filter->pc; + if (parse_white(filter) == -1) goto error; + if (*filter->pc == ',') ++filter->pc; else break; } - if (*filter->pc==']') + if (*filter->pc == ']') { - d[dataLength].character=(uschar*)0; - d[dataLength].length=-1; + d[dataLength].s = (uschar*)0; + d[dataLength].ptr = -1; ++filter->pc; - *data=d; + *data = d; return 1; } else { - filter->errmsg=CUS "missing closing bracket"; + filter->errmsg = CUS "missing closing bracket"; goto error; } } else /* single string */ { - if (!(d=store_get(sizeof(struct String)*2, GET_UNTAINTED))) + if (!(d = store_get(sizeof(gstring)*2, GET_UNTAINTED))) return -1; - m=parse_string(filter,&d[0]); - if (m==-1) + m = parse_string(filter, &d[0]); + if (m == -1) return -1; - else if (m==0) + else if (m == 0) { - filter->pc=orig; + filter->pc = orig; return 0; } else { - d[1].character=(uschar*)0; - d[1].length=-1; - *data=d; + d[1].s = (uschar*)0; + d[1].ptr = -1; + *data = d; return 1; } } error: -filter->errmsg=CUS "missing string list"; +filter->errmsg = CUS "missing string list"; return -1; } @@ -1792,7 +1736,7 @@ return -1; /* Grammar: address-part = ":localpart" / ":domain" / ":all" - address-part =/ ":user" / ":detail" + address-part = / ":user" / ":detail" Arguments: filter points to the Sieve filter including its state @@ -1803,44 +1747,45 @@ Returns: 1 success -1 syntax error */ -static int parse_addresspart(struct Sieve *filter, enum AddressPart *a) +static int +parse_addresspart(struct Sieve *filter, enum AddressPart *a) { #ifdef SUBADDRESS -if (parse_identifier(filter,CUS ":user")==1) +if (parse_identifier(filter, CUS ":user") == 1) { if (!filter->require_subaddress) { - filter->errmsg=CUS "missing previous require \"subaddress\";"; + filter->errmsg = CUS "missing previous require \"subaddress\";"; return -1; } - *a=ADDRPART_USER; + *a = ADDRPART_USER; return 1; } -else if (parse_identifier(filter,CUS ":detail")==1) +else if (parse_identifier(filter, CUS ":detail") == 1) { if (!filter->require_subaddress) { - filter->errmsg=CUS "missing previous require \"subaddress\";"; + filter->errmsg = CUS "missing previous require \"subaddress\";"; return -1; } - *a=ADDRPART_DETAIL; + *a = ADDRPART_DETAIL; return 1; } else #endif -if (parse_identifier(filter,CUS ":localpart")==1) +if (parse_identifier(filter, CUS ":localpart") == 1) { - *a=ADDRPART_LOCALPART; + *a = ADDRPART_LOCALPART; return 1; } -else if (parse_identifier(filter,CUS ":domain")==1) +else if (parse_identifier(filter, CUS ":domain") == 1) { - *a=ADDRPART_DOMAIN; + *a = ADDRPART_DOMAIN; return 1; } -else if (parse_identifier(filter,CUS ":all")==1) +else if (parse_identifier(filter, CUS ":all") == 1) { - *a=ADDRPART_ALL; + *a = ADDRPART_ALL; return 1; } else return 0; @@ -1864,48 +1809,49 @@ Returns: 1 success -1 incomplete comparator found */ -static int parse_comparator(struct Sieve *filter, enum Comparator *c) +static int +parse_comparator(struct Sieve *filter, enum Comparator *c) { -struct String comparator_name; +gstring comparator_name; -if (parse_identifier(filter,CUS ":comparator")==0) return 0; -if (parse_white(filter)==-1) return -1; -switch (parse_string(filter,&comparator_name)) +if (parse_identifier(filter, CUS ":comparator") == 0) return 0; +if (parse_white(filter) == -1) return -1; +switch (parse_string(filter, &comparator_name)) { case -1: return -1; case 0: { - filter->errmsg=CUS "missing comparator"; + filter->errmsg = CUS "missing comparator"; return -1; } default: { int match; - if (eq_asciicase(&comparator_name,&str_ioctet,0)) + if (eq_asciicase(&comparator_name, &str_ioctet, FALSE)) { - *c=COMP_OCTET; - match=1; + *c = COMP_OCTET; + match = 1; } - else if (eq_asciicase(&comparator_name,&str_iascii_casemap,0)) + else if (eq_asciicase(&comparator_name, &str_iascii_casemap, FALSE)) { - *c=COMP_EN_ASCII_CASEMAP; - match=1; + *c = COMP_EN_ASCII_CASEMAP; + match = 1; } - else if (eq_asciicase(&comparator_name,&str_enascii_casemap,0)) + else if (eq_asciicase(&comparator_name, &str_enascii_casemap, FALSE)) { - *c=COMP_EN_ASCII_CASEMAP; - match=1; + *c = COMP_EN_ASCII_CASEMAP; + match = 1; } - else if (eq_asciicase(&comparator_name,&str_iascii_numeric,0)) + else if (eq_asciicase(&comparator_name, &str_iascii_numeric, FALSE)) { - *c=COMP_ASCII_NUMERIC; - match=1; + *c = COMP_ASCII_NUMERIC; + match = 1; } else { - filter->errmsg=CUS "invalid comparator"; - match=-1; + filter->errmsg = CUS "invalid comparator"; + match = -1; } return match; } @@ -1929,24 +1875,25 @@ Returns: 1 success 0 no match type found */ -static int parse_matchtype(struct Sieve *filter, enum MatchType *m) +static int +parse_matchtype(struct Sieve *filter, enum MatchType *m) { - if (parse_identifier(filter,CUS ":is")==1) - { - *m=MATCH_IS; - return 1; - } - else if (parse_identifier(filter,CUS ":contains")==1) - { - *m=MATCH_CONTAINS; - return 1; - } - else if (parse_identifier(filter,CUS ":matches")==1) - { - *m=MATCH_MATCHES; - return 1; - } - else return 0; +if (parse_identifier(filter, CUS ":is") == 1) +{ + *m = MATCH_IS; + return 1; +} +else if (parse_identifier(filter, CUS ":contains") == 1) +{ + *m = MATCH_CONTAINS; + return 1; +} +else if (parse_identifier(filter, CUS ":matches") == 1) +{ + *m = MATCH_MATCHES; + return 1; +} +else return 0; } @@ -1969,36 +1916,37 @@ Returns: 1 success -1 syntax or execution error */ -static int parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec) +static int +parse_testlist(struct Sieve *filter, int *n, int *num_true, int exec) { -if (parse_white(filter)==-1) return -1; -if (*filter->pc=='(') +if (parse_white(filter) == -1) return -1; +if (*filter->pc == '(') { ++filter->pc; - *n=0; - *num_true=0; + *n = 0; + *num_true = 0; for (;;) { int cond; - switch (parse_test(filter,&cond,exec)) + switch (parse_test(filter, &cond, exec)) { case -1: return -1; - case 0: filter->errmsg=CUS "missing test"; return -1; + case 0: filter->errmsg = CUS "missing test"; return -1; default: ++*n; if (cond) ++*num_true; break; } - if (parse_white(filter)==-1) return -1; - if (*filter->pc==',') ++filter->pc; + if (parse_white(filter) == -1) return -1; + if (*filter->pc == ',') ++filter->pc; else break; } - if (*filter->pc==')') + if (*filter->pc == ')') { ++filter->pc; return 1; } else { - filter->errmsg=CUS "missing closing paren"; + filter->errmsg = CUS "missing closing paren"; return -1; } } @@ -2024,8 +1972,8 @@ Returns: 1 success static int parse_test(struct Sieve *filter, int *cond, int exec) { -if (parse_white(filter)==-1) return -1; -if (parse_identifier(filter,CUS "address")) +if (parse_white(filter) == -1) return -1; +if (parse_identifier(filter, CUS "address")) { /* address-test = "address" { [address-part] [comparator] [match-type] } @@ -2034,85 +1982,85 @@ if (parse_identifier(filter,CUS "address")) header-list From, To, Cc, Bcc, Sender, Resent-From, Resent-To */ - enum AddressPart addressPart=ADDRPART_ALL; - enum Comparator comparator=COMP_EN_ASCII_CASEMAP; - enum MatchType matchType=MATCH_IS; - struct String *hdr,*key; + enum AddressPart addressPart = ADDRPART_ALL; + enum Comparator comparator = COMP_EN_ASCII_CASEMAP; + enum MatchType matchType = MATCH_IS; + gstring *hdr, *key; int m; - int ap=0,co=0,mt=0; + int ap = 0, co = 0, mt = 0; for (;;) { - if (parse_white(filter)==-1) return -1; - if ((m=parse_addresspart(filter,&addressPart))!=0) + if (parse_white(filter) == -1) return -1; + if ((m = parse_addresspart(filter, &addressPart)) != 0) { - if (m==-1) return -1; + if (m == -1) return -1; if (ap) { - filter->errmsg=CUS "address part already specified"; + filter->errmsg = CUS "address part already specified"; return -1; } - else ap=1; + else ap = 1; } - else if ((m=parse_comparator(filter,&comparator))!=0) + else if ((m = parse_comparator(filter, &comparator)) != 0) { - if (m==-1) return -1; + if (m == -1) return -1; if (co) { - filter->errmsg=CUS "comparator already specified"; + filter->errmsg = CUS "comparator already specified"; return -1; } - else co=1; + else co = 1; } - else if ((m=parse_matchtype(filter,&matchType))!=0) + else if ((m = parse_matchtype(filter, &matchType)) != 0) { - if (m==-1) return -1; + if (m == -1) return -1; if (mt) { - filter->errmsg=CUS "match type already specified"; + filter->errmsg = CUS "match type already specified"; return -1; } - else mt=1; + else mt = 1; } else break; } - if (parse_white(filter)==-1) return -1; - if ((m=parse_stringlist(filter,&hdr))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_stringlist(filter, &hdr)) != 1) { - if (m==0) filter->errmsg=CUS "header string list expected"; + if (m == 0) filter->errmsg = CUS "header string list expected"; return -1; } - if (parse_white(filter)==-1) return -1; - if ((m=parse_stringlist(filter,&key))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_stringlist(filter, &key)) != 1) { - if (m==0) filter->errmsg=CUS "key string list expected"; + if (m == 0) filter->errmsg = CUS "key string list expected"; return -1; } - *cond=0; - for (struct String * h = hdr; h->length!=-1 && !*cond; ++h) + *cond = 0; + for (gstring * h = hdr; h->ptr != -1 && !*cond; ++h) { - uschar *header_value=(uschar*)0,*extracted_addr,*end_addr; + uschar * header_value = NULL, * extracted_addr, * end_addr; - if - ( - !eq_asciicase(h,&str_from,0) - && !eq_asciicase(h,&str_to,0) - && !eq_asciicase(h,&str_cc,0) - && !eq_asciicase(h,&str_bcc,0) - && !eq_asciicase(h,&str_sender,0) - && !eq_asciicase(h,&str_resent_from,0) - && !eq_asciicase(h,&str_resent_to,0) - ) + if ( !eq_asciicase(h, &str_from, FALSE) + && !eq_asciicase(h, &str_to, FALSE) + && !eq_asciicase(h, &str_cc, FALSE) + && !eq_asciicase(h, &str_bcc, FALSE) + && !eq_asciicase(h, &str_sender, FALSE) + && !eq_asciicase(h, &str_resent_from, FALSE) + && !eq_asciicase(h, &str_resent_to, FALSE) + ) { - filter->errmsg=CUS "invalid header field"; + filter->errmsg = CUS "invalid header field"; return -1; } if (exec) { /* We are only interested in addresses below, so no MIME decoding */ - if (!(header_value = expand_string(string_sprintf("$rheader_%s",quote(h))))) + if (!(header_value = expand_string(string_sprintf("$rheader_%s", quote(h))))) { - filter->errmsg=CUS "header string expansion failed"; + filter->errmsg = CUS "header string expansion failed"; return -1; } f.parse_allow_group = TRUE; @@ -2121,7 +2069,7 @@ if (parse_identifier(filter,CUS "address")) uschar *error; int start, end, domain; int saveend; - uschar *part=NULL; + uschar *part = NULL; end_addr = parse_find_address_end(header_value, FALSE); saveend = *end_addr; @@ -2130,32 +2078,29 @@ if (parse_identifier(filter,CUS "address")) if (extracted_addr) switch (addressPart) { - case ADDRPART_ALL: part=extracted_addr; break; + case ADDRPART_ALL: part = extracted_addr; break; #ifdef SUBADDRESS case ADDRPART_USER: #endif - case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break; - case ADDRPART_DOMAIN: part=extracted_addr+domain; break; + case ADDRPART_LOCALPART: part = extracted_addr; part[domain-1] = '\0'; break; + case ADDRPART_DOMAIN: part = extracted_addr+domain; break; #ifdef SUBADDRESS - case ADDRPART_DETAIL: part=NULL; break; + case ADDRPART_DETAIL: part = NULL; break; #endif } *end_addr = saveend; - if (part) - { - for (struct String * k = key; k->length !=- 1; ++k) + if (part && extracted_addr) + { + gstring partStr = {.s = part, .ptr = Ustrlen(part), .size = Ustrlen(part)+1}; + for (gstring * k = key; k->ptr != - 1; ++k) { - struct String partStr = {.character = part, .length = Ustrlen(part)}; - - if (extracted_addr) - { - *cond=compare(filter,k,&partStr,comparator,matchType); - if (*cond==-1) return -1; - if (*cond) break; - } + *cond = compare(filter, k, &partStr, comparator, matchType); + if (*cond == -1) return -1; + if (*cond) break; } - } + } + if (saveend == 0) break; header_value = end_addr + 1; } @@ -2165,170 +2110,174 @@ if (parse_identifier(filter,CUS "address")) } return 1; } -else if (parse_identifier(filter,CUS "allof")) +else if (parse_identifier(filter, CUS "allof")) { /* allof-test = "allof" */ - int n,num_true; + int n, num_true; - switch (parse_testlist(filter,&n,&num_true,exec)) + switch (parse_testlist(filter, &n, &num_true, exec)) { case -1: return -1; - case 0: filter->errmsg=CUS "missing test list"; return -1; - default: *cond=(n==num_true); return 1; + case 0: filter->errmsg = CUS "missing test list"; return -1; + default: *cond = (n == num_true); return 1; } } -else if (parse_identifier(filter,CUS "anyof")) +else if (parse_identifier(filter, CUS "anyof")) { /* anyof-test = "anyof" */ - int n,num_true; + int n, num_true; - switch (parse_testlist(filter,&n,&num_true,exec)) + switch (parse_testlist(filter, &n, &num_true, exec)) { case -1: return -1; - case 0: filter->errmsg=CUS "missing test list"; return -1; - default: *cond=(num_true>0); return 1; + case 0: filter->errmsg = CUS "missing test list"; return -1; + default: *cond = (num_true>0); return 1; } } -else if (parse_identifier(filter,CUS "exists")) +else if (parse_identifier(filter, CUS "exists")) { /* exists-test = "exists" */ - struct String *hdr; + gstring *hdr; int m; - if (parse_white(filter)==-1) return -1; - if ((m=parse_stringlist(filter,&hdr))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_stringlist(filter, &hdr)) != 1) { - if (m==0) filter->errmsg=CUS "header string list expected"; + if (m == 0) filter->errmsg = CUS "header string list expected"; return -1; } if (exec) { - *cond=1; - for (struct String * h = hdr; h->length != -1 && *cond; ++h) + *cond = 1; + for (gstring * h = hdr; h->ptr != -1 && *cond; ++h) { uschar *header_def; - header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h))); + header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}", quote(h))); if (!header_def) { - filter->errmsg=CUS "header string expansion failed"; + filter->errmsg = CUS "header string expansion failed"; return -1; } - if (Ustrcmp(header_def,"false")==0) *cond=0; + if (Ustrcmp(header_def,"false") == 0) *cond = 0; } } return 1; } -else if (parse_identifier(filter,CUS "false")) +else if (parse_identifier(filter, CUS "false")) { /* false-test = "false" */ - *cond=0; + *cond = 0; return 1; } -else if (parse_identifier(filter,CUS "header")) +else if (parse_identifier(filter, CUS "header")) { /* header-test = "header" { [comparator] [match-type] } */ - enum Comparator comparator=COMP_EN_ASCII_CASEMAP; - enum MatchType matchType=MATCH_IS; - struct String *hdr,*key; + enum Comparator comparator = COMP_EN_ASCII_CASEMAP; + enum MatchType matchType = MATCH_IS; + gstring *hdr, *key; int m; - int co=0,mt=0; + int co = 0, mt = 0; for (;;) { - if (parse_white(filter)==-1) return -1; - if ((m=parse_comparator(filter,&comparator))!=0) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_comparator(filter, &comparator)) != 0) { - if (m==-1) return -1; + if (m == -1) return -1; if (co) { - filter->errmsg=CUS "comparator already specified"; + filter->errmsg = CUS "comparator already specified"; return -1; } - else co=1; + else co = 1; } - else if ((m=parse_matchtype(filter,&matchType))!=0) + else if ((m = parse_matchtype(filter, &matchType)) != 0) { - if (m==-1) return -1; + if (m == -1) return -1; if (mt) { - filter->errmsg=CUS "match type already specified"; + filter->errmsg = CUS "match type already specified"; return -1; } - else mt=1; + else mt = 1; } else break; } - if (parse_white(filter)==-1) return -1; - if ((m=parse_stringlist(filter,&hdr))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_stringlist(filter, &hdr)) != 1) { - if (m==0) filter->errmsg=CUS "header string list expected"; + if (m == 0) filter->errmsg = CUS "header string list expected"; return -1; } - if (parse_white(filter)==-1) return -1; - if ((m=parse_stringlist(filter,&key))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_stringlist(filter, &key)) != 1) { - if (m==0) filter->errmsg=CUS "key string list expected"; + if (m == 0) filter->errmsg = CUS "key string list expected"; return -1; } - *cond=0; - for (struct String * h = hdr; h->length != -1 && !*cond; ++h) + *cond = 0; + for (gstring * h = hdr; h->ptr != -1 && !*cond; ++h) { if (!is_header(h)) { - filter->errmsg=CUS "invalid header field"; + filter->errmsg = CUS "invalid header field"; return -1; } if (exec) { - struct String header_value; + gstring header_value; uschar *header_def; - expand_header(&header_value,h); - header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}",quote(h))); - if (!header_value.character || !header_def) + expand_header(&header_value, h); + header_def = expand_string(string_sprintf("${if def:header_%s {true}{false}}", quote(h))); + if (!header_value.s || !header_def) { - filter->errmsg=CUS "header string expansion failed"; + filter->errmsg = CUS "header string expansion failed"; return -1; } - for (struct String * k = key; k->length != -1; ++k) - if (Ustrcmp(header_def,"true")==0) + for (gstring * k = key; k->ptr != -1; ++k) + if (Ustrcmp(header_def,"true") == 0) { - *cond=compare(filter,k,&header_value,comparator,matchType); - if (*cond==-1) return -1; + *cond = compare(filter, k, &header_value, comparator, matchType); + if (*cond == -1) return -1; if (*cond) break; } } } return 1; } -else if (parse_identifier(filter,CUS "not")) +else if (parse_identifier(filter, CUS "not")) { - if (parse_white(filter)==-1) return -1; - switch (parse_test(filter,cond,exec)) + if (parse_white(filter) == -1) return -1; + switch (parse_test(filter, cond, exec)) { case -1: return -1; - case 0: filter->errmsg=CUS "missing test"; return -1; - default: *cond=!*cond; return 1; + case 0: filter->errmsg = CUS "missing test"; return -1; + default: *cond = !*cond; return 1; } } -else if (parse_identifier(filter,CUS "size")) +else if (parse_identifier(filter, CUS "size")) { /* relop = ":over" / ":under" @@ -2338,25 +2287,25 @@ else if (parse_identifier(filter,CUS "size")) unsigned long limit; int overNotUnder; - if (parse_white(filter)==-1) return -1; - if (parse_identifier(filter,CUS ":over")) overNotUnder=1; - else if (parse_identifier(filter,CUS ":under")) overNotUnder=0; + if (parse_white(filter) == -1) return -1; + if (parse_identifier(filter, CUS ":over")) overNotUnder = 1; + else if (parse_identifier(filter, CUS ":under")) overNotUnder = 0; else { - filter->errmsg=CUS "missing :over or :under"; + filter->errmsg = CUS "missing :over or :under"; return -1; } - if (parse_white(filter)==-1) return -1; - if (parse_number(filter,&limit)==-1) return -1; - *cond=(overNotUnder ? (message_size>limit) : (message_sizelimit) : (message_sizerequire_envelope) { - filter->errmsg=CUS "missing previous require \"envelope\";"; + filter->errmsg = CUS "missing previous require \"envelope\";"; return -1; } for (;;) { - if (parse_white(filter)==-1) return -1; - if ((m=parse_comparator(filter,&comparator))!=0) + if (parse_white(filter) == -1) return -1; + if ((m = parse_comparator(filter, &comparator)) != 0) { - if (m==-1) return -1; + if (m == -1) return -1; if (co) { - filter->errmsg=CUS "comparator already specified"; + filter->errmsg = CUS "comparator already specified"; return -1; } - else co=1; + else co = 1; } - else if ((m=parse_addresspart(filter,&addressPart))!=0) + else if ((m = parse_addresspart(filter, &addressPart)) != 0) { - if (m==-1) return -1; + if (m == -1) return -1; if (ap) { - filter->errmsg=CUS "address part already specified"; + filter->errmsg = CUS "address part already specified"; return -1; } - else ap=1; + else ap = 1; } - else if ((m=parse_matchtype(filter,&matchType))!=0) + else if ((m = parse_matchtype(filter, &matchType)) != 0) { - if (m==-1) return -1; + if (m == -1) return -1; if (mt) { - filter->errmsg=CUS "match type already specified"; + filter->errmsg = CUS "match type already specified"; return -1; } - else mt=1; + else mt = 1; } else break; } - if (parse_white(filter)==-1) return -1; - if ((m=parse_stringlist(filter,&env))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_stringlist(filter, &env)) != 1) { - if (m==0) filter->errmsg=CUS "envelope string list expected"; + if (m == 0) filter->errmsg = CUS "envelope string list expected"; return -1; } - if (parse_white(filter)==-1) return -1; - if ((m=parse_stringlist(filter,&key))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_stringlist(filter, &key)) != 1) { - if (m==0) filter->errmsg=CUS "key string list expected"; + if (m == 0) filter->errmsg = CUS "key string list expected"; return -1; } - *cond=0; - for (struct String * e = env; e->length != -1 && !*cond; ++e) + *cond = 0; + for (gstring * e = env; e->ptr != -1 && !*cond; ++e) { - const uschar *envelopeExpr=CUS 0; - uschar *envelope=US 0; + const uschar *envelopeExpr = CUS 0; + uschar *envelope = US 0; - if (eq_asciicase(e,&str_from,0)) + if (eq_asciicase(e, &str_from, FALSE)) { switch (addressPart) { - case ADDRPART_ALL: envelopeExpr=CUS "$sender_address"; break; + case ADDRPART_ALL: envelopeExpr = CUS "$sender_address"; break; #ifdef SUBADDRESS case ADDRPART_USER: #endif - case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break; - case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break; + case ADDRPART_LOCALPART: envelopeExpr = CUS "${local_part:$sender_address}"; break; + case ADDRPART_DOMAIN: envelopeExpr = CUS "${domain:$sender_address}"; break; #ifdef SUBADDRESS - case ADDRPART_DETAIL: envelopeExpr=CUS 0; break; + case ADDRPART_DETAIL: envelopeExpr = CUS 0; break; #endif } } - else if (eq_asciicase(e,&str_to,0)) + else if (eq_asciicase(e, &str_to, FALSE)) { switch (addressPart) { - case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break; + case ADDRPART_ALL: envelopeExpr = CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break; #ifdef SUBADDRESS - case ADDRPART_USER: envelopeExpr=filter->useraddress; break; - case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break; + case ADDRPART_USER: envelopeExpr = filter->useraddress; break; + case ADDRPART_DETAIL: envelopeExpr = filter->subaddress; break; #endif - case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break; - case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break; + case ADDRPART_LOCALPART: envelopeExpr = CUS "$local_part_prefix$local_part$local_part_suffix"; break; + case ADDRPART_DOMAIN: envelopeExpr = CUS "$domain"; break; } } #ifdef ENVELOPE_AUTH - else if (eq_asciicase(e,&str_auth,0)) + else if (eq_asciicase(e, &str_auth, FALSE)) { switch (addressPart) { - case ADDRPART_ALL: envelopeExpr=CUS "$authenticated_sender"; break; + case ADDRPART_ALL: envelopeExpr = CUS "$authenticated_sender"; break; #ifdef SUBADDRESS case ADDRPART_USER: #endif - case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$authenticated_sender}"; break; - case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$authenticated_sender}"; break; + case ADDRPART_LOCALPART: envelopeExpr = CUS "${local_part:$authenticated_sender}"; break; + case ADDRPART_DOMAIN: envelopeExpr = CUS "${domain:$authenticated_sender}"; break; #ifdef SUBADDRESS - case ADDRPART_DETAIL: envelopeExpr=CUS 0; break; + case ADDRPART_DETAIL: envelopeExpr = CUS 0; break; #endif } } #endif else { - filter->errmsg=CUS "invalid envelope string"; + filter->errmsg = CUS "invalid envelope string"; return -1; } if (exec && envelopeExpr) { - if (!(envelope=expand_string(US envelopeExpr))) + if (!(envelope = expand_string(US envelopeExpr))) { - filter->errmsg=CUS "header string expansion failed"; + filter->errmsg = CUS "header string expansion failed"; return -1; } - for (struct String * k = key; k->length != -1; ++k) + for (gstring * k = key; k->ptr != -1; ++k) { - struct String envelopeStr = {.character = envelope, .length = Ustrlen(envelope)}; + gstring envelopeStr = {.s = envelope, .ptr = Ustrlen(envelope), .size = Ustrlen(envelope)+1}; - *cond=compare(filter,k,&envelopeStr,comparator,matchType); - if (*cond==-1) return -1; + *cond = compare(filter, k, &envelopeStr, comparator, matchType); + if (*cond == -1) return -1; if (*cond) break; } } @@ -2503,49 +2454,45 @@ else if (parse_identifier(filter,CUS "envelope")) return 1; } #ifdef ENOTIFY -else if (parse_identifier(filter,CUS "valid_notify_method")) +else if (parse_identifier(filter, CUS "valid_notify_method")) { /* valid_notify_method = "valid_notify_method" */ - struct String *uris; + gstring *uris; int m; if (!filter->require_enotify) { - filter->errmsg=CUS "missing previous require \"enotify\";"; + filter->errmsg = CUS "missing previous require \"enotify\";"; return -1; } - if (parse_white(filter)==-1) return -1; - if ((m=parse_stringlist(filter,&uris))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_stringlist(filter, &uris)) != 1) { - if (m==0) filter->errmsg=CUS "URI string list expected"; + if (m == 0) filter->errmsg = CUS "URI string list expected"; return -1; } if (exec) { - *cond=1; - for (struct String * u = uris; u->length != -1 && *cond; ++u) + *cond = 1; + for (gstring * u = uris; u->ptr != -1 && *cond; ++u) { - string_item *recipient; - struct String header,subject,body; + string_item * recipient = NULL; + gstring header = { .s = NULL, .ptr = -1 }; + gstring subject = { .s = NULL, .ptr = -1 }; + gstring body = { .s = NULL, .ptr = -1 }; - recipient=NULL; - header.length=-1; - header.character=(uschar*)0; - subject.length=-1; - subject.character=(uschar*)0; - body.length=-1; - body.character=(uschar*)0; - if (parse_mailto_uri(filter,u->character,&recipient,&header,&subject,&body)!=1) - *cond=0; + if (parse_mailto_uri(filter, u->s, &recipient, &header, &subject, &body) != 1) + *cond = 0; } } return 1; } -else if (parse_identifier(filter,CUS "notify_method_capability")) +else if (parse_identifier(filter, CUS "notify_method_capability")) { /* notify_method_capability = "notify_method_capability" [COMPARATOR] [MATCH-TYPE] @@ -2555,78 +2502,75 @@ else if (parse_identifier(filter,CUS "notify_method_capability")) */ int m; - int co=0,mt=0; + int co = 0, mt = 0; - enum Comparator comparator=COMP_EN_ASCII_CASEMAP; - enum MatchType matchType=MATCH_IS; - struct String uri,capa,*keys; + enum Comparator comparator = COMP_EN_ASCII_CASEMAP; + enum MatchType matchType = MATCH_IS; + gstring uri, capa, *keys; if (!filter->require_enotify) { - filter->errmsg=CUS "missing previous require \"enotify\";"; + filter->errmsg = CUS "missing previous require \"enotify\";"; return -1; } for (;;) { - if (parse_white(filter)==-1) return -1; - if ((m=parse_comparator(filter,&comparator))!=0) + if (parse_white(filter) == -1) return -1; + if ((m = parse_comparator(filter, &comparator)) != 0) { - if (m==-1) return -1; + if (m == -1) return -1; if (co) { - filter->errmsg=CUS "comparator already specified"; + filter->errmsg = CUS "comparator already specified"; return -1; } - else co=1; + else co = 1; } - else if ((m=parse_matchtype(filter,&matchType))!=0) + else if ((m = parse_matchtype(filter, &matchType)) != 0) { - if (m==-1) return -1; + if (m == -1) return -1; if (mt) { - filter->errmsg=CUS "match type already specified"; + filter->errmsg = CUS "match type already specified"; return -1; } - else mt=1; + else mt = 1; } else break; } - if ((m=parse_string(filter,&uri))!=1) + if ((m = parse_string(filter, &uri)) != 1) { - if (m==0) filter->errmsg=CUS "missing notification URI string"; + if (m == 0) filter->errmsg = CUS "missing notification URI string"; return -1; } - if (parse_white(filter)==-1) return -1; - if ((m=parse_string(filter,&capa))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_string(filter, &capa)) != 1) { - if (m==0) filter->errmsg=CUS "missing notification capability string"; + if (m == 0) filter->errmsg = CUS "missing notification capability string"; return -1; } - if (parse_white(filter)==-1) return -1; - if ((m=parse_stringlist(filter,&keys))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_stringlist(filter, &keys)) != 1) { - if (m==0) filter->errmsg=CUS "missing key string list"; + if (m == 0) filter->errmsg = CUS "missing key string list"; return -1; } if (exec) { - string_item *recipient; - struct String header,subject,body; - - *cond=0; - recipient=NULL; - header.length=-1; - header.character=(uschar*)0; - subject.length=-1; - subject.character=(uschar*)0; - body.length=-1; - body.character=(uschar*)0; - if (parse_mailto_uri(filter,uri.character,&recipient,&header,&subject,&body)==1) - if (eq_asciicase(&capa,&str_online,0)==1) - for (struct String * k = keys; k->length != -1; ++k) + string_item * recipient = NULL; + gstring header = { .s = NULL, .ptr = -1 }; + gstring subject = { .s = NULL, .ptr = -1 }; + gstring body = { .s = NULL, .ptr = -1 }; + + *cond = 0; + if (parse_mailto_uri(filter, uri.s, &recipient, &header, &subject, &body) == 1) + if (eq_asciicase(&capa, &str_online, FALSE) == 1) + for (gstring * k = keys; k->ptr != -1; ++k) { - *cond=compare(filter,k,&str_maybe,comparator,matchType); - if (*cond==-1) return -1; + *cond = compare(filter, k, &str_maybe, comparator, matchType); + if (*cond == -1) return -1; if (*cond) break; } } @@ -2654,28 +2598,25 @@ Returns: 2 success by stop */ static int -parse_block(struct Sieve *filter, int exec, - address_item **generated) +parse_block(struct Sieve * filter, int exec, address_item ** generated) { int r; -if (parse_white(filter)==-1) return -1; -if (*filter->pc=='{') +if (parse_white(filter) == -1) + return -1; +if (*filter->pc == '{') { ++filter->pc; - if ((r=parse_commands(filter,exec,generated))==-1 || r==2) return r; - if (*filter->pc=='}') + if ((r = parse_commands(filter, exec, generated)) == -1 || r == 2) return r; + if (*filter->pc == '}') { ++filter->pc; return 1; } - else - { - filter->errmsg=CUS "expecting command or closing brace"; - return -1; - } + filter->errmsg = CUS "expecting command or closing brace"; + return -1; } -else return 0; +return 0; } @@ -2691,19 +2632,18 @@ Returns: 1 success -1 syntax error */ -static int parse_semicolon(struct Sieve *filter) +static int +parse_semicolon(struct Sieve *filter) { - if (parse_white(filter)==-1) return -1; - if (*filter->pc==';') - { - ++filter->pc; - return 1; - } - else +if (parse_white(filter) == -1) + return -1; +if (*filter->pc == ';') { - filter->errmsg=CUS "missing semicolon"; - return -1; + ++filter->pc; + return 1; } +filter->errmsg = CUS "missing semicolon"; +return -1; } @@ -2726,222 +2666,244 @@ parse_commands(struct Sieve *filter, int exec, address_item **generated) { while (*filter->pc) { - if (parse_white(filter)==-1) return -1; - if (parse_identifier(filter,CUS "if")) + if (parse_white(filter) == -1) + return -1; + if (parse_identifier(filter, CUS "if")) { /* if-command = "if" test block *( "elsif" test block ) [ else block ] */ - int cond,m,unsuccessful; + int cond, m, unsuccessful; /* test block */ - if (parse_white(filter)==-1) return -1; - if ((m=parse_test(filter,&cond,exec))==-1) return -1; - if (m==0) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_test(filter, &cond, exec)) == -1) + return -1; + if (m == 0) { - filter->errmsg=CUS "missing test"; + filter->errmsg = CUS "missing test"; return -1; } if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) { - if (exec) debug_printf_indent("if %s\n",cond?"true":"false"); + if (exec) debug_printf_indent("if %s\n", cond?"true":"false"); } - m=parse_block(filter,exec ? cond : 0, generated); - if (m==-1 || m==2) return m; - if (m==0) + m = parse_block(filter, exec ? cond : 0, generated); + if (m == -1 || m == 2) + return m; + if (m == 0) { - filter->errmsg=CUS "missing block"; + filter->errmsg = CUS "missing block"; return -1; } unsuccessful = !cond; for (;;) /* elsif test block */ { - if (parse_white(filter)==-1) return -1; - if (parse_identifier(filter,CUS "elsif")) + if (parse_white(filter) == -1) + return -1; + if (parse_identifier(filter, CUS "elsif")) { - if (parse_white(filter)==-1) return -1; - m=parse_test(filter,&cond,exec && unsuccessful); - if (m==-1 || m==2) return m; - if (m==0) + if (parse_white(filter) == -1) + return -1; + m = parse_test(filter, &cond, exec && unsuccessful); + if (m == -1 || m == 2) + return m; + if (m == 0) { - filter->errmsg=CUS "missing test"; + filter->errmsg = CUS "missing test"; return -1; } if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) { - if (exec) debug_printf_indent("elsif %s\n",cond?"true":"false"); + if (exec) debug_printf_indent("elsif %s\n", cond?"true":"false"); } - m=parse_block(filter,exec && unsuccessful ? cond : 0, generated); - if (m==-1 || m==2) return m; - if (m==0) + m = parse_block(filter, exec && unsuccessful ? cond : 0, generated); + if (m == -1 || m == 2) + return m; + if (m == 0) { - filter->errmsg=CUS "missing block"; + filter->errmsg = CUS "missing block"; return -1; } - if (exec && unsuccessful && cond) unsuccessful = 0; + if (exec && unsuccessful && cond) + unsuccessful = 0; } else break; } /* else block */ - if (parse_white(filter)==-1) return -1; - if (parse_identifier(filter,CUS "else")) + if (parse_white(filter) == -1) + return -1; + if (parse_identifier(filter, CUS "else")) { - m=parse_block(filter,exec && unsuccessful, generated); - if (m==-1 || m==2) return m; - if (m==0) + m = parse_block(filter, exec && unsuccessful, generated); + if (m == -1 || m == 2) + return m; + if (m == 0) { - filter->errmsg=CUS "missing block"; + filter->errmsg = CUS "missing block"; return -1; } } } - else if (parse_identifier(filter,CUS "stop")) + else if (parse_identifier(filter, CUS "stop")) { /* stop-command = "stop" { stop-options } ";" stop-options = */ - if (parse_semicolon(filter)==-1) return -1; + if (parse_semicolon(filter) == -1) + return -1; if (exec) { - filter->pc+=Ustrlen(filter->pc); + filter->pc += Ustrlen(filter->pc); return 2; } } - else if (parse_identifier(filter,CUS "keep")) + else if (parse_identifier(filter, CUS "keep")) { /* keep-command = "keep" { keep-options } ";" keep-options = */ - if (parse_semicolon(filter)==-1) return -1; + if (parse_semicolon(filter) == -1) + return -1; if (exec) { - add_addr(generated,US"inbox",1,0,0,0); + add_addr(generated, US"inbox", 1, 0, 0, 0); filter->keep = 0; } } - else if (parse_identifier(filter,CUS "discard")) + else if (parse_identifier(filter, CUS "discard")) { /* discard-command = "discard" { discard-options } ";" discard-options = */ - if (parse_semicolon(filter)==-1) return -1; - if (exec) filter->keep=0; + if (parse_semicolon(filter) == -1) + return -1; + if (exec) filter->keep = 0; } - else if (parse_identifier(filter,CUS "redirect")) + else if (parse_identifier(filter, CUS "redirect")) { /* redirect-command = "redirect" redirect-options "string" ";" redirect-options = - redirect-options =) ":copy" + redirect-options = ) ":copy" */ - struct String recipient; + gstring recipient; int m; - int copy=0; + int copy = 0; for (;;) { - if (parse_white(filter)==-1) return -1; - if (parse_identifier(filter,CUS ":copy")==1) + if (parse_white(filter) == -1) + return -1; + if (parse_identifier(filter, CUS ":copy") == 1) { if (!filter->require_copy) { - filter->errmsg=CUS "missing previous require \"copy\";"; + filter->errmsg = CUS "missing previous require \"copy\";"; return -1; } - copy=1; + copy = 1; } else break; } - if (parse_white(filter)==-1) return -1; - if ((m=parse_string(filter,&recipient))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_string(filter, &recipient)) != 1) { - if (m==0) filter->errmsg=CUS "missing redirect recipient string"; + if (m == 0) + filter->errmsg = CUS "missing redirect recipient string"; return -1; } - if (strchr(CCS recipient.character,'@')==(char*)0) + if (strchr(CCS recipient.s, '@') == NULL) { - filter->errmsg=CUS "unqualified recipient address"; + filter->errmsg = CUS "unqualified recipient address"; return -1; } if (exec) { - add_addr(generated,recipient.character,0,0,0,0); + add_addr(generated, recipient.s, 0, 0, 0, 0); if (!copy) filter->keep = 0; } - if (parse_semicolon(filter)==-1) return -1; + if (parse_semicolon(filter) == -1) return -1; } - else if (parse_identifier(filter,CUS "fileinto")) + else if (parse_identifier(filter, CUS "fileinto")) { /* fileinto-command = "fileinto" { fileinto-options } string ";" fileinto-options = - fileinto-options =) [ ":copy" ] + fileinto-options = ) [ ":copy" ] */ - struct String folder; + gstring folder; uschar *s; int m; unsigned long maxage, maxmessages, maxstorage; - int copy=0; + int copy = 0; maxage = maxmessages = maxstorage = 0; if (!filter->require_fileinto) { - filter->errmsg=CUS "missing previous require \"fileinto\";"; + filter->errmsg = CUS "missing previous require \"fileinto\";"; return -1; } for (;;) { - if (parse_white(filter)==-1) return -1; - if (parse_identifier(filter,CUS ":copy")==1) + if (parse_white(filter) == -1) + return -1; + if (parse_identifier(filter, CUS ":copy") == 1) { if (!filter->require_copy) { - filter->errmsg=CUS "missing previous require \"copy\";"; + filter->errmsg = CUS "missing previous require \"copy\";"; return -1; } - copy=1; + copy = 1; } else break; } - if (parse_white(filter)==-1) return -1; - if ((m=parse_string(filter,&folder))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_string(filter, &folder)) != 1) { - if (m==0) filter->errmsg=CUS "missing fileinto folder string"; + if (m == 0) filter->errmsg = CUS "missing fileinto folder string"; return -1; } - m=0; s=folder.character; - if (folder.length==0) m=1; - if (Ustrcmp(s,"..")==0 || Ustrncmp(s,"../",3)==0) m=1; + m = 0; s = folder.s; + if (folder.ptr == 0) + m = 1; + if (Ustrcmp(s,"..") == 0 || Ustrncmp(s,"../", 3) == 0) + m = 1; else while (*s) { - if (Ustrcmp(s,"/..")==0 || Ustrncmp(s,"/../",4)==0) { m=1; break; } + if (Ustrcmp(s,"/..") == 0 || Ustrncmp(s,"/../", 4) == 0) { m = 1; break; } ++s; } if (m) { - filter->errmsg=CUS "invalid folder"; + filter->errmsg = CUS "invalid folder"; return -1; } if (exec) { - add_addr(generated, folder.character, 1, maxage, maxmessages, maxstorage); + add_addr(generated, folder.s, 1, maxage, maxmessages, maxstorage); if (!copy) filter->keep = 0; } - if (parse_semicolon(filter)==-1) return -1; + if (parse_semicolon(filter) == -1) + return -1; } #ifdef ENOTIFY - else if (parse_identifier(filter,CUS "notify")) + else if (parse_identifier(filter, CUS "notify")) { /* notify-command = "notify" { notify-options } ";" @@ -2952,126 +2914,125 @@ while (*filter->pc) */ int m; - struct String from; - struct String importance; - struct String message; - struct String method; + gstring from = { .s = NULL, .ptr = -1 }; + gstring importance = { .s = NULL, .ptr = -1 }; + gstring message = { .s = NULL, .ptr = -1 }; + gstring method; struct Notification *already; - string_item *recipient; - struct String header; - struct String subject; - struct String body; + string_item * recipient = NULL; + gstring header = { .s = NULL, .ptr = -1 }; + gstring subject = { .s = NULL, .ptr = -1 }; + gstring body = { .s = NULL, .ptr = -1 }; uschar *envelope_from; - struct String auto_submitted_value; + gstring auto_submitted_value; uschar *auto_submitted_def; if (!filter->require_enotify) { - filter->errmsg=CUS "missing previous require \"enotify\";"; + filter->errmsg = CUS "missing previous require \"enotify\";"; return -1; } - from.character=(uschar*)0; - from.length=-1; - importance.character=(uschar*)0; - importance.length=-1; - message.character=(uschar*)0; - message.length=-1; - recipient=NULL; - header.length=-1; - header.character=(uschar*)0; - subject.length=-1; - subject.character=(uschar*)0; - body.length=-1; - body.character=(uschar*)0; envelope_from = sender_address && sender_address[0] ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US ""; if (!envelope_from) { - filter->errmsg=CUS "expansion failure for envelope from"; + filter->errmsg = CUS "expansion failure for envelope from"; return -1; } for (;;) { - if (parse_white(filter)==-1) return -1; - if (parse_identifier(filter,CUS ":from")==1) + if (parse_white(filter) == -1) + return -1; + if (parse_identifier(filter, CUS ":from") == 1) { - if (parse_white(filter)==-1) return -1; - if ((m=parse_string(filter,&from))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_string(filter, &from)) != 1) { - if (m==0) filter->errmsg=CUS "from string expected"; + if (m == 0) filter->errmsg = CUS "from string expected"; return -1; } } - else if (parse_identifier(filter,CUS ":importance")==1) + else if (parse_identifier(filter, CUS ":importance") == 1) { - if (parse_white(filter)==-1) return -1; - if ((m=parse_string(filter,&importance))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_string(filter, &importance)) != 1) { - if (m==0) filter->errmsg=CUS "importance string expected"; + if (m == 0) + filter->errmsg = CUS "importance string expected"; return -1; } - if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3') + if (importance.ptr != 1 || importance.s[0] < '1' || importance.s[0] > '3') { - filter->errmsg=CUS "invalid importance"; + filter->errmsg = CUS "invalid importance"; return -1; } } - else if (parse_identifier(filter,CUS ":options")==1) + else if (parse_identifier(filter, CUS ":options") == 1) { - if (parse_white(filter)==-1) return -1; + if (parse_white(filter) == -1) + return -1; } - else if (parse_identifier(filter,CUS ":message")==1) + else if (parse_identifier(filter, CUS ":message") == 1) { - if (parse_white(filter)==-1) return -1; - if ((m=parse_string(filter,&message))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_string(filter, &message)) != 1) { - if (m==0) filter->errmsg=CUS "message string expected"; + if (m == 0) + filter->errmsg = CUS "message string expected"; return -1; } } else break; } - if (parse_white(filter)==-1) return -1; - if ((m=parse_string(filter,&method))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_string(filter, &method)) != 1) { - if (m==0) filter->errmsg=CUS "missing method string"; + if (m == 0) + filter->errmsg = CUS "missing method string"; return -1; } - if (parse_semicolon(filter)==-1) return -1; - if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1) + if (parse_semicolon(filter) == -1) + return -1; + if (parse_mailto_uri(filter, method.s, &recipient, &header, &subject, &body) != 1) return -1; if (exec) { - if (message.length==-1) message=subject; - if (message.length==-1) expand_header(&message,&str_subject); - expand_header(&auto_submitted_value,&str_auto_submitted); - auto_submitted_def=expand_string(US"${if def:header_auto-submitted {true}{false}}"); - if (!auto_submitted_value.character || !auto_submitted_def) + if (message.ptr == -1) + message = subject; + if (message.ptr == -1) + expand_header(&message, &str_subject); + expand_header(&auto_submitted_value, &str_auto_submitted); + auto_submitted_def = expand_string(US"${if def:header_auto-submitted {true}{false}}"); + if (!auto_submitted_value.s || !auto_submitted_def) { - filter->errmsg=CUS "header string expansion failed"; + filter->errmsg = CUS "header string expansion failed"; return -1; } - if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0) + if (Ustrcmp(auto_submitted_def,"true") != 0 || Ustrcmp(auto_submitted_value.s,"no") == 0) { - for (already=filter->notified; already; already=already->next) + for (already = filter->notified; already; already = already->next) { - if (already->method.length==method.length - && (method.length==-1 || Ustrcmp(already->method.character,method.character)==0) - && already->importance.length==importance.length - && (importance.length==-1 || Ustrcmp(already->importance.character,importance.character)==0) - && already->message.length==message.length - && (message.length==-1 || Ustrcmp(already->message.character,message.character)==0)) + if ( already->method.ptr == method.ptr + && (method.ptr == -1 || Ustrcmp(already->method.s, method.s) == 0) + && already->importance.ptr == importance.ptr + && (importance.ptr == -1 || Ustrcmp(already->importance.s, importance.s) == 0) + && already->message.ptr == message.ptr + && (message.ptr == -1 || Ustrcmp(already->message.s, message.s) == 0)) break; } if (!already) /* New notification, process it */ { struct Notification * sent = store_get(sizeof(struct Notification), GET_UNTAINTED); - sent->method=method; - sent->importance=importance; - sent->message=message; - sent->next=filter->notified; - filter->notified=sent; + sent->method = method; + sent->importance = importance; + sent->message = message; + sent->next = filter->notified; + filter->notified = sent; #ifndef COMPILE_SYNTAX_CHECKER if (filter_test == FTEST_NONE) { @@ -3082,35 +3043,35 @@ while (*filter->pc) { FILE * f = fdopen(fd, "wb"); - fprintf(f,"From: %s\n", from.length == -1 + fprintf(f,"From: %s\n", from.ptr == -1 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") - : from.character); - for (string_item * p = recipient; p; p=p->next) - fprintf(f, "To: %s\n",p->text); + : from.s); + for (string_item * p = recipient; p; p = p->next) + fprintf(f, "To: %s\n", p->text); fprintf(f, "Auto-Submitted: auto-notified; %s\n", filter->enotify_mailto_owner); - if (header.length > 0) fprintf(f, "%s", header.character); - if (message.length==-1) + if (header.ptr > 0) fprintf(f, "%s", header.s); + if (message.ptr == -1) { - message.character=US"Notification"; - message.length=Ustrlen(message.character); + message.s = US"Notification"; + message.ptr = Ustrlen(message.s); } - if (message.length != -1) - fprintf(f, "Subject: %s\n", parse_quote_2047(message.character, - message.length, US"utf-8", TRUE)); + if (message.ptr != -1) + fprintf(f, "Subject: %s\n", parse_quote_2047(message.s, + message.ptr, US"utf-8", TRUE)); fprintf(f,"\n"); - if (body.length > 0) fprintf(f, "%s\n", body.character); + if (body.ptr > 0) fprintf(f, "%s\n", body.s); fflush(f); (void)fclose(f); (void)child_close(pid, 0); } } if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter) - debug_printf_indent("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS ""); + debug_printf_indent("Notification to `%s': '%s'.\n", method.s, message.ptr != -1 ? message.s : CUS ""); #endif } else if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter) - debug_printf_indent("Repeated notification to `%s' ignored.\n",method.character); + debug_printf_indent("Repeated notification to `%s' ignored.\n", method.s); } else if ((filter_test != FTEST_NONE && debug_selector != 0) || debug_selector & D_filter) @@ -3119,7 +3080,7 @@ while (*filter->pc) } #endif #ifdef VACATION - else if (parse_identifier(filter,CUS "vacation")) + else if (parse_identifier(filter, CUS "vacation")) { /* vacation-command = "vacation" { vacation-options } ";" @@ -3133,119 +3094,134 @@ while (*filter->pc) int m; unsigned long days; - struct String subject; - struct String from; - struct String *addresses; + gstring subject; + gstring from; + gstring *addresses; int reason_is_mime; string_item *aliases; - struct String handle; - struct String reason; + gstring handle; + gstring reason; if (!filter->require_vacation) { - filter->errmsg=CUS "missing previous require \"vacation\";"; + filter->errmsg = CUS "missing previous require \"vacation\";"; return -1; } if (exec) { if (filter->vacation_ran) { - filter->errmsg=CUS "trying to execute vacation more than once"; + filter->errmsg = CUS "trying to execute vacation more than once"; return -1; } - filter->vacation_ran=1; - } - days=VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7; - subject.character=(uschar*)0; - subject.length=-1; - from.character=(uschar*)0; - from.length=-1; - addresses=(struct String*)0; - aliases=NULL; - reason_is_mime=0; - handle.character=(uschar*)0; - handle.length=-1; + filter->vacation_ran = TRUE; + } + days = VACATION_MIN_DAYS>7 ? VACATION_MIN_DAYS : 7; + subject.s = (uschar*)0; + subject.ptr = -1; + from.s = (uschar*)0; + from.ptr = -1; + addresses = (gstring*)0; + aliases = NULL; + reason_is_mime = 0; + handle.s = (uschar*)0; + handle.ptr = -1; for (;;) { - if (parse_white(filter)==-1) return -1; - if (parse_identifier(filter,CUS ":days")==1) + if (parse_white(filter) == -1) + return -1; + if (parse_identifier(filter, CUS ":days") == 1) { - if (parse_white(filter)==-1) return -1; - if (parse_number(filter,&days)==-1) return -1; - if (daysVACATION_MAX_DAYS) days=VACATION_MAX_DAYS; + if (parse_white(filter) == -1) + return -1; + if (parse_number(filter, &days) == -1) + return -1; + if (daysVACATION_MAX_DAYS) + days = VACATION_MAX_DAYS; } - else if (parse_identifier(filter,CUS ":subject")==1) + else if (parse_identifier(filter, CUS ":subject") == 1) { - if (parse_white(filter)==-1) return -1; - if ((m=parse_string(filter,&subject))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_string(filter, &subject)) != 1) { - if (m==0) filter->errmsg=CUS "subject string expected"; + if (m == 0) + filter->errmsg = CUS "subject string expected"; return -1; } } - else if (parse_identifier(filter,CUS ":from")==1) + else if (parse_identifier(filter, CUS ":from") == 1) { - if (parse_white(filter)==-1) return -1; - if ((m=parse_string(filter,&from))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_string(filter, &from)) != 1) { - if (m==0) filter->errmsg=CUS "from string expected"; + if (m == 0) + filter->errmsg = CUS "from string expected"; return -1; } - if (check_mail_address(filter,&from)!=1) + if (check_mail_address(filter, &from) != 1) return -1; } - else if (parse_identifier(filter,CUS ":addresses")==1) + else if (parse_identifier(filter, CUS ":addresses") == 1) { - if (parse_white(filter)==-1) return -1; - if ((m=parse_stringlist(filter,&addresses))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_stringlist(filter, &addresses)) != 1) { - if (m==0) filter->errmsg=CUS "addresses string list expected"; + if (m == 0) + filter->errmsg = CUS "addresses string list expected"; return -1; } - for (struct String * a = addresses; a->length != -1; ++a) + for (gstring * a = addresses; a->ptr != -1; ++a) { string_item * new = store_get(sizeof(string_item), GET_UNTAINTED); - new->text = store_get(a->length+1, a->character); - if (a->length) memcpy(new->text,a->character,a->length); - new->text[a->length]='\0'; - new->next=aliases; - aliases=new; + new->text = store_get(a->ptr+1, a->s); + if (a->ptr) memcpy(new->text, a->s, a->ptr); + new->text[a->ptr] = '\0'; + new->next = aliases; + aliases = new; } } - else if (parse_identifier(filter,CUS ":mime")==1) - reason_is_mime=1; - else if (parse_identifier(filter,CUS ":handle")==1) + else if (parse_identifier(filter, CUS ":mime") == 1) + reason_is_mime = 1; + else if (parse_identifier(filter, CUS ":handle") == 1) { - if (parse_white(filter)==-1) return -1; - if ((m=parse_string(filter,&from))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_string(filter, &from)) != 1) { - if (m==0) filter->errmsg=CUS "handle string expected"; + if (m == 0) + filter->errmsg = CUS "handle string expected"; return -1; } } else break; } - if (parse_white(filter)==-1) return -1; - if ((m=parse_string(filter,&reason))!=1) + if (parse_white(filter) == -1) + return -1; + if ((m = parse_string(filter, &reason)) != 1) { - if (m==0) filter->errmsg=CUS "missing reason string"; + if (m == 0) + filter->errmsg = CUS "missing reason string"; return -1; } if (reason_is_mime) { - uschar *s,*end; + uschar *s, *end; - for (s = reason.character, end = reason.character + reason.length; - serrmsg=CUS "MIME reason string contains 8bit text"; + filter->errmsg = CUS "MIME reason string contains 8bit text"; return -1; } } - if (parse_semicolon(filter)==-1) return -1; + if (parse_semicolon(filter) == -1) return -1; if (exec) { @@ -3255,7 +3231,7 @@ while (*filter->pc) uschar hexdigest[33]; gstring * once; - if (filter_personal(aliases,TRUE)) + if (filter_personal(aliases, TRUE)) { if (filter_test == FTEST_NONE) { @@ -3267,19 +3243,22 @@ while (*filter->pc) md5_start(&base); - if (handle.length==-1) + if (handle.ptr == -1) { gstring * key = NULL; - if (subject.length!=-1) key =string_catn(key, subject.character, subject.length); - if (from.length!=-1) key = string_catn(key, from.character, from.length); + if (subject.ptr != -1) + key = string_catn(key, subject.s, subject.ptr); + if (from.ptr != -1) + key = string_catn(key, from.s, from.ptr); key = string_catn(key, reason_is_mime?US"1":US"0", 1); - key = string_catn(key, reason.character, reason.length); + key = string_catn(key, reason.s, reason.ptr); md5_end(&base, key->s, key->ptr, digest); } else - md5_end(&base, handle.character, handle.length, digest); + md5_end(&base, handle.s, handle.ptr, digest); - for (int i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]); + for (int i = 0; i < 16; i++) + sprintf(CS (hexdigest+2*i), "%02X", digest[i]); if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0) debug_printf_indent("Sieve: mail was personal, vacation file basename: %s\n", hexdigest); @@ -3292,23 +3271,23 @@ while (*filter->pc) /* process subject */ - if (subject.length==-1) + if (subject.ptr == -1) { uschar * subject_def; subject_def = expand_string(US"${if def:header_subject {true}{false}}"); - if (subject_def && Ustrcmp(subject_def,"true")==0) + if (subject_def && Ustrcmp(subject_def,"true") == 0) { gstring * g = string_catn(NULL, US"Auto: ", 6); - expand_header(&subject,&str_subject); - g = string_catn(g, subject.character, subject.length); - subject.length = len_string_from_gstring(g, &subject.character); + expand_header(&subject, &str_subject); + g = string_catn(g, subject.s, subject.ptr); + subject.ptr = len_string_from_gstring(g, &subject.s); } else { - subject.character = US"Automated reply"; - subject.length = Ustrlen(subject.character); + subject.s = US"Automated reply"; + subject.ptr = Ustrlen(subject.s); } } @@ -3320,45 +3299,44 @@ while (*filter->pc) addr->next = *generated; *generated = addr; addr->reply = store_get(sizeof(reply_item), GET_UNTAINTED); - memset(addr->reply,0,sizeof(reply_item)); /* XXX */ + memset(addr->reply, 0, sizeof(reply_item)); /* XXX */ addr->reply->to = string_copy(sender_address); - if (from.length==-1) + if (from.ptr == -1) addr->reply->from = expand_string(US"$local_part@$domain"); else - addr->reply->from = from.character; + addr->reply->from = from.s; /* deconst cast safe as we pass in a non-const item */ - addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", TRUE); + addr->reply->subject = US parse_quote_2047(subject.s, subject.ptr, US"utf-8", TRUE); addr->reply->oncelog = string_from_gstring(once); - addr->reply->once_repeat=days*86400; + addr->reply->once_repeat = days*86400; /* build body and MIME headers */ if (reason_is_mime) { - uschar *mime_body,*reason_end; - static const uschar nlnl[]="\r\n\r\n"; + uschar *mime_body, *reason_end; + static const uschar nlnl[] = "\r\n\r\n"; for ( - mime_body = reason.character, reason_end = reason.character + reason.length; + mime_body = reason.s, reason_end = reason.s + reason.ptr; mime_body < (reason_end-(sizeof(nlnl)-1)) && memcmp(mime_body, nlnl, (sizeof(nlnl)-1)); ) mime_body++; - addr->reply->headers = string_copyn(reason.character, mime_body-reason.character); + addr->reply->headers = string_copyn(reason.s, mime_body-reason.s); - if (mime_body+(sizeof(nlnl)-1)reply->text = string_copyn(mime_body, reason_end-mime_body); } else { - struct String qp = { .character = NULL, .length = 0 }; /* Keep compiler happy (PH) */ - addr->reply->headers = US"MIME-Version: 1.0\n" "Content-Type: text/plain;\n" "\tcharset=\"utf-8\"\n" "Content-Transfer-Encoding: quoted-printable"; - addr->reply->text = quoted_printable_encode(&reason,&qp)->character; + addr->reply->text = quoted_printable_encode(&reason)->s; } } } @@ -3390,32 +3368,32 @@ Returns: 1 success static int parse_start(struct Sieve *filter, int exec, address_item **generated) { -filter->pc=filter->filter; -filter->line=1; -filter->keep=1; -filter->require_envelope=0; -filter->require_fileinto=0; +filter->pc = filter->filter; +filter->line = 1; +filter->keep = 1; +filter->require_envelope = 0; +filter->require_fileinto = 0; #ifdef ENCODED_CHARACTER -filter->require_encoded_character=0; +filter->require_encoded_character = FALSE; #endif #ifdef ENVELOPE_AUTH -filter->require_envelope_auth=0; +filter->require_envelope_auth = 0; #endif #ifdef ENOTIFY -filter->require_enotify=0; -filter->notified=(struct Notification*)0; +filter->require_enotify = 0; +filter->notified = (struct Notification*)0; #endif #ifdef SUBADDRESS -filter->require_subaddress=0; +filter->require_subaddress = FALSE; #endif #ifdef VACATION -filter->require_vacation=0; -filter->vacation_ran=0; +filter->require_vacation = FALSE; +filter->vacation_ran = 0; /*XXX missing init? */ #endif -filter->require_copy=0; -filter->require_iascii_numeric=0; +filter->require_copy = FALSE; +filter->require_iascii_numeric = FALSE; -if (parse_white(filter)==-1) return -1; +if (parse_white(filter) == -1) return -1; if (exec && filter->vacation_directory && filter_test == FTEST_NONE) { @@ -3438,83 +3416,83 @@ if (exec && filter->vacation_directory && filter_test == FTEST_NONE) time(&now); while ((oncelog = readdir(oncelogdir))) - if (strlen(oncelog->d_name)==32) + if (strlen(oncelog->d_name) == 32) { uschar *s = string_sprintf("%s/%s", filter->vacation_directory, oncelog->d_name); - if (Ustat(s,&properties) == 0 && properties.st_mtime+VACATION_MAX_DAYS*86400 < now) + if (Ustat(s, &properties) == 0 && properties.st_mtime+VACATION_MAX_DAYS*86400 < now) Uunlink(s); } closedir(oncelogdir); } } -while (parse_identifier(filter,CUS "require")) +while (parse_identifier(filter, CUS "require")) { /* require-command = "require" */ - struct String *cap; + gstring *cap; int m; - if (parse_white(filter)==-1) return -1; - if ((m=parse_stringlist(filter,&cap))!=1) + if (parse_white(filter) == -1) return -1; + if ((m = parse_stringlist(filter, &cap)) != 1) { - if (m==0) filter->errmsg=CUS "capability string list expected"; + if (m == 0) filter->errmsg = CUS "capability string list expected"; return -1; } - for (struct String * check = cap; check->character; ++check) + for (gstring * check = cap; check->s; ++check) { - if (eq_octet(check,&str_envelope,0)) filter->require_envelope=1; - else if (eq_octet(check,&str_fileinto,0)) filter->require_fileinto=1; + if (eq_octet(check, &str_envelope, FALSE)) filter->require_envelope = 1; + else if (eq_octet(check, &str_fileinto, FALSE)) filter->require_fileinto = 1; #ifdef ENCODED_CHARACTER - else if (eq_octet(check,&str_encoded_character,0)) filter->require_encoded_character=1; + else if (eq_octet(check, &str_encoded_character, FALSE)) filter->require_encoded_character = TRUE; #endif #ifdef ENVELOPE_AUTH - else if (eq_octet(check,&str_envelope_auth,0)) filter->require_envelope_auth=1; + else if (eq_octet(check, &str_envelope_auth, FALSE)) filter->require_envelope_auth = 1; #endif #ifdef ENOTIFY - else if (eq_octet(check,&str_enotify,0)) + else if (eq_octet(check, &str_enotify, FALSE)) { if (!filter->enotify_mailto_owner) { - filter->errmsg=CUS "enotify disabled"; + filter->errmsg = CUS "enotify disabled"; return -1; } - filter->require_enotify=1; + filter->require_enotify = 1; } #endif #ifdef SUBADDRESS - else if (eq_octet(check,&str_subaddress,0)) filter->require_subaddress=1; + else if (eq_octet(check, &str_subaddress, FALSE)) filter->require_subaddress = TRUE; #endif #ifdef VACATION - else if (eq_octet(check,&str_vacation,0)) + else if (eq_octet(check, &str_vacation, FALSE)) { if (filter_test == FTEST_NONE && !filter->vacation_directory) { - filter->errmsg=CUS "vacation disabled"; + filter->errmsg = CUS "vacation disabled"; return -1; } - filter->require_vacation=1; + filter->require_vacation = TRUE; } #endif - else if (eq_octet(check,&str_copy,0)) filter->require_copy=1; - else if (eq_octet(check,&str_comparator_ioctet,0)) ; - else if (eq_octet(check,&str_comparator_iascii_casemap,0)) ; - else if (eq_octet(check,&str_comparator_enascii_casemap,0)) ; - else if (eq_octet(check,&str_comparator_iascii_numeric,0)) filter->require_iascii_numeric=1; + else if (eq_octet(check, &str_copy, FALSE)) filter->require_copy = TRUE; + else if (eq_octet(check, &str_comparator_ioctet, FALSE)) ; + else if (eq_octet(check, &str_comparator_iascii_casemap, FALSE)) ; + else if (eq_octet(check, &str_comparator_enascii_casemap, FALSE)) ; + else if (eq_octet(check, &str_comparator_iascii_numeric, FALSE)) filter->require_iascii_numeric = TRUE; else { - filter->errmsg=CUS "unknown capability"; + filter->errmsg = CUS "unknown capability"; return -1; } } - if (parse_semicolon(filter)==-1) return -1; + if (parse_semicolon(filter) == -1) return -1; } - if (parse_commands(filter,exec,generated)==-1) return -1; + if (parse_commands(filter, exec, generated) == -1) return -1; if (*filter->pc) { - filter->errmsg=CUS "syntax error"; + filter->errmsg = CUS "syntax error"; return -1; } return 1; @@ -3601,12 +3579,12 @@ if (parse_start(&sieve, 1, generated) == 1) } else { - msg = string_sprintf("Sieve error: %s in line %d",sieve.errmsg,sieve.line); + msg = string_sprintf("Sieve error: %s in line %d", sieve.errmsg, sieve.line); #ifdef COMPILE_SYNTAX_CHECKER r = FF_ERROR; *error = msg; #else - add_addr(generated,US"inbox",1,0,0,0); + add_addr(generated, US"inbox", 1, 0, 0, 0); r = FF_DELIVERED; #endif } diff --git a/src/src/structs.h b/src/src/structs.h index 3f237fce5..9d2a76ef2 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -469,8 +469,8 @@ typedef struct ip_address_item { /* Structure for chaining together arbitrary strings. */ typedef struct string_item { - struct string_item *next; - uschar *text; + struct string_item * next; + uschar * text; } string_item; /* Information about a soft delivery failure, for use when calculating commit 1209e3e19e292cee517e43a2ccfe9b44b33bb1dc Author: Jasen Betts Date: Sun Jul 23 13:43:59 2023 +0100 Expansions: disallow UTF-16 surrogates from ${utf8clean:...}. Bug 2998 diff --git a/src/src/expand.c b/src/src/expand.c index fea6501fe..d8ea7ae6b 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -7862,7 +7862,7 @@ NOT_ITEM: ; case EOP_UTF8CLEAN: { int seq_len = 0, index = 0, bytes_left = 0, complete; - long codepoint = -1; + ulong codepoint = (ulong)-1; uschar seq_buff[4]; /* accumulate utf-8 here */ /* Manually track tainting, as we deal in individual chars below */ @@ -7896,6 +7896,15 @@ NOT_ITEM: ; if (--bytes_left == 0) /* codepoint complete */ if(codepoint > 0x10FFFF) /* is it too large? */ complete = -1; /* error (RFC3629 limit) */ + else if ( (codepoint & 0x1FF800 ) == 0xD800 ) /* surrogate */ + /* A UTF-16 surrogate (which should be one of a pair that + encode a Unicode codepoint that is outside the Basic + Multilingual Plane). Error, not UTF8. + RFC2279.2 is slightly unclear on this, but + https://unicodebook.readthedocs.io/issues.html#strict-utf8-decoder + says "Surrogates characters are also invalid in UTF-8: + characters in U+D800—U+DFFF have to be rejected." */ + complete = -1; else { /* finished; output utf-8 sequence */ yield = string_catn(yield, seq_buff, seq_len); @@ -7905,27 +7914,25 @@ NOT_ITEM: ; } else /* no bytes left: new sequence */ { - if(!(c & 0x80)) /* 1-byte sequence, US-ASCII, keep it */ + if (!(c & 0x80)) /* 1-byte sequence, US-ASCII, keep it */ { yield = string_catn(yield, &c, 1); continue; } - if((c & 0xe0) == 0xc0) /* 2-byte sequence */ - { - if(c == 0xc0 || c == 0xc1) /* 0xc0 and 0xc1 are illegal */ + if ((c & 0xe0) == 0xc0) /* 2-byte sequence */ + if (c == 0xc0 || c == 0xc1) /* 0xc0 and 0xc1 are illegal */ complete = -1; else { - bytes_left = 1; - codepoint = c & 0x1f; + bytes_left = 1; + codepoint = c & 0x1f; } - } - else if((c & 0xf0) == 0xe0) /* 3-byte sequence */ + else if ((c & 0xf0) == 0xe0) /* 3-byte sequence */ { bytes_left = 2; codepoint = c & 0x0f; } - else if((c & 0xf8) == 0xf0) /* 4-byte sequence */ + else if ((c & 0xf8) == 0xf0) /* 4-byte sequence */ { bytes_left = 3; codepoint = c & 0x07; commit 249f39385d608eaa6a3daabce6f9bcfa15eb2d3c Author: Jeremy Harris Date: Sun Jul 23 13:56:32 2023 +0100 Fix use of typedef for FreeBSD Broken-by: 1209e3e19e29 diff --git a/src/src/expand.c b/src/src/expand.c index d8ea7ae6b..ae1657549 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -7862,7 +7862,7 @@ NOT_ITEM: ; case EOP_UTF8CLEAN: { int seq_len = 0, index = 0, bytes_left = 0, complete; - ulong codepoint = (ulong)-1; + u_long codepoint = (u_long)-1; uschar seq_buff[4]; /* accumulate utf-8 here */ /* Manually track tainting, as we deal in individual chars below */ commit 8e9770348dc4173ab83657ee023c22f479ebb712 Author: Jeremy Harris Date: Mon Jul 24 13:30:40 2023 +0100 GnuTLS: fix crash with "tls_dhparam = none" diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index c3e2d98e8..dd70e73e1 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -727,7 +727,7 @@ file is never present. If two processes both compute some new parameters, you waste a bit of effort, but it doesn't seem worth messing around with locking to prevent this. -Returns: OK/DEFER/FAIL +Returns: OK/DEFER (expansion issue)/FAIL (requested none) */ static int @@ -765,7 +765,7 @@ else if (Ustrcmp(exp_tls_dhparam, "historic") == 0) else if (Ustrcmp(exp_tls_dhparam, "none") == 0) { DEBUG(D_tls) debug_printf("Requested no DH parameters\n"); - return OK; + return FAIL; } else if (exp_tls_dhparam[0] != '/') { @@ -2002,10 +2002,10 @@ Returns: OK/DEFER/FAIL */ static int -tls_set_remaining_x509(exim_gnutls_state_st *state, uschar ** errstr) +tls_set_remaining_x509(exim_gnutls_state_st * state, uschar ** errstr) { -int rc; -const host_item *host = state->host; /* macro should be reconsidered? */ +int rc = OK; +const host_item * host = state->host; /* macro should be reconsidered? */ /* Create D-H parameters, or read them from the cache file. This function does its own SMTP error messaging. This only happens for the server, TLS D-H ignores @@ -2014,11 +2014,13 @@ client-side params. */ if (!state->host) { if (!dh_server_params) - if ((rc = init_server_dh(errstr)) != OK) return rc; + if ((rc = init_server_dh(errstr)) == DEFER) return rc; /* Unnecessary & discouraged with 3.6.0 or later, according to docs. But without it, no DHE- ciphers are advertised. */ - gnutls_certificate_set_dh_params(state->lib_state.x509_cred, dh_server_params); + + if (rc == OK) + gnutls_certificate_set_dh_params(state->lib_state.x509_cred, dh_server_params); } /* Link the credentials to the session. */ commit c5768a4180949d5b817a95850f5ff287c8765099 Author: Jeremy Harris Date: Mon Jul 24 15:05:35 2023 +0100 Fix DYNLOOKUP build for lsearch. Bug 3012 diff --git a/src/src/lookups/lsearch.c b/src/src/lookups/lsearch.c index f668f60f2..fcbd36952 100644 --- a/src/src/lookups/lsearch.c +++ b/src/src/lookups/lsearch.c @@ -421,7 +421,7 @@ gstring * lsearch_version_report(gstring * g) { #ifdef DYNLOOKUP -g = string_fmt_append(g, "Library version: lsearch: Exim version %s\n", EXIM_VERSION_STR)); +g = string_fmt_append(g, "Library version: lsearch: Exim version %s\n", EXIM_VERSION_STR); #endif return g; } commit cf3fecb9e873df38a9245775a3887e73a8716083 Author: Jeremy Harris Date: Thu Aug 3 18:34:06 2023 +0100 Fix free of $value after ${run...} diff --git a/src/src/expand.c b/src/src/expand.c index ae1657549..e0c571ade 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -5624,6 +5624,8 @@ while (*s) FILE * f; const uschar * arg, ** argv; BOOL late_expand = TRUE; + uschar * save_value = lookup_value; + int yesno; if (expand_forbid & RDO_RUN) { @@ -5747,20 +5749,24 @@ while (*s) expand_string_message = string_sprintf("command killed by signal %d", -runrc); + lookup_value = save_value; goto EXPAND_FAILED; } } /* Process the yes/no strings; $value may be useful in both cases */ - switch(process_yesno( + yesno = process_yesno( flags, /* were previously skipping */ runrc == 0, /* success/failure indicator */ lookup_value, /* value to reset for string2 */ &s, /* input pointer */ &yield, /* output pointer */ US"run", /* condition type */ - &resetok)) + &resetok); + lookup_value = save_value; + + switch(yesno) { case 1: goto EXPAND_FAILED; /* when all is well, the */ case 2: goto EXPAND_FAILED_CURLY; /* returned value is 0 */ commit 6707bfa9fb78858de938a1abca2846c820c5ded7 Author: Jeremy Harris Date: Thu Aug 3 18:40:42 2023 +0100 Fix $recipients expansion when used within ${run...}. Bug 3013 Broken-by: cfe6acff2ddc diff --git a/src/src/deliver.c b/src/src/deliver.c index bea38c5d1..52270368e 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -2376,7 +2376,7 @@ if ((pid = exim_fork(US"delivery-local")) == 0) { ok = transport_set_up_command(&transport_filter_argv, tp->filter_command, - TRUE, PANIC, addr, FALSE, US"transport filter", NULL); + TSUC_EXPAND_ARGS, PANIC, addr, US"transport filter", NULL); transport_filter_timeout = tp->filter_timeout; } else transport_filter_argv = NULL; diff --git a/src/src/expand.c b/src/src/expand.c index e0c571ade..259d463a4 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -5623,7 +5623,7 @@ while (*s) { FILE * f; const uschar * arg, ** argv; - BOOL late_expand = TRUE; + unsigned late_expand = TSUC_EXPAND_ARGS | TSUC_ALLOW_TAINTED_ARGS | TSUC_ALLOW_RECIPIENTS; uschar * save_value = lookup_value; int yesno; @@ -5637,7 +5637,7 @@ while (*s) while (*s == ',') if (Ustrncmp(++s, "preexpand", 9) == 0) - { late_expand = FALSE; s += 9; } + { late_expand = 0; s += 9; } else { const uschar * t = s; @@ -5697,7 +5697,6 @@ while (*s) late_expand, /* expand args if not already done */ 0, /* not relevant when... */ NULL, /* no transporting address */ - late_expand, /* allow tainted args, when expand-after-split */ US"${run} expansion", /* for error messages */ &expand_string_message)) /* where to put error message */ goto EXPAND_FAILED; diff --git a/src/src/functions.h b/src/src/functions.h index b5829a54c..0b030e4fe 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -634,7 +634,7 @@ extern BOOL transport_pass_socket(const uschar *, const uschar *, const uscha ); extern uschar *transport_rcpt_address(address_item *, BOOL); extern BOOL transport_set_up_command(const uschar ***, const uschar *, - BOOL, int, address_item *, BOOL, const uschar *, uschar **); + unsigned, int, address_item *, const uschar *, uschar **); extern void transport_update_waiting(host_item *, uschar *); extern BOOL transport_write_block(transport_ctx *, uschar *, int, BOOL); extern void transport_write_reset(int); diff --git a/src/src/macros.h b/src/src/macros.h index ed7a259aa..941c4f00c 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -1153,4 +1153,9 @@ typedef unsigned mcs_flags; #define QL_MSGID_ONLY 3 #define QL_UNSORTED 8 +/* Flags for transport_set_up_command() */ +#define TSUC_EXPAND_ARGS BIT(0) +#define TSUC_ALLOW_TAINTED_ARGS BIT(1) +#define TSUC_ALLOW_RECIPIENTS BIT(2) + /* End of macros.h */ diff --git a/src/src/routers/queryprogram.c b/src/src/routers/queryprogram.c index 51fdad229..ae33682e2 100644 --- a/src/src/routers/queryprogram.c +++ b/src/src/routers/queryprogram.c @@ -289,10 +289,9 @@ if (curr_uid != root_uid && (uid != curr_uid || gid != curr_gid)) if (!transport_set_up_command(&argvptr, /* anchor for arg list */ ob->command, /* raw command */ - TRUE, /* expand the arguments */ + TSUC_EXPAND_ARGS, /* arguments expanded but must not be tainted */ 0, /* not relevant when... */ NULL, /* no transporting address */ - FALSE, /* args must be untainted */ US"queryprogram router", /* for error messages */ &addr->message)) /* where to put error message */ return DEFER; diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index e6f9808dd..765d33bf4 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -5485,8 +5485,8 @@ while (done <= 0) BOOL rc; etrn_command = smtp_etrn_command; deliver_domain = smtp_cmd_data; - rc = transport_set_up_command(&argv, smtp_etrn_command, TRUE, 0, NULL, - FALSE, US"ETRN processing", &error); + rc = transport_set_up_command(&argv, smtp_etrn_command, TSUC_EXPAND_ARGS, 0, NULL, + US"ETRN processing", &error); deliver_domain = NULL; if (!rc) { diff --git a/src/src/transport.c b/src/src/transport.c index c125cc7c3..1e8bb4aa7 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -2133,18 +2133,18 @@ return FALSE; /* This function is called when a command line is to be parsed and executed directly, without the use of /bin/sh. It is called by the pipe transport, -the queryprogram router, and also from the main delivery code when setting up a +the queryprogram router, for any ${run } expansion, +and also from the main delivery code when setting up a transport filter process. The code for ETRN also makes use of this; in that case, no addresses are passed. Arguments: argvptr pointer to anchor for argv vector cmd points to the command string (modified IN PLACE) - expand_arguments true if expansion is to occur + flags bits for expand-args, allow taint, allow $recipients expand_failed error value to set if expansion fails; not relevant if addr == NULL addr chain of addresses, or NULL - allow_tainted_args as it says; used for ${run} etext text for use in error messages errptr where to put error message if addr is NULL; otherwise it is put in the first address @@ -2155,8 +2155,8 @@ Returns: TRUE if all went well; otherwise an error will be BOOL transport_set_up_command(const uschar *** argvptr, const uschar * cmd, - BOOL expand_arguments, int expand_failed, address_item * addr, - BOOL allow_tainted_args, const uschar * etext, uschar ** errptr) + unsigned flags, int expand_failed, address_item * addr, + const uschar * etext, uschar ** errptr) { const uschar ** argv, * s; int address_count = 0, argcount = 0, max_args; @@ -2231,10 +2231,10 @@ DEBUG(D_transport) debug_printf(" argv[%d] = '%s'\n", i, string_printing(argv[i])); } -if (expand_arguments) +if (flags & TSUC_EXPAND_ARGS) { - BOOL allow_dollar_recipients = addr && addr->parent - && Ustrcmp(addr->parent->address, "system-filter") == 0; + BOOL allow_dollar_recipients = (flags & TSUC_ALLOW_RECIPIENTS) + || (addr && addr->parent && Ustrcmp(addr->parent->address, "system-filter") == 0); /*XXX could we check this at caller? */ for (int i = 0; argv[i]; i++) { @@ -2421,7 +2421,7 @@ if (expand_arguments) debug_printf("SPECIFIC TESTSUITE EXEMPTION: tainted arg '%s'\n", expanded_arg); } - else if ( !allow_tainted_args + else if ( !(flags & TSUC_ALLOW_TAINTED_ARGS) && arg_is_tainted(expanded_arg, i, addr, etext, errptr)) return FALSE; argv[i] = expanded_arg; diff --git a/src/src/transports/lmtp.c b/src/src/transports/lmtp.c index 776c40e05..2dd0f328b 100644 --- a/src/src/transports/lmtp.c +++ b/src/src/transports/lmtp.c @@ -490,8 +490,8 @@ if (ob->cmd) { DEBUG(D_transport) debug_printf("using command %s\n", ob->cmd); sprintf(CS buffer, "%.50s transport", tblock->name); - if (!transport_set_up_command(&argv, ob->cmd, TRUE, PANIC, addrlist, FALSE, - buffer, NULL)) + if (!transport_set_up_command(&argv, ob->cmd, TSUC_EXPAND_ARGS, PANIC, + addrlist, buffer, NULL)) return FALSE; /* If the -N option is set, can't do any more. Presume all has gone well. */ diff --git a/src/src/transports/pipe.c b/src/src/transports/pipe.c index c3547eefe..18f9fd84e 100644 --- a/src/src/transports/pipe.c +++ b/src/src/transports/pipe.c @@ -292,9 +292,9 @@ Returns: TRUE if all went well; otherwise an error will be */ static BOOL -set_up_direct_command(const uschar ***argvptr, uschar *cmd, - BOOL expand_arguments, int expand_fail, address_item *addr, uschar *tname, - pipe_transport_options_block *ob) +set_up_direct_command(const uschar *** argvptr, uschar * cmd, + BOOL expand_arguments, int expand_fail, address_item * addr, uschar * tname, + pipe_transport_options_block * ob) { BOOL permitted = FALSE; const uschar **argv; @@ -304,8 +304,9 @@ call the common function for creating an argument list and expanding the items if necessary. If it fails, this function fails (error information is in the addresses). */ -if (!transport_set_up_command(argvptr, cmd, expand_arguments, expand_fail, - addr, FALSE, string_sprintf("%.50s transport", tname), NULL)) +if (!transport_set_up_command(argvptr, cmd, + expand_arguments ? TSUC_EXPAND_ARGS : 0, + expand_fail, addr, string_sprintf("%.50s transport", tname), NULL)) return FALSE; /* Point to the set-up arguments. */ diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index c502d7365..df94eebde 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -3833,7 +3833,7 @@ if (tblock->filter_command) yield ERROR. */ if (!transport_set_up_command(&transport_filter_argv, - tblock->filter_command, TRUE, DEFER, addrlist, FALSE, + tblock->filter_command, TSUC_EXPAND_ARGS, DEFER, addrlist, string_sprintf("%.50s transport filter", tblock->name), NULL)) { set_errno_nohost(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER, commit 8dcd332fbfd7ecefe548be074637fccae8cf23f0 Author: Jeremy Harris Date: Mon Aug 7 15:51:38 2023 +0100 Logging: convert an internal element from static to allocated buffer diff --git a/src/src/host.c b/src/src/host.c index 9c66e9aac..e274673a0 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -600,35 +600,36 @@ return depends on whether sender_fullhost and sender_ident are set or not: ident set, no host => U=ident ident set, host set => H=sender_fullhost U=ident -Use taint-unchecked routines on the assumption we'll never expand the results. - Arguments: useflag TRUE if first item to be flagged (H= or U=); if there are two items, the second is always flagged -Returns: pointer to a string in big_buffer +Returns: pointer to an allocated string */ uschar * host_and_ident(BOOL useflag) { +gstring * g = NULL; + if (!sender_fullhost) - string_format_nt(big_buffer, big_buffer_size, "%s%s", useflag ? "U=" : "", - sender_ident ? sender_ident : US"unknown"); + { + if (useflag) + g = string_catn(g, US"U=", 2); + g = string_cat(g, sender_ident ? sender_ident : US"unknown"); + } else { - uschar * flag = useflag ? US"H=" : US""; - uschar * iface = US""; + if (useflag) + g = string_catn(g, US"H=", 2); + g = string_cat(g, sender_fullhost); if (LOGGING(incoming_interface) && interface_address) - iface = string_sprintf(" I=[%s]:%d", interface_address, interface_port); + g = string_fmt_append(g, " I=[%s]:%d", interface_address, interface_port); if (sender_ident) - string_format_nt(big_buffer, big_buffer_size, "%s%s%s U=%s", - flag, sender_fullhost, iface, sender_ident); - else - string_format_nt(big_buffer, big_buffer_size, "%s%s%s", - flag, sender_fullhost, iface); + g = string_fmt_append(g, " U=%s", sender_ident); } -return big_buffer; +gstring_release_unused(g); +return string_from_gstring(g); } #endif /* STAND_ALONE */ commit ddaf34e7b7fe02cbbb99a6bf515eb4298d6b2d4b Author: Jeremy Harris Date: Sun Aug 6 14:50:36 2023 +0100 New $recipients_list. Bug 2726 diff --git a/src/src/expand.c b/src/src/expand.c index 259d463a4..ca954ebc2 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -475,6 +475,7 @@ typedef struct { typedef uschar * stringptr_fn_t(void); static uschar * fn_recipients(void); +static uschar * fn_recipients_list(void); static uschar * fn_queue_size(void); /* This table must be kept in alphabetical order. */ @@ -694,6 +695,7 @@ static var_entry var_table[] = { { "recipient_verify_failure",vtype_stringptr,&recipient_verify_failure }, { "recipients", vtype_string_func, (void *) &fn_recipients }, { "recipients_count", vtype_int, &recipients_count }, + { "recipients_list", vtype_string_func, (void *) &fn_recipients_list }, { "regex_cachesize", vtype_int, ®ex_cachesize },/* undocumented; devel observability */ #ifdef WITH_CONTENT_SCAN { "regex_match_string", vtype_stringptr, ®ex_match_string }, @@ -839,6 +841,7 @@ uschar * fn_arc_domains(void) {return NULL;} uschar * fn_hdrs_added(void) {return NULL;} uschar * fn_queue_size(void) {return NULL;} uschar * fn_recipients(void) {return NULL;} +uschar * fn_recipients_list(void) {return NULL;} uschar * sender_helo_verified_boolstr(void) {return NULL;} uschar * smtp_cmd_hist(void) {return NULL;} @@ -1800,7 +1803,9 @@ return g; *************************************************/ /* A recipients list is available only during system message filtering, during ACL processing after DATA, and while expanding pipe commands -generated from a system filter, but not elsewhere. */ +generated from a system filter, but not elsewhere. Note that this does +not check for comman in the elements, and uses comma-space as seperator - +so cannot be used as an exim list as-is. */ static uschar * fn_recipients(void) @@ -1815,6 +1820,23 @@ for (int i = 0; i < recipients_count; i++) s = recipients_list[i].address; g = string_append2_listele_n(g, US", ", s, Ustrlen(s)); } +gstring_release_unused(g); +return string_from_gstring(g); +} + +/* Similar, but as a properly-quoted exim list */ + + +static uschar * +fn_recipients_list(void) +{ +gstring * g = NULL; + +if (!f.enable_dollar_recipients) return NULL; + +for (int i = 0; i < recipients_count; i++) + g = string_append_listele(g, ':', recipients_list[i].address); +gstring_release_unused(g); return string_from_gstring(g); } @@ -2119,7 +2141,7 @@ switch (vp->type) case vtype_string_func: { stringptr_fn_t * fn = (stringptr_fn_t *) val; - uschar* s = fn(); + uschar * s = fn(); return s ? s : US""; } commit 4e871f9b28dff4cacfd012aede1d092cc8cfbd36 Author: Jeremy Harris Date: Tue Aug 8 23:33:41 2023 +0100 Logging: connection_id diff --git a/src/src/daemon.c b/src/src/daemon.c index f6867b882..028626c0e 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -181,7 +181,7 @@ Returns: nothing */ static void -handle_smtp_call(struct pollfd *fd_polls, int listen_socket_count, +handle_smtp_call(struct pollfd * fd_polls, int listen_socket_count, int accept_socket, struct sockaddr *accepted) { pid_t pid; @@ -360,31 +360,8 @@ if (max_for_this_host > 0 && smtp_accept_count >= max_for_this_host) } } -/* OK, the connection count checks have been passed. Before we can fork the -accepting process, we must first log the connection if requested. This logging -used to happen in the subprocess, but doing that means that the value of -smtp_accept_count can be out of step by the time it is logged. So we have to do -the logging here and accept the performance cost. Note that smtp_accept_count -hasn't yet been incremented to take account of this connection. - -In order to minimize the cost (because this is going to happen for every -connection), do a preliminary selector test here. This saves ploughing through -the generalized logging code each time when the selector is false. If the -selector is set, check whether the host is on the list for logging. If not, -arrange to unset the selector in the subprocess. */ - -if (LOGGING(smtp_connection)) - { - uschar *list = hosts_connection_nolog; - memset(sender_host_cache, 0, sizeof(sender_host_cache)); - if (list && verify_check_host(&list) == OK) - save_log_selector &= ~L_smtp_connection; - else - log_write(L_smtp_connection, LOG_MAIN, "SMTP connection from %Y " - "(TCP/IP connection count = %d)", whofrom, smtp_accept_count + 1); - } - -/* Now we can fork the accepting process; do a lookup tidy, just in case any +/* OK, the connection count checks have been passed. +Now we can fork the accepting process; do a lookup tidy, just in case any expansion above did a lookup. */ search_tidyup(); @@ -404,6 +381,33 @@ if (pid == 0) #endif smtp_accept_count++; /* So that it includes this process */ + connection_id = getpid(); + + /* Log the connection if requested. + In order to minimize the cost (because this is going to happen for every + connection), do a preliminary selector test here. This saves ploughing through + the generalized logging code each time when the selector is false. If the + selector is set, check whether the host is on the list for logging. If not, + arrange to unset the selector in the subprocess. + + jgh 2023/08/08 :- moved this logging in from the parent process, just + pre-fork. There was a claim back from 2004 that smtp_accept_count could have + become out-of-date by the time the child could log it, and I can't see how + that could happen. */ + + if (LOGGING(smtp_connection)) + { + uschar * list = hosts_connection_nolog; + memset(sender_host_cache, 0, sizeof(sender_host_cache)); + if (list && verify_check_host(&list) == OK) + save_log_selector &= ~L_smtp_connection; + else if (LOGGING(connection_id)) + log_write(L_smtp_connection, LOG_MAIN, "SMTP connection from %Y " + "Ci=%lu (TCP/IP connection count = %d)", whofrom, connection_id, smtp_accept_count); + else + log_write(L_smtp_connection, LOG_MAIN, "SMTP connection from %Y " + "(TCP/IP connection count = %d)", whofrom, smtp_accept_count); + } /* If the listen backlog was over the monitoring level, log it. */ diff --git a/src/src/exim.c b/src/src/exim.c index 94061f97d..c44c7cb1b 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -5418,6 +5418,7 @@ if (host_checking) "**** This is not for real!\n\n", sender_host_address); + connection_id = getpid(); memset(sender_host_cache, 0, sizeof(sender_host_cache)); if (verify_check_host(&hosts_connection_nolog) == OK) { @@ -5606,6 +5607,7 @@ because a log line has already been written for all its failure exists (usually "connection refused: ") and writing another one is unnecessary clutter. */ +connection_id = getpid(); if (smtp_input) { smtp_in = stdin; diff --git a/src/src/globals.c b/src/src/globals.c index 9f4053937..56d192781 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -737,6 +737,7 @@ uid_t config_uid = CONFIGURE_OWNER; uid_t config_uid = 0; #endif +ulong connection_id = 0L; int connection_max_messages= -1; uschar *continue_proxy_cipher = NULL; BOOL continue_proxy_dane = FALSE; @@ -1089,6 +1090,7 @@ bit_table log_options[] = { /* must be in alphabetical order, BIT_TABLE(L, all), BIT_TABLE(L, all_parents), BIT_TABLE(L, arguments), + BIT_TABLE(L, connection_id), BIT_TABLE(L, connection_reject), BIT_TABLE(L, delay_delivery), BIT_TABLE(L, deliver_time), diff --git a/src/src/globals.h b/src/src/globals.h index 3a5513382..2458066dd 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -432,6 +432,7 @@ extern gstring *client_cmd_log; /* debug log of client cmds & responses * extern int clmacro_count; /* Number of command line macros */ extern uschar *clmacros[]; /* Copy of them, for re-exec */ extern BOOL commandline_checks_require_admin; /* belt and braces for insecure setups */ +extern ulong connection_id; /* per-daemon connection number */ extern int connection_max_messages;/* Max down one SMTP connection */ extern FILE *config_file; /* Configuration file */ extern const uschar *config_filename; /* Configuration file name */ diff --git a/src/src/host.c b/src/src/host.c index e274673a0..3e5a88660 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -628,6 +628,8 @@ else if (sender_ident) g = string_fmt_append(g, " U=%s", sender_ident); } +if (LOGGING(connection_id)) + g = string_fmt_append(g, " Ci=%lu", connection_id); gstring_release_unused(g); return string_from_gstring(g); } diff --git a/src/src/macros.h b/src/src/macros.h index 941c4f00c..47d75044b 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -466,6 +466,7 @@ enum logbit { Li_8bitmime = BITWORDSIZE, Li_acl_warn_skipped, Li_arguments, + Li_connection_id, Li_deliver_time, Li_delivery_size, Li_dkim, diff --git a/src/src/receive.c b/src/src/receive.c index 4271561d7..14038f2ec 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1156,7 +1156,7 @@ Returns: the SMTP response */ static uschar * -handle_lost_connection(uschar *s) +handle_lost_connection(uschar * s) { log_write(L_lost_incoming_connection | L_smtp_connection, LOG_MAIN, "%s lost while reading message data%s", smtp_get_connection_info(), s); @@ -1379,6 +1379,8 @@ if (f.tcp_in_fastopen && !f.tcp_in_fastopen_logged) } if (sender_ident) g = string_append(g, 2, US" U=", sender_ident); +if (LOGGING(connection_id)) + g = string_fmt_append(g, " Ci=%lu", connection_id); if (received_protocol) g = string_append(g, 2, US" P=", received_protocol); if (LOGGING(pipelining) && f.smtp_in_pipelining_advertised) diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 765d33bf4..18cde79b1 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -1342,21 +1342,29 @@ smtp_get_connection_info(void) { const uschar * hostname = sender_fullhost ? sender_fullhost : sender_host_address; +gstring * g = string_catn(NULL, US"SMTP connection", 15); + +if (LOGGING(connection_id)) + g = string_fmt_append(g, " Ci=%lu", connection_id); +g = string_catn(g, US" from ", 6); if (host_checking) - return string_sprintf("SMTP connection from %s", hostname); + g = string_cat(g, hostname); + +else if (f.sender_host_unknown || f.sender_host_notsocket) + g = string_cat(g, sender_ident); -if (f.sender_host_unknown || f.sender_host_notsocket) - return string_sprintf("SMTP connection from %s", sender_ident); +else if (f.is_inetd) + g = string_append(g, 2, hostname, US" (via inetd)"); -if (f.is_inetd) - return string_sprintf("SMTP connection from %s (via inetd)", hostname); +else if (LOGGING(incoming_interface) && interface_address) + g = string_fmt_append(g, "%s I=[%s]:%d", hostname, interface_address, interface_port); -if (LOGGING(incoming_interface) && interface_address) - return string_sprintf("SMTP connection from %s I=[%s]:%d", hostname, - interface_address, interface_port); +else + g = string_cat(g, hostname); -return string_sprintf("SMTP connection from %s", hostname); +gstring_release_unused(g); +return string_from_gstring(g); } commit ecb6fe6728708adefdf4f2f2aad134e36f09fc28 Author: Jeremy Harris Date: Fri Jan 20 17:41:14 2023 +0000 Testsuite: support TLS cross-library testing diff --git a/src/Makefile b/src/Makefile index d190d9aa0..b8d88054d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -37,10 +37,12 @@ all: Local/Makefile configure @cd build-$(buildname); $(MAKE) SHELL=$(SHELL) $(MFLAGS) # This pair for the convenience of of the Debian maintainers -exim: Local/Makefile configure - @cd build-$(buildname); $(MAKE) SHELL=$(SHELL) $(MFLAGS) exim -utils: Local/Makefile configure - @cd build-$(buildname); $(MAKE) SHELL=$(SHELL) $(MFLAGS) utils +exim utils: Local/Makefile configure + @cd build-$(buildname); $(MAKE) SHELL=$(SHELL) $(MFLAGS) $@ + +# For testsuite builds +exim_openssl exim_gnutls: Local/Makefile configure + @cd build-$(buildname); $(MAKE) SHELL=$(SHELL) $(MFLAGS) $@ Local/Makefile: @echo "" diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index c01e911ce..e0aa1dc6c 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -39,6 +39,9 @@ FE = $(FULLECHO) all: utils exim config: $(EDITME) checklocalmake Makefile os.c config.h version.h version.sh macro.c +exim_openssl exim_gnutls: clean exim + cp exim $@ + checklocalmake: @if $(SHELL) $(SCRIPTS)/newer $(EDITME)-$(OSTYPE) $(EDITME) || \ $(SHELL) $(SCRIPTS)/newer $(EDITME)-$(ARCHTYPE) $(EDITME) || \ commit ed4f9175333dfdad9b8766c0b168d7edbccbd595 Author: Jeremy Harris Date: Wed Aug 9 13:08:50 2023 +0100 Unbreak FreeBSD buld Broken-by: 4e871f9b28df diff --git a/src/src/globals.c b/src/src/globals.c index 56d192781..f945379a0 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -737,7 +737,7 @@ uid_t config_uid = CONFIGURE_OWNER; uid_t config_uid = 0; #endif -ulong connection_id = 0L; +uint64_t connection_id = 0L; int connection_max_messages= -1; uschar *continue_proxy_cipher = NULL; BOOL continue_proxy_dane = FALSE; diff --git a/src/src/globals.h b/src/src/globals.h index 2458066dd..ff82bef6d 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -432,7 +432,7 @@ extern gstring *client_cmd_log; /* debug log of client cmds & responses * extern int clmacro_count; /* Number of command line macros */ extern uschar *clmacros[]; /* Copy of them, for re-exec */ extern BOOL commandline_checks_require_admin; /* belt and braces for insecure setups */ -extern ulong connection_id; /* per-daemon connection number */ +extern uint64_t connection_id; /* connection number */ extern int connection_max_messages;/* Max down one SMTP connection */ extern FILE *config_file; /* Configuration file */ extern const uschar *config_filename; /* Configuration file name */ commit 56a6b0606bb11f53653e9d208f81ffffc4b1d5a6 Author: Jeremy Harris Date: Wed Aug 9 14:03:38 2023 +0100 typoes Briken-by: 3b6774c818ba diff --git a/src/src/dmarc.c b/src/src/dmarc.c index 409d8a47d..d667faa77 100644 --- a/src/src/dmarc.c +++ b/src/src/dmarc.c @@ -270,16 +270,16 @@ g = string_fmt_append(NULL, header_from_sender, expand_string(US"$sender_address_domain")); if (spf_response) - g = string_fmt_append(s, "spf %d\n", dmarc_spf_ares_result); + g = string_fmt_append(g, "spf %d\n", dmarc_spf_ares_result); g = string_fmt_append(g, "%spdomain %s\npolicy %d\n", dkim_history_buffer, dmarc_used_domain, dmarc_policy); if ((rua = opendmarc_policy_fetch_rua(dmarc_pctx, NULL, 0, 1))) for (tmp_ans = 0; rua[tmp_ans]; tmp_ans++) - g = string_fmd_append(g, "rua %s\n", rua[tmp_ans]); + g = string_fmt_append(g, "rua %s\n", rua[tmp_ans]); else - g = string_fmtappend(g, "rua -\n"); + g = string_fmt_append(g, "rua -\n"); opendmarc_policy_fetch_pct(dmarc_pctx, &tmp_ans); g = atring_fmt_append(g, "pct %d\n", tmp_ans); commit fb911b21a156dfe5967b9c79f4f1408e4f90458b Author: Jeremy Harris Date: Wed Aug 9 14:43:34 2023 +0100 typoes Broken-by: 3b6774c818ba diff --git a/src/src/dmarc.c b/src/src/dmarc.c index d667faa77..b2609b4fe 100644 --- a/src/src/dmarc.c +++ b/src/src/dmarc.c @@ -282,21 +282,21 @@ else g = string_fmt_append(g, "rua -\n"); opendmarc_policy_fetch_pct(dmarc_pctx, &tmp_ans); -g = atring_fmt_append(g, "pct %d\n", tmp_ans); +g = string_fmt_append(g, "pct %d\n", tmp_ans); opendmarc_policy_fetch_adkim(dmarc_pctx, &tmp_ans); -g = atring_fmt_append(g, "adkim %d\n", tmp_ans); +g = string_fmt_append(g, "adkim %d\n", tmp_ans); opendmarc_policy_fetch_aspf(dmarc_pctx, &tmp_ans); -g = atring_fmt_append(g, "aspf %d\n", tmp_ans); +g = string_fmt_append(g, "aspf %d\n", tmp_ans); opendmarc_policy_fetch_p(dmarc_pctx, &tmp_ans); -g = atring_fmt_append(g, "p %d\n", tmp_ans); +g = string_fmt_append(g, "p %d\n", tmp_ans); opendmarc_policy_fetch_sp(dmarc_pctx, &tmp_ans); -g = atring_fmt_append(g, "sp %d\n", tmp_ans); +g = string_fmt_append(g, "sp %d\n", tmp_ans); -g = atring_fmt_append(g, "align_dkim %d\nalign_spf %d\naction %d\n", +g = string_fmt_append(g, "align_dkim %d\nalign_spf %d\naction %d\n", da, sa, action); /* Write the contents to the history file */ commit 3e7e6162870de6545f3ee53d0c52d14a6b9434ef Author: Jeremy Harris Date: Fri Aug 11 13:24:57 2023 +0100 Make printf of gstring null-safe Broken-by: 00392be0e7cf diff --git a/src/src/string.c b/src/src/string.c index 854cf0d34..52b1d2fb5 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -1579,8 +1579,8 @@ while (*fp) case 'Y': /* gstring pointer */ { gstring * zg = va_arg(ap, gstring *); - s = CS zg->s; - slen = zg->ptr; + if (zg) { s = CS zg->s; slen = zg->ptr; } + else { s = null; slen = Ustrlen(s); } goto INSERT_GSTRING; } commit 36bc854c86908ee921225c1d30e35c4d59eed822 Author: Andreas Metzler Date: Mon Aug 14 17:27:16 2023 +0100 GnuTLS: fix autogen cert expiry date. Bug 3014 Broken-by: 48e9099006 diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index dd70e73e1..e706b6386 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -1016,7 +1016,7 @@ now = 1; if ( (rc = gnutls_x509_crt_set_version(cert, 3)) || (rc = gnutls_x509_crt_set_serial(cert, &now, sizeof(now))) || (rc = gnutls_x509_crt_set_activation_time(cert, now = time(NULL))) - || (rc = gnutls_x509_crt_set_expiration_time(cert, (long)2 * 60 * 60)) /* 2 hour */ + || (rc = gnutls_x509_crt_set_expiration_time(cert, now + (long)2 * 60 * 60)) /* 2 hour */ || (rc = gnutls_x509_crt_set_key(cert, pkey)) || (rc = gnutls_x509_crt_set_dn_by_oid(cert, commit 8a2fd4b22738aa6cceaf2ddd7755f376cb8460a3 Author: Jeremy Harris Date: Sat Aug 19 22:52:27 2023 +0100 DMARC: write history file even under testsuite diff --git a/src/src/dmarc.c b/src/src/dmarc.c index b2609b4fe..48a72541f 100644 --- a/src/src/dmarc.c +++ b/src/src/dmarc.c @@ -243,7 +243,7 @@ return NULL; static int dmarc_write_history_file() { -int history_file_fd; +int history_file_fd = 0; ssize_t written_len; int tmp_ans; u_char ** rua; /* aggregate report addressees */ @@ -254,14 +254,13 @@ if (!dmarc_history_file) DEBUG(D_receive) debug_printf("DMARC history file not set\n"); return DMARC_HIST_DISABLED; } -history_file_fd = log_open_as_exim(dmarc_history_file); - -if (history_file_fd < 0) - { - log_write(0, LOG_MAIN|LOG_PANIC, "failure to create DMARC history file: %s", - dmarc_history_file); - return DMARC_HIST_FILE_ERR; - } +if (!host_checking) + if ((history_file_fd = log_open_as_exim(dmarc_history_file)) < 0) + { + log_write(0, LOG_MAIN|LOG_PANIC, "failure to create DMARC history file: %s", + dmarc_history_file); + return DMARC_HIST_FILE_ERR; + } /* Generate the contents of the history file */ g = string_fmt_append(NULL, @@ -272,14 +271,17 @@ g = string_fmt_append(NULL, if (spf_response) g = string_fmt_append(g, "spf %d\n", dmarc_spf_ares_result); -g = string_fmt_append(g, "%spdomain %s\npolicy %d\n", - dkim_history_buffer, dmarc_used_domain, dmarc_policy); +if (dkim_history_buffer) + g = string_fmt_append(g, "%s\n", dkim_history_buffer); + +g = string_fmt_append(g, "pdomain %s\npolicy %d\n", + dmarc_used_domain, dmarc_policy); if ((rua = opendmarc_policy_fetch_rua(dmarc_pctx, NULL, 0, 1))) for (tmp_ans = 0; rua[tmp_ans]; tmp_ans++) g = string_fmt_append(g, "rua %s\n", rua[tmp_ans]); else - g = string_fmt_append(g, "rua -\n"); + g = string_catn(g, US"rua -\n", 6); opendmarc_policy_fetch_pct(dmarc_pctx, &tmp_ans); g = string_fmt_append(g, "pct %d\n", tmp_ans); @@ -301,14 +303,16 @@ g = string_fmt_append(g, "align_dkim %d\nalign_spf %d\naction %d\n", /* Write the contents to the history file */ DEBUG(D_receive) - debug_printf("DMARC logging history data for opendmarc reporting%s\n", - (host_checking || f.running_in_test_harness) ? " (not really)" : ""); -if (host_checking || f.running_in_test_harness) { - DEBUG(D_receive) - debug_printf("DMARC history data for debugging:\n%Y", g); + debug_printf("DMARC logging history data for opendmarc reporting%s\n", + host_checking ? " (not really)" : ""); + debug_printf_indent("DMARC history data for debugging:\n"); + expand_level++; + debug_printf_indent("%Y", g); + expand_level--; } -else + +if (!host_checking) { written_len = write_to_fd_buf(history_file_fd, g->s, commit 3586c3794ff2df6724c92f41311d0109ca2e632d Author: Jeremy Harris Date: Sun Aug 20 13:12:26 2023 +0100 Testsuite: add with-dkim case to DMARC testcase diff --git a/src/src/dmarc.c b/src/src/dmarc.c index 48a72541f..070885111 100644 --- a/src/src/dmarc.c +++ b/src/src/dmarc.c @@ -272,7 +272,7 @@ if (spf_response) g = string_fmt_append(g, "spf %d\n", dmarc_spf_ares_result); if (dkim_history_buffer) - g = string_fmt_append(g, "%s\n", dkim_history_buffer); + g = string_cat(g, dkim_history_buffer); g = string_fmt_append(g, "pdomain %s\npolicy %d\n", dmarc_used_domain, dmarc_policy); commit f9c35778a660c11ba350f0dbfd9b012cf2295e26 Author: Victor Ustugov Date: Sat Aug 19 23:08:58 2023 +0100 DMARC: for version 1.4.x libraries, add selector to dkim lines in history file diff --git a/src/src/dmarc.c b/src/src/dmarc.c index 070885111..c8d3f37f0 100644 --- a/src/src/dmarc.c +++ b/src/src/dmarc.c @@ -492,7 +492,13 @@ The EDITME provides a DMARC_API variable */ ves == PDKIM_VERIFY_INVALID_PUBKEY_IMPORT ? ARES_RESULT_PERMERROR : ARES_RESULT_UNKNOWN : ARES_RESULT_UNKNOWN; - g = string_fmt_append(g, "dkim %s %d\n", sig->domain, dkim_ares_result); +#if DMARC_API >= 100400 + g = string_fmt_append(g, + "dkim %s %s %d\n", sig->domain, sig->selector, dkim_ares_result); +#else + g = string_fmt_append(g, + "dkim %s %d\n", sig->domain, dkim_ares_result); +#endif } dkim_history_buffer = string_from_gstring(g); commit 9f947338832b85a25480fe89e1d16ffec33e5dda Author: Jeremy Harris Date: Thu Aug 17 17:02:18 2023 +0100 tidying diff --git a/src/src/dmarc.c b/src/src/dmarc.c index c8d3f37f0..070095660 100644 --- a/src/src/dmarc.c +++ b/src/src/dmarc.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ /* DMARC support. - Copyright (c) The Exim Maintainers 2019 - 2022 + Copyright (c) The Exim Maintainers 2019 - 2023 Copyright (c) Todd Lyons 2012 - 2014 License: GPL */ /* SPDX-License-Identifier: GPL-2.0-or-later */ @@ -37,7 +37,6 @@ uschar *spf_sender_domain = NULL; uschar *spf_human_readable = NULL; u_char *header_from_sender = NULL; int history_file_status = DMARC_HIST_OK; -uschar *dkim_history_buffer= NULL; typedef struct dmarc_exim_p { uschar *name; @@ -92,7 +91,7 @@ messages on the same SMTP connection (that come from the same host with the same HELO string) */ int -dmarc_init() +dmarc_init(void) { int *netmask = NULL; /* Ignored */ int is_ipv6 = 0; @@ -124,7 +123,7 @@ if (libdm_status != DMARC_PARSE_OKAY) } if (!dmarc_tld_file || !*dmarc_tld_file) { - DEBUG(D_receive) debug_printf("DMARC: no dmarc_tld_file\n"); + DEBUG(D_receive) debug_printf_indent("DMARC: no dmarc_tld_file\n"); dmarc_abort = TRUE; } else if (opendmarc_tld_read_file(CS dmarc_tld_file, NULL, NULL, NULL)) @@ -135,7 +134,7 @@ else if (opendmarc_tld_read_file(CS dmarc_tld_file, NULL, NULL, NULL)) } if (!sender_host_address) { - DEBUG(D_receive) debug_printf("DMARC: no sender_host_address\n"); + DEBUG(D_receive) debug_printf_indent("DMARC: no sender_host_address\n"); dmarc_abort = TRUE; } /* This catches locally originated email and startup errors above. */ @@ -203,7 +202,7 @@ if ( dmarc_policy == DMARC_POLICY_REJECT && action == DMARC_RESULT_REJECT /* Move to first character past the colon */ recipient += 7; DEBUG(D_receive) - debug_printf("DMARC forensic report to %s%s\n", recipient, + debug_printf_indent("DMARC forensic report to %s%s\n", recipient, (host_checking || f.running_in_test_harness) ? " (not really)" : ""); if (host_checking || f.running_in_test_harness) continue; @@ -241,7 +240,7 @@ return NULL; static int -dmarc_write_history_file() +dmarc_write_history_file(const gstring * dkim_history_buffer) { int history_file_fd = 0; ssize_t written_len; @@ -251,14 +250,15 @@ gstring * g; if (!dmarc_history_file) { - DEBUG(D_receive) debug_printf("DMARC history file not set\n"); + DEBUG(D_receive) debug_printf_indent("DMARC history file not set\n"); return DMARC_HIST_DISABLED; } if (!host_checking) if ((history_file_fd = log_open_as_exim(dmarc_history_file)) < 0) { - log_write(0, LOG_MAIN|LOG_PANIC, "failure to create DMARC history file: %s", - dmarc_history_file); + log_write(0, LOG_MAIN|LOG_PANIC, + "failure to create DMARC history file: %s: %s", + dmarc_history_file, strerror(errno)); return DMARC_HIST_FILE_ERR; } @@ -272,7 +272,7 @@ if (spf_response) g = string_fmt_append(g, "spf %d\n", dmarc_spf_ares_result); if (dkim_history_buffer) - g = string_cat(g, dkim_history_buffer); + g = string_fmt_append(g, "%Y", dkim_history_buffer); g = string_fmt_append(g, "pdomain %s\npolicy %d\n", dmarc_used_domain, dmarc_policy); @@ -304,7 +304,7 @@ g = string_fmt_append(g, "align_dkim %d\nalign_spf %d\naction %d\n", /* Write the contents to the history file */ DEBUG(D_receive) { - debug_printf("DMARC logging history data for opendmarc reporting%s\n", + debug_printf_indent("DMARC logging history data for opendmarc reporting%s\n", host_checking ? " (not really)" : ""); debug_printf_indent("DMARC history data for debugging:\n"); expand_level++; @@ -334,7 +334,7 @@ context (if any), retrieves the result, sets up expansion strings and evaluates the condition outcome. */ int -dmarc_process() +dmarc_process(void) { int sr, origin; /* used in SPF section */ int dmarc_spf_result = 0; /* stores spf into dmarc conn ctx */ @@ -356,7 +356,7 @@ there was a previous error. */ if (!from_header) { - DEBUG(D_receive) debug_printf("DMARC: no From: header\n"); + DEBUG(D_receive) debug_printf_indent("DMARC: no From: header\n"); dmarc_abort = TRUE; } else if (!dmarc_abort) @@ -397,7 +397,7 @@ instead do this in the ACLs. */ if (!dmarc_abort && !sender_host_authenticated) { uschar * dmarc_domain; - gstring * g = NULL; + gstring * dkim_history_buffer = NULL; /* Use the envelope sender domain for this part of DMARC */ @@ -413,7 +413,7 @@ if (!dmarc_abort && !sender_host_authenticated) log_write(0, LOG_MAIN, "DMARC using synthesized SPF sender domain = %s\n", spf_sender_domain); DEBUG(D_receive) - debug_printf("DMARC using synthesized SPF sender domain = %s\n", + debug_printf_indent("DMARC using synthesized SPF sender domain = %s\n", spf_sender_domain); } dmarc_spf_result = DMARC_POLICY_SPF_OUTCOME_NONE; @@ -440,7 +440,7 @@ if (!dmarc_abort && !sender_host_authenticated) origin = DMARC_POLICY_SPF_ORIGIN_MAILFROM; spf_human_readable = US spf_response->header_comment; DEBUG(D_receive) - debug_printf("DMARC using SPF sender domain = %s\n", spf_sender_domain); + debug_printf_indent("DMARC using SPF sender domain = %s\n", spf_sender_domain); } if (strcmp( CCS spf_sender_domain, "") == 0) dmarc_abort = TRUE; @@ -468,14 +468,14 @@ if (!dmarc_abort && !sender_host_authenticated) DMARC_POLICY_DKIM_OUTCOME_NONE; libdm_status = opendmarc_policy_store_dkim(dmarc_pctx, US sig->domain, -/* The opendmarc project broke its API in a way we can't detect * easily. +/* The opendmarc project broke its API in a way we can't detect easily. The EDITME provides a DMARC_API variable */ #if DMARC_API >= 100400 sig->selector, #endif dkim_result, US""); DEBUG(D_receive) - debug_printf("DMARC adding DKIM sender domain = %s\n", sig->domain); + debug_printf_indent("DMARC adding DKIM sender domain = %s\n", sig->domain); if (libdm_status != DMARC_PARSE_OKAY) log_write(0, LOG_MAIN|LOG_PANIC, "failure to store dkim (%s) for DMARC: %s", @@ -493,14 +493,13 @@ The EDITME provides a DMARC_API variable */ ARES_RESULT_UNKNOWN : ARES_RESULT_UNKNOWN; #if DMARC_API >= 100400 - g = string_fmt_append(g, + dkim_history_buffer = string_fmt_append(dkim_history_buffer, "dkim %s %s %d\n", sig->domain, sig->selector, dkim_ares_result); #else - g = string_fmt_append(g, + dkim_history_buffer = string_fmt_append(dkim_history_buffer, "dkim %s %d\n", sig->domain, dkim_ares_result); #endif } - dkim_history_buffer = string_from_gstring(g); /* Look up DMARC policy record in DNS. We do this explicitly, rather than letting the dmarc library do it with opendmarc_policy_query_dmarc(), so that @@ -515,23 +514,24 @@ The EDITME provides a DMARC_API variable */ case DMARC_DNS_ERROR_NXDOMAIN: case DMARC_DNS_ERROR_NO_RECORD: DEBUG(D_receive) - debug_printf("DMARC no record found for %s\n", header_from_sender); + debug_printf_indent("DMARC no record found for %s\n", header_from_sender); has_dmarc_record = FALSE; break; case DMARC_PARSE_OKAY: DEBUG(D_receive) - debug_printf("DMARC record found for %s\n", header_from_sender); + debug_printf_indent("DMARC record found for %s\n", header_from_sender); break; case DMARC_PARSE_ERROR_BAD_VALUE: DEBUG(D_receive) - debug_printf("DMARC record parse error for %s\n", header_from_sender); + debug_printf_indent("DMARC record parse error for %s\n", header_from_sender); has_dmarc_record = FALSE; break; default: /* everything else, skip dmarc */ DEBUG(D_receive) - debug_printf("DMARC skipping (%d), unsure what to do with %s", - libdm_status, from_header->text); + debug_printf_indent("DMARC skipping (%s), unsure what to do with %s", + opendmarc_policy_status_to_str(libdm_status), + from_header->text); has_dmarc_record = FALSE; break; } @@ -620,7 +620,7 @@ The EDITME provides a DMARC_API variable */ sa==DMARC_POLICY_SPF_ALIGNMENT_PASS ?"yes":"no", da==DMARC_POLICY_DKIM_ALIGNMENT_PASS ?"yes":"no", dmarc_status_text); - history_file_status = dmarc_write_history_file(); + history_file_status = dmarc_write_history_file(dkim_history_buffer); /* Now get the forensic reporting addresses, if any */ ruf = opendmarc_policy_fetch_ruf(dmarc_pctx, NULL, 0, 1); dmarc_send_forensic_report(ruf); diff --git a/src/src/dmarc.h b/src/src/dmarc.h index 86d3b1e1c..7ce0ca953 100644 --- a/src/src/dmarc.h +++ b/src/src/dmarc.h @@ -3,7 +3,7 @@ *************************************************/ /* Experimental DMARC support. - Copyright (c) The Exim Maintainers 2021 - 2022 + Copyright (c) The Exim Maintainers 2021 - 2023 Copyright (c) Todd Lyons 2012 - 2014 License: GPL */ /* SPDX-License-Identifier: GPL-2.0-or-later */ @@ -20,9 +20,9 @@ /* prototypes */ gstring * dmarc_version_report(gstring *); -int dmarc_init(); +int dmarc_init(void); int dmarc_store_data(header_line *); -int dmarc_process(); +int dmarc_process(void); uschar *dmarc_exim_expand_query(int); uschar *dmarc_exim_expand_defaults(int); commit 0fef2df059417b3cc2cc555f65c6064b7dca5442 Author: Jeremy Harris Date: Tue Aug 22 15:26:04 2023 +0100 tidying: specified-initializers diff --git a/src/src/filter.c b/src/src/filter.c index fc6970f23..7f02327e3 100644 --- a/src/src/filter.c +++ b/src/src/filter.c @@ -194,8 +194,7 @@ static int cond_types[] = { cond_BEGINS, cond_BEGINS, cond_CONTAINS, cond_above, cond_begins, cond_begins, cond_below, cond_contains, cond_contains, cond_ends, cond_ends, cond_is, cond_matches, cond_matches }; -/* Command identities: must be kept in step with the list of command words -and the list of expanded argument counts which follow. */ +/* Command identities */ enum { add_command, defer_command, deliver_command, elif_command, else_command, endif_command, finish_command, fail_command, freeze_command, @@ -203,10 +202,28 @@ enum { add_command, defer_command, deliver_command, elif_command, else_command, mail_command, noerror_command, pipe_command, save_command, seen_command, testprint_command, unseen_command, vacation_command }; -static const char *command_list[] = { - "add", "defer", "deliver", "elif", "else", "endif", "finish", - "fail", "freeze", "headers", "if", "logfile", "logwrite", "mail", - "noerror", "pipe", "save", "seen", "testprint", "unseen", "vacation" +static const char * command_list[] = { + [add_command] = "add", + [defer_command] = "defer", + [deliver_command] = "deliver", + [elif_command] = "elif", + [else_command] = "else", + [endif_command] = "endif", + [finish_command] = "finish", + [fail_command] = "fail", + [freeze_command] = "freeze", + [headers_command] = "headers", + [if_command] = "if", + [logfile_command] = "logfile", + [logwrite_command] = "logwrite", + [mail_command] = "mail", + [noerror_command] = "noerror", + [pipe_command] = "pipe", + [save_command] = "save", + [seen_command] = "seen", + [testprint_command] = "testprint", + [unseen_command] = "unseen", + [vacation_command] = "vacation" }; static int command_list_count = nelem(command_list); @@ -215,27 +232,27 @@ static int command_list_count = nelem(command_list); If the top bit is set, it means that the default for the command is "seen". */ static uschar command_exparg_count[] = { - 2, /* add */ - 1, /* defer */ - 128+2, /* deliver */ - 0, /* elif */ - 0, /* else */ - 0, /* endif */ - 0, /* finish */ - 1, /* fail */ - 1, /* freeze */ - 1, /* headers */ - 0, /* if */ - 1, /* logfile */ - 1, /* logwrite */ - MAILARGS_STRING_COUNT, /* mail */ - 0, /* noerror */ - 128+0, /* pipe */ - 128+1, /* save */ - 0, /* seen */ - 1, /* testprint */ - 0, /* unseen */ - MAILARGS_STRING_COUNT /* vacation */ + [add_command] = 2, + [defer_command] = 1, + [deliver_command] = 128+2, + [elif_command] = 0, + [else_command] = 0, + [endif_command] = 0, + [finish_command] = 0, + [fail_command] = 1, + [freeze_command] = 1, + [headers_command] = 1, + [if_command] = 0, + [logfile_command] = 1, + [logwrite_command] = 1, + [mail_command] = MAILARGS_STRING_COUNT, + [noerror_command] = 0, + [pipe_command] = 128+0, + [save_command] = 128+1, + [seen_command] = 0, + [testprint_command] = 1, + [unseen_command] = 0, + [vacation_command] = MAILARGS_STRING_COUNT }; commit c18090c685f592d2ae944c2d24926394937c15c2 Author: Jeremy Harris Date: Thu Aug 24 15:44:31 2023 +0100 Revert "Fix free of $value after ${run...}" This reverts commit cf3fecb9e873df38a9245775a3887e73a8716083. diff --git a/src/src/expand.c b/src/src/expand.c index ca954ebc2..590b40383 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -5646,8 +5646,6 @@ while (*s) FILE * f; const uschar * arg, ** argv; unsigned late_expand = TSUC_EXPAND_ARGS | TSUC_ALLOW_TAINTED_ARGS | TSUC_ALLOW_RECIPIENTS; - uschar * save_value = lookup_value; - int yesno; if (expand_forbid & RDO_RUN) { @@ -5770,24 +5768,20 @@ while (*s) expand_string_message = string_sprintf("command killed by signal %d", -runrc); - lookup_value = save_value; goto EXPAND_FAILED; } } /* Process the yes/no strings; $value may be useful in both cases */ - yesno = process_yesno( + switch(process_yesno( flags, /* were previously skipping */ runrc == 0, /* success/failure indicator */ lookup_value, /* value to reset for string2 */ &s, /* input pointer */ &yield, /* output pointer */ US"run", /* condition type */ - &resetok); - lookup_value = save_value; - - switch(yesno) + &resetok)) { case 1: goto EXPAND_FAILED; /* when all is well, the */ case 2: goto EXPAND_FAILED_CURLY; /* returned value is 0 */ commit 21b172df101c2c52faf0cc56a502395451975be9 Author: Jeremy Harris Date: Thu Aug 24 15:51:21 2023 +0100 Re-fix live variable $value free. The inital fix resulted in $value from ${run...} not being available later, which is a documented feature. Broken=by: cf3fecb9e873 diff --git a/src/src/exim.c b/src/src/exim.c index c44c7cb1b..a96d12167 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -5779,7 +5779,7 @@ for (BOOL more = TRUE; more; ) int start, end, domain; uschar * errmess; /* There can be multiple addresses, so EXIM_DISPLAYMAIL_MAX (tuned for 1) is too short. - * We'll still want to cap it to something, just in case. */ + We'll still want to cap it to something, just in case. */ uschar * s = string_copy_taint( exim_str_fail_toolong(list[i], BIG_BUFFER_SIZE, "address argument"), GET_TAINTED); @@ -6114,6 +6114,7 @@ MORELOOP: deliver_localpart_data = deliver_domain_data = recipient_data = sender_data = NULL; acl_var_m = NULL; + lookup_value = NULL; /* Can be set by ACL */ store_reset(reset_point); } commit 6d9b05ae272ca2122b48451c317d601e449af932 Author: Jeremy Harris Date: Thu Aug 24 20:22:43 2023 +0100 DMARC: add ARC info to history records diff --git a/src/src/arc.c b/src/src/arc.c index ef44672f8..611697021 100644 --- a/src/src/arc.c +++ b/src/src/arc.c @@ -18,6 +18,10 @@ # include "pdkim/pdkim.h" # include "pdkim/signing.h" +# ifdef SUPPORT_DMARC +# include "dmarc.h" +# endif + extern pdkim_ctx * dkim_verify_ctx; extern pdkim_ctx dkim_sign_ctx; @@ -50,6 +54,7 @@ typedef struct arc_line { blob s; blob c; blob l; + blob ip; /* tag content sub-portions */ blob a_algo; @@ -89,12 +94,43 @@ typedef struct arc_ctx { #define HDR_AR US"Authentication-Results:" #define HDRLEN_AR 23 +typedef enum line_extract { + le_instance_only, + le_instance_plus_ip, + le_all +} line_extract_t; + static time_t now; static time_t expire; static hdr_rlist * headers_rlist; static arc_ctx arc_sign_ctx = { NULL }; static arc_ctx arc_verify_ctx = { NULL }; +/* We build a context for either Sign or Verify. + +For Verify, it's a fresh new one for ACL verify=arc - there is no connection +with the single line handling done during reception via the DKIM feed. + +For Verify we do it twice; initially during reception (via the DKIM feed) +and then later for the full verification. + +The former only looks at AMS headers, to discover what hash(es) we need done for +ARC on the message body; we call back to the DKIM code to set up so that it does +them for us during reception. That call needs info from many of the AMS tags; +arc_parse_line() for only the AMS is called asking for all the tag types. +That context is then discarded. + +Later, for Verify, we look at ARC headers again and then grab the hash result +from the DKIM layer. arc_parse_line() is called for all 3 line types, +gathering info for only 'i' and 'ip' tags from AAR headers, +for all tag types from AMS and AS headers. + + +For Sign, while running through the existing headers (before adding any for +this signing operation, we "take copies" of the headers, we call +arc_parse_line() gathering only the 'i' tag (instance) information. +*/ + /******************************************************************************/ @@ -188,18 +224,23 @@ return NULL; /* Inspect a header line, noting known tag fields. -Check for duplicates. */ +Check for duplicate named tags. + +See the file block comment for how this is used. + +Return: NULL for good, or an error string +*/ static uschar * -arc_parse_line(arc_line * al, header_line * h, unsigned off, BOOL instance_only) +arc_parse_line(arc_line * al, header_line * h, unsigned off, line_extract_t l_ext) { uschar * s = h->text + off; -uschar * r = NULL; /* compiler-quietening */ +uschar * r = NULL; uschar c; al->complete = h; -if (!instance_only) +if (l_ext == le_all) /* need to grab rawsig_no_b */ { al->rawsig_no_b_val.data = store_get(h->slen + 1, GET_TAINTED); memcpy(al->rawsig_no_b_val.data, h->text, off); /* copy the header name blind */ @@ -218,75 +259,77 @@ while ((c = *s)) uschar * bstart = NULL, * bend; /* tag-spec = [FWS] tag-name [FWS] "=" [FWS] tag-value [FWS] */ + /*X or just a naked FQDN, in a AAR ! */ - s = skip_fws(s); /* FWS */ + s = skip_fws(s); /* leading FWS */ if (!*s) break; -/* debug_printf("%s: consider '%s'\n", __FUNCTION__, s); */ tagchar = *s++; - s = skip_fws(s); /* FWS */ - if (!*s) break; + if (!*(s = skip_fws(s))) break; /* FWS */ - if (!instance_only || tagchar == 'i') switch (tagchar) + switch (tagchar) { case 'a': /* a= AMS algorithm */ - { - if (*s != '=') return US"no 'a' value"; - if (arc_insert_tagvalue(al, offsetof(arc_line, a), &s)) return US"a tag dup"; - - /* substructure: algo-hash (eg. rsa-sha256) */ - - t = al->a_algo.data = al->a.data; - while (*t != '-') - if (!*t++ || ++i > al->a.len) return US"no '-' in 'a' value"; - al->a_algo.len = i; - if (*t++ != '-') return US"no '-' in 'a' value"; - al->a_hash.data = t; - al->a_hash.len = al->a.len - i - 1; - } + if (l_ext == le_all && *s == '=') + { + if (arc_insert_tagvalue(al, offsetof(arc_line, a), &s)) return US"a tag dup"; + + /* substructure: algo-hash (eg. rsa-sha256) */ + + t = al->a_algo.data = al->a.data; + while (*t != '-') + if (!*t++ || ++i > al->a.len) return US"no '-' in 'a' value"; + al->a_algo.len = i; + if (*t++ != '-') return US"no '-' in 'a' value"; + al->a_hash.data = t; + al->a_hash.len = al->a.len - i - 1; + } break; case 'b': - { - gstring * g = NULL; - - switch (*s) + if (l_ext == le_all) { - case '=': /* b= AMS signature */ - if (al->b.data) return US"already b data"; - bstart = s+1; - - /* The signature can have FWS inserted in the content; - make a stripped copy */ - - while ((c = *++s) && c != ';') - if (c != ' ' && c != '\t' && c != '\n' && c != '\r') - g = string_catn(g, s, 1); - if (!g) return US"no b= value"; - al->b.len = len_string_from_gstring(g, &al->b.data); - gstring_release_unused(g); - bend = s; - break; - case 'h': /* bh= AMS body hash */ - s = skip_fws(++s); /* FWS */ - if (*s != '=') return US"no bh value"; - if (al->bh.data) return US"already bh data"; - - /* The bodyhash can have FWS inserted in the content; - make a stripped copy */ - - while ((c = *++s) && c != ';') - if (c != ' ' && c != '\t' && c != '\n' && c != '\r') - g = string_catn(g, s, 1); - if (!g) return US"no bh= value"; - al->bh.len = len_string_from_gstring(g, &al->bh.data); - gstring_release_unused(g); - break; - default: - return US"b? tag"; + gstring * g = NULL; + + switch (*s) + { + case '=': /* b= AMS signature */ + if (al->b.data) return US"already b data"; + bstart = s+1; + + /* The signature can have FWS inserted in the content; + make a stripped copy */ + + while ((c = *++s) && c != ';') + if (c != ' ' && c != '\t' && c != '\n' && c != '\r') + g = string_catn(g, s, 1); + if (!g) return US"no b= value"; + al->b.len = len_string_from_gstring(g, &al->b.data); + gstring_release_unused(g); + bend = s; + break; + case 'h': /* bh= AMS body hash */ + s = skip_fws(++s); /* FWS */ + if (*s == '=') + { + if (al->bh.data) return US"already bh data"; + + /* The bodyhash can have FWS inserted in the content; + make a stripped copy */ + + while ((c = *++s) && c != ';') + if (c != ' ' && c != '\t' && c != '\n' && c != '\r') + g = string_catn(g, s, 1); + if (!g) return US"no bh= value"; + al->bh.len = len_string_from_gstring(g, &al->bh.data); + gstring_release_unused(g); + } + break; + default: + return US"b? tag"; + } } - } break; case 'c': - switch (*s) + if (l_ext == le_all) switch (*s) { case '=': /* c= AMS canonicalisation */ if (arc_insert_tagvalue(al, offsetof(arc_line, c), &s)) return US"c tag dup"; @@ -309,43 +352,62 @@ while ((c = *s)) } break; case 'v': /* cv= AS validity */ - if (*++s != '=') return US"cv tag val"; - if (arc_insert_tagvalue(al, offsetof(arc_line, cv), &s)) return US"cv tag dup"; + s = skip_fws(s); + if (*++s == '=') + if (arc_insert_tagvalue(al, offsetof(arc_line, cv), &s)) + return US"cv tag dup"; break; - default: - return US"c? tag"; } break; case 'd': /* d= AMS domain */ - if (*s != '=') return US"d tag val"; - if (arc_insert_tagvalue(al, offsetof(arc_line, d), &s)) return US"d tag dup"; + if (l_ext == le_all && *s == '=') + if (arc_insert_tagvalue(al, offsetof(arc_line, d), &s)) + return US"d tag dup"; break; case 'h': /* h= AMS headers */ - if (*s != '=') return US"h tag val"; - if (arc_insert_tagvalue(al, offsetof(arc_line, h), &s)) return US"h tag dup"; + if (*s == '=') + if (arc_insert_tagvalue(al, offsetof(arc_line, h), &s)) + return US"h tag dup"; break; case 'i': /* i= ARC set instance */ - if (*s != '=') return US"i tag val"; - if (arc_insert_tagvalue(al, offsetof(arc_line, i), &s)) return US"i tag dup"; - if (instance_only) goto done; + if (*s == '=') + { + if (arc_insert_tagvalue(al, offsetof(arc_line, i), &s)) + return US"i tag dup"; + if (l_ext == le_instance_only) + goto done; /* early-out */ + } break; case 'l': /* l= bodylength */ - if (*s != '=') return US"l tag val"; - if (arc_insert_tagvalue(al, offsetof(arc_line, l), &s)) return US"l tag dup"; + if (l_ext == le_all && *s == '=') + if (arc_insert_tagvalue(al, offsetof(arc_line, l), &s)) + return US"l tag dup"; break; - case 's': /* s= AMS selector */ - if (*s != '=') return US"s tag val"; - if (arc_insert_tagvalue(al, offsetof(arc_line, s), &s)) return US"s tag dup"; + case 's': + if (*s == '=' && l_ext == le_all) + { + if (arc_insert_tagvalue(al, offsetof(arc_line, s), &s)) + return US"s tag dup"; + } + else if ( l_ext == le_instance_plus_ip + && Ustrncmp(s, "mtp.remote-ip", 13) == 0) + { /* smtp.remote-ip= AAR reception data */ + s += 13; + s = skip_fws(s); + if (*s != '=') return US"smtp.remote_ip tag val"; + if (arc_insert_tagvalue(al, offsetof(arc_line, ip), &s)) + return US"ip tag dup"; + } break; } - while ((c = *s) && c != ';') s++; + while ((c = *s) && c != ';') s++; /* end of this tag=value */ if (c) s++; /* ; after tag-spec */ /* for all but the b= tag, copy the field including FWS. For the b=, drop the tag content. */ - if (!instance_only) + if (r) if (bstart) { size_t n = bstart - fieldstart; @@ -366,7 +428,7 @@ while ((c = *s)) } } -if (!instance_only) +if (r) *r = '\0'; done: @@ -381,7 +443,7 @@ adding instances as needed and checking for duplicate lines. static uschar * arc_insert_hdr(arc_ctx * ctx, header_line * h, unsigned off, unsigned hoff, - BOOL instance_only, arc_line ** alp_ret) + line_extract_t l_ext, arc_line ** alp_ret) { unsigned i; arc_set * as; @@ -390,10 +452,10 @@ uschar * e; memset(al, 0, sizeof(arc_line)); -if ((e = arc_parse_line(al, h, off, instance_only))) +if ((e = arc_parse_line(al, h, off, l_ext))) { DEBUG(D_acl) if (e) debug_printf("ARC: %s\n", e); - return US"line parse"; + return string_sprintf("line parse: %s", e); } if (!(i = arc_instance_from_hdr(al))) return US"instance find"; if (i > 50) return US"overlarge instance number"; @@ -407,9 +469,10 @@ return NULL; +/* Called for both Sign and Verify */ static const uschar * -arc_try_header(arc_ctx * ctx, header_line * h, BOOL instance_only) +arc_try_header(arc_ctx * ctx, header_line * h, BOOL is_signing) { const uschar * e; @@ -425,10 +488,10 @@ if (strncmpic(ARC_HDR_AAR, h->text, ARC_HDRLEN_AAR) == 0) debug_printf("ARC: found AAR: %.*s\n", len, h->text); } if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AAR, offsetof(arc_set, hdr_aar), - TRUE, NULL))) + is_signing ? le_instance_only : le_instance_plus_ip, NULL))) { DEBUG(D_acl) debug_printf("inserting AAR: %s\n", e); - return US"inserting AAR"; + return string_sprintf("inserting AAR: %s", e); } } else if (strncmpic(ARC_HDR_AMS, h->text, ARC_HDRLEN_AMS) == 0) @@ -444,10 +507,10 @@ else if (strncmpic(ARC_HDR_AMS, h->text, ARC_HDRLEN_AMS) == 0) debug_printf("ARC: found AMS: %.*s\n", len, h->text); } if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AMS, offsetof(arc_set, hdr_ams), - instance_only, &ams))) + is_signing ? le_instance_only : le_all, &ams))) { DEBUG(D_acl) debug_printf("inserting AMS: %s\n", e); - return US"inserting AMS"; + return string_sprintf("inserting AMS: %s", e); } /* defaults */ @@ -468,10 +531,10 @@ else if (strncmpic(ARC_HDR_AS, h->text, ARC_HDRLEN_AS) == 0) debug_printf("ARC: found AS: %.*s\n", len, h->text); } if ((e = arc_insert_hdr(ctx, h, ARC_HDRLEN_AS, offsetof(arc_set, hdr_as), - instance_only, NULL))) + is_signing ? le_instance_only : le_all, NULL))) { DEBUG(D_acl) debug_printf("inserting AS: %s\n", e); - return US"inserting AS"; + return string_sprintf("inserting AS: %s", e); } } return NULL; @@ -481,7 +544,8 @@ return NULL; /* Gather the chain of arc sets from the headers. Check for duplicates while that is done. Also build the -reverse-order headers list; +reverse-order headers list. +Called on an ACL verify=arc condition. Return: ARC state if determined, eg. by lack of any ARC chain. */ @@ -1194,7 +1258,8 @@ arc_line * al = (arc_line *)(as+1); header_line * h = (header_line *)(al+1); g = string_catn(g, ARC_HDR_AAR, ARC_HDRLEN_AAR); -g = string_fmt_append(g, " i=%d; %s;\r\n\t", instance, identity); +g = string_fmt_append(g, " i=%d; %s; smtp.remote-ip=%s;\r\n\t", + instance, identity, sender_host_address); g = string_catn(g, US ar->data, ar->len); h->slen = g->ptr - aar_off; @@ -1773,7 +1838,7 @@ DEBUG(D_receive) debug_printf("ARC: spotted AMS header\n"); memset(&al, 0, sizeof(arc_line)); h.next = NULL; h.slen = len_string_from_gstring(g, &h.text); -if ((errstr = arc_parse_line(&al, &h, ARC_HDRLEN_AMS, FALSE))) +if ((errstr = arc_parse_line(&al, &h, ARC_HDRLEN_AMS, le_all))) { DEBUG(D_acl) if (errstr) debug_printf("ARC: %s\n", errstr); goto badline; @@ -1887,13 +1952,70 @@ if (arc_state) } else if (arc_state_reason) g = string_append(g, 3, US" (", arc_state_reason, US")"); - DEBUG(D_acl) debug_printf("ARC: authres '%.*s'\n", + DEBUG(D_acl) debug_printf("ARC:\tauthres '%.*s'\n", gstring_length(g) - start - 3, g->s + start + 3); } else - DEBUG(D_acl) debug_printf("ARC: no authres\n"); + DEBUG(D_acl) debug_printf("ARC:\tno authres\n"); +return g; +} + + +# ifdef SUPPORT_DMARC +/* Append a DMARC history record pair for ARC, to the given history set */ + +gstring * +arc_dmarc_hist_append(gstring * g) +{ +if (arc_state) + { + BOOL first = TRUE; + int i = Ustrcmp(arc_state, "pass") == 0 ? ARES_RESULT_PASS + : Ustrcmp(arc_state, "fail") == 0 ? ARES_RESULT_FAIL + : ARES_RESULT_UNKNOWN; + g = string_fmt_append(g, "arc %d\n", i); + g = string_fmt_append(g, "arc_policy %d json[", + i == ARES_RESULT_PASS ? DMARC_ARC_POLICY_RESULT_PASS + : i == ARES_RESULT_FAIL ? DMARC_ARC_POLICY_RESULT_FAIL + : DMARC_ARC_POLICY_RESULT_UNUSED); + /*XXX would we prefer this backwards? */ + for (arc_set * as = arc_verify_ctx.arcset_chain; as; + as = as->next, first = FALSE) + { + arc_line * line = as->hdr_as; + if (line) + { + blob * d = &line->d; + blob * s = &line->s; + + if (!first) + g = string_catn(g, US",", 1); + + g = string_fmt_append(g, " (\"i\":%u," /*)*/ + " \"d\":\"%.*s\"," + " \"s\":\"%.*s\"", + as->instance, + d->data ? (int)d->len : 0, d->data && d->len ? d->data : US"", + s->data ? (int)s->len : 0, s->data && s->len ? s->data : US"" + ); + if ((line = as->hdr_aar)) + { + blob * ip = &line->ip; + if (ip->data && ip->len) + g = string_fmt_append(g, ", \"ip\":\"%.*s\"", (int)ip->len, ip->data); + } + + g = string_catn(g, US")", 1); + } + } + g = string_catn(g, US" ]\n", 3); + } +else + g = string_fmt_append(g, "arc %d\narc_policy $d json:[]\n", + ARES_RESULT_UNKNOWN, DMARC_ARC_POLICY_RESULT_UNUSED); return g; } +# endif # endif /* DISABLE_DKIM */ diff --git a/src/src/dkim.c b/src/src/dkim.c index 068b802e0..a49c8d764 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -885,9 +885,9 @@ for (pdkim_signature * sig = dkim_signatures; sig; sig = sig->next) DEBUG(D_acl) if (gstring_length(g) == start) - debug_printf("DKIM: no authres\n"); + debug_printf("DKIM:\tno authres\n"); else - debug_printf("DKIM: authres '%.*s'\n", g->ptr - start - 3, g->s + start + 3); + debug_printf("DKIM:\tauthres '%.*s'\n", g->ptr - start - 3, g->s + start + 3); return g; } diff --git a/src/src/dmarc.c b/src/src/dmarc.c index 070095660..042ebe982 100644 --- a/src/src/dmarc.c +++ b/src/src/dmarc.c @@ -97,7 +97,8 @@ int *netmask = NULL; /* Ignored */ int is_ipv6 = 0; /* Set some sane defaults. Also clears previous results when - * multiple messages in one connection. */ +multiple messages in one connection. */ + dmarc_pctx = NULL; dmarc_status = US"none"; dmarc_abort = FALSE; @@ -153,11 +154,12 @@ return OK; } -/* dmarc_store_data stores the header data so that subsequent -dmarc_process can access the data */ +/* dmarc_store_data stores the header data so that subsequent dmarc_process can +access the data. +Called after the entire message has been received, with the From: header. */ int -dmarc_store_data(header_line *hdr) +dmarc_store_data(header_line * hdr) { /* No debug output because would change every test debug output */ if (!f.dmarc_disable_verify) @@ -167,7 +169,7 @@ return OK; static void -dmarc_send_forensic_report(u_char **ruf) +dmarc_send_forensic_report(u_char ** ruf) { uschar *recipient, *save_sender; BOOL send_status = FALSE; @@ -254,15 +256,19 @@ if (!dmarc_history_file) return DMARC_HIST_DISABLED; } if (!host_checking) - if ((history_file_fd = log_open_as_exim(dmarc_history_file)) < 0) + { + uschar * s = string_copy(dmarc_history_file); /* need a writeable copy */ + if ((history_file_fd = log_open_as_exim(s)) < 0) { log_write(0, LOG_MAIN|LOG_PANIC, "failure to create DMARC history file: %s: %s", - dmarc_history_file, strerror(errno)); + s, strerror(errno)); return DMARC_HIST_FILE_ERR; } + } + +/* Generate the contents of the history file entry */ -/* Generate the contents of the history file */ g = string_fmt_append(NULL, "job %s\nreporter %s\nreceived %ld\nipaddr %s\nfrom %s\nmfrom %s\n", message_id, primary_hostname, time(NULL), sender_host_address, @@ -301,6 +307,15 @@ g = string_fmt_append(g, "sp %d\n", tmp_ans); g = string_fmt_append(g, "align_dkim %d\nalign_spf %d\naction %d\n", da, sa, action); +#if DMARC_API >= 100400 +# ifdef EXPERIMENTAL_ARC +g = arc_dmarc_hist_append(g); +# else +g = string_fmt_append(g, "arc %d\narc_policy $d json:[]\n", + ARES_RESULT_UNKNOWN, DMARC_ARC_POLICY_RESULT_UNUSED); +# endif +#endif + /* Write the contents to the history file */ DEBUG(D_receive) { @@ -331,7 +346,8 @@ return DMARC_HIST_OK; /* dmarc_process adds the envelope sender address to the existing context (if any), retrieves the result, sets up expansion -strings and evaluates the condition outcome. */ +strings and evaluates the condition outcome. +Called for the first ACL dmarc= condition. */ int dmarc_process(void) @@ -536,7 +552,7 @@ The EDITME provides a DMARC_API variable */ break; } -/* Store the policy string in an expandable variable. */ + /* Store the policy string in an expandable variable. */ libdm_status = opendmarc_policy_fetch_p(dmarc_pctx, &tmp_ans); for (c = 0; dmarc_policy_description[c].name; c++) @@ -661,10 +677,16 @@ authres_dmarc(gstring * g) { if (f.dmarc_has_been_checked) { + int start = 0; /* Compiler quietening */ + DEBUG(D_acl) start = gstring_length(g); g = string_append(g, 2, US";\n\tdmarc=", dmarc_pass_fail); if (header_from_sender) g = string_append(g, 2, US" header.from=", header_from_sender); + DEBUG(D_acl) debug_printf("DMARC:\tauthres '%.*s'\n", + gstring_length(g) - start - 3, g->s + start + 3); } +else + DEBUG(D_acl) debug_printf("DMARC:\tno authres\n"); return g; } diff --git a/src/src/dmarc.h b/src/src/dmarc.h index 7ce0ca953..fa366dd06 100644 --- a/src/src/dmarc.h +++ b/src/src/dmarc.h @@ -58,4 +58,8 @@ uschar *dmarc_exim_expand_defaults(int); #define ARES_RESULT_UNKNOWN 11 #define ARES_RESULT_DISCARD 12 +#define DMARC_ARC_POLICY_RESULT_PASS 0 +#define DMARC_ARC_POLICY_RESULT_UNUSED 1 +#define DMARC_ARC_POLICY_RESULT_FAIL 2 + #endif /* SUPPORT_DMARC */ diff --git a/src/src/functions.h b/src/src/functions.h index 0b030e4fe..5db9bc610 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -109,6 +109,9 @@ extern tree_node *acl_var_create(uschar *); extern void acl_var_write(uschar *, uschar *, void *); #ifdef EXPERIMENTAL_ARC +# ifdef SUPPORT_DMARC +extern gstring *arc_dmarc_hist_append(gstring *); +# endif extern void *arc_ams_setup_sign_bodyhash(void); extern const uschar *arc_header_feed(gstring *, BOOL); extern gstring *arc_sign(const uschar *, gstring *, uschar **); diff --git a/src/src/spf.c b/src/src/spf.c index 6f0917a9c..3d83f07ba 100644 --- a/src/src/spf.c +++ b/src/src/spf.c @@ -401,20 +401,31 @@ gstring * authres_spf(gstring * g) { uschar * s; -if (!spf_result) return g; - -g = string_append(g, 2, US";\n\tspf=", spf_result); -if (spf_result_guessed) - g = string_cat(g, US" (best guess record for domain)"); +if (spf_result) + { + int start = 0; /* Compiler quietening */ + DEBUG(D_acl) start = gstring_length(g); -s = expand_string(US"$sender_address_domain"); -if (s && *s) - return string_append(g, 2, US" smtp.mailfrom=", s); + g = string_append(g, 2, US";\n\tspf=", spf_result); + if (spf_result_guessed) + g = string_cat(g, US" (best guess record for domain)"); -s = sender_helo_name; -return s && *s - ? string_append(g, 2, US" smtp.helo=", s) - : string_cat(g, US" smtp.mailfrom=<>"); + s = expand_string(US"$sender_address_domain"); + if (s && *s) + g = string_append(g, 2, US" smtp.mailfrom=", s); + else + { + s = sender_helo_name; + g = s && *s + ? string_append(g, 2, US" smtp.helo=", s) + : string_cat(g, US" smtp.mailfrom=<>"); + } + DEBUG(D_acl) debug_printf("SPF:\tauthres '%.*s'\n", + gstring_length(g) - start - 3, g->s + start + 3); + } +else + DEBUG(D_acl) debug_printf("SPF:\tno authres\n"); +return g; } commit 92914be2140997c19f610d7f5f17fa0d9c347b9c Author: Jeremy Harris Date: Thu Aug 31 15:16:09 2023 +0100 Avoid sending DSN when message was accepted under fakereject or fakedefer. Bug 3016 diff --git a/src/src/acl.c b/src/src/acl.c index ab991ef41..118e4b35d 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -3428,7 +3428,7 @@ for (; cb; cb = cb->next) case CONTROL_FAKEREJECT: cancel_cutthrough_connection(TRUE, US"fakereject"); case CONTROL_FAKEDEFER: - fake_response = (control_type == CONTROL_FAKEDEFER) ? DEFER : FAIL; + fake_response = control_type == CONTROL_FAKEDEFER ? DEFER : FAIL; if (*p == '/') { const uschar *pp = p + 1; diff --git a/src/src/deliver.c b/src/src/deliver.c index 52270368e..c9a1d074b 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -6193,11 +6193,11 @@ return child_close(pid, 0) == 0; *************************************************/ static void -maybe_send_dsn(void) +maybe_send_dsn(const address_item * const addr_succeed) { address_item * addr_senddsn = NULL; -for (address_item * a = addr_succeed; a; a = a->next) +for (const address_item * a = addr_succeed; a; a = a->next) { /* af_ignore_error not honored here. it's not an error */ DEBUG(D_deliver) debug_printf("DSN: processing router : %s\n" @@ -8136,7 +8136,7 @@ else if (!f.dont_deliver) /* Send DSN for successful messages if requested */ -maybe_send_dsn(); +maybe_send_dsn(addr_succeed); /* If any addresses failed, we must send a message to somebody, unless af_ignore_error is set, in which case no action is taken. It is possible for diff --git a/src/src/receive.c b/src/src/receive.c index 14038f2ec..3c139b3af 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -514,7 +514,7 @@ Returns: nothing */ void -receive_add_recipient(uschar *recipient, int pno) +receive_add_recipient(uschar * recipient, int pno) { if (recipients_count >= recipients_list_max) { @@ -2669,7 +2669,7 @@ if (extract_recip) that this has happened, in order to give a better error if there are no recipients left. */ - else if (recipient != NULL) + else if (recipient) { if (tree_search(tree_nonrecipients, recipient) == NULL) receive_add_recipient(recipient, -1); @@ -2679,7 +2679,7 @@ if (extract_recip) /* Move on past this address */ - s = ss + (*ss? 1:0); + s = ss + (*ss ? 1 : 0); while (isspace(*s)) s++; } /* Next address */ @@ -3861,10 +3861,10 @@ the spool file gets corrupted. Ensure that all recipients are qualified. */ if (rc == LOCAL_SCAN_ACCEPT) { if (local_scan_data) - for (uschar * s = local_scan_data; *s != 0; s++) if (*s == '\n') *s = ' '; - for (int i = 0; i < recipients_count; i++) + for (uschar * s = local_scan_data; *s; s++) if (*s == '\n') *s = ' '; + for (recipient_item * r = recipients_list; + r < recipients_list + recipients_count; r++) { - recipient_item *r = recipients_list + i; r->address = rewrite_address_qualify(r->address, TRUE); if (r->errors_to) r->errors_to = rewrite_address_qualify(r->errors_to, TRUE); @@ -3946,6 +3946,19 @@ signal(SIGTERM, SIG_IGN); signal(SIGINT, SIG_IGN); #endif /* HAVE_LOCAL_SCAN */ +/* If we are faking a reject or defer, avoid sennding a DSN for the +actually-accepted message */ + +if (fake_response != OK) + for (recipient_item * r = recipients_list; + r < recipients_list + recipients_count; r++) + { + DEBUG(D_receive) if (r->dsn_flags & (rf_notify_success | rf_notify_delay)) + debug_printf("DSN: clearing flags due to fake-response for message\n"); + r->dsn_flags = r->dsn_flags & ~(rf_notify_success | rf_notify_delay) + | rf_notify_never; + } + /* Ensure the first time flag is set in the newly-received message. */ commit 0f3894451894638eff8bf9537e5134a43420837e Author: Jeremy Harris Date: Thu Aug 31 19:06:05 2023 +0100 Taint: de-taint $2 (fixed part of key) from a matching partial-search diff --git a/src/src/search.c b/src/src/search.c index 2b6e5d37f..b00bc9ab0 100644 --- a/src/src/search.c +++ b/src/src/search.c @@ -812,7 +812,7 @@ just in case the original key is too long for the string_sprintf() buffer (it else if (partial >= 0) { int len = Ustrlen(keystring); - uschar *keystring2; + uschar * keystring2; /* Try with the affix on the front, except for a zero-length affix */ @@ -833,20 +833,20 @@ else if (partial >= 0) if (!yield) { int dotcount = 0; - uschar *keystring3 = keystring2 + affixlen; - uschar *s = keystring3; - while (*s != 0) if (*s++ == '.') dotcount++; + uschar * keystring3 = keystring2 + affixlen; + + for(uschar * s = keystring3; *s; ) if (*s++ == '.') dotcount++; while (dotcount-- >= partial) { - while (*keystring3 != 0 && *keystring3 != '.') keystring3++; + while (*keystring3 && *keystring3 != '.') keystring3++; /* If we get right to the end of the string (which will be the last time through this loop), we've failed if the affix is null. Otherwise do one last lookup for the affix itself, but if it is longer than 1 character, remove the last character if it is ".". */ - if (*keystring3 == 0) + if (!*keystring3) { if (affixlen < 1) break; if (affixlen > 1 && affix[affixlen-1] == '.') affixlen--; @@ -867,7 +867,8 @@ else if (partial >= 0) if (yield) { /* First variable is the wild part; second is the fixed part. Take care - to get it right when keystring3 is just "*". */ + to get it right when keystring3 is just "*". Return a de-tainted version + of the fixed part, on the grounds it has been validated by the lookup. */ if (expand_setup && *expand_setup >= 0) { @@ -877,8 +878,10 @@ else if (partial >= 0) expand_nstring[*expand_setup] = keystring; expand_nlength[*expand_setup] = wildlength; *expand_setup += 1; - expand_nstring[*expand_setup] = keystring + wildlength + 1; - expand_nlength[*expand_setup] = (fixedlength < 0)? 0 : fixedlength; + if (fixedlength < 0) fixedlength = 0; + expand_nstring[*expand_setup] = string_copyn_taint( + keystring + wildlength + 1, fixedlength, GET_UNTAINTED); + expand_nlength[*expand_setup] = fixedlength; } break; } @@ -896,10 +899,10 @@ is set to the string to the left of the @. */ if (!yield && starflags & SEARCH_STARAT) { uschar *atat = Ustrrchr(keystring, '@'); - if (atat != NULL && atat > keystring) + if (atat && atat > keystring) { int savechar; - savechar = *(--atat); + savechar = *--atat; *atat = '*'; DEBUG(D_lookup) debug_printf_indent("trying default match %s\n", atat); @@ -943,16 +946,19 @@ complete non-wild domain entry, or we matched a wild-carded entry without chopping off any of the domain components, set up the expansion variables (if required) so that the first one is empty, and the second one is the fixed part of the domain. The set_null_wild flag is set only when yield is not -NULL. */ +NULL. Return a de-tainted version of the fixed part, on the grounds it has been +validated by the lookup. */ if (set_null_wild && expand_setup && *expand_setup >= 0) { + int fixedlength = Ustrlen(keystring); *expand_setup += 1; expand_nstring[*expand_setup] = keystring; expand_nlength[*expand_setup] = 0; *expand_setup += 1; - expand_nstring[*expand_setup] = keystring; - expand_nlength[*expand_setup] = Ustrlen(keystring); + expand_nstring[*expand_setup] = string_copyn_taint( + keystring, fixedlength, GET_UNTAINTED); + expand_nlength[*expand_setup] = fixedlength; } /* If we have a result, check the options to see if the key was wanted rather commit 9b810c775c6e9dd1f8a87a743b943b465a1ca5a1 Author: Jeremy Harris Date: Fri Sep 1 11:44:32 2023 +0100 Taint: track SASL auth intermediate inputs diff --git a/src/src/auths/cram_md5.c b/src/src/auths/cram_md5.c index 280b5293a..583080211 100644 --- a/src/src/auths/cram_md5.c +++ b/src/src/auths/cram_md5.c @@ -163,13 +163,13 @@ md5_end(&base, md5secret, 16, digestptr); /* For interface, see auths/README */ int -auth_cram_md5_server(auth_instance *ablock, uschar *data) +auth_cram_md5_server(auth_instance * ablock, uschar * data) { -auth_cram_md5_options_block *ob = +auth_cram_md5_options_block * ob = (auth_cram_md5_options_block *)(ablock->options_block); -uschar *challenge = string_sprintf("<%d.%ld@%s>", getpid(), +uschar * challenge = string_sprintf("<%d.%ld@%s>", getpid(), (long int) time(NULL), primary_hostname); -uschar *clear, *secret; +uschar * clear, * secret; uschar digest[16]; int i, rc, len; @@ -186,7 +186,7 @@ if (*data) return UNEXPECTED; /* Send the challenge, read the return */ if ((rc = auth_get_data(&data, challenge, Ustrlen(challenge))) != OK) return rc; -if ((len = b64decode(data, &clear)) < 0) return BAD64; +if ((len = b64decode(data, &clear, GET_TAINTED)) < 0) return BAD64; /* The return consists of a user name, space-separated from the CRAM-MD5 digest, expressed in hex. Extract the user name and put it in $auth1 and $1. @@ -298,7 +298,7 @@ if (smtp_write_command(sx, SCMD_FLUSH, "AUTH %s\r\n", ablock->public_name) < 0) if (!smtp_read_response(sx, buffer, buffsize, '3', timeout)) return FAIL; -if (b64decode(buffer + 4, &challenge) < 0) +if (b64decode(buffer + 4, &challenge, buffer + 4) < 0) { string_format(buffer, buffsize, "bad base 64 string in challenge: %s", big_buffer + 4); diff --git a/src/src/auths/cyrus_sasl.c b/src/src/auths/cyrus_sasl.c index b5d2d1d3b..a3d3906b8 100644 --- a/src/src/auths/cyrus_sasl.c +++ b/src/src/auths/cyrus_sasl.c @@ -204,16 +204,16 @@ sasl_done(); within a shortlived child */ int -auth_cyrus_sasl_server(auth_instance *ablock, uschar *data) +auth_cyrus_sasl_server(auth_instance * ablock, uschar * data) { -auth_cyrus_sasl_options_block *ob = +auth_cyrus_sasl_options_block * ob = (auth_cyrus_sasl_options_block *)(ablock->options_block); -uschar *output, *out2, *input, *clear, *hname; -uschar *debug = NULL; /* Stops compiler complaining */ +uschar * output, * out2, * input, * clear, * hname; +uschar * debug = NULL; /* Stops compiler complaining */ sasl_callback_t cbs[] = {{SASL_CB_LIST_END, NULL, NULL}}; -sasl_conn_t *conn; +sasl_conn_t * conn; char * realm_expanded = NULL; -int rc, firsttime = 1, clen, *negotiated_ssf_ptr = NULL, negotiated_ssf; +int rc, firsttime = 1, clen, * negotiated_ssf_ptr = NULL, negotiated_ssf; unsigned int inlen, outlen; input = data; @@ -232,7 +232,7 @@ if (!hname || !realm_expanded && ob->server_realm) if (inlen) { - if ((clen = b64decode(input, &clear)) < 0) + if ((clen = b64decode(input, &clear, input)) < 0) return BAD64; input = clear; inlen = clen; @@ -345,10 +345,10 @@ for (rc = SASL_CONTINUE; rc == SASL_CONTINUE; ) } inlen = Ustrlen(input); - HDEBUG(D_auth) debug = string_copy(input); + HDEBUG(D_auth) debug = string_copy_taint(input, GET_TAINTED); if (inlen) { - if ((clen = b64decode(input, &clear)) < 0) + if ((clen = b64decode(input, &clear, GET_TAINTED)) < 0) { sasl_dispose(&conn); sasl_done(); diff --git a/src/src/auths/get_data.c b/src/src/auths/get_data.c index caf4cfdb8..4a35ed064 100644 --- a/src/src/auths/get_data.c +++ b/src/src/auths/get_data.c @@ -33,7 +33,7 @@ else uschar * clear, * end; int len; - if ((len = b64decode(data, &clear)) < 0) return BAD64; + if ((len = b64decode(data, &clear, GET_TAINTED)) < 0) return BAD64; DEBUG(D_auth) debug_printf("auth input decode:"); for (end = clear + len; clear < end && expand_nmax < EXPAND_MAXN; ) { @@ -66,6 +66,10 @@ Arguments: Returns: OK on success BAD64 if response too large for buffer CANCELLED if response is "*" + +NOTE: the data came from the wire so should be tainted - but +big_buffer is not taint-tracked. EVERY CALLER needs to apply +tainting. */ int @@ -97,7 +101,7 @@ uschar * resp, * clear, * end; if ((rc = auth_get_data(&resp, challenge, Ustrlen(challenge))) != OK) return rc; -if ((len = b64decode(resp, &clear)) < 0) +if ((len = b64decode(resp, &clear, GET_TAINTED)) < 0) return BAD64; end = clear + len; @@ -228,7 +232,7 @@ if (flags & AUTH_ITEM_LAST) /* Now that we know we'll continue, we put the received data into $auth, if possible. First, decode it: buffer+4 skips over the SMTP status code. */ -clear_len = b64decode(buffer+4, &clear); +clear_len = b64decode(buffer+4, &clear, buffer+4); /* If decoding failed, the default is to terminate the authentication, and return FAIL, with the SMTP response still in the buffer. However, if client_ diff --git a/src/src/auths/heimdal_gssapi.c b/src/src/auths/heimdal_gssapi.c index 7a74d5be5..59884ef58 100644 --- a/src/src/auths/heimdal_gssapi.c +++ b/src/src/auths/heimdal_gssapi.c @@ -334,7 +334,7 @@ while (step < 4) break; case 1: - gbufdesc_in.length = b64decode(from_client, USS &gbufdesc_in.value); + gbufdesc_in.length = b64decode(from_client, USS &gbufdesc_in.value, GET_TAINTED); if (gclient) { maj_stat = gss_release_name(&min_stat, &gclient); @@ -419,7 +419,7 @@ while (step < 4) break; case 3: - gbufdesc_in.length = b64decode(from_client, USS &gbufdesc_in.value); + gbufdesc_in.length = b64decode(from_client, USS &gbufdesc_in.value, GET_TAINTED); maj_stat = gss_unwrap(&min_stat, gcontext, &gbufdesc_in, /* data from client */ diff --git a/src/src/base64.c b/src/src/base64.c index e9ac41a55..591ea3d5b 100644 --- a/src/src/base64.c +++ b/src/src/base64.c @@ -152,7 +152,7 @@ static uschar dec64table[] = { }; int -b64decode(const uschar *code, uschar **ptr) +b64decode(const uschar * code, uschar ** ptr, const void * proto_mem) { int x, y; @@ -160,7 +160,7 @@ uschar *result; { int l = Ustrlen(code); - *ptr = result = store_get(1 + l/4 * 3 + l%4, code); + *ptr = result = store_get(1 + l/4 * 3 + l%4, proto_mem); } /* Each cycle of the loop handles a quantum of 4 input bytes. For the last diff --git a/src/src/expand.c b/src/src/expand.c index 590b40383..b4a76b3e7 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -8135,7 +8135,7 @@ NOT_ITEM: ; case EOP_BASE64D: { uschar * s; - int len = b64decode(sub, &s); + int len = b64decode(sub, &s, sub); if (len < 0) { expand_string_message = string_sprintf("string \"%s\" is not " diff --git a/src/src/functions.h b/src/src/functions.h index 5db9bc610..4222c623a 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -157,7 +157,7 @@ extern gstring *authres_spf(gstring *); extern uschar *b64encode(const uschar *, int); extern uschar *b64encode_taint(const uschar *, int, const void *); -extern int b64decode(const uschar *, uschar **); +extern int b64decode(const uschar *, uschar **, const void *); extern int bdat_getc(unsigned); extern uschar *bdat_getbuf(unsigned *); extern BOOL bdat_hasc(void); diff --git a/src/src/lss.c b/src/src/lss.c index e6ec1d6d1..55df5775e 100644 --- a/src/src/lss.c +++ b/src/src/lss.c @@ -134,9 +134,9 @@ A zero is added on to the end to make it easy in cases where the result is to be interpreted as text. This is not included in the count. */ int -lss_b64decode(uschar *code, uschar **ptr) +lss_b64decode(uschar * code, uschar ** ptr) { -return b64decode(code, ptr); +return b64decode(code, ptr, code); } diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index c8f180a58..30cb0437c 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -448,7 +448,7 @@ return n; void pdkim_decode_base64(const uschar * str, blob * b) { -int dlen = b64decode(str, &b->data); +int dlen = b64decode(str, &b->data, str); if (dlen < 0) b->data = NULL; b->len = dlen; } diff --git a/src/src/pdkim/signing.c b/src/src/pdkim/signing.c index 07737ab41..35ca79fc1 100644 --- a/src/src/pdkim/signing.c +++ b/src/src/pdkim/signing.c @@ -419,7 +419,7 @@ if ( !(s1 = Ustrstr(CS privkey_pem, "-----BEGIN RSA PRIVATE KEY-----")) *s2 = '\0'; -if ((rc = b64decode(s1, &der.data) < 0)) +if ((rc = b64decode(s1, &der.data, s1) < 0)) return US"Bad PEM-DER b64 decode"; der.len = rc; diff --git a/src/src/rfc2047.c b/src/src/rfc2047.c index d5e33b9b1..9d7a6e023 100644 --- a/src/src/rfc2047.c +++ b/src/src/rfc2047.c @@ -122,7 +122,7 @@ for (;; string = mimeword + 2) encoding = toupper((*q1ptr)[1]); **endptr = 0; if (encoding == 'B') - dlen = b64decode(*q2ptr+1, dptrptr); + dlen = b64decode(*q2ptr+1, dptrptr, *q2ptr+1); else if (encoding == 'Q') dlen = rfc2047_qpdecode(*q2ptr+1, dptrptr); **endptr = '?'; /* restore */ commit 5304327c962ff5d8d5a1cb15fbb44923fe213435 Author: Jeremy Harris Date: Thu Sep 7 16:02:03 2023 +0100 Build: check during make for perl script library requirements diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base index e0aa1dc6c..6778331c7 100644 --- a/src/OS/Makefile-Base +++ b/src/OS/Makefile-Base @@ -287,6 +287,7 @@ exicyclog: config ../src/exicyclog.src ../src/exicyclog.src > exicyclog-t @mv exicyclog-t exicyclog @chmod a+x exicyclog + @./exicyclog -v 2>&1 >/dev/null @echo ">>> exicyclog script built" # Target for the exinext utility script @@ -304,6 +305,7 @@ exinext: config ../src/exinext.src ../src/exinext.src > exinext-t @mv exinext-t exinext @chmod a+x exinext + @./exinext -v 2>&1 >/dev/null @echo ">>> exinext script built" # Target for the exiwhat utility script @@ -328,6 +330,7 @@ exiwhat: config ../src/exiwhat.src ../src/exiwhat.src > exiwhat-t @mv exiwhat-t exiwhat @chmod a+x exiwhat + @./exiwhat -v 2>&1 >/dev/null @echo ">>> exiwhat script built" # Target for the exim_checkaccess utility script @@ -346,6 +349,7 @@ exim_checkaccess: config ../src/exim_checkaccess.src ../src/exim_checkaccess.src > exim_checkaccess-t @mv exim_checkaccess-t exim_checkaccess @chmod a+x exim_checkaccess + # @./exim_checkaccess -v 2>&1 >/dev/null @echo ">>> exim_checkaccess script built"; echo "" # Target for the Exim monitor start-up script @@ -366,6 +370,7 @@ eximon: config ../src/eximon.src ../OS/eximon.conf-Default \ -e "s?EXIM_RELEASE_VERSION?$${EXIM_RELEASE_VERSION}?" \ -e "s?EXIM_VARIANT_VERSION?$${EXIM_VARIANT_VERSION}?" \ ../src/eximon.src >> eximon + @./eximon -v 2>&1 >/dev/null @echo ">>> eximon script built"; echo "" # Targets for utilities; these are all Perl scripts that have to get the @@ -385,6 +390,7 @@ exigrep: config ../src/exigrep.src ../src/exigrep.src > exigrep-t @mv exigrep-t exigrep @chmod a+x exigrep + @./exigrep --version 2>&1 >/dev/null @echo ">>> exigrep script built" exim_msgdate: config ../src/exim_msgdate.src @@ -402,6 +408,7 @@ exim_msgdate: config ../src/exim_msgdate.src ../src/exim_msgdate.src > exim_msgdate-t @mv exim_msgdate-t exim_msgdate @chmod a+x exim_msgdate + @./exim_msgdate -v 2>&1 >/dev/null @echo ">>> exim_msgdate script built" eximstats: config ../src/eximstats.src @@ -413,6 +420,7 @@ eximstats: config ../src/eximstats.src ../src/eximstats.src > eximstats-t @mv eximstats-t eximstats @chmod a+x eximstats + @./eximstats -v 2>&1 >/dev/null @echo ">>> eximstats script built" exiqgrep: config ../src/exiqgrep.src @@ -428,6 +436,7 @@ exiqgrep: config ../src/exiqgrep.src ../src/exiqgrep.src > exiqgrep-t @mv exiqgrep-t exiqgrep @chmod a+x exiqgrep + @./exiqgrep -v 2>&1 >/dev/null @echo ">>> exiqgrep script built" exiqsumm: config ../src/exiqsumm.src @@ -439,6 +448,7 @@ exiqsumm: config ../src/exiqsumm.src ../src/exiqsumm.src > exiqsumm-t @mv exiqsumm-t exiqsumm @chmod a+x exiqsumm + @./exiqsumm -v 2>&1 >/dev/null @echo ">>> exiqsumm script built" exipick: config ../src/exipick.src @@ -452,6 +462,7 @@ exipick: config ../src/exipick.src ../src/exipick.src > exipick-t @mv exipick-t exipick @chmod a+x exipick + @./exipick -v 2>&1 >/dev/null @echo ">>> exipick script built" exim_id_update: config ../src/exim_id_update.src @@ -465,6 +476,7 @@ exim_id_update: config ../src/exim_id_update.src ../src/exim_id_update.src > exim_id_update-t @mv exim_id_update-t exim_id_update @chmod a+x exim_id_update + @./exim_id_update -v 2>&1 >/dev/null @echo ">>> exim_id_update script built" transport-filter.pl: config ../src/transport-filter.src @@ -487,6 +499,7 @@ convert4r3: config ../src/convert4r3.src ../src/convert4r3.src > convert4r3-t @mv convert4r3-t convert4r3 @chmod a+x convert4r3 + @./convert4r3 -v 2>&1 >/dev/null @echo ">>> convert4r3 script built" convert4r4: config ../src/convert4r4.src @@ -498,6 +511,7 @@ convert4r4: config ../src/convert4r4.src ../src/convert4r4.src > convert4r4-t @mv convert4r4-t convert4r4 @chmod a+x convert4r4 + @./convert4r4 -v 2>&1 >/dev/null @echo ">>> convert4r4 script built" diff --git a/src/src/exicyclog.src b/src/src/exicyclog.src index 20bf9fcd4..ce43b80b0 100644 --- a/src/src/exicyclog.src +++ b/src/src/exicyclog.src @@ -72,7 +72,7 @@ while [ $# -gt 0 ] ; do -k) keep=$2 shift ;; - --version) + --version|-v) echo "`basename $0`: $0" echo "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION" exit 0 diff --git a/src/src/exim_checkaccess.src b/src/src/exim_checkaccess.src index 360f307ba..159d9a472 100755 --- a/src/src/exim_checkaccess.src +++ b/src/src/exim_checkaccess.src @@ -68,11 +68,11 @@ use FileHandle; use File::Basename; use IPC::Open2; -if ($ARGV[0] eq '--version') { +if ($ARGV[0] eq '--version' || $ARGV[0] eq '-v') { print basename($0) . ": $0\n", "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n", "perl(runtime): $]\n"; - exit 0; + exit 0; } if (scalar(@ARGV) < 3) diff --git a/src/src/exim_id_update.src b/src/src/exim_id_update.src index 28fff1c4d..1597accc9 100644 --- a/src/src/exim_id_update.src +++ b/src/src/exim_id_update.src @@ -25,12 +25,18 @@ my $id; my $b62 = '[0-9A-Za-z]'; -if ( !getopts('hud', \%opt) +if ( !getopts('hudv', \%opt) || $opt{h} - || !$opt{u} && !$opt{d} + || !$opt{v} && !$opt{u} && !$opt{d} ) { &help; exit 1; } +if ($opt{v}) { + print "exim_id_update:\n", + "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n", + "perl(runtime): $]\n"; + exit 0; +} $spool = $ARGV[0] if ($ARGV[0]); $mode_upgrade = $opt{u}; @@ -42,11 +48,12 @@ in spool files. Only the filenames is first-line ID tag values are affected; not message content such as Message-ID fields. Only -H, -D and -J files are handled. -Syntax: exim_id_update [-d | -u | -h] [spooldir] +Syntax: exim_id_update [-d | -u | -h | -v] [spooldir] -d Downgrade mode -h This help message -u Upgrade mode + -v Version Exactly one of -d or -u must be given. The spool directory defaults to the build-time value, diff --git a/src/src/eximon.src b/src/src/eximon.src index 6293a7cc2..77bd88050 100644 --- a/src/src/eximon.src +++ b/src/src/eximon.src @@ -19,7 +19,7 @@ # PROCESSED_FLAG # -if test "x$1" = x--version +if [ "x$1" = x--version -o "x$1" = x-v ] then echo "`basename $0`: $0" echo "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION" diff --git a/src/src/eximstats.src b/src/src/eximstats.src index 3b89e6bd7..b961df8e1 100644 --- a/src/src/eximstats.src +++ b/src/src/eximstats.src @@ -557,7 +557,7 @@ use File::Basename; # use Time::Local; # PH/FANF use POSIX; -if (@ARGV and $ARGV[0] eq '--version') { +if (@ARGV and ($ARGV[0] eq '--version' || $ARGV[0] eq '-v')) { print basename($0) . ": $0\n", "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n", "perl(runtime): $]\n"; diff --git a/src/src/exinext.src b/src/src/exinext.src index 913801867..30e08a5e2 100644 --- a/src/src/exinext.src +++ b/src/src/exinext.src @@ -25,7 +25,7 @@ config= eximmacdef= exim_path= -if test "x$1" = x--version +if [ "x$1" = x--version -o "x$1" = x-v ] then echo "`basename $0`: $0" echo "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION" diff --git a/src/src/exipick.src b/src/src/exipick.src index 61527ca64..c3830f4a5 100644 --- a/src/src/exipick.src +++ b/src/src/exipick.src @@ -90,7 +90,7 @@ GetOptions( 'show-tests' => \$G::show_tests, # display tests as applied to each message 'man' => sub { pod2usage(-verbose => 2, -exit => 0, -noperldoc => system('perldoc -V >/dev/null 2>&1')) }, 'help' => sub { pod2usage(-verbose => 1, -exit => 0) }, - 'version' => sub { + 'v|version' => sub { print "$p_name: $0\n", "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n", "perl(runtime): $]\n"; diff --git a/src/src/exiqgrep.src b/src/src/exiqgrep.src index 74c29f7ef..6a0d40b51 100644 --- a/src/src/exiqgrep.src +++ b/src/src/exiqgrep.src @@ -50,7 +50,7 @@ if ($^O eq 'darwin') { # aka MacOS X $base = 62; }; -if ($ARGV[0] eq '--version') { +if ($ARGV[0] eq '--version' || $ARGV[0] eq '-v') { print basename($0) . ": $0\n", "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n", "perl(runtime): $]\n"; diff --git a/src/src/exiqsumm.src b/src/src/exiqsumm.src index afb74fdcd..3918ab0b8 100644 --- a/src/src/exiqsumm.src +++ b/src/src/exiqsumm.src @@ -50,7 +50,7 @@ use warnings; BEGIN { pop @INC if $INC[-1] eq '.' }; use File::Basename; -if (@ARGV && $ARGV[0] eq '--version') { +if (@ARGV && ($ARGV[0] eq '--version' || ($ARGV[0] eq '-v'))) { print basename($0) . ": $0\n", "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n", "perl(runtime): $]\n"; diff --git a/src/src/exiwhat.src b/src/src/exiwhat.src index a1f748edd..812f0b149 100644 --- a/src/src/exiwhat.src +++ b/src/src/exiwhat.src @@ -55,7 +55,7 @@ signal=EXIWHAT_KILL_SIGNAL # See if this installation is using the esoteric "USE_NODE" feature of Exim, # in which it uses the host's name as a suffix for the configuration file name. -if test "x$1" = x--version +if [ "x$1" = x--version -o "x$1" = x-v ] then echo "`basename $0`: $0" echo "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION" diff --git a/src/src/transport-filter.src b/src/src/transport-filter.src index db00d877f..ba86d8f46 100644 --- a/src/src/transport-filter.src +++ b/src/src/transport-filter.src @@ -15,7 +15,7 @@ use warnings; BEGIN { pop @INC if $INC[-1] eq '.' }; use File::Basename; -if ($ARGV[0] eq '--version') { +if ($ARGV[0] eq '--version' || $ARGV[0] eq '-v') { print basename($0) . ": $0\n", "build: EXIM_RELEASE_VERSIONEXIM_VARIANT_VERSION\n", "perl(runtime): $]\n"; commit 87ae73f8bea31b51fbcd6e1ae80742cd4d792308 Author: u34 Date: Thu Sep 7 16:14:55 2023 +0100 Docs: typo in comment in example config. Bug 3022 diff --git a/src/src/configure.default b/src/src/configure.default index 83380bdaa..633c6539e 100644 --- a/src/src/configure.default +++ b/src/src/configure.default @@ -847,7 +847,7 @@ smarthost_smtp: # request with your smarthost provider to get things fixed: hosts_require_tls = * tls_verify_hosts = * - # As long as tls_verify_hosts is enabled, this this will have no effect, + # As long as tls_verify_hosts is enabled this will have no effect, # but if you have to comment it out then this will at least log whether # you succeed or not: tls_try_verify_hosts = * commit 445c8a471eefdfc7341a7b53d692650582ade484 Author: Jeremy Harris Date: Sun Sep 10 10:06:56 2023 +0100 Feature advertisements for radius and pwcheck diff --git a/src/src/exim.c b/src/src/exim.c index a96d12167..8a78689ff 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -1026,47 +1026,53 @@ gstring * g = NULL; DEBUG(D_any) {} else g = show_db_version(g); g = string_cat(g, US"Support for:"); +#ifdef WITH_CONTENT_SCAN + g = string_cat(g, US" Content_Scanning"); +#endif #ifdef SUPPORT_CRYPTEQ g = string_cat(g, US" crypteq"); #endif +#ifdef EXPAND_DLFUNC + g = string_cat(g, US" Expand_dlfunc"); +#endif #if HAVE_ICONV g = string_cat(g, US" iconv()"); #endif #if HAVE_IPV6 g = string_cat(g, US" IPv6"); #endif -#ifdef HAVE_SETCLASSRESOURCES - g = string_cat(g, US" use_setclassresources"); -#endif #ifdef SUPPORT_PAM g = string_cat(g, US" PAM"); #endif #ifdef EXIM_PERL g = string_cat(g, US" Perl"); #endif -#ifdef EXPAND_DLFUNC - g = string_cat(g, US" Expand_dlfunc"); -#endif -#ifdef USE_TCP_WRAPPERS - g = string_cat(g, US" TCPwrappers"); -#endif #ifdef USE_GNUTLS g = string_cat(g, US" GnuTLS"); #endif +#ifdef SUPPORT_MOVE_FROZEN_MESSAGES + g = string_cat(g, US" move_frozen_messages"); +#endif #ifdef USE_OPENSSL g = string_cat(g, US" OpenSSL"); #endif +#if defined(CYRUS_PWCHECK_SOCKET) + g = string_cat(g, US" pwcheck"); +#endif +#if defined(RADIUS_CONFIG_FILE) + g = string_cat(g, US" radius"); +#endif #ifndef DISABLE_TLS_RESUME g = string_cat(g, US" TLS_resume"); #endif #ifdef SUPPORT_TRANSLATE_IP_ADDRESS g = string_cat(g, US" translate_ip_address"); #endif -#ifdef SUPPORT_MOVE_FROZEN_MESSAGES - g = string_cat(g, US" move_frozen_messages"); +#ifdef USE_TCP_WRAPPERS + g = string_cat(g, US" TCPwrappers"); #endif -#ifdef WITH_CONTENT_SCAN - g = string_cat(g, US" Content_Scanning"); +#ifdef HAVE_SETCLASSRESOURCES + g = string_cat(g, US" use_setclassresources"); #endif #ifdef SUPPORT_DANE g = string_cat(g, US" DANE"); diff --git a/src/src/macro_predef.c b/src/src/macro_predef.c index 8fade68ca..1cf1a4742 100644 --- a/src/src/macro_predef.c +++ b/src/src/macro_predef.c @@ -119,6 +119,12 @@ due to conflicts with other common macros. */ #ifdef SUPPORT_PAM builtin_macro_create(US"_HAVE_PAM"); #endif +#ifdef RADIUS_CONFIG_FILE + builtin_macro_create(US"_HAVE_RADIUS"); +#endif +#ifdef CYRUS_PWCHECK_SOCKET + builtin_macro_create(US"_HAVE_PWCHECK"); +#endif #ifdef EXIM_PERL builtin_macro_create(US"_HAVE_PERL"); #endif commit cf1e30bc975d646511945e3dd3538fb97637559d Author: Andreas Metzler Date: Sun Sep 10 16:50:36 2023 +0100 typo diff --git a/src/src/exim_id_update.src b/src/src/exim_id_update.src index 1597accc9..8d4920e9c 100644 --- a/src/src/exim_id_update.src +++ b/src/src/exim_id_update.src @@ -44,7 +44,7 @@ $mode_upgrade = $opt{u}; sub help(){ print <<'EOF' Utility for one-time down/upgrade of Exim message-id formats -in spool files. Only the filenames is first-line ID tag values +in spool files. Only the filenames and first-line ID tag values are affected; not message content such as Message-ID fields. Only -H, -D and -J files are handled. commit b015574531cf18b2126edb9da5a99dad659207dd Author: Jeremy Harris Date: Mon Sep 11 15:50:35 2023 +0100 Fix ${tr...} and empty-strings. Bug 3023 diff --git a/src/src/expand.c b/src/src/expand.c index b4a76b3e7..aa8bfe643 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -5807,16 +5807,15 @@ while (*s) case 3: goto EXPAND_FAILED; } - yield = string_cat(yield, sub[0]); - o2m = Ustrlen(sub[2]) - 1; - - if (o2m >= 0) for (; oldptr < yield->ptr; oldptr++) + if ( (yield = string_cat(yield, sub[0])) + && (o2m = Ustrlen(sub[2]) - 1) >= 0) + for (; oldptr < yield->ptr; oldptr++) { uschar * m = Ustrrchr(sub[1], yield->s[oldptr]); if (m) { int o = m - sub[1]; - yield->s[oldptr] = sub[2][(o < o2m)? o : o2m]; + yield->s[oldptr] = sub[2][o < o2m ? o : o2m]; } } commit e2fe20104068e079266859fbe7a95fdab5d3fee2 Author: Jeremy Harris Date: Sun Sep 24 17:02:52 2023 +0100 Docs: remove claim that -Mg causes specific wording in bounce. Bug 3026 While investigating, ensure EXPERIMENTAL_DSN_INFO matches diff --git a/src/src/deliver.c b/src/src/deliver.c index c9a1d074b..fa624f9de 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -5743,7 +5743,7 @@ wording. */ if (addr->return_file >= 0) { - paddr = &(addr->next); + paddr = &addr->next; filecount++; } @@ -5850,6 +5850,9 @@ wording. */ for (address_item * addr = handled_addr; addr; addr = addr->next) { host_item * hu; +#ifdef EXPERIMENTAL_DSN_INFO + const uschar * s; +#endif print_dsn_addr_action(fp, addr, US"failed", US"5.0.0"); @@ -5857,8 +5860,6 @@ wording. */ { fprintf(fp, "Remote-MTA: dns; %s\n", hu->name); #ifdef EXPERIMENTAL_DSN_INFO - { - const uschar * s; if (hu->address) { uschar * p = hu->port == 25 @@ -5869,12 +5870,15 @@ wording. */ dsn_put_wrapped(fp, US"X-Remote-MTA-smtp-greeting: X-str; ", s); if ((s = addr->helo_response) && *s) dsn_put_wrapped(fp, US"X-Remote-MTA-helo-response: X-str; ", s); - if ((s = addr->message) && *s) + if (testflag(addr, af_pass_message) && (s = addr->message) && *s) dsn_put_wrapped(fp, US"X-Exim-Diagnostic: X-str; ", s); - } #endif print_dsn_diagnostic_code(addr, fp); } +#ifdef EXPERIMENTAL_DSN_INFO + else if (testflag(addr, af_pass_message) && (s = addr->message) && *s) + dsn_put_wrapped(fp, US"X-Exim-Diagnostic: X-str; ", s); +#endif fputc('\n', fp); } @@ -7009,8 +7013,9 @@ if (process_recipients != RECIP_IGNORE) for (i = 0; i < recipients_count; i++) if (!tree_search(tree_nonrecipients, recipients_list[i].address)) { - recipient_item *r = recipients_list + i; - address_item *new = deliver_make_addr(r->address, FALSE); + recipient_item * r = recipients_list + i; + address_item * new = deliver_make_addr(r->address, FALSE); + new->prop.errors_address = r->errors_to; #ifdef SUPPORT_I18N if ((new->prop.utf8_msg = message_smtputf8)) @@ -7070,6 +7075,8 @@ if (process_recipients != RECIP_IGNORE) case RECIP_FAIL: new->message = US"delivery cancelled by administrator"; + /* not setting af_pass_message here means that will not + appear in the bounce message */ /* Fall through */ /* Common code for the failure cases above. If this is not a bounce @@ -7078,7 +7085,7 @@ if (process_recipients != RECIP_IGNORE) The incident has already been logged. */ RECIP_QUEUE_FAILED: - if (sender_address[0]) + if (*sender_address) { new->next = addr_failed; addr_failed = new; commit 953303cf3170248dae7f284b0a55cf105a66371c Author: Dean Brooks Date: Sun Sep 24 19:24:38 2023 +0100 Docs: inbound_srs behavior for empty secret. Bug 3025 Additional docs commentary and code-tidying by committer diff --git a/src/src/expand.c b/src/src/expand.c index aa8bfe643..bcfa60fb6 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -3583,53 +3583,50 @@ switch(cond_type = identify_operator(&s, &opname)) /* If a zero-length secret was given, we're done. Otherwise carry on and validate the given SRS local_part againt our secret. */ - if (!*sub[1]) + if (*sub[1]) { - boolvalue = TRUE; - goto srs_result; - } + /* check the timestamp */ + { + struct timeval now; + uschar * ss = sub[0] + ovec[4]; /* substring 2, the timestamp */ + long d; + int n; - /* check the timestamp */ - { - struct timeval now; - uschar * ss = sub[0] + ovec[4]; /* substring 2, the timestamp */ - long d; - int n; + gettimeofday(&now, NULL); + now.tv_sec /= 86400; /* days since epoch */ - gettimeofday(&now, NULL); - now.tv_sec /= 86400; /* days since epoch */ + /* Decode substring 2 from base32 to a number */ - /* Decode substring 2 from base32 to a number */ + for (d = 0, n = ovec[5]-ovec[4]; n; n--) + { + uschar * t = Ustrchr(base32_chars, *ss++); + d = d * 32 + (t - base32_chars); + } - for (d = 0, n = ovec[5]-ovec[4]; n; n--) - { - uschar * t = Ustrchr(base32_chars, *ss++); - d = d * 32 + (t - base32_chars); + if (((now.tv_sec - d) & 0x3ff) > 10) /* days since SRS generated */ + { + DEBUG(D_expand) debug_printf("SRS too old\n"); + goto srs_result; + } } - if (((now.tv_sec - d) & 0x3ff) > 10) /* days since SRS generated */ + /* check length of substring 1, the offered checksum */ + + if (ovec[3]-ovec[2] != 4) { - DEBUG(D_expand) debug_printf("SRS too old\n"); + DEBUG(D_expand) debug_printf("SRS checksum wrong size\n"); goto srs_result; } - } - - /* check length of substring 1, the offered checksum */ - - if (ovec[3]-ovec[2] != 4) - { - DEBUG(D_expand) debug_printf("SRS checksum wrong size\n"); - goto srs_result; - } - /* Hash the address with our secret, and compare that computed checksum - with the one extracted from the arg */ + /* Hash the address with our secret, and compare that computed checksum + with the one extracted from the arg */ - hmac_md5(sub[1], srs_recipient, cksum, sizeof(cksum)); - if (Ustrncmp(cksum, sub[0] + ovec[2], 4) != 0) - { - DEBUG(D_expand) debug_printf("SRS checksum mismatch\n"); - goto srs_result; + hmac_md5(sub[1], srs_recipient, cksum, sizeof(cksum)); + if (Ustrncmp(cksum, sub[0] + ovec[2], 4) != 0) + { + DEBUG(D_expand) debug_printf("SRS checksum mismatch\n"); + goto srs_result; + } } boolvalue = TRUE; commit 45f7d54c984e73a056d21ce0ab5fb1d2f0e886c5 Author: Jeremy Harris Date: Sun Sep 24 20:50:26 2023 +0100 more detail in error messages diff --git a/src/src/exim.c b/src/src/exim.c index 8a78689ff..e200fc062 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -4478,7 +4478,8 @@ if (!f.admin_user) || queue_name_dest && prod_requires_admin || debugset && !f.running_in_test_harness ) - exim_fail("exim:%s permission denied\n", debugset ? " debugging" : ""); + exim_fail("exim:%s permission denied; not admin\n", + debugset ? " debugging" : ""); } /* If the real user is not root or the exim uid, the argument for passing @@ -4494,7 +4495,7 @@ if ( real_uid != root_uid && real_uid != exim_uid ) ) && !f.running_in_test_harness ) - exim_fail("exim: Permission denied\n"); + exim_fail("exim: Permission denied; not exim user or root\n"); /* If the caller is not trusted, certain arguments are ignored when running for real, but are permitted when checking things (-be, -bv, -bt, -bh, -bf, -bF). diff --git a/src/src/readconf.c b/src/src/readconf.c index 7d48f085d..d6d6f53a5 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -2915,7 +2915,7 @@ else if (Ustrcmp(type, "macro") == 0) for printing. So we have an admin_users restriction. */ if (!f.admin_user) { - fprintf(stderr, "exim: permission denied\n"); + fprintf(stderr, "exim: permission denied; not admin\n"); return FALSE; } for (macro_item * m = macros; m; m = m->next) commit 62ebdc13d2e889666221bc18d6fed022554daf64 Author: Hendrik Jäger Date: Fri Sep 29 13:47:36 2023 +0100 Docs: tidying diff --git a/src/src/lookups/README b/src/src/lookups/README index 2e87edadd..614b349ec 100644 --- a/src/src/lookups/README +++ b/src/src/lookups/README @@ -75,7 +75,7 @@ The arguments are: uschar **errmsg where to put an error message if there is a problem The yield of xxx_open() is a void * value representing the open file or -database. For real files is is normally the FILE or DBM value. For other +database. For real files it is normally the FILE or DBM value. For other kinds of lookup, if there is no natural value to use, (-1) is recommended. The value should not be NULL (or 0) as that is taken to indicate failure of the xxx_open() function. For single-key lookups, the handle is cached along diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 2e537a160..01622560f 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -1438,7 +1438,7 @@ SNI handling. Separately we might try to replace using OCSP_basic_verify() - which seems to not be a public interface into the OpenSSL library (there's no manual entry) - -(in 3.0.0 + is is public) +(in 3.0.0 + it is public) But what with? We also use OCSP_basic_verify in the client stapling callback. And there we NEED it; we must verify that status... unless the library does it for us anyway? */ diff --git a/src/src/tree.c b/src/src/tree.c index 13fc28cc2..92176668a 100644 --- a/src/src/tree.c +++ b/src/src/tree.c @@ -48,7 +48,7 @@ if (!tree_insertnode(&tree_nonrecipients, node)) store_reset(rpoint); Argument: s string to add - addr the address is is a duplicate of + addr the address it is a duplicate of Returns: nothing */