Beispiel #1
0
void
do_log(LogLevel level, const char *fmt, va_list args)
{
#if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT)
	struct syslog_data sdata = SYSLOG_DATA_INIT;
#endif
	char msgbuf[MSGBUFSIZ];
	char fmtbuf[MSGBUFSIZ];
	char *txt = NULL;
	int pri = LOG_INFO;
	int saved_errno = errno;
	log_handler_fn *tmp_handler;

	if (level > log_level)
		return;

	switch (level) {
	case SYSLOG_LEVEL_FATAL:
		if (!log_on_stderr)
			txt = "fatal";
		pri = LOG_CRIT;
		break;
	case SYSLOG_LEVEL_ERROR:
		if (!log_on_stderr)
			txt = "error";
		pri = LOG_ERR;
		break;
	case SYSLOG_LEVEL_INFO:
		pri = LOG_INFO;
		break;
	case SYSLOG_LEVEL_VERBOSE:
		pri = LOG_INFO;
		break;
	case SYSLOG_LEVEL_DEBUG1:
		txt = "debug1";
		pri = LOG_DEBUG;
		break;
	case SYSLOG_LEVEL_DEBUG2:
		txt = "debug2";
		pri = LOG_DEBUG;
		break;
	case SYSLOG_LEVEL_DEBUG3:
		txt = "debug3";
		pri = LOG_DEBUG;
		break;
	default:
		txt = "internal error";
		pri = LOG_ERR;
		break;
	}
	if (txt != NULL && log_handler == NULL) {
		snprintf(fmtbuf, sizeof(fmtbuf), "%s: %s", txt, fmt);
		vsnprintf(msgbuf, sizeof(msgbuf), fmtbuf, args);
	} else {
		vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
	}
	strnvis(fmtbuf, msgbuf, sizeof(fmtbuf),
	    log_on_stderr ? LOG_STDERR_VIS : LOG_SYSLOG_VIS);
	if (log_handler != NULL) {
		/* Avoid recursion */
		tmp_handler = log_handler;
		log_handler = NULL;
		tmp_handler(level, fmtbuf, log_handler_ctx);
		log_handler = tmp_handler;
	} else if (log_on_stderr) {
		snprintf(msgbuf, sizeof msgbuf, "%s\r\n", fmtbuf);
		(void)write(log_stderr_fd, msgbuf, strlen(msgbuf));
	} else {
#if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT)
		openlog_r(argv0 ? argv0 : __progname, LOG_PID, log_facility, &sdata);
		syslog_r(pri, &sdata, "%.500s", fmtbuf);
		closelog_r(&sdata);
#else
		openlog(argv0 ? argv0 : __progname, LOG_PID, log_facility);
		syslog(pri, "%.500s", fmtbuf);
		closelog();
#endif
	}
	errno = saved_errno;
}
Beispiel #2
0
/*
 * This is used by both syslog_r and syslog.  The latter supplies
 * a non-NULL gettime callback for filling in the date, but we also
 * use the presence of that callback to decide whether it's safe
 * to call strerror and what the name of the caller is
 */
void
__vsyslog_r(int pri, struct syslog_data *data,
    size_t (*gettime)(char *, size_t), const char *fmt, va_list ap)
{
	int cnt;
	char ch, *p, *t;
	int fd, saved_errno, error;
#define	TBUF_LEN	2048
#define	FMT_LEN		1024
	char *stdp, tbuf[TBUF_LEN], fmt_cpy[FMT_LEN];
	int tbuf_left, fmt_left, prlen;

#define	INTERNALLOG	LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
	/* Check for invalid bits. */
	if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
		syslog_r(INTERNALLOG, data,
		    "syslog%s: unknown facility/priority: %x",
		    gettime != NULL ? "" : "_r", pri);
		pri &= LOG_PRIMASK|LOG_FACMASK;
	}

	/* Check priority against setlogmask values. */
	if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask))
		return;

	saved_errno = errno;

	/* Set default facility if none specified. */
	if ((pri & LOG_FACMASK) == 0)
		pri |= data->log_fac;

	p = tbuf;
	tbuf_left = TBUF_LEN;

