smrsh.c revision 64565
138032Speter/*
264565Sgshapiro * Copyright (c) 1998-2000 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
1438032Speter#ifndef lint
1564565Sgshapirostatic char copyright[] =
1664565Sgshapiro"@(#) Copyright (c) 1998, 1999 Sendmail, Inc. and its suppliers.\n\
1764565Sgshapiro	All rights reserved.\n\
1864565Sgshapiro     Copyright (c) 1993 Eric P. Allman.  All rights reserved.\n\
1964565Sgshapiro     Copyright (c) 1993\n\
2064565Sgshapiro	The Regents of the University of California.  All rights reserved.\n";
2164565Sgshapiro#endif /* ! lint */
2238032Speter
2364565Sgshapiro#ifndef lint
2464565Sgshapirostatic char id[] = "@(#)$Id: smrsh.c,v 8.31.4.4 2000/05/25 21:44:29 gshapiro Exp $";
2564565Sgshapiro#endif /* ! lint */
2664565Sgshapiro
2764565Sgshapiro/* $FreeBSD: head/contrib/sendmail/smrsh/smrsh.c 64565 2000-08-12 22:19:16Z gshapiro $ */
2864565Sgshapiro
2938032Speter/*
3038032Speter**  SMRSH -- sendmail restricted shell
3138032Speter**
3238032Speter**	This is a patch to get around the prog mailer bugs in most
3338032Speter**	versions of sendmail.
3438032Speter**
3538032Speter**	Use this in place of /bin/sh in the "prog" mailer definition
3638032Speter**	in your sendmail.cf file.  You then create CMDDIR (owned by
3738032Speter**	root, mode 755) and put links to any programs you want
3838032Speter**	available to prog mailers in that directory.  This should
3938032Speter**	include things like "vacation" and "procmail", but not "sed"
4038032Speter**	or "sh".
4138032Speter**
4238032Speter**	Leading pathnames are stripped from program names so that
4338032Speter**	existing .forward files that reference things like
4438081Speter**	"/usr/bin/vacation" will continue to work.
4538032Speter**
4638032Speter**	The following characters are completely illegal:
4764565Sgshapiro**		<  >  ^  &  `  (  ) \n \r
4864565Sgshapiro**	The following characters are sometimes illegal:
4964565Sgshapiro**		|  &
5038032Speter**	This is more restrictive than strictly necessary.
5138032Speter**
5264565Sgshapiro**	To use this, add FEATURE(`smrsh') to your .mc file.
5338032Speter**
5438032Speter**	This can be used on any version of sendmail.
5538032Speter**
5638032Speter**	In loving memory of RTM.  11/02/93.
5738032Speter*/
5838032Speter
5938032Speter#include <unistd.h>
6038032Speter#include <stdio.h>
6138032Speter#include <sys/file.h>
6238032Speter#include <string.h>
6338032Speter#include <ctype.h>
6464565Sgshapiro#include <errno.h>
6538032Speter#ifdef EX_OK
6638032Speter# undef EX_OK
6764565Sgshapiro#endif /* EX_OK */
6838032Speter#include <sysexits.h>
6938032Speter#include <syslog.h>
7038032Speter#include <stdlib.h>
7138032Speter
7264565Sgshapiro#ifndef TRUE
7364565Sgshapiro# define TRUE	1
7464565Sgshapiro# define FALSE	0
7564565Sgshapiro#endif /* ! TRUE */
7664565Sgshapiro
7738032Speter/* directory in which all commands must reside */
7838032Speter#ifndef CMDDIR
7938081Speter# define CMDDIR		"/usr/libexec/sm.bin"
8064565Sgshapiro#endif /* ! CMDDIR */
8138032Speter
8238032Speter/* characters disallowed in the shell "-c" argument */
8338032Speter#define SPECIALS	"<|>^();&`$\r\n"
8438032Speter
8538032Speter/* default search path */
8638032Speter#ifndef PATH
8738081Speter# define PATH		"/bin:/usr/bin"
8864565Sgshapiro#endif /* ! PATH */
8938032Speter
9064565Sgshapiro#ifndef __P
9164565Sgshapiro# include "sendmail/cdefs.h"
9264565Sgshapiro#endif /* ! __P */
9364565Sgshapiro
9464565Sgshapiroextern size_t	strlcpy __P((char *, const char *, size_t));
9564565Sgshapiroextern size_t	strlcat __P((char *, const char *, size_t));
9664565Sgshapiro
9764565Sgshapirochar newcmdbuf[1000];
9864565Sgshapirochar *prg, *par;
9964565Sgshapiro
10064565Sgshapiro/*
10164565Sgshapiro**  ADDCMD -- add a string to newcmdbuf, check for overflow
10264565Sgshapiro**
10364565Sgshapiro**    Parameters:
10464565Sgshapiro**	s -- string to add
10564565Sgshapiro**	cmd -- it's a command: prepend CMDDIR/
10664565Sgshapiro**	len -- length of string to add
10764565Sgshapiro**
10864565Sgshapiro**    Side Effects:
10964565Sgshapiro**	changes newcmdbuf or exits with a failure.
11064565Sgshapiro**
11164565Sgshapiro*/
11264565Sgshapiro
11364565Sgshapirovoid
11464565Sgshapiroaddcmd(s, cmd, len)
11564565Sgshapiro	char *s;
11664565Sgshapiro	int cmd;
11764565Sgshapiro	int len;
11864565Sgshapiro{
11964565Sgshapiro	if (s == NULL || *s == '\0')
12064565Sgshapiro		return;
12164565Sgshapiro
12264565Sgshapiro	if (sizeof newcmdbuf - strlen(newcmdbuf) <=
12364565Sgshapiro	    len + (cmd ? (strlen(CMDDIR) + 1) : 0))
12464565Sgshapiro	{
12564565Sgshapiro		fprintf(stderr, "%s: command too long: %s\n", prg, par);
12664565Sgshapiro#ifndef DEBUG
12764565Sgshapiro		syslog(LOG_WARNING, "command too long: %.40s", par);
12864565Sgshapiro#endif /* ! DEBUG */
12964565Sgshapiro		exit(EX_UNAVAILABLE);
13064565Sgshapiro	}
13164565Sgshapiro	if (cmd)
13264565Sgshapiro	{
13364565Sgshapiro		(void) strlcat(newcmdbuf, CMDDIR, sizeof newcmdbuf);
13464565Sgshapiro		(void) strlcat(newcmdbuf, "/", sizeof newcmdbuf);
13564565Sgshapiro	}
13664565Sgshapiro	(void) strlcat(newcmdbuf, s, sizeof newcmdbuf);
13764565Sgshapiro}
13864565Sgshapiro
13938032Speterint
14038032Spetermain(argc, argv)
14138032Speter	int argc;
14238032Speter	char **argv;
14338032Speter{
14438032Speter	register char *p;
14538032Speter	register char *q;
14664565Sgshapiro	register char *r;
14738032Speter	register char *cmd;
14838032Speter	int i;
14964565Sgshapiro	int isexec;
15064565Sgshapiro	int save_errno;
15138032Speter	char *newenv[2];
15238032Speter	char cmdbuf[1000];
15338032Speter	char pathbuf[1000];
15464565Sgshapiro	char specialbuf[32];
15538032Speter
15664565Sgshapiro#ifndef DEBUG
15764565Sgshapiro# ifndef LOG_MAIL
15838032Speter	openlog("smrsh", 0);
15964565Sgshapiro# else /* ! LOG_MAIL */
16038032Speter	openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL);
16164565Sgshapiro# endif /* ! LOG_MAIL */
16264565Sgshapiro#endif /* ! DEBUG */
16338032Speter
16464565Sgshapiro	(void) strlcpy(pathbuf, "PATH=", sizeof pathbuf);
16564565Sgshapiro	(void) strlcat(pathbuf, PATH, sizeof pathbuf);
16638032Speter	newenv[0] = pathbuf;
16738032Speter	newenv[1] = NULL;
16838032Speter
16938032Speter	/*
17038032Speter	**  Do basic argv usage checking
17138032Speter	*/
17238032Speter
17364565Sgshapiro	prg = argv[0];
17464565Sgshapiro	par = argv[2];
17564565Sgshapiro
17638032Speter	if (argc != 3 || strcmp(argv[1], "-c") != 0)
17738032Speter	{
17864565Sgshapiro		fprintf(stderr, "Usage: %s -c command\n", prg);
17964565Sgshapiro#ifndef DEBUG
18038032Speter		syslog(LOG_ERR, "usage");
18164565Sgshapiro#endif /* ! DEBUG */
18238032Speter		exit(EX_USAGE);
18338032Speter	}
18438032Speter
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	}
19964565Sgshapiro	(void) strlcpy(specialbuf, SPECIALS, sizeof specialbuf);
20064565Sgshapiro	for (p = specialbuf; *p != '\0'; p++)
20164565Sgshapiro		*p |= '\200';
20264565Sgshapiro	(void) strlcat(specialbuf, SPECIALS, sizeof specialbuf);
20338032Speter
20438032Speter	/*
20538032Speter	**  Do a quick sanity check on command line length.
20638032Speter	*/
20738032Speter
20864565Sgshapiro	i = strlen(par);
20964565Sgshapiro	if (i > (sizeof newcmdbuf - sizeof CMDDIR - 2))
21038032Speter	{
21164565Sgshapiro		fprintf(stderr, "%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';
22064565Sgshapiro	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			{
23664565Sgshapiro				fprintf(stderr, "%s: missing command to exec\n",
23764565Sgshapiro					prg);
23864565Sgshapiro#ifndef DEBUG
23964565Sgshapiro				syslog(LOG_CRIT, "uid %d: missing command to exec", getuid());
24064565Sgshapiro#endif /* ! DEBUG */
24164565Sgshapiro				exit(EX_UNAVAILABLE);
24264565Sgshapiro			}
24338032Speter			break;
24438032Speter		}
24538032Speter
24664565Sgshapiro		/* find the end of the command name */
24764565Sgshapiro		p = strpbrk(q, " \t");
24864565Sgshapiro		if (p == NULL)
24964565Sgshapiro			cmd = &q[strlen(q)];
25064565Sgshapiro		else
25164565Sgshapiro		{
25264565Sgshapiro			*p = '\0';
25364565Sgshapiro			cmd = p;
25464565Sgshapiro		}
25564565Sgshapiro		/* search backwards for last / (allow for 0200 bit) */
25664565Sgshapiro		while (cmd > q)
25764565Sgshapiro		{
25864565Sgshapiro			if ((*--cmd & 0177) == '/')
25964565Sgshapiro			{
26064565Sgshapiro				cmd++;
26164565Sgshapiro				break;
26264565Sgshapiro			}
26364565Sgshapiro		}
26464565Sgshapiro		/* cmd now points at final component of path name */
26538032Speter
26664565Sgshapiro		/* allow a few shell builtins */
26764565Sgshapiro		if (strcmp(q, "exec") == 0 && p != NULL)
26864565Sgshapiro		{
26964565Sgshapiro			addcmd("exec ", FALSE, strlen("exec "));
27064565Sgshapiro			/* test _next_ arg */
27164565Sgshapiro			q = ++p;
27264565Sgshapiro			isexec = TRUE;
27364565Sgshapiro			continue;
27464565Sgshapiro		}
27564565Sgshapiro		else if (strcmp(q, "exit") == 0 || strcmp(q, "echo") == 0)
27664565Sgshapiro		{
27764565Sgshapiro			addcmd(cmd, FALSE, strlen(cmd));
27864565Sgshapiro			/* test following chars */
27964565Sgshapiro		}
28064565Sgshapiro		else
28164565Sgshapiro		{
28264565Sgshapiro			/*
28364565Sgshapiro			**  Check to see if the command name is legal.
28464565Sgshapiro			*/
28564565Sgshapiro			(void) strlcpy(cmdbuf, CMDDIR, sizeof cmdbuf);
28664565Sgshapiro			(void) strlcat(cmdbuf, "/", sizeof cmdbuf);
28764565Sgshapiro			(void) strlcat(cmdbuf, cmd, sizeof cmdbuf);
28864565Sgshapiro#ifdef DEBUG
28964565Sgshapiro			printf("Trying %s\n", cmdbuf);
29064565Sgshapiro#endif /* DEBUG */
29164565Sgshapiro			if (access(cmdbuf, X_OK) < 0)
29264565Sgshapiro			{
29364565Sgshapiro				/* oops....  crack attack possiblity */
29464565Sgshapiro				fprintf(stderr,
29564565Sgshapiro					"%s: %s not available for sendmail programs\n",
29664565Sgshapiro					prg, cmd);
29764565Sgshapiro				if (p != NULL)
29864565Sgshapiro					*p = ' ';
29964565Sgshapiro#ifndef DEBUG
30064565Sgshapiro				syslog(LOG_CRIT, "uid %d: attempt to use %s",
30164565Sgshapiro				       getuid(), cmd);
30264565Sgshapiro#endif /* ! DEBUG */
30364565Sgshapiro				exit(EX_UNAVAILABLE);
30464565Sgshapiro			}
30538032Speter
30664565Sgshapiro			/*
30764565Sgshapiro			**  Create the actual shell input.
30864565Sgshapiro			*/
30964565Sgshapiro
31064565Sgshapiro			addcmd(cmd, TRUE, strlen(cmd));
31164565Sgshapiro		}
31264565Sgshapiro		isexec = FALSE;
31364565Sgshapiro
31438032Speter		if (p != NULL)
31538032Speter			*p = ' ';
31664565Sgshapiro		else
31764565Sgshapiro			break;
31864565Sgshapiro
31964565Sgshapiro		r = strpbrk(p, specialbuf);
32064565Sgshapiro		if (r == NULL) {
32164565Sgshapiro			addcmd(p, FALSE, strlen(p));
32264565Sgshapiro			break;
32364565Sgshapiro		}
32464565Sgshapiro#if ALLOWSEMI
32564565Sgshapiro		if (*r == ';') {
32664565Sgshapiro			addcmd(p, FALSE,  r - p + 1);
32764565Sgshapiro			q = r + 1;
32864565Sgshapiro			continue;
32964565Sgshapiro		}
33064565Sgshapiro#endif /* ALLOWSEMI */
33164565Sgshapiro		if ((*r == '&' && *(r + 1) == '&') ||
33264565Sgshapiro		    (*r == '|' && *(r + 1) == '|'))
33364565Sgshapiro		{
33464565Sgshapiro			addcmd(p, FALSE,  r - p + 2);
33564565Sgshapiro			q = r + 2;
33664565Sgshapiro			continue;
33764565Sgshapiro		}
33864565Sgshapiro
33964565Sgshapiro		fprintf(stderr, "%s: cannot use %c in command\n", prg, *r);
34064565Sgshapiro#ifndef DEBUG
34164565Sgshapiro		syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s",
34264565Sgshapiro			getuid(), *r, par);
34364565Sgshapiro#endif /* ! DEBUG */
34438032Speter		exit(EX_UNAVAILABLE);
34564565Sgshapiro	}		/* end of while *q */
34664565Sgshapiro	if (isexec)
34764565Sgshapiro	{
34864565Sgshapiro		fprintf(stderr, "%s: missing command to exec\n", prg);
34964565Sgshapiro#ifndef DEBUG
35064565Sgshapiro		syslog(LOG_CRIT, "uid %d: missing command to exec", getuid());
35164565Sgshapiro#endif /* ! DEBUG */
35264565Sgshapiro		exit(EX_UNAVAILABLE);
35338032Speter	}
35464565Sgshapiro	/* make sure we created something */
35564565Sgshapiro	if (newcmdbuf[0] == '\0')
35664565Sgshapiro	{
35764565Sgshapiro		fprintf(stderr, "Usage: %s -c command\n", prg);
35864565Sgshapiro#ifndef DEBUG
35964565Sgshapiro		syslog(LOG_ERR, "usage");
36064565Sgshapiro#endif /* ! DEBUG */
36164565Sgshapiro		exit(EX_USAGE);
36264565Sgshapiro	}
36338032Speter
36438032Speter	/*
36538032Speter	**  Now invoke the shell
36638032Speter	*/
36738032Speter
36838032Speter#ifdef DEBUG
36964565Sgshapiro	printf("%s\n", newcmdbuf);
37064565Sgshapiro#endif /* DEBUG */
37164565Sgshapiro	(void) execle("/bin/sh", "/bin/sh", "-c", newcmdbuf, NULL, newenv);
37264565Sgshapiro	save_errno = errno;
37364565Sgshapiro#ifndef DEBUG
37438032Speter	syslog(LOG_CRIT, "Cannot exec /bin/sh: %m");
37564565Sgshapiro#endif /* ! DEBUG */
37664565Sgshapiro	errno = save_errno;
37738032Speter	perror("/bin/sh");
37838032Speter	exit(EX_OSFILE);
37964565Sgshapiro	/* NOTREACHED */
38064565Sgshapiro	return EX_OSFILE;
38138032Speter}
382