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