#define	DEC()	\
	do {					\
		if (prlen < 0)			\
			prlen = 0;		\
		if (prlen >= tbuf_left)		\
			prlen = tbuf_left - 1;	\
		p += prlen;			\
		tbuf_left -= prlen;		\
	} while (0)

	prlen = snprintf(p, tbuf_left, "<%d>", pri);
	DEC();

	/* 
	 * syslogd will expand time automagically for reentrant case, and
	 * for normal case, invoke the callback to do it just do like before
	 */
	if (gettime != NULL) {
		prlen = gettime(p, tbuf_left);
		DEC();
	}

	if (data->log_stat & LOG_PERROR)
		stdp = p;
	if (data->log_tag == NULL)
		data->log_tag = __progname;
	if (data->log_tag != NULL) {
		prlen = snprintf(p, tbuf_left, "%s", data->log_tag);
		DEC();
	}
	if (data->log_stat & LOG_PID) {
		prlen = snprintf(p, tbuf_left, "[%ld]", (long)getpid());
		DEC();
	}
	if (data->log_tag != NULL) {
		if (tbuf_left > 1) {
			*p++ = ':';
			tbuf_left--;
		}
		if (tbuf_left > 1) {
			*p++ = ' ';
			tbuf_left--;
		}
	}

	/* strerror() is not reentrant */

	for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt); ++fmt) {
		if (ch == '%' && fmt[1] == 'm') {
			++fmt;
			if (gettime != NULL) {
				prlen = snprintf(t, fmt_left, "%s",
				    strerror(saved_errno)); 
			} else {
				prlen = snprintf(t, fmt_left, "Error %d",
				    saved_errno); 
			}
			if (prlen < 0)
				prlen = 0;
			if (prlen >= fmt_left)
				prlen = fmt_left - 1;
			t += prlen;
			fmt_left -= prlen;
		} else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) {
			*t++ = '%';
			*t++ = '%';
			fmt++;
			fmt_left -= 2;
		} else {
			if (fmt_left > 1) {
				*t++ = ch;
				fmt_left--;
			}
		}
	}
	*t = '\0';

	prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap);
	DEC();
	cnt = p - tbuf;

	/* Output to stderr if requested. */
	if (data->log_stat & LOG_PERROR) {
		struct iovec iov[2];

		iov[0].iov_base = stdp;
		iov[0].iov_len = cnt - (stdp - tbuf);
		iov[1].iov_base = "\n";
		iov[1].iov_len = 1;
		(void)writev(STDERR_FILENO, iov, 2);
	}

	/* Get connected, output the message to the local logger. */
	if (!data->opened)
		openlog_r(data->log_tag, data->log_stat, 0, data);
	connectlog_r(data);

	/*
	 * If the send() failed, there are two likely scenarios:
	 *  1) syslogd was restarted
	 *  2) /dev/log is out of socket buffer space
	 * We attempt to reconnect to /dev/log to take care of
	 * case #1 and keep send()ing data to cover case #2
	 * to give syslogd a chance to empty its socket buffer.
	 */
	if ((error = send(data->log_file, tbuf, cnt, 0)) < 0) {
		if (errno != ENOBUFS) {
			disconnectlog_r(data);
			connectlog_r(data);
		}
		do {
			struct timespec rqt = { 0, 1000 };

			nanosleep(&rqt, NULL);
			if ((error = send(data->log_file, tbuf, cnt, 0)) >= 0)
				break;
		} while (errno == ENOBUFS);
	}

	/*
	 * Output the message to the console; try not to block
	 * as a blocking console should not stop other processes.
	 * Make sure the error reported is the one from the syslogd failure.
	 */
	if (error == -1 && (data->log_stat & LOG_CONS) &&
	    (fd = open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) {
		struct iovec iov[2];
		
		p = strchr(tbuf, '>') + 1;
		iov[0].iov_base = p;
		iov[0].iov_len = cnt - (p - tbuf);
		iov[1].iov_base = "\r\n";
		iov[1].iov_len = 2;
		(void)writev(fd, iov, 2);
		(void)close(fd);
	}
}
Beispiel #3
0
int
main(int argc, char *argv[])
{
	fd_set 		set;
	int 		ch, uflag = 0, p;
	struct passwd  *pw;

	if (geteuid() != 0) {
		(void) fprintf(stderr, "%s: need root privileges\n",
			       __progname);
		exit(1);
	}
	pw = getpwnam("_discardd");
	if (pw == NULL) {
		(void) fprintf(stderr, "%s: no _discardd user\n", __progname);
		exit(1);
	}

	while ((ch = getopt(argc, argv, "u")) != -1) {
		switch (ch) {
		case 'u':
			uflag = 1;
			break;
		default:
			usage();
			/* NOTREACHED */
		}
	}
	argc -= optind;
	argv += optind;

	openlog_r(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON, &sdata);

	/* Start TCP and UDP protocols separately. */
	starttcp();
	if (uflag)
		startudp();

	/* Drop privileges */
	if (chroot("/var/empty") != 0 || chdir("/") != 0) {
		(void) fprintf(stderr, "%s: could not chroot\n", __progname);
		exit(1);
	}
	if (setgroups(1, &pw->pw_gid) ||
	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) {
		(void) fprintf(stderr, "%s: can't drop privileges\n",
			       __progname);
		exit(1);
	}

        if (daemon(0, 0) < 0) {
                (void) fprintf(stderr, "%s: unable to daemonize\n", __progname);
		exit(1);
	}

#ifdef __OpenBSD__
	if ((p = pledge("stdio inet proc", NULL)) == -1) {
		fprintf(stderr, "%s: pledge(2) failed: %s\n", __progname,
			strerror(errno));
		exit(1);
	}
#endif

	signal(SIGCHLD, SIG_IGN);
	signal(SIGUSR1, quit);

	FD_ZERO(&set);

	while (1) {
		FD_SET(tcpfd, &set);
		if (uflag)
			FD_SET(udpfd, &set);

		if (select(MAX(tcpfd, udpfd) + 1, &set, NULL, NULL, NULL) < 0) {
			syslog_r(LOG_ERR, &sdata, "%s", strerror(errno));
			exit(1);
		}

		if (tcpfd > -1 && FD_ISSET(tcpfd, &set))
			discardtcp();

		if (uflag && udpfd > -1 && FD_ISSET(udpfd, &set))
			discardudp();
	}

	return 0;
}
Beispiel #4
0
STATUS
ERsend(i4 flag, char *message, i4 msg_length, CL_ERR_DESC *err_code)
{
# ifdef NT_GENERIC
    static bool		er_init = FALSE;
    static bool		is_w95 = FALSE;
# else /* !NT_GENERIC */
    static int		er_ifi = -2;
    static int          ar_ifi = -2;
# endif /* !NT_GENERIC */
    STATUS		status;
    char                tmp_buf[ER_MAX_LEN];
    char*               logmsg = message;

    /*	Check for bad paramters. */

    CL_CLEAR_ERR( err_code );

    if ((message == 0 || msg_length == 0) && flag != ER_AUDIT_MSG)
        return (ER_BADPARAM);

    if ((flag != ER_ERROR_MSG) && (flag != ER_AUDIT_MSG) &&
        ( flag != ER_OPER_MSG))
        return (ER_BADPARAM);

# ifndef NT_GENERIC
    if (flag & ER_AUDIT_MSG)
    {
        key_t msg_key;
        char  *ipc_number;
        struct
        {
            long    mtype;
            char    mtext[ER_MAX_LEN];
        }   msg;

        if (ar_ifi == -2)
        {
            NMgtAt("II_AUDIT_IPC", &ipc_number);
            if (ipc_number && ipc_number[0])
            {
                CVal(ipc_number, &msg_key);
                ar_ifi = msgget(msg_key, 0);
                if (ar_ifi == -1)
                {
                    SETCLERR(err_code, 0, ER_open);
                    return(ER_NO_AUDIT);
                }
            }
            else
            {
                SETCLERR(err_code, 0, ER_open);
                return(ER_NO_AUDIT);
            }

        }
 
        /*  Handle special case to connect only but not send message. */
 
        if (msg_length == 0 && message == 0)
                return (OK);

        MEcopy(message, msg_length, msg.mtext);
        msg.mtype = 1;
        if (msgsnd(ar_ifi, &msg, msg_length, 0))
        {
            SETCLERR(err_code, 0, ER_open);
            return(ER_BADSEND);
        }
        return (OK);
    }
    else
# endif /* ! NT_GENERIC */
    if (flag & ER_OPER_MSG)
    {
        char    hostname[GL_MAXNAME];
        STATUS  status;
 
        message[msg_length] = EOS;
        TRdisplay("ER Operator:\"%s\"\n",message);
	if (!ERsysinit)
	    ERinitsyslog();
# ifdef NT_GENERIC
        {
        wchar_t *wmessage = NULL;

        /*
        ** Update the ReportEvent to report information in the event log.
        */
        if ( ReportEvent( EventLog,
                        (WORD) EVENTLOG_INFORMATION_TYPE,
                        (WORD) 0,             /* event category */
                        (DWORD) I_ING_INFO,   /* event identifier */
                        (PSID) NULL,
                        (WORD) 1,             /* number of strings */
                        (DWORD) 0,
                        &message,
                        NULL ) == FALSE)   
                status = GetLastError();
	if ( !er_init )
	{
	    char		VersionString[256];
	    FUNC_EXTERN BOOL	GVosvers(char *OSVersionString);

	    GVosvers(VersionString);
	    is_w95 = ( STstrindex(VersionString, "Microsoft Windows 9",
				  0, FALSE) != NULL ) ? TRUE : FALSE;

	    if ( !is_w95 ) /* netapi32 only on NT */
	    {
		HANDLE hDll;
                if ((hDll = LoadLibrary(TEXT("netapi32.dll"))) != NULL)
                {
                    pNetMessageNameAdd = 
		     (NET_API_STATUS (*)(LPCWSTR,LPCWSTR))
		     GetProcAddress(hDll, TEXT("NetMessageNameAdd"));
                    pNetMessageNameDel = 
		     (NET_API_STATUS (*)(LPCWSTR,LPCWSTR))
		     GetProcAddress(hDll, TEXT("NetMessageNameDel"));
                    pNetMessageBufferSend = 
		      (NET_API_STATUS (*)(LPCWSTR,LPCWSTR,LPCWSTR,LPBYTE,DWORD))
		      GetProcAddress(hDll, TEXT("NetMessageBufferSend"));
		}
		/* if any problem, pretend we don't support it */
		if ( pNetMessageNameAdd == NULL ||
		     pNetMessageNameDel == NULL ||
		     pNetMessageBufferSend == NULL )
		    is_w95 = TRUE;
	    }
	}

	if ( !is_w95 )
	{
	    /*
	    ** Now, send the message to the server console,
	    ** putting up a message box (if the messenger service
	    ** is running.  Everything must be in Unicode.
	    */

	    if ( whostname[0] == 0 )
	    {
		unsigned int len = sizeof(hostname);
                /* 
		** get the hostname in Unicode format for use 
		** by messenger service 
		*/
                GetComputerName( (char *)hostname, &len );
		MultiByteToWideChar( GetACP(), 0,
				     hostname, sizeof(hostname),
				     whostname, sizeof(whostname) );
	    }
            /* initialize the messenger service */
            status = (*pNetMessageNameAdd)( whostname, msgname );
            if ( status != NERR_Success )
	        status = GetLastError();

	    /* Allocate a buffer for the Unicode */
	    wmessage = (wchar_t *) MEreqmem( 0, msg_length * sizeof(wchar_t), 
				             TRUE, &status );
	    if ( wmessage )
	    {
	        /* copy the message to the Unicode buffer */
		MultiByteToWideChar( GetACP(), 0,
				     message, msg_length,
				     wmessage, msg_length * sizeof(wchar_t) );
                status = (*pNetMessageBufferSend)( whostname, 
					       msgname, 
					       NULL, 
				               (LPBYTE) wmessage, 
					       msg_length*sizeof(wchar_t) );
                if ( status != NERR_Success )
	            status = GetLastError();
	        MEfree( (PTR)wmessage );
	    }

            /* re-initialize the messenger service */
            status = (*pNetMessageNameDel)( whostname, msgname );
            if ( status != NERR_Success )
	        status = GetLastError();

	}
	}
# elif defined(OS_THREADS_USED) && defined(any_aix)
	syslog_r( LOG_ALERT|LOG_ERR, message );
# else
	syslog( LOG_ALERT|LOG_ERR, message );
# endif /* NT_GENERIC */
    }

    if (flag & ER_OPER_MSG)
    {
        i4 msglen = 0;
	char* host = PMhost();

        MEfill( ER_MAX_LEN, 0, tmp_buf );

        /*
        ** Format the message string for the event log.  As the source is
        ** not known a fixed string of INGSYSLOG is used.
        */
        TRformat( NULL, 0, tmp_buf, ER_MAX_LEN - 1,
            "%8.8t::[INGSYSLOG         , 00000000]: %@ ", STlength(host),
            host );
        msglen = STlength(tmp_buf);
        STcat( tmp_buf, message );  /* append original message */
        msg_length += msglen;
        logmsg = tmp_buf;
    }
    status = ERlog( logmsg, msg_length, err_code );
    return( status );
}
Beispiel #5
0
void
nextstate(struct con *cp)
{
	if (match(cp->ibuf, "QUIT") && cp->state < 99) {
		snprintf(cp->obuf, cp->osize, "221 %s\r\n", hostname);
		cp->op = cp->obuf;
		cp->ol = strlen(cp->op);
		cp->w = t + cp->stutter;
		cp->laststate = cp->state;
		cp->state = 99;
		return;
	}

	if (match(cp->ibuf, "RSET") && cp->state > 2 && cp->state < 50) {
		snprintf(cp->obuf, cp->osize,
		    "250 " RSET_RESPONSE "\r\n");
		cp->op = cp->obuf;
		cp->ol = strlen(cp->op);
		cp->w = t + cp->stutter;
		cp->laststate = cp->state;
		cp->state = 2;
		return;
	}
	switch (cp->state) {
	case 0:
		/* banner sent; wait for input */
		cp->ip = cp->ibuf;
		cp->il = sizeof(cp->ibuf) - 1;
		cp->laststate = cp->state;
		cp->state = 1;
		cp->r = t;
		break;
	case 1:
		/* received input: parse, and select next state */
		if (match(cp->ibuf, "HELO") ||
		    match(cp->ibuf, "EHLO")) {
			int nextstate = 2;
			cp->helo[0] = '\0'; 
			gethelo(cp->helo, sizeof cp->helo, cp->ibuf);
			if (cp->helo[0] == '\0') {
				nextstate = 0;
				snprintf(cp->obuf, cp->osize,
				    "501 helo requires domain name.\r\n");
			} else {
				snprintf(cp->obuf, cp->osize,
			    	    "250 " HELO_RESPONSE "\r\n");
			}
			cp->op = cp->obuf;
			cp->ol = strlen(cp->op);
			cp->laststate = cp->state;
			cp->state = nextstate;
			cp->w = t + cp->stutter;
			break;
		}
		goto mail;
	case 2:
		/* sent 250 Hello, wait for input */
		cp->ip = cp->ibuf;
		cp->il = sizeof(cp->ibuf) - 1;
		cp->laststate = cp->state;
		cp->state = 3;
		cp->r = t;
		break;
	case 3:
	mail:
		if (match(cp->ibuf, "MAIL")) {
			setlog(cp->mail, sizeof cp->mail, cp->ibuf);
			snprintf(cp->obuf, cp->osize,
			    "250 " MAIL_RESPONSE "\r\n");
			cp->op = cp->obuf;
			cp->ol = strlen(cp->op);
			cp->laststate = cp->state;
			cp->state = 4;
			cp->w = t + cp->stutter;
			break;
		}
		goto rcpt;
	case 4:
		/* sent 250 Sender ok */
		cp->ip = cp->ibuf;
		cp->il = sizeof(cp->ibuf) - 1;
		cp->laststate = cp->state;
		cp->state = 5;
		cp->r = t;
		break;
	case 5:
	rcpt:
		if (match(cp->ibuf, "RCPT")) {
			setlog(cp->rcpt, sizeof(cp->rcpt), cp->ibuf);
			snprintf(cp->obuf, cp->osize,
			    "250 " RCPT_RESPONSE "\r\n");
			cp->op = cp->obuf;
			cp->ol = strlen(cp->op);
			cp->laststate = cp->state;
			cp->state = 6;
			cp->w = t + cp->stutter;
			if (cp->mail[0] && cp->rcpt[0]) {
				if (verbose)
					syslog_r(LOG_INFO, &sdata,
					    "(%s) %s: %s -> %s",
					    cp->blacklists ? "BLACK" : "GREY",
					    cp->addr, cp->mail,
					    cp->rcpt);
				if (debug)
					fprintf(stderr, "(%s) %s: %s -> %s\n",
					    cp->blacklists ? "BLACK" : "GREY",
					    cp->addr, cp->mail, cp->rcpt);
				if (greylist && cp->blacklists == NULL) {
					/* send this info to the greylister */
					getcaddr(cp);
					fprintf(grey,
					    "CO:%s\nHE:%s\nIP:%s\nFR:%s\nTO:%s\n",
					    cp->caddr, cp->helo, cp->addr,
					    cp->mail, cp->rcpt);
					fflush(grey);
				}
			}
			break;
		}
		goto spam;
	case 6:
		/* sent 250 blah */
		cp->ip = cp->ibuf;
		cp->il = sizeof(cp->ibuf) - 1;
		cp->laststate = cp->state;
		cp->state = 5;
		cp->r = t;
		break;

	case 50:
	spam:
		if (match(cp->ibuf, "DATA")) {
			snprintf(cp->obuf, cp->osize,
			    "354 " DATA_RESPONSE "\r\n");
			cp->state = 60;
			if (window && setsockopt(cp->fd, SOL_SOCKET, SO_RCVBUF,
			    &window, sizeof(window)) == -1) {
				syslog_r(LOG_DEBUG, &sdata,"setsockopt: %m");
				/* don't fail if this doesn't work. */
			}
			cp->ip = cp->ibuf;
			cp->il = sizeof(cp->ibuf) - 1;
			cp->op = cp->obuf;
			cp->ol = strlen(cp->op);
			cp->w = t + cp->stutter;
			if (greylist && cp->blacklists == NULL) {
				cp->laststate = cp->state;
				cp->state = 98;
				goto done;
			}
		} else {
			if (match(cp->ibuf, "NOOP")) 
				snprintf(cp->obuf, cp->osize,
				    "250 " NOOP_RESPONSE "\r\n");
			else
                        	snprintf(cp->obuf, cp->osize,
				    "500 " UNRECOGNIZED_RESPONSE "\r\n");
			cp->state = cp->laststate;
			cp->ip = cp->ibuf;
			cp->il = sizeof(cp->ibuf) - 1;
			cp->op = cp->obuf;
			cp->ol = strlen(cp->op);
			cp->w = t + cp->stutter;
		}
		break;
	case 60:
		/* sent 354 blah */
		cp->ip = cp->ibuf;
		cp->il = sizeof(cp->ibuf) - 1;
		cp->laststate = cp->state;
		cp->state = 70;
		cp->r = t;
		break;
	case 70: {
		char *p, *q;

		for (p = q = cp->ibuf; q <= cp->ip; ++q)
			if (*q == '\n' || q == cp->ip) {
				*q = 0;
				if (q > p && q[-1] == '\r')
					q[-1] = 0;
				if (!strcmp(p, ".") ||
				    (cp->data_body && ++cp->data_lines >= 10)) {
					cp->laststate = cp->state;
					cp->state = 98;
					goto done;
				}
				if (!cp->data_body && !*p)
					cp->data_body = 1;
				if (verbose && cp->data_body && *p)
					syslog_r(LOG_DEBUG, &sdata, "%s: "
					    "Body: %s", cp->addr, p);
				else if (verbose && (match(p, "FROM:") ||
				    match(p, "TO:") || match(p, "SUBJECT:")))
					syslog_r(LOG_INFO, &sdata, "%s: %s",
					    cp->addr, p);
				p = ++q;
			}
		cp->ip = cp->ibuf;
		cp->il = sizeof(cp->ibuf) - 1;
		cp->r = t;
		break;
	}
	case 98:
	done:
		doreply(cp);
		cp->op = cp->obuf;
		cp->ol = strlen(cp->op);
		cp->w = t + cp->stutter;
		cp->laststate = cp->state;
		cp->state = 99;
		break;
	case 99:
		closecon(cp);
		break;
	default:
		errx(1, "illegal state %d", cp->state);
		break;
	}
}
Beispiel #6
0
int
main(int argc, char *argv[])
{
	fd_set *fdsr = NULL, *fdsw = NULL;
	struct sockaddr_in sin;
	struct sockaddr_in lin;
	int ch, s, s2, conflisten = 0, syncfd = 0, i, omax = 0, one = 1;
	socklen_t sinlen;
	u_short port;
	struct servent *ent;
	struct rlimit rlp;
	char *bind_address = NULL;
	const char *errstr;
	char *sync_iface = NULL;
	char *sync_baddr = NULL;

	tzset();
	openlog_r("spamd", LOG_PID | LOG_NDELAY, LOG_DAEMON, &sdata);

	if ((ent = getservbyname("spamd", "tcp")) == NULL)
		errx(1, "Can't find service \"spamd\" in /etc/services");
	port = ntohs(ent->s_port);
	if ((ent = getservbyname("spamd-cfg", "tcp")) == NULL)
		errx(1, "Can't find service \"spamd-cfg\" in /etc/services");
	cfg_port = ntohs(ent->s_port);
	if ((ent = getservbyname("spamd-sync", "udp")) == NULL)
		errx(1, "Can't find service \"spamd-sync\" in /etc/services");
	sync_port = ntohs(ent->s_port);

	if (gethostname(hostname, sizeof hostname) == -1)
		err(1, "gethostname");
	maxfiles = get_maxfiles();
	if (maxcon > maxfiles)
		maxcon = maxfiles;
	if (maxblack > maxfiles)
		maxblack = maxfiles;
	while ((ch =
	    getopt(argc, argv, "45l:c:B:p:bdG:h:r:s:S:M:n:vw:y:Y:")) != -1) {
		switch (ch) {
		case '4':
			nreply = "450";
			break;
		case '5':
			nreply = "550";
			break;
		case 'l':
			bind_address = optarg;
			break;
		case 'B':
			i = atoi(optarg);
			maxblack = i;
			break;
		case 'c':
			i = atoi(optarg);
			if (i > maxfiles) {
				fprintf(stderr,
				    "%d > system max of %d connections\n",
				    i, maxfiles);
				usage();
			}
			maxcon = i;
			break;
		case 'p':
			i = atoi(optarg);
			port = i;
			break;
		case 'd':
			debug = 1;
			break;
		case 'b':
			greylist = 0;
			break;
		case 'G':
			if (sscanf(optarg, "%d:%d:%d", &passtime, &greyexp,
			    &whiteexp) != 3)
				usage();
			/* convert to seconds from minutes */
			passtime *= 60;
			/* convert to seconds from hours */
			whiteexp *= (60 * 60);
			/* convert to seconds from hours */
			greyexp *= (60 * 60);
			break;
		case 'h':
			bzero(&hostname, sizeof(hostname));
			if (strlcpy(hostname, optarg, sizeof(hostname)) >=
			    sizeof(hostname))
				errx(1, "-h arg too long");
			break;
		case 's':
			i = atoi(optarg);
			if (i < 0 || i > 10)
				usage();
			stutter = i;
			break;
		case 'S':
			i = strtonum(optarg, 0, 90, &errstr);
			if (errstr)
				usage();
			grey_stutter = i;
			break;
		case 'M':
			low_prio_mx_ip = optarg;
			break;
		case 'n':
			spamd = optarg;
			break;
		case 'v':
			verbose = 1;
			break;
		case 'w':
			window = atoi(optarg);
			if (window <= 0)
				usage();
			break;
		case 'Y':
			if (sync_addhost(optarg, sync_port) != 0)
				sync_iface = optarg;
			syncsend++;
			break;
		case 'y':
			sync_baddr = optarg;
			syncrecv++;
			break;
		default:
			usage();
			break;
		}
	}

	setproctitle("[priv]%s%s",
	    greylist ? " (greylist)" : "",
	    (syncrecv || syncsend) ? " (sync)" : "");

	if (!greylist)
		maxblack = maxcon;
	else if (maxblack > maxcon)
		usage();

	rlp.rlim_cur = rlp.rlim_max = maxcon + 15;
	if (setrlimit(RLIMIT_NOFILE, &rlp) == -1)
		err(1, "setrlimit");

	con = calloc(maxcon, sizeof(*con));
	if (con == NULL)
		err(1, "calloc");

	con->obuf = malloc(8192);

	if (con->obuf == NULL)
		err(1, "malloc");
	con->osize = 8192;

	for (i = 0; i < maxcon; i++)
		con[i].fd = -1;

	signal(SIGPIPE, SIG_IGN);

	s = socket(AF_INET, SOCK_STREAM, 0);
	if (s == -1)
		err(1, "socket");

	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one,
	    sizeof(one)) == -1)
		return (-1);

	conflisten = socket(AF_INET, SOCK_STREAM, 0);
	if (conflisten == -1)
		err(1, "socket");

	if (setsockopt(conflisten, SOL_SOCKET, SO_REUSEADDR, &one,
	    sizeof(one)) == -1)
		return (-1);

	memset(&sin, 0, sizeof sin);
	sin.sin_len = sizeof(sin);
	if (bind_address) {
		if (inet_pton(AF_INET, bind_address, &sin.sin_addr) != 1)
			err(1, "inet_pton");
	} else
		sin.sin_addr.s_addr = htonl(INADDR_ANY);
	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);

	if (bind(s, (struct sockaddr *)&sin, sizeof sin) == -1)
		err(1, "bind");

	memset(&lin, 0, sizeof sin);
	lin.sin_len = sizeof(sin);
	lin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	lin.sin_family = AF_INET;
	lin.sin_port = htons(cfg_port);

	if (bind(conflisten, (struct sockaddr *)&lin, sizeof lin) == -1)
		err(1, "bind local");

	if (syncsend || syncrecv) {
		syncfd = sync_init(sync_iface, sync_baddr, sync_port);
		if (syncfd == -1)
			err(1, "sync init");
	}

	pw = getpwnam("_spamd");
	if (!pw)
		pw = getpwnam("nobody");

	if (debug == 0) {
		if (daemon(1, 1) == -1)
			err(1, "daemon");
	}

	if (greylist) {
		pfdev = open("/dev/pf", O_RDWR);
		if (pfdev == -1) {
			syslog_r(LOG_ERR, &sdata, "open /dev/pf: %m");
			exit(1);
		}

		maxblack = (maxblack >= maxcon) ? maxcon - 100 : maxblack;
		if (maxblack < 0)
			maxblack = 0;

		/* open pipe to talk to greylister */
		if (pipe(greypipe) == -1) {
			syslog(LOG_ERR, "pipe (%m)");
			exit(1);
		}
		/* open pipe to recieve spamtrap configs */
		if (pipe(trappipe) == -1) {
			syslog(LOG_ERR, "pipe (%m)");
			exit(1);
		}
		jail_pid = fork();
		switch (jail_pid) {
		case -1:
			syslog(LOG_ERR, "fork (%m)");
			exit(1);
		case 0:
			/* child - continue */
			signal(SIGPIPE, SIG_IGN);
			grey = fdopen(greypipe[1], "w");
			if (grey == NULL) {
				syslog(LOG_ERR, "fdopen (%m)");
				_exit(1);
			}
			close(greypipe[0]);
			trapfd = trappipe[0];
			trapcfg = fdopen(trappipe[0], "r");
			if (trapcfg == NULL) {
				syslog(LOG_ERR, "fdopen (%m)");
				_exit(1);
			}
			close(trappipe[1]);
			goto jail;
		}
		/* parent - run greylister */
		grey = fdopen(greypipe[0], "r");
		if (grey == NULL) {
			syslog(LOG_ERR, "fdopen (%m)");
			exit(1);
		}
		close(greypipe[1]);
		trapcfg = fdopen(trappipe[1], "w");
		if (trapcfg == NULL) {
			syslog(LOG_ERR, "fdopen (%m)");
			exit(1);
		}
		close(trappipe[0]);
		return (greywatcher());
		/* NOTREACHED */
	}

