#include <sm/gen.h>
SM_IDSTR(copyright,
"@(#) Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.\n\
All rights reserved.\n\
Copyright (c) 1993 Eric P. Allman. All rights reserved.\n\
Copyright (c) 1993\n\
The Regents of the University of California. All rights reserved.\n")
SM_IDSTR(id, "@(#)$Id: smrsh.c,v 8.65 2004/08/06 18:54:22 ca Exp $")
#include <unistd.h>
#include <sm/io.h>
#include <sm/limits.h>
#include <sm/string.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#ifdef EX_OK
# undef EX_OK
#endif
#include <sysexits.h>
#include <syslog.h>
#include <stdlib.h>
#include <sm/conf.h>
#include <sm/errstring.h>
#ifndef CMDDIR
# ifdef SMRSH_CMDDIR
# define CMDDIR SMRSH_CMDDIR
# else
# define CMDDIR "/usr/adm/sm.bin"
# endif
#endif
#define SPECIALS "<|>^();&`$\r\n"
#ifndef PATH
# ifdef SMRSH_PATH
# define PATH SMRSH_PATH
# else
# define PATH "/bin:/usr/bin:/usr/ucb"
# endif
#endif
char newcmdbuf[1000];
char *prg, *par;
static void addcmd __P((char *, bool, size_t));
static void
addcmd(s, cmd, len)
char *s;
bool cmd;
size_t len;
{
if (s == NULL || *s == '\0')
return;
if (sizeof newcmdbuf - strlen(newcmdbuf) <=
len + 1 + (cmd ? (strlen(CMDDIR) + 1) : 0))
{
(void)sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
"%s: command too long: %s\n", prg, par);
#ifndef DEBUG
syslog(LOG_WARNING, "command too long: %.40s", par);
#endif
exit(EX_UNAVAILABLE);
}
if (cmd)
(void) sm_strlcat2(newcmdbuf, CMDDIR, "/", sizeof newcmdbuf);
(void) strncat(newcmdbuf, s, len);
}
int
main(argc, argv)
int argc;
char **argv;
{
register char *p;
register char *q;
register char *r;
register char *cmd;
int isexec;
int save_errno;
char *newenv[2];
char pathbuf[1000];
char specialbuf[32];
struct stat st;
#ifndef DEBUG
# ifndef LOG_MAIL
openlog("smrsh", 0);
# else
openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL);
# endif
#endif
(void) sm_strlcpyn(pathbuf, sizeof pathbuf, 2, "PATH=", PATH);
newenv[0] = pathbuf;
newenv[1] = NULL;
prg = argv[0];
if (argc != 3 || strcmp(argv[1], "-c") != 0)
{
(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
"Usage: %s -c command\n", prg);
#ifndef DEBUG
syslog(LOG_ERR, "usage");
#endif
exit(EX_USAGE);
}
par = argv[2];
if (strlen(SPECIALS) * 2 >= sizeof specialbuf)
{
#ifndef DEBUG
syslog(LOG_ERR, "too many specials: %.40s", SPECIALS);
#endif
exit(EX_UNAVAILABLE);
}
(void) sm_strlcpy(specialbuf, SPECIALS, sizeof specialbuf);
for (p = specialbuf; *p != '\0'; p++)
*p |= '\200';
(void) sm_strlcat(specialbuf, SPECIALS, sizeof specialbuf);
if (strlen(par) > (sizeof newcmdbuf - sizeof CMDDIR - 2))
{
(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
"%s: command too long: %s\n", prg, par);
#ifndef DEBUG
syslog(LOG_WARNING, "command too long: %.40s", par);
#endif
exit(EX_UNAVAILABLE);
}
q = par;
newcmdbuf[0] = '\0';
isexec = false;
while (*q != '\0')
{
while (*q != '\0' && isascii(*q) && isspace(*q))
q++;
if (*q == '\0')
{
if (isexec)
{
(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
"%s: missing command to exec\n",
prg);
#ifndef DEBUG
syslog(LOG_CRIT, "uid %d: missing command to exec", (int) getuid());
#endif
exit(EX_UNAVAILABLE);
}
break;
}
p = strpbrk(q, " \t");
if (p == NULL)
cmd = &q[strlen(q)];
else
{
*p = '\0';
cmd = p;
}
while (cmd > q)
{
if ((*--cmd & 0177) == '/')
{
cmd++;
break;
}
}
if (strcmp(q, "exec") == 0 && p != NULL)
{
addcmd("exec ", false, strlen("exec "));
q = ++p;
isexec = true;
continue;
}
else if (strcmp(q, "exit") == 0 || strcmp(q, "echo") == 0)
{
addcmd(cmd, false, strlen(cmd));
}
else
{
char cmdbuf[MAXPATHLEN];
if (sm_strlcpyn(cmdbuf, sizeof cmdbuf, 3, CMDDIR,
"/", cmd) >= sizeof cmdbuf)
{
(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
"%s: \"%s\" not available for sendmail programs (filename too long)\n",
prg, cmd);
if (p != NULL)
*p = ' ';
#ifndef DEBUG
syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (filename too long)",
(int) getuid(), cmd);
#endif
exit(EX_UNAVAILABLE);
}
#ifdef DEBUG
(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
"Trying %s\n", cmdbuf);
#endif
if (stat(cmdbuf, &st) < 0)
{
(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
"%s: \"%s\" not available for sendmail programs (stat failed)\n",
prg, cmd);
if (p != NULL)
*p = ' ';
#ifndef DEBUG
syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (stat failed)",
(int) getuid(), cmd);
#endif
exit(EX_UNAVAILABLE);
}
if (!S_ISREG(st.st_mode)
#ifdef S_ISLNK
&& !S_ISLNK(st.st_mode)
#endif
)
{
(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
"%s: \"%s\" not available for sendmail programs (not a file)\n",
prg, cmd);
if (p != NULL)
*p = ' ';
#ifndef DEBUG
syslog(LOG_CRIT, "uid %d: attempt to use \"%s\" (not a file)",
(int) getuid(), cmd);
#endif
exit(EX_UNAVAILABLE);
}
if (access(cmdbuf, X_OK) < 0)
{
(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
"%s: \"%s\" not available for sendmail programs\n",
prg, cmd);
if (p != NULL)
*p = ' ';
#ifndef DEBUG
syslog(LOG_CRIT, "uid %d: attempt to use \"%s\"",
(int) getuid(), cmd);
#endif
exit(EX_UNAVAILABLE);
}
addcmd(cmd, true, strlen(cmd));
}
isexec = false;
if (p != NULL)
*p = ' ';
else
break;
r = strpbrk(p, specialbuf);
if (r == NULL)
{
addcmd(p, false, strlen(p));
break;
}
#if ALLOWSEMI
if (*r == ';')
{
addcmd(p, false, r - p + 1);
q = r + 1;
continue;
}
#endif
if ((*r == '&' && *(r + 1) == '&') ||
(*r == '|' && *(r + 1) == '|'))
{
addcmd(p, false, r - p + 2);
q = r + 2;
continue;
}
(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
"%s: cannot use %c in command\n", prg, *r);
#ifndef DEBUG
syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s",
(int) getuid(), *r, par);
#endif
exit(EX_UNAVAILABLE);
}
if (isexec)
{
(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
"%s: missing command to exec\n", prg);
#ifndef DEBUG
syslog(LOG_CRIT, "uid %d: missing command to exec",
(int) getuid());
#endif
exit(EX_UNAVAILABLE);
}
if (newcmdbuf[0] == '\0')
{
(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
"Usage: %s -c command\n", prg);
#ifndef DEBUG
syslog(LOG_ERR, "usage");
#endif
exit(EX_USAGE);
}
#ifdef DEBUG
(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", newcmdbuf);
#endif
(void) execle("/bin/sh", "/bin/sh", "-c", newcmdbuf,
(char *)NULL, newenv);
save_errno = errno;
#ifndef DEBUG
syslog(LOG_CRIT, "Cannot exec /bin/sh: %s", sm_errstring(errno));
#endif
errno = save_errno;
sm_perror("/bin/sh");
exit(EX_OSFILE);
return EX_OSFILE;
}