/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mailprocd.h" #include "pathnames.h" #define ENVELOPE_BUF_MAX (164+(ADDRESS_MAX*3)) static char myname[MAXHOSTNAMELEN]; /* Hostname */ static char envelope[ENVELOPE_BUF_MAX]; /* Envelope prepend buffer */ static size_t envelopeLen = 0; TBL *Tmailbox, *Tuid, *Tgid, *Trules, *Tlists; /* Tables */ static char *pathMailbox, *pathUID, *pathGID, *pathRules, *pathLists; static char *suspShell, *sendmailPath, *relayAddr, *relayPort; static char *ruleHeader, *loopHeader; static int emulQmail, suspBounce; int LOCAL_Init(CFG_File *cf) { FILE *f; char *ln; size_t len; char *key, *val, *c; int using_flock = 0; if (gethostname(myname, sizeof(myname)) == -1) Strlcpy(myname, "unknown", sizeof(myname)); CFG_GetInt(cf, "local.emul-qmail", &emulQmail, 1); CFG_GetStr(cf, "local.susp-shell", &suspShell, _PATH_SUSPENDED_SH); CFG_GetInt(cf, "local.susp-bounce", &suspBounce, 1); CFG_GetStr(cf, "local.sendmail-path", &sendmailPath, _PATH_SENDMAIL); CFG_GetStr(cf, "local.relay-addr", &relayAddr, "127.0.0.1"); CFG_GetStr(cf, "local.relay-port", &relayPort, "425"); CFG_GetStr(cf, "local.rule-header", &ruleHeader, "X-Csoft-Rule: "); CFG_GetStr(cf, "local.loop-header", &loopHeader, "X-Csoft-Pipe: "); CFG_GetStr(cf, "mailbox-db", &pathMailbox, _PATH_DB_MAILBOX); CFG_GetStr(cf, "uid-db", &pathUID, _PATH_DB_UID); CFG_GetStr(cf, "gid-db", &pathGID, _PATH_DB_GID); CFG_GetStr(cf, "rules-db", &pathRules, _PATH_DB_RULES); CFG_GetStr(cf, "lists-db", &pathLists, _PATH_DB_LISTS); /* * Make damn sure that Postfix is using flock() locking for * delivering to mailboxes. */ if ((f = fopen(_PATH_POSTFIX_MAIN_CF, "r")) != NULL) { while ((ln = Fgetln(f, &len)) != NULL) { if (ln[0] == '#' || ln[0] == '\n' || ln[len-1] != '\n') { continue; } ln[len-1] = '\0'; if ((key = strsep(&ln, "=")) == NULL || (val = strsep(&ln, "=")) == NULL) { continue; } for (; isspace(*key); key++) ;; for (; isspace(*val); val++) ;; for (c = &key[0]; *c != '\0' && !isspace(*c); c++) ;; *c = '\0'; for (c = &val[0]; *c != '\0' && !isspace(*c); c++) ;; *c = '\0'; if (strcmp(key, "virtual_mailbox_lock") == 0) { if (strcmp(val, "flock") == 0) using_flock = 1; } } if (!using_flock) { MPD_SetError("Please set $virtual_mailbox_lock = " "flock in %s", _PATH_POSTFIX_MAIN_CF); return (-1); } fclose(f); } return LOCAL_OpenDatabases(); } void LOCAL_Destroy(void) { LOCAL_CloseDatabases(); } int LOCAL_OpenDatabases(void) { if ((Tmailbox = TBL_Load(pathMailbox)) == NULL|| (Tuid = TBL_Load(pathUID)) == NULL || (Tgid = TBL_Load(pathGID)) == NULL || (Trules = TBL_Load(pathRules)) == NULL || (Tlists = TBL_Load(pathLists)) == NULL) { return (-1); } return (0); } void LOCAL_CloseDatabases(void) { TBL_Destroy(Tmailbox); TBL_Destroy(Tuid); TBL_Destroy(Tgid); TBL_Destroy(Trules); TBL_Destroy(Tlists); } static int LOCAL_DatabaseUpdate(TBL **pTbl, const char *path) { TBL *nTbl; #if 0 struct stat sb; if (fstat((*pTbl)->fd, &sb) == -1) { syslog(LOG_ERR, "fstat(%s): %s", path, strerror(errno)); return (-1); } #endif fprintf(mpdLog, "Reloading database: %s\n", path); if ((nTbl = TBL_Load(path)) == NULL) { fprintf(mpdLog, "%s: Reload failed: %s\n", path, MPD_GetError()); syslog(LOG_ERR, "%s: Reload failed: %s", path, MPD_GetError()); return (-1); } TBL_Destroy(*pTbl); *pTbl = nTbl; return (0); } void LOCAL_ReloadDatabases(void) { LOCAL_DatabaseUpdate(&Tmailbox, pathMailbox); LOCAL_DatabaseUpdate(&Tuid, pathUID); LOCAL_DatabaseUpdate(&Tgid, pathGID); LOCAL_DatabaseUpdate(&Trules, pathRules); LOCAL_DatabaseUpdate(&Tlists, pathLists); } static __inline__ int LOCAL_DropEffectivePrivs(uid_t uid, gid_t gid) { sigset_t allsigs; sigfillset(&allsigs); sigprocmask(SIG_BLOCK, &allsigs, NULL); if (setegid(gid) < 0) { MPD_SetErrorS("setegid failed!"); goto fail; } if (seteuid(uid) < 0) { MPD_SetErrorS("seteuid failed!"); setegid(getgid()); goto fail; } sigprocmask(SIG_UNBLOCK, &allsigs, NULL); return (0); fail: sigprocmask(SIG_UNBLOCK, &allsigs, NULL); return (-1); } static __inline__ void LOCAL_RegainEffectivePrivs(void) { sigset_t allsigs; sigfillset(&allsigs); sigprocmask(SIG_BLOCK, &allsigs, NULL); if (seteuid(0) < 0) { sigprocmask(SIG_UNBLOCK, &allsigs, NULL); syslog(LOG_CRIT, "could not regain euid"); abort(); } if (setegid(0) < 0) { sigprocmask(SIG_UNBLOCK, &allsigs, NULL); syslog(LOG_CRIT, "could not regain egid"); abort(); } sigprocmask(SIG_UNBLOCK, &allsigs, NULL); } /* Check if a given user is suspended. */ int LOCAL_SuspendedUser(struct passwd *pwd) { if (strcmp(pwd->pw_shell, suspShell) == 0) { MPD_SetErrorS("Account is temporarily suspended"); return (1); } return (0); } /* Look up the named symbolic ruleset. */ MPD_Ruleset * LOCAL_GetRulesetByName(char *name) { char *data; if ((data = TBL_Lookup(Trules, name)) == NULL) { MPD_SetError("No such ruleset: %s", name); return (NULL); } return (MPD_RulesetParse(name, data)); } /* * Return the ruleset matching the given recipient address (or -1). * Search Order: , . */ MPD_Ruleset * LOCAL_GetRulesetByRcpt(char *rcpt) { char *data, *dom; if ((data = TBL_Lookup(Trules, rcpt)) == NULL) { for (dom = &rcpt[0]; *dom != '@' && *dom != '\0'; dom++) ;; if (dom[0] == '@' && dom[1] != '\0') { if ((data = TBL_Lookup(Trules, &dom[0])) == NULL) return (NULL); } else { return (NULL); } } return (MPD_RulesetParse(rcpt, data)); } /* * Lookup the default UID and GID of the given recipient. * Search Order: , . */ int LOCAL_GetDefaultRecipientUID(char *rcpt, uid_t *uid, gid_t *gid) { char *key, *uid_data, *gid_data; char *dom, *ep; key = rcpt; if ((uid_data = TBL_Lookup(Tuid, key)) == NULL || (gid_data = TBL_Lookup(Tgid, key)) == NULL) { for (dom = &rcpt[0]; *dom != '@' && *dom != '\0'; dom++) ;; if (dom[0] == '@' && dom[1] != '\0') { key = dom; if ((uid_data = TBL_Lookup(Tuid, key)) == NULL || (gid_data = TBL_Lookup(Tgid, key)) == NULL) { MPD_SetError("No uid/gid for %s", key); return (-1); } } else { MPD_SetError("No uid/gid for %s", key); return (-1); } } *uid = (uid_t)strtoul(uid_data, &ep, 10); if (*uid < QMGR_UIDMIN || *ep != '\0') { syslog(LOG_ERR, "Bad uid entry (%s)", key); MPD_SetErrorS("Bad uid"); return (-1); } *gid = (gid_t)strtoul(gid_data, &ep, 10); if (*gid < QMGR_GIDMIN || *ep != '\0') { syslog(LOG_ERR, "Bad gid entry (%s)", key); MPD_SetErrorS("Bad gid"); return (-1); } return (0); } /* * Prepend the "Return-Path" and "X-Original-To" headers, along with optional * "From", "X-Csoft-Rule" and "X-Csoft-Pipe" headers. */ #define ENVELOPE_FROM 0x01 /* From