jail:
	if (chroot("/var/empty") == -1 || chdir("/") == -1) {
		syslog(LOG_ERR, "cannot chdir to /var/empty.");
		exit(1);
	}

	if (pw)
#ifdef __OpenBSD__
		if (setgroups(1, &pw->pw_gid) ||
		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
			err(1, "failed to drop privs");
#else
/* TODO */
		setgroups(1, &pw->pw_gid);
		setegid(pw->pw_gid);
		setgid(pw->pw_gid);
		seteuid(pw->pw_uid);
		setuid(pw->pw_uid);
#endif

	if (listen(s, 10) == -1)
		err(1, "listen");

	if (listen(conflisten, 10) == -1)
		err(1, "listen");

	if (debug != 0)
		printf("listening for incoming connections.\n");
	syslog_r(LOG_WARNING, &sdata, "listening for incoming connections.");

	while (1) {
		struct timeval tv, *tvp;
		int max, n;
		int writers;

		max = MAX(s, conflisten);
		if (syncrecv)
			max = MAX(max, syncfd);
		max = MAX(max, conffd);
		max = MAX(max, trapfd);

		time(&t);
		for (i = 0; i < maxcon; i++)
			if (con[i].fd != -1)
				max = MAX(max, con[i].fd);

		if (max > omax) {
			free(fdsr);
			fdsr = NULL;
			free(fdsw);
			fdsw = NULL;
			fdsr = (fd_set *)calloc(howmany(max+1, NFDBITS),
			    sizeof(fd_mask));
			if (fdsr == NULL)
				err(1, "calloc");
			fdsw = (fd_set *)calloc(howmany(max+1, NFDBITS),
			    sizeof(fd_mask));
			if (fdsw == NULL)
				err(1, "calloc");
			omax = max;
		} else {
			memset(fdsr, 0, howmany(max+1, NFDBITS) *
			    sizeof(fd_mask));
			memset(fdsw, 0, howmany(max+1, NFDBITS) *
			    sizeof(fd_mask));
		}

		writers = 0;
		for (i = 0; i < maxcon; i++) {
			if (con[i].fd != -1 && con[i].r) {
				if (con[i].r + MAXTIME <= t) {
					closecon(&con[i]);
					continue;
				}
				FD_SET(con[i].fd, fdsr);
			}
			if (con[i].fd != -1 && con[i].w) {
				if (con[i].w + MAXTIME <= t) {
					closecon(&con[i]);
					continue;
				}
				if (con[i].w <= t)
					FD_SET(con[i].fd, fdsw);
				writers = 1;
			}
		}
		FD_SET(s, fdsr);

		/* only one active config conn at a time */
		if (conffd == -1)
			FD_SET(conflisten, fdsr);
		else
			FD_SET(conffd, fdsr);
		if (trapfd != -1)
			FD_SET(trapfd, fdsr);
		if (syncrecv)
			FD_SET(syncfd, fdsr);

		if (writers == 0) {
			tvp = NULL;
		} else {
			tv.tv_sec = 1;
			tv.tv_usec = 0;
			tvp = &tv;
		}

		n = select(max+1, fdsr, fdsw, NULL, tvp);
		if (n == -1) {
			if (errno != EINTR)
				err(1, "select");
			continue;
		}
		if (n == 0)
			continue;

		for (i = 0; i < maxcon; i++) {
			if (con[i].fd != -1 && FD_ISSET(con[i].fd, fdsr))
				handler(&con[i]);
			if (con[i].fd != -1 && FD_ISSET(con[i].fd, fdsw))
				handlew(&con[i], clients + 5 < maxcon);
		}
		if (FD_ISSET(s, fdsr)) {
			sinlen = sizeof(sin);
			s2 = accept(s, (struct sockaddr *)&sin, &sinlen);
			if (s2 == -1)
				/* accept failed, they may try again */
				continue;
			for (i = 0; i < maxcon; i++)
				if (con[i].fd == -1)
					break;
			if (i == maxcon)
				close(s2);
			else {
				initcon(&con[i], s2, (struct sockaddr *)&sin);
				syslog_r(LOG_INFO, &sdata,
				    "%s: connected (%d/%d)%s%s",
				    con[i].addr, clients, blackcount,
				    ((con[i].lists == NULL) ? "" :
				    ", lists:"),
				    ((con[i].lists == NULL) ? "":
				    con[i].lists));
			}
		}
		if (FD_ISSET(conflisten, fdsr)) {
			sinlen = sizeof(lin);
			conffd = accept(conflisten, (struct sockaddr *)&lin,
			    &sinlen);
			if (conffd == -1)
				/* accept failed, they may try again */
				continue;
			else if (ntohs(lin.sin_port) >= IPPORT_RESERVED) {
				close(conffd);
				conffd = -1;
			}
		}
		if (conffd != -1 && FD_ISSET(conffd, fdsr))
			do_config();
		if (trapfd != -1 && FD_ISSET(trapfd, fdsr))
			read_configline(trapcfg);
		if (syncrecv && FD_ISSET(syncfd, fdsr))
			sync_recv();
	}
	exit(1);
}
void
nextstate(struct con *cp)
{
	if (match(cp->ibuf, "QUIT") && cp->state < 99) {
		snprintf(cp->obuf, cp->osize, "221 %s\r\n", hostname);
		cp->op = cp->obuf;
		cp->ol = strlen(cp->op);
		cp->w = t + cp->stutter;
		cp->laststate = cp->state;
		cp->state = 99;
		return;
	}

	if (match(cp->ibuf, "RSET") && cp->state > 2 && cp->state < 50) {
		snprintf(cp->obuf, cp->osize,
		    "250 Ok to start over.\r\n");
		cp->op = cp->obuf;
		cp->ol = strlen(cp->op);
		cp->w = t + cp->stutter;
		cp->laststate = cp->state;
		cp->state = 2;
		return;
	}
	switch (cp->state) {
	case 0:
		/* banner sent; wait for input */
		cp->ip = cp->ibuf;
		cp->il = sizeof(cp->ibuf) - 1;
		cp->laststate = cp->state;
		cp->state = 1;
		cp->r = t;
		break;
	case 1:
		/* received input: parse, and select next state */
		if (match(cp->ibuf, "HELO") ||
		    match(cp->ibuf, "EHLO")) {
			gethelo(cp->helo, sizeof cp->helo, cp->ibuf);
			snprintf(cp->obuf, cp->osize,
			    "250 Hello, spam sender. "
			    "Pleased to be wasting your time.\r\n");
			cp->op = cp->obuf;
			cp->ol = strlen(cp->op);
			cp->laststate = cp->state;
			cp->state = 2;
			cp->w = t + cp->stutter;
			break;
		}
		goto mail;
	case 2:
		/* sent 250 Hello, wait for input */
		cp->ip = cp->ibuf;
		cp->il = sizeof(cp->ibuf) - 1;
		cp->laststate = cp->state;
		cp->state = 3;
		cp->r = t;
		break;
	case 3:
	mail:
		if (match(cp->ibuf, "MAIL")) {
			setlog(cp->mail, sizeof cp->mail, cp->ibuf);
			snprintf(cp->obuf, cp->osize,
			    "250 You are about to try to deliver spam. "
			    "Your time will be spent, for nothing.\r\n");
			cp->op = cp->obuf;
			cp->ol = strlen(cp->op);
			cp->laststate = cp->state;
			cp->state = 4;
			cp->w = t + cp->stutter;
			break;
		}
		goto rcpt;
	case 4:
		/* sent 250 Sender ok */
		cp->ip = cp->ibuf;
		cp->il = sizeof(cp->ibuf) - 1;
		cp->laststate = cp->state;
		cp->state = 5;
		cp->r = t;
		break;
	case 5:
	rcpt:
		if (match(cp->ibuf, "RCPT")) {
			setlog(cp->rcpt, sizeof(cp->rcpt), cp->ibuf);
			snprintf(cp->obuf, cp->osize,
			    "250 This is hurting you more than it is "
			    "hurting me.\r\n");
			cp->op = cp->obuf;
			cp->ol = strlen(cp->op);
			cp->laststate = cp->state;
			cp->state = 6;
			cp->w = t + cp->stutter;
			if (cp->mail[0] && cp->rcpt[0]) {
				if (verbose)
					syslog_r(LOG_INFO, &sdata,
					    "(%s) %s: %s -> %s",
					    cp->blacklists ? "BLACK" : "GREY",
					    cp->addr, cp->mail,
					    cp->rcpt);
				if (debug)
					fprintf(stderr, "(%s) %s: %s -> %s\n",
					    cp->blacklists ? "BLACK" : "GREY",
					    cp->addr, cp->mail, cp->rcpt);
				if (greylist && cp->blacklists == NULL) {
					/* send this info to the greylister */
					getcaddr(cp);
					if (debug) /* XXX */
						fprintf(stderr,
					    "CO:%s\nHE:%s\nIP:%s\nFR:%s\nTO:%s\n",
					    cp->caddr, cp->helo, cp->addr,
					    cp->mail, cp->rcpt);
					fprintf(grey,
					    "CO:%s\nHE:%s\nIP:%s\nFR:%s\nTO:%s\n",
					    cp->caddr, cp->helo, cp->addr,
					    cp->mail, cp->rcpt);
					fflush(grey);
				}
			}
			break;
		}
		goto spam;
	case 6:
		/* sent 250 blah */
		cp->ip = cp->ibuf;
		cp->il = sizeof(cp->ibuf) - 1;
		cp->laststate = cp->state;
		cp->state = 5;
		cp->r = t;
		break;

	case 50:
	spam:
		if (match(cp->ibuf, "DATA")) {
			snprintf(cp->obuf, cp->osize,
			    "354 Enter spam, end with \".\" on a line by "
			    "itself\r\n");
			cp->state = 60;
			if (window && setsockopt(cp->fd, SOL_SOCKET, SO_RCVBUF,
			    &window, sizeof(window)) == -1) {
				syslog_r(LOG_DEBUG, &sdata,"setsockopt: %m");
				/* don't fail if this doesn't work. */
			}
			cp->ip = cp->ibuf;
			cp->il = sizeof(cp->ibuf) - 1;
			cp->op = cp->obuf;
			cp->ol = strlen(cp->op);
			cp->w = t + cp->stutter;
			if (greylist && cp->blacklists == NULL) {
				cp->laststate = cp->state;
				cp->state = 98;
				goto done;
			}
		} else {
			if (match(cp->ibuf, "NOOP")) 
				snprintf(cp->obuf, cp->osize,
				    "250 2.0.0 OK I did nothing\r\n");
			else
                        	snprintf(cp->obuf, cp->osize,
				    "500 5.5.1 Command unrecognized\r\n");
			cp->state = cp->laststate;
			cp->ip = cp->ibuf;
			cp->il = sizeof(cp->ibuf) - 1;
			cp->op = cp->obuf;
			cp->ol = strlen(cp->op);
			cp->w = t + cp->stutter;
		}
		break;
	case 60:
		/* sent 354 blah */
		cp->ip = cp->ibuf;
		cp->il = sizeof(cp->ibuf) - 1;
		cp->laststate = cp->state;
		cp->state = 70;
		cp->r = t;
		break;
	case 70: {
		char *p, *q;

		for (p = q = cp->ibuf; q <= cp->ip; ++q)
			if (*q == '\n' || q == cp->ip) {
				*q = 0;
				if (q > p && q[-1] == '\r')
					q[-1] = 0;
				if (!strcmp(p, ".") ||
				    (cp->data_body && ++cp->data_lines >= 10)) {
					cp->laststate = cp->state;
					cp->state = 98;
					goto done;
				}
				if (!cp->data_body && !*p)
					cp->data_body = 1;
				if (verbose && cp->data_body && *p)
					syslog_r(LOG_DEBUG, &sdata, "%s: "
					    "Body: %s", cp->addr, p);
				else if (verbose && (match(p, "FROM:") ||
				    match(p, "TO:") || match(p, "SUBJECT:")))
					syslog_r(LOG_INFO, &sdata, "%s: %s",
					    cp->addr, p);
				p = ++q;
			}
		cp->ip = cp->ibuf;
		cp->il = sizeof(cp->ibuf) - 1;
		cp->r = t;
		break;
	}
	case 98:
	done:
		doreply(cp);
		cp->op = cp->obuf;
		cp->ol = strlen(cp->op);
		cp->w = t + cp->stutter;
		cp->laststate = cp->state;
		cp->state = 99;
		break;
	case 99:
		closecon(cp);
		break;
	default:
		errx(1, "illegal state %d", cp->state);
		break;
	}
}