2
* Based(V2.4) on amavis-milter.c,v 1.1.2.3.2.40 2003/06/06 12:34:58 lhecking Exp
6
* sendmail/milter client for amavis
9
* Author: Geoff Winkless <gwinkless@users.sourceforge.net>
10
* Additional work and patches by:
14
* Pierre-Yves Bonnetain
24
* Add some copyright notice here ...
31
#include <sys/types.h>
34
#include <sys/utsname.h>
44
#include <netinet/in.h>
45
#include <arpa/inet.h>
46
#include <sys/socket.h>
56
#include "libmilter/mfapi.h"
58
#ifndef HAVE_SM_GEN_BOOL_TYPE
63
/* Must be the same as the buffer length for recv() in amavisd */
64
#define SOCKBUFLEN 8192
67
# define RUNTIME_DIR "/var/amavis"
70
#ifndef AMAVISD_SOCKET
71
# define AMAVISD_SOCKET RUNTIME_DIR ## "/amavisd.sock"
74
/* Activate the sendmail add-on features */
75
#define WITH_SENDMAIL_QUEUEID_TEMP_DNAME 1
76
#define WITH_SYNTHESIZED_RECEIVED_HEADER 1
78
#define D_TEMPPREFIX "/amavis-milter-"
79
#define D_TEMPLATE "XXXXXXXX"
80
#define F_TEMPLATE "/email.txt"
82
#define DEVNULL "/dev/null"
84
/* #ifndef AMAVIS_USER
85
* # define AMAVIS_USER "amavis"
87
* #ifndef MILTER_SOCKET_GROUP
88
* # define MILTER_SOCKET_GROUP "amavis"
92
/* Extracted from the code for better configurability
93
* These will be set by configure/make eventually */
95
# define X_HEADER_TAG "X-Virus-Scanned"
98
# define X_HEADER_LINE "by amavisd-milter (http://www.amavis.org/)"
107
typedef struct llstrct {
109
struct llstrct *next;
113
char *mlfi_fname; /* temporary file name */
114
FILE *mlfi_fp; /* file descriptor of the temporary file */
116
char *mlfi_client_addr;
117
char *mlfi_client_name;
125
static int verbosity = DBG_WARN;
126
static int AM_DAEMON = 1;
128
static struct group *miltergroup;
129
static gid_t amavis_gid;
130
static struct utsname amavis_uts;
131
static int enable_x_header = 1; /* enabled by default */
134
static char pidfile[] = "/var/run/amavis/amavisd-new-milter.pid";
136
static void amavis_syslog(const int, const char *, ...);
137
static char *amavis_mkdtemp(char *, int);
138
static int group_member(const char *);
139
static void freeenvto(ll *);
140
static sfsistat clearpriv(SMFICTX *, sfsistat, int);
141
static int allocmem(SMFICTX *);
142
static sfsistat mlfi_connect(SMFICTX *, char *, _SOCK_ADDR *);
143
static sfsistat mlfi_helo(SMFICTX *, char *);
144
static sfsistat mlfi_envfrom(SMFICTX *, char **);
145
static sfsistat mlfi_envto(SMFICTX *, char **);
146
static sfsistat mlfi_header(SMFICTX *, char *, char *);
147
static sfsistat mlfi_eoh(SMFICTX *);
148
static sfsistat mlfi_body(SMFICTX *, u_char *, size_t);
149
static sfsistat mlfi_eom(SMFICTX *);
150
static sfsistat mlfi_abort(SMFICTX *);
151
static sfsistat mlfi_close(SMFICTX *);
152
static sfsistat mlfi_cleanup(SMFICTX *, sfsistat, bool);
157
/* check if another daemon is running */
160
FILE *daemon_lockfp = NULL;
163
if (((fd = open(pidfile, O_RDWR|O_CREAT, 0644)) == -1 )
164
|| ((daemon_lockfp = fdopen(fd, "r+"))) == NULL)
166
amavis_syslog(DBG_FATAL, "can't open or create %s", pidfile);
167
exit(EX_UNAVAILABLE);
169
if ( flock(fd, LOCK_EX|LOCK_NB) != 0 )
171
fscanf(daemon_lockfp, "%d", &otherpid);
172
amavis_syslog(DBG_INFO, "can't lock %s, running daemon's pid may be %d", pidfile, otherpid);
173
exit(EX_UNAVAILABLE);
176
fcntl(fd, F_SETFD, 1);
178
rewind(daemon_lockfp);
179
fprintf(daemon_lockfp, "%d\n", (int) daemon_pid);
180
fflush(daemon_lockfp);
181
ftruncate(fileno(daemon_lockfp), ftell(daemon_lockfp));
182
/* abandon fd and daemon_lockfp even though the file is open. we need to-
183
* keep it open and locked, but we don't need the handles elsewhere.
188
amavis_syslog(const int level, const char *fmt, ...)
196
if (level > verbosity) return;
197
switch (level) { /* map internal log level to syslog priority */
198
case DBG_FATAL: loglevel = LOG_ERR; break;
199
case DBG_WARN: loglevel = LOG_WARNING; break;
200
case DBG_INFO: loglevel = LOG_INFO; break;
201
case DBG_DEBUG: loglevel = LOG_DEBUG; break;
202
default: loglevel = LOG_INFO;
204
if (verbosity > 1 && loglevel == LOG_DEBUG) loglevel = LOG_INFO;
208
if (AM_DAEMON == 0) {
210
timestamp = ctime(&tmpt);
211
/* A 26 character string according ctime(3c)
212
* we cut off the trailing \n\0 */
215
snprintf(buf,sizeof(buf),"%s %s amavis-milter[%ld]: ",
217
(amavis_uts.nodename ? amavis_uts.nodename : "localhost"),
221
vsnprintf(buf+strlen(buf),sizeof(buf)-strlen(buf),fmt,ap);
224
if (AM_DAEMON == 0) {
225
fprintf(stderr,"%s\n",buf);
228
/* HG: does it make sense to open and close the log each time? */
229
openlog("amavis-milter", LOG_PID|LOG_CONS, LOG_MAIL);
231
syslog(loglevel,"%s\n",buf);
236
amavis_mkdtemp(char *s, int use_fixed_name)
241
if (use_fixed_name) {
242
if (!mkdir(s, S_IRWXU|S_IRGRP|S_IXGRP)) return s; /*succeeded */
243
amavis_syslog(DBG_FATAL, "(amavis_mkdtemp) creating directory %s failed: %s",
246
/* fall back to inventing temporary directory names */
247
strcat(s, D_TEMPLATE); /* storage has been preallocated */
252
amavis_syslog(DBG_FATAL, "(amavis_mkdtemp) mkdtemp %s failed: %s",
256
/* magic number alert */
257
while (count++ < 20) {
261
/* relies on template format */
262
stt = strrchr(s, '-') + 1;
264
/* more magic number alert */
265
snprintf(stt, strlen(s) - 1 - (stt - s), "%08d", lrand48() / 215);
268
/* invalid template */
269
amavis_syslog(DBG_FATAL, "(amavis_mkdtemp) mktemp failed %s",s);
274
if (!mkdir(s, S_IRWXU|S_IRGRP|S_IXGRP)) {
281
amavis_syslog(DBG_FATAL, "(amavis_mkdtemp) creating (3) directory %s failed: %s",
284
#endif /* HAVE_MKDTEMP */
288
int group_member(const char *group)
291
gid_t *grouplist = 0;
293
if (!(miltergroup = getgrnam(group))) {
298
if ((r = getgroups(0, grouplist)) < 0) {
300
} else if ((grouplist = malloc (r*sizeof(gid_t))) == NULL) {
303
} else if ((r = getgroups(r, grouplist)) < 0) {
309
if (miltergroup->gr_gid == grouplist[i]) {
321
#define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx))
324
freeenvto(ll * envto)
327
ll *new = envto->next;
338
clearpriv(SMFICTX *ctx, sfsistat retme, int clearall)
340
/* clear or release private memory and return retme */
341
struct mlfiPriv *priv = MLFIPRIV;
345
if (fclose(priv->mlfi_fp) != 0)
346
amavis_syslog(DBG_FATAL, "(clearpriv) close failed: %s",
348
priv->mlfi_fp = NULL;
350
if (priv->mlfi_fname)
351
{ free(priv->mlfi_fname); priv->mlfi_fname = NULL; }
352
if (priv->mlfi_queueid)
353
{ free(priv->mlfi_queueid); priv->mlfi_queueid = NULL; }
354
if (priv->mlfi_envfrom)
355
{ free(priv->mlfi_envfrom); priv->mlfi_envfrom = NULL; }
356
if (priv->mlfi_envto.next)
357
{ freeenvto(priv->mlfi_envto.next); priv->mlfi_envto.next = NULL; }
358
if (priv->mlfi_envto.str)
359
{ free(priv->mlfi_envto.str); priv->mlfi_envto.str = NULL; }
360
priv->mlfi_thisenvto = NULL;
361
priv->mlfi_numto = 0;
363
if (priv->mlfi_client_addr)
364
{ free(priv->mlfi_client_addr); priv->mlfi_client_addr = NULL; }
365
if (priv->mlfi_client_name)
366
{ free(priv->mlfi_client_name); priv->mlfi_client_name = NULL; }
368
{ free(priv->mlfi_helo); priv->mlfi_helo = NULL; }
369
free(priv); priv = NULL;
370
if (smfi_setpriv(ctx, priv) != MI_SUCCESS) {
371
/* Not sure what we need to do here */
372
amavis_syslog(DBG_WARN, "(clearpriv) smfi_setpriv failed");
380
* allocate some private memory if not already allocated
381
* returns 0 if ok, 1 if not
384
allocmem(SMFICTX * ctx)
386
struct mlfiPriv *priv = MLFIPRIV;
389
/* amavis_syslog(DBG_DEBUG, "allocmem not needed"); */
391
amavis_syslog(DBG_DEBUG, "(allocmem) allocating private variables");
392
priv = malloc(sizeof *priv);
394
/* can't accept this message right now */
395
amavis_syslog(DBG_FATAL, "failed to malloc %d bytes for private store: %s",
396
sizeof(*priv), strerror(errno));
399
memset(priv, 0, sizeof *priv);
400
amavis_syslog(DBG_DEBUG, "malloced priv successfully");
401
if (smfi_setpriv(ctx, priv) != MI_SUCCESS) {
402
/* Not sure what we need to do here */
403
amavis_syslog(DBG_WARN, "(allocmem) smfi_setpriv failed");
410
mlfi_connect(SMFICTX * ctx, char *hostname, _SOCK_ADDR * gen_hostaddr)
412
struct mlfiPriv *priv;
413
/* discard any possible data from previous session */
414
amavis_syslog(DBG_INFO, "(mlfi_connect) client connect: hostname %s; clearing all variables", hostname);
415
clearpriv(ctx, SMFIS_CONTINUE, 1); /* discard data if any, just in case */
416
if (allocmem(ctx)) return SMFIS_TEMPFAIL;
419
if (priv->mlfi_client_addr)
420
{ free(priv->mlfi_client_addr); priv->mlfi_client_addr = NULL; }
421
if (priv->mlfi_client_name)
422
{ free(priv->mlfi_client_name); priv->mlfi_client_name = NULL; }
424
char *s = inet_ntoa( ((struct sockaddr_in *)gen_hostaddr)->sin_addr );
426
if ((priv->mlfi_client_addr = strdup(s)) == NULL)
427
return (SMFIS_TEMPFAIL);
431
if ((priv->mlfi_client_name = strdup(hostname)) == NULL)
432
return (SMFIS_TEMPFAIL);
434
return SMFIS_CONTINUE;
438
mlfi_helo(SMFICTX * ctx, char *helohost)
440
struct mlfiPriv *priv;
441
amavis_syslog(DBG_INFO, "(mlfi_helo) HELO argument is %s", helohost);
442
if (allocmem(ctx)) return SMFIS_TEMPFAIL;
444
if (priv->mlfi_helo) { free(priv->mlfi_helo); priv->mlfi_helo = NULL; }
445
if ((priv->mlfi_helo = strdup(helohost)) == NULL) return (SMFIS_TEMPFAIL);
446
return SMFIS_CONTINUE;
449
/* write synthesized received header to temp file as the first header */
450
void write_received(SMFICTX *ctx)
452
#ifdef WITH_SYNTHESIZED_RECEIVED_HEADER
454
struct mlfiPriv *priv = MLFIPRIV;
455
/* sendmail macros present by default */
456
const char *quid = smfi_getsymval(ctx, "i"); /* sendmail queue id */
457
const char *hostname = smfi_getsymval(ctx, "j"); /* sendmail's host */
458
/* optional sendmail milter macros */
459
const char *date = smfi_getsymval(ctx, "b"); /* time of transaction */
460
if (!date) { /* fallback if milter macro {b} is not defined */
463
if (!strftime(date_str, sizeof(date_str), "%a, %e %b %Y %H:%M:%S %z",
464
localtime(&t))) { date = NULL; }
466
if (fprintf(priv->mlfi_fp,
467
"Received: from %s (%s [%s])\n\tby %s (amavis-milter) id %s; %s\n",
468
priv->mlfi_helo && *(priv->mlfi_helo) ? priv->mlfi_helo : "unknown",
469
priv->mlfi_client_name ? priv->mlfi_client_name : "",
470
priv->mlfi_client_addr ? priv->mlfi_client_addr : "",
471
hostname ? hostname : "(milter macro {j} not defined)",
472
quid ? quid : "(milter macro {i} not defined)",
473
date ? date : "(milter macro {b} not defined)"
475
) amavis_syslog(DBG_FATAL,"(write_received) write of header failed: %s",
481
mlfi_envfrom(SMFICTX * ctx, char **envfrom)
483
struct mlfiPriv *priv;
486
const char *sendmail_queueid = NULL;
489
/* discard any message data from previous SMTP transaction */
490
amavis_syslog(DBG_DEBUG, "(mlfi_envfrom) clearing message variables");
491
clearpriv(ctx, SMFIS_CONTINUE, 0); /* discard previos msg data if any */
492
if (allocmem(ctx)) return SMFIS_TEMPFAIL;
495
sendmail_queueid = smfi_getsymval(ctx, "i");
496
if (!sendmail_queueid) sendmail_queueid = "";
497
priv->mlfi_queueid = strdup(sendmail_queueid);
498
if (!priv->mlfi_queueid) {
499
amavis_syslog(DBG_FATAL,"%s: (mlfi_envfrom) failed to alloc mlfi_queueid", sendmail_queueid);
500
return SMFIS_TEMPFAIL;
502
priv->mlfi_envfrom = strdup(*envfrom);
503
if (!priv->mlfi_envfrom) {
504
amavis_syslog(DBG_FATAL,"%s: (mlfi_envfrom) failed to alloc mlfi_envfrom", sendmail_queueid);
505
return SMFIS_TEMPFAIL;
509
messagepath = malloc(strlen(RUNTIME_DIR) +
510
strlen(D_TEMPPREFIX) + strlen(D_TEMPLATE) + /*reserve for worst case*/
511
(!sendmail_queueid ? 0 : strlen(sendmail_queueid)) +
512
strlen(F_TEMPLATE) + 1);
513
if (messagepath == NULL) {
514
amavis_syslog(DBG_FATAL, "%s: (mlfi_envfrom) failed to allocate memory for temp file name: %s",
515
sendmail_queueid, strerror(errno));
516
return SMFIS_TEMPFAIL;
519
strcpy(messagepath, RUNTIME_DIR);
520
strcat(messagepath, D_TEMPPREFIX);
522
#ifdef WITH_SENDMAIL_QUEUEID_TEMP_DNAME
523
if (sendmail_queueid && *sendmail_queueid) {
524
strcat(messagepath, sendmail_queueid); use_fixed_name = 1;
527
if (amavis_mkdtemp(messagepath,use_fixed_name) == NULL) {
528
amavis_syslog(DBG_FATAL, "%s: (mlfi_envfrom) failed to create temp dir %s: %s", messagepath,
529
sendmail_queueid, strerror(errno));
530
return SMFIS_TEMPFAIL;
532
/* if (chown(messagepath, (uid_t)-1, amavis_gid) < 0) {
533
* amavis_syslog(DBG_FATAL, "Failed to adjust %s group ownership (%d): %s",
534
* messagepath, amavis_gid, strerror(errno));
535
* return SMFIS_TEMPFAIL;
538
if (lstat(messagepath, &StatBuf) < 0) {
539
amavis_syslog(DBG_FATAL, "%s: (mlfi_envfrom) lstat(%s) failed: %s",
540
sendmail_queueid, messagepath, strerror(errno));
541
return SMFIS_TEMPFAIL;
543
/* may be too restrictive for you, but is good to avoid problems */
544
if (!S_ISDIR(StatBuf.st_mode) ||
545
StatBuf.st_uid != geteuid() || StatBuf.st_gid != getegid() ) {
546
amavis_syslog(DBG_FATAL,
547
"%s, Security Warning: %s must be a directory, owned by User %d "
548
"and Group %d", messagepath, sendmail_queueid, geteuid(), getegid());
549
} else if ( ((StatBuf.st_mode & 0777) != (S_IRWXU|S_IRGRP|S_IXGRP)) ) {
550
amavis_syslog(DBG_FATAL,
551
"%s, Security Warning: %s %o07 must be readable/writeable by the "
552
"User %d and readable by Group %d only",
553
sendmail_queueid, messagepath, StatBuf.st_mode, geteuid(), getegid());
555
/* there is still a race condition here if RUNTIME_DIR is writeable by the attacker :-\ */
558
strcat(messagepath, F_TEMPLATE);
559
amavis_syslog(DBG_INFO, "%s: (mlfi_envfrom) MAIL FROM: %s, tempdir: %s",
560
sendmail_queueid, *envfrom, messagepath);
561
priv->mlfi_fname = messagepath; messagepath = NULL;
563
if ((priv->mlfi_fp = fopen(priv->mlfi_fname, "w+")) == NULL) {
564
amavis_syslog(DBG_FATAL, "%s: (mlfi_envfrom) creating file %s failed: %s",
565
sendmail_queueid, priv->mlfi_fname, strerror(errno));
566
return SMFIS_TEMPFAIL;
567
} else if (fchmod(fileno(priv->mlfi_fp), S_IRUSR|S_IWUSR|S_IRGRP) == -1) {
568
amavis_syslog(DBG_FATAL, "%s: (mlfi_envfrom) fchmod on %s failed: %s",
569
sendmail_queueid, priv->mlfi_fname, strerror(errno));
570
return SMFIS_TEMPFAIL;
573
/* prepend synthesized header to the temporary file */
576
/* continue processing */
577
return SMFIS_CONTINUE;
581
mlfi_envto(SMFICTX * ctx, char **envto)
583
struct mlfiPriv *priv;
584
const char *sendmail_queueid;
585
if (allocmem(ctx)) return SMFIS_TEMPFAIL;
587
sendmail_queueid = !priv->mlfi_queueid ? "" : priv->mlfi_queueid;
588
if (!(priv->mlfi_thisenvto)) {
590
priv->mlfi_thisenvto = &(priv->mlfi_envto);
591
priv->mlfi_numto = 1;
593
if ((priv->mlfi_thisenvto->next = malloc(sizeof(ll))) == NULL)
594
return (SMFIS_TEMPFAIL);
595
priv->mlfi_thisenvto = priv->mlfi_thisenvto->next;
598
priv->mlfi_thisenvto->next = NULL;
599
priv->mlfi_thisenvto->str = NULL;
600
if ((priv->mlfi_thisenvto->str = strdup(*envto)) == NULL)
601
return (SMFIS_TEMPFAIL);
602
amavis_syslog(DBG_INFO, "%s: (mlfi_envto) RCPT TO: %s",
603
(!priv->mlfi_queueid ? "" : priv->mlfi_queueid), *envto);
604
return SMFIS_CONTINUE;
608
mlfi_header(SMFICTX *ctx, char *headerf, char *headerv)
610
struct mlfiPriv *priv = MLFIPRIV;
612
/* write the header to the temporary file */
613
if (fprintf(priv->mlfi_fp, "%s: %s\n", headerf, headerv) < 0)
614
amavis_syslog(DBG_FATAL, "%s: (mlfi_header) write of header failed: %s",
615
(!priv->mlfi_queueid ? "" : priv->mlfi_queueid), strerror(errno));
617
/* continue processing */
618
return SMFIS_CONTINUE;
622
mlfi_eoh(SMFICTX *ctx)
624
struct mlfiPriv *priv = MLFIPRIV;
625
const char *sendmail_queueid;
627
sendmail_queueid = !priv->mlfi_queueid ? "" : priv->mlfi_queueid;
628
amavis_syslog(DBG_DEBUG, "%s: (mlfi_eoh)", sendmail_queueid);
629
/* output the blank line between the header and the body */
630
if (fprintf(priv->mlfi_fp, "\n") < 0)
631
amavis_syslog(DBG_FATAL, "%s: (mlfi_eoh) writing an empty line failed: %s",
632
sendmail_queueid, strerror(errno));
633
/* continue processing */
634
return SMFIS_CONTINUE;
638
mlfi_body(SMFICTX *ctx, u_char *bodyp, size_t bodylen)
640
struct mlfiPriv *priv = MLFIPRIV;
641
/* output body block to log file */
642
u_char *d = bodyp, *s = bodyp;
643
u_char *lastc = bodyp + bodylen - 1;
645
/* convert crlf to lf */
647
if (s != lastc && *s == 13 && *(s+1) == 10)
652
bodylen = (size_t)(d - bodyp);
654
if (bodylen && fwrite(bodyp, bodylen, 1, priv->mlfi_fp) <= 0) {
655
amavis_syslog(DBG_FATAL, "%s: (mlfi_body) write of %d bytes failed: %s",
656
(!priv->mlfi_queueid ? "" : priv->mlfi_queueid),
657
bodylen, strerror(errno));
658
(void) fclose(priv->mlfi_fp); priv->mlfi_fp = NULL;
659
return mlfi_cleanup(ctx, SMFIS_TEMPFAIL, 0); /* write failed */
662
/* continue processing */
663
return SMFIS_CONTINUE;
666
/* Simple "protocol" */
667
const char _EOT = '\3';
670
mlfi_eom(SMFICTX *ctx)
672
struct mlfiPriv *priv = MLFIPRIV;
677
struct sockaddr_un saddr;
678
sfsistat rstat = SMFIS_CONTINUE;
679
const char *sendmail_queueid;
681
if (!priv) { /* no priv object */
682
amavis_syslog(DBG_FATAL, "(mlfi_eom) no private object");
683
rstat = SMFIS_TEMPFAIL;
686
sendmail_queueid = !priv->mlfi_queueid ? "" : priv->mlfi_queueid;
687
amavis_syslog(DBG_DEBUG, "%s: (mlfi_eom)", sendmail_queueid);
688
/* close the file so we can run checks on it */
690
if (fclose(priv->mlfi_fp) != 0)
691
amavis_syslog(DBG_FATAL, "%s: (mlfi_eom) close failed: %s",
692
sendmail_queueid, strerror(errno));
693
priv->mlfi_fp = NULL;
695
/* AFAIK, AF_UNIX is obsolete. POSIX defines AF_LOCAL */
696
saddr.sun_family = AF_UNIX;
697
if (strlen(AMAVISD_SOCKET)+1 > sizeof(saddr.sun_path)) {
698
amavis_syslog(DBG_FATAL, "%s: (mlfi_eom) socket path too long: %d",
699
sendmail_queueid, strlen(AMAVISD_SOCKET));
702
strcpy(saddr.sun_path, AMAVISD_SOCKET);
703
amavis_syslog(DBG_DEBUG, "%s: (mlfi_eom) allocate socket()", sendmail_queueid);
704
r = (sock = socket(PF_UNIX, SOCK_STREAM, 0));
706
amavis_syslog(DBG_FATAL, "%s: (mlfi_eom) failed to allocate socket: %s",
707
sendmail_queueid, strerror(errno));
710
amavis_syslog(DBG_DEBUG, "%s: (mlfi_eom) connect", sendmail_queueid);
711
r = connect(sock, (struct sockaddr *) (&saddr), sizeof(saddr));
713
amavis_syslog(DBG_FATAL, "%s: (mlfi_eom) failed to connect(): %s",
714
sendmail_queueid, strerror(errno));
717
char *p = strrchr(priv->mlfi_fname, '/');
718
amavis_syslog(DBG_DEBUG, "%s: (mlfi_eom) sendfile", sendmail_queueid);
719
/* amavisd wants the directory, not the filename */
721
r = send(sock, priv->mlfi_fname, strlen(priv->mlfi_fname), 0);
724
amavis_syslog(DBG_FATAL, "%s: (mlfi_eom) failed to send() file name: %s",
725
sendmail_queueid, strerror(errno));
728
r = recv(sock, &retval, 1, 0);
730
amavis_syslog(DBG_FATAL, "%s: (mlfi_eom) failed to recv() file name confirmation: %s",
731
sendmail_queueid, strerror(errno));
735
sender = (strlen(priv->mlfi_envfrom) > 0) ? priv->mlfi_envfrom : "<>";
736
amavis_syslog(DBG_DEBUG, "%s: (mlfi_eom) sendfrom() %s", sendmail_queueid, sender);
737
sender_l = strlen(sender);
738
if (sender_l > SOCKBUFLEN) {
739
amavis_syslog(DBG_WARN, "%s: (mlfi_eom) Sender too long (%d), truncated to %d characters",
740
sendmail_queueid, sender_l, SOCKBUFLEN);
741
sender_l = SOCKBUFLEN;
743
r = send(sock, sender, sender_l, 0);
745
amavis_syslog(DBG_FATAL, "%s: (mlfi_eom) failed to send() Sender: %s",
746
sendmail_queueid, strerror(errno));
747
else if (r < sender_l)
748
amavis_syslog(DBG_WARN, "%s: (mlfi_eom) failed to send() complete Sender, truncated to %d characters",
749
sendmail_queueid, r);
752
r = recv(sock, &retval, 1, 0);
754
amavis_syslog(DBG_FATAL, "%s: (mlfi_eom) failed to recv() ok for Sender info: %s",
755
sendmail_queueid, strerror(errno));
759
priv->mlfi_thisenvto = &(priv->mlfi_envto);
760
for (x = 0; (r >= 0) && (x < priv->mlfi_numto); x++) {
762
amavis_syslog(DBG_DEBUG, "%s: (mlfi_eom) sendto() %s",
763
sendmail_queueid, priv->mlfi_thisenvto->str);
764
recipient_l = strlen(priv->mlfi_thisenvto->str);
765
if (recipient_l > SOCKBUFLEN) {
766
amavis_syslog(DBG_WARN, "%s: (mlfi_eom) Recipient too long (%d), truncated to %d characters",
767
sendmail_queueid, recipient_l, SOCKBUFLEN);
768
recipient_l = SOCKBUFLEN;
770
r = send(sock, priv->mlfi_thisenvto->str, recipient_l, 0);
772
amavis_syslog(DBG_FATAL, "%s: (mlfi_eom) failed to send() Recipient: %s",
773
sendmail_queueid, strerror(errno));
776
amavis_syslog(DBG_WARN, "%s: (mlfi_eom) failed to send() complete Recipient, truncated to %d characters ",
777
sendmail_queueid, r);
778
r = recv(sock, &retval, 1, 0);
780
amavis_syslog(DBG_FATAL, "%s: (mlfi_eom) failed to recv() ok for recip info: %s",
781
sendmail_queueid, strerror(errno));
782
priv->mlfi_thisenvto = priv->mlfi_thisenvto->next;
787
amavis_syslog(DBG_DEBUG, "%s: (mlfi_eom) send() EOT", sendmail_queueid);
788
r = send(sock, &_EOT, 1, 0);
789
/* send "end of args" msg */
791
amavis_syslog(DBG_FATAL, "%s: (mlfi_eom) failed to send() EOT: %s",
792
sendmail_queueid, strerror(errno));
794
/* get result from amavisd */
795
r = recv(sock, buff, 6, 0);
796
amavis_syslog(DBG_DEBUG, "%s: (mlfi_eom) received %s from daemon", sendmail_queueid, buff);
798
amavis_syslog(DBG_FATAL, "%s: (mlfi_eom) Failed to recv() final result: %s",
799
sendmail_queueid, strerror(errno));
801
amavis_syslog(DBG_FATAL, "%s: (mlfi_eom) Failed to recv() final result: empty status string",
803
/* get back final result */
809
/* some point of the communication failed miserably - so give up */
810
amavis_syslog(DBG_FATAL, "%s: (mlfi_eom) communication failure", sendmail_queueid);
811
return mlfi_cleanup(ctx, SMFIS_TEMPFAIL, 0);
813
amavis_syslog(DBG_DEBUG, "%s: (mlfi_eom) finished conversation", sendmail_queueid);
815
/* Protect against empty return string */
822
amavis_syslog(DBG_INFO, "%s: (mlfi_eom) discarding mail, retval is %d",
823
sendmail_queueid, retval);
824
rstat = SMFIS_DISCARD;
825
} else if (retval == EX_UNAVAILABLE) { /* REJECT handling */
826
/* by Didi Rieder and Mark Martinec */
827
amavis_syslog(DBG_INFO, "%s: (mlfi_eom) rejecting mail, retval is %d",
828
sendmail_queueid, retval);
829
if (smfi_setreply(ctx, "550", "5.7.1", "Message content rejected") != MI_SUCCESS) {
830
/* Not sure what we need to do here */
831
amavis_syslog(DBG_FATAL, "%s: (mlfi_eom) smfi_setreply failed",
834
rstat = SMFIS_REJECT;
835
} else if (retval == 0) {
836
if (enable_x_header) {
837
amavis_syslog(DBG_DEBUG, "%s: (mlfi_eom) adding/changing header", sendmail_queueid);
838
if (smfi_chgheader(ctx, X_HEADER_TAG, 1, X_HEADER_LINE) == MI_FAILURE) {
839
amavis_syslog(DBG_DEBUG, "%s: (mlfi_eom) adding header", sendmail_queueid);
840
if (smfi_addheader(ctx, X_HEADER_TAG, X_HEADER_LINE) != MI_SUCCESS) {
841
amavis_syslog(DBG_FATAL,
842
"%s: (mlfi_eom) smfi_addheader failed, perhaps milter session timed out",
847
amavis_syslog(DBG_INFO, "%s: (mlfi_eom) CONTINUE delivery", sendmail_queueid);
848
rstat = SMFIS_CONTINUE;
850
/* if we got any unexpected exit status, we didn't check the file...
851
* so don't add the header. We return TEMPFAIL instead */
852
amavis_syslog(DBG_WARN, "%s: (mlfi_eom) TEMPFAIL, retval is %d",
853
sendmail_queueid, retval);
854
rstat = SMFIS_TEMPFAIL;
856
/* return mlfi_cleanup(ctx, rstat, 0); */ /* _we_ must delete dir & file */
857
return mlfi_cleanup(ctx, rstat, 1); /* server will delete the dir & file */
861
mlfi_abort(SMFICTX *ctx)
863
struct mlfiPriv *priv = MLFIPRIV;
864
amavis_syslog(DBG_DEBUG, "%s: (mlfi_abort)",
865
(!priv || !priv->mlfi_queueid ? "?" : priv->mlfi_queueid) );
866
return mlfi_cleanup(ctx, SMFIS_CONTINUE, 0);
870
mlfi_close(SMFICTX *ctx)
872
struct mlfiPriv *priv = MLFIPRIV;
873
amavis_syslog(DBG_DEBUG, "(mlfi_close) %sclearing all variables",
874
(!priv || !priv->mlfi_queueid ? "" : priv->mlfi_queueid) );
875
return clearpriv(ctx, SMFIS_CONTINUE, 1); /* discard all data */
879
mlfi_cleanup(SMFICTX *ctx, sfsistat rstat, bool keep)
881
struct mlfiPriv *priv = MLFIPRIV;
882
const char *sendmail_queueid;
886
sendmail_queueid = !priv->mlfi_queueid ? "" : priv->mlfi_queueid;
889
/* don't delete the file */
891
/* message was aborted -- delete the archive file */
893
if (fclose(priv->mlfi_fp) != 0)
894
amavis_syslog(DBG_FATAL, "%s: (mlfi_cleanup) close failed: %s",
895
sendmail_queueid, strerror(errno));
896
priv->mlfi_fp = NULL;
898
if (priv->mlfi_fname) {
900
amavis_syslog(DBG_DEBUG, "%s: (mlfi_cleanup) deleting temp file",
902
if (unlink(priv->mlfi_fname) < 0)
903
amavis_syslog(DBG_FATAL, "%s: (mlfi_cleanup) unlinking %s failed: %s",
904
sendmail_queueid, priv->mlfi_fname, strerror(errno));
905
p = strrchr(priv->mlfi_fname, '/');
907
amavis_syslog(DBG_FATAL, "%s: (mlfi_cleanup) no '/' in %s",
908
sendmail_queueid, priv->mlfi_fname);
911
if (rmdir(priv->mlfi_fname) < 0)
912
amavis_syslog(DBG_FATAL, "%s: (mlfi_cleanup) rmdir of %s failed: %s",
913
sendmail_queueid, priv->mlfi_fname, strerror(errno));
919
/* clear message data, return status */
920
amavis_syslog(DBG_DEBUG, "%s: (mlfi_cleanup) clearing message variables",
922
return clearpriv(ctx, rstat, 0); /* discard message data if any */
926
struct smfiDesc smfilter = {
927
"amavis-milter", /* filter name */
928
SMFI_VERSION, /* version code -- do not change */
929
SMFIF_ADDHDRS|SMFIF_CHGHDRS,/* flags */
930
mlfi_connect, /* connection info filter */
931
mlfi_helo, /* SMTP HELO command filter */
932
mlfi_envfrom, /* envelope sender filter */
933
mlfi_envto, /* envelope recipient filter */
934
mlfi_header, /* header filter */
935
mlfi_eoh, /* end of header */
936
mlfi_body, /* body block filter */
937
mlfi_eom, /* end of message */
938
mlfi_abort, /* message aborted */
939
mlfi_close /* connection cleanup */
946
fprintf(stderr, "usage:\n");
947
fprintf(stderr, " amavis-milter -p local:<unix-socket> [-d] [-v]\n");
948
fprintf(stderr, " amavis-milter -p inet:port@0.0.0.0 [-d] [-v]\n");
949
fprintf(stderr, " amavis-milter -h\n");
950
fprintf(stderr, "\n");
951
fprintf(stderr, "-p specifies a milter socket on which amavis-milter\n");
952
fprintf(stderr, " will listen for connections from sendmail.\n");
953
fprintf(stderr, " The argument is passed directly to libmilter, see sendmail milter\n");
954
fprintf(stderr, " documentation for details. The socket specified must match the\n");
955
fprintf(stderr, " INPUT_MAIL_FILTER macro call in the sendmail configuration file.\n");
956
fprintf(stderr, "-d debug: disables daemonisation and turns log level fully up (-vvvv) \n");
957
fprintf(stderr, "-v increases logging level by one, may be specified up to 4 times\n");
958
fprintf(stderr, "-h help: displays this usage text and exits\n");
959
fprintf(stderr, "\n");
960
fprintf(stderr, "Options -g, -x, -D are allowed for compatibility but ignored.\n");
961
fprintf(stderr, "\n");
962
fprintf(stderr, "This helper prgram (milter daemon) is normally started as:\n");
963
fprintf(stderr, "# su amavis -c '/usr/local/sbin/amavis-milter -p local:/var/amavis/amavis-milter.sock'\n");
967
main(int argc, char *argv[])
969
/* struct passwd *userinfo; *amavis uid* */
971
char *p, *milter_socket = NULL, *milter_socket_group = NULL;
972
/* const char *args = "dg:p:vx"; */
973
const char *args = ":hdg:p:Dvx"; /* some mix of old and new options!!! */
979
#if !defined(HAVE_MKDTEMP) && !defined(HAVE_MKTEMP)
980
int mypid = getpid();
982
srand48(time(NULL) ^ (mypid + (mypid << 15)));
987
/* Process command line options */
988
while ((c = getopt(argc, argv, args)) != -1) {
991
/* don't daemonise, log to stderr */
992
verbosity = DBG_DEBUG; /* full debugging log level */
996
/* name of milter socket group owner */
997
if (optarg == NULL || *optarg == '\0') {
998
fprintf(stderr, "%s: Illegal group: %s\n", argv[0], optarg);
1000
fprintf(stderr, "%s: group specification ignored (not implemented)\n", argv[0]);
1001
milter_socket_group = strdup(optarg);
1004
/* socket name - see smfi_setconn man page */
1005
if (optarg == NULL || *optarg == '\0') {
1006
fprintf(stderr, "%s: Illegal conn: %s\n", argv[0], optarg);
1009
milter_socket = strdup(optarg);
1015
AM_DAEMON = 1; /* which is also a default, unless debugging */
1018
/* enable_x_header++; */ /* older versions */
1019
/* enable_x_header = 0;*/ /* since 1.1.2.3.2.40 */
1020
fprintf(stderr, "%s: option -x ignored to avoid confusion with older versions\n", argv[0]);
1032
if (smfi_register(smfilter) == MI_FAILURE) {
1033
fprintf(stderr, "%s: smfi_register failed\n", argv[0]);
1039
/* check user and group */
1040
/* if (!(userinfo = getpwnam(AMAVIS_USER))) {
1041
* perror("getpwnam");
1042
* exit(EXIT_FAILURE);
1044
* amavis_gid = userinfo->pw_gid;
1045
* if (!milter_socket_group) {
1046
* milter_socket_group = strdup(MILTER_SOCKET_GROUP);
1047
* if (!milter_socket_group) {
1049
* exit(EXIT_FAILURE);
1052
* if (group_member(milter_socket_group) < 0) {
1053
* fprintf(stderr, "%s not member of %s group\n", AMAVIS_USER, milter_socket_group);
1054
* exit(EXIT_FAILURE);
1057
if (!milter_socket) {
1058
fprintf(stderr, "%s: no milter socket specified (missing option -p)\n\n", argv[0]);
1064
if ((p = strchr(milter_socket,'/'))) {
1065
/* Unlink any existing file that might be in place of
1066
* the socket we want to create. This might not exactly
1067
* be safe, or friendly, but I'll deal with that later.
1068
* Be nice and issue a warning if we have a problem, but
1069
* other than that, ignore it. */
1070
if (unlink(p) < 0) {
1071
amavis_syslog(DBG_INFO, "INFO: Cannot unlink old socket %s: %s", milter_socket, strerror(errno));
1075
/* Errors are detected in smfi_main */
1076
if (smfi_setconn(milter_socket) != MI_SUCCESS) {
1077
amavis_syslog(DBG_FATAL, "(main) smfi_setconn failed");
1080
/* See if we're supposed to become a daemonized process */
1081
if (AM_DAEMON == 1) {
1083
/* 2001/11/09 Anne Bennett: daemonize properly.
1084
* OK, let's be a real daemon. Taken from page 417
1085
* of Stevens' "Advanced Programming in the UNIX Environment".
1088
/* Step 1: Fork and have parent exit. This not only
1089
* backgrounds us but makes sure we are not a process group
1093
/* Fork ourselves into the background, and see if it worked */
1094
if ((pid = fork()) > 0) {
1096
amavis_syslog(DBG_INFO, "amavis-milter forked into background");
1097
/* We are the parent; exit. */
1100
} else if (pid == -1) {
1105
/* OK, we're backgrounded.
1106
* Step 2: Call setsid to create a new session. This makes
1107
* sure among other things that we have no controlling
1110
if (setsid() < (pid_t)0) {
1111
amavis_syslog(DBG_FATAL, "setsid() returned error: %s", strerror(errno));
1115
/* Step 3: Set the working directory appropriately. */
1116
if (chdir("/") < 0 ) {
1117
amavis_syslog(DBG_FATAL, "chdir(/) returned error: %s", strerror(errno));
1121
/* Step 4: Close all file descriptors. */
1122
for (i = 0; i < _POSIX_OPEN_MAX ; i++) {
1126
/* Open /dev/null read-only (fd 0 = STDIN) */
1127
if ((devnull = open(DEVNULL, O_RDONLY, 0)) < 0) {
1128
amavis_syslog(DBG_FATAL, "Could not open %s as STDIN: %s", DEVNULL, strerror(errno));
1132
amavis_syslog(DBG_FATAL, "Got wrong file descriptor as STDIN: %s != 0", DEVNULL);
1136
/* Open /dev/null write-only (fd 1 = STDOUT) */
1137
if ((devnull = open(DEVNULL, O_WRONLY, 0)) < 0) {
1138
amavis_syslog(DBG_FATAL, "Could not open %s as STDOUT: %s", DEVNULL, strerror(errno));
1142
amavis_syslog(DBG_FATAL, "Got wrong file descriptor as STDOUT: %s != 1", DEVNULL);
1146
/* Open /dev/null write-only (fd 2 = STDERR) */
1147
if ((devnull = open(DEVNULL, O_WRONLY, 0)) < 0) {
1148
amavis_syslog(DBG_FATAL, "Could not open %s as STDERR: %s", DEVNULL, strerror(errno));
1152
amavis_syslog(DBG_FATAL, "Got wrong file descriptor as STDERR: %s != 2", DEVNULL);
1157
/* change process group id */
1158
if (miltergroup && (setgid(miltergroup->gr_gid)) < 0) {
1159
amavis_syslog(DBG_FATAL, "setgid(%d): %s", miltergroup->gr_gid, strerror(errno));
1160
exit(EX_UNAVAILABLE);
1163
/* smfi_settimeout(1800); */ /* defaults to 7210 seconds */
1165
daemon_pid = getpid();
1168
/* hand control over to libmilter */
1169
amavis_syslog(DBG_INFO, "Starting, handing off to smfi_main");