/* * 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 */