bmf-milter

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

bmf-milter.c (9263B)


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