1 /*
2 **  Copyright (c) 2007-2009 Sendmail, Inc. and its suppliers.
3 **	All rights reserved.
4 **  Sendmail, Inc. Confidential
5 **
6 **  $Id: batv-filter.c,v 1.67 2009/05/27 16:16:45 msk Exp $
7 */
8 
9 #ifndef lint
10 static char batv_filter_c_id[] = "@(#)$Id: batv-filter.c,v 1.67 2009/05/27 16:16:45 msk Exp $";
11 #endif /* !lint */
12 
13 /* system includes */
14 #include <sys/param.h>
15 #include <sys/types.h>
16 #include <sys/wait.h>
17 #include <sys/socket.h>
18 #include <sys/stat.h>
19 #ifdef __linux__
20 # include <sys/prctl.h>
21 #endif /* __linux__ */
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include <syslog.h>
28 #include <errno.h>
29 #include <sysexits.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <signal.h>
33 #include <assert.h>
34 #include <regex.h>
35 #include <ctype.h>
36 #include <time.h>
37 #include <pwd.h>
38 #ifdef DEBUG
39 # include <netdb.h>
40 #endif /* DEBUG */
41 #ifndef SOLARIS
42 # include <paths.h>
43 #else /* SOLARIS */
44 # if (SOLARIS >= 21000)
45 #  include <fenv.h>
46 # else /* (SOLARIS >= 21000) */
47 #  include <floatingpoint.h>
48 # endif /* (SOLARIS >= 21000) */
49 # include <iso/limits_iso.h>
50 #endif /* ! SOLARIS */
51 
52 /* openssl includes */
53 #include <openssl/sha.h>
54 
55 /* libsm includes */
56 #ifdef WITHOUT_SMSTRING
57 # define sm_strlcat     strlcat
58 # define sm_strlcpy     strlcpy
59 #else /* WITHOUT_SMSTRING */
60 # include <sm/string.h>
61 #endif /* WITHOUT_SMSTRING */
62 
63 /* libmilter includes */
64 #ifndef DEBUG
65 #include "libmilter/mfapi.h"
66 #endif /* !DEBUG */
67 
68 /* batv-filter includes */
69 #include "batv-filter.h"
70 #include "config.h"
71 #include "batv-config.h"
72 #include "util.h"
73 
74 /* macros */
75 #ifndef MIN
76 # define MIN(x,y)	((x) < (y) ? (x) : (y))
77 #endif /* ! MIN */
78 
79 #ifdef SOLARIS
80 # ifndef INADDR_NONE
81 #  define INADDR_NONE	(in_addr_t)(-1)
82 # endif /* INADDR_NONE */
83 #endif /* SOLARIS */
84 
85 #define BATV_MODE_SIGN		0x01
86 #define	BATV_MODE_VERIFY	0x02
87 #define BATV_MODE_DEFAULT	(BATV_MODE_SIGN | BATV_MODE_VERIFY)
88 
89 #ifdef DEBUG
90 /* DEBUGGING STUFF */
91 # define MI_SUCCESS	1
92 # define MI_FAILURE	(-1)
93 # define SMFIS_CONTINUE	0
94 # define SMFIS_ACCEPT	1
95 # define SMFIS_REJECT	2
96 # define SMFIS_DISCARD	3
97 # define SMFIS_TEMPFAIL	4
98 # define SMFIF_NONE	0x00000000L
99 # define SMFIF_ADDHDRS	0x00000001L
100 # define SMFIF_CHGBODY	0x00000002L
101 # define SMFIF_MODBODY	SMFIF_CHGBODY
102 # define SMFIF_ADDRCPT	0x00000004L
103 # define SMFIF_DELRCPT	0x00000008L
104 # define SMFIF_CHGHDRS	0x00000010L
105 # define SMFIF_QUARANTINE 0x00000020L
106 # define SMFIF_CHGFROM	0x00000040L
107 # define SMFIF_ADDRCPT_PAR	0x00000080L
108 # define SMFIF_SETSYMLIST	0x00000100L
109 # define SMFIP_NOCONNECT 0x00000001L
110 # define SMFIP_NOHELO	0x00000002L
111 # define SMFIP_NOMAIL	0x00000004L
112 # define SMFIP_NORCPT	0x00000008L
113 # define SMFIP_NOBODY	0x00000010L
114 # define SMFIP_NOHDRS	0x00000020L
115 # define SMFIP_NOEOH	0x00000040L
116 # define SMFIP_NR_HDR	0x00000080L
117 # define SMFIP_NOHREPL	SMFIP_NR_HDR
118 # define SMFIP_NOUNKNOWN 0x00000100L
119 # define SMFIP_NODATA    0x00000200L
120 # define SMFIP_SKIP	0x00000400L
121 # define SMFIP_RCPT_REJ	0x00000800L
122 # define SMFIP_NR_CONN	0x00001000L
123 # define SMFIP_NR_HELO	0x00002000L
124 # define SMFIP_NR_MAIL	0x00004000L
125 # define SMFIP_NR_RCPT	0x00008000L
126 # define SMFIP_NR_DATA	0x00010000L
127 # define SMFIP_NR_UNKN	0x00020000L
128 # define SMFIP_NR_EOH	0x00040000L
129 # define SMFIP_NR_BODY	0x00080000L
130 # define SMFIP_HDR_LEADSPC 0x00100000L
131 # define sfsistat	int
132 # define SMFICTX	void
133 # define _SOCK_ADDR	struct sockaddr
134 
135 int smfi_insheader __P((void *, int, char *, char *));
136 int smfi_chgfrom __P((void *, char *, char *));
137 void *smfi_getpriv __P((void *));
138 char *smfi_getsymval __P((void *, char *));
139 int smfi_opensocket __P((int));
140 int smfi_quarantine __P((void *, char *));
141 void smfi_setconn __P((char *));
142 void smfi_setdbg __P((int));
143 void smfi_setpriv __P((void *, void *));
144 int smfi_setreply __P((void *, char *, char *, char *));
145 
146 char *smfis_ret[] = {
147 	"SMFIS_CONTINUE",
148 	"SMFIS_ACCEPT",
149 	"SMFIS_REJECT",
150 	"SMFIS_DISCARD",
151 	"SMFIS_TEMPFAIL",
152 };
153 
154 static void *fakepriv;				/* fake private space */
155 #endif /* DEBUG */
156 
157 /*
158 **  RCPTLIST -- recipient list (old and new)
159 */
160 
161 struct batv_rcptlist
162 {
163 	bool		rcpt_signed;		/* signature present */
164 	bool		rcpt_verified;		/* signature verified */
165 	bool		rcpt_badversion;	/* version mismatch */
166 	char *		rcpt_orig;		/* original recipient */
167 	char *		rcpt_new;		/* replacement */
168 	struct batv_rcptlist * rcpt_next;	/* next node */
169 };
170 typedef struct batv_rcptlist * RCPTLIST;
171 
172 /*
173 **  CONTEXT -- thread context
174 */
175 
176 struct batv_context
177 {
178 	bool		ctx_authed;		/* authenticated client */
179 	char *		ctx_jobid;		/* job ID */
180 	char *		ctx_sender;		/* envelope sender */
181 	char *		ctx_newsender;		/* new envelope sender */
182 	char *		ctx_esmtp;		/* ESMTP stuff */
183 	RCPTLIST	ctx_rcpts;		/* recipients */
184 };
185 typedef struct batv_context * CONTEXT;
186 
187 /*
188 **  RELIST -- regular expression list
189 */
190 
191 struct batv_relist
192 {
193 	regex_t		re_re;			/* regex_t */
194 	struct batv_relist * re_next;		/* next node */
195 };
196 typedef struct batv_relist * RELIST;
197 
198 /*
199 **  HOSTLIST -- hostname/CIDR list
200 */
201 
202 struct batv_hostlist
203 {
204 	char *		host_name;		/* regex_t */
205 	struct in_addr	host_addr;		/* address */
206 	struct in_addr	host_mask;		/* mask */
207 	struct batv_hostlist * host_next;	/* next node */
208 };
209 typedef struct batv_hostlist * HOSTLIST;
210 
211 /*
212 **  DOMLIST -- domains to sign/verify
213 */
214 
215 struct batv_domlist
216 {
217 	char *		dom_name;		/* name */
218 	struct batv_domlist * dom_next;		/* next node */
219 };
220 typedef struct batv_domlist * DOMLIST;
221 
222 /*
223 **  CONNCTX -- connection context, containing thread-specific data
224 */
225 
226 struct connctx
227 {
228 	bool		cctx_milterv2;		/* milter v2 available */
229 	bool		cctx_noleadspc;		/* no leading spaces */
230 	bool		cctx_internal;		/* internal host */
231 	char		cctx_mtaname[MAXHOSTNAMELEN + 1];
232 						/* MTA name */
233 	char		cctx_hostname[MAXHOSTNAMELEN + 1];
234 						/* client hostname */
235 	_SOCK_ADDR	cctx_addr;		/* client address */
236 	CONTEXT		cctx_msgctx;		/* message context */
237 };
238 typedef struct connctx * connctx;
239 
240 /*
241 **  LOOKUP -- lookup table
242 */
243 
244 struct lookup
245 {
246 	char *		lu_str;
247 	int		lu_code;
248 };
249 
250 /* globals */
251 bool dolog;
252 bool logwhy;
253 bool die;
254 bool addxhdr;
255 bool addarhdr;
256 bool notreally;
257 bool bounceonly;
258 bool skipauth;
259 bool setreply;
260 bool sendmail;
261 bool quarantine;
262 bool authservjobid;
263 int diesig;
264 unsigned int opmode;
265 unsigned int version;
266 unsigned int siglife;
267 Peer peerlist;
268 char *progname;
269 char *key;
270 char *authservid;
271 char **macros;
272 char **mtas;
273 char **values;
274 char hostname[MAXHOSTNAMELEN + 1];
275 RELIST allow;
276 HOSTLIST signlist;
277 DOMLIST domains;
278 
279 struct lookup log_facilities[] =
280 {
281 	{ "auth",		LOG_AUTH },
282 	{ "cron",		LOG_CRON },
283 	{ "daemon",		LOG_DAEMON },
284 	{ "kern",		LOG_KERN },
285 	{ "lpr",		LOG_LPR },
286 	{ "mail",		LOG_MAIL },
287 	{ "news",		LOG_NEWS },
288 	{ "security",		LOG_AUTH },       /* DEPRECATED */
289 	{ "syslog",		LOG_SYSLOG },
290 	{ "user",		LOG_USER },
291 	{ "uucp",		LOG_UUCP },
292 	{ "local0",		LOG_LOCAL0 },
293 	{ "local1",		LOG_LOCAL1 },
294 	{ "local2",		LOG_LOCAL2 },
295 	{ "local3",		LOG_LOCAL3 },
296 	{ "local4",		LOG_LOCAL4 },
297 	{ "local5",		LOG_LOCAL5 },
298 	{ "local6",		LOG_LOCAL6 },
299 	{ "local7",		LOG_LOCAL7 },
300 	{ NULL,			-1 }
301 };
302 
303 /*
304 **  ============================= LOCAL STUFF =============================
305 */
306 
307 /*
308 **  BATVF_RESTART_CHECK -- initialize/check restart rate information
309 **
310 **  Parameters:
311 **  	n -- size of restart rate array to initialize/enforce
312 **  	t -- maximum time range for restarts (0 == init)
313 **
314 **  Return value:
315 **  	true -- OK to continue
316 **  	false -- error
317 */
318 
319 static bool
batvf_restart_check(int n,time_t t)320 batvf_restart_check(int n, time_t t)
321 {
322 	static int idx;				/* last filled slot */
323 	static int alen;			/* allocated length */
324 	static time_t *list;
325 
326 	if (t == 0)
327 	{
328 		alen = n * sizeof(time_t);
329 
330 		list = (time_t *) malloc(alen);
331 
332 		if (list == NULL)
333 			return FALSE;
334 
335 		memset(list, '\0', alen);
336 
337 		idx = 0;
338 		alen = n;
339 
340 		return TRUE;
341 	}
342 	else
343 	{
344 		int which;
345 
346 		time_t now;
347 
348 		(void) time(&now);
349 
350 		which = (idx - 1) % alen;
351 		if (which == -1)
352 			which = alen - 1;
353 
354 		if (list[which] != 0 &&
355 		    list[which] + t > now)
356 			return false;
357 
358 		list[which] = t;
359 		idx++;
360 
361 		return TRUE;
362 	}
363 }
364 
365 /*
366 **  BATV_INIT_SYSLOG -- initialize syslog()
367 **
368 **  Parameters:
369 **  	facility -- name of the syslog facility to use when logging;
370 **  	            can be NULL to request the default
371 **
372 **  Return value:
373 **  	None.
374 */
375 
376 static void
batv_init_syslog(char * facility)377 batv_init_syslog(char *facility)
378 {
379 #ifdef LOG_MAIL
380 	int code;
381 	struct lookup *p = NULL;
382 
383 	closelog();
384 
385 	code = LOG_MAIL;
386 	if (facility != NULL)
387 	{
388 		for (p = log_facilities; p != NULL; p++)
389 		{
390 			if (strcasecmp(p->lu_str, facility) == 0)
391 			{
392 				code = p->lu_code;
393 				break;
394 			}
395 		}
396 	}
397 
398 	openlog(progname, LOG_PID, code);
399 #else /* LOG_MAIL */
400 	closelog();
401 
402 	openlog(progname, LOG_PID);
403 #endif /* LOG_MAIL */
404 }
405 
406 /*
407 **  BATV_SIGHANDLER -- signal handler
408 **
409 **  Parameters:
410 **  	sig -- signal received
411 **
412 **  Return value:
413 **  	None.
414 */
415 
416 static void
batv_sighandler(int sig)417 batv_sighandler(int sig)
418 {
419 	if (sig == SIGINT || sig == SIGTERM || sig == SIGHUP)
420 	{
421 		diesig = sig;
422 		die = TRUE;
423 	}
424 }
425 
426 /*
427 **  BATV_STDIO -- set up the base descriptors to go nowhere
428 **
429 **  Parameters:
430 **  	None.
431 **
432 **  Return value:
433 **  	None.
434 */
435 
436 static void
batv_stdio(void)437 batv_stdio(void)
438 {
439 	int devnull;
440 
441 	/* this only fails silently, but that's OK */
442 	devnull = open(_PATH_DEVNULL, O_RDWR, 0);
443 	if (devnull != -1)
444 	{
445 		(void) dup2(devnull, 0);
446 		(void) dup2(devnull, 1);
447 		(void) dup2(devnull, 2);
448 		if (devnull > 2)
449 			(void) close(devnull);
450 	}
451 
452 	(void) setsid();
453 }
454 
455 /*
456 **  BATV_KILLCHILD -- kill child process
457 **
458 **  Parameters:
459 **  	pid -- process ID to signal
460 **  	sig -- signal to use
461 **  	dolog -- log it?
462 **
463 **  Return value:
464 **  	None.
465 */
466 
467 static void
batv_killchild(pid_t pid,int sig)468 batv_killchild(pid_t pid, int sig)
469 {
470 	if (kill(pid, sig) == -1 && dolog)
471 	{
472 		syslog(LOG_ERR, "kill(%d, %d): %s", pid, sig,
473 		       strerror(errno));
474 	}
475 }
476 
477 /*
478 **  BATV_INITCONTEXT -- initialize a context
479 **
480 **  Parameters:
481 **  	None.
482 **
483 **  Return value:
484 **  	A new CONTEXT handle, or NULL on error.
485 */
486 
487 static CONTEXT
batv_initcontext(void)488 batv_initcontext(void)
489 {
490 	CONTEXT new;
491 
492 	new = (CONTEXT) malloc(sizeof *new);
493 	if (new == NULL)
494 		return NULL;
495 
496 	memset(new, '\0', sizeof *new);
497 
498 	return new;
499 }
500 
501 #define	CLOBBER(x)	if ((x) != NULL) { \
502 				free((x)); \
503 				(x) = NULL; \
504 			}
505 
506 /*
507 **  BATV_CLEANUP -- clean up a filter context
508 **
509 **  Parameters:
510 **  	cfc -- CONTEXT handle to be cleaned up
511 **  	msg -- message stuff only, or the whole shebang?
512 **
513 **  Return value:
514 **  	None.
515 */
516 
517 static void
batv_cleanup(connctx cc,bool msg)518 batv_cleanup(connctx cc, bool msg)
519 {
520 	RCPTLIST rcpt;
521 	RCPTLIST next;
522 	CONTEXT bfc;
523 
524 	assert(cc != NULL);
525 
526 	bfc = cc->cctx_msgctx;
527 	if (bfc == NULL)
528 		return;
529 
530 	CLOBBER(bfc->ctx_jobid);
531 	CLOBBER(bfc->ctx_sender);
532 	CLOBBER(bfc->ctx_newsender);
533 	CLOBBER(bfc->ctx_esmtp);
534 
535 	rcpt = bfc->ctx_rcpts;
536 	while (rcpt != NULL)
537 	{
538 		next = rcpt->rcpt_next;
539 
540 		CLOBBER(rcpt->rcpt_orig);
541 		CLOBBER(rcpt->rcpt_new);
542 
543 		free(rcpt);
544 
545 		rcpt = next;
546 	}
547 
548 	free(bfc);
549 
550 	cc->cctx_msgctx = NULL;
551 }
552 
553 /*
554 **  BATV_STRIPBRACKETS -- remove angle brackets from the sender address
555 **
556 **  Parameters:
557 ** 	addr -- address to be processed
558 **
559 **  Return value:
560 **  	None.
561 */
562 
563 static void
batv_stripbrackets(char * addr)564 batv_stripbrackets(char *addr)
565 {
566 	char *p, *q;
567 
568 	assert(addr != NULL);
569 
570 	p = addr;
571 	q = addr + strlen(addr) - 1;
572 
573 	while (*p == '<' && *q == '>')
574 	{
575 		p++;
576 		*q-- = '\0';
577 	}
578 
579 	if (p != addr)
580 	{
581 		for (q = addr; *p != '\0'; p++, q++)
582 			*q = *p;
583 		*q = '\0';
584 	}
585 }
586 
587 /*
588 **  BATV_MKREGEXP -- make a regular expression
589 **
590 **  Parameters:
591 **  	str -- input string
592 **  	re -- destination string
593 **
594 **  Return value:
595 **   	None.
596 */
597 
598 static void
batv_mkregexp(str,addr,len)599 batv_mkregexp(str, addr, len)
600 	char *str;
601 	char *addr;
602 	size_t len;
603 {
604 	char *p;
605 	char *q;
606 	char *end;
607 
608 	assert(str != NULL);
609 	assert(addr != NULL);
610 
611 	memset(addr, '\0', len);
612 
613 	addr[0] = '^';
614 
615 	end = addr + len - 1;
616 
617 	for (p = str, q = addr + 1; *p != '\0' && q <= end; p++)
618 	{
619 		switch (*p)
620 		{
621 		  case '*':
622 			if (q >= end - 2)
623 				return;
624 			*q++ = '.';
625 			*q++ = '*';
626 			break;
627 
628 		  case '.':
629 			if (q >= end - 2)
630 				return;
631 			*q++ = '\\';
632 			*q++ = '.';
633 			break;
634 
635 		  default:
636 			*q++ = *p;
637 			break;
638 		}
639 	}
640 
641 	if (q < end)
642 		*q = '$';
643 }
644 
645 /*
646 **  BATV_ISBLANK -- blank line?
647 **
648 **  Parameters:
649 **  	str -- string
650 **
651 **  Return value:
652 **  	TRUE iff "str" is either zero-length or contains only spaces.
653 */
654 
655 static bool
batv_isblank(char * str)656 batv_isblank(char *str)
657 {
658 	char *p;
659 
660 	assert(str != NULL);
661 
662 	for (p = str; *p != '\0'; p++)
663 	{
664 		if (!isascii(*p) || !isspace(*p))
665 			return FALSE;
666 	}
667 
668 	return TRUE;
669 }
670 
671 /*
672 **  ============================= DEBUG STUFF =============================
673 */
674 
675 #ifdef DEBUG
676 int
smfi_chgfrom(void * ctx,char * from,char * esmtp)677 smfi_chgfrom(void *ctx, char *from, char *esmtp)
678 {
679 	printf("smfi_chgfrom(<ctx>, `%s', `%s')\n", from,
680 	       esmtp == NULL ? "(null)" : esmtp);
681 	return MI_SUCCESS;
682 }
683 
684 int
smfi_insheader(void * ctx,int idx,char * hdr,char * val)685 smfi_insheader(void *ctx, int idx, char *hdr, char *val)
686 {
687 	printf("smfi_insheader(<ctx>, %d, `%s', `%s')\n", idx, hdr, val);
688 	return MI_SUCCESS;
689 }
690 
691 int
smfi_addrcpt(void * ctx,char * rcpt)692 smfi_addrcpt(void *ctx, char *rcpt)
693 {
694 	printf("smfi_addrcpt(<ctx>, `%s')\n", rcpt);
695 	return MI_SUCCESS;
696 }
697 
698 int
smfi_delrcpt(void * ctx,char * rcpt)699 smfi_delrcpt(void *ctx, char *rcpt)
700 {
701 	printf("smfi_delrcpt(<ctx>, `%s')\n", rcpt);
702 	return MI_SUCCESS;
703 }
704 
705 int
smfi_opensocket(int param)706 smfi_opensocket(int param)
707 {
708 	printf("smfi_opensocket(%d)\n", param);
709 
710 	return MI_SUCCESS;
711 }
712 
713 int
smfi_quarantine(void * ctx,char * reason)714 smfi_quarantine(void *ctx, char *reason)
715 {
716 	printf("smfi_quarantine(<ctx>, `%s')\n", reason);
717 
718 	return MI_SUCCESS;
719 }
720 
721 void
smfi_setdbg(int param)722 smfi_setdbg(int param)
723 {
724 	printf("smfi_setdbg(%d)\n", param);
725 }
726 
727 void
smfi_setconn(char * file)728 smfi_setconn(char *file)
729 {
730 	printf("smfi_setconn(`%s')\n", file);
731 }
732 
733 void
smfi_setpriv(void * ctx,void * priv)734 smfi_setpriv(void *ctx, void *priv)
735 {
736 	fakepriv = priv;
737 }
738 
739 void *
smfi_getpriv(void * ctx)740 smfi_getpriv(void *ctx)
741 {
742 	return fakepriv;
743 }
744 
745 char *
smfi_getsymval(void * ctx,char * sym)746 smfi_getsymval(void *ctx, char *sym)
747 {
748 	char *ret;
749 	size_t l;
750 	CONTEXT cc;
751 
752 	l = strlen(sym) + 6 + 1;
753 	cc = fakepriv;
754 
755 	printf("smfi_getsymval(<ctx>, `%s')\n", sym);
756 	ret = malloc(l);
757 	snprintf(ret, l, "DEBUG-%s", sym);
758 	return ret;
759 }
760 
761 int
smfi_setreply(void * ctx,char * smtp,char * esc,char * txt)762 smfi_setreply(void *ctx, char *smtp, char *esc, char *txt)
763 {
764 	printf("smfi_setreply(<ctx>, `%s', `%s', `%s')\n", smtp, esc, txt);
765 
766 	return MI_SUCCESS;
767 }
768 #endif /* DEBUG */
769 
770 /*
771 **  ============================= MILTER STUFF =============================
772 */
773 
774 /*
775 **  MLFI_NEGOTIATE -- handler called on new SMTP connection to negotiate
776 **                    MTA options
777 **
778 **  Parameters:
779 **  	ctx -- milter context
780 **	f0  -- actions offered by the MTA
781 **	f1  -- protocol steps offered by the MTA
782 **	f2  -- reserved for future extensions
783 **	f3  -- reserved for future extensions
784 **	pf0 -- actions requested by the milter
785 **	pf1 -- protocol steps requested by the milter
786 **	pf2 -- reserved for future extensions
787 **	pf3 -- reserved for future extensions
788 **
789 **  Return value:
790 **  	An SMFIS_* constant.
791 */
792 
793 static sfsistat
mlfi_negotiate(SMFICTX * ctx,unsigned long f0,unsigned long f1,SM_UNUSED (unsigned long f2),SM_UNUSED (unsigned long f3),unsigned long * pf0,unsigned long * pf1,unsigned long * pf2,unsigned long * pf3)794 mlfi_negotiate(SMFICTX *ctx,
795 	unsigned long f0, unsigned long f1,
796 	SM_UNUSED(unsigned long f2),
797 	SM_UNUSED(unsigned long f3),
798 	unsigned long *pf0, unsigned long *pf1,
799 	unsigned long *pf2, unsigned long *pf3)
800 {
801 	unsigned long reqactions = (SMFIF_CHGFROM |
802 	                            SMFIF_ADDHDRS |
803 	                            SMFIF_DELRCPT |
804 	                            SMFIF_ADDRCPT );
805 	unsigned long protosteps = (SMFIP_NOHELO |
806 	                            SMFIP_NOHDRS |
807 	                            SMFIP_NOEOH |
808 	                            SMFIP_NOUNKNOWN |
809 	                            SMFIP_NODATA |
810 	                            SMFIP_NOBODY );
811 	connctx cc;
812 
813 	/* initialize connection context */
814 	cc = malloc(sizeof(struct connctx));
815 	if (cc != NULL)
816 	{
817 		memset(cc, '\0', sizeof(struct connctx));
818 
819 		smfi_setpriv(ctx, cc);
820 	}
821 
822 	/* verify the actions we need are available */
823 	if (quarantine)
824 		reqactions |= SMFIF_QUARANTINE;
825 	if ((f0 & reqactions) != reqactions)
826 	{
827 		if (dolog)
828 		{
829 			syslog(LOG_ERR,
830 			       "mlfi_negotiate(): required milter action(s) not available (got 0x%lx, need 0x%lx)",
831 			       f0, reqactions);
832 		}
833 
834 		return SMFIS_REJECT;
835 	}
836 
837 	/* set the actions we want */
838 	*pf0 = reqactions;
839 
840 	/* disable as many protocol steps we don't need as are available */
841 	*pf1 = (protosteps & f1);
842 
843 # ifdef SMFIP_HDR_LEADSPC
844 	/* request preservation of leading spaces if possible */
845 	if ((f1 & SMFIP_HDR_LEADSPC) != 0)
846 	{
847 		if (cc != NULL)
848 		{
849 			cc->cctx_noleadspc = TRUE;
850 			*pf1 |= SMFIP_HDR_LEADSPC;
851 		}
852 	}
853 # endif /* SMFIP_HDR_LEADSPC */
854 
855 	*pf2 = 0;
856 	*pf3 = 0;
857 
858 	return SMFIS_CONTINUE;
859 }
860 
861 /*
862 **  MLFI_CONNECT -- handle a connection announcement
863 **
864 **  Parameters:
865 **  	ctx -- milter context
866 **  	host -- hostname of the client
867 **  	addr -- address information for the client
868 **
869 **  Return value:
870 **  	An SMFIS_* constant.
871 */
872 
873 sfsistat
mlfi_connect(SMFICTX * ctx,char * host,_SOCK_ADDR * addr)874 mlfi_connect(SMFICTX *ctx, char *host, _SOCK_ADDR *addr)
875 {
876 	HOSTLIST hc;
877 	connctx cc;
878 	char *dot;
879 
880 	cc = smfi_getpriv(ctx);
881 	assert(cc != NULL);
882 
883 	/* if the client is on an ignored host, then ignore it */
884 	if (peerlist != NULL)
885 	{
886 		/* try hostname, if available */
887 		if (host != NULL && host[0] != '\0' && host[0] != '[')
888 		{
889 			batvf_lowercase(host);
890 			if (batvf_checkhost(peerlist, host))
891 				return SMFIS_ACCEPT;
892 		}
893 
894 		/* try IP address, if available */
895 		if (addr != NULL && addr->sa_family == AF_INET)
896 		{
897 			if (batvf_checkip(peerlist, addr))
898 				return SMFIS_ACCEPT;
899 		}
900 	}
901 
902 	if (authservid != NULL)
903 	{
904 		sm_strlcpy(cc->cctx_mtaname, authservid,
905 		           sizeof cc->cctx_mtaname);
906 	}
907 	else
908 	{
909 		char *mtaname;
910 
911 		mtaname = smfi_getsymval(ctx, "j");
912 		if (mtaname != NULL)
913 		{
914 			sm_strlcpy(cc->cctx_mtaname, mtaname,
915 			           sizeof cc->cctx_mtaname);
916 		}
917 		else
918 		{
919 			sm_strlcpy(cc->cctx_mtaname, MTAUNKNOWN,
920 			           sizeof cc->cctx_mtaname);
921 		}
922 	}
923 
924 	sm_strlcpy(cc->cctx_hostname, host, sizeof cc->cctx_hostname);
925 	memcpy(&cc->cctx_addr, addr, sizeof cc->cctx_addr);
926 
927 	/* is the client on the "internal" (sign) list? */
928 	for (hc = signlist; hc != NULL; hc = hc->host_next)
929 	{
930 		if (hc->host_name != NULL)
931 		{
932 			/* full hostname */
933 			if (strcasecmp(hc->host_name, cc->cctx_hostname) == 0)
934 			{
935 				cc->cctx_internal = TRUE;
936 				break;
937 			}
938 
939 			for (dot = strchr(cc->cctx_hostname, '.');
940 			     dot != NULL;
941 			     dot = strchr(dot + 1, '.'))
942 			{
943 				if (strcasecmp(hc->host_name, dot) == 0)
944 				{
945 					cc->cctx_internal = TRUE;
946 					break;
947 				}
948 			}
949 
950 			if (cc->cctx_internal)
951 				break;
952 		}
953 
954 		if (hc->host_addr.s_addr != 0 &&
955 		    hc->host_mask.s_addr != 0 &&
956 		    cc->cctx_addr.sa_family == AF_INET)
957 		{
958 			struct sockaddr_in *in;
959 
960 			in = (struct sockaddr_in *) &cc->cctx_addr;
961 
962 			if ((in->sin_addr.s_addr & hc->host_mask.s_addr) ==
963 			    (hc->host_addr.s_addr & hc->host_mask.s_addr))
964 			{
965 				cc->cctx_internal = TRUE;
966 				break;
967 			}
968 		}
969 	}
970 
971 	return SMFIS_CONTINUE;
972 }
973 
974 /*
975 **  MLFI_ENVFROM -- process envelope sender
976 **
977 **  Parameters:
978 **  	ctx -- milter context
979 **  	args -- MAIL FROM argument vector
980 **
981 **  Return value:
982 **  	An SMFIS_* constant.
983 */
984 
985 sfsistat
mlfi_envfrom(SMFICTX * ctx,char ** args)986 mlfi_envfrom(SMFICTX *ctx, char **args)
987 {
988 	bool sign = FALSE;
989 	int status;
990 	CONTEXT bfc = NULL;
991 	connctx cc = NULL;
992 	char *jobid;
993 	char *domain;
994 	char *dot;
995 	RELIST ac;
996 	DOMLIST dc;
997 
998 	cc = smfi_getpriv(ctx);
999 	if (cc == NULL)
1000 	{
1001 		if (dolog)
1002 		{
1003 			syslog(LOG_ERR,
1004 			       "envelope data received before connection information; temp-failing");
1005 		}
1006 
1007 		return SMFIS_TEMPFAIL;
1008 	}
1009 
1010 	batv_cleanup(cc, TRUE);
1011 
1012 	bfc = batv_initcontext();
1013 	if (bfc == NULL)
1014 	{
1015 		if (dolog)
1016 			syslog(LOG_ERR, "malloc(): %s", strerror(errno));
1017 
1018 		return SMFIS_TEMPFAIL;
1019 	}
1020 
1021 	cc->cctx_msgctx = bfc;
1022 
1023 	jobid = smfi_getsymval(ctx, "i");
1024 	if (jobid != NULL)
1025 	{
1026 		bfc->ctx_jobid = strdup(jobid);
1027 		if (bfc->ctx_jobid == NULL)
1028 		{
1029 			if (dolog)
1030 			{
1031 				syslog(LOG_ERR, "strdup(): %s",
1032 				       strerror(errno));
1033 			}
1034 
1035 			return SMFIS_TEMPFAIL;
1036 		}
1037 	}
1038 	else
1039 	{
1040 		bfc->ctx_jobid = strdup(JOBIDUNKNOWN);
1041 	}
1042 
1043 	/* arrange to skip verification of authenticated senders */
1044 	if (skipauth && smfi_getsymval(ctx, "{auth_authen}") != NULL)
1045 		bfc->ctx_authed = TRUE;
1046 
1047 	/* copy the envelope sender and clean it */
1048 	bfc->ctx_sender = strdup(args[0]);
1049 	if (bfc->ctx_sender == NULL)
1050 	{
1051 		if (dolog)
1052 		{
1053 			syslog(LOG_ERR, "strdup(): %s",
1054 			       strerror(errno));
1055 		}
1056 
1057 		return SMFIS_TEMPFAIL;
1058 	}
1059 	batv_stripbrackets(bfc->ctx_sender);
1060 	domain = strchr(bfc->ctx_sender, '@');
1061 
1062 	if (domain == NULL)
1063 	{
1064 		if (logwhy && dolog)
1065 		{
1066 			syslog(LOG_INFO,
1067 			       "%s no domain found in sender; skipping",
1068 			       bfc->ctx_jobid);
1069 		}
1070 
1071 		return SMFIS_CONTINUE;
1072 	}
1073 	else
1074 	{
1075 		domain++;
1076 	}
1077 
1078 	/* apply allow list to envelope sender */
1079 	for (ac = allow; ac != NULL; ac = ac->re_next)
1080 	{
1081 		status = regexec(&ac->re_re, bfc->ctx_sender, 0, NULL, 0);
1082 		if (status == 0)
1083 		{
1084 			if (logwhy && dolog)
1085 			{
1086 				syslog(LOG_INFO,
1087 				       "%s sender matches allow list; skipping",
1088 				       bfc->ctx_jobid);
1089 			}
1090 
1091 			return SMFIS_CONTINUE;
1092 		}
1093 		else if (status != REG_NOMATCH)
1094 		{
1095 			if (dolog)
1096 			{
1097 				char errbuf[BUFRSZ + 1];
1098 
1099 				memset(errbuf, '\0', sizeof errbuf);
1100 				(void) regerror(status, &ac->re_re, errbuf,
1101 				                BUFRSZ);
1102 				syslog(LOG_ERR, "%s: regexec(): %s",
1103 				       bfc->ctx_jobid, errbuf);
1104 			}
1105 
1106 			return SMFIS_TEMPFAIL;
1107 		}
1108 	}
1109 
1110 	/* is it a domain we care about? */
1111 	if (domains == NULL)
1112 	{
1113 		if (logwhy && dolog)
1114 		{
1115 			syslog(LOG_INFO,
1116 			       "%s signing for all domains; message will be signed",
1117 			       bfc->ctx_jobid);
1118 		}
1119 
1120 		sign = TRUE;
1121 	}
1122 	else
1123 	{
1124 		sign = FALSE;
1125 		for (dc = domains; dc != NULL; dc = dc->dom_next)
1126 		{
1127 			/* full hostname */
1128 			if (strcasecmp(dc->dom_name, domain) == 0)
1129 			{
1130 				sign = TRUE;
1131 				break;
1132 			}
1133 
1134 			for (dot = strchr(domain, '.');
1135 			     dot != NULL;
1136 			     dot = strchr(dot + 1, '.'))
1137 			{
1138 				if (strcasecmp(dc->dom_name, dot) == 0)
1139 				{
1140 					sign = TRUE;
1141 					break;
1142 				}
1143 			}
1144 
1145 			if (sign)
1146 				break;
1147 		}
1148 
1149 		if (sign && logwhy && dolog)
1150 		{
1151 			syslog(LOG_INFO,
1152 			       "%s sender matches domain list; message will be signed",
1153 			       bfc->ctx_jobid);
1154 		}
1155 	}
1156 
1157 	/* see if it came in on an authorized MSA/MTA connection */
1158 	if (mtas != NULL && !sign)
1159 	{
1160 		int n;
1161 		char *mtaname;
1162 
1163 		mtaname = smfi_getsymval(ctx, "{daemon_name}");
1164 
1165 		if (mtaname != NULL)
1166 		{
1167 			for (n = 0; mtas[n] != NULL; n++)
1168 			{
1169 				if (strcasecmp(mtaname, mtas[n]) == 0)
1170 				{
1171 					sign = TRUE;
1172 					break;
1173 				}
1174 			}
1175 		}
1176 
1177 		if (sign && logwhy && dolog)
1178 		{
1179 			syslog(LOG_INFO,
1180 			       "%s MTA name matches MTA name list; message will be signed",
1181 			       bfc->ctx_jobid);
1182 		}
1183 	}
1184 
1185 	/* now test macros, if any */
1186 	if (macros != NULL && !sign)
1187 	{
1188 		bool done = FALSE;
1189 		int n;
1190 		char *val;
1191 		char *p;
1192 		char name[BUFRSZ + 1];
1193 		char vals[BUFRSZ + 1];
1194 
1195 		for (n = 0; !done && macros[n] != NULL; n++)
1196 		{
1197 			/* retrieve the macro */
1198 			snprintf(name, sizeof name, "{%s}", macros[n]);
1199 			val = smfi_getsymval(ctx, name);
1200 
1201 			/* short-circuit if the macro's not set */
1202 			if (val == NULL)
1203 				continue;
1204 
1205 			/* macro set and we don't care about the value */
1206 			if (val != NULL && values[n] == NULL)
1207 			{
1208 				sign = TRUE;
1209 				break;
1210 			}
1211 
1212 			sm_strlcpy(vals, values[n], sizeof vals);
1213 
1214 			for (p = strtok(vals, "|");
1215 			     !done && p != NULL;
1216 			     p = strtok(NULL, "|"))
1217 			{
1218 				if (strcasecmp(val, p) == 0)
1219 				{
1220 					sign = TRUE;
1221 					done = TRUE;
1222 				}
1223 			}
1224 		}
1225 
1226 		if (sign && logwhy && dolog)
1227 		{
1228 			syslog(LOG_INFO,
1229 			       "%s macro value matches macro list; message will be signed",
1230 			       bfc->ctx_jobid);
1231 		}
1232 	}
1233 
1234 	/* not to be signed, not in signing mode */
1235 	if (!sign || (opmode & BATV_MODE_SIGN) == 0)
1236 	{
1237 		if (logwhy && dolog)
1238 		{
1239 			if (!sign)
1240 			{
1241 				syslog(LOG_INFO,
1242 				       "%s signing mode not selected",
1243 				       bfc->ctx_jobid);
1244 			}
1245 			else
1246 			{
1247 				syslog(LOG_INFO, "%s signing mode disabled",
1248 				       bfc->ctx_jobid);
1249 			}
1250 		}
1251 
1252 		return SMFIS_CONTINUE;
1253 	}
1254 
1255 	/* sign only for internal/authenticated clients */
1256 	if (cc->cctx_internal || bfc->ctx_authed)
1257 	{
1258 		int expire;
1259 		time_t now;
1260 		SHA_CTX sha1;
1261 		char buf[BUFRSZ + 1];
1262 		unsigned char digest[SHA1_DIGEST_SIZE];
1263 		char sender[BUFRSZ + 1];
1264 
1265 		/* continue if already signed */
1266 		if (!sendmail && strncmp(bfc->ctx_sender, "prvs=", 5) == 0)
1267 			return SMFIS_CONTINUE;
1268 		if (sendmail && strstr(bfc->ctx_sender, "+prvs=") != NULL)
1269 			return SMFIS_CONTINUE;
1270 
1271 		(void) time(&now);
1272 		expire = ((now / 86400) + siglife) % 1000;
1273 
1274 		snprintf(buf, sizeof buf, "%1d%03d%s%s", version, expire,
1275 		         bfc->ctx_sender, key);
1276 
1277 		SHA1_Init(&sha1);
1278 		SHA1_Update(&sha1, buf, strlen(buf));
1279 		SHA1_Final(digest, &sha1);
1280 
1281 		if (sendmail)
1282 		{
1283 			char *at;
1284 			char tmp[BUFRSZ + 1];
1285 
1286 			sm_strlcpy(tmp, bfc->ctx_sender, sizeof tmp);
1287 
1288 			at = strchr(tmp, '@');
1289 			if (at != NULL)
1290 			{
1291 				*at = '\0';
1292 
1293 				snprintf(sender, sizeof sender,
1294 				         "%s+prvs=%1d%03d%02X%02X%02X@%s",
1295 				         tmp, version, expire, digest[0],
1296 				         digest[1], digest[2], at + 1);
1297 			}
1298 			else
1299 			{
1300 				snprintf(sender, sizeof sender,
1301 				         "%s+prvs=%1d%03d%02X%02X%02X",
1302 				         bfc->ctx_sender, version, expire,
1303 				         digest[0], digest[1], digest[2]);
1304 			}
1305 		}
1306 		else
1307 		{
1308 			snprintf(sender, sizeof sender,
1309 			         "prvs=%1d%03d%02X%02X%02X=%s",
1310 			         version, expire, digest[0], digest[1],
1311 			         digest[2], bfc->ctx_sender);
1312 		}
1313 
1314 		bfc->ctx_newsender = strdup(sender);
1315 		if (bfc->ctx_newsender == NULL)
1316 		{
1317 			if (dolog)
1318 			{
1319 				syslog(LOG_ERR, "%s: strdup(): %s",
1320 				       bfc->ctx_jobid, strerror(errno));
1321 			}
1322 
1323 			return SMFIS_TEMPFAIL;
1324 		}
1325 	}
1326 	else if (logwhy && dolog)
1327 	{
1328 		syslog(LOG_INFO,
1329 		       "%s client not internal and not authenticated; signing skipped",
1330 		       bfc->ctx_jobid);
1331 	}
1332 
1333 	/* if we have a new sender, we have to replace the ESMTP stuff later */
1334 	if (bfc->ctx_newsender != NULL)
1335 	{
1336 		int c;
1337 		char esmtp[BUFRSZ + 1];
1338 
1339 		memset(esmtp, '\0', sizeof esmtp);
1340 
1341 		for (c = 1; args[c] != NULL; c++)
1342 		{
1343 			if (c > 1)
1344 				sm_strlcat(esmtp, " ", sizeof esmtp);
1345 
1346 			sm_strlcat(esmtp, args[c], sizeof esmtp);
1347 		}
1348 
1349 		if (esmtp[0] != '\0')
1350 		{
1351 			bfc->ctx_esmtp = strdup(esmtp);
1352 			if (bfc->ctx_esmtp == NULL)
1353 			{
1354 				if (dolog)
1355 				{
1356 					syslog(LOG_ERR, "%s: strdup(): %s",
1357 					       bfc->ctx_jobid,
1358 					       strerror(errno));
1359 				}
1360 
1361 				return SMFIS_TEMPFAIL;
1362 			}
1363 		}
1364 	}
1365 
1366 	return SMFIS_CONTINUE;
1367 }
1368 
1369 /*
1370 **  MLFI_ENVRCPT -- envelope recipient
1371 **
1372 **  Parameters:
1373 **  	ctx -- milter context
1374 **  	args -- RCPT TO argument vector
1375 **
1376 **  Return value:
1377 **  	An SMFIS_* constant.
1378 */
1379 
1380 sfsistat
mlfi_envrcpt(SMFICTX * ctx,char ** args)1381 mlfi_envrcpt(SMFICTX *ctx, char **args)
1382 {
1383 	bool verify;
1384 	int status;
1385 	CONTEXT bfc;
1386 	connctx cc;
1387 	char *domain;
1388 	char *dot;
1389 	DOMLIST dc;
1390 	RCPTLIST rcpt;
1391 	RELIST ac;
1392 	char addr[BUFRSZ + 1];
1393 
1394 	cc = smfi_getpriv(ctx);
1395 	assert(cc != NULL);
1396 	bfc = cc->cctx_msgctx;
1397 	assert(bfc != NULL);
1398 
1399 	if (bfc == NULL)
1400 	{
1401 		if (dolog)
1402 		{
1403 			syslog(LOG_ERR,
1404 			       "envelope data received before connection information; temp-failing");
1405 		}
1406 
1407 		return SMFIS_TEMPFAIL;
1408 	}
1409 
1410 	sm_strlcpy(addr, args[0], sizeof addr);
1411 	batv_stripbrackets(addr);
1412 	domain = strchr(addr, '@');
1413 
1414 	if (domain == NULL)
1415 	{
1416 		if (logwhy && dolog)
1417 		{
1418 			syslog(LOG_INFO,
1419 			       "%s no domain found in recipient; skipping",
1420 			       bfc->ctx_jobid);
1421 		}
1422 
1423 		return SMFIS_CONTINUE;
1424 	}
1425 	else
1426 	{
1427 		domain++;
1428 	}
1429 
1430 	/* apply allow list to envelope recipient */
1431 	for (ac = allow; ac != NULL; ac = ac->re_next)
1432 	{
1433 		status = regexec(&ac->re_re, addr, 0, NULL, 0);
1434 		if (status == 0)
1435 		{
1436 			if (logwhy && dolog)
1437 			{
1438 				syslog(LOG_INFO,
1439 				       "%s recipient matches allow list; skipping",
1440 				       bfc->ctx_jobid);
1441 			}
1442 
1443 			return SMFIS_CONTINUE;
1444 		}
1445 		else if (status != REG_NOMATCH)
1446 		{
1447 			if (dolog)
1448 			{
1449 				char errbuf[BUFRSZ + 1];
1450 
1451 				memset(errbuf, '\0', sizeof errbuf);
1452 				(void) regerror(status, &ac->re_re, errbuf,
1453 				                BUFRSZ);
1454 				syslog(LOG_ERR, "%s: regexec(): %s",
1455 				       bfc->ctx_jobid, errbuf);
1456 			}
1457 
1458 			return SMFIS_TEMPFAIL;
1459 		}
1460 	}
1461 
1462 	/* is it a domain we care about? */
1463 	if (domains == NULL)
1464 	{
1465 		if (logwhy && dolog)
1466 		{
1467 			syslog(LOG_INFO,
1468 			       "%s verifying for all domains; message will be verified",
1469 			       bfc->ctx_jobid);
1470 		}
1471 
1472 		verify = TRUE;
1473 	}
1474 	else
1475 	{
1476 		verify = FALSE;
1477 		for (dc = domains; dc != NULL; dc = dc->dom_next)
1478 		{
1479 			/* full hostname */
1480 			if (strcasecmp(dc->dom_name, domain) == 0)
1481 			{
1482 				verify = TRUE;
1483 				break;
1484 			}
1485 
1486 			for (dot = strchr(domain, '.');
1487 			     dot != NULL;
1488 			     dot = strchr(dot + 1, '.'))
1489 			{
1490 				if (strcasecmp(dc->dom_name, dot) == 0)
1491 				{
1492 					verify = TRUE;
1493 					break;
1494 				}
1495 			}
1496 
1497 			if (verify)
1498 				break;
1499 		}
1500 
1501 		if (verify && logwhy && dolog)
1502 		{
1503 			syslog(LOG_INFO,
1504 			       "%s recipient matches domain list; message will be verified",
1505 			       bfc->ctx_jobid);
1506 		}
1507 	}
1508 
1509 	/* see if it came in on an authorized MSA/MTA connection */
1510 	if (mtas != NULL && !verify)
1511 	{
1512 		int n;
1513 		char *mtaname;
1514 
1515 		mtaname = smfi_getsymval(ctx, "{daemon_name}");
1516 
1517 		if (mtaname != NULL)
1518 		{
1519 			for (n = 0; mtas[n] != NULL; n++)
1520 			{
1521 				if (strcasecmp(mtaname, mtas[n]) == 0)
1522 				{
1523 					verify = TRUE;
1524 					break;
1525 				}
1526 			}
1527 		}
1528 
1529 		if (verify && logwhy && dolog)
1530 		{
1531 			syslog(LOG_INFO,
1532 			       "%s MTA name matches MTA name list; message will be verified",
1533 			       bfc->ctx_jobid);
1534 		}
1535 	}
1536 
1537 	/* now test macros, if any */
1538 	if (macros != NULL && !verify)
1539 	{
1540 		bool done = FALSE;
1541 		int n;
1542 		char *val;
1543 		char *p;
1544 		char name[BUFRSZ + 1];
1545 		char vals[BUFRSZ + 1];
1546 
1547 		for (n = 0; !done && macros[n] != NULL; n++)
1548 		{
1549 			/* retrieve the macro */
1550 			snprintf(name, sizeof name, "{%s}", macros[n]);
1551 			val = smfi_getsymval(ctx, name);
1552 
1553 			/* short-circuit if the macro's not set */
1554 			if (val == NULL)
1555 				continue;
1556 
1557 			/* macro set and we don't care about the value */
1558 			if (val != NULL && values[n] == NULL)
1559 			{
1560 				verify = TRUE;
1561 				break;
1562 			}
1563 
1564 			sm_strlcpy(vals, values[n], sizeof vals);
1565 
1566 			for (p = strtok(vals, "|");
1567 			     !done && p != NULL;
1568 			     p = strtok(NULL, "|"))
1569 			{
1570 				if (strcasecmp(val, p) == 0)
1571 				{
1572 					verify = TRUE;
1573 					done = TRUE;
1574 				}
1575 			}
1576 		}
1577 
1578 		if (verify && logwhy && dolog)
1579 		{
1580 			syslog(LOG_INFO,
1581 			       "%s macro value matches macro list; message will be verified",
1582 			       bfc->ctx_jobid);
1583 		}
1584 	}
1585 
1586 	/* not to be verified, or not in verify mode */
1587 	if (!verify || (opmode & BATV_MODE_VERIFY) == 0)
1588 	{
1589 		if (logwhy && dolog)
1590 		{
1591 			if (!verify)
1592 			{
1593 				syslog(LOG_INFO,
1594 				       "%s verifying mode not selected",
1595 				       bfc->ctx_jobid);
1596 			}
1597 			else
1598 			{
1599 				syslog(LOG_INFO, "%s verifying mode disabled",
1600 				       bfc->ctx_jobid);
1601 			}
1602 		}
1603 
1604 		return SMFIS_CONTINUE;
1605 	}
1606 
1607 	/* skip mail from internal hosts and auth'd clients */
1608 	if (cc->cctx_internal || bfc->ctx_authed)
1609 	{
1610 		if (logwhy && dolog)
1611 		{
1612 			syslog(LOG_INFO,
1613 			       "%s client not internal and not authenticated; verifying skipped",
1614 			       bfc->ctx_jobid);
1615 		}
1616 
1617 		return SMFIS_CONTINUE;
1618 	}
1619 
1620 	/* add the recipient */
1621 	rcpt = (RCPTLIST) malloc(sizeof *rcpt);
1622 	if (rcpt == NULL)
1623 	{
1624 		if (dolog)
1625 			syslog(LOG_ERR, "malloc(): %s", strerror(errno));
1626 
1627 		return SMFIS_TEMPFAIL;
1628 	}
1629 
1630 	rcpt->rcpt_orig = strdup(addr);
1631 	if (rcpt->rcpt_orig == NULL)
1632 	{
1633 		if (dolog)
1634 			syslog(LOG_ERR, "strdup(): %s", strerror(errno));
1635 
1636 		free(rcpt);
1637 
1638 		return SMFIS_TEMPFAIL;
1639 	}
1640 
1641 	rcpt->rcpt_next = bfc->ctx_rcpts;
1642 	bfc->ctx_rcpts = rcpt;
1643 
1644 	/* if it's unsigned, complain */
1645 	if ((!sendmail && strncmp(addr, "prvs=", 5) != 0) ||
1646 	    (sendmail && strstr(addr, "+prvs=") == NULL))
1647 	{
1648 		/* only complain if it's a bounce, or we're being fascist */
1649 		if (strlen(bfc->ctx_sender) == 0 || !bounceonly)
1650 		{
1651 			if (dolog)
1652 			{
1653 				syslog(LOG_ERR,
1654 				       "%s: BATV signature not present in `%s'",
1655 				       bfc->ctx_jobid, addr);
1656 			}
1657 
1658 			if (!notreally && !addarhdr)
1659 			{
1660 				if (setreply &&
1661 				    smfi_setreply(ctx, BATV_REJECTSMTP,
1662 				                  BATV_REJECTESC,
1663 				                  "BATV signature missing") == MI_FAILURE)
1664 				{
1665 					if (dolog)
1666 					{
1667 						syslog(LOG_ERR,
1668 						       "%s: smfi_setreply() failed",
1669 						       bfc->ctx_jobid);
1670 					}
1671 				}
1672 			}
1673 		}
1674 	}
1675 	/* if it's signed, verify */
1676 	else
1677 	{
1678 		int daynum;
1679 		int sigexpire;
1680 		int sigversion;
1681 		time_t now;
1682 		char *sig = &addr[5];
1683 		char *eq;
1684 		SHA_CTX sha1;
1685 		char orcpt[BUFRSZ + 1];
1686 		char tmp[BUFRSZ + 1];
1687 		char buf[BUFRSZ + 1];
1688 		unsigned char digest[SHA1_DIGEST_SIZE];
1689 
1690 		rcpt->rcpt_signed = TRUE;
1691 		rcpt->rcpt_verified = TRUE;
1692 
1693 		if (sendmail)
1694 		{
1695 			char *plus;
1696 
1697 			plus = strstr(addr, "+prvs=");
1698 			assert(plus != NULL);
1699 
1700 			sig = plus + 6;
1701 		}
1702 
1703 		if (sendmail)
1704 		{
1705 			char *at;
1706 			char *plus;
1707 
1708 			at = strchr(sig, '@');
1709 			assert(at != NULL);
1710 
1711 			sm_strlcpy(orcpt, addr, sizeof orcpt);
1712 
1713 			plus = strstr(orcpt, "+prvs=");
1714 			assert(plus != NULL);
1715 			*plus = '\0';
1716 
1717 			sm_strlcat(orcpt, at, sizeof orcpt);
1718 		}
1719 		else
1720 		{
1721 			eq = strchr(sig, '=');
1722 			if (eq == NULL)
1723 			{
1724 				if (dolog)
1725 				{
1726 					syslog(LOG_ERR,
1727 					       "%s: BATV syntax error in `%s'",
1728 					       bfc->ctx_jobid, addr);
1729 				}
1730 
1731 				rcpt->rcpt_verified = FALSE;
1732 				return SMFIS_TEMPFAIL;
1733 			}
1734 
1735 			sm_strlcpy(orcpt, eq + 1, sizeof orcpt);
1736 		}
1737 
1738 		if (!isdigit(sig[0]) ||
1739 		    !isdigit(sig[1]) ||
1740 		    !isdigit(sig[2]) ||
1741 		    !isdigit(sig[3]) ||
1742 		    !isxdigit(sig[4]) ||
1743 		    !isxdigit(sig[5]) ||
1744 		    !isxdigit(sig[6]) ||
1745 		    !isxdigit(sig[7]) ||
1746 		    !isxdigit(sig[8]) ||
1747 		    !isxdigit(sig[9]))
1748 		{
1749 			if (dolog)
1750 			{
1751 				syslog(LOG_ERR,
1752 				       "%s: BATV syntax error in `%s'",
1753 				       bfc->ctx_jobid, addr);
1754 			}
1755 
1756 			rcpt->rcpt_verified = FALSE;
1757 			return SMFIS_TEMPFAIL;
1758 		}
1759 
1760 		sigversion = sig[0] - '0';
1761 		if (sigversion != version)
1762 		{
1763 			if (dolog)
1764 			{
1765 				syslog(LOG_ERR,
1766 				       "%s: BATV version mismatch in `%s'",
1767 				       bfc->ctx_jobid, addr);
1768 			}
1769 
1770 			if (addarhdr)
1771 			{
1772 				rcpt->rcpt_badversion = TRUE;
1773 				return SMFIS_CONTINUE;
1774 			}
1775 
1776 			return SMFIS_TEMPFAIL;
1777 		}
1778 
1779 		sigexpire = (sig[1] - '0') * 100 +
1780 		            (sig[2] - '0') * 10 +
1781 		            (sig[3] - '0');
1782 
1783 		memset(buf, '\0', sizeof buf);
1784 		buf[0] = sig[0];
1785 		buf[1] = sig[1];
1786 		buf[2] = sig[2];
1787 		buf[3] = sig[3];
1788 		sm_strlcat(buf, orcpt, sizeof buf);
1789 		sm_strlcat(buf, key, sizeof buf);
1790 
1791 		SHA1_Init(&sha1);
1792 		SHA1_Update(&sha1, buf, strlen(buf));
1793 		SHA1_Final(digest, &sha1);
1794 
1795 		if (sendmail)
1796 		{
1797 			char *at;
1798 			char tmpaddr[BUFRSZ + 1];
1799 
1800 			sm_strlcpy(tmpaddr, orcpt, sizeof tmpaddr);
1801 
1802 			at = strchr(tmpaddr, '@');
1803 			assert(at != NULL);
1804 			*at = '\0';
1805 
1806 			snprintf(tmp, sizeof tmp,
1807 			         "%s+prvs=%1d%03d%02X%02X%02X@%s",
1808 			         tmpaddr, sigversion, sigexpire,
1809 			         digest[0], digest[1], digest[2], at + 1);
1810 		}
1811 		else
1812 		{
1813 			snprintf(tmp, sizeof tmp,
1814 			         "prvs=%1d%03d%02X%02X%02X=%s",
1815 			         sigversion, sigexpire, digest[0], digest[1],
1816 			         digest[2], orcpt);
1817 		}
1818 
1819 		if (strcmp(tmp, addr) != 0)
1820 		{
1821 			rcpt->rcpt_verified = FALSE;
1822 
1823 			if (dolog)
1824 			{
1825 				syslog(LOG_ERR,
1826 				       "%s: BATV validation failed in `%s'",
1827 				       bfc->ctx_jobid, addr);
1828 			}
1829 
1830 			if (!notreally && !addarhdr)
1831 			{
1832 				if (setreply &&
1833 				    smfi_setreply(ctx, BATV_REJECTSMTP,
1834 				                  BATV_REJECTESC,
1835 				                  "BATV signature validation failed") == MI_FAILURE)
1836 				{
1837 					if (dolog)
1838 					{
1839 						syslog(LOG_ERR,
1840 						       "%s: smfi_setreply() failed",
1841 						       bfc->ctx_jobid);
1842 					}
1843 				}
1844 
1845 				return SMFIS_REJECT;
1846 			}
1847 		}
1848 
1849 		(void) time(&now);
1850 		daynum = (now / 86400) % 1000;
1851 
1852 		/* check expiration */
1853 		if (sigexpire < siglife && daynum >= 1000 - siglife)
1854 			daynum -= 1000;
1855 		if (daynum > sigexpire)
1856 		{
1857 			rcpt->rcpt_verified = FALSE;
1858 
1859 			if (dolog)
1860 			{
1861 				syslog(LOG_ERR,
1862 				       "%s: BATV signature expired in `%s'",
1863 				       bfc->ctx_jobid, addr);
1864 			}
1865 
1866 			if (!notreally && !addarhdr)
1867 			{
1868 				if (setreply &&
1869 				    smfi_setreply(ctx, BATV_REJECTSMTP,
1870 				                  BATV_REJECTESC,
1871 				                  "BATV signature expired") == MI_FAILURE)
1872 				{
1873 					if (dolog)
1874 					{
1875 						syslog(LOG_ERR,
1876 						       "%s: smfi_setreply() failed",
1877 						       bfc->ctx_jobid);
1878 					}
1879 				}
1880 
1881 				return SMFIS_REJECT;
1882 			}
1883 		}
1884 
1885 		/* new recipient is the original recipient */
1886 		rcpt->rcpt_new = strdup(orcpt);
1887 		if (rcpt->rcpt_new == NULL)
1888 		{
1889 			if (dolog)
1890 			{
1891 				syslog(LOG_ERR, "%s: strdup(): %s",
1892 				       bfc->ctx_jobid, strerror(errno));
1893 			}
1894 
1895 			return SMFIS_TEMPFAIL;
1896 		}
1897 	}
1898 
1899 	return SMFIS_CONTINUE;
1900 }
1901 
1902 /*
1903 **  MLFI_EOM -- end of message
1904 **
1905 **  Parameters:
1906 **  	ctx -- milter context
1907 **
1908 **  Return value:
1909 **  	SMFIS_CONTINUE
1910 */
1911 
1912 sfsistat
mlfi_eom(SMFICTX * ctx)1913 mlfi_eom(SMFICTX *ctx)
1914 {
1915 	bool doquarantine = FALSE;
1916 	CONTEXT bfc;
1917 	RCPTLIST rcpt;
1918 	connctx cc;
1919 
1920 	cc = smfi_getpriv(ctx);
1921 	assert(cc != NULL);
1922 	bfc = cc->cctx_msgctx;
1923 	assert(bfc != NULL);
1924 
1925 	if (bfc->ctx_newsender != NULL)
1926 	{
1927 		if (notreally)
1928 		{
1929 			syslog(LOG_INFO,
1930 			       "%s suppressed sender change from `%s' to `%s'",
1931 			       bfc->ctx_jobid, bfc->ctx_sender,
1932 			       bfc->ctx_newsender);
1933 		}
1934 		else if (smfi_chgfrom(ctx, bfc->ctx_newsender,
1935 #if SMFI_VERSION >= 0x01000001
1936 		                      bfc->ctx_esmtp) != MI_SUCCESS)
1937 #else /* SMFI_VERSION >= 0x01000001 */
1938 		                      NULL) != MI_SUCCESS)
1939 #endif /* SMFI_VERSION >= 0x01000001 */
1940 		{
1941 			if (dolog)
1942 			{
1943 				syslog(LOG_ERR,
1944 				       "%s sender change to `%s' failed",
1945 				       bfc->ctx_jobid, bfc->ctx_newsender);
1946 			}
1947 
1948 			return SMFIS_TEMPFAIL;
1949 		}
1950 	}
1951 
1952 	for (rcpt = bfc->ctx_rcpts; rcpt != NULL; rcpt = rcpt->rcpt_next)
1953 	{
1954 		assert(rcpt->rcpt_orig != NULL);
1955 
1956 		if (!rcpt->rcpt_signed || !rcpt->rcpt_verified)
1957 			doquarantine = TRUE;
1958 
1959 		if (addarhdr)
1960 		{
1961 			char arhdr[BATV_MAXHEADER + 1];
1962 
1963 			memset(arhdr, '\0', sizeof arhdr);
1964 
1965 			if (rcpt->rcpt_badversion)
1966 			{
1967 				snprintf(arhdr, sizeof arhdr,
1968 				         "%s%s%s%s; x-batv=%s smtp.rcpt=%s",
1969 				         cc->cctx_noleadspc ? " " : "",
1970 				         cc->cctx_mtaname,
1971 				         authservjobid ? "/" : "",
1972 				         authservjobid ? bfc->ctx_jobid : "",
1973 				         "fail (version mismatch)",
1974 				         rcpt->rcpt_orig);
1975 			}
1976 			else if (rcpt->rcpt_signed)
1977 			{
1978 				snprintf(arhdr, sizeof arhdr,
1979 				         "%s%s%s%s; x-batv=%s smtp.rcpt=%s",
1980 				         cc->cctx_noleadspc ? " " : "",
1981 				         cc->cctx_mtaname,
1982 				         authservjobid ? "/" : "",
1983 				         authservjobid ? bfc->ctx_jobid : "",
1984 				         rcpt->rcpt_verified ? "pass"
1985 				                             : "fail (verification failed)",
1986 				         rcpt->rcpt_orig);
1987 			}
1988 			else if (!rcpt->rcpt_signed)
1989 			{
1990 				snprintf(arhdr, sizeof arhdr,
1991 				         "%s%s%s%s; x-batv=%s smtp.rcpt=%s",
1992 				         cc->cctx_noleadspc ? " " : "",
1993 				         cc->cctx_mtaname,
1994 				         authservjobid ? "/" : "",
1995 				         authservjobid ? bfc->ctx_jobid : "",
1996 				         "fail (not signed)", rcpt->rcpt_orig);
1997 			}
1998 
1999 			if (arhdr[0] != '\0' &&
2000 			    smfi_insheader(ctx, 1, AUTHRESULTSHDR,
2001 			                   arhdr) != MI_SUCCESS)
2002 			{
2003 				if (dolog)
2004 				{
2005 					syslog(LOG_ERR,
2006 					       "%s \"%s\" header add failed",
2007 					       bfc->ctx_jobid, AUTHRESULTSHDR);
2008 				}
2009 
2010 				return SMFIS_TEMPFAIL;
2011 			}
2012 		}
2013 
2014 		if (rcpt->rcpt_new != NULL)
2015 		{
2016 			if (notreally)
2017 			{
2018 				syslog(LOG_INFO,
2019 				       "%s suppressed recipient change from `%s' to `%s'",
2020 				       bfc->ctx_jobid, rcpt->rcpt_orig,
2021 				       rcpt->rcpt_new);
2022 				continue;
2023 			}
2024 
2025 			if (smfi_delrcpt(ctx, rcpt->rcpt_orig) != MI_SUCCESS)
2026 			{
2027 				if (dolog)
2028 				{
2029 					syslog(LOG_ERR,
2030 					       "%s recipient delete of `%s' failed",
2031 					       bfc->ctx_jobid, rcpt->rcpt_orig);
2032 				}
2033 
2034 				return SMFIS_TEMPFAIL;
2035 			}
2036 
2037 			if (smfi_addrcpt(ctx, rcpt->rcpt_new) != MI_SUCCESS)
2038 			{
2039 				if (dolog)
2040 				{
2041 					syslog(LOG_ERR,
2042 					       "%s recipient add of `%s' failed",
2043 					       bfc->ctx_jobid, rcpt->rcpt_new);
2044 				}
2045 
2046 				return SMFIS_TEMPFAIL;
2047 			}
2048 		}
2049 	}
2050 
2051 	if (addxhdr)
2052 	{
2053 		char xfhdr[BATV_MAXHEADER + 1];
2054 
2055 		memset(xfhdr, '\0', sizeof xfhdr);
2056 
2057 		snprintf(xfhdr, BATV_MAXHEADER, "%s v%s %s %s", BATV_PRODUCT,
2058 		         BATV_VERSION, hostname,
2059 		         bfc->ctx_jobid != NULL ? bfc->ctx_jobid
2060 		                                : JOBIDUNKNOWN);
2061 
2062 		if (smfi_insheader(ctx, 1, XHEADERNAME, xfhdr) != MI_SUCCESS)
2063 		{
2064 			if (dolog)
2065 			{
2066 				syslog(LOG_ERR, "%s \"%s\" header add failed",
2067 				       bfc->ctx_jobid, XHEADERNAME);
2068 			}
2069 
2070 			return SMFIS_TEMPFAIL;
2071 		}
2072 	}
2073 
2074 	if (doquarantine && quarantine)
2075 	{
2076 		if (smfi_quarantine(ctx, QREASON) != MI_SUCCESS)
2077 		{
2078 			if (dolog)
2079 			{
2080 				syslog(LOG_ERR, "%s smfi_quarantine",
2081 				       bfc->ctx_jobid);
2082 			}
2083 
2084 			return SMFIS_TEMPFAIL;
2085 		}
2086 	}
2087 
2088 	return SMFIS_CONTINUE;
2089 }
2090 
2091 /*
2092 **  MLFI_CLOSE -- shut down a connection
2093 **
2094 **  Parameters:
2095 **  	ctx -- milter context
2096 **
2097 **  Return value:
2098 **  	SMFIS_CONTINUE
2099 */
2100 
2101 sfsistat
mlfi_close(SMFICTX * ctx)2102 mlfi_close(SMFICTX *ctx)
2103 {
2104 	connctx cc;
2105 
2106 	cc = smfi_getpriv(ctx);
2107 
2108 	if (cc != NULL)
2109 	{
2110 		batv_cleanup(cc, FALSE);
2111 
2112 		free(cc);
2113 
2114 		smfi_setpriv(ctx, NULL);
2115 	}
2116 
2117 	return SMFIS_CONTINUE;
2118 }
2119 
2120 /*
2121 **  ============================= GENERAL STUFF =============================
2122 */
2123 
2124 /*
2125 **  BATV_DEBUG -- debugging code; simulates libmilter calls
2126 **
2127 **  Parameters:
2128 **  	None.
2129 **
2130 **  Return value:
2131 **  	None.
2132 */
2133 
2134 #ifdef DEBUG
2135 int
batv_debug(void)2136 batv_debug(void)
2137 {
2138 	bool done;
2139 	int status;
2140 	size_t len;
2141 	time_t now;
2142 	char *p;
2143 	char *env[2];
2144 	char tmpblock[4096];
2145 	char data[513];
2146 	char block[4096];
2147 
2148 	time(&now);
2149 	srandom(now);
2150 
2151 	memset(data, '\0', sizeof data);
2152 	memset(tmpblock, '\0', sizeof tmpblock);
2153 
2154 	for (;;)
2155 	{
2156 		if (fgets(data, 512, stdin) == NULL)
2157 			return 1;
2158 
2159 		for (p = data; *p != '\0'; p++)
2160 		{
2161 			if (*p == '\r' || *p == '\n')
2162 			{
2163 				*p = '\0';
2164 				break;
2165 			}
2166 		}
2167 
2168 		if (strcmp(data, ".") == 0)
2169 			break;
2170 
2171 		env[0] = &data[1];
2172 		env[1] = NULL;
2173 
2174 		if (data[0] == 'C')
2175 		{
2176 			struct hostent *h;
2177 			struct sockaddr_in in;
2178 			unsigned long l0, l1, l2, l3, l4, l5, l6, l7;
2179 
2180 			h = gethostbyname(&data[1]);
2181 			if (h == NULL)
2182 			{
2183 				printf("gethostbyname(\"%s\") failed\n",
2184 				       &data[1]);
2185 				return 1;
2186 			}
2187 			in.sin_family = AF_INET;
2188 			in.sin_port = htons(time(NULL) % 65536);
2189 			memcpy(&in.sin_addr.s_addr, h->h_addr,
2190 			       sizeof in.sin_addr.s_addr);
2191 
2192 			l0 = 0xffffffffL;
2193 			l1 = 0xffffffffL;
2194 			l2 = 0;
2195 			l3 = 0;
2196 			l4 = 0;
2197 			l5 = 0;
2198 			l6 = 0;
2199 			l7 = 0;
2200 			status = mlfi_negotiate(NULL, l0, l1, l2, l3,
2201 			                        &l4, &l5, &l6, &l7);
2202 			printf("mlfi_negotiate(...) returns %s\n",
2203 			       smfis_ret[status]);
2204 
2205 			status = mlfi_connect(NULL, &data[1],
2206 			                      (_SOCK_ADDR *) &in);
2207 			printf("mlfi_connect(NULL, `%s', `%s') returns %s\n",
2208 			       &data[1], inet_ntoa(in.sin_addr),
2209 			       smfis_ret[status]);
2210 		}
2211 		else if (data[0] == 'F')
2212 		{
2213 			status = mlfi_envfrom(NULL, env);
2214 			printf("mlfi_envfrom(NULL, `%s') returns %s\n", env[0],
2215 	       		       smfis_ret[status]);
2216 		}
2217 		else if (data[0] == 'R')
2218 		{
2219 			status = mlfi_envrcpt(NULL, env);
2220 			printf("mlfi_envrcpt(NULL, `%s') returns %s\n", env[0],
2221 	       		       smfis_ret[status]);
2222 		}
2223 		else
2224 		{
2225 			printf("?\n");
2226 			continue;
2227 		}
2228 		if (status != SMFIS_CONTINUE)
2229 			return 0;
2230 	}
2231 
2232 	done = FALSE;
2233 	while (!done)
2234 	{
2235 		len = fread(block, 1, sizeof block, stdin);
2236 
2237 		if (len < sizeof block)
2238 			done = TRUE;
2239 	}
2240 
2241 	status = mlfi_eom(NULL);
2242 	printf("mlfi_eom(NULL) returns %s\n", smfis_ret[status]);
2243 	return 0;
2244 }
2245 #endif /* DEBUG */
2246 
2247 /*
2248 **  USAGE -- usage message
2249 **
2250 **  Parameters:
2251 **  	None.
2252 **
2253 **  Return value:
2254 **  	EX_USAGE
2255 */
2256 
2257 int
usage(void)2258 usage(void)
2259 {
2260 	fprintf(stderr, "%s: usage: %s -p socketfile -k key [options]\n"
2261 	                "\t-a allowaddrs         \tfile containing addresses to pass\n"
2262 	                "\t-A                    \tenable auto-restart\n"
2263 	                "\t-c conffile           \tconfiguration file\n"
2264 	                "\t-d domainlist         \tdomain(s) to verify\n"
2265 	                "\t-D debuglevel         \tset milter debug level\n"
2266 	                "\t-f                    \tdon't fork-and-exit\n"
2267 	                "\t-i internallist       \tinternal host list\n"
2268 	                "\t-l                    \tlog activity to system log\n"
2269 	                "\t-m mtalist            \tMTA name(s) for which to sign\n"
2270 	                "\t-M macro[=value][,...]\tmacro tests for signing\n"
2271 	                "\t-n                    \tdon't actually do any filtering\n"
2272 	                "\t-P pidfile            \tfile to which to write pid\n"
2273 	                "\t-q                    \tquarantine messages that fail verification\n"
2274 	                "\t-r                    \tadd header instead of filtering\n"
2275 	                "\t-s                    \tskip mail from authenticated clients\n"
2276 	                "\t-S                    \trequest meaningful SMTP replies when rejecting\n"
2277 			"\t-t ttl                \tsignature time-to-live (days)\n"
2278 			"\t-u userid             \tchange to specified userid\n"
2279 			"\t-v version            \tkey version\n"
2280 	                "\t-V                    \tprint version and terminate\n"
2281 	                "\t-x                    \texperimental sendmail mode\n",
2282 	        progname, progname);
2283 	return EX_USAGE;
2284 }
2285 
2286 #ifndef DEBUG
2287 /*
2288 **  smfilter -- the milter module description
2289 */
2290 
2291 struct smfiDesc smfilter =
2292 {
2293 	"Sendmail BATV Filter",	/* filter name */
2294 	SMFI_VERSION,		/* version code -- do not change */
2295 	SMFIF_ADDHDRS|SMFIF_CHGFROM|SMFIF_DELRCPT|SMFIF_ADDRCPT, /* flags */
2296 	mlfi_connect,		/* connection info filter */
2297 	NULL,			/* SMTP HELO command filter */
2298 	mlfi_envfrom,		/* envelope sender filter */
2299 	mlfi_envrcpt,		/* envelope recipient filter */
2300 	NULL,			/* header filter */
2301 	NULL,			/* end of headers */
2302 	NULL,			/* body block filter */
2303 	mlfi_eom,		/* end of message */
2304 	NULL,			/* message aborted */
2305 	mlfi_close,		/* shutdown */
2306 	NULL,			/* unknown commands */
2307 	NULL,			/* DATA command */
2308 	mlfi_negotiate		/* option negotiation */
2309 };
2310 #endif /* ! DEBUG */
2311 
2312 /*
2313 **  MAIN -- program mainline
2314 **
2315 **  Parameters:
2316 **  	The usual.
2317 **
2318 **  Return value:
2319 **  	Exit status.
2320 */
2321 
2322 int
main(int argc,char ** argv)2323 main(int argc, char **argv)
2324 {
2325 	bool autorestart = FALSE;
2326 	bool dofork = TRUE;
2327 	bool enablecores = FALSE;
2328 	int c;
2329 	int dbglevel = -1;
2330 #ifndef DEBUG
2331 	int devnull;
2332 #endif /* ! DEBUG */
2333 	int status;
2334 	int maxrestarts = 0;
2335 	int maxrestartrate_n = 0;
2336 	size_t rlen;
2337 	time_t maxrestartrate_t = 0;
2338 	FILE *f;
2339 	char *p;
2340 	char *conn = NULL;
2341 	char *user = NULL;
2342 	char *domainlist = NULL;
2343 	char *mtanames = NULL;
2344 	char *ilist = NULL;
2345 	char *pidfile = NULL;
2346 	char *allowlist = NULL;
2347 	char *macrolist = NULL;
2348 	char *keyfile = NULL;
2349 	char *conffile = NULL;
2350 	char *basedir = NULL;
2351 	char *facility = NULL;
2352 	struct stat s;
2353 
2354 	progname = (p = strrchr(argv[0], '/')) == NULL ? argv[0] : p + 1;
2355 
2356 	addxhdr = FALSE;
2357 	addarhdr = FALSE;
2358 	notreally = FALSE;
2359 	quarantine = FALSE;
2360 	die = FALSE;
2361 	dolog = FALSE;
2362 	logwhy = FALSE;
2363 	authservjobid = FALSE;
2364 	bounceonly = TRUE;
2365 	skipauth = FALSE;
2366 	sendmail = FALSE;
2367 	setreply = FALSE;
2368 	key = NULL;
2369 	allow = NULL;
2370 	signlist = NULL;
2371 	authservid = NULL;
2372 	macros = NULL;
2373 	values = NULL;
2374 	mtas = NULL;
2375 	peerlist = NULL;
2376 	version = (unsigned int) -1;
2377 	siglife = (unsigned int) -1;
2378 	opmode = BATV_MODE_DEFAULT;
2379 
2380 	memset(hostname, '\0', sizeof hostname);
2381 	gethostname(hostname, MAXHOSTNAMELEN);
2382 
2383 	/* Process command line options */
2384 	while ((c = getopt(argc, argv, CMDLINEOPTS)) != -1)
2385 	{
2386 		switch (c)
2387 		{
2388 		  case 'a':
2389 			if (allowlist != NULL)
2390 			{
2391 				fprintf(stderr,
2392 				        "%s: multiple use of -%c not allowed\n",
2393 				        progname, c);
2394 				return EX_USAGE;
2395 			}
2396 			allowlist = optarg;
2397 			break;
2398 
2399 		  case 'A':
2400 			autorestart = TRUE;
2401 			break;
2402 
2403 		  case 'b':
2404 			bounceonly = FALSE;
2405 			break;
2406 
2407 		  case 'c':
2408 			if (conffile != NULL)
2409 			{
2410 				fprintf(stderr,
2411 				        "%s: multiple use of -%c not allowed\n",
2412 				        progname, c);
2413 				return EX_USAGE;
2414 			}
2415 			conffile = optarg;
2416 			break;
2417 
2418 		  case 'd':
2419 			if (domainlist != NULL)
2420 			{
2421 				fprintf(stderr,
2422 				        "%s: multiple use of -%c not allowed\n",
2423 				        progname, c);
2424 				return EX_USAGE;
2425 			}
2426 			domainlist = optarg;
2427 			break;
2428 
2429 		  case 'D':
2430 			dbglevel = (int) strtol(optarg, &p, 10);
2431 			if (*p != '\0')
2432 			{
2433 				fprintf(stderr,
2434 				        "%s: invalid debug level `%s'\n",
2435 				        progname, optarg);
2436 				return EX_USAGE;
2437 			}
2438 			smfi_setdbg(dbglevel);
2439 			break;
2440 
2441 		  case 'f':
2442 			dofork = FALSE;
2443 			break;
2444 
2445 		  case 'h':
2446 			addxhdr = TRUE;
2447 			break;
2448 
2449 		  case 'i':
2450 			if (ilist != NULL)
2451 			{
2452 				fprintf(stderr,
2453 				        "%s: multiple use of -%c not allowed\n",
2454 				        progname, c);
2455 				return EX_USAGE;
2456 			}
2457 			ilist = optarg;
2458 			break;
2459 
2460 		  case 'k':
2461 			if (keyfile != NULL)
2462 			{
2463 				fprintf(stderr,
2464 				        "%s: multiple use of -%c not allowed\n",
2465 				        progname, c);
2466 				return EX_USAGE;
2467 			}
2468 			keyfile = optarg;
2469 			break;
2470 
2471 		  case 'l':
2472 			dolog = TRUE;
2473 			break;
2474 
2475 		  case 'm':
2476 			mtanames = optarg;
2477 			break;
2478 
2479 		  case 'M':
2480 			macrolist = optarg;
2481 			break;
2482 
2483 		  case 'n':
2484 			notreally = TRUE;
2485 			break;
2486 
2487 		  case 'p':
2488 			if (conn != NULL)
2489 			{
2490 				fprintf(stderr,
2491 				        "%s: multiple use of -%c not allowed\n",
2492 				        progname, c);
2493 				return EX_USAGE;
2494 			}
2495 			conn = optarg;
2496 			break;
2497 
2498 		  case 'P':
2499 			if (pidfile != NULL)
2500 			{
2501 				fprintf(stderr,
2502 				        "%s: multiple use of -%c not allowed\n",
2503 				        progname, c);
2504 				return EX_USAGE;
2505 			}
2506 			pidfile = optarg;
2507 			break;
2508 
2509 		  case 'q':
2510 			quarantine = TRUE;
2511 			break;
2512 
2513 		  case 'r':
2514 			addarhdr = TRUE;
2515 			break;
2516 
2517 		  case 's':
2518 			skipauth = TRUE;
2519 			break;
2520 
2521 		  case 'S':
2522 			setreply = TRUE;
2523 			break;
2524 
2525 		  case 't':
2526 			errno = 0;
2527 			siglife = (u_int) strtoul(optarg, &p, 10);
2528 			if (*p != '\0' || errno == ERANGE)
2529 			{
2530 				fprintf(stderr,
2531 				        "%s: invalid signature TTL `%s'\n",
2532 				        progname, optarg);
2533 				return EX_USAGE;
2534 			}
2535 			break;
2536 
2537 		  case 'u':
2538 			if (user != NULL)
2539 			{
2540 				fprintf(stderr,
2541 				        "%s: multiple use of -%c not allowed\n",
2542 				        progname, c);
2543 				return EX_USAGE;
2544 			}
2545 			user = optarg;
2546 			break;
2547 
2548 		  case 'v':
2549 			errno = 0;
2550 			version = (u_int) strtoul(optarg, &p, 10);
2551 			if (*p != '\0' || errno == ERANGE || version > 9)
2552 			{
2553 				fprintf(stderr,
2554 				        "%s: invalid key version `%s'\n",
2555 				        progname, optarg);
2556 				return EX_USAGE;
2557 			}
2558 			break;
2559 
2560 		  case 'V':
2561 			fprintf(stdout, "%s: %s v%s\n", progname,
2562 			        BATV_PRODUCT, BATV_VERSION);
2563 			return 0;
2564 
2565 		  case 'x':
2566 			sendmail = TRUE;
2567 			break;
2568 
2569 		  default:
2570 			return usage();
2571 		}
2572 	}
2573 
2574 	if (conffile != NULL)
2575 	{
2576 		int line;
2577 		struct config *cfg;
2578 		char *missing;
2579 		char *tmp;
2580 		char path[MAXPATHLEN + 1];
2581 
2582 		sm_strlcpy(path, conffile, sizeof path);
2583 
2584 		cfg = config_load(conffile, batvf_config, &line,
2585 		                  path, sizeof path);
2586 
2587 		if (cfg == NULL)
2588 		{
2589 			fprintf(stderr,
2590 			        "%s: %s: configuration error at line %u: %s\n",
2591 			        progname, path, line, config_error());
2592 
2593 			return EX_CONFIG;
2594 		}
2595 
2596 		missing = config_check(cfg, batvf_config);
2597 		if (missing != NULL)
2598 		{
2599 			if (dolog)
2600 			{
2601 				fprintf(stderr,
2602 				        "%s: %s: required parameter \"%s\" missing\n",
2603 				        progname, conffile, missing);
2604 			}
2605 
2606 			config_free(cfg);
2607 			return EX_CONFIG;
2608 		}
2609 
2610 		/*
2611 		**  Process config file contents.
2612 		*/
2613 
2614 		/* "AddAuthenticationResults" */
2615 		if (!addarhdr)
2616 		{
2617 			(void) config_get(cfg, "AddAuthenticationResults",
2618 			                  &addarhdr, sizeof addarhdr);
2619 		}
2620 
2621 		/* "AllSenders" */
2622 		if (bounceonly)
2623 		{
2624 			(void) config_get(cfg, "AllSenders",
2625 			                  &bounceonly, sizeof bounceonly);
2626 		}
2627 
2628 		/* "AuthservID" */
2629 		(void) config_get(cfg, "AuthservID",
2630 		                  &authservid, sizeof authservid);
2631 
2632 		/* "AuthservIDWithJobID" */
2633 		(void) config_get(cfg, "AuthservIDWithJobID",
2634 		                  &authservjobid, sizeof authservjobid);
2635 
2636 		/* "AutoRestart" */
2637 		if (!autorestart)
2638 		{
2639 			(void) config_get(cfg, "AutoRestart",
2640 			                  &autorestart, sizeof autorestart);
2641 		}
2642 
2643 		/* "AutoRestartCount" and "AutoRestartRate" */
2644 		if (autorestart)
2645 		{
2646 			char *rate;
2647 
2648 			(void) config_get(cfg, "AutoRestartCount",
2649 			                  &maxrestarts, sizeof maxrestarts);
2650 
2651 			(void) config_get(cfg, "AutoRestartRate", &rate,
2652 			                  sizeof rate);
2653 
2654 			if (rate != NULL)
2655 			{
2656 				int n;
2657 				time_t t;
2658 				char *q;
2659 
2660 				p = strchr(rate, '/');
2661 				if (p == NULL)
2662 				{
2663 					fprintf(stderr,
2664 					        "%s: AutoRestartRate invalid\n",
2665 					        progname);
2666 
2667 					config_free(cfg);
2668 					return EX_CONFIG;
2669 				}
2670 
2671 				*p = '\0';
2672 				n = strtol(rate, &q, 10);
2673 				if (n < 0 || *q != '\0')
2674 				{
2675 					fprintf(stderr,
2676 					        "%s: AutoRestartRate invalid\n",
2677 					        progname);
2678 
2679 					config_free(cfg);
2680 					return EX_CONFIG;
2681 				}
2682 
2683 				t = (time_t) strtoul(p + 1, &q, 10);
2684 				switch (*q)
2685 				{
2686 				  case 'd':
2687 				  case 'D':
2688 					t *= 86400;
2689 					break;
2690 
2691 				  case 'h':
2692 				  case 'H':
2693 					t *= 3600;
2694 					break;
2695 
2696 				  case 'm':
2697 				  case 'M':
2698 					t *= 60;
2699 					break;
2700 
2701 				  case '\0':
2702 				  case 's':
2703 				  case 'S':
2704 					break;
2705 
2706 				  default:
2707 					t = 0;
2708 					break;
2709 				}
2710 
2711 				if (*q != '\0' && *(q + 1) != '\0')
2712 					t = 0;
2713 
2714 				if (t == 0)
2715 				{
2716 					fprintf(stderr,
2717 					        "%s: AutoRestartRate invalid\n",
2718 					        progname);
2719 
2720 					config_free(cfg);
2721 					return EX_CONFIG;
2722 				}
2723 
2724 				maxrestartrate_n = n;
2725 				maxrestartrate_t = t;
2726 			}
2727 		}
2728 
2729 		/* "Background" */
2730 		if (dofork)
2731 		{
2732 			(void) config_get(cfg, "Background",
2733 			                  &dofork, sizeof dofork);
2734 		}
2735 
2736 		/* "BaseDirectory" */
2737 		(void) config_get(cfg, "BaseDirectory",
2738 		                  &basedir, sizeof basedir);
2739 
2740 		/* "Domain" */
2741 		if (domainlist == NULL)
2742 		{
2743 			(void) config_get(cfg, "Domain",
2744 			                  &domainlist, sizeof domainlist);
2745 		}
2746 
2747 		/* "DontSignMailTo" */
2748 		if (allowlist == NULL)
2749 		{
2750 			(void) config_get(cfg, "DontSignMailTo",
2751 			                  &allowlist, sizeof allowlist);
2752 		}
2753 
2754 		/* "EnableCoredumps" */
2755 		(void) config_get(cfg, "EnableCoredumps",
2756 		                  &enablecores, sizeof enablecores);
2757 
2758 		/* "IgnoreAuthenticatedClients" */
2759 		if (!skipauth)
2760 		{
2761 			(void) config_get(cfg, "IgnoreAuthenticatedClients",
2762 			                  &skipauth, sizeof skipauth);
2763 		}
2764 
2765 		/* "InternalHosts" */
2766 		if (ilist == NULL)
2767 		{
2768 			(void) config_get(cfg, "InternalHosts",
2769 			                  &ilist, sizeof ilist);
2770 		}
2771 
2772 		/* "KeyFile" */
2773 		if (keyfile == NULL)
2774 		{
2775 			(void) config_get(cfg, "KeyFile",
2776 			                  &keyfile, sizeof keyfile);
2777 		}
2778 
2779 		/* XXX -- "KeyList" */
2780 
2781 		/* "KeyVersion" */
2782 		if (version == (unsigned int) -1)
2783 		{
2784 			(void) config_get(cfg, "KeyVersion",
2785 			                  &version, sizeof version);
2786 
2787 			if (version != (unsigned int) -1 && version > 9)
2788 			{
2789 				fprintf(stderr, "%s: %s: invalid key version\n",
2790 				        progname, conffile);
2791 
2792 				config_free(cfg);
2793 				return EX_CONFIG;
2794 			}
2795 		}
2796 
2797 		/* "LogWhy" */
2798 		(void) config_get(cfg, "LogWhy", &logwhy, sizeof logwhy);
2799 
2800 		/* "MacroList" */
2801 		if (macrolist == NULL)
2802 		{
2803 			(void) config_get(cfg, "MacroList",
2804 			                  &macrolist, sizeof macrolist);
2805 		}
2806 
2807 		/* "MilterDebug" */
2808 		if (dbglevel == -1)
2809 		{
2810 			(void) config_get(cfg, "MilterDebug",
2811 			                  &dbglevel, sizeof dbglevel);
2812 			(void) smfi_setdbg(dbglevel);
2813 		}
2814 
2815 		/* "Mode" */
2816 		tmp = NULL;
2817 		(void) config_get(cfg, "Mode", &tmp, sizeof tmp);
2818 		if (tmp != NULL)
2819 		{
2820 			unsigned int newmode = 0;
2821 
2822 			for (p = tmp; *p != '\0'; p++)
2823 			{
2824 				switch (*p)
2825 				{
2826 				  case 's':
2827 					newmode |= BATV_MODE_SIGN;
2828 					break;
2829 
2830 				  case 'v':
2831 					newmode |= BATV_MODE_VERIFY;
2832 					break;
2833 
2834 				  default:
2835 					fprintf(stderr,
2836 					        "%s: %s: invalid mode\n",
2837 					        progname, conffile);
2838 
2839 					config_free(cfg);
2840 					return EX_CONFIG;
2841 				}
2842 			}
2843 
2844 			opmode = newmode;
2845 		}
2846 
2847 		/* "MTA" */
2848 		if (mtanames == NULL)
2849 		{
2850 			(void) config_get(cfg, "MTA",
2851 			                  &mtanames, sizeof mtanames);
2852 		}
2853 
2854 		/* "PeerList" */
2855 		tmp = NULL;
2856 		(void) config_get(cfg, "PeerList", &tmp, sizeof tmp);
2857 		if (tmp != NULL)
2858 		{
2859 			f = fopen(tmp, "r");
2860 			if (f == NULL)
2861 			{
2862 				fprintf(stderr, "%s: %s: fopen(): %s\n",
2863 				        progname, tmp, strerror(errno));
2864 
2865 				config_free(cfg);
2866 				return EX_UNAVAILABLE;
2867 			}
2868 
2869 			if (!batvf_load_list(f, &peerlist))
2870 			{
2871 				fprintf(stderr,
2872 				        "%s: failed to load PeerList from %s\n",
2873 				        progname, tmp);
2874 
2875 				fclose(f);
2876 
2877 				config_free(cfg);
2878 				return EX_UNAVAILABLE;
2879 			}
2880 
2881 			fclose(f);
2882 		}
2883 
2884 		/* "PidFile" */
2885 		if (pidfile == NULL)
2886 		{
2887 			(void) config_get(cfg, "PidFile",
2888 			                  &pidfile, sizeof pidfile);
2889 		}
2890 
2891 		/* "Quarantine" */
2892 		if (!quarantine)
2893 		{
2894 			(void) config_get(cfg, "Quarantine",
2895 			                  &quarantine, sizeof quarantine);
2896 		}
2897 
2898 		/* XXX -- "RemoveARAll" */
2899 		/* XXX -- "RemoveARFrom" */
2900 
2901 		/* "SendmailFormat" */
2902 		if (!sendmail)
2903 		{
2904 			(void) config_get(cfg, "SendmailFormat",
2905 			                  &sendmail, sizeof sendmail);
2906 		}
2907 
2908 		/* "SetReplies" */
2909 		if (!setreply)
2910 		{
2911 			(void) config_get(cfg, "SetReplies",
2912 			                  &setreply, sizeof setreply);
2913 		}
2914 
2915 		/* "SignatureTTL" */
2916 		if (siglife == (unsigned int) -1)
2917 		{
2918 			(void) config_get(cfg, "SignatureTTL",
2919 			                  &siglife, sizeof siglife);
2920 		}
2921 
2922 		/* "Socket" */
2923 		if (conn == NULL)
2924 			(void) config_get(cfg, "Socket", &conn, sizeof conn);
2925 
2926 		/* "Syslog" */
2927 		if (!dolog)
2928 		{
2929 			(void) config_get(cfg, "Syslog",
2930 			                  &dolog, sizeof dolog);
2931 		}
2932 
2933 		/* "SyslogFacility" */
2934 		(void) config_get(cfg, "SyslogFacility",
2935 		                  &facility, sizeof facility);
2936 
2937 		/* "TestMode" */
2938 		if (!notreally)
2939 		{
2940 			(void) config_get(cfg, "TestMode",
2941 			                  &notreally, sizeof notreally);
2942 		}
2943 
2944 		/* "UserID" */
2945 		if (user == NULL)
2946 			(void) config_get(cfg, "UserID", &user, sizeof user);
2947 
2948 		/* "UserID" */
2949 		if (!addxhdr)
2950 		{
2951 			(void) config_get(cfg, "X-Header",
2952 			                  &addxhdr, sizeof addxhdr);
2953 		}
2954 	}
2955 
2956 	if (enablecores)
2957 	{
2958 		bool enabled = FALSE;
2959 
2960 #ifdef __linux__
2961 		if (prctl(PR_SET_DUMPABLE, 1) == -1)
2962 		{
2963 			if (dolog)
2964 			{
2965 				syslog(LOG_ERR, "prctl(): %s",
2966 				       strerror(errno));
2967 			}
2968 
2969 			fprintf(stderr, "%s: prctl(): %s\n",
2970 			        progname, strerror(errno));
2971 		}
2972 		else
2973 		{
2974 			enabled = TRUE;
2975 		}
2976 #endif /* __linux__ */
2977 
2978 		if (!enabled)
2979 		{
2980 			if (dolog)
2981 			{
2982 				syslog(LOG_WARNING,
2983 				       "can't enable coredumps; continuing");
2984 			}
2985 
2986 			fprintf(stderr,
2987 			        "%s: can't enable coredumps; continuing\n",
2988 			        progname);
2989 		}
2990 	}
2991 
2992 	/* conn is required */
2993 	if (conn == NULL || keyfile == NULL)
2994 		return usage();
2995 
2996 	/* assert default version */
2997 	if (version == (unsigned int) -1)
2998 		version = 0;
2999 
3000 	/* assert default signature lifetime */
3001 	if (siglife == (unsigned int) -1)
3002 		siglife = DEFSIGLIFETIME;
3003 
3004 	/* change to base directory if requested */
3005 	if (basedir != NULL)
3006 	{
3007 		status = chdir(basedir);
3008 		if (status != 0)
3009 		{
3010 			fprintf(stderr, "%s: %s: chdir(): %s\n",
3011 			        progname, basedir, strerror(errno));
3012 
3013 			return EX_UNAVAILABLE;
3014 		}
3015 	}
3016 
3017 	/* load the key */
3018 	f = fopen(keyfile, "r");
3019 	if (f == NULL)
3020 	{
3021 		fprintf(stderr, "%s: %s: fopen(): %s\n", progname, keyfile,
3022 		        strerror(errno));
3023 		return EX_UNAVAILABLE;
3024 	}
3025 	else if (fstat(fileno(f), &s) == -1)
3026 	{
3027 		fprintf(stderr, "%s: %s: fstat(): %s\n", progname, keyfile,
3028 		        strerror(errno));
3029 		fclose(f);
3030 		return EX_UNAVAILABLE;
3031 	}
3032 	else if (s.st_size == 0)
3033 	{
3034 		fprintf(stderr, "%s: %s: empty file\n", progname, keyfile);
3035 		fclose(f);
3036 		return EX_UNAVAILABLE;
3037 	}
3038 
3039 	key = (char *) malloc(s.st_size);
3040 	if (key == NULL)
3041 	{
3042 		fprintf(stderr, "%s: malloc(): %s\n", progname,
3043 		        strerror(errno));
3044 		fclose(f);
3045 		return EX_UNAVAILABLE;
3046 	}
3047 	rlen = fread(key, 1, s.st_size, f);
3048 	if (rlen < s.st_size)
3049 	{
3050 		if (ferror(f))
3051 		{
3052 			fprintf(stderr, "%s: %s: fread(): %s\n", progname,
3053 			        keyfile, strerror(errno));
3054 		}
3055 		else if (feof(f))
3056 		{
3057 			fprintf(stderr,
3058 			        "%s: %s: fread(): unexpected end-of-file\n",
3059 			        progname, keyfile);
3060 		}
3061 
3062 		fclose(f);
3063 		return EX_UNAVAILABLE;
3064 	}
3065 
3066 	/* chop the key at the first newline found */
3067 	for (p = key, c = 0; c < s.st_size; p++, c++)
3068 	{
3069 		if (*p == '\n')
3070 		{
3071 			*p = '\0';
3072 			break;
3073 		}
3074 	}
3075 
3076 	fclose(f);
3077 
3078 	/* load allow list */
3079 	if (allowlist != NULL)
3080 	{
3081 		RELIST new;
3082 		char buf[BUFRSZ + 1];
3083 		char restr[BUFRSZ + 1];
3084 
3085 		f = fopen(allowlist, "r");
3086 		if (f == NULL)
3087 		{
3088 			fprintf(stderr, "%s: %s: fopen(): %s\n", progname,
3089 			        allowlist, strerror(errno));
3090 			free(key);
3091 			return EX_UNAVAILABLE;
3092 		}
3093 
3094 		memset(buf, '\0', sizeof buf);
3095 		while (fgets(buf, sizeof buf - 1 ,f) != NULL)
3096 		{
3097 			for (p = buf; *p != '\0'; p++)
3098 			{
3099 				if (*p == '\n' || *p == '#')
3100 				{
3101 					*p = '\0';
3102 					break;
3103 				}
3104 			}
3105 
3106 			if (batv_isblank(buf))
3107 				continue;
3108 
3109 			batv_mkregexp(buf, restr, sizeof restr);
3110 
3111 			new = (RELIST) malloc(sizeof *new);
3112 			if (new == NULL)
3113 			{
3114 				fprintf(stderr, "%s: malloc(): %s\n",
3115 				        progname, strerror(errno));
3116 				fclose(f);
3117 				return EX_UNAVAILABLE;
3118 			}
3119 
3120 			status = regcomp(&new->re_re, restr, REG_ICASE);
3121 			if (status != 0)
3122 			{
3123 				char errbuf[BUFRSZ + 1];
3124 
3125 				memset(errbuf, '\0', sizeof errbuf);
3126 				(void) regerror(status, &new->re_re, errbuf,
3127 				                sizeof errbuf);
3128 
3129 				fprintf(stderr, "%s: regcomp(): %s\n",
3130 				        progname, errbuf);
3131 				fclose(f);
3132 				return EX_UNAVAILABLE;
3133 			}
3134 
3135 			new->re_next = allow;
3136 			allow = new;
3137 		}
3138 
3139 		if (ferror(f))
3140 		{
3141 			fprintf(stderr, "%s: %s: fgets(): %s\n", progname,
3142 			        allowlist, strerror(errno));
3143 			free(key);
3144 			fclose(f);
3145 			return EX_UNAVAILABLE;
3146 		}
3147 
3148 		fclose(f);
3149 	}
3150 
3151 	/* load sign list */
3152 	if (ilist != NULL)
3153 	{
3154 		HOSTLIST new;
3155 		struct in_addr addr;
3156 		char *slash;
3157 		char buf[BUFRSZ + 1];
3158 
3159 		f = fopen(ilist, "r");
3160 		if (f == NULL)
3161 		{
3162 			fprintf(stderr, "%s: %s: fopen(): %s\n", progname,
3163 			        ilist, strerror(errno));
3164 			free(key);
3165 			return EX_UNAVAILABLE;
3166 		}
3167 
3168 		memset(buf, '\0', sizeof buf);
3169 		while (fgets(buf, sizeof buf - 1 ,f) != NULL)
3170 		{
3171 			for (p = buf; *p != '\0'; p++)
3172 			{
3173 				if (*p == '\n' || *p == '#')
3174 				{
3175 					*p = '\0';
3176 					break;
3177 				}
3178 			}
3179 
3180 			if (batv_isblank(buf))
3181 				continue;
3182 
3183 			new = (HOSTLIST) malloc(sizeof *new);
3184 			if (new == NULL)
3185 			{
3186 				fprintf(stderr, "%s: malloc(): %s\n",
3187 				        progname, strerror(errno));
3188 				fclose(f);
3189 				return EX_UNAVAILABLE;
3190 			}
3191 			memset(new, '\0', sizeof *new);
3192 
3193 			slash = strchr(buf, '/');
3194 			if (slash != NULL)
3195 				*slash = '\0';
3196 
3197 			addr.s_addr = inet_addr(buf);
3198 			if (addr.s_addr == INADDR_NONE)
3199 			{
3200 				new->host_name = strdup(buf);
3201 			}
3202 			else
3203 			{
3204 				if (slash == NULL)
3205 				{
3206 					new->host_addr.s_addr = addr.s_addr;
3207 					new->host_mask.s_addr = INADDR_BROADCAST;
3208 				}
3209 				else
3210 				{
3211 					int bits;
3212 					char *q;
3213 					struct in_addr mask;
3214 
3215 					mask.s_addr = 0;
3216 
3217 					bits = strtoul(slash + 1, &q, 10);
3218 					if (*q != '\0')
3219 					{
3220 						mask.s_addr = inet_addr(slash + 1);
3221 						if (mask.s_addr == INADDR_NONE)
3222 						{
3223 							fprintf(stderr,
3224 							        "%s: %s: invalid CIDR specification `%s/%s'\n",
3225 							        progname,
3226 							        ilist, buf,
3227 							        slash + 1);
3228 						}
3229 						fclose(f);
3230 						return EX_UNAVAILABLE;
3231 					}
3232 					else
3233 					{
3234 						int n;
3235 
3236 						for (n = 31;
3237 						     bits > 0;
3238 						     bits--, n--)
3239 							mask.s_addr |= htonl(1 << n);
3240 					}
3241 
3242 					new->host_addr.s_addr = addr.s_addr;
3243 					new->host_mask.s_addr = mask.s_addr;
3244 				}
3245 			}
3246 
3247 			new->host_next = signlist;
3248 			signlist = new;
3249 		}
3250 
3251 		if (ferror(f))
3252 		{
3253 			fprintf(stderr, "%s: %s: fgets(): %s\n", progname,
3254 			        ilist, strerror(errno));
3255 			free(key);
3256 			fclose(f);
3257 			return EX_UNAVAILABLE;
3258 		}
3259 
3260 		fclose(f);
3261 	}
3262 	else
3263 	{
3264 		HOSTLIST new;
3265 
3266 		new = (HOSTLIST) malloc(sizeof *new);
3267 		if (new == NULL)
3268 		{
3269 			fprintf(stderr, "%s: malloc(): %s\n",
3270 			        progname, strerror(errno));
3271 			fclose(f);
3272 			return EX_UNAVAILABLE;
3273 		}
3274 
3275 		new->host_name = strdup(LOCALHOST);
3276 		new->host_addr.s_addr = htonl(INADDR_LOOPBACK);
3277 		new->host_mask.s_addr = htonl(INADDR_BROADCAST);
3278 		new->host_next = NULL;
3279 
3280 		signlist = new;
3281 	}
3282 
3283 	/* store domain list */
3284 	if (domainlist != NULL)
3285 	{
3286 		DOMLIST new;
3287 
3288 		if (domainlist[0] == '/' && access(domainlist, F_OK) == 0)
3289 		{
3290 			char line[BUFRSZ + 1];
3291 
3292 			f = fopen(domainlist, "r");
3293 			if (f == NULL)
3294 			{
3295 				fprintf(stderr, "%s: %s: fopen(): %s\n",
3296 				        progname, domainlist, strerror(errno));
3297 				return EX_OSERR;
3298 			}
3299 
3300 			memset(line, '\0', sizeof line);
3301 			while (fgets(line, BUFRSZ, f) != NULL)
3302 			{
3303 				for (p = line; *p != '\0'; p++)
3304 				{
3305 					if (*p == '\n' || *p == '#')
3306 					{
3307 						*p = '\0';
3308 						break;
3309 					}
3310 				}
3311 
3312 				batvf_trimspaces(line);
3313 				if (strlen(line) == 0)
3314 					continue;
3315 
3316 				new = (DOMLIST) malloc(sizeof *new);
3317 				if (new == NULL)
3318 				{
3319 					fprintf(stderr,
3320 					        "%s: malloc(): %s\n",
3321 					        progname, strerror(errno));
3322 					fclose(f);
3323 					return EX_OSERR;
3324 				}
3325 
3326 				new->dom_name = strdup(line);
3327 				new->dom_next = domains;
3328 				domains = new;
3329 			}
3330 
3331 			fclose(f);
3332 		}
3333 		else
3334 		{
3335 			char *tmp;
3336 
3337 			tmp = strdup(domainlist);
3338 			if (tmp == NULL)
3339 			{
3340 				fprintf(stderr, "%s: strdup(): %s\n",
3341 				        progname, strerror(errno));
3342 				fclose(f);
3343 				return EX_OSERR;
3344 			}
3345 
3346 			for (p = strtok(tmp, ",");
3347 			     p != NULL;
3348 			     p = strtok(NULL, ","))
3349 			{
3350 				new = (DOMLIST) malloc(sizeof *new);
3351 				if (new == NULL)
3352 				{
3353 					fprintf(stderr,
3354 					        "%s: malloc(): %s\n",
3355 					        progname, strerror(errno));
3356 					fclose(f);
3357 					return EX_OSERR;
3358 				}
3359 
3360 				new->dom_name = strdup(p);
3361 				new->dom_next = domains;
3362 				domains = new;
3363 			}
3364 		}
3365 	}
3366 
3367 	/* store MTA name list */
3368 	if (mtanames != NULL)
3369 	{
3370 		int n = 1;
3371 		char *tmp;
3372 
3373 		tmp = strdup(mtanames);
3374 		if (tmp == NULL)
3375 		{
3376 			fprintf(stderr, "%s: strdup(): %s\n",
3377 			        progname, strerror(errno));
3378 			return EX_UNAVAILABLE;
3379 		}
3380 
3381 		for (p = tmp; *p != '\0'; p++)
3382 		{
3383 			if (*p == ',')
3384 				n++;
3385 		}
3386 
3387 		mtas = (char **) malloc((n + 1) * sizeof(char *));
3388 
3389 		if (mtas == NULL)
3390 		{
3391 			fprintf(stderr, "%s: malloc(): %s\n",
3392 			        progname, strerror(errno));
3393 			return EX_UNAVAILABLE;
3394 		}
3395 
3396 		n = 0;
3397 		for (p = strtok(tmp, ",");
3398 		     p != NULL;
3399 		     p = strtok(NULL, ","))
3400 		{
3401 			mtas[n] = p;
3402 			n++;
3403 		}
3404 
3405 		mtas[n] = NULL;
3406 	}
3407 
3408 	/* store macro list */
3409 	if (macrolist != NULL)
3410 	{
3411 		int n = 1;
3412 		char *tmp;
3413 
3414 		tmp = strdup(macrolist);
3415 		if (tmp == NULL)
3416 		{
3417 			fprintf(stderr, "%s: strdup(): %s\n",
3418 			        progname, strerror(errno));
3419 			return EX_UNAVAILABLE;
3420 		}
3421 
3422 		for (p = tmp; *p != '\0'; p++)
3423 		{
3424 			if (*p == ',')
3425 				n++;
3426 		}
3427 
3428 		macros = (char **) malloc((n + 1) * sizeof(char *));
3429 		values = (char **) malloc((n + 1) * sizeof(char *));
3430 
3431 		if (macros == NULL || values == NULL)
3432 		{
3433 			fprintf(stderr, "%s: malloc(): %s\n",
3434 			        progname, strerror(errno));
3435 			return EX_UNAVAILABLE;
3436 		}
3437 
3438 		n = 0;
3439 		for (p = strtok(tmp, ",");
3440 		     p != NULL;
3441 		     p = strtok(NULL, ","))
3442 		{
3443 			macros[n] = p;
3444 			values[n] = strchr(p, '=');
3445 			if (values[n] != NULL)
3446 			{
3447 				*(values[n]) = '\0';
3448 				values[n] += 1;
3449 			}
3450 			n++;
3451 		}
3452 		macros[n] = NULL;
3453 		values[n] = NULL;
3454 	}
3455 
3456 #ifdef DEBUG
3457 	return batv_debug();
3458 
3459 #else /* DEBUG */
3460 
3461 	/* Change user if appropriate */
3462 	if (user != NULL)
3463 	{
3464 		struct passwd* pw;
3465 
3466 		pw = getpwnam(user);
3467 		if (pw == NULL)
3468 		{
3469 			char *q;
3470 			uid_t uid;
3471 
3472 			uid = (uid_t) strtol(user, &q, 10);
3473 			if (*q == '\0')
3474 				pw = getpwuid(uid);
3475 
3476 			if (pw == NULL)
3477 			{
3478 				fprintf(stderr, "%s: no such user `%s'\n",
3479 					progname, user);
3480 				return EX_DATAERR;
3481 			}
3482 		}
3483 
3484 		(void) endpwent();
3485 
3486 		if (setuid(pw->pw_uid) != 0)
3487 		{
3488 			fprintf(stderr, "%s: setuid(): %s\n", progname,
3489 				strerror(errno));
3490 			return EX_NOPERM;
3491 		}
3492 	}
3493 
3494 	/* Activate logging */
3495 	if (dolog)
3496 		batv_init_syslog(facility);
3497 
3498 	if (autorestart)
3499 	{
3500 		bool quitloop = FALSE;
3501 		int restarts = 0;
3502 		pid_t pid;
3503 		pid_t wpid;
3504 		struct sigaction sa;
3505 
3506 		if (dofork)
3507 		{
3508 			pid = fork();
3509 			switch (pid)
3510 			{
3511 			  case -1:
3512 				if (dolog)
3513 				{
3514 					int saveerrno;
3515 
3516 					saveerrno = errno;
3517 
3518 					syslog(LOG_ERR, "fork(): %s",
3519 					       strerror(errno));
3520 
3521 					errno = saveerrno;
3522 				}
3523 
3524 				fprintf(stderr, "%s: fork(): %s\n",
3525 				        progname, strerror(errno));
3526 
3527 				return EX_OSERR;
3528 
3529 			  case 0:
3530 				batv_stdio();
3531 				break;
3532 
3533 			  default:
3534 				return EX_OK;
3535 			}
3536 		}
3537 
3538 		if (pidfile != NULL)
3539 		{
3540 			f = fopen(pidfile, "w");
3541 			if (f != NULL)
3542 			{
3543 				fprintf(f, "%ld\n", (long) getpid());
3544 				(void) fclose(f);
3545 			}
3546 			else
3547 			{
3548 				if (dolog)
3549 				{
3550 					syslog(LOG_ERR,
3551 					       "can't write pid to %s: %s",
3552 					       pidfile, strerror(errno));
3553 				}
3554 			}
3555 		}
3556 
3557 		sa.sa_handler = batv_sighandler;
3558 		sigemptyset(&sa.sa_mask);
3559 		sigaddset(&sa.sa_mask, SIGHUP);
3560 		sigaddset(&sa.sa_mask, SIGINT);
3561 		sigaddset(&sa.sa_mask, SIGTERM);
3562 		sa.sa_flags = 0;
3563 
3564 		if (sigaction(SIGHUP, &sa, NULL) != 0 ||
3565 		    sigaction(SIGINT, &sa, NULL) != 0 ||
3566 		    sigaction(SIGTERM, &sa, NULL) != 0)
3567 		{
3568 			if (dolog)
3569 			{
3570 				syslog(LOG_ERR, "[parent] sigaction(): %s",
3571 				       strerror(errno));
3572 			}
3573 		}
3574 
3575 		while (!quitloop)
3576 		{
3577 			status = batvf_socket_cleanup(conn);
3578 			if (status != 0)
3579 			{
3580 				if (dolog)
3581 				{
3582 					syslog(LOG_ERR,
3583 					       "[parent] socket cleanup failed: %s",
3584 					       strerror(status));
3585 				}
3586 				return EX_UNAVAILABLE;
3587 			}
3588 
3589 			pid = fork();
3590 			switch (pid)
3591 			{
3592 			  case -1:
3593 				if (dolog)
3594 				{
3595 					syslog(LOG_ERR, "fork(): %s",
3596 					       strerror(errno));
3597 				}
3598 
3599 				return EX_OSERR;
3600 
3601 			  case 0:
3602 				sa.sa_handler = SIG_DFL;
3603 
3604 				if (sigaction(SIGHUP, &sa, NULL) != 0 ||
3605 				    sigaction(SIGINT, &sa, NULL) != 0 ||
3606 				    sigaction(SIGTERM, &sa, NULL) != 0)
3607 				{
3608 					if (dolog)
3609 					{
3610 						syslog(LOG_ERR,
3611 						       "[child] sigaction(): %s",
3612 						       strerror(errno));
3613 					}
3614 				}
3615 
3616 				quitloop = TRUE;
3617 				break;
3618 
3619 			  default:
3620 				for (;;)
3621 				{
3622 					wpid = wait(&status);
3623 
3624 					if (wpid == -1 && errno == EINTR)
3625 					{
3626 						if (die)
3627 						{
3628 							batv_killchild(pid,
3629 							               diesig);
3630 
3631 							while (wpid != pid)
3632 								wpid = wait(&status);
3633 
3634 							if (pidfile != NULL)
3635 								(void) unlink(pidfile);
3636 
3637 							exit(EX_OK);
3638 						}
3639 					}
3640 
3641 					if (pid != wpid)
3642 						continue;
3643 
3644 					if (wpid != -1 && dolog)
3645 					{
3646 						if (WIFSIGNALED(status))
3647 						{
3648 							syslog(LOG_NOTICE,
3649 							       "terminated with signal %d, restarting",
3650 							       WTERMSIG(status));
3651 						}
3652 						else if (WIFEXITED(status))
3653 						{
3654 							syslog(LOG_NOTICE,
3655 							       "exited with status %d, restarting",
3656 							       WEXITSTATUS(status));
3657 						}
3658 					}
3659 
3660 					break;
3661 				}
3662 				break;
3663 			}
3664 
3665 			if (maxrestarts > 0 && restarts >= maxrestarts)
3666 			{
3667 				if (dolog)
3668 				{
3669 					syslog(LOG_ERR,
3670 					       "maximum restart count exceeded");
3671 				}
3672 
3673 				return EX_UNAVAILABLE;
3674 			}
3675 
3676 			if (maxrestartrate_n > 0 &&
3677 			    maxrestartrate_t > 0 &&
3678 			    !batvf_restart_check(0, maxrestartrate_t))
3679 			{
3680 				if (dolog)
3681 				{
3682 					syslog(LOG_ERR,
3683 					       "maximum restart rate exceeded");
3684 				}
3685 
3686 				return EX_UNAVAILABLE;
3687 			}
3688 
3689 			restarts++;
3690 		}
3691 	}
3692 
3693 	/* register with the milter interface */
3694 	if (smfi_register(smfilter) == MI_FAILURE)
3695 	{
3696 		if (dolog)
3697 			syslog(LOG_ERR, "smfi_register() failed");
3698 
3699 		fprintf(stderr, "%s: smfi_register() failed\n", progname);
3700 
3701 		return EX_UNAVAILABLE;
3702 	}
3703 
3704 	/* try to establish the socket */
3705 	(void) smfi_setconn(conn);
3706 	if (smfi_opensocket(TRUE) == MI_FAILURE)
3707 	{
3708 		if (dolog)
3709 			syslog(LOG_ERR, "smfi_opensocket() failed");
3710 
3711 		fprintf(stderr, "%s: smfi_opensocket() failed\n", progname);
3712 
3713 		return EX_UNAVAILABLE;
3714 	}
3715 
3716 	if (!autorestart && dofork)
3717 	{
3718 		int save_errno;
3719 		pid_t pid;
3720 
3721 		pid = fork();
3722 		switch (pid)
3723 		{
3724 		  case -1:
3725 			save_errno = errno;
3726 			(void) fprintf(stderr, "%s: fork(): %s\n",
3727 				       progname, strerror(save_errno));
3728 			if (dolog)
3729 			{
3730 				errno = save_errno;
3731 				syslog(LOG_ERR, "fork(): %s", strerror(errno));
3732 			}
3733 			return EX_OSERR;
3734 
3735 		  case 0:
3736 			break;
3737 
3738 		  default:
3739 			return EX_OK;
3740 		}
3741 	}
3742 
3743 	/*
3744 	**  setsid() works on POSIX only.  Fortunately, that's
3745 	**  all we need for now.  Check sendmail/conf.c for a
3746 	**  portable version if needed.
3747 	*/
3748 
3749 	(void) setsid();
3750 
3751 	if (!autorestart && !dofork)
3752 	{
3753 		/* redirect stdin, stdout, stderr to /dev/null */
3754 		devnull = open(_PATH_DEVNULL, O_RDWR, 0);
3755 		if (devnull < 0)
3756 		{
3757 			fprintf(stderr, "%s: %s: open(): %s\n", progname,
3758 			        _PATH_DEVNULL, strerror(errno));
3759 		}
3760 
3761 		status = dup2(devnull, 0);
3762 		if (status != -1)
3763 			status = dup2(devnull, 1);
3764 		if (status != -1)
3765 			status = dup2(devnull, 2);
3766 		if (status == -1)
3767 		{
3768 			fprintf(stderr, "%s: %s: dup2(): %s\n",
3769 			        progname, _PATH_DEVNULL,
3770 			        strerror(errno));
3771 		}
3772 
3773 		close(devnull);
3774 	}
3775 
3776 	/* write out the pid */
3777 	if (!autorestart && pidfile != NULL)
3778 	{
3779 		f = fopen(pidfile, "w");
3780 		if (f != NULL)
3781 		{
3782 			fprintf(f, "%u\n", getpid());
3783 			(void) fclose(f);
3784 		}
3785 		else if (dolog)
3786 		{
3787 			syslog(LOG_ERR, "can't write pid to %s: %s",
3788 			       pidfile, strerror(errno));
3789 		}
3790 	}
3791 
3792 	/* call the milter mainline */
3793 	if (dolog)
3794 	{
3795 		size_t n;
3796 		char argstr[MAXARGV];
3797 		char *end;
3798 
3799 		memset(argstr, '\0', sizeof argstr);
3800 		end = &argstr[sizeof argstr - 1];
3801 		n = sizeof argstr;
3802 		for (c = 1, p = argstr; c < argc && p < end; c++)
3803 		{
3804 			if (strchr(argv[c], ' ') != NULL)
3805 			{
3806 				status = snprintf(p, n, "%s \"%s\"",
3807 				                  c == 1 ? "args:" : "",
3808 				                  argv[c]);
3809 			}
3810 			else
3811 			{
3812 				status = snprintf(p, n, "%s %s",
3813 				                  c == 1 ? "args:" : "",
3814 				                  argv[c]);
3815 			}
3816 
3817 			p += status;
3818 			n -= status;
3819 		}
3820 
3821 		syslog(LOG_INFO, "%s v%s starting (%s)", BATV_PRODUCT,
3822 		       BATV_VERSION, argstr);
3823 	}
3824 
3825 	status = smfi_main();
3826 
3827 	if (!autorestart && pidfile != NULL)
3828 		(void) unlink(pidfile);
3829 
3830 	return status;
3831 
3832 #endif /* DEBUG */
3833 }
3834