bmf-milter

bmf filter milter daemon
git clone git://r-36.net/bmf-milter
Log | Files | Refs | LICENSE

bmf-milter.c (10322B)


      1 /*
      2  * Copy me if you can.
      3  * by 20h
      4  */
      5 
      6 #include <sys/types.h>
      7 #include <sys/stat.h>
      8 #include <sys/wait.h>
      9 #include <fcntl.h>
     10 #include <errno.h>
     11 #include <stdio.h>
     12 #include <grp.h>
     13 #include <pwd.h>
     14 #include <stdlib.h>
     15 #include <string.h>
     16 #include <stdarg.h>
     17 #include <sysexits.h>
     18 #include <unistd.h>
     19 #include <signal.h>
     20 
     21 #include "libmilter/mfapi.h"
     22 #include "libmilter/mfdef.h"
     23 
     24 #include "arg.h"
     25 
     26 /*
     27  * In case any requires feature is not negotiable, simply do nothing,
     28  * thus always return SMFI_CONTINUE. If we would return any FAIL, the
     29  * message would be rejected.
     30  */
     31 int donothing = 0, dodebug = 0;
     32 char *bmfdb = NULL, *bmfpath = "/usr/bin/bmf";
     33 
     34 struct Priv {
     35 	/* read from execpipe[0], write to execpipe[1] */
     36 	int	execpipe[2];
     37 	int	execpid;
     38 };
     39 
     40 #define MLFIPRIV ((struct Priv *) smfi_getpriv(ctx))
     41 
     42 int
     43 start_bmf(SMFICTX *ctx)
     44 {
     45 	struct Priv *priv;
     46 	char *ident, *bmfargs[6];
     47 	int pid, nullfd, argp;
     48 
     49 	if (dodebug)
     50 		fprintf(stderr, "start_bmf()\n");
     51 
     52 	if (donothing) {
     53 		smfi_setpriv(ctx, NULL);
     54 		return 1;
     55 	}
     56 
     57 	priv = malloc(sizeof(*priv));
     58 	if (priv == NULL)
     59 		return 1;
     60 	memset(priv, '\0', sizeof(*priv));
     61 
     62 	smfi_setpriv(ctx, priv);
     63 
     64 	if (pipe(priv->execpipe) < 0) {
     65 		free(priv);
     66 		smfi_setpriv(ctx, NULL);
     67 		return 1;
     68 	}
     69 
     70 	switch ((pid = fork())) {
     71 	case 0:
     72 		while(dup2(priv->execpipe[0], 0) < 0 && errno == EINTR);
     73 		close(priv->execpipe[1]);
     74 
     75 		argp = 0;
     76 		bmfargs[argp++] = "bmf";
     77 		/* Test for spam mode. */
     78 		bmfargs[argp++] = "-t";
     79 
     80 		if (!dodebug) {
     81 			nullfd = open("/dev/null", O_WRONLY);
     82 			if (nullfd < 0) {
     83 				perror("open");
     84 				_exit(1);
     85 			}
     86 			while(dup2(priv->execpipe[0], 1) < 0
     87 					&& errno == EINTR);
     88 			while(dup2(priv->execpipe[0], 2) < 0
     89 					&& errno == EINTR);
     90 
     91 			/* Verbose for bmf. */
     92 			bmfargs[argp++] = "-v";
     93 		}
     94 
     95 		if (bmfdb != NULL) {
     96 			/* Set database directory, if set. */
     97 			bmfargs[argp++] = "-d";
     98 			bmfargs[argp++] = bmfdb;
     99 		}
    100 		bmfargs[argp++] = NULL;
    101 
    102 		if (execv(bmfpath, bmfargs) < 0) {
    103 			perror("execv");
    104 			_exit(1);
    105 		}
    106 		break;
    107 	case -1:
    108 		free(priv);
    109 		smfi_setpriv(ctx, NULL);
    110 		break;
    111 	default:
    112 		if (dodebug)
    113 			fprintf(stderr, "start_bmf(pid = %d)\n", pid);
    114 		priv->execpid = pid;
    115 		close(priv->execpipe[0]);
    116 		break;
    117 	}
    118 
    119 	return 0;
    120 }
    121 
    122 sfsistat
    123 mlfi_cleanup(SMFICTX *ctx, int iseom)
    124 {
    125 	struct Priv *priv = MLFIPRIV;
    126 	int retcode = -1;
    127 
    128 	if (dodebug)
    129 		fprintf(stderr, "mlfi_cleanup(iseom = %d)\n", iseom);
    130 
    131 	if (priv == NULL)
    132 		return SMFIS_CONTINUE;
    133 	if (dodebug)
    134 		fprintf(stderr, "mlfi_cleanup(closing execpipe[1])\n");
    135 	close(priv->execpipe[1]);
    136 	priv->execpipe[1] = -1;
    137 	if (dodebug)
    138 		fprintf(stderr, "mlfi_cleanup(waitpid)\n");
    139 	waitpid(priv->execpid, &retcode, 0);
    140 	if (dodebug)
    141 		fprintf(stderr, "mlfi_cleanup(retcode = %d)\n", retcode);
    142 
    143 	/*
    144 	 * smfi_addheader is only allowed in eom.
    145 	 */
    146 	if (iseom) {
    147 		if (retcode == 0) {
    148 			if (smfi_addheader(ctx, "X-Spam-Flag",
    149 						" YES") == MI_FAILURE) {
    150 				if (dodebug) {
    151 					fprintf(stderr,
    152 					"mlfi_cleanup(x-spam-flag failed)\n");
    153 				}
    154 			} else {
    155 				if (dodebug) {
    156 					fprintf(stderr,
    157 					"mlfi_cleanup(x-spam-flag = yes added)\n");
    158 				}
    159 			}
    160 		}
    161 		if (smfi_addheader(ctx, "X-BMF-Processed",
    162 					" YES") == MI_FAILURE) {
    163 			if (dodebug) {
    164 				fprintf(stderr,
    165 				"mlfi_cleanup(x-bmf-processed failed)\n");
    166 			}
    167 		} else {
    168 			if (dodebug) {
    169 				fprintf(stderr,
    170 				"mlfi_cleanup(x-bmf-processed = yes added)\n");
    171 			}
    172 		}
    173 	}
    174 
    175 	return SMFIS_CONTINUE;
    176 }
    177 
    178 sfsistat
    179 mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr)
    180 {
    181 	if (dodebug)
    182 		fprintf(stderr, "mlfi_connect(%s)\n", hostname);
    183 
    184 	return SMFIS_CONTINUE;
    185 }
    186 
    187 sfsistat
    188 mlfi_helo(SMFICTX *ctx, char *helohost)
    189 {
    190 	if (dodebug)
    191 		fprintf(stderr, "mlfi_helo(%s)\n", helohost);
    192 
    193 	smfi_setpriv(ctx, NULL);
    194 
    195 	return SMFIS_CONTINUE;
    196 }
    197 
    198 sfsistat
    199 mlfi_envfrom(SMFICTX *ctx, char *argv[])
    200 {
    201 	struct Priv *priv = MLFIPRIV;
    202 
    203 	if (dodebug)
    204 		fprintf(stderr, "mlfi_envfrom(%s)\n", argv[0]);
    205 
    206 	if (priv == NULL) {
    207 		if (start_bmf(ctx))
    208 			return SMFIS_CONTINUE;
    209 		priv = MLFIPRIV;
    210 	}
    211 	dprintf(priv->execpipe[1], "From: %s\n", argv[0]);
    212 
    213 	return SMFIS_CONTINUE;
    214 }
    215 
    216 sfsistat
    217 mlfi_envrcpt(SMFICTX *ctx, char *argv[])
    218 {
    219 	struct Priv *priv = MLFIPRIV;
    220 
    221 	if (dodebug)
    222 		fprintf(stderr, "mlfi_envrcpt(%s)\n", argv[0]);
    223 
    224 	if (priv == NULL) {
    225 		if (start_bmf(ctx))
    226 			return SMFIS_CONTINUE;
    227 		priv = MLFIPRIV;
    228 	}
    229 	dprintf(priv->execpipe[1], "To: %s\n", argv[0]);
    230 
    231 	return SMFIS_CONTINUE;
    232 }
    233 
    234 sfsistat
    235 mlfi_header(SMFICTX *ctx, char *headerf, char *headerv)
    236 {
    237 	struct Priv *priv = MLFIPRIV;
    238 
    239 	if (dodebug)
    240 		fprintf(stderr, "mlfi_header(%s = '%s')\n", headerf, headerv);
    241 
    242 	if (priv == NULL) {
    243 		if (start_bmf(ctx))
    244 			return SMFIS_CONTINUE;
    245 		priv = MLFIPRIV;
    246 	}
    247 	dprintf(priv->execpipe[1], "%s:%s\n", headerf, headerv);
    248 
    249 	return SMFIS_CONTINUE;
    250 }
    251 
    252 sfsistat
    253 mlfi_eoh(SMFICTX *ctx)
    254 {
    255 	struct Priv *priv = MLFIPRIV;
    256 
    257 	if (dodebug)
    258 		fprintf(stderr, "mlfi_eoh()\n");
    259 
    260 	if (priv == NULL) {
    261 		if (start_bmf(ctx))
    262 			return SMFIS_CONTINUE;
    263 		priv = MLFIPRIV;
    264 	}
    265 	dprintf(priv->execpipe[1], "\r\n");
    266 
    267 	return SMFIS_CONTINUE;
    268 }
    269 
    270 sfsistat
    271 mlfi_body(SMFICTX *ctx, unsigned char *bodyp, size_t bodylen)
    272 {
    273         struct Priv *priv = MLFIPRIV;
    274 	int written;
    275 
    276 	if (dodebug)
    277 		fprintf(stderr, "mlfi_body(%ld bytes)\n", bodylen);
    278 
    279 	if (priv == NULL) {
    280 		if (start_bmf(ctx))
    281 			return SMFIS_CONTINUE;
    282 		priv = MLFIPRIV;
    283 	}
    284 	for (int written = 0, rw = 0; written < bodylen; written += rw)
    285 		rw = write(priv->execpipe[1], bodyp+written, bodylen-written);
    286 
    287 	return SMFIS_CONTINUE;
    288 }
    289 
    290 sfsistat
    291 mlfi_eom(SMFICTX *ctx)
    292 {
    293 	if (dodebug)
    294 		fprintf(stderr, "mlfi_eom()\n");
    295 
    296 	return mlfi_cleanup(ctx, 1);
    297 }
    298 
    299 sfsistat
    300 mlfi_abort(SMFICTX *ctx)
    301 {
    302 	if (dodebug)
    303 		fprintf(stderr, "mlfi_abort()\n");
    304 
    305 	return mlfi_cleanup(ctx, 0);
    306 }
    307 
    308 sfsistat
    309 mlfi_close(SMFICTX *ctx)
    310 {
    311 	struct Priv *priv = MLFIPRIV;
    312 
    313 	if (dodebug)
    314 		fprintf(stderr, "mlfi_close()\n");
    315 
    316 	if (priv != NULL) {
    317 		if (priv->execpipe[1] > 0) {
    318 			if (dodebug) {
    319 				fprintf(stderr,
    320 				"mlfi_close(close execpipe[1])\n");
    321 			}
    322 			close(priv->execpipe[1]);
    323 		}
    324 		if (priv->execpid > 0) {
    325 			if (dodebug) {
    326 				fprintf(stderr,
    327 				"mlfi_close(kill pid %d)\n", priv->execpid);
    328 			}
    329 			kill(priv->execpid, SIGKILL);
    330 			waitpid(priv->execpid, NULL, 0);
    331 		}
    332 		if (dodebug)
    333 			fprintf(stderr, "mlfi_close(free priv)\n");
    334 		free(priv);
    335 		smfi_setpriv(ctx, NULL);
    336 	}
    337 
    338 	return SMFIS_CONTINUE;
    339 }
    340 
    341 sfsistat
    342 mlfi_negotiate(SMFICTX *ctx,
    343 		unsigned long f0,
    344 		unsigned long f1,
    345 		unsigned long f2,
    346 		unsigned long f3,
    347 		unsigned long *pf0,
    348 		unsigned long *pf1,
    349 		unsigned long *pf2,
    350 		unsigned long *pf3)
    351 {
    352 	if (dodebug)
    353 		fprintf(stderr, "mlfi_negotiate()\n");
    354 
    355 	/* milter actions */
    356 	*pf0 = 0;
    357 	if (f0 & SMFIF_ADDHDRS) {
    358 		*pf0 |= SMFIF_ADDHDRS;
    359 	} else {
    360 		donothing = 1;
    361 	}
    362 
    363 	/* milter protocol steps */
    364 	*pf1 = f1 & (SMFIP_NOUNKNOWN|SMFIP_NODATA);
    365 	if (f1 & SMFIP_HDR_LEADSPC) {
    366 		*pf1 |= SMFIP_HDR_LEADSPC;
    367 	} else {
    368 		donothing = 1;
    369 	}
    370 
    371 	/* future */
    372 	*pf2 = 0;
    373 	*pf3 = 0;
    374 
    375 	return SMFIS_CONTINUE;
    376 }
    377 
    378 struct smfiDesc smfilter =
    379 {
    380 	"BMF",		/* filter name */
    381 	SMFI_VERSION,	/* version code -- do not change */
    382 	SMFIF_ADDHDRS,  /* flags */
    383 	mlfi_connect,	/* connection info filter */
    384 	mlfi_helo,	/* SMTP HELO command filter */
    385 	mlfi_envfrom,	/* envelope sender filter */
    386 	mlfi_envrcpt,	/* envelope recipient filter */
    387 	mlfi_header,	/* header filter */
    388 	mlfi_eoh,	/* end of header */
    389 	mlfi_body,	/* body block filter */
    390 	mlfi_eom,	/* end of message */
    391 	mlfi_abort,	/* message aborted */
    392 	mlfi_close,	/* connection cleanup */
    393 	NULL,		/* unknown SMTP commands */
    394 	NULL,		/* DATA command */
    395 	mlfi_negotiate	/* Once, at the start of each SMTP connection */
    396 };
    397 
    398 void
    399 sighandler(int sig)
    400 {
    401 	int i;
    402 
    403 	switch (sig) {
    404 	case SIGCHLD:
    405 		while (waitpid(-1, NULL, WNOHANG) > 0);
    406 		break;
    407 	case SIGINT:
    408 	case SIGQUIT:
    409 	case SIGABRT:
    410 	case SIGTERM:
    411 	case SIGKILL:
    412 		smfi_stop();
    413 		break;
    414 	default:
    415 		break;
    416 	}
    417 }
    418 
    419 void
    420 initsignals(void)
    421 {
    422 	signal(SIGCHLD, sighandler);
    423 	signal(SIGHUP, sighandler);
    424 	signal(SIGINT, sighandler);
    425 	signal(SIGQUIT, sighandler);
    426 	signal(SIGABRT, sighandler);
    427 	signal(SIGTERM, sighandler);
    428 	signal(SIGKILL, sighandler);
    429 
    430 	/*
    431 	 * done by smfi_main():
    432 	 * signal(SIGPIPE, SIG_IGN);
    433 	 */
    434 }
    435 
    436 void
    437 usage(char *argv0)
    438 {
    439 	fprintf(stderr,
    440 		"Usage: %s [-hd] [-b bmfdb] [-c conndef] [-f bmfpath] "
    441 		"[-g group] [-t timeout] [-u user] [-v dbglevel]\n",
    442 		argv0);
    443 }
    444 
    445 int
    446 main(int argc, char *argv[])
    447 {
    448 	char *argv0, *user = NULL, *group = NULL, *conndef = "inet:9957";
    449 	int timeout = -1, dofork = 1;
    450 	struct passwd *us = NULL;
    451 	struct group *gr = NULL;
    452 
    453 	ARGBEGIN(argv0) {
    454 	case 'b':
    455 		bmfdb = EARGF(usage(argv0));
    456 		break;
    457 	case 'c':
    458 		conndef = EARGF(usage(argv0));
    459 		break;
    460 	case 'd':
    461 		dofork = 0;
    462 		break;
    463 	case 'f':
    464 		bmfpath = EARGF(usage(argv0));
    465 		break;
    466 	case 'g':
    467 		group = EARGF(usage(argv0));
    468 		break;
    469 	case 't':
    470 		timeout = atoi(EARGF(usage(argv0)));
    471 		break;
    472 	case 'u':
    473 		user = EARGF(usage(argv0));
    474 		break;
    475 	case 'v':
    476 		smfi_setdbg(atoi(EARGF(argv0)));
    477 		dodebug = 1;
    478 		break;
    479 	default:
    480 		usage(argv0);
    481 		return 1;
    482 	} ARGEND;
    483 
    484 	if (group != NULL) {
    485 		errno = 0;
    486 		if ((gr = getgrnam(group)) == NULL) {
    487 			if (errno == 0) {
    488 				fprintf(stderr, "no such group '%s'\n",
    489 						group);
    490 			} else {
    491 				perror("getgrnam");
    492 			}
    493 			return 1;
    494 		}
    495 	}
    496 
    497 	if (user != NULL) {
    498 		errno = 0;
    499 		if ((us = getpwnam(user)) == NULL) {
    500 			if (errno == 0) {
    501 				fprintf(stderr, "no such user '%s'\n",
    502 						user);
    503 			} else {
    504 				perror("getpwnam");
    505 			}
    506 			return 1;
    507 		}
    508 	}
    509 
    510 	if (dofork) {
    511 		switch (fork()) {
    512 		case -1:
    513 			perror("fork");
    514 			return 1;
    515 		case 0:
    516 			break;
    517 		default:
    518 			return 0;
    519 		}
    520 	}
    521 
    522 	if (gr != NULL) {
    523 		if (setgroups(1, &gr->gr_gid) != 0 || setgid(gr->gr_gid) != 0) {
    524 			perror("setgroups");
    525 			return -1;
    526 		}
    527 	}
    528 	if (us != NULL) {
    529 		if (gr == NULL) {
    530 			if (setgroups(1, &us->pw_gid) != 0 ||
    531 			    setgid(us->pw_gid) != 0) {
    532 				perror("setgroups");
    533 				return -1;
    534 			}
    535 		}
    536 		if (setuid(us->pw_uid) != 0) {
    537 			perror("setuid");
    538 			return -1;
    539 		}
    540 	}
    541 
    542 	if (smfi_setconn(conndef) == MI_FAILURE) {
    543 		perror("smfi_setconn");
    544 		return 1;
    545 	}
    546 	if (dodebug)
    547 		fprintf(stderr, "conn set to '%s'\n", conndef);
    548 
    549 	if (timeout > 0) {
    550 		if (smfi_settimeout(timeout) == MI_FAILURE) {
    551 			perror("smfi_settimout");
    552 			return 1;
    553 		}
    554 		if (dodebug)
    555 			fprintf(stderr, "timeout set to %d\n", timeout);
    556 	}
    557 
    558 	if (smfi_register(smfilter) == MI_FAILURE) {
    559 		perror("smfi_register");
    560 		return 1;
    561 	}
    562 
    563 	return smfi_main();
    564 }
    565