smrsh.c revision 90795
138032Speter/*
273191Sgshapiro * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
364565Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1993 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1490795Sgshapiro#include <sm/gen.h>
1590795Sgshapiro
1690795SgshapiroSM_IDSTR(copyright,
1773191Sgshapiro"@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\
1864565Sgshapiro	All rights reserved.\n\
1964565Sgshapiro     Copyright (c) 1993 Eric P. Allman.  All rights reserved.\n\
2064565Sgshapiro     Copyright (c) 1993\n\
2190795Sgshapiro	The Regents of the University of California.  All rights reserved.\n")
2238032Speter
2390795SgshapiroSM_IDSTR(id, "@(#)$Id: smrsh.c,v 8.55 2001/09/11 04:05:22 gshapiro Exp $")
2464565Sgshapiro
2564565Sgshapiro/* $FreeBSD: head/contrib/sendmail/smrsh/smrsh.c 90795 2002-02-17 21:58:34Z gshapiro $ */
2664565Sgshapiro
2738032Speter/*
2838032Speter**  SMRSH -- sendmail restricted shell
2938032Speter**
3038032Speter**	This is a patch to get around the prog mailer bugs in most
3138032Speter**	versions of sendmail.
3238032Speter**
3338032Speter**	Use this in place of /bin/sh in the "prog" mailer definition
3438032Speter**	in your sendmail.cf file.  You then create CMDDIR (owned by
3538032Speter**	root, mode 755) and put links to any programs you want
3638032Speter**	available to prog mailers in that directory.  This should
3738032Speter**	include things like "vacation" and "procmail", but not "sed"
3838032Speter**	or "sh".
3938032Speter**
4038032Speter**	Leading pathnames are stripped from program names so that
4138032Speter**	existing .forward files that reference things like
4238081Speter**	"/usr/bin/vacation" will continue to work.
4338032Speter**
4438032Speter**	The following characters are completely illegal:
4564565Sgshapiro**		<  >  ^  &  `  (  ) \n \r
4664565Sgshapiro**	The following characters are sometimes illegal:
4764565Sgshapiro**		|  &
4838032Speter**	This is more restrictive than strictly necessary.
4938032Speter**
5064565Sgshapiro**	To use this, add FEATURE(`smrsh') to your .mc file.
5138032Speter**
5238032Speter**	This can be used on any version of sendmail.
5338032Speter**
5438032Speter**	In loving memory of RTM.  11/02/93.
5538032Speter*/
5638032Speter
5738032Speter#include <unistd.h>
5890795Sgshapiro#include <sm/io.h>
5990795Sgshapiro#include <sm/string.h>
6038032Speter#include <sys/file.h>
6138032Speter#include <string.h>
6238032Speter#include <ctype.h>
6364565Sgshapiro#include <errno.h>
6438032Speter#ifdef EX_OK
6538032Speter# undef EX_OK
6664565Sgshapiro#endif /* EX_OK */
6738032Speter#include <sysexits.h>
6838032Speter#include <syslog.h>
6938032Speter#include <stdlib.h>
7038032Speter
7190795Sgshapiro#include <sm/conf.h>
7290795Sgshapiro#include <sm/errstring.h>
7364565Sgshapiro
7438032Speter/* directory in which all commands must reside */
7538032Speter#ifndef CMDDIR
7690795Sgshapiro# ifdef SMRSH_CMDDIR
7790795Sgshapiro#  define CMDDIR	SMRSH_CMDDIR
7890795Sgshapiro# else /* SMRSH_CMDDIR */
7990795Sgshapiro#  define CMDDIR	"/usr/adm/sm.bin"
8090795Sgshapiro# endif /* SMRSH_CMDDIR */
8164565Sgshapiro#endif /* ! CMDDIR */
8238032Speter
8338032Speter/* characters disallowed in the shell "-c" argument */
8438032Speter#define SPECIALS	"<|>^();&`$\r\n"
8538032Speter
8638032Speter/* default search path */
8738032Speter#ifndef PATH
8890795Sgshapiro# ifdef SMRSH_PATH
8990795Sgshapiro#  define PATH		SMRSH_PATH
9090795Sgshapiro# else /* SMRSH_PATH */
9190795Sgshapiro#  define PATH		"/bin:/usr/bin:/usr/ucb"
9290795Sgshapiro# endif /* SMRSH_PATH */
9364565Sgshapiro#endif /* ! PATH */
9438032Speter
9564565Sgshapirochar newcmdbuf[1000];
9664565Sgshapirochar *prg, *par;
9764565Sgshapiro
9864565Sgshapiro/*
9964565Sgshapiro**  ADDCMD -- add a string to newcmdbuf, check for overflow
10064565Sgshapiro**
10164565Sgshapiro**    Parameters:
10264565Sgshapiro**	s -- string to add
10364565Sgshapiro**	cmd -- it's a command: prepend CMDDIR/
10464565Sgshapiro**	len -- length of string to add
10564565Sgshapiro**
10664565Sgshapiro**    Side Effects:
10764565Sgshapiro**	changes newcmdbuf or exits with a failure.
10864565Sgshapiro**
10964565Sgshapiro*/
11064565Sgshapiro
11164565Sgshapirovoid
11264565Sgshapiroaddcmd(s, cmd, len)
11364565Sgshapiro	char *s;
11490795Sgshapiro	bool cmd;
11590795Sgshapiro	size_t len;
11664565Sgshapiro{
11764565Sgshapiro	if (s == NULL || *s == '\0')
11864565Sgshapiro		return;
11964565Sgshapiro
12064565Sgshapiro	if (sizeof newcmdbuf - strlen(newcmdbuf) <=
12164565Sgshapiro	    len + (cmd ? (strlen(CMDDIR) + 1) : 0))
12264565Sgshapiro	{
12390795Sgshapiro		(void)sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
12490795Sgshapiro				    "%s: command too long: %s\n", prg, par);
12564565Sgshapiro#ifndef DEBUG
12664565Sgshapiro		syslog(LOG_WARNING, "command too long: %.40s", par);
12764565Sgshapiro#endif /* ! DEBUG */
12864565Sgshapiro		exit(EX_UNAVAILABLE);
12964565Sgshapiro	}
13064565Sgshapiro	if (cmd)
13164565Sgshapiro	{
13290795Sgshapiro		(void) sm_strlcat(newcmdbuf, CMDDIR, sizeof newcmdbuf);
13390795Sgshapiro		(void) sm_strlcat(newcmdbuf, "/", sizeof newcmdbuf);
13464565Sgshapiro	}
13590795Sgshapiro	(void) sm_strlcat(newcmdbuf, s, sizeof newcmdbuf);
13664565Sgshapiro}
13764565Sgshapiro
13838032Speterint
13938032Spetermain(argc, argv)
14038032Speter	int argc;
14138032Speter	char **argv;
14238032Speter{
14338032Speter	register char *p;
14438032Speter	register char *q;
14564565Sgshapiro	register char *r;
14638032Speter	register char *cmd;
14764565Sgshapiro	int isexec;
14864565Sgshapiro	int save_errno;
14938032Speter	char *newenv[2];
15038032Speter	char cmdbuf[1000];
15138032Speter	char pathbuf[1000];
15264565Sgshapiro	char specialbuf[32];
15338032Speter
15464565Sgshapiro#ifndef DEBUG
15564565Sgshapiro# ifndef LOG_MAIL
15638032Speter	openlog("smrsh", 0);
15764565Sgshapiro# else /* ! LOG_MAIL */
15838032Speter	openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL);
15964565Sgshapiro# endif /* ! LOG_MAIL */
16064565Sgshapiro#endif /* ! DEBUG */
16138032Speter
16290795Sgshapiro	(void) sm_strlcpy(pathbuf, "PATH=", sizeof pathbuf);
16390795Sgshapiro	(void) sm_strlcat(pathbuf, PATH, sizeof pathbuf);
16438032Speter	newenv[0] = pathbuf;
16538032Speter	newenv[1] = NULL;
16638032Speter
16738032Speter	/*
16838032Speter	**  Do basic argv usage checking
16938032Speter	*/
17038032Speter
17164565Sgshapiro	prg = argv[0];
17264565Sgshapiro
17338032Speter	if (argc != 3 || strcmp(argv[1], "-c") != 0)
17438032Speter	{
17590795Sgshapiro		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
17690795Sgshapiro				     "Usage: %s -c command\n", prg);
17764565Sgshapiro#ifndef DEBUG
17838032Speter		syslog(LOG_ERR, "usage");
17964565Sgshapiro#endif /* ! DEBUG */
18038032Speter		exit(EX_USAGE);
18138032Speter	}
18238032Speter
18377352Sgshapiro	par = argv[2];
18477352Sgshapiro
18538032Speter	/*
18638032Speter	**  Disallow special shell syntax.  This is overly restrictive,
18738032Speter	**  but it should shut down all attacks.
18838032Speter	**  Be sure to include 8-bit versions, since many shells strip
18938032Speter	**  the address to 7 bits before checking.
19038032Speter	*/
19138032Speter
19264565Sgshapiro	if (strlen(SPECIALS) * 2 >= sizeof specialbuf)
19338032Speter	{
19464565Sgshapiro#ifndef DEBUG
19564565Sgshapiro		syslog(LOG_ERR, "too many specials: %.40s", SPECIALS);
19664565Sgshapiro#endif /* ! DEBUG */
19738032Speter		exit(EX_UNAVAILABLE);
19838032Speter	}
19990795Sgshapiro	(void) sm_strlcpy(specialbuf, SPECIALS, sizeof specialbuf);
20064565Sgshapiro	for (p = specialbuf; *p != '\0'; p++)
20164565Sgshapiro		*p |= '\200';
20290795Sgshapiro	(void) sm_strlcat(specialbuf, SPECIALS, sizeof specialbuf);
20338032Speter
20438032Speter	/*
20538032Speter	**  Do a quick sanity check on command line length.
20638032Speter	*/
20738032Speter
20890795Sgshapiro	if (strlen(par) > (sizeof newcmdbuf - sizeof CMDDIR - 2))
20938032Speter	{
21090795Sgshapiro		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
21190795Sgshapiro				     "%s: command too long: %s\n", prg, par);
21264565Sgshapiro#ifndef DEBUG
21364565Sgshapiro		syslog(LOG_WARNING, "command too long: %.40s", par);
21464565Sgshapiro#endif /* ! DEBUG */
21538032Speter		exit(EX_UNAVAILABLE);
21638032Speter	}
21738032Speter
21864565Sgshapiro	q = par;
21964565Sgshapiro	newcmdbuf[0] = '\0';
22090795Sgshapiro	isexec = false;
22138032Speter
22264565Sgshapiro	while (*q)
22338032Speter	{
22464565Sgshapiro		/*
22564565Sgshapiro		**  Strip off a leading pathname on the command name.  For
22664565Sgshapiro		**  example, change /usr/ucb/vacation to vacation.
22764565Sgshapiro		*/
22838032Speter
22964565Sgshapiro		/* strip leading spaces */
23064565Sgshapiro		while (*q != '\0' && isascii(*q) && isspace(*q))
23164565Sgshapiro			q++;
23264565Sgshapiro		if (*q == '\0')
23338032Speter		{
23464565Sgshapiro			if (isexec)
23564565Sgshapiro			{
23690795Sgshapiro				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
23790795Sgshapiro						     "%s: missing command to exec\n",
23890795Sgshapiro						     prg);
23964565Sgshapiro#ifndef DEBUG
24090795Sgshapiro				syslog(LOG_CRIT, "uid %d: missing command to exec", (int) getuid());
24164565Sgshapiro#endif /* ! DEBUG */
24264565Sgshapiro				exit(EX_UNAVAILABLE);
24364565Sgshapiro			}
24438032Speter			break;
24538032Speter		}
24638032Speter
24764565Sgshapiro		/* find the end of the command name */
24864565Sgshapiro		p = strpbrk(q, " \t");
24964565Sgshapiro		if (p == NULL)
25064565Sgshapiro			cmd = &q[strlen(q)];
25164565Sgshapiro		else
25264565Sgshapiro		{
25364565Sgshapiro			*p = '\0';
25464565Sgshapiro			cmd = p;
25564565Sgshapiro		}
25664565Sgshapiro		/* search backwards for last / (allow for 0200 bit) */
25764565Sgshapiro		while (cmd > q)
25864565Sgshapiro		{
25964565Sgshapiro			if ((*--cmd & 0177) == '/')
26064565Sgshapiro			{
26164565Sgshapiro				cmd++;
26264565Sgshapiro				break;
26364565Sgshapiro			}
26464565Sgshapiro		}
26564565Sgshapiro		/* cmd now points at final component of path name */
26638032Speter
26764565Sgshapiro		/* allow a few shell builtins */
26864565Sgshapiro		if (strcmp(q, "exec") == 0 && p != NULL)
26964565Sgshapiro		{
27090795Sgshapiro			addcmd("exec ", false, strlen("exec "));
27164565Sgshapiro			/* test _next_ arg */
27264565Sgshapiro			q = ++p;
27390795Sgshapiro			isexec = true;
27464565Sgshapiro			continue;
27564565Sgshapiro		}
27664565Sgshapiro		else if (strcmp(q, "exit") == 0 || strcmp(q, "echo") == 0)
27764565Sgshapiro		{
27890795Sgshapiro			addcmd(cmd, false, strlen(cmd));
27964565Sgshapiro			/* test following chars */
28064565Sgshapiro		}
28164565Sgshapiro		else
28264565Sgshapiro		{
28364565Sgshapiro			/*
28464565Sgshapiro			**  Check to see if the command name is legal.
28564565Sgshapiro			*/
28690795Sgshapiro			(void) sm_strlcpy(cmdbuf, CMDDIR, sizeof cmdbuf);
28790795Sgshapiro			(void) sm_strlcat(cmdbuf, "/", sizeof cmdbuf);
28890795Sgshapiro			(void) sm_strlcat(cmdbuf, cmd, sizeof cmdbuf);
28964565Sgshapiro#ifdef DEBUG
29090795Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
29190795Sgshapiro					     "Trying %s\n", cmdbuf);
29264565Sgshapiro#endif /* DEBUG */
29364565Sgshapiro			if (access(cmdbuf, X_OK) < 0)
29464565Sgshapiro			{
29564565Sgshapiro				/* oops....  crack attack possiblity */
29690795Sgshapiro				(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
29790795Sgshapiro						     "%s: %s not available for sendmail programs\n",
29890795Sgshapiro						      prg, cmd);
29964565Sgshapiro				if (p != NULL)
30064565Sgshapiro					*p = ' ';
30164565Sgshapiro#ifndef DEBUG
30264565Sgshapiro				syslog(LOG_CRIT, "uid %d: attempt to use %s",
30390795Sgshapiro				       (int) getuid(), cmd);
30464565Sgshapiro#endif /* ! DEBUG */
30564565Sgshapiro				exit(EX_UNAVAILABLE);
30664565Sgshapiro			}
30738032Speter
30864565Sgshapiro			/*
30964565Sgshapiro			**  Create the actual shell input.
31064565Sgshapiro			*/
31164565Sgshapiro
31290795Sgshapiro			addcmd(cmd, true, strlen(cmd));
31364565Sgshapiro		}
31490795Sgshapiro		isexec = false;
31564565Sgshapiro
31638032Speter		if (p != NULL)
31738032Speter			*p = ' ';
31864565Sgshapiro		else
31964565Sgshapiro			break;
32064565Sgshapiro
32164565Sgshapiro		r = strpbrk(p, specialbuf);
32290795Sgshapiro		if (r == NULL)
32390795Sgshapiro		{
32490795Sgshapiro			addcmd(p, false, strlen(p));
32564565Sgshapiro			break;
32664565Sgshapiro		}
32764565Sgshapiro#if ALLOWSEMI
32890795Sgshapiro		if (*r == ';')
32990795Sgshapiro		{
33090795Sgshapiro			addcmd(p, false,  r - p + 1);
33164565Sgshapiro			q = r + 1;
33264565Sgshapiro			continue;
33364565Sgshapiro		}
33464565Sgshapiro#endif /* ALLOWSEMI */
33564565Sgshapiro		if ((*r == '&' && *(r + 1) == '&') ||
33664565Sgshapiro		    (*r == '|' && *(r + 1) == '|'))
33764565Sgshapiro		{
33890795Sgshapiro			addcmd(p, false,  r - p + 2);
33964565Sgshapiro			q = r + 2;
34064565Sgshapiro			continue;
34164565Sgshapiro		}
34264565Sgshapiro
34390795Sgshapiro		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
34490795Sgshapiro				     "%s: cannot use %c in command\n", prg, *r);
34564565Sgshapiro#ifndef DEBUG
34664565Sgshapiro		syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s",
34790795Sgshapiro		       (int) getuid(), *r, par);
34864565Sgshapiro#endif /* ! DEBUG */
34938032Speter		exit(EX_UNAVAILABLE);
35064565Sgshapiro	}		/* end of while *q */
35164565Sgshapiro	if (isexec)
35264565Sgshapiro	{
35390795Sgshapiro		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
35490795Sgshapiro				     "%s: missing command to exec\n", prg);
35564565Sgshapiro#ifndef DEBUG
35690795Sgshapiro		syslog(LOG_CRIT, "uid %d: missing command to exec",
35790795Sgshapiro		       (int) getuid());
35864565Sgshapiro#endif /* ! DEBUG */
35964565Sgshapiro		exit(EX_UNAVAILABLE);
36038032Speter	}
36164565Sgshapiro	/* make sure we created something */
36264565Sgshapiro	if (newcmdbuf[0] == '\0')
36364565Sgshapiro	{
36490795Sgshapiro		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
36590795Sgshapiro				     "Usage: %s -c command\n", prg);
36664565Sgshapiro#ifndef DEBUG
36764565Sgshapiro		syslog(LOG_ERR, "usage");
36864565Sgshapiro#endif /* ! DEBUG */
36964565Sgshapiro		exit(EX_USAGE);
37064565Sgshapiro	}
37138032Speter
37238032Speter	/*
37338032Speter	**  Now invoke the shell
37438032Speter	*/
37538032Speter
37638032Speter#ifdef DEBUG
37790795Sgshapiro	(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", newcmdbuf);
37864565Sgshapiro#endif /* DEBUG */
37964565Sgshapiro	(void) execle("/bin/sh", "/bin/sh", "-c", newcmdbuf, NULL, newenv);
38064565Sgshapiro	save_errno = errno;
38164565Sgshapiro#ifndef DEBUG
38290795Sgshapiro	syslog(LOG_CRIT, "Cannot exec /bin/sh: %s", sm_errstring(errno));
38364565Sgshapiro#endif /* ! DEBUG */
38464565Sgshapiro	errno = save_errno;
38590795Sgshapiro	sm_perror("/bin/sh");
38638032Speter	exit(EX_OSFILE);
38764565Sgshapiro	/* NOTREACHED */
38864565Sgshapiro	return EX_OSFILE;
38938032Speter}
390