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