1 /* $Id: milter-greylist.h,v 1.100 2017/07/11 03:17:33 manu Exp $ */ 2 3 /* 4 * Copyright (c) 2004-2012 Emmanuel Dreyfus 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Emmanuel Dreyfus 18 * 19 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 20 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 29 * OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #ifndef _MILTER_GREYLIST_H_ 33 #define _MILTER_GREYLIST_H_ 34 35 #include <sys/socket.h> 36 #include <netinet/in.h> 37 #include <arpa/inet.h> 38 #include <netdb.h> 39 40 #include <libmilter/mfapi.h> 41 #include "config.h" 42 #include "dump.h" 43 44 #ifdef USE_DKIM 45 #ifdef HAVE_STDBOOL_H 46 #include <stdbool.h> 47 #endif 48 #include <dkim.h> 49 #endif 50 51 /* environment of Solaris workaround for stdio descriptor limitation */ 52 #include "fd_pool.h" 53 54 #define NUMLEN 20 55 #define QSTRLEN 4096 56 #define REGEXLEN 1024 57 #define HDRLEN 1024 58 #define HEADERNAME "X-Greylist" 59 /* 60 * Maximum URL length. This is just a hint, 61 * the code will adjust the buffer if needed. 62 */ 63 #define URLMAXLEN 2083 64 65 LIST_HEAD(urlchecklist, urlcheck_entry); 66 67 68 69 #if defined(HAVE_GETNAMEINFO) 70 #define IPADDRSTRLEN NI_MAXHOST 71 #elif defined(INET6_ADDRSTRLEN) 72 #define IPADDRSTRLEN INET6_ADDRSTRLEN 73 #else 74 #define IPADDRSTRLEN IPADDRLEN 75 #endif 76 77 typedef union { 78 struct in_addr in4; 79 #ifdef AF_INET6 80 struct in6_addr in6; 81 #endif 82 } ipaddr; 83 84 typedef union { 85 struct sockaddr sa; 86 struct sockaddr_in sin; 87 #ifdef AF_INET6 88 struct sockaddr_in6 sin6; 89 #endif 90 } sockaddr_t; 91 92 #define SA(sa) ((struct sockaddr *)(sa)) 93 #define SA4(sa) ((struct sockaddr_in *)(sa)) 94 #define SADDR4(sa) (&SA4(sa)->sin_addr) 95 #ifdef AF_INET6 96 #define SA6(sa) ((struct sockaddr_in6 *)(sa)) 97 #define SADDR6(sa) (&SA6(sa)->sin6_addr) 98 #endif 99 100 /* Notes: 101 * -For IPv6 not using s6_addr32 as Solaris 8 for some reason has it only 102 * defined for its kernel... 103 * -Using also first two characters in "from" and "rcpt" to distribute 104 * potentially lot of triplets coming from a single host (first two chars 105 * only because "<>" is the "shortest" email address) 106 */ 107 #define F2B(s) (tolower((int)*(s)) | (tolower((int)*((s)+1)) << 8)) 108 #define F2B_SPICE(from, rcpt) (conf.c_lazyaw ? 0 : (F2B(from) ^ F2B(rcpt))) 109 110 #define BUCKET_HASH_V4(v4a, v4m, from, rcpt, bucket_count) \ 111 ((ntohl((v4a)->s_addr & (v4m)->s_addr) \ 112 ^ F2B_SPICE(from, rcpt)) \ 113 % bucket_count) 114 115 #ifdef AF_INET6 116 #define IN6CAST32(_a) ((uint32_t *)(&(_a)->s6_addr)) 117 118 #define BUCKET_HASH_V6(v6a, v6m, from, rcpt, bucket_count) \ 119 ((ntohl(IN6CAST32(v6a)[0] & IN6CAST32(v6m)[0]) ^ \ 120 ntohl(IN6CAST32(v6a)[1] & IN6CAST32(v6m)[1]) ^ \ 121 ntohl(IN6CAST32(v6a)[2] & IN6CAST32(v6m)[2]) ^ \ 122 ntohl(IN6CAST32(v6a)[3] & IN6CAST32(v6m)[3]) \ 123 ^ F2B_SPICE(from, rcpt)) \ 124 % bucket_count) 125 126 #define BUCKET_HASH(sa, from, rcpt, bucket_count) \ 127 (sa->sa_family == AF_INET ? \ 128 BUCKET_HASH_V4(SADDR4(sa), \ 129 &conf.c_match_mask, \ 130 from, rcpt, bucket_count) \ 131 : sa->sa_family == AF_INET6 ? \ 132 BUCKET_HASH_V6(SADDR6(sa), \ 133 &conf.c_match_mask6, \ 134 from, rcpt, bucket_count) \ 135 : 0) 136 137 #else /* AF_INET6 */ 138 139 #define BUCKET_HASH(sa, from, rcpt, bucket_count) \ 140 (sa->sa_family == AF_INET ? \ 141 BUCKET_HASH_V4(SADDR4(sa), \ 142 &conf.c_match_mask, \ 143 from, rcpt, bucket_count) \ 144 : 0) 145 146 #endif 147 148 typedef enum { 149 TAP_SESSION, 150 TAP_COMMAND 151 } tarpit_scope_t; 152 153 struct smtp_reply { 154 long long sr_whitelist; 155 int sr_nowhitelist; 156 time_t sr_elapsed; 157 time_t sr_remaining; 158 int sr_acl_line; 159 char *sr_acl_id; 160 time_t sr_delay; 161 time_t sr_autowhite; 162 time_t sr_tarpit; 163 tarpit_scope_t sr_tarpit_scope; 164 char *sr_code; 165 char *sr_ecode; 166 char *sr_msg; 167 char *sr_msg_x; 168 char *sr_report; 169 char *sr_report_x; 170 char *sr_addheader; 171 int sr_addheader_index; 172 char *sr_addfooter; 173 char *sr_subjtag; 174 sfsistat sr_retcode; 175 int sr_nmatch; 176 char **sr_pmatch; 177 }; 178 179 struct rcpt { 180 char r_addr[ADDRLEN + 1]; 181 LIST_ENTRY(rcpt) r_list; 182 }; 183 184 struct line { 185 char *l_line; 186 size_t l_len; 187 TAILQ_ENTRY(line) l_list; 188 }; 189 190 TAILQ_HEAD(bh_line, line); 191 192 struct mlfi_priv { 193 SMFICTX *priv_ctx; 194 sockaddr_t priv_addr; 195 socklen_t priv_addrlen; 196 char priv_hostname[ADDRLEN + 1]; 197 char priv_helo[ADDRLEN + 1]; 198 char priv_from[ADDRLEN + 1]; 199 char priv_rawfrom[ADDRLEN + 1]; 200 LIST_HEAD(, rcpt) priv_rcpt; 201 char priv_cur_rcpt[ADDRLEN + 1]; 202 int priv_rcptcount; 203 struct bh_line priv_header; 204 struct bh_line priv_body; 205 size_t priv_maxpeek; 206 #if defined(USE_GEOIP) || defined(USE_GEOIP2) 207 const char *priv_ccode; 208 #endif 209 size_t priv_msgcount; 210 size_t priv_peekcount; 211 int priv_peekdone; 212 char *priv_buf; 213 size_t priv_buflen; 214 char *priv_queueid; 215 int priv_delayed_reject; 216 struct smtp_reply priv_sr; 217 time_t priv_max_elapsed; 218 long long priv_last_whitelist; 219 LIST_HEAD(, prop) priv_prop; 220 struct prop *priv_prop_match; 221 #ifdef USE_DNSRBL 222 LIST_HEAD(, dnsrbl_list) priv_dnsrbl; 223 #endif 224 #ifdef HAVE_SPF2 225 char *priv_spf_header; 226 #endif 227 #if (defined(HAVE_SPF) || defined(HAVE_SPF_ALT) || \ 228 defined(HAVE_SPF2_10) || defined(HAVE_SPF2)) 229 char priv_spf_result[10]; 230 #endif 231 #ifdef USE_DKIM 232 DKIM *priv_dkim; 233 DKIM_STAT priv_dkimstat; 234 char priv_dkim_result[10]; 235 #endif 236 #ifdef USE_P0F 237 char *priv_p0f; 238 #endif 239 #ifdef USE_SPAMD 240 int priv_spamd_flags; 241 int priv_spamd_score10; 242 #endif 243 time_t priv_max_tarpitted; 244 time_t priv_total_tarpitted; 245 struct acl_entry *priv_acl; /* current ACL being evaluated */ 246 }; 247 248 sfsistat mlfi_connect(SMFICTX *, char *, _SOCK_ADDR *); 249 sfsistat mlfi_helo(SMFICTX *, char *); 250 sfsistat mlfi_envfrom(SMFICTX *, char **); 251 sfsistat mlfi_envrcpt(SMFICTX *, char **); 252 sfsistat mlfi_header(SMFICTX *, char *, char *); 253 sfsistat mlfi_eoh(SMFICTX *); 254 sfsistat mlfi_body(SMFICTX *, unsigned char *, size_t); 255 sfsistat mlfi_eom(SMFICTX *); 256 sfsistat mlfi_abort(SMFICTX *); 257 sfsistat mlfi_close(SMFICTX *); 258 #ifdef HAVE_DATA_CALLBACK 259 sfsistat mlfi_data(SMFICTX *); 260 #endif 261 void usage(char *); 262 int humanized_atoi(char *); 263 char *local_ipstr(struct mlfi_priv *); 264 struct in_addr *prefix2mask4(int, struct in_addr *); 265 #ifdef AF_INET6 266 struct in6_addr *prefix2mask6(int, struct in6_addr *); 267 #endif 268 void unmappedaddr(struct sockaddr *, socklen_t *); 269 void final_dump(void); 270 int main(int, char **); 271 void mg_log(int, char *, ...); 272 char *strncpy_rmsp(char *, char *, size_t); 273 char *fstring_expand(struct mlfi_priv *, 274 char *, const char *, char *(*)(char *)); 275 char *fstring_escape(char *); 276 void mkparentdir(char *, mode_t); 277 278 #ifdef HAVE_STRLCAT 279 /* #include <string.h> */ 280 #define mystrlcat strlcat 281 #else 282 size_t mystrlcat(char *, const char *src, size_t size); 283 #endif 284 285 /* 286 * Locking management 287 */ 288 #define WRLOCK(lock) { \ 289 int err; \ 290 \ 291 if ((err = pthread_rwlock_wrlock(&(lock))) != 0) { \ 292 syslog(LOG_ERR, "%s:%d pthread_rwlock_wrlock failed: %s", \ 293 __FILE__, __LINE__, strerror(err)); \ 294 exit(EX_SOFTWARE); \ 295 } \ 296 } 297 298 #define RDLOCK(lock) { \ 299 int err; \ 300 \ 301 if ((err = pthread_rwlock_rdlock(&(lock))) != 0) { \ 302 syslog(LOG_ERR, "%s:%d pthread_rwlock_rdlock failed: %s", \ 303 __FILE__, __LINE__, strerror(err)); \ 304 exit(EX_SOFTWARE); \ 305 } \ 306 } 307 308 #define TSS_SET(key, val) do { \ 309 int err; \ 310 \ 311 if ((err = pthread_setspecific(key, val)) != 0) { \ 312 mg_log(LOG_ERR, "%s:%d pthread_setspecific failed: %s", \ 313 __FILE__, __LINE__, strerror(err)); \ 314 exit(EX_SOFTWARE); \ 315 } \ 316 } while (/*CONSTCOND*/ 0) 317 318 /* 319 * There is a bug in GNU pth-2.0.0 that will cause a spurious EPERM 320 * error when a thread releases a read lock that has been shared by 321 * two threads and already released by the other one. As a workaround 322 * for that problem, we just avoid quitting on this error. 323 */ 324 #ifndef HAVE_BROKEN_RWLOCK 325 #define UNLOCK(lock) { \ 326 int err; \ 327 \ 328 if ((err = pthread_rwlock_unlock(&(lock))) != 0) { \ 329 syslog(LOG_ERR, "%s:%d pthread_rwlock_unlock failed: %s", \ 330 __FILE__, __LINE__, strerror(err)); \ 331 exit(EX_SOFTWARE); \ 332 } \ 333 } 334 #else 335 #define UNLOCK(lock) { \ 336 int err; \ 337 \ 338 if ((err = pthread_rwlock_unlock(&(lock))) != 0) { \ 339 syslog(LOG_DEBUG, "%s:%d pthread_rwlock_unlock failed: " \ 340 "%s (ignored)", __FILE__, __LINE__, strerror(err)); \ 341 } \ 342 } 343 #endif 344 345 #ifdef HAVE_MISSING_TIMERADD 346 #define timeradd(tvp, uvp, vvp) \ 347 do { \ 348 (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ 349 (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ 350 if ((vvp)->tv_usec >= 1000000) { \ 351 (vvp)->tv_sec++; \ 352 (vvp)->tv_usec -= 1000000; \ 353 } \ 354 } while (/* CONSTCOND */ 0) 355 #define timersub(tvp, uvp, vvp) \ 356 do { \ 357 (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ 358 (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ 359 if ((vvp)->tv_usec < 0) { \ 360 (vvp)->tv_sec--; \ 361 (vvp)->tv_usec += 1000000; \ 362 } \ 363 } while (/* CONSTCOND */ 0) 364 #endif 365 366 #define ADD_REASON(whystr, reason) \ 367 { \ 368 if (whystr[0] != '\0') \ 369 mystrlcat(whystr, ", ", sizeof(whystr)); \ 370 mystrlcat(whystr, reason, sizeof(whystr)); \ 371 } 372 373 /* 374 * Due to race conditions in the libmilter shipped with sendmail <= 8.13.8, 375 * the whole process may die after receiving a signal. 376 * It makes impossible the final dump. Apply the following patch ASAP: 377 * http://www.j10n.org/files/libmilter-8.13.8-signal.patch 378 * 379 * If you don't want to apply it, the following knob enables an uncertain 380 * effort to workaround the bug. Do not ask me about this. 381 * 382 */ 383 /* #define WORKAROUND_LIBMILTER_RACE_CONDITION */ 384 385 /* 386 * We may want to check SOCK_CLOEXEC as well for close-on-exec 387 * availlability for sockets. On the other hand, if the 388 * functionnality is not there, fcntl(F_SETFD) will just 389 * silently fail. 390 */ 391 #ifdef FD_CLOEXEC 392 #define SET_CLOEXEC(fd) do { \ 393 int flags = fcntl((fd), F_GETFD, 0); \ 394 \ 395 if (flags != -1) \ 396 (void)fcntl(fd, F_SETFD, flags|FD_CLOEXEC); \ 397 } while (0 /* CONSTCOND */) 398 #else /* FD_CLOEXEC */ 399 #define SET_CLOEXEC(fd) 400 #endif /* FD_CLOEXEC */ 401 402 #endif /* _MILTER_GREYLIST_H_ */ 403