diff --git a/mail-filter/libspf2/Manifest b/mail-filter/libspf2/Manifest new file mode 100644 index 0000000..4dd71f1 --- /dev/null +++ b/mail-filter/libspf2/Manifest @@ -0,0 +1,9 @@ +AUX libspf2-1.2.11-integer-underflow.patch 1004 BLAKE2B c08506cb484c06b2c047900c39e07184682628b5a1816f2c5fb7fc421059fdf6b84273ec29f57f9ada18f32ea1a970801850c0ae880d7b598552ba2f79121ade SHA512 884f041ea99d37e919128d612d5417ea6ecb668580b385e3e6ad994ac53e2ba79be6ff443e348946d7940e7982c3ca3a97e5a3e6a4e1130aa2c9d3d717cb56b7 +AUX libspf2-1.2.11-memset-include-string-h.patch 317 BLAKE2B 4bafbb6274a433d6fbbba149fc81ddc30734baa6509493820a9ca3a1c50d90c33db9846d8a63536e05e7960f4ff45c605a66b461bd93b6fab36533b9745512fc SHA512 0c1bf85d52e8c5a8ec0b32485e210139c7c09e17dbca2dbc810eedd7fd9b096fa26d66194b02067186d572a54c049a276dc8f7f1358ca6a9d6883a760b4735a9 +AUX libspf2-1.2.11-musl.patch 816 BLAKE2B 4dde68a54b54f55ad184cdb3d735020f446fcaca5f6751d892eb299962df92fdef144a7ef17ef9775655c088ec73f81eea69bec4aabb65a97ddd809673114a99 SHA512 30d2e27e185d2656d0a2d2fe14f4e735bc533db66f52c1554df19cc635bd1024c7877bb48e7ec20432f23c4e9cc66bdbd7c2b0c8816cf692e627112792ed1fd4 +AUX libspf2-1.2.11-rfc7208-hardening.patch 40985 BLAKE2B f7a688c2e6bb3352f306f3f803e9a1fa7f75989e34c801aef8cf1e3d9e722bfbace8f55da60f4f885686827749f13f704057cb2a1f5b4238602ba3f37baa760d SHA512 96c54bba4929f878d716584fd1a65d700859205776e8a0626085758fbdda84d9fd778bcc3bce92636b5fbc4c8ddac1d7323b72096d3e949fea42f76bd0a76164 +AUX libspf2-1.2.11-rfc7208-test.c 9354 BLAKE2B 8cad36a532928e535fea4b7519c4f385fdb8b9b849dc9ab8e42934ce20e5f1be5ed63b9a0cb117e5e23908cebb23f9b5377cd5875952b11e3a2fe488ceedd710 SHA512 0a81c52c37aa6b34b6702887103f9d13e0fc83ee00290d20f6c61a115a3910a340bbdf532f870bc4902a41850625ab3149d646e9a15cec51127ac215211dcc17 +AUX libspf2-1.2.11-undefined-dn_.patch 5455 BLAKE2B f970809534d3e15961f7fe76775becb8aa80168d4ced28d401417e3a99f2220386e153ca21c49936703a79d9e8e354c30ac0a6ebb808706b374703fd1a29da87 SHA512 77bae829029c713fd1384f69cdc340f815ed0ae9c2d3d2a9d5b88052fda3f4b0a9491d096e70ae116be0256c77d91c517ee19acedf4af76b24d26409706ec616 +DIST libspf2-1.2.11.tar.gz 565497 BLAKE2B 4000e15204c962ef438cc8bcf6411f4f965e1451e356923e2764d5a1209cbc9153a38e1d83d25c8b4b26646bfb306ec49b71d273fdbabe200b78ac2694c05a4f SHA512 ad7bea800ad6ec8d00a7f8f381b59d9ba074fb6c5932d8babcb05ab5ca59defb78445dd7bd89767e5c272b6c6638c8000c8c525c560d1803c35712f0ff86fa9b +EBUILD libspf2-1.2.11-r2.ebuild 2022 BLAKE2B 1ecfe0dbec7a34fe9b79bcc4bf1ebcf0526dc4dc2c9b5145cfdbe655dac7861aecac239a259a439dfe69227c93252022b0856afcbd9ad4ca6b378db6ca6a740b SHA512 0cf3996c6505c3781139c5293050eb4488ab260d3959e39579c6752c8a8bda41e3f0fb7a315323fe91975a703a177ac9ca16a8b3c30380d99638becaf5c2c11b +MISC metadata.xml 661 BLAKE2B abbc96b07e546e6ca06763b871e1b21b1e83220c37f38fff3045485a26394784ad5391d0c5682d0716fb8d68693465b65d3642afdfec9ff33ce2e08bf8a8e776 SHA512 bb8fbd3142a69092b10c0ce665d74db7bb5a11f287a027ab499e89433fa7e4bc35a397d738d7ca7105f723bb941b94e01b2ad6bff515d94d42445fd6d5bc6e59 diff --git a/mail-filter/libspf2/files/libspf2-1.2.11-integer-underflow.patch b/mail-filter/libspf2/files/libspf2-1.2.11-integer-underflow.patch new file mode 100644 index 0000000..fabfd6c --- /dev/null +++ b/mail-filter/libspf2/files/libspf2-1.2.11-integer-underflow.patch @@ -0,0 +1,26 @@ +From d14abff4b544cfc53a8b5ef54cbc2353866b5081 Mon Sep 17 00:00:00 2001 +From: Simon Arlott +Date: Sat, 30 Sep 2023 12:18:51 +0100 +Subject: [PATCH] Fix integer underflow + +--- + src/libspf2/spf_compile.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/libspf2/spf_compile.c b/src/libspf2/spf_compile.c +index b08ffe2..d401028 100644 +--- a/src/libspf2/spf_compile.c ++++ b/src/libspf2/spf_compile.c +@@ -455,7 +455,11 @@ SPF_c_parse_var(SPF_response_t *spf_response, SPF_data_var_t *data, + /* Magic numbers for x/Nc in gdb. */ \ + data->ds.__unused0 = 0xba; data->ds.__unused1 = 0xbe; \ + dst = SPF_data_str( data ); \ +- ds_avail = _avail - sizeof(SPF_data_t); \ ++ if ((_avail) < sizeof(SPF_data_t)) \ ++ return SPF_response_add_error_ptr(spf_response, \ ++ SPF_E_BIG_STRING, NULL, src, \ ++ "Out of memory for string literal");\ ++ ds_avail = (_avail) - sizeof(SPF_data_t); \ + ds_len = 0; \ + } while(0) + diff --git a/mail-filter/libspf2/files/libspf2-1.2.11-memset-include-string-h.patch b/mail-filter/libspf2/files/libspf2-1.2.11-memset-include-string-h.patch new file mode 100644 index 0000000..5b5a772 --- /dev/null +++ b/mail-filter/libspf2/files/libspf2-1.2.11-memset-include-string-h.patch @@ -0,0 +1,12 @@ +grobian@gentoo.org: include string.h so memset is declared + +--- a/src/libspf2/spf_utils.c ++++ b/src/libspf2/spf_utils.c +@@ -19,6 +19,7 @@ + #ifdef STDC_HEADERS + # include /* malloc / free */ + # include /* isupper / tolower */ ++# include /* memset */ + #endif + + #ifdef HAVE_MEMORY_H diff --git a/mail-filter/libspf2/files/libspf2-1.2.11-musl.patch b/mail-filter/libspf2/files/libspf2-1.2.11-musl.patch new file mode 100644 index 0000000..9867dc0 --- /dev/null +++ b/mail-filter/libspf2/files/libspf2-1.2.11-musl.patch @@ -0,0 +1,30 @@ +assorted fixes for musl + +--- a/src/include/spf_dns.h ++++ a/src/include/spf_dns.h +@@ -99,11 +99,13 @@ + */ + + #if !defined(HAVE_NETDB_H) && !defined(_WIN32) +-#define NETDB_SUCCESS 0 + #define HOST_NOT_FOUND 1 /**< NXDOMAIN (authoritative answer)*/ + #define TRY_AGAIN 2 /**< SERVFAIL (no authoritative answer)*/ + #define NO_RECOVERY 3 /**< invalid/unimplmeneted query */ + #define NO_DATA 4 /**< host found, but no RR of req type*/ ++#endif ++#ifndef NETDB_SUCCESS ++#define NETDB_SUCCESS 0 + #endif + typedef int SPF_dns_stat_t; + +--- a/src/libspf2/spf_dns_resolv.c ++++ b/src/libspf2/spf_dns_resolv.c +@@ -606,7 +606,7 @@ SPF_dns_resolv_free(SPF_dns_server_t *spf_dns_server) + { + SPF_ASSERT_NOTNULL(spf_dns_server); + +-#if ! HAVE_DECL_RES_NINIT ++#if ! HAVE_DECL_RES_NINIT && HAVE_RES_CLOSE + res_close(); + #endif + diff --git a/mail-filter/libspf2/files/libspf2-1.2.11-rfc7208-hardening.patch b/mail-filter/libspf2/files/libspf2-1.2.11-rfc7208-hardening.patch new file mode 100644 index 0000000..3e5725d --- /dev/null +++ b/mail-filter/libspf2/files/libspf2-1.2.11-rfc7208-hardening.patch @@ -0,0 +1,1289 @@ +diff --git a/configure.ac b/configure.ac +index a36ca2f..e41817b 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -150,6 +150,10 @@ AC_CHECK_LIB(nsl, inet_pton) + AC_CHECK_LIB(socket, socket) + AC_CHECK_LIB(intl, gettext) + AC_CHECK_LIB(pthread, pthread_create) ++AC_CHECK_HEADERS([idn2.h], [], ++ [AC_MSG_ERROR([libidn2 headers are required for EAI domain support])]) ++AC_CHECK_LIB(idn2, idn2_to_ascii_8z, [], ++ [AC_MSG_ERROR([libidn2 is required for EAI domain support])]) + + + # Checks for typedefs, structures, and compiler characteristics. +@@ -232,6 +236,23 @@ AC_CHECK_DECLS([res_ndestroy], [], [], [[ + # include + #endif + ]]) ++AC_CHECK_DECLS([res_close], [], [], [[ ++ #ifdef HAVE_SYS_TYPES_H ++ # include ++ #endif ++ #ifdef HAVE_SYS_SOCKET_H ++ # include ++ #endif ++ #ifdef HAVE_NETINET_IN_H ++ # include ++ #endif ++ #ifdef HAVE_ARPA_NAMESER_H ++ # include ++ #endif ++ #ifdef HAVE_RESOLV_H ++ # include ++ #endif ++ ]]) + + AC_CHECK_DECLS([ns_t_spf], [], [], [[ + #ifdef HAVE_SYS_TYPES_H +diff --git a/src/include/spf.h b/src/include/spf.h +index 866f3f2..d451850 100644 +--- a/src/include/spf.h ++++ b/src/include/spf.h +@@ -21,6 +21,12 @@ + #ifndef INC_SPF + #define INC_SPF + ++/* This libspf2 build includes the RFC 7208 processing limits and TXT-only ++ * record selection fixes. Consumers can use this ++ * marker to avoid silently building against the unmaintained RFC 4408 ++ * behaviour shipped by older libspf2 releases. */ ++#define LIBSPF2_RFC7208 1 ++ + + #include "spf_lib_version.h" + +diff --git a/src/include/spf_internal.h b/src/include/spf_internal.h +index c9d974e..eb557ec 100644 +--- a/src/include/spf_internal.h ++++ b/src/include/spf_internal.h +@@ -139,6 +139,24 @@ char *SPF_sanitize( SPF_server_t *spf_server, char *str ); + + void SPF_print_sizeof(void); + ++/* Kept private so RFC 7208 accounting does not alter any public structure. */ ++typedef struct SPF_eval_ctx_struct { ++ unsigned int dns_mech; ++ unsigned int dns_void; ++} SPF_eval_ctx_t; ++ ++SPF_errcode_t SPF_i_note_void_lookup(SPF_eval_ctx_t *spf_eval, ++ SPF_dns_rr_t *spfrr); ++SPF_errcode_t SPF_i_record_interpret(SPF_record_t *spf_record, ++ SPF_request_t *spf_request, ++ SPF_response_t *spf_response, ++ SPF_eval_ctx_t *spf_eval, int depth); ++SPF_errcode_t SPF_i_server_get_record(SPF_server_t *spf_server, ++ SPF_request_t *spf_request, ++ SPF_response_t *spf_response, ++ SPF_record_t **spf_recordp, ++ SPF_eval_ctx_t *spf_eval); ++ + /** + * A special purpose realloc for text buffers. + * +diff --git a/src/include/spf_log.h b/src/include/spf_log.h +index 9b09714..0e551ea 100644 +--- a/src/include/spf_log.h ++++ b/src/include/spf_log.h +@@ -60,10 +60,10 @@ void SPF_debugv( const char *file, int line, const char *format, va_list ap ) __ + + #if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 199901L + +-#define SPF_errorf(format, ... ) SPF_errorx( __FILE__, __LINE__, format, ##__VA_ARGS__ ) +-#define SPF_warningf(format, ... ) SPF_warningx( __FILE__, __LINE__, format, ##__VA_ARGS__ ) +-#define SPF_infof(format, ... ) SPF_infox( __FILE__, __LINE__, format, ##__VA_ARGS__ ) +-#define SPF_debugf(format, ... ) SPF_debugx( __FILE__, __LINE__, format, ##__VA_ARGS__ ) ++#define SPF_errorf(...) SPF_errorx( __FILE__, __LINE__, __VA_ARGS__ ) ++#define SPF_warningf(...) SPF_warningx( __FILE__, __LINE__, __VA_ARGS__ ) ++#define SPF_infof(...) SPF_infox( __FILE__, __LINE__, __VA_ARGS__ ) ++#define SPF_debugf(...) SPF_debugx( __FILE__, __LINE__, __VA_ARGS__ ) + + #elif defined( __GNUC__ ) + +diff --git a/src/include/spf_server.h b/src/include/spf_server.h +index 8bc4b52..c2aedde 100644 +--- a/src/include/spf_server.h ++++ b/src/include/spf_server.h +@@ -51,6 +51,11 @@ typedef struct SPF_server_struct SPF_server_t; + */ + #define SPF_MAX_DNS_MX 10 + #endif ++#ifndef SPF_MAX_DNS_VOID ++/* RFC 7208 section 4.6.4 recommends permitting at most two DNS queries ++ * which return NXDOMAIN or no answer records. */ ++#define SPF_MAX_DNS_VOID 2 ++#endif + + struct SPF_server_struct { + SPF_dns_server_t*resolver; /**< SPF DNS resolver. */ +diff --git a/src/libreplace/__ns_get16.c b/src/libreplace/__ns_get16.c +index 805345d..21c9883 100644 +--- a/src/libreplace/__ns_get16.c ++++ b/src/libreplace/__ns_get16.c +@@ -12,8 +12,7 @@ + + + u_int +-__ns_get16(src) +- const u_char *src; ++__ns_get16(const u_char *src) + { + u_int dst; + +diff --git a/src/libreplace/__ns_initparse.c b/src/libreplace/__ns_initparse.c +index 10a7c9c..4028d84 100644 +--- a/src/libreplace/__ns_initparse.c ++++ b/src/libreplace/__ns_initparse.c +@@ -15,10 +15,6 @@ + * SOFTWARE. + */ + +-#ifndef lint +-static const char rcsid[] = "$Id: ns_parse.c,v 1.3.2.1 2003/06/27 03:51:42 marka Exp $"; +-#endif +- + /* Import. */ + + /* #include "port_before.h" */ +diff --git a/src/libspf2/Makefile.am b/src/libspf2/Makefile.am +index 24a1d46..0e387f4 100644 +--- a/src/libspf2/Makefile.am ++++ b/src/libspf2/Makefile.am +@@ -2,7 +2,7 @@ + + AUTOMAKE_OPTIONS = foreign + +-AM_CPPFLAGS = -I$(top_srcdir)/src/include -I$(top_srcdir)/src ++AM_CPPFLAGS = -I$(top_builddir)/src/include -I$(top_srcdir)/src/include + + lib_LTLIBRARIES = libspf2.la + +diff --git a/src/libspf2/spf_compile.c b/src/libspf2/spf_compile.c +index d401028..e70e930 100644 +--- a/src/libspf2/spf_compile.c ++++ b/src/libspf2/spf_compile.c +@@ -608,7 +608,7 @@ SPF_c_parse_macro(SPF_server_t *spf_server, + + default: + if (spf_server->debug > 3) +- SPF_debugf("Adding illegal %%-follower '%c' at %d", ++ SPF_debugf("Adding illegal %%-follower '%c' at %zu", + src[idx], idx); + /* SPF spec says to treat it as a literal, not + * SPF_E_INVALID_ESC */ +@@ -1061,6 +1061,7 @@ SPF_record_lint(SPF_server_t *spf_server, + SPF_record_t *spf_record) + { + SPF_data_t *d, *data_end; ++ (void)spf_server; + + char *s; + char *s_end; +@@ -1201,6 +1202,9 @@ SPF_record_compile(SPF_server_t *spf_server, + + const char *p; + int i; ++ const SPF_mechtype_t *redirect_mechtype = NULL; ++ int redirect_prefix = PREFIX_UNKNOWN; ++ const char *redirect_val_start = NULL; + + + /* +@@ -1386,8 +1390,6 @@ SPF_record_compile(SPF_server_t *spf_server, + continue; + } + #endif +- /* FIXME the redirect mechanism needs to be moved to +- * the very end */ + else if ( STREQ_SIZEOF_N(name_start, "redirect", name_len) ) + mechtype = SPF_mechtype_find(MECH_REDIRECT); + else +@@ -1407,16 +1409,29 @@ SPF_record_compile(SPF_server_t *spf_server, + "a recognised mechanism"); + } + +- if (spf_server->debug) +- SPF_debugf("Adding mechanism type %d", +- (int)mechtype->mech_type); +- + val_start = p; +- err = SPF_c_mech_add(spf_server, ++ if (mechtype->mech_type == MECH_REDIRECT) { ++ if (redirect_mechtype != NULL) { ++ SPF_response_add_error_ptr(spf_response, ++ SPF_E_INVALID_OPT, NULL, name_start, ++ "Duplicate redirect modifier"); ++ } ++ else { ++ redirect_mechtype = mechtype; ++ redirect_prefix = prefix; ++ redirect_val_start = val_start; ++ } ++ } ++ else { ++ if (spf_server->debug) ++ SPF_debugf("Adding mechanism type %d", ++ (int)mechtype->mech_type); ++ err = SPF_c_mech_add(spf_server, + spf_record, spf_response, + mechtype, prefix, &val_start); +- if (err == SPF_E_NO_MEMORY) +- return err; ++ if (err == SPF_E_NO_MEMORY) ++ return err; ++ } + /* XXX Else do nothing. Continue for the next error. */ + /* We shouldn't have to worry about the child function + * updating the pointer. So we just use our 'well known' +@@ -1471,6 +1486,17 @@ SPF_record_compile(SPF_server_t *spf_server, + break; + } + } ++ ++ /* redirect is a modifier and is evaluated only after all mechanisms. */ ++ if (redirect_mechtype != NULL) { ++ if (spf_server->debug) ++ SPF_debugf("Adding mechanism type %d", ++ (int)redirect_mechtype->mech_type); ++ err = SPF_c_mech_add(spf_server, spf_record, spf_response, ++ redirect_mechtype, redirect_prefix, &redirect_val_start); ++ if (err == SPF_E_NO_MEMORY) ++ return err; ++ } + + + /* +diff --git a/src/libspf2/spf_dns.c b/src/libspf2/spf_dns.c +index 7d27373..76d0e34 100644 +--- a/src/libspf2/spf_dns.c ++++ b/src/libspf2/spf_dns.c +@@ -48,6 +48,7 @@ static void + SPF_dns_debug_pre(SPF_dns_server_t *spf_dns_server, const char *domain, + ns_type rr_type, int should_cache) + { ++ (void)should_cache; + if (spf_dns_server->debug) { + SPF_debugf("DNS[%s] lookup: %s %s (%d)", + spf_dns_server->name, domain, +diff --git a/src/libspf2/spf_dns_cache.c b/src/libspf2/spf_dns_cache.c +index 16c9819..69ae08f 100644 +--- a/src/libspf2/spf_dns_cache.c ++++ b/src/libspf2/spf_dns_cache.c +@@ -112,8 +112,6 @@ typedef struct + + static inline SPF_dns_cache_config_t *SPF_voidp2spfhook( void *hook ) + { return (SPF_dns_cache_config_t *)hook; } +-static inline void *SPF_spfhook2voidp( SPF_dns_cache_config_t *spfhook ) +- { return (void *)spfhook; } + + + /* +@@ -327,7 +325,7 @@ SPF_dns_cache_rr_fixup(SPF_dns_cache_config_t *spfhook, + char *new_domain; + size_t new_len = strlen(domain) + 1; + +- if (cached_rr->domain_buf_len < new_len) { ++ if (cached_rr->domain == NULL || cached_rr->domain_buf_len < new_len) { + new_domain = realloc(cached_rr->domain, new_len); + if (new_domain == NULL) + return SPF_E_NO_MEMORY; +@@ -342,7 +340,7 @@ SPF_dns_cache_rr_fixup(SPF_dns_cache_config_t *spfhook, + cached_rr->ttl = spfhook->min_ttl; + + if ( cached_rr->ttl < spfhook->txt_ttl +- && cached_rr->rr_type == ns_t_txt || cached_rr->rr_type == ns_t_spf ) ++ && (cached_rr->rr_type == ns_t_txt || cached_rr->rr_type == ns_t_spf) ) + cached_rr->ttl = spfhook->txt_ttl; + + if ( cached_rr->ttl < spfhook->err_ttl +diff --git a/src/libspf2/spf_dns_resolv.c b/src/libspf2/spf_dns_resolv.c +index f31d94d..c433cc9 100644 +--- a/src/libspf2/spf_dns_resolv.c ++++ b/src/libspf2/spf_dns_resolv.c +@@ -113,6 +113,7 @@ SPF_dns_resolv_debug(SPF_dns_server_t *spf_dns_server, ns_rr rr, + const u_char *responsebuf, size_t responselen, + const u_char *rdata, size_t rdlen) + { ++ (void)spf_dns_server; + char ip4_buf[ INET_ADDRSTRLEN ]; + char ip6_buf[ INET6_ADDRSTRLEN ]; + char name_buf[ NS_MAXDNAME ]; +@@ -318,18 +319,15 @@ SPF_dns_resolv_lookup(SPF_dns_server_t *spf_dns_server, + return SPF_dns_rr_new_init(spf_dns_server, + domain, rr_type, 0, SPF_h_errno); + } +- else if (dns_len > responselen) { ++ else if ((size_t)dns_len > responselen) { + void *tmp; + /* We managed a lookup but our buffer was too small. */ +- responselen = dns_len + (dns_len >> 1); +-#if 0 +- /* Sanity-trap - we should never hit this. */ +- if (responselen > 1048576) { /* One megabyte. */ ++ if ((unsigned int)dns_len > 65535U) { + free(responsebuf); + return SPF_dns_rr_new_init(spf_dns_server, +- domain, rr_type, 0, SPF_h_errno); ++ domain, rr_type, 0, NO_RECOVERY); + } +-#endif ++ responselen = (size_t)dns_len; + tmp = realloc(responsebuf, responselen); + if (!tmp) { + free(responsebuf); +@@ -383,13 +381,13 @@ SPF_dns_resolv_lookup(SPF_dns_server_t *spf_dns_server, + + + /* FIXME the error handling from here on is suspect at best */ ++ spfrr->num_rr = 0; + for (ns_sect = 0; ns_sect < num_ns_sect; ns_sect++) { + /* We pass this point if: + * - We are the 'answer' section. +- * - We are debugging. + * Otherwise, we continue to the next section. + */ +- if (ns_sects[ns_sect].number != ns_s_an && spf_dns_server->debug <= 1) ++ if (ns_sects[ns_sect].number != ns_s_an) + continue; + + nrec = ns_msg_count(ns_handle, ns_sects[ns_sect].number); +@@ -397,7 +395,6 @@ SPF_dns_resolv_lookup(SPF_dns_server_t *spf_dns_server, + if (spf_dns_server->debug > 1) + SPF_debugf("%s: %d", ns_sects[ns_sect].name, nrec); + +- spfrr->num_rr = 0; + cnt = 0; + for (i = 0; i < nrec; i++) { + err = ns_parserr(&ns_handle, ns_sects[ns_sect].number, i, &rr); +@@ -592,7 +589,7 @@ SPF_dns_resolv_lookup(SPF_dns_server_t *spf_dns_server, + } + } + +- spfrr->num_rr = cnt; ++ spfrr->num_rr += cnt; + } + + if (spfrr->num_rr == 0) +@@ -608,7 +605,7 @@ SPF_dns_resolv_free(SPF_dns_server_t *spf_dns_server) + { + SPF_ASSERT_NOTNULL(spf_dns_server); + +-#if ! HAVE_DECL_RES_NINIT && HAVE_RES_CLOSE ++#if HAVE_DECL_RES_CLOSE + res_close(); + #endif + +diff --git a/src/libspf2/spf_dns_zone.c b/src/libspf2/spf_dns_zone.c +index 66334ea..188a2d7 100644 +--- a/src/libspf2/spf_dns_zone.c ++++ b/src/libspf2/spf_dns_zone.c +@@ -82,8 +82,6 @@ typedef struct + + static inline SPF_dns_zone_config_t *SPF_voidp2spfhook( void *hook ) + { return (SPF_dns_zone_config_t *)hook; } +-static inline void *SPF_spfhook2voidp( SPF_dns_zone_config_t *spfhook ) +- { return (void *)spfhook; } + + + +diff --git a/src/libspf2/spf_expand.c b/src/libspf2/spf_expand.c +index f93005d..cb585f4 100644 +--- a/src/libspf2/spf_expand.c ++++ b/src/libspf2/spf_expand.c +@@ -85,9 +85,9 @@ SPF_record_expand_data(SPF_server_t *spf_server, + char **bufp, size_t *buflenp) + { + SPF_data_t *d, *data_end; ++ (void)spf_response; + + size_t len, label_len; +- const char *p_err; // XXX Check this value, when returned. + char *p, *p_end; + const char *p_read; + const char *p_read_end; +@@ -187,8 +187,9 @@ top: + continue; + } + if (spf_request->client_ver == AF_INET) { +- p_err = inet_ntop(AF_INET, &spf_request->ipv4, +- ip4_buf, sizeof(ip4_buf)); ++ if (inet_ntop(AF_INET, &spf_request->ipv4, ++ ip4_buf, sizeof(ip4_buf)) == NULL) ++ return SPF_E_INTERNAL_ERROR; + var = ip4_buf; + } + else if (spf_request->client_ver == AF_INET6) { +@@ -217,13 +218,15 @@ top: + continue; + } + if (spf_request->client_ver == AF_INET) { +- p_err = inet_ntop(AF_INET, &spf_request->ipv4, +- ip4_buf, sizeof(ip4_buf)); ++ if (inet_ntop(AF_INET, &spf_request->ipv4, ++ ip4_buf, sizeof(ip4_buf)) == NULL) ++ return SPF_E_INTERNAL_ERROR; + var = ip4_buf; + } + else if (spf_request->client_ver == AF_INET6) { +- p_err = inet_ntop(AF_INET6, &spf_request->ipv6, +- ip6_buf, sizeof(ip6_buf)); ++ if (inet_ntop(AF_INET6, &spf_request->ipv6, ++ ip6_buf, sizeof(ip6_buf)) == NULL) ++ return SPF_E_INTERNAL_ERROR; + var = ip6_buf; + } + break; +@@ -354,7 +357,9 @@ top: + break; + p_write--; + } +- p_write++; /* Move to just after the '.' */ ++ /* Move past the delimiter only when one was actually found. */ ++ if (num_found != 0) ++ p_write++; + /* This moves the '\0' as well. */ + len = p_read_end - p_write; + memmove(munged_var, p_write, len + 1); +diff --git a/src/libspf2/spf_get_exp.c b/src/libspf2/spf_get_exp.c +index f4b5055..4a663e4 100644 +--- a/src/libspf2/spf_get_exp.c ++++ b/src/libspf2/spf_get_exp.c +@@ -62,7 +62,7 @@ SPF_server_get_default_explanation(SPF_server_t *spf_server, + } + else { + size_t len = sizeof(SPF_LAME_EXP) + 1; +- if (*buflenp < len) { ++ if (*bufp == NULL || *buflenp < len) { + char *tmp = realloc(*bufp, len); + if (tmp == NULL) + return SPF_E_NO_MEMORY; +diff --git a/src/libspf2/spf_interpret.c b/src/libspf2/spf_interpret.c +index b89ce74..fad41bb 100644 +--- a/src/libspf2/spf_interpret.c ++++ b/src/libspf2/spf_interpret.c +@@ -576,15 +576,31 @@ SPF_i_match_domain(SPF_server_t *spf_server, + return (strcasecmp(hp, domain) == 0); + } + ++SPF_errcode_t ++SPF_i_note_void_lookup(SPF_eval_ctx_t *spf_eval, SPF_dns_rr_t *spfrr) ++{ ++ SPF_ASSERT_NOTNULL(spf_eval); ++ SPF_ASSERT_NOTNULL(spfrr); ++ ++ if (spfrr->herrno == HOST_NOT_FOUND || spfrr->herrno == NO_DATA || ++ (spfrr->herrno == NETDB_SUCCESS && spfrr->num_rr == 0)) { ++ spf_eval->dns_void++; ++ if (spf_eval->dns_void > SPF_MAX_DNS_VOID) ++ return SPF_E_BIG_DNS; ++ } ++ ++ return SPF_E_SUCCESS; ++} ++ + + /* + * Set cur_dom (to either sender or or helo_dom) before calling this. + */ + + SPF_errcode_t +-SPF_record_interpret(SPF_record_t *spf_record, ++SPF_i_record_interpret(SPF_record_t *spf_record, + SPF_request_t *spf_request, SPF_response_t *spf_response, +- int depth) ++ SPF_eval_ctx_t *spf_eval, int depth) + { + SPF_server_t *spf_server; + +@@ -637,6 +653,7 @@ SPF_record_interpret(SPF_record_t *spf_record, + SPF_ASSERT_NOTNULL(spf_record); + SPF_ASSERT_NOTNULL(spf_request); + SPF_ASSERT_NOTNULL(spf_response); ++ SPF_ASSERT_NOTNULL(spf_eval); + spf_server = spf_record->spf_server; + SPF_ASSERT_NOTNULL(spf_server); + +@@ -718,7 +735,27 @@ SPF_record_interpret(SPF_record_t *spf_record, + * evaluate the mechanisms + */ + +-#define SPF_ADD_DNS_MECH() do { spf_response->num_dns_mech++; } while(0) ++#define SPF_ADD_DNS_MECH() \ ++ do { \ ++ spf_eval->dns_mech++; \ ++ spf_response->num_dns_mech = spf_eval->dns_mech; \ ++ if (spf_eval->dns_mech > \ ++ (unsigned int)spf_server->max_dns_mech) { \ ++ SPF_FREE_LOOKUP_DATA(); \ ++ return DONE(SPF_RESULT_PERMERROR, SPF_REASON_NONE, \ ++ SPF_E_BIG_DNS); \ ++ } \ ++ } while(0) ++ ++#define SPF_CHECK_VOID_LOOKUP(rr) \ ++ do { \ ++ if (SPF_i_note_void_lookup(spf_eval, (rr)) == SPF_E_BIG_DNS) { \ ++ SPF_dns_rr_free((rr)); \ ++ SPF_FREE_LOOKUP_DATA(); \ ++ return DONE(SPF_RESULT_PERMERROR, SPF_REASON_NONE, \ ++ SPF_E_BIG_DNS); \ ++ } \ ++ } while(0) + + #define SPF_MAYBE_SKIP_CIDR() \ + do { \ +@@ -755,13 +792,6 @@ SPF_record_interpret(SPF_record_t *spf_record, + mech = spf_record->mech_first; + for (m = 0; m < spf_record->num_mech; m++) { + +- /* This is as good a place as any. */ +- /* XXX Rip this out and put it into a macro which can go into inner loops. */ +- if (spf_response->num_dns_mech > spf_server->max_dns_mech) { +- SPF_FREE_LOOKUP_DATA(); +- return DONE(SPF_RESULT_PERMERROR, SPF_REASON_NONE, SPF_E_BIG_DNS); +- } +- + data = SPF_mech_data(mech); + data_end = SPF_mech_end_data(mech); + +@@ -777,6 +807,7 @@ SPF_record_interpret(SPF_record_t *spf_record, + fetch_ns_type = ns_t_aaaa; + + rr_a = SPF_dns_lookup(resolver, lookup, fetch_ns_type, TRUE); ++ SPF_CHECK_VOID_LOOKUP(rr_a); + + if (spf_server->debug) + SPF_debugf("found %d A records for %s (herrno: %d)", +@@ -818,6 +849,7 @@ SPF_record_interpret(SPF_record_t *spf_record, + SPF_GET_LOOKUP_DATA(); + + rr_mx = SPF_dns_lookup(resolver, lookup, ns_t_mx, TRUE); ++ SPF_CHECK_VOID_LOOKUP(rr_mx); + + if (spf_server->debug) + SPF_debugf("found %d MX records for %s (herrno: %d)", +@@ -849,6 +881,13 @@ SPF_record_interpret(SPF_record_t *spf_record, + + rr_a = SPF_dns_lookup(resolver, rr_mx->rr[j]->mx, + fetch_ns_type, TRUE ); ++ if (SPF_i_note_void_lookup(spf_eval, rr_a) == SPF_E_BIG_DNS) { ++ SPF_dns_rr_free(rr_mx); ++ SPF_dns_rr_free(rr_a); ++ SPF_FREE_LOOKUP_DATA(); ++ return DONE(SPF_RESULT_PERMERROR, SPF_REASON_NONE, ++ SPF_E_BIG_DNS); ++ } + + if (spf_server->debug) + SPF_debugf("%d: found %d A records for %s (herrno: %d)", +@@ -860,6 +899,14 @@ SPF_record_interpret(SPF_record_t *spf_record, + return DONE_TEMPERR(SPF_E_DNS_ERROR); + } + ++ if (rr_a->num_rr > spf_server->max_dns_mx) { ++ SPF_dns_rr_free(rr_mx); ++ SPF_dns_rr_free(rr_a); ++ SPF_FREE_LOOKUP_DATA(); ++ return DONE(SPF_RESULT_PERMERROR, SPF_REASON_NONE, ++ SPF_E_BIG_DNS); ++ } ++ + for (i = 0; i < rr_a->num_rr; i++) { + /* XXX Should this be hoisted? */ + if (rr_a->rr_type != fetch_ns_type) +@@ -903,6 +950,7 @@ SPF_record_interpret(SPF_record_t *spf_record, + if (spf_request->client_ver == AF_INET) { + rr_ptr = SPF_dns_rlookup(resolver, + spf_request->ipv4, ns_t_ptr, TRUE); ++ SPF_CHECK_VOID_LOOKUP(rr_ptr); + + if (spf_server->debug) { + INET_NTOP(AF_INET, &spf_request->ipv4.s_addr, +@@ -920,9 +968,7 @@ SPF_record_interpret(SPF_record_t *spf_record, + + /* The maximum number of PTR records we will inspect. */ + max_ptr = rr_ptr->num_rr; +- max_exceeded = 0; + if (max_ptr > spf_server->max_dns_ptr) { +- max_exceeded = 1; + max_ptr = SPF_server_get_max_dns_ptr(spf_server); + } + +@@ -931,6 +977,13 @@ SPF_record_interpret(SPF_record_t *spf_record, + + rr_a = SPF_dns_lookup(resolver, + rr_ptr->rr[i]->ptr, ns_t_a, TRUE); ++ if (SPF_i_note_void_lookup(spf_eval, rr_a) == SPF_E_BIG_DNS) { ++ SPF_dns_rr_free(rr_ptr); ++ SPF_dns_rr_free(rr_a); ++ SPF_FREE_LOOKUP_DATA(); ++ return DONE(SPF_RESULT_PERMERROR, SPF_REASON_NONE, ++ SPF_E_BIG_DNS); ++ } + + if (spf_server->debug) + SPF_debugf( "%d: found %d A records for %s (herrno: %d)", +@@ -967,15 +1020,13 @@ SPF_record_interpret(SPF_record_t *spf_record, + } + SPF_dns_rr_free(rr_ptr); + +- if (max_exceeded) { +- SPF_FREE_LOOKUP_DATA(); +- return DONE(SPF_RESULT_PERMERROR, SPF_REASON_NONE, SPF_E_BIG_DNS); +- } ++ /* RFC 7208 says to ignore PTR names after the first ten. */ + } + + else if ( spf_request->client_ver == AF_INET6 ) { + rr_ptr = SPF_dns_rlookup6(resolver, + spf_request->ipv6, ns_t_ptr, TRUE); ++ SPF_CHECK_VOID_LOOKUP(rr_ptr); + + if ( spf_server->debug ) { + INET_NTOP( AF_INET6, &spf_request->ipv6.s6_addr, +@@ -991,10 +1042,8 @@ SPF_record_interpret(SPF_record_t *spf_record, + + + max_ptr = rr_ptr->num_rr; +- max_exceeded = 0; + if (max_ptr > spf_server->max_dns_ptr) { + max_ptr = SPF_server_get_max_dns_ptr(spf_server); +- max_exceeded = 1; + } + + for (i = 0; i < max_ptr; i++) { +@@ -1002,6 +1051,13 @@ SPF_record_interpret(SPF_record_t *spf_record, + + rr_aaaa = SPF_dns_lookup(resolver, + rr_ptr->rr[i]->ptr, ns_t_aaaa, TRUE); ++ if (SPF_i_note_void_lookup(spf_eval, rr_aaaa) == SPF_E_BIG_DNS) { ++ SPF_dns_rr_free(rr_ptr); ++ SPF_dns_rr_free(rr_aaaa); ++ SPF_FREE_LOOKUP_DATA(); ++ return DONE(SPF_RESULT_PERMERROR, SPF_REASON_NONE, ++ SPF_E_BIG_DNS); ++ } + + if ( spf_server->debug ) + SPF_debugf("%d: found %d AAAA records for %s (herrno: %d)", +@@ -1038,10 +1094,7 @@ SPF_record_interpret(SPF_record_t *spf_record, + } + SPF_dns_rr_free(rr_ptr); + +- if (max_exceeded) { +- SPF_FREE_LOOKUP_DATA(); +- return DONE(SPF_RESULT_PERMERROR, SPF_REASON_NONE, SPF_E_BIG_DNS); +- } ++ /* RFC 7208 says to ignore PTR names after the first ten. */ + } + + +@@ -1079,8 +1132,8 @@ SPF_record_interpret(SPF_record_t *spf_record, + /* Remember to reset this. */ + save_cur_dom = spf_request->cur_dom; + spf_request->cur_dom = lookup; +- err = SPF_server_get_record(spf_server, spf_request, +- spf_response, &spf_record_subr); ++ err = SPF_i_server_get_record(spf_server, spf_request, ++ spf_response, &spf_record_subr, spf_eval); + + if ( spf_server->debug > 0 ) + SPF_debugf( "include/redirect: got SPF record: %s", +@@ -1124,8 +1177,8 @@ SPF_record_interpret(SPF_record_t *spf_record, + /* + * find out whether this configuration passes + */ +- err = SPF_record_interpret(spf_record_subr, +- spf_request, spf_response, depth + 1); ++ err = SPF_i_record_interpret(spf_record_subr, ++ spf_request, spf_response, spf_eval, depth + 1); + spf_request->cur_dom = save_cur_dom; + /* Now, if we were a redirect, the child called done() + * and used spf_record_exp. In that case, we need not +@@ -1150,6 +1203,7 @@ SPF_record_interpret(SPF_record_t *spf_record, + spf_response_subr = spf_response; + spf_response = save_spf_response; + save_spf_response = NULL; ++ spf_response->num_dns_mech = spf_eval->dns_mech; + + /* Rewrite according to prefix of include */ + switch (SPF_response_result(spf_response_subr)) { +@@ -1225,6 +1279,7 @@ SPF_record_interpret(SPF_record_t *spf_record, + lookup = buf; + + rr_a = SPF_dns_lookup(resolver, lookup, ns_t_a, FALSE ); ++ SPF_CHECK_VOID_LOOKUP(rr_a); + + if ( spf_server->debug ) + SPF_debugf( "found %d A records for %s (herrno: %d)", +@@ -1262,8 +1317,8 @@ SPF_record_interpret(SPF_record_t *spf_record, + */ + + if ( mech == local_policy ) { +- err = SPF_record_interpret(spf_server->local_policy, +- spf_request, spf_response, depth + 1); ++ err = SPF_i_record_interpret(spf_server->local_policy, ++ spf_request, spf_response, spf_eval, depth + 1); + + if ( spf_server->debug > 0 ) + SPF_debugf( "local_policy: executed SPF record: %s result: %s reason: %s", +@@ -1284,3 +1339,13 @@ SPF_record_interpret(SPF_record_t *spf_record, + /* falling off the end is the same as ?all */ + return DONE( SPF_RESULT_NEUTRAL, SPF_REASON_DEFAULT, SPF_E_SUCCESS ); + } ++ ++SPF_errcode_t ++SPF_record_interpret(SPF_record_t *spf_record, ++ SPF_request_t *spf_request, SPF_response_t *spf_response, ++ int depth) ++{ ++ SPF_eval_ctx_t spf_eval = { 0, 0 }; ++ return SPF_i_record_interpret(spf_record, spf_request, spf_response, ++ &spf_eval, depth); ++} +diff --git a/src/libspf2/spf_record.c b/src/libspf2/spf_record.c +index 3109aa0..5a54d76 100644 +--- a/src/libspf2/spf_record.c ++++ b/src/libspf2/spf_record.c +@@ -48,6 +48,7 @@ SPF_record_t * + SPF_record_new(SPF_server_t *spf_server, const char *text) + { + SPF_record_t *rp; ++ (void)text; + + rp = (SPF_record_t *)malloc(sizeof(SPF_record_t)); + if (!rp) +diff --git a/src/libspf2/spf_request.c b/src/libspf2/spf_request.c +index c310a5a..5a1ba81 100644 +--- a/src/libspf2/spf_request.c ++++ b/src/libspf2/spf_request.c +@@ -34,9 +34,46 @@ + #include "spf_request.h" + #include "spf_internal.h" + ++#include ++ + #define SPF_FREE(x) \ + do { if (x) free(x); (x) = NULL; } while(0) + ++static int ++SPF_i_has_non_ascii(const char *text) ++{ ++ const unsigned char *cursor = (const unsigned char *)text; ++ while (*cursor != '\0') { ++ if ((*cursor & 0x80U) != 0U) ++ return TRUE; ++ cursor++; ++ } ++ return FALSE; ++} ++ ++static SPF_errcode_t ++SPF_i_dup_dns_domain(const char *domain, char **result) ++{ ++ char *alabel = NULL; ++ int idn_error; ++ ++ SPF_ASSERT_NOTNULL(domain); ++ SPF_ASSERT_NOTNULL(result); ++ *result = NULL; ++ if (!SPF_i_has_non_ascii(domain)) { ++ *result = strdup(domain); ++ return *result != NULL ? SPF_E_SUCCESS : SPF_E_NO_MEMORY; ++ } ++ ++ idn_error = idn2_to_ascii_8z(domain, &alabel, ++ IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL | IDN2_USE_STD3_ASCII_RULES); ++ if (idn_error != IDN2_OK) ++ return SPF_E_INVALID_CHAR; ++ *result = strdup(alabel); ++ idn2_free(alabel); ++ return *result != NULL ? SPF_E_SUCCESS : SPF_E_NO_MEMORY; ++} ++ + SPF_request_t * + SPF_request_new(SPF_server_t *spf_server) + { +@@ -116,14 +153,16 @@ SPF_request_set_ipv6_str(SPF_request_t *sr, const char *astr) + SPF_errcode_t + SPF_request_set_helo_dom(SPF_request_t *sr, const char *dom) + { ++ SPF_errcode_t err; ++ + SPF_ASSERT_NOTNULL(dom); + SPF_FREE(sr->helo_dom); +- sr->helo_dom = strdup(dom); +- if (! sr->helo_dom) +- return SPF_E_NO_MEMORY; ++ err = SPF_i_dup_dns_domain(dom, &sr->helo_dom); ++ if (err != SPF_E_SUCCESS) ++ return err; + /* set cur_dom and env_from? */ + if (sr->env_from == NULL) +- return SPF_request_set_env_from(sr, dom); ++ return SPF_request_set_env_from(sr, sr->helo_dom); + return SPF_E_SUCCESS; + } + +@@ -138,7 +177,9 @@ SPF_request_get_rec_dom(SPF_request_t *sr) + int + SPF_request_set_env_from(SPF_request_t *sr, const char *from) + { +- char *cp; ++ const char *cp; ++ char *dns_domain = NULL; ++ SPF_errcode_t err; + size_t len; + + SPF_ASSERT_NOTNULL(from); +@@ -162,31 +203,32 @@ SPF_request_set_env_from(SPF_request_t *sr, const char *from) + } + strncpy(sr->env_from_lp, from, len); + sr->env_from_lp[len] = '\0'; +- sr->env_from_dp = strdup(cp + 1); +- if (!sr->env_from_dp) { ++ err = SPF_i_dup_dns_domain(cp + 1, &sr->env_from_dp); ++ if (err != SPF_E_SUCCESS) { + SPF_FREE(sr->env_from); + SPF_FREE(sr->env_from_lp); +- return SPF_E_NO_MEMORY; ++ return err; + } + } + else { + if (cp == from) from++; /* "@domain.example" */ +- len = sizeof("postmaster@") + strlen(from); ++ err = SPF_i_dup_dns_domain(from, &dns_domain); ++ if (err != SPF_E_SUCCESS) ++ return err; ++ len = sizeof("postmaster@") + strlen(dns_domain); + sr->env_from = malloc(len + 1); /* sizeof("") == 1? */ +- if (! sr->env_from) ++ if (! sr->env_from) { ++ free(dns_domain); + return SPF_E_NO_MEMORY; +- sprintf(sr->env_from, "postmaster@%s", from); ++ } ++ sprintf(sr->env_from, "postmaster@%s", dns_domain); + sr->env_from_lp = strdup("postmaster"); + if (!sr->env_from_lp) { + SPF_FREE(sr->env_from); ++ free(dns_domain); + return SPF_E_NO_MEMORY; + } +- sr->env_from_dp = strdup(from); +- if (!sr->env_from_dp) { +- SPF_FREE(sr->env_from); +- SPF_FREE(sr->env_from_lp); +- return SPF_E_NO_MEMORY; +- } ++ sr->env_from_dp = dns_domain; + } + + return 0; // SPF_E_SUCCESS +@@ -241,11 +283,19 @@ static SPF_errcode_t + SPF_request_query_record(SPF_request_t *spf_request, + SPF_response_t *spf_response, + SPF_record_t *spf_record, +- SPF_errcode_t err) ++ SPF_errcode_t err, ++ SPF_eval_ctx_t *spf_eval) + { + if (err != SPF_E_SUCCESS) { + if (spf_record) + SPF_record_free(spf_record); ++ if (spf_response->result == SPF_RESULT_INVALID) { ++ spf_response->result = (err == SPF_E_NO_MEMORY || ++ err == SPF_E_DNS_ERROR || err == SPF_E_INTERNAL_ERROR) ++ ? SPF_RESULT_TEMPERROR : SPF_RESULT_PERMERROR; ++ spf_response->reason = SPF_REASON_FAILURE; ++ spf_response->err = err; ++ } + SPF_i_done(spf_response, spf_response->result, spf_response->reason, spf_response->err); + return err; + } +@@ -256,9 +306,10 @@ SPF_request_query_record(SPF_request_t *spf_request, + SPF_response_errors(spf_response)); + /* If we get here, spf_record better not be NULL */ + spf_response->spf_record_exp = spf_record; +- err = SPF_record_interpret(spf_record, +- spf_request, spf_response, 0); ++ err = SPF_i_record_interpret(spf_record, ++ spf_request, spf_response, spf_eval, 0); + SPF_record_free(spf_record); ++ spf_response->spf_record_exp = NULL; + + return err; + } +@@ -273,6 +324,7 @@ SPF_request_query_mailfrom(SPF_request_t *spf_request, + SPF_server_t *spf_server; + SPF_record_t *spf_record; + SPF_errcode_t err; ++ SPF_eval_ctx_t spf_eval = { 0, 0 }; + + SPF_ASSERT_NOTNULL(spf_request); + spf_server = spf_request->spf_server; +@@ -289,10 +341,10 @@ SPF_request_query_mailfrom(SPF_request_t *spf_request, + + SPF_request_prepare(spf_request); + +- err = SPF_server_get_record(spf_server, spf_request, +- *spf_responsep, &spf_record); ++ err = SPF_i_server_get_record(spf_server, spf_request, ++ *spf_responsep, &spf_record, &spf_eval); + return SPF_request_query_record(spf_request, *spf_responsep, +- spf_record, err); ++ spf_record, err, &spf_eval); + } + + /* This interface isn't finalised. */ +@@ -304,6 +356,7 @@ SPF_request_query_fallback(SPF_request_t *spf_request, + SPF_server_t *spf_server; + SPF_record_t *spf_record; + SPF_errcode_t err; ++ SPF_eval_ctx_t spf_eval = { 0, 0 }; + + SPF_ASSERT_NOTNULL(spf_request); + spf_server = spf_request->spf_server; +@@ -324,7 +377,7 @@ SPF_request_query_fallback(SPF_request_t *spf_request, + *spf_responsep, &spf_record, + record); + return SPF_request_query_record(spf_request, *spf_responsep, +- spf_record, err); ++ spf_record, err, &spf_eval); + } + + /** +@@ -344,7 +397,10 @@ SPF_request_query_rcptto(SPF_request_t *spf_request, + SPF_server_t *spf_server; + SPF_record_t *spf_record; + SPF_errcode_t err; ++ SPF_eval_ctx_t spf_eval = { 0, 0 }; + const char *rcpt_to_dom; ++ const char *saved_cur_dom; ++ char *dns_domain = NULL; + char *record; + size_t len; + +@@ -366,17 +422,27 @@ SPF_request_query_rcptto(SPF_request_t *spf_request, + rcpt_to_dom = rcpt_to; + else + rcpt_to_dom++; +- spf_request->cur_dom = rcpt_to_dom; ++ err = SPF_i_dup_dns_domain(rcpt_to_dom, &dns_domain); ++ if (err != SPF_E_SUCCESS) ++ return err; ++ saved_cur_dom = spf_request->cur_dom; ++ spf_request->cur_dom = dns_domain; + +- len = sizeof(SPF_VER_STR) + 64 + strlen(rcpt_to_dom); ++ len = sizeof(SPF_VER_STR) + 64 + strlen(dns_domain); + record = malloc(len); +- if (! record) ++ if (! record) { ++ spf_request->cur_dom = saved_cur_dom; ++ free(dns_domain); + return SPF_E_NO_MEMORY; +- snprintf(record, len, SPF_VER_STR " mx:%s", rcpt_to_dom); ++ } ++ snprintf(record, len, SPF_VER_STR " mx:%s", dns_domain); + err = SPF_record_compile(spf_server, + *spf_responsep, &spf_record, + record); + free(record); +- return SPF_request_query_record(spf_request, *spf_responsep, +- spf_record, err); ++ err = SPF_request_query_record(spf_request, *spf_responsep, ++ spf_record, err, &spf_eval); ++ spf_request->cur_dom = saved_cur_dom; ++ free(dns_domain); ++ return err; + } +diff --git a/src/libspf2/spf_server.c b/src/libspf2/spf_server.c +index 282567b..71c109f 100644 +--- a/src/libspf2/spf_server.c ++++ b/src/libspf2/spf_server.c +@@ -67,14 +67,14 @@ __attribute__((warn_unused_result)) + static SPF_errcode_t + SPF_server_set_rec_dom_ghbn(SPF_server_t *sp) + { +- sp->rec_dom = malloc(HOST_NAME_MAX); ++ sp->rec_dom = malloc(HOST_NAME_MAX + 1); + if (! sp->rec_dom) + return SPF_E_NO_MEMORY; + #ifdef _WIN32 + gethostnameFQDN(sp->rec_dom, HOST_NAME_MAX); + return 0; /* XXX FIXME? */ + #else +- if (gethostname(sp->rec_dom, HOST_NAME_MAX) < 0) ++ if (gethostname(sp->rec_dom, HOST_NAME_MAX + 1) < 0) + /* XXX Error using strerror. */ + return SPF_E_INTERNAL_ERROR; + #endif +@@ -313,17 +313,17 @@ SPF_server_set_localpolicy(SPF_server_t *sp, const char *policy, + } + + SPF_errcode_t +-SPF_server_get_record(SPF_server_t *spf_server, +- SPF_request_t *spf_request, +- SPF_response_t *spf_response, +- SPF_record_t **spf_recordp) ++SPF_i_server_get_record(SPF_server_t *spf_server, ++ SPF_request_t *spf_request, ++ SPF_response_t *spf_response, ++ SPF_record_t **spf_recordp, ++ SPF_eval_ctx_t *spf_eval) + { + SPF_dns_server_t *resolver; + SPF_dns_rr_t *rr_txt; + SPF_errcode_t err; + SPF_dns_stat_t herrno; + const char *domain; +- ns_type rr_type; + int num_found; + int idx_found; + int i; +@@ -345,20 +345,22 @@ SPF_server_get_record(SPF_server_t *spf_server, + return resolver->get_spf(spf_server, spf_request, + spf_response, spf_recordp); + +- /* I am VERY, VERY sorry about the gotos. Shevek. */ +- rr_type = ns_t_spf; +-retry: +- rr_txt = SPF_dns_lookup(resolver, domain, rr_type, TRUE); ++ /* RFC 7208 removed the experimental SPF RR type (99). SPF records ++ * are selected exclusively from DNS TXT records. */ ++ rr_txt = SPF_dns_lookup(resolver, domain, ns_t_txt, TRUE); ++ if (SPF_i_note_void_lookup(spf_eval, rr_txt) == SPF_E_BIG_DNS) { ++ SPF_dns_rr_free(rr_txt); ++ spf_response->result = SPF_RESULT_PERMERROR; ++ spf_response->reason = SPF_REASON_FAILURE; ++ return SPF_response_add_error(spf_response, SPF_E_BIG_DNS, ++ "Too many void DNS lookups while resolving '%s'.", domain); ++ } + + switch (rr_txt->herrno) { + case HOST_NOT_FOUND: + if (spf_server->debug > 0) + SPF_debugf("get_record(%s): HOST_NOT_FOUND", domain); + SPF_dns_rr_free(rr_txt); +- if (rr_type == ns_t_spf) { +- rr_type = ns_t_txt; +- goto retry; +- } + spf_response->result = SPF_RESULT_NONE; + spf_response->reason = SPF_REASON_FAILURE; + return SPF_response_add_error(spf_response, SPF_E_NOT_SPF, +@@ -369,10 +371,6 @@ retry: + if (spf_server->debug > 0) + SPF_debugf("get_record(%s): NO_DATA", domain); + SPF_dns_rr_free(rr_txt); +- if (rr_type == ns_t_spf) { +- rr_type = ns_t_txt; +- goto retry; +- } + spf_response->result = SPF_RESULT_NONE; + spf_response->reason = SPF_REASON_FAILURE; + return SPF_response_add_error(spf_response, SPF_E_NOT_SPF, +@@ -383,10 +381,6 @@ retry: + if (spf_server->debug > 0) + SPF_debugf("get_record(%s): TRY_AGAIN", domain); + SPF_dns_rr_free(rr_txt); +- if (rr_type == ns_t_spf) { +- rr_type = ns_t_txt; +- goto retry; +- } + spf_response->result = SPF_RESULT_TEMPERROR; + spf_response->reason = SPF_REASON_FAILURE; + return SPF_response_add_error(spf_response, SPF_E_DNS_ERROR, +@@ -397,10 +391,6 @@ retry: + if (spf_server->debug > 0) + SPF_debugf("get_record(%s): NO_RECOVERY", domain); + SPF_dns_rr_free(rr_txt); +- if (rr_type == ns_t_spf) { +- rr_type = ns_t_txt; +- goto retry; +- } + spf_response->result = SPF_RESULT_PERMERROR; + spf_response->reason = SPF_REASON_FAILURE; + return SPF_response_add_error(spf_response, SPF_E_DNS_ERROR, +@@ -417,10 +407,6 @@ retry: + SPF_debugf("get_record(%s): UNKNOWN_ERROR", domain); + herrno = rr_txt->herrno; // Avoid use-after-free + SPF_dns_rr_free(rr_txt); +- if (rr_type == ns_t_spf) { +- rr_type = ns_t_txt; +- goto retry; +- } + return SPF_response_add_error(spf_response, SPF_E_DNS_ERROR, + "Unknown DNS failure for '%s': %d.", + domain, herrno); +@@ -429,10 +415,6 @@ retry: + + if (rr_txt->num_rr == 0) { + SPF_dns_rr_free(rr_txt); +- if (rr_type == ns_t_spf) { +- rr_type = ns_t_txt; +- goto retry; +- } + return SPF_response_add_error(spf_response, SPF_E_NOT_SPF, + "No TXT records returned from DNS lookup for '%s'", + domain); +@@ -463,10 +445,6 @@ retry: + + if (num_found == 0) { + SPF_dns_rr_free(rr_txt); +- if (rr_type == ns_t_spf) { +- rr_type = ns_t_txt; +- goto retry; +- } + spf_response->result = SPF_RESULT_NONE; + spf_response->reason = SPF_REASON_FAILURE; + return SPF_response_add_error(spf_response, SPF_E_NOT_SPF, +@@ -496,24 +474,32 @@ retry: + return SPF_E_SUCCESS; + } + ++SPF_errcode_t ++SPF_server_get_record(SPF_server_t *spf_server, ++ SPF_request_t *spf_request, ++ SPF_response_t *spf_response, ++ SPF_record_t **spf_recordp) ++{ ++ SPF_eval_ctx_t spf_eval = { 0, 0 }; ++ return SPF_i_server_get_record(spf_server, spf_request, spf_response, ++ spf_recordp, &spf_eval); ++} ++ + /** + * Various accessors. + * +- * The user is permitted to override the maximums. ++ * The user may lower the limits, but RFC 7208 limits are security bounds and ++ * must not be raised by configuration. + */ +-#define SPF_ACCESS_INT(f) \ ++#define SPF_ACCESS_LIMITED_INT(f, maximum) \ + SPF_errcode_t SPF_server_set_ ## f(SPF_server_t *s, int n) { \ ++ if (n < 0 || n > (maximum)) return SPF_E_INVALID_OPT; \ + s->f = n; return SPF_E_SUCCESS; \ + } \ + int SPF_server_get_ ## f(SPF_server_t *s) { \ + return s->f; \ + } + +-/** +- * The return values from these getter functions are used without +- * modification. If you set a value higher than the specified +- * maximum, it will be used. Beware. +- */ +-SPF_ACCESS_INT(max_dns_mech); +-SPF_ACCESS_INT(max_dns_ptr); +-SPF_ACCESS_INT(max_dns_mx); ++SPF_ACCESS_LIMITED_INT(max_dns_mech, SPF_MAX_DNS_MECH); ++SPF_ACCESS_LIMITED_INT(max_dns_ptr, SPF_MAX_DNS_PTR); ++SPF_ACCESS_LIMITED_INT(max_dns_mx, SPF_MAX_DNS_MX); +diff --git a/src/spfd/Makefile.am b/src/spfd/Makefile.am +index 7c7b6e4..d855c39 100644 +--- a/src/spfd/Makefile.am ++++ b/src/spfd/Makefile.am +@@ -1,7 +1,7 @@ + # Makefile prototype + + AUTOMAKE_OPTIONS = foreign +-AM_CPPFLAGS = -I$(top_srcdir)/src/include -I$(top_srcdir)/src ++AM_CPPFLAGS = -I$(top_builddir)/src/include -I$(top_srcdir)/src/include + + #LIBS = /usr/lib/libresolv.a + +diff --git a/src/spfd/spfd.c b/src/spfd/spfd.c +index 6ad8baf..1f28598 100644 +--- a/src/spfd/spfd.c ++++ b/src/spfd/spfd.c +@@ -773,15 +773,21 @@ handle_datagram(void *arg) + value = strchr(key, '='); + + /* Did that line contain an '='? */ +- if (!value) /* XXX WARN */ ++ if (!value) { /* XXX WARN */ ++ key = end + 1; ++ while (key < (req->data + req->datalen) && ++ strchr("\r\n", *key) != NULL) ++ key++; + continue; ++ } + + *value++ = '\0'; + fp = find_field(req, key); + if (fp != NULL) + *fp = value; +- else +- /* warned already */ ; ++ else { ++ /* warned already */ ++ } + + key = end + 1; + while (key < (req->data + req->datalen)) { +@@ -847,8 +853,9 @@ handle_stream(void *arg) + fp = find_field(req, key); + if (fp != NULL) + *fp = strdup(value); +- else +- /* warned already */ ; ++ else { ++ /* warned already */ ++ } + } + + request_handle(req); +diff --git a/src/spfquery/Makefile.am b/src/spfquery/Makefile.am +index 8978f9b..99771f1 100644 +--- a/src/spfquery/Makefile.am ++++ b/src/spfquery/Makefile.am +@@ -1,7 +1,7 @@ + # Makefile prototype + + AUTOMAKE_OPTIONS = foreign +-AM_CPPFLAGS = -I$(top_srcdir)/src/include -I$(top_srcdir)/src ++AM_CPPFLAGS = -I$(top_builddir)/src/include -I$(top_srcdir)/src/include -I$(top_srcdir)/src + + #LIBS = /usr/lib/libresolv.a + +diff --git a/src/spfquery/spfquery.c b/src/spfquery/spfquery.c +index c4dc5bb..b5babbc 100644 +--- a/src/spfquery/spfquery.c ++++ b/src/spfquery/spfquery.c +@@ -124,14 +124,19 @@ + #define FAIL_ERROR do { res = 255; goto error; } while(0) + + #define RESIZE_RESULT(n) do { \ ++ char *new_result; \ + if (result == NULL) { \ + result_len = 256 + n; \ +- result = malloc(result_len); \ ++ new_result = malloc(result_len); \ ++ if (new_result == NULL) FAIL_ERROR; \ ++ result = new_result; \ + result[0] = '\0'; \ + } \ + else if (strlen(result) + n >= result_len) { \ + result_len = result_len + (result_len >> 1) + 8 + n; \ +- result = realloc(result, result_len); \ ++ new_result = realloc(result, result_len); \ ++ if (new_result == NULL) FAIL_ERROR; \ ++ result = new_result; \ + } \ + } while(0) + #define APPEND_RESULT(n) do { \ +@@ -356,7 +361,7 @@ int main( int argc, char *argv[] ) + + const char *partial_result; + char *result = NULL; +- int result_len = 0; ++ size_t result_len = 0; + + opts = (SPF_client_options_t *)malloc(sizeof(SPF_client_options_t)); + memset(opts, 0, sizeof(SPF_client_options_t)); diff --git a/mail-filter/libspf2/files/libspf2-1.2.11-rfc7208-test.c b/mail-filter/libspf2/files/libspf2-1.2.11-rfc7208-test.c new file mode 100644 index 0000000..00fe143 --- /dev/null +++ b/mail-filter/libspf2/files/libspf2-1.2.11-rfc7208-test.c @@ -0,0 +1,272 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +static int failures; + +static void +check_result(const char *name, SPF_server_t *server, const char *ip, + const char *helo, const char *mail_from, SPF_result_t expected) +{ + SPF_request_t *request = SPF_request_new(server); + SPF_response_t *response = NULL; + SPF_errcode_t error; + SPF_result_t actual = SPF_RESULT_INVALID; + + if (request == NULL || + (strchr(ip, ':') != NULL + ? SPF_request_set_ipv6_str(request, ip) + : SPF_request_set_ipv4_str(request, ip)) != SPF_E_SUCCESS || + SPF_request_set_helo_dom(request, helo) != SPF_E_SUCCESS || + SPF_request_set_env_from(request, mail_from) != SPF_E_SUCCESS) { + fprintf(stderr, "%s: request setup failed\n", name); + failures++; + if (request != NULL) + SPF_request_free(request); + return; + } + + error = SPF_request_query_mailfrom(request, &response); + if (response != NULL) + actual = SPF_response_result(response); + if (actual != expected) { + fprintf(stderr, "%s: expected %s, got %s (error %d)\n", name, + SPF_strresult(expected), SPF_strresult(actual), (int)error); + failures++; + } + + SPF_response_free(response); + if (request != NULL) + SPF_request_free(request); +} + +static SPF_server_t * +new_server(void) +{ + SPF_server_t *server = SPF_server_new(SPF_DNS_ZONE, 0); + if (server == NULL) { + fputs("cannot create SPF server\n", stderr); + exit(2); + } + if (SPF_server_set_rec_dom(server, "receiver.example") != SPF_E_SUCCESS) { + fputs("cannot set receiver domain\n", stderr); + exit(2); + } + return server; +} + +static void +add(SPF_server_t *server, const char *name, ns_type type, const char *value) +{ + if (SPF_dns_zone_add_str(server->resolver, name, type, NETDB_SUCCESS, + value) != SPF_E_SUCCESS) { + fprintf(stderr, "cannot add DNS record for %s\n", name); + exit(2); + } +} + +static void +test_txt_only(void) +{ + SPF_server_t *server = new_server(); + add(server, "type99.example", ns_t_spf, "v=spf1 +all"); + check_result("TXT-only record selection", server, "192.0.2.1", + "mx.example", "sender@type99.example", SPF_RESULT_NONE); + SPF_server_free(server); +} + +static void +test_nested_lookup_limit(void) +{ + SPF_server_t *server = new_server(); + char domain[64]; + char next[128]; + int i; + + for (i = 0; i < 11; i++) { + snprintf(domain, sizeof(domain), "n%d.example", i); + snprintf(next, sizeof(next), "v=spf1 include:n%d.example -all", i + 1); + add(server, domain, ns_t_txt, next); + } + add(server, "n11.example", ns_t_txt, "v=spf1 +all"); + check_result("global nested DNS-term limit", server, "192.0.2.1", + "mx.example", "sender@n0.example", SPF_RESULT_PERMERROR); + SPF_server_free(server); + + server = new_server(); + for (i = 0; i < 10; i++) { + snprintf(domain, sizeof(domain), "ok%d.example", i); + snprintf(next, sizeof(next), "v=spf1 include:ok%d.example -all", i + 1); + add(server, domain, ns_t_txt, next); + } + add(server, "ok10.example", ns_t_txt, "v=spf1 +all"); + check_result("ten nested DNS terms remain valid", server, "192.0.2.1", + "mx.example", "sender@ok0.example", SPF_RESULT_PASS); + SPF_server_free(server); +} + +static void +test_void_lookup_limit(void) +{ + SPF_server_t *server = new_server(); + add(server, "void.example", ns_t_txt, + "v=spf1 exists:one.invalid exists:two.invalid exists:three.invalid -all"); + check_result("third void lookup", server, "192.0.2.1", "mx.example", + "sender@void.example", SPF_RESULT_PERMERROR); + SPF_server_free(server); + + server = new_server(); + add(server, "two-void.example", ns_t_txt, + "v=spf1 exists:one.invalid exists:two.invalid -all"); + check_result("two void lookups remain valid", server, "192.0.2.1", + "mx.example", "sender@two-void.example", SPF_RESULT_FAIL); + SPF_server_free(server); +} + +static void +test_mx_limits(void) +{ + SPF_server_t *server = new_server(); + char address[32]; + int i; + + add(server, "mx-address.example", ns_t_txt, "v=spf1 mx -all"); + add(server, "mx-address.example", ns_t_mx, "10 mail.mx-address.example"); + for (i = 1; i <= 11; i++) { + snprintf(address, sizeof(address), "198.51.100.%d", i); + add(server, "mail.mx-address.example", ns_t_a, address); + } + check_result("MX address record limit", server, "198.51.100.1", + "mx.example", "sender@mx-address.example", + SPF_RESULT_PERMERROR); + SPF_server_free(server); + + server = new_server(); + add(server, "mx-host.example", ns_t_txt, "v=spf1 mx -all"); + for (i = 1; i <= 11; i++) { + snprintf(address, sizeof(address), "%d mail%d.mx-host.example", i, i); + add(server, "mx-host.example", ns_t_mx, address); + } + check_result("MX host record limit", server, "198.51.100.1", + "mx.example", "sender@mx-host.example", SPF_RESULT_PERMERROR); + SPF_server_free(server); +} + +static void +test_identities_and_addresses(void) +{ + SPF_server_t *server = new_server(); + add(server, "helo.example", ns_t_txt, "v=spf1 ip4:192.0.2.10 -all"); + check_result("null sender uses HELO identity", server, "192.0.2.10", + "helo.example", "", SPF_RESULT_PASS); + SPF_server_free(server); + + server = new_server(); + add(server, "ipv6.example", ns_t_txt, "v=spf1 ip6:2001:db8::/32 -all"); + check_result("IPv6 SPF", server, "2001:db8::1", "helo.example", + "sender@ipv6.example", SPF_RESULT_PASS); + SPF_server_free(server); + + server = new_server(); + add(server, "multiple.example", ns_t_txt, "v=spf1 +all"); + add(server, "multiple.example", ns_t_txt, "v=spf1 -all"); + check_result("multiple SPF TXT records", server, "192.0.2.1", + "helo.example", "sender@multiple.example", + SPF_RESULT_PERMERROR); + SPF_server_free(server); +} + +static void +test_open_pull_request_regressions(void) +{ + SPF_server_t *server = new_server(); + + add(server, "redirect-order.example", ns_t_txt, + "v=spf1 redirect=redirect-target.example ip4:192.0.2.44"); + add(server, "redirect-target.example", ns_t_txt, "v=spf1 -all"); + check_result("redirect is evaluated after mechanisms", server, + "192.0.2.44", "helo.example", + "sender@redirect-order.example", SPF_RESULT_PASS); + SPF_server_free(server); + + server = new_server(); + add(server, "duplicate-redirect.example", ns_t_txt, + "v=spf1 redirect=one.example redirect=two.example"); + check_result("duplicate redirect modifier", server, "192.0.2.1", + "helo.example", "sender@duplicate-redirect.example", + SPF_RESULT_PERMERROR); + SPF_server_free(server); + + server = new_server(); + add(server, "macro.example", ns_t_txt, + "v=spf1 exists:%{l1}.macro.example -all"); + add(server, "alice.macro.example", ns_t_a, "192.0.2.1"); + check_result("macro truncation without delimiter", server, "192.0.2.1", + "helo.example", "alice@macro.example", SPF_RESULT_PASS); + SPF_server_free(server); +} + +static void +test_eai_domains(void) +{ + SPF_server_t *server = new_server(); + SPF_request_t *request; + SPF_response_t *response = NULL; + SPF_errcode_t error; + + add(server, "xn--mnchen-3ya.example", ns_t_txt, + "v=spf1 ip4:192.0.2.60 -all"); + check_result("EAI MAIL FROM domain", server, "192.0.2.60", + "helo.example", "b\303\274ro@m\303\274nchen.example", + SPF_RESULT_PASS); + check_result("EAI HELO with null sender", server, "192.0.2.60", + "m\303\274nchen.example", "", SPF_RESULT_PASS); + + add(server, "xn--mnchen-3ya.example", ns_t_mx, + "10 mail.xn--mnchen-3ya.example"); + add(server, "mail.xn--mnchen-3ya.example", ns_t_a, "192.0.2.60"); + request = SPF_request_new(server); + if (request == NULL || + SPF_request_set_ipv4_str(request, "192.0.2.60") != SPF_E_SUCCESS || + SPF_request_set_helo_dom(request, "helo.example") != SPF_E_SUCCESS) { + fputs("EAI RCPT TO: request setup failed\n", stderr); + failures++; + } + else { + error = SPF_request_query_rcptto(request, &response, + "postmaster@m\303\274nchen.example"); + if (error != SPF_E_SUCCESS || response == NULL || + SPF_response_result(response) != SPF_RESULT_PASS) { + fprintf(stderr, "EAI RCPT TO: expected pass (error %d)\n", + (int)error); + failures++; + } + } + SPF_response_free(response); + if (request != NULL) + SPF_request_free(request); + SPF_server_free(server); +} + +int +main(void) +{ +#ifndef LIBSPF2_RFC7208 + fputs("RFC 7208 package marker missing\n", stderr); + return 2; +#endif + test_txt_only(); + test_nested_lookup_limit(); + test_void_lookup_limit(); + test_mx_limits(); + test_identities_and_addresses(); + test_open_pull_request_regressions(); + test_eai_domains(); + return failures == 0 ? 0 : 1; +} diff --git a/mail-filter/libspf2/files/libspf2-1.2.11-undefined-dn_.patch b/mail-filter/libspf2/files/libspf2-1.2.11-undefined-dn_.patch new file mode 100644 index 0000000..7815d3a --- /dev/null +++ b/mail-filter/libspf2/files/libspf2-1.2.11-undefined-dn_.patch @@ -0,0 +1,147 @@ +From 808b584d22106828aaaa3f2a52b1d076b6e66040 Mon Sep 17 00:00:00 2001 +From: Natanael Copa +Date: Thu, 11 Sep 2014 08:09:34 +0000 +Subject: [PATCH] provide dn_skipname + +Modified for Gentoo to include dn_expand fix +keep dn_expand available, for musl and glibc-2.34 +also added declaration fix for dn_expand for glibc because we +cannot include resolv.h, which defines it + +--- + configure.ac | 2 +- + src/libreplace/__dn_skipname.c | 87 ++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 88 insertions(+), 1 deletion(-) + create mode 100644 src/libreplace/__dn_skipname.c + +diff --git a/configure.ac b/configure.ac +index 63e398c..739da3b 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -269,6 +269,7 @@ + #endif + ]]) + ++AC_CHECK_FUNCS([dn_expand]) + AC_CHECK_FUNCS([ftruncate gethostname memmove memset socket]) + AC_CHECK_FUNCS([strchr strcspn strdup strerror]) + AC_CHECK_FUNCS([strcasecmp strncasecmp strspn strtoul]) +@@ -274,7 +274,7 @@ + AC_CHECK_FUNCS([gethostbyname inet_ntoa select strrchr strstr strtol]) + + AC_REPLACE_FUNCS([getopt_long_only strncasecmp]) +-AC_REPLACE_FUNCS([__ns_initparse __ns_name_uncompress __ns_msg_getflag]) ++AC_REPLACE_FUNCS([__ns_initparse __ns_name_uncompress __ns_msg_getflag __dn_skipname]) + dnl Magnus Holmgren says this avoids linking a private function + AC_LIBOBJ([__ns_get16]) + +diff --git a/src/libreplace/__dn_skipname.c b/src/libreplace/__dn_skipname.c +new file mode 100644 +index 0000000..88d48b5 +--- /dev/null ++++ b/src/libreplace/__dn_skipname.c +@@ -0,0 +1,87 @@ ++/* taken from OpenBSD source */ ++/* ++ * ++Copyright++ 1985, 1993 ++ * - ++ * Copyright (c) 1985, 1993 ++ * The Regents of the University of California. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the University nor the names of its contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * - ++ * Portions Copyright (c) 1993 by Digital Equipment Corporation. ++ * ++ * Permission to use, copy, modify, and distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies, and that ++ * the name of Digital Equipment Corporation not be used in advertising or ++ * publicity pertaining to distribution of the document or software without ++ * specific, written prior permission. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL ++ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT ++ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL ++ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR ++ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ++ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS ++ * SOFTWARE. ++ * - ++ * --Copyright-- ++ */ ++ ++/* ++ * Skip over a compressed domain name. Return the size or -1. ++ */ ++#include ++#include ++ ++int ++__dn_skipname(const u_char *comp_dn, const u_char *eom) ++{ ++ const u_char *cp; ++ int n; ++ ++ cp = comp_dn; ++ while (cp < eom && (n = *cp++)) { ++ /* ++ * check for indirection ++ */ ++ switch (n & INDIR_MASK) { ++ case 0: /* normal case, n == len */ ++ cp += n; ++ continue; ++ case INDIR_MASK: /* indirection */ ++ cp++; ++ break; ++ default: /* illegal type */ ++ return (-1); ++ } ++ break; ++ } ++ if (cp > eom) ++ return (-1); ++ return (cp - comp_dn); ++} ++ ++ +-- +2.1.0 + +--- a/src/libreplace/arpa_nameser.h ++++ b/src/libreplace/arpa_nameser.h +@@ -59,7 +59,10 @@ + # define __P(x) x + #endif + ++#if defined(HAVE_DN_EXPAND) ++int dn_expand __P((const u_char *, const u_char *, const u_char *, char *, int)); ++#endif +-#if defined( HAVE_ARPA_NAMESER_H ) && defined( HAVE_NS_TYPE ) ++#if !defined(HAVE_DN_EXPAND) && defined( HAVE_ARPA_NAMESER_H ) && defined( HAVE_NS_TYPE ) + #define dn_expand __dn_expand + int dn_expand __P((const u_char *, const u_char *, const u_char *, + char *, int)); diff --git a/mail-filter/libspf2/libspf2-1.2.11-r2.ebuild b/mail-filter/libspf2/libspf2-1.2.11-r2.ebuild new file mode 100644 index 0000000..f716900 --- /dev/null +++ b/mail-filter/libspf2/libspf2-1.2.11-r2.ebuild @@ -0,0 +1,71 @@ +# Copyright 1999-2026 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=7 + +inherit autotools toolchain-funcs + +GHASH=4915c308d57ff3abac9fb241f09c4bed2ab54815 # 1.2.11 +DESCRIPTION="Implementation of Sender Policy Framework (SPF)" +HOMEPAGE="https://www.libspf2.net/" +SRC_URI="https://github.com/shevek/${PN}/archive/${GHASH}.tar.gz -> ${P}.tar.gz" + +LICENSE="|| ( LGPL-2.1 BSD-2 )" +SLOT="0" +KEYWORDS="~alpha amd64 arm ~arm64 ~hppa ppc ppc64 ~sparc x86" + +RDEPEND="net-dns/libidn2" +DEPEND="${RDEPEND}" + +PATCHES=( + "${FILESDIR}"/${PN}-1.2.11-memset-include-string-h.patch + "${FILESDIR}"/${PN}-1.2.11-undefined-dn_.patch + "${FILESDIR}"/${PN}-1.2.11-musl.patch + "${FILESDIR}"/${PN}-1.2.11-integer-underflow.patch + "${FILESDIR}"/${PN}-1.2.11-rfc7208-hardening.patch +) + +S=${WORKDIR}/${PN}-${GHASH} + +src_prepare() { + default + + sed -i -e '/bin_PROGRAMS/s/spfquery_static//' src/spfquery/Makefile.am \ + -e '/bin_PROGRAMS/s/spftest_static//' src/spftest/Makefile.am \ + -e '/bin_PROGRAMS/s/spfd_static//' src/spfd/Makefile.am \ + -e '/bin_PROGRAMS/s/spf_example_static//' src/spf_example/Makefile.am \ + || die + + sed -i 's/AM_CONFIG_HEADER/AC_CONFIG_HEADERS/' configure.ac || die + sed -i -e '/AX_WITH_PERL/d' configure.ac || die # bug 885055 + + eautoreconf +} + +src_configure() { + econf --disable-static +} + +src_test() { + local test_include=${T}/libspf2-test-include + local test_binary=${T}/libspf2-rfc7208-test + + mkdir -p "${test_include}" || die + ln -s "${S}/src/include" "${test_include}/spf2" || die + + "$(tc-getCC)" ${CFLAGS} ${CPPFLAGS} -std=c99 -Wall -Wextra \ + -I"${test_include}" \ + "${FILESDIR}"/${PN}-1.2.11-rfc7208-test.c \ + -L"${S}/src/libspf2/.libs" \ + -Wl,-rpath,"${S}/src/libspf2/.libs" \ + -o "${test_binary}" -lspf2 -lresolv -lpthread -lidn2 \ + || die "failed to build the RFC 7208 regression test" + "${test_binary}" || die "RFC 7208 regression test failed" +} + +src_install() { + emake DESTDIR="${D}" install + dodoc README TODO INSTALL + + find "${ED}" -name '*.la' -delete || die +} diff --git a/mail-filter/libspf2/metadata.xml b/mail-filter/libspf2/metadata.xml new file mode 100644 index 0000000..9b90a0b --- /dev/null +++ b/mail-filter/libspf2/metadata.xml @@ -0,0 +1,17 @@ + + + + + grobian@gentoo.org + + + libspf2 implements the Sender Policy Framework, a part of the + SPF/SRS protocol pair. + libspf2 is a library which allows email systems such as + Sendmail, Postfix, Exim, Zmailer and MS Exchange to + check SPF records and make sure that the email is authorized by + the domain name that it is coming from. This prevents email + forgery, commonly used by spammers, scammers and email + viruses/worms. + + diff --git a/mail-filter/libsrs2/Manifest b/mail-filter/libsrs2/Manifest new file mode 100644 index 0000000..233448b --- /dev/null +++ b/mail-filter/libsrs2/Manifest @@ -0,0 +1,5 @@ +AUX libsrs2-1.0.18-parallel-make.diff 366 BLAKE2B ed7257f90d466e2e3f503c265e1fadcff2146396c20bf67d10e331bf68f9471574de96f88733fa4ebdc3890510af859232d56178a47f0554b9fd4e9813492c2c SHA512 5995ad456ab133d30e35abc8969ea6b8fa348e6f44d957b541654d25bd33a2018759f933fe7718f4ba38917e9e4533e2073a2f2b0184c6cda153b4774abf13aa +AUX libsrs2-1.0.18-security-hardening.patch 19884 BLAKE2B 6f20e4e311886e38063cf44615615a0dd147628078c35fab2b5672fe0b1feb4cbc406aa1be9c4e7c196a7025a9e5696a08feddf38c63dd8225e394fa6ca2d64a SHA512 1d951f43efff80b42c775775dabfdb50dfbce6bade77cede294f67cc5702e0344c2ff0e4110bd2e6cf401b62647a05c1fa8cc2b32a597944f2b119c5f7bad8a7 +DIST libsrs2-1.0.18.tar.gz 295748 BLAKE2B c4572ec58b474f47d34e127db7ce142ba6e715fb7f987801ae55a26628b5e2535b645b8209b6f5b106150b7999d2757e6b7bb64ee556d4b1c52df73144081626 SHA512 b9c189caa227487e90566f65430345f09a3d545fc286a3eb0c445aee3a74905c1a9248ce78b36a7cfb6a2936f6cd1efbe99ba7b8df49a613a81c6435396f5422 +EBUILD libsrs2-1.0.18-r2.ebuild 727 BLAKE2B cee3f402ea633725cc428f2336ffdcbf592c6bd283b78f94250dc1b5767161a1c18fa7208ab710633071e874d264adb06ea12cf0ed36f9dea83dfedeadd5b6a3 SHA512 ac29f9e32919e46f8bbb4cb58f250629d78b872537c69a1a234fdcd0bf6b333767e5039b4a284a53098557962abaa1173afe2e30cde721f9dcdbd90cca360e37 +MISC metadata.xml 691 BLAKE2B ef744563d78fa86d79ebf3afe8090d747a982e9d2024faf5fef7558844eb6cbfca9f671708bd2b029a9f47a3cbcc3404f66233304f3321fcc9eddf9e7e928196 SHA512 62488be7a6eb79c4848fc31ebaad5a343587fe9a97f444b6252eb113c0e112d0af3a7894fe7989fd70efcf604981483eef6d2ebd9fac068a8e0fdcd947095676 diff --git a/mail-filter/libsrs2/files/libsrs2-1.0.18-parallel-make.diff b/mail-filter/libsrs2/files/libsrs2-1.0.18-parallel-make.diff new file mode 100644 index 0000000..9447bb2 --- /dev/null +++ b/mail-filter/libsrs2/files/libsrs2-1.0.18-parallel-make.diff @@ -0,0 +1,12 @@ +--- a/libsrs2/Makefile.am 2004-06-10 11:33:28.000000000 +0200 ++++ b/libsrs2/Makefile.am 2009-09-29 16:08:29.000000000 +0200 +@@ -3,9 +3,6 @@ + include_HEADERS = srs2.h + noinst_HEADERS = win32.h + +-lib_LIBRARIES = libsrs2.a +-libsrs2_a_SOURCES = srs2.c sha1.c +- + lib_LTLIBRARIES = libsrs2.la + libsrs2_la_LDFLAGS = -version-info 0:0:0 + libsrs2_la_SOURCES = srs2.c sha1.c diff --git a/mail-filter/libsrs2/files/libsrs2-1.0.18-security-hardening.patch b/mail-filter/libsrs2/files/libsrs2-1.0.18-security-hardening.patch new file mode 100644 index 0000000..4accf44 --- /dev/null +++ b/mail-filter/libsrs2/files/libsrs2-1.0.18-security-hardening.patch @@ -0,0 +1,813 @@ +--- a/libsrs2/srs2.c ++++ b/libsrs2/srs2.c +@@ -13,14 +13,16 @@ + + #undef USE_OPENSSL + +-#include +-#include +-#include +- + #ifdef HAVE_CONFIG_H + #include "../config.h" + #endif + ++#include ++#include ++#include ++#include ++#include ++ + #ifdef _WIN32 + #include "win32.h" + #endif +@@ -45,6 +47,10 @@ + #include /* memcpy, strcpy, memset */ + #endif + ++#ifdef HAVE_STRINGS_H ++#include /* strcasecmp, strncasecmp */ ++#endif ++ + #ifdef HAVE_ALLOCA_H + #include + #endif +@@ -80,9 +86,39 @@ + static srs_realloc_t srs_f_realloc = realloc; + static srs_free_t srs_f_free = free; + ++static void ++srs_clear(void *ptr, size_t len) ++{ ++ volatile unsigned char *p = ptr; ++ ++ while (len-- > 0) ++ *p++ = 0; ++} ++ ++static int ++srs_format(char *buf, int buflen, const char *format, ...) ++{ ++ va_list ap; ++ int len; ++ ++ if (buf == NULL || buflen <= 0) ++ return SRS_EBUFTOOSMALL; ++ ++ va_start(ap, format); ++ len = vsnprintf(buf, (size_t)buflen, format, ap); ++ va_end(ap); ++ if (len < 0 || len >= buflen) { ++ buf[0] = '\0'; ++ return SRS_EBUFTOOSMALL; ++ } ++ return SRS_SUCCESS; ++} ++ + int + srs_set_malloc(srs_malloc_t m, srs_realloc_t r, srs_free_t f) + { ++ if (m == NULL || r == NULL || f == NULL) ++ return SRS_EINVAL; + srs_f_malloc = m; + srs_f_realloc = r; + srs_f_free = f; +@@ -110,6 +146,10 @@ + return "No at sign in sender address"; + case SRS_EBUFTOOSMALL: + return "Buffer too small."; ++ case SRS_ENOMEM: ++ return "Unable to allocate memory."; ++ case SRS_EINVAL: ++ return "Invalid argument."; + + /* Syntax errors */ + case SRS_ENOSRS0HOST: +@@ -146,6 +186,8 @@ + srs_new() + { + srs_t *srs = (srs_t *)srs_f_malloc(sizeof(srs_t)); ++ if (srs == NULL) ++ return NULL; + srs_init(srs); + return srs; + } +@@ -153,6 +195,8 @@ + void + srs_init(srs_t *srs) + { ++ if (srs == NULL) ++ return; + memset(srs, 0, sizeof(srs_t)); + srs->secrets = NULL; + srs->numsecrets = 0; +@@ -167,27 +211,57 @@ + srs_free(srs_t *srs) + { + int i; ++ ++ if (srs == NULL) ++ return; + for (i = 0; i < srs->numsecrets; i++) { +- memset(srs->secrets[i], 0, strlen(srs->secrets[i])); ++ if (srs->secrets[i] == NULL) ++ continue; ++ srs_clear(srs->secrets[i], strlen(srs->secrets[i])); + srs_f_free(srs->secrets[i]); +- srs->secrets[i] = '\0'; ++ srs->secrets[i] = NULL; + } ++ srs_f_free(srs->secrets); ++ srs_clear(srs, sizeof(*srs)); + srs_f_free(srs); + } + + int + srs_add_secret(srs_t *srs, const char *secret) + { +- int newlen = (srs->numsecrets + 1) * sizeof(char *); +- srs->secrets = (char **)srs_f_realloc(srs->secrets, newlen); +- srs->secrets[srs->numsecrets++] = strdup(secret); ++ char **newsecrets; ++ char *newsecret; ++ size_t secretlen; ++ size_t count; ++ ++ if (srs == NULL || secret == NULL || srs->numsecrets < 0) ++ return SRS_EINVAL; ++ count = (size_t)srs->numsecrets + 1; ++ if (count > SIZE_MAX / sizeof(*newsecrets)) ++ return SRS_ENOMEM; ++ secretlen = strlen(secret); ++ if (secretlen == SIZE_MAX) ++ return SRS_ENOMEM; ++ newsecret = srs_f_malloc(secretlen + 1); ++ if (newsecret == NULL) ++ return SRS_ENOMEM; ++ memcpy(newsecret, secret, secretlen + 1); ++ newsecrets = srs_f_realloc(srs->secrets, ++ count * sizeof(*newsecrets)); ++ if (newsecrets == NULL) { ++ srs_clear(newsecret, secretlen); ++ srs_f_free(newsecret); ++ return SRS_ENOMEM; ++ } ++ srs->secrets = newsecrets; ++ srs->secrets[srs->numsecrets++] = newsecret; + return SRS_SUCCESS; + } + + const char * + srs_get_secret(srs_t *srs, int idx) + { +- if (idx < srs->numsecrets) ++ if (srs != NULL && idx >= 0 && idx < srs->numsecrets) + return srs->secrets[idx]; + return NULL; + } +@@ -239,6 +313,7 @@ + int + srs_timestamp_create(srs_t *srs, char *buf, time_t now) + { ++ (void)srs; + now = now / SRS_TIME_PRECISION; + buf[1] = SRS_TIME_BASECHARS[now & ((1 << SRS_TIME_BASEBITS) - 1)]; + now = now >> SRS_TIME_BASEBITS; +@@ -251,7 +326,7 @@ + srs_timestamp_check(srs_t *srs, const char *stamp) + { + const char *sp; +- char *bp; ++ const char *bp; + int off; + time_t now; + time_t then; +@@ -259,7 +334,7 @@ + /* We had better go around this loop exactly twice! */ + then = 0; + for (sp = stamp; *sp; sp++) { +- bp = strchr(SRS_TIME_BASECHARS, toupper(*sp)); ++ bp = strchr(SRS_TIME_BASECHARS, toupper((unsigned char)*sp)); + if (bp == NULL) + return SRS_EBADTIMESTAMPCHAR; + off = bp - SRS_TIME_BASECHARS; +@@ -315,7 +390,7 @@ + lcdata = alloca(len + 1); + for (j = 0; j < len; j++) { + if (isupper(data[j])) +- lcdata[j] = tolower(data[j]); ++ lcdata[j] = tolower((unsigned char)data[j]); + else + lcdata[j] = data[j]; + } +@@ -424,7 +499,6 @@ + const char *aliashost) { + char *srshash; + char srsstamp[SRS_TIME_SIZE + 1]; +- int len; + int ret; + + /* This never happens if we get called from guarded() */ +@@ -438,14 +512,6 @@ + return SRS_ENOSRS0USER; + } + +- len = strlen(SRS0TAG) + 1 + +- srs->hashlength + 1 + +- SRS_TIME_SIZE + 1 + +- strlen(sendhost) + 1 + strlen(senduser) +- + 1 + strlen(aliashost); +- if (len >= buflen) +- return SRS_EBUFTOOSMALL; +- + ret = srs_timestamp_create(srs, srsstamp, time(NULL)); + if (ret != SRS_SUCCESS) + return ret; +@@ -454,12 +520,11 @@ + if (ret != SRS_SUCCESS) + return ret; + +- sprintf(buf, SRS0TAG "%c%s%c%s%c%s%c%s@%s", srs->separator, ++ return srs_format(buf, buflen, ++ SRS0TAG "%c%s%c%s%c%s%c%s@%s", srs->separator, + srshash, SRSSEP, srsstamp, SRSSEP, + sendhost, SRSSEP, senduser, + aliashost); +- +- return SRS_SUCCESS; + } + + int +@@ -470,7 +535,6 @@ + char *srshost; + char *srsuser; + char *srshash; +- int len; + int ret; + + if ((strncasecmp(senduser, SRS1TAG, 4) == 0) && +@@ -492,17 +556,11 @@ + ret = srs_hash_create(srs, srshash, 2, srshost, srsuser); + if (ret != SRS_SUCCESS) + return ret; +- len = strlen(SRS1TAG) + 1 + +- srs->hashlength + 1 + +- strlen(srshost) + 1 + strlen(srsuser) +- + 1 + strlen(aliashost); +- if (len >= buflen) +- return SRS_EBUFTOOSMALL; +- sprintf(buf, SRS1TAG "%c%s%c%s%c%s@%s", srs->separator, ++ return srs_format(buf, buflen, ++ SRS1TAG "%c%s%c%s%c%s@%s", srs->separator, + srshash, SRSSEP, + srshost, SRSSEP, srsuser, + aliashost); +- return SRS_SUCCESS; + } + else if ((strncasecmp(senduser, SRS0TAG, 4) == 0) && + (strchr(srs_separators, senduser[4]) != NULL)) { +@@ -512,13 +570,8 @@ + ret = srs_hash_create(srs, srshash, 2, srshost, srsuser); + if (ret != SRS_SUCCESS) + return ret; +- len = strlen(SRS1TAG) + 1 + +- srs->hashlength + 1 + +- strlen(srshost) + 1 + strlen(srsuser) +- + 1 + strlen(aliashost); +- if (len >= buflen) +- return SRS_EBUFTOOSMALL; +- sprintf(buf, SRS1TAG "%c%s%c%s%c%s@%s", srs->separator, ++ return srs_format(buf, buflen, ++ SRS1TAG "%c%s%c%s%c%s@%s", srs->separator, + srshash, SRSSEP, + srshost, SRSSEP, srsuser, + aliashost); +@@ -527,8 +580,6 @@ + return srs_compile_shortcut(srs, buf, buflen, + sendhost, senduser, aliashost); + } +- +- return SRS_SUCCESS; + } + + int +@@ -563,8 +614,7 @@ + srshost, srsuser); + if (ret != SRS_SUCCESS) + return ret; +- sprintf(buf, "%s@%s", srsuser, srshost); +- return SRS_SUCCESS; ++ return srs_format(buf, buflen, "%s@%s", srsuser, srshost); + } + + return SRS_ENOTSRSADDRESS; +@@ -593,8 +643,8 @@ + ret = srs_hash_check(srs, srshash, 2, srshost, srsuser); + if (ret != SRS_SUCCESS) + return ret; +- sprintf(buf, SRS0TAG "%s@%s", srsuser, srshost); +- return SRS_SUCCESS; ++ return srs_format(buf, buflen, SRS0TAG "%s@%s", srsuser, ++ srshost); + } + else { + return srs_parse_shortcut(srs, buf, buflen, senduser); +@@ -608,40 +658,52 @@ + char *senduser; + char *sendhost; + char *tmp; +- int len; ++ const char *at; ++ size_t len; ++ int ret; ++ ++ if (srs == NULL || buf == NULL || sender == NULL || alias == NULL || ++ buflen <= 0) ++ return SRS_EBUFTOOSMALL; + + if (srs->noforward) + return SRS_ENOTREWRITTEN; + + /* This is allowed to be a plain domain */ +- while ((tmp = strchr(alias, '@')) != NULL) +- alias = tmp + 1; ++ while ((at = strchr(alias, '@')) != NULL) ++ alias = at + 1; + +- tmp = strchr(sender, '@'); +- if (tmp == NULL) ++ at = strchr(sender, '@'); ++ if (at == NULL) + return SRS_ENOSENDERATSIGN; +- sendhost = tmp + 1; ++ sendhost = (char *)at + 1; + + len = strlen(sender); ++ if (len >= INT_MAX) ++ return SRS_EBUFTOOSMALL; + + if (! srs->alwaysrewrite) { + if (strcasecmp(sendhost, alias) == 0) { +- if (strlen(sender) >= buflen) ++ if (strlen(sender) >= (size_t)buflen) + return SRS_EBUFTOOSMALL; +- strcpy(buf, sender); ++ memcpy(buf, sender, strlen(sender) + 1); + return SRS_SUCCESS; + } + } + +- /* Reconstruct the whole show into our alloca() buffer. */ +- senduser = alloca(len + 1); +- strcpy(senduser, sender); +- tmp = (senduser + (tmp - sender)); ++ /* The sender is untrusted input; do not copy it to the process stack. */ ++ senduser = srs_f_malloc(len + 1); ++ if (senduser == NULL) ++ return SRS_ENOMEM; ++ memcpy(senduser, sender, len + 1); ++ tmp = senduser + (at - sender); + sendhost = tmp + 1; + *tmp = '\0'; + +- return srs_compile_guarded(srs, buf, buflen, ++ ret = srs_compile_guarded(srs, buf, buflen, + sendhost, senduser, alias); ++ srs_f_free(senduser); ++ return ret; + } + + int +@@ -649,10 +711,15 @@ + const char *sender, const char *alias) + { + char *buf; +- int slen; +- int alen; +- int len; ++ size_t slen; ++ size_t alen; ++ size_t len; + int ret; ++ size_t overhead; ++ ++ if (srs == NULL || sptr == NULL || sender == NULL || alias == NULL) ++ return SRS_EBUFTOOSMALL; ++ *sptr = NULL; + + if (srs->noforward) + return SRS_ENOTREWRITTEN; +@@ -661,10 +728,18 @@ + alen = strlen(alias); + + /* strlen(SRSxTAG) + strlen("====+@") < 64 */ +- len = slen + alen + srs->hashlength + SRS_TIME_SIZE + 64; ++ if (srs->hashlength < 0) ++ return SRS_EBUFTOOSMALL; ++ overhead = (size_t)srs->hashlength + SRS_TIME_SIZE + 64; ++ if (slen > (size_t)INT_MAX - overhead || ++ alen > (size_t)INT_MAX - overhead - slen) ++ return SRS_EBUFTOOSMALL; ++ len = slen + alen + overhead; + buf = (char *)srs_f_malloc(len); ++ if (buf == NULL) ++ return SRS_ENOMEM; + +- ret = srs_forward(srs, buf, len, sender, alias); ++ ret = srs_forward(srs, buf, (int)len, sender, alias); + + if (ret == SRS_SUCCESS) + *sptr = buf; +@@ -679,7 +754,11 @@ + { + char *senduser; + char *tmp; +- int len; ++ size_t len; ++ int ret; ++ ++ if (srs == NULL || buf == NULL || sender == NULL || buflen <= 0) ++ return SRS_EBUFTOOSMALL; + + if (!SRS_IS_SRS_ADDRESS(sender)) + return SRS_ENOTSRSADDRESS; +@@ -688,25 +767,31 @@ + return SRS_ENOTREWRITTEN; + + len = strlen(sender); +- if (len >= buflen) ++ if (len >= (size_t)buflen || len >= INT_MAX) + return SRS_EBUFTOOSMALL; +- senduser = alloca(len + 1); +- strcpy(senduser, sender); ++ senduser = srs_f_malloc(len + 1); ++ if (senduser == NULL) ++ return SRS_ENOMEM; ++ memcpy(senduser, sender, len + 1); + + /* We don't really care about the host for reversal. */ + tmp = strchr(senduser, '@'); + if (tmp != NULL) + *tmp = '\0'; +- return srs_parse_guarded(srs, buf, buflen, senduser); ++ ret = srs_parse_guarded(srs, buf, buflen, senduser); ++ srs_f_free(senduser); ++ return ret; + } + + int + srs_reverse_alloc(srs_t *srs, char **sptr, const char *sender) + { + char *buf; +- int len; ++ size_t len; + int ret; + ++ if (srs == NULL || sptr == NULL || sender == NULL) ++ return SRS_EBUFTOOSMALL; + *sptr = NULL; + + if (!SRS_IS_SRS_ADDRESS(sender)) +@@ -715,10 +800,15 @@ + if (srs->noreverse) + return SRS_ENOTREWRITTEN; + +- len = strlen(sender) + 1; ++ len = strlen(sender); ++ if (len >= INT_MAX) ++ return SRS_EBUFTOOSMALL; ++ len++; + buf = (char *)srs_f_malloc(len); ++ if (buf == NULL) ++ return SRS_ENOMEM; + +- ret = srs_reverse(srs, buf, len, sender); ++ ret = srs_reverse(srs, buf, (int)len, sender); + + if (ret == SRS_SUCCESS) + *sptr = buf; +--- a/libsrs2/srs2.h ++++ b/libsrs2/srs2.h +@@ -14,6 +14,15 @@ + #ifndef __SRS2_H__ + #define __SRS2_H__ + ++#include ++#include ++#ifndef _WIN32 ++#include ++#endif ++#include ++ ++#define SRS2_SECURE_FREE_SECRETS 1 ++ + #ifndef __BEGIN_DECLS + #define __BEGIN_DECLS + #define __END_DECLS +@@ -57,6 +66,8 @@ + + #define SRS_ENOSENDERATSIGN (SRS_ERRTYPE_INPUT | 1) + #define SRS_EBUFTOOSMALL (SRS_ERRTYPE_INPUT | 2) ++#define SRS_ENOMEM (SRS_ERRTYPE_INPUT | 3) ++#define SRS_EINVAL (SRS_ERRTYPE_INPUT | 4) + + #define SRS_ENOSRS0HOST (SRS_ERRTYPE_SYNTAX | 1) + #define SRS_ENOSRS0USER (SRS_ERRTYPE_SYNTAX | 2) +--- a/libsrs2/sha1.c ++++ b/libsrs2/sha1.c +@@ -114,63 +114,15 @@ + + dp = sha_info->data; + +-/* +-the following makes sure that at least one code block below is +-traversed or an error is reported, without the necessity for nested +-preprocessor if/else/endif blocks, which are a great pain in the +-nether regions of the anatomy... +-*/ +-#undef SWAP_DONE +- +-#if BYTEORDER == 0x1234 +-#define SWAP_DONE +- /* assert(sizeof(ULONG) == 4); */ +- for (i = 0; i < 16; ++i) { +- T = *((ULONG *) dp); +- dp += 4; +- W[i] = ((T << 24) & 0xff000000) | ((T << 8) & 0x00ff0000) | +- ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff); +- } +-#endif +- +-#if BYTEORDER == 0x4321 +-#define SWAP_DONE +- /* assert(sizeof(ULONG) == 4); */ ++ /* SHA-1 consumes big-endian 32-bit words. Assemble them byte by ++ * byte so this remains aligned and in bounds when ULONG is 64 bits. */ + for (i = 0; i < 16; ++i) { +- T = *((ULONG *) dp); ++ W[i] = ((ULONG)dp[0] << 24) | ++ ((ULONG)dp[1] << 16) | ++ ((ULONG)dp[2] << 8) | ++ (ULONG)dp[3]; + dp += 4; +- W[i] = T32(T); +- } +-#endif +- +-#if BYTEORDER == 0x12345678 +-#define SWAP_DONE +- /* assert(sizeof(ULONG) == 8); */ +- for (i = 0; i < 16; i += 2) { +- T = *((ULONG *) dp); +- dp += 8; +- W[i] = ((T << 24) & 0xff000000) | ((T << 8) & 0x00ff0000) | +- ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff); +- T >>= 32; +- W[i+1] = ((T << 24) & 0xff000000) | ((T << 8) & 0x00ff0000) | +- ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff); + } +-#endif +- +-#if BYTEORDER == 0x87654321 +-#define SWAP_DONE +- /* assert(sizeof(ULONG) == 8); */ +- for (i = 0; i < 16; i += 2) { +- T = *((ULONG *) dp); +- dp += 8; +- W[i] = T32(T >> 32); +- W[i+1] = T32(T); +- } +-#endif +- +-#ifndef SWAP_DONE +-#error Unknown byte order -- you need to add code here +-#endif /* SWAP_DONE */ + + for (i = 16; i < 80; ++i) { + W[i] = W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]; +@@ -240,7 +192,7 @@ + /* update the SHA digest */ + + static void +-sha_update(SHA_INFO *sha_info, sha_byte *buffer, int count) ++sha_update(SHA_INFO *sha_info, const sha_byte *buffer, int count) + { + int i; + ULONG clo; +@@ -353,7 +305,7 @@ + */ + + static void +-sha_digest(char *out, char *data, int len) ++sha_digest(unsigned char *out, const unsigned char *data, int len) + { + SHA_INFO ctx; + sha_init(&ctx); +@@ -364,13 +316,13 @@ + void + srs_hmac_init(srs_hmac_ctx_t *ctx, char *secret, int len) + { +- char sbuf[SHA_BLOCKSIZE]; ++ unsigned char sbuf[SHA_BLOCKSIZE]; + int i; + + if (len > SHA_BLOCKSIZE) { +- sha_digest(sbuf, secret, len); +- secret = sbuf; +- len = strlen(sbuf); /* SHA_BLOCKSIZE? */ ++ sha_digest(sbuf, (const unsigned char *)secret, len); ++ secret = (char *)sbuf; ++ len = SHA_DIGESTSIZE; + } + + memset(ctx->ipad, 0x36, SHA_BLOCKSIZE); +@@ -383,23 +335,23 @@ + memset(sbuf, 0, SHA_BLOCKSIZE); + + sha_init(&ctx->sctx); +- sha_update(&ctx->sctx, ctx->ipad, SHA_BLOCKSIZE); ++ sha_update(&ctx->sctx, (const unsigned char *)ctx->ipad, SHA_BLOCKSIZE); + } + + void + srs_hmac_update(srs_hmac_ctx_t *ctx, char *data, int len) + { +- sha_update(&ctx->sctx, data, len); ++ sha_update(&ctx->sctx, (const unsigned char *)data, len); + } + + void + srs_hmac_fini(srs_hmac_ctx_t *ctx, char *out) + { +- char buf[SHA_DIGESTSIZE + 1]; ++ unsigned char buf[SHA_DIGESTSIZE]; + + sha_final(buf, &ctx->sctx); + sha_init(&ctx->sctx); +- sha_update(&ctx->sctx, ctx->opad, SHA_BLOCKSIZE); ++ sha_update(&ctx->sctx, (const unsigned char *)ctx->opad, SHA_BLOCKSIZE); + sha_update(&ctx->sctx, buf, SHA_DIGESTSIZE); +- sha_final(out, &ctx->sctx); ++ sha_final((unsigned char *)out, &ctx->sctx); + } +--- a/tools/srs.c ++++ b/tools/srs.c +@@ -133,6 +133,27 @@ + } + + static int ++write_all(int fd, const char *buf, size_t len) ++{ ++ while (len > 0) { ++ ssize_t written = write(fd, buf, len); ++ ++ if (written < 0) { ++ if (errno == EINTR) ++ continue; ++ return -1; ++ } ++ if (written == 0) { ++ errno = EIO; ++ return -1; ++ } ++ buf += written; ++ len -= (size_t)written; ++ } ++ return 0; ++} ++ ++static int + listen_socket(char *path) + { + struct sockaddr_un addr; +@@ -144,7 +165,12 @@ + } + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; +- strcpy(addr.sun_path, path); ++ if (strlen(path) >= sizeof(addr.sun_path)) { ++ errno = ENAMETOOLONG; ++ perror("socket path"); ++ SRS_DIE(); ++ } ++ memcpy(addr.sun_path, path, strlen(path) + 1); + if (unlink(path) < 0) { + if (errno != ENOENT) { + perror("unlink"); +@@ -174,8 +200,9 @@ + int sock; + int maxfd; + int fd; +- char line[BUFSIZ]; +- int len; ++ int client_fd; ++ char line[BUFSIZ + 1]; ++ ssize_t len; + char buf[BUFSIZ]; + char *cp; + char *address; +@@ -190,8 +217,8 @@ + FD_SET(sock, &readfds_sv); + maxfd = sock + 1; + +-#define FINISH(fd) close(fd); FD_CLR(fd, &readfds_sv) +-#define SKIPWHITE(cp) while (isspace(*cp)) cp++; ++#define FINISH(fd) do { close(fd); FD_CLR(fd, &readfds_sv); } while (0) ++#define SKIPWHITE(cp) while (isspace((unsigned char)*(cp))) (cp)++; + + if (!(daemonflags & DF_NOFORK)) { + if (daemon(0, 0) < 0) +@@ -201,19 +228,31 @@ + for (;;) { + memcpy(&readfds, &readfds_sv, sizeof(fd_set)); + +- select(maxfd, &readfds, NULL, NULL, NULL); ++ ret = select(maxfd, &readfds, NULL, NULL, NULL); ++ if (ret < 0) { ++ if (errno == EINTR) ++ continue; ++ perror("select"); ++ break; ++ } + for (fd = 0; fd < maxfd; fd++) { + if (FD_ISSET(fd, &readfds)) { + if (fd == sock) { +- fd = accept(sock, (struct sockaddr*)&addr,&addrlen); +- if (fd < 0) { ++ addrlen = sizeof(addr); ++ client_fd = accept(sock, (struct sockaddr *)&addr, ++ &addrlen); ++ if (client_fd < 0) { + perror("accept"); + continue; + } +- FD_SET(fd, &readfds_sv); +- if (fd >= maxfd) +- maxfd = fd + 1; +- fprintf(stderr, "Accept %d\n", fd); ++ if (client_fd >= FD_SETSIZE) { ++ close(client_fd); ++ continue; ++ } ++ FD_SET(client_fd, &readfds_sv); ++ if (client_fd >= maxfd) ++ maxfd = client_fd + 1; ++ fprintf(stderr, "Accept %d\n", client_fd); + continue; + } + else { +@@ -225,12 +264,13 @@ + FINISH(fd); + continue; + } ++ line[len] = '\0'; + line[strcspn(line, "\r\n")] = '\0'; + fprintf(stderr, "%d: %s\n", fd, line); + + if (strncasecmp(line, "forward ", 8) == 0) { + cp = line + 8; +- while (isspace(*cp)) ++ while (isspace((unsigned char)*cp)) + cp++; + address = cp; + cp = strchr(address, ' '); +@@ -241,7 +281,7 @@ + continue; + } + *cp++ = '\0'; +- while (isspace(*cp)) ++ while (isspace((unsigned char)*cp)) + cp++; + alias = cp; + ret = srs_forward(srs, buf, BUFSIZ, +@@ -254,14 +294,15 @@ + } + fprintf(stderr, "Forward %s, %s -> %s\n", + address, alias, buf); +- write(fd, buf, strlen(buf)); +- write(fd, "\n", 1); ++ if (write_all(fd, buf, strlen(buf)) < 0 || ++ write_all(fd, "\n", 1) < 0) ++ perror("write"); + FINISH(fd); + continue; + } + else if (strncasecmp(line, "reverse ", 8) == 0) { + cp = line + 8; +- while (isspace(*cp)) ++ while (isspace((unsigned char)*cp)) + cp++; + address = cp; + ret = srs_reverse(srs, buf, BUFSIZ, address); +@@ -273,8 +314,9 @@ + } + fprintf(stderr, "Reverse %s -> %s\n", + address, buf); +- write(fd, buf, strlen(buf)); +- write(fd, "\n", 1); ++ if (write_all(fd, buf, strlen(buf)) < 0 || ++ write_all(fd, "\n", 1) < 0) ++ perror("write"); + FINISH(fd); + continue; + } diff --git a/mail-filter/libsrs2/libsrs2-1.0.18-r2.ebuild b/mail-filter/libsrs2/libsrs2-1.0.18-r2.ebuild new file mode 100644 index 0000000..6ed8a34 --- /dev/null +++ b/mail-filter/libsrs2/libsrs2-1.0.18-r2.ebuild @@ -0,0 +1,41 @@ +# Copyright 1999-2020 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=7 + +inherit autotools + +DESCRIPTION="libsrs2 is the next generation Sender Rewriting Scheme library" +HOMEPAGE="https://www.libsrs2.org/" +SRC_URI="https://www.libsrs2.org/srs/${P}.tar.gz" + +LICENSE="GPL-2" +SLOT="0" +KEYWORDS="amd64 ppc x86" +IUSE="static-libs" + +DEPEND=" + !dev-perl/Mail-SRS + !mail-filter/libsrs_alt +" +RDEPEND="${DEPEND}" + +PATCHES=( + "${FILESDIR}/${P}-parallel-make.diff" + "${FILESDIR}/${P}-security-hardening.patch" +) + +src_prepare() { + default + eautoreconf +} + +src_configure() { + econf \ + $(use_enable static-libs static) +} + +src_install() { + default + use static-libs || find "${ED}" -name '*.la' -delete +} diff --git a/mail-filter/libsrs2/metadata.xml b/mail-filter/libsrs2/metadata.xml new file mode 100644 index 0000000..2609d95 --- /dev/null +++ b/mail-filter/libsrs2/metadata.xml @@ -0,0 +1,14 @@ + + + + + + libsrs2 is the next generation SRS library from the original designer of + SRS. It implements the Sender Rewriting Scheme, a part of the SPF/SRS + protocol pair. Libsrs2 has been written from an entirely clean codebase with + compliance, speed and versatility in mind. It is platform independent and + has no external dependencies. It is thread-safe and heap-safe, and is + suitable for large scale applications and embedded systems and can operate + without many standard system facilities. + + diff --git a/mail-mta/bongo/Manifest b/mail-mta/bongo/Manifest index 9e46891..a1a6d98 100644 --- a/mail-mta/bongo/Manifest +++ b/mail-mta/bongo/Manifest @@ -11,6 +11,6 @@ AUX bongo.init 524 BLAKE2B 832c5240553de08f2aa8ccb2cd4a1f517202357ef918f9331c1ef AUX mailer.conf 301 BLAKE2B 6c285d7a4acc2fd73990d112c95604dd1bd4ac28c9e296767f2521dc3b1b92c765a5972c51de46d70110aa723b395759289a587d0fd88d7a2aa1a8a9911be397 SHA512 ee02584838312730277150605206c6963035e958f7c2c3327d882cbadc343a21fd2fa2ddd9af43021cc6573f27080f2dd574c9aca6b294efa3d720966751cf47 DIST bongo-0.6.1.tar.gz 2488587 BLAKE2B fdb3596a751e8bc859796019f144b47b23e23e85c265b91d5b34c88da3171d23b90b313a202ecd53c9226490fbc0458d122af1520ef0e23f0b5fcc42fdfb9932 SHA512 27aadcc8cb45a5541c343b8c0871e35d1555f2170e68edebd9a13ccc9c5beeab4c32817c569950d4cf28f6f2f11dbe0a6d7d31172c15cef548c8ac471fe54aac EBUILD bongo-0.6.1.ebuild 2258 BLAKE2B 642e63ce229d0c14005fce1f50bbc85869e8226fb298531dfc728500f805aeab499862634ad0bd25d9497857ef498bdb33951fbad6fda69cadfbe1b220446972 SHA512 b736649b12389afc10c60e13677704fe73eb3fe4bd3070091aabd99e05088ecf1522c1d307c078ae4e92cb5069edb11a811294254b4d46d143fbed040c23a603 -EBUILD bongo-9999.ebuild 3762 BLAKE2B 86c5f6b8aa208b143da1de4a4c7e4c8422af9fdb71eb31dc232f6f23eab9eacb15e70bbe72aa9fc0e6c680821fdb1997e474014efa1871cd3e1f25adeaf8571d SHA512 c75c0dc515d1ccbdf2645829b7f51e75b6288715e44a47325e8868792e26347af79fe91e22b97193828dfabd071c223efe04ad7131e4098b7403032dacbe7996 +EBUILD bongo-9999.ebuild 3815 BLAKE2B a82af35a1e9ace165fa049297166bc88015bcbcb84754f6cb61d57f1ed21aa7b203f2d9c9afad4f385cb541caf5268c199ae15e9336213551958429708d885cb SHA512 17cc90588a52670b13a77301fa42aaec6f0eaf4276eda95ff3ef61246d2c84b514eae57afa5711763767492e3124ae3694efe7572b49e6ca149baea660d649c1 MISC ChangeLog 1707 BLAKE2B 9668b8a388b560b09f0f49ee8c14e1d67690b0ae98fb5eafe257ee72f25b065c344284f40d5719b9c111e74d6ed578406447f0d15af0347f4e6472f50f361ba4 SHA512 9f1fd1ca681cc63b09dabd9cb285d1eb2b564cb79c3e2c1d0b18af8092040dd8bf67e37b69da8557ee5cf1b4bdabd64336951f1dc67e9c4ac97acb5790f312db MISC metadata.xml 818 BLAKE2B 956e4e155b231625b9e306587644ac9d0e29aa41a9e00ebe8d463fbb9a1baf1098ccb169816aedc0eb1c24484166a80df43d02cc7b7e16fb6356569ac6a83913 SHA512 27b9a939e45c151aca4ccad5c536bf369892ebff76ca99ab959c24a72f42c3de90085aa774cd27db02d72a77cde91bbce77db54cc6cdb7e86cc04f58f4bd8cf9 diff --git a/mail-mta/bongo/bongo-9999.ebuild b/mail-mta/bongo/bongo-9999.ebuild index d12bb62..093b880 100644 --- a/mail-mta/bongo/bongo-9999.ebuild +++ b/mail-mta/bongo/bongo-9999.ebuild @@ -41,6 +41,7 @@ DEPEND=" mail-filter/opendmarc media-gfx/qrencode net-libs/gnutls:= + net-dns/libidn2 >=net-libs/ldns-1.8.0 net-libs/libpsl net-mail/mailutils @@ -49,6 +50,7 @@ DEPEND=" sys-auth/oath-toolkit[-pam] $(python_gen_cond_dep ' dev-python/dnspython[${PYTHON_USEDEP}] + dev-python/idna[${PYTHON_USEDEP}] dev-python/nh3[${PYTHON_USEDEP}] dev-python/python-dateutil[${PYTHON_USEDEP}] dev-python/vobject[${PYTHON_USEDEP}]