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