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 ¯olist, 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 ¬really, 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