/*
* Copyright (c) 2006-2017 Hypertriton, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include
#ifdef HAVE_POLICY
#include
#include
#include
#include
#include
#include
#include "mailprocd.h"
#include "pathnames.h"
pid_t polProc = 0;
char *polSockPath; /* Policy control socket */
char *polOwner; /* Owner of policy socket */
static size_t polCurSize = 0; /* Size of current message */
void
POL_Init(CFG_File *cf)
{
CFG_GetStr(cf, "pol.socket-path", &polSockPath, _PATH_POL_SOCKET);
#ifdef __FreeBSD__
CFG_GetStr(cf, "pol.socket-owner", &polOwner, "postfix");
#else
CFG_GetStr(cf, "pol.socket-owner", &polOwner, "_postfix");
#endif
}
void
POL_Destroy(void)
{
unlink(polSockPath);
}
void
POL_ForkInstance(int sock)
{
pid_t pid;
struct passwd *user;
if ((pid = fork()) == -1) {
syslog(LOG_ERR, "fork(POL): %m");
return;
} else if (pid > 0) {
polProc = pid;
} else {
if ((user = getpwnam(polOwner)) == NULL) {
syslog(LOG_ERR, "Invalid pol.socket-owner setting: %s",
polOwner);
exit(1);
}
if (setegid(user->pw_gid) < 0 || setgid(user->pw_gid) < 0) {
syslog(LOG_ERR, "Cannot setgid %d", user->pw_gid);
exit(1);
}
if (seteuid(user->pw_uid) < 0 || setuid(user->pw_uid) < 0) {
syslog(LOG_ERR, "Cannot setuid %d", user->pw_uid);
exit(1);
}
dup2(sock, 0);
dup2(sock, 1);
MPD_EnterServerProc("mailprocd");
Setproctitle("policy");
setvbuf(stdout, NULL, _IONBF, 0);
POL_Session();
shutdown(fileno(stdin), SHUT_RDWR);
shutdown(fileno(stdout), SHUT_RDWR);
MPD_ExitServerProc(0);
}
}
static __inline__ int
POL_GetAttrib(POL_Request *req, const char *key, char **val)
{
int i;
for (i = 0; i < req->nattrs; i++) {
POL_Attrib *attr = &req->attrs[i];
if (strcmp(attr->key, key) == 0) {
*val = &attr->val[0];
return (1);
}
}
return (0);
}
static void
POL_RequestInit(POL_Request *req)
{
req->proto = POL_UNKNOWN_PROTO;
req->state = POL_UNKNOWN_STATE;
req->nattrs = 0;
}
/* Evaluate the given rule for the given request. */
static __inline__ int
POL_MatchRule(POL_Request *req, MPD_Rule *rule)
{
char *s;
if (strcmp(rule->cond, "any") == 0) {
return (1);
}
if (req->state == POL_END_OF_MESSAGE &&
strncmp(rule->cond, "size", 4) == 0 &&
rule->cond[4] != '\0' && rule->cond[5] != '\0') {
size_t rule_size = (size_t)strtoul(&rule->cond[6], NULL, 10);
return ((rule->cond[4] == '<' && polCurSize <= rule_size) ||
(rule->cond[4] == '>' && polCurSize >= rule_size));
}
if (strncmp(rule->cond, "sender=", 7) == 0 &&
POL_GetAttrib(req, "sender", &s)) {
return (strcmp(&rule->cond[7], s) == 0);
}
if (strncmp(rule->cond, "client_address=", 15) == 0 &&
POL_GetAttrib(req, "client_address", &s)) {
return (strcmp(&rule->cond[15], s) == 0);
}
if (strncmp(rule->cond, "helo_name=", 10) == 0 &&
POL_GetAttrib(req, "helo_name", &s)) {
return (strcmp(&rule->cond[10], s) == 0);
}
return (0);
}
#if 0
/* Run specialized checks for mailing lists. */
static void
POL_ProcessListRequest(ML_MailListReq *ml)
{
if (ml->owner != NULL && LOCAL_SuspendedUser(ml->owner)) {
Debug("Rejecting list request (owner suspended)");
printf("action=REJECT "
"Sorry, the account hosting this list "
"has been temporarily suspended\n\n");
fflush(stdout);
return;
}
if (ml->maxSize > 0 && polCurSize > ml->maxSize) {
Debug("Rejecting list request (size limit)");
printf("action=REJECT This list does not allow posts "
"exceeding %lu bytes\n\n", (Ulong)ml->maxSize);
fflush(stdout);
return;
}
printf("action=OK\n\n");
fflush(stdout);
}
#endif
/* Apply SMTP action rules. */
static void
POL_Process(POL_Request *req)
{
char *size_spec;
char *rcpt;
MPD_Ruleset *ruleset;
MPD_Rule *rule;
if (POL_GetAttrib(req, "size", &size_spec)) {
polCurSize = (size_t)strtoul(size_spec, NULL, 10);
} else {
polCurSize = 0;
}
if (!POL_GetAttrib(req, "recipient", &rcpt)) {
syslog(LOG_ERR, "Missing recipient attribute");
goto pass;
}
if ((ruleset = LOCAL_GetRulesetByRcpt(rcpt)) == NULL) {
#if 0
ML_MailListReq *ml;
/* TODO SYNC */
if ((ml = ML_GetMailListReq(rcpt)) != NULL) {
syslog(LOG_INFO, "Received list request %s (op=%s)",
ml->name, ml->op);
POL_ProcessListRequest(ml);
free(ml);
return;
}
#endif
goto pass; /* Pass by default */
}
TAILQ_FOREACH(rule, &ruleset->rules, rules) {
if (rule->insn[0] != '>' || /* SMTP context? */
rule->insn[1] == '\0') {
continue;
}
if ((!(rule->flags&RULE_NEGATE) && !POL_MatchRule(req, rule)) ||
( (rule->flags&RULE_NEGATE) && POL_MatchRule(req, rule)))
continue;
printf("action=%s\n\n", &rule->insn[1]);
fflush(stdout);
MPD_RulesetFree(ruleset);
return;
}
MPD_RulesetFree(ruleset);
pass:
puts("action=OK\n\n");
fflush(stdout);
}
void
POL_Session(void)
{
char *buf, *lbuf = NULL;
char *key, *val;
size_t len;
POL_Request req;
POL_RequestInit(&req);
while ((buf = Fgetln(stdin, &len)) != NULL) {
if (buf[len-1] == '\n') {
if (len > 1 && buf[len-2] == '\r') {
len--;
}
buf[len-1] = '\0';
} else {
if ((lbuf = malloc(len+1)) == NULL) {
syslog(LOG_ERR, "Out of memory");
return;
}
memcpy(lbuf, buf, len);
lbuf[len] = '\0';
buf = lbuf;
}
if (buf[0] == '\0') { break; }
key = strsep(&buf, "=");
val = strsep(&buf, "=");
if (key == NULL || key[0] == '\0' || val == NULL) {
continue;
}
#if 0
if (val[0] != '\0') { Debug("Attr: %s = [%s]", key, val); }
#endif
if (strcmp(key, "protocol_name") == 0) {
if (val[0] == 'S') { req.proto = POL_SMTP; }
else if (val[0] == 'L') { req.proto = POL_LMTP; }
} else if (strcmp(key, "protocol_state") == 0) {
if (val[0] == 'E') {
req.state = (val[1]=='N') ? POL_END_OF_MESSAGE :
POL_ETRN;
} else if (val[0] == 'R') {
req.state = POL_RCPT;
}
} else {
POL_Attrib *attr = &req.attrs[req.nattrs];
if (++req.nattrs >= POL_MAX_ATTRS) {
syslog(LOG_ERR, "Too many attributes given!");
return;
}
Strlcpy(attr->key, key, sizeof(attr->key));
attr->len = Strlcpy(attr->val, val, sizeof(attr->val))
+ 1;
}
if (lbuf != NULL) { free(lbuf); lbuf = NULL; }
}
POL_Process(&req);
return;
}
#endif /* HAVE_POLICY */