📄 main.c
字号:
/*XXX should probably add equivalents for all short macros here XXX*/
}
/*
** DISCONNECT -- remove our connection with any foreground process
**
** Parameters:
** droplev -- how "deeply" we should drop the line.
** 0 -- ignore signals, mail back errors, make sure
** output goes to stdout.
** 1 -- also, make stdout go to /dev/null.
** 2 -- also, disconnect from controlling terminal
** (only for daemon mode).
** e -- the current envelope.
**
** Returns:
** none
**
** Side Effects:
** Trys to insure that we are immune to vagaries of
** the controlling tty.
*/
void
disconnect(droplev, e)
int droplev;
register ENVELOPE *e;
{
int fd;
if (tTd(52, 1))
dprintf("disconnect: In %d Out %d, e=%lx\n",
fileno(InChannel), fileno(OutChannel), (u_long) e);
if (tTd(52, 100))
{
dprintf("don't\n");
return;
}
if (LogLevel > 93)
sm_syslog(LOG_DEBUG, e->e_id,
"disconnect level %d",
droplev);
/* be sure we don't get nasty signals */
(void) setsignal(SIGINT, SIG_IGN);
(void) setsignal(SIGQUIT, SIG_IGN);
/* we can't communicate with our caller, so.... */
HoldErrs = TRUE;
CurEnv->e_errormode = EM_MAIL;
Verbose = 0;
DisConnected = TRUE;
/* all input from /dev/null */
if (InChannel != stdin)
{
(void) fclose(InChannel);
InChannel = stdin;
}
if (freopen("/dev/null", "r", stdin) == NULL)
sm_syslog(LOG_ERR, e->e_id,
"disconnect: freopen(\"/dev/null\") failed: %s",
errstring(errno));
/* output to the transcript */
if (OutChannel != stdout)
{
(void) fclose(OutChannel);
OutChannel = stdout;
}
if (droplev > 0)
{
fd = open("/dev/null", O_WRONLY, 0666);
if (fd == -1)
sm_syslog(LOG_ERR, e->e_id,
"disconnect: open(\"/dev/null\") failed: %s",
errstring(errno));
(void) fflush(stdout);
(void) dup2(fd, STDOUT_FILENO);
(void) dup2(fd, STDERR_FILENO);
(void) close(fd);
}
/* drop our controlling TTY completely if possible */
if (droplev > 1)
{
(void) setsid();
errno = 0;
}
#if XDEBUG
checkfd012("disconnect");
#endif /* XDEBUG */
if (LogLevel > 71)
sm_syslog(LOG_DEBUG, e->e_id,
"in background, pid=%d",
getpid());
errno = 0;
}
static void
obsolete(argv)
char *argv[];
{
register char *ap;
register char *op;
while ((ap = *++argv) != NULL)
{
/* Return if "--" or not an option of any form. */
if (ap[0] != '-' || ap[1] == '-')
return;
/* skip over options that do have a value */
op = strchr(OPTIONS, ap[1]);
if (op != NULL && *++op == ':' && ap[2] == '\0' &&
ap[1] != 'd' &&
#if defined(sony_news)
ap[1] != 'E' && ap[1] != 'J' &&
#endif /* defined(sony_news) */
argv[1] != NULL && argv[1][0] != '-')
{
argv++;
continue;
}
/* If -C doesn't have an argument, use sendmail.cf. */
#define __DEFPATH "sendmail.cf"
if (ap[1] == 'C' && ap[2] == '\0')
{
*argv = xalloc(sizeof(__DEFPATH) + 2);
(void) snprintf(argv[0], sizeof(__DEFPATH) + 2, "-C%s",
__DEFPATH);
}
/* If -q doesn't have an argument, run it once. */
if (ap[1] == 'q' && ap[2] == '\0')
*argv = "-q0";
/* if -d doesn't have an argument, use 0-99.1 */
if (ap[1] == 'd' && ap[2] == '\0')
*argv = "-d0-99.1";
#if defined(sony_news)
/* if -E doesn't have an argument, use -EC */
if (ap[1] == 'E' && ap[2] == '\0')
*argv = "-EC";
/* if -J doesn't have an argument, use -JJ */
if (ap[1] == 'J' && ap[2] == '\0')
*argv = "-JJ";
#endif /* defined(sony_news) */
}
}
/*
** AUTH_WARNING -- specify authorization warning
**
** Parameters:
** e -- the current envelope.
** msg -- the text of the message.
** args -- arguments to the message.
**
** Returns:
** none.
*/
void
#ifdef __STDC__
auth_warning(register ENVELOPE *e, const char *msg, ...)
#else /* __STDC__ */
auth_warning(e, msg, va_alist)
register ENVELOPE *e;
const char *msg;
va_dcl
#endif /* __STDC__ */
{
char buf[MAXLINE];
VA_LOCAL_DECL
if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
{
register char *p;
static char hostbuf[48];
if (hostbuf[0] == '\0')
(void) myhostname(hostbuf, sizeof hostbuf);
(void) snprintf(buf, sizeof buf, "%s: ", hostbuf);
p = &buf[strlen(buf)];
VA_START(msg);
vsnprintf(p, SPACELEFT(buf, p), msg, ap);
VA_END;
addheader("X-Authentication-Warning", buf, 0, &e->e_header);
if (LogLevel > 3)
sm_syslog(LOG_INFO, e->e_id,
"Authentication-Warning: %.400s",
buf);
}
}
/*
** GETEXTENV -- get from external environment
**
** Parameters:
** envar -- the name of the variable to retrieve
**
** Returns:
** The value, if any.
*/
char *
getextenv(envar)
const char *envar;
{
char **envp;
int l;
l = strlen(envar);
for (envp = ExternalEnviron; *envp != NULL; envp++)
{
if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
return &(*envp)[l + 1];
}
return NULL;
}
/*
** SETUSERENV -- set an environment in the propogated environment
**
** Parameters:
** envar -- the name of the environment variable.
** value -- the value to which it should be set. If
** null, this is extracted from the incoming
** environment. If that is not set, the call
** to setuserenv is ignored.
**
** Returns:
** none.
*/
void
setuserenv(envar, value)
const char *envar;
const char *value;
{
int i, l;
char **evp = UserEnviron;
char *p;
if (value == NULL)
{
value = getextenv(envar);
if (value == NULL)
return;
}
i = strlen(envar) + 1;
l = strlen(value) + i + 1;
p = (char *) xalloc(l);
(void) snprintf(p, l, "%s=%s", envar, value);
while (*evp != NULL && strncmp(*evp, p, i) != 0)
evp++;
if (*evp != NULL)
{
*evp++ = p;
}
else if (evp < &UserEnviron[MAXUSERENVIRON])
{
*evp++ = p;
*evp = NULL;
}
/* make sure it is in our environment as well */
if (putenv(p) < 0)
syserr("setuserenv: putenv(%s) failed", p);
}
/*
** DUMPSTATE -- dump state
**
** For debugging.
*/
void
dumpstate(when)
char *when;
{
register char *j = macvalue('j', CurEnv);
int rs;
extern int NextMacroId;
sm_syslog(LOG_DEBUG, CurEnv->e_id,
"--- dumping state on %s: $j = %s ---",
when,
j == NULL ? "<NULL>" : j);
if (j != NULL)
{
if (!wordinclass(j, 'w'))
sm_syslog(LOG_DEBUG, CurEnv->e_id,
"*** $j not in $=w ***");
}
sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)\n",
NextMacroId, MAXMACROID);
sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
printopenfds(TRUE);
sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
mci_dump_all(TRUE);
rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
if (rs > 0)
{
int status;
register char **pvp;
char *pv[MAXATOM + 1];
pv[0] = NULL;
status = rewrite(pv, rs, 0, CurEnv);
sm_syslog(LOG_DEBUG, CurEnv->e_id,
"--- ruleset debug_dumpstate returns stat %d, pv: ---",
status);
for (pvp = pv; *pvp != NULL; pvp++)
sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
}
sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
}
/* ARGSUSED */
SIGFUNC_DECL
sigusr1(sig)
int sig;
{
dumpstate("user signal");
return SIGFUNC_RETURN;
}
/* ARGSUSED */
SIGFUNC_DECL
sighup(sig)
int sig;
{
int i;
extern int DtableSize;
clear_events();
(void) alarm(0);
if (SaveArgv[0][0] != '/')
{
if (LogLevel > 3)
sm_syslog(LOG_INFO, NOQID,
"could not restart: need full path");
finis(FALSE, EX_OSFILE);
}
if (LogLevel > 3)
sm_syslog(LOG_INFO, NOQID, "restarting %s %s",
sig == 0 ? "due to control command" : "on signal",
SaveArgv[0]);
/* Control socket restart? */
if (sig != 0)
(void) releasesignal(SIGHUP);
closecontrolsocket(TRUE);
if (drop_privileges(TRUE) != EX_OK)
{
if (LogLevel > 0)
sm_syslog(LOG_ALERT, NOQID,
"could not set[ug]id(%d, %d): %m",
RunAsUid, RunAsGid);
finis(FALSE, EX_OSERR);
}
/* arrange for all the files to be closed */
for (i = 3; i < DtableSize; i++)
{
register int j;
if ((j = fcntl(i, F_GETFD, 0)) != -1)
(void) fcntl(i, F_SETFD, j | FD_CLOEXEC);
}
(void) execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
if (LogLevel > 0)
sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m",
SaveArgv[0]);
finis(FALSE, EX_OSFILE);
}
/*
** DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
**
** Parameters:
** to_real_uid -- if set, drop to the real uid instead
** of the RunAsUser.
**
** Returns:
** EX_OSERR if the setuid failed.
** EX_OK otherwise.
*/
int
drop_privileges(to_real_uid)
bool to_real_uid;
{
int rval = EX_OK;
GIDSET_T emptygidset[1];
if (tTd(47, 1))
dprintf("drop_privileges(%d): Real[UG]id=%d:%d, RunAs[UG]id=%d:%d\n",
(int)to_real_uid, (int)RealUid,
(int)RealGid, (int)RunAsUid, (int)RunAsGid);
if (to_real_uid)
{
RunAsUserName = RealUserName;
RunAsUid = RealUid;
RunAsGid = RealGid;
}
/* make sure no one can grab open descriptors for secret files */
endpwent();
/* reset group permissions; these can be set later */
emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
{
syserr("drop_privileges: setgroups(1, %d) failed",
(int)emptygidset[0]);
rval = EX_OSERR;
}
/* reset primary group and user id */
if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0)
{
syserr("drop_privileges: setgid(%d) failed", (int)RunAsGid);
rval = EX_OSERR;
}
if (to_real_uid || RunAsUid != 0)
{
uid_t euid = geteuid();
if (setuid(RunAsUid) < 0)
{
syserr("drop_privileges: setuid(%d) failed",
(int)RunAsUid);
rval = EX_OSERR;
}
else if (RunAsUid != 0 && setuid(0) == 0)
{
/*
** Believe it or not, the Linux capability model
** allows a non-root process to override setuid()
** on a process running as root and prevent that
** process from dropping privileges.
*/
syserr("drop_privileges: setuid(0) succeeded (when it should not)");
rval = EX_OSERR;
}
else if (RunAsUid != euid && setuid(euid) == 0)
{
/*
** Some operating systems will keep the saved-uid
** if a non-root effective-uid calls setuid(real-uid)
** making it possible to set it back again later.
*/
syserr("drop_privileges: Unable to drop non-root set-user-id privileges");
rval = EX_OSERR;
}
}
if (tTd(47, 5))
{
dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
(int)geteuid(), (int)getuid(),
(int)getegid(), (int)getgid());
dprintf("drop_privileges: RunAsUser = %d:%d\n",
(int)RunAsUid, (int)RunAsGid);
if (tTd(47, 10))
dprintf("drop_privileges: rval = %d\n", rval);
}
return rval;
}
/*
** FILL_FD -- make sure a file descriptor has been properly allocated
**
** Used to make sure that stdin/out/err are allocated on startup
**
** Parameters:
** fd -- the file descriptor to be filled.
** where -- a string used for logging. If NULL, this is
** being called on startup, and logging should
** not be done.
**
** Returns:
** none
*/
void
fill_fd(fd, where)
int fd;
char *where;
{
int i;
struct stat stbuf;
if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
return;
if (where != NULL)
syserr("fill_fd: %s: fd %d not open", where, fd);
else
MissingFds |= 1 << fd;
i = open("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY, 0666);
if (i < 0)
{
syserr("!fill_fd: %s: cannot open /dev/null",
where == NULL ? "startup" : where);
}
if (fd != i)
{
(void) dup2(i, fd);
(void) close(i);
}
}
/*
** TESTMODELINE -- process a test mode input line
**
** Parameters:
** line -- the input line.
** e -- the current environment.
** Syntax:
** # a comment
** .X process X as a configuration line
** =X dump a configuration item (such as mailers)
** $X dump a macro or class
** /X try an activity
** X normal process through rule set X
*/
static void
testmodeline(line, e)
char *line;
ENVELOPE *e;
{
register char *p;
char *q;
auto char *delimptr;
int mi
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -