smrsh.c revision 77352
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
1438032Speter#ifndef lint
1564565Sgshapirostatic char copyright[] =
1673191Sgshapiro"@(#) Copyright (c) 1998-2001 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
2477352Sgshapirostatic char id[] = "@(#)$Id: smrsh.c,v 8.31.4.9 2001/04/24 04:11:51 ca Exp $";
2564565Sgshapiro#endif /* ! lint */
2664565Sgshapiro
2764565Sgshapiro/* $FreeBSD: head/contrib/sendmail/smrsh/smrsh.c 77352 2001-05-28 17:10:35Z 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
7971348Sgshapiro# if defined(HPUX10) || defined(HPUX11) || SOLARIS >= 20800
8071348Sgshapiro#  define CMDDIR	"/var/adm/sm.bin"
8173191Sgshapiro# else /* HPUX10 || HPUX11 || SOLARIS >= 20800 */
8271348Sgshapiro#  define CMDDIR	"/usr/libexec/sm.bin"
8373191Sgshapiro# endif /* HPUX10 || HPUX11 || SOLARIS >= 20800 */
8464565Sgshapiro#endif /* ! CMDDIR */
8538032Speter
8638032Speter/* characters disallowed in the shell "-c" argument */
8738032Speter#define SPECIALS	"<|>^();&`$\r\n"
8838032Speter
8938032Speter/* default search path */
9038032Speter#ifndef PATH
9138081Speter# define PATH		"/bin:/usr/bin"
9264565Sgshapiro#endif /* ! PATH */
9338032Speter
9464565Sgshapiro#ifndef __P
9564565Sgshapiro# include "sendmail/cdefs.h"
9664565Sgshapiro#endif /* ! __P */
9764565Sgshapiro
9864565Sgshapiroextern size_t	strlcpy __P((char *, const char *, size_t));
9964565Sgshapiroextern size_t	strlcat __P((char *, const char *, size_t));
10064565Sgshapiro
10164565Sgshapirochar newcmdbuf[1000];
10264565Sgshapirochar *prg, *par;
10364565Sgshapiro
10464565Sgshapiro/*
10564565Sgshapiro**  ADDCMD -- add a string to newcmdbuf, check for overflow
10664565Sgshapiro**
10764565Sgshapiro**    Parameters:
10864565Sgshapiro**	s -- string to add
10964565Sgshapiro**	cmd -- it's a command: prepend CMDDIR/
11064565Sgshapiro**	len -- length of string to add
11164565Sgshapiro**
11264565Sgshapiro**    Side Effects:
11364565Sgshapiro**	changes newcmdbuf or exits with a failure.
11464565Sgshapiro**
11564565Sgshapiro*/
11664565Sgshapiro
11764565Sgshapirovoid
11864565Sgshapiroaddcmd(s, cmd, len)
11964565Sgshapiro	char *s;
12064565Sgshapiro	int cmd;
12164565Sgshapiro	int len;
12264565Sgshapiro{
12364565Sgshapiro	if (s == NULL || *s == '\0')
12464565Sgshapiro		return;
12564565Sgshapiro
12664565Sgshapiro	if (sizeof newcmdbuf - strlen(newcmdbuf) <=
12764565Sgshapiro	    len + (cmd ? (strlen(CMDDIR) + 1) : 0))
12864565Sgshapiro	{
12964565Sgshapiro		fprintf(stderr, "%s: command too long: %s\n", prg, par);
13064565Sgshapiro#ifndef DEBUG
13164565Sgshapiro		syslog(LOG_WARNING, "command too long: %.40s", par);
13264565Sgshapiro#endif /* ! DEBUG */
13364565Sgshapiro		exit(EX_UNAVAILABLE);
13464565Sgshapiro	}
13564565Sgshapiro	if (cmd)
13664565Sgshapiro	{
13764565Sgshapiro		(void) strlcat(newcmdbuf, CMDDIR, sizeof newcmdbuf);
13864565Sgshapiro		(void) strlcat(newcmdbuf, "/", sizeof newcmdbuf);
13964565Sgshapiro	}
14064565Sgshapiro	(void) strlcat(newcmdbuf, s, sizeof newcmdbuf);
14164565Sgshapiro}
14264565Sgshapiro
14338032Speterint
14438032Spetermain(argc, argv)
14538032Speter	int argc;
14638032Speter	char **argv;
14738032Speter{
14838032Speter	register char *p;
14938032Speter	register char *q;
15064565Sgshapiro	register char *r;
15138032Speter	register char *cmd;
15238032Speter	int i;
15364565Sgshapiro	int isexec;
15464565Sgshapiro	int save_errno;
15538032Speter	char *newenv[2];
15638032Speter	char cmdbuf[1000];
15738032Speter	char pathbuf[1000];
15864565Sgshapiro	char specialbuf[32];
15938032Speter
16064565Sgshapiro#ifndef DEBUG
16164565Sgshapiro# ifndef LOG_MAIL
16238032Speter	openlog("smrsh", 0);
16364565Sgshapiro# else /* ! LOG_MAIL */
16438032Speter	openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL);
16564565Sgshapiro# endif /* ! LOG_MAIL */
16664565Sgshapiro#endif /* ! DEBUG */
16738032Speter
16864565Sgshapiro	(void) strlcpy(pathbuf, "PATH=", sizeof pathbuf);
16964565Sgshapiro	(void) strlcat(pathbuf, PATH, sizeof pathbuf);
17038032Speter	newenv[0] = pathbuf;
17138032Speter	newenv[1] = NULL;
17238032Speter
17338032Speter	/*
17438032Speter	**  Do basic argv usage checking
17538032Speter	*/
17638032Speter
17764565Sgshapiro	prg = argv[0];
17864565Sgshapiro
17938032Speter	if (argc != 3 || strcmp(argv[1], "-c") != 0)
18038032Speter	{
18164565Sgshapiro		fprintf(stderr, "Usage: %s -c command\n", prg);
18264565Sgshapiro#ifndef DEBUG
18338032Speter		syslog(LOG_ERR, "usage");
18464565Sgshapiro#endif /* ! DEBUG */
18538032Speter		exit(EX_USAGE);
18638032Speter	}
18738032Speter
18877352Sgshapiro	par = argv[2];
18977352Sgshapiro
19038032Speter	/*
19138032Speter	**  Disallow special shell syntax.  This is overly restrictive,
19238032Speter	**  but it should shut down all attacks.
19338032Speter	**  Be sure to include 8-bit versions, since many shells strip
19438032Speter	**  the address to 7 bits before checking.
19538032Speter	*/
19638032Speter
19764565Sgshapiro	if (strlen(SPECIALS) * 2 >= sizeof specialbuf)
19838032Speter	{
19964565Sgshapiro#ifndef DEBUG
20064565Sgshapiro		syslog(LOG_ERR, "too many specials: %.40s", SPECIALS);
20164565Sgshapiro#endif /* ! DEBUG */
20238032Speter		exit(EX_UNAVAILABLE);
20338032Speter	}
20464565Sgshapiro	(void) strlcpy(specialbuf, SPECIALS, sizeof specialbuf);
20564565Sgshapiro	for (p = specialbuf; *p != '\0'; p++)
20664565Sgshapiro		*p |= '\200';
20764565Sgshapiro	(void) strlcat(specialbuf, SPECIALS, sizeof specialbuf);
20838032Speter
20938032Speter	/*
21038032Speter	**  Do a quick sanity check on command line length.
21138032Speter	*/
21238032Speter
21364565Sgshapiro	i = strlen(par);
21464565Sgshapiro	if (i > (sizeof newcmdbuf - sizeof CMDDIR - 2))
21538032Speter	{
21664565Sgshapiro		fprintf(stderr, "%s: command too long: %s\n", prg, par);
21764565Sgshapiro#ifndef DEBUG
21864565Sgshapiro		syslog(LOG_WARNING, "command too long: %.40s", par);
21964565Sgshapiro#endif /* ! DEBUG */
22038032Speter		exit(EX_UNAVAILABLE);
22138032Speter	}
22238032Speter
22364565Sgshapiro	q = par;
22464565Sgshapiro	newcmdbuf[0] = '\0';
22564565Sgshapiro	isexec = FALSE;
22638032Speter
22764565Sgshapiro	while (*q)
22838032Speter	{
22964565Sgshapiro		/*
23064565Sgshapiro		**  Strip off a leading pathname on the command name.  For
23164565Sgshapiro		**  example, change /usr/ucb/vacation to vacation.
23264565Sgshapiro		*/
23338032Speter
23464565Sgshapiro		/* strip leading spaces */
23564565Sgshapiro		while (*q != '\0' && isascii(*q) && isspace(*q))
23664565Sgshapiro			q++;
23764565Sgshapiro		if (*q == '\0')
23838032Speter		{
23964565Sgshapiro			if (isexec)
24064565Sgshapiro			{
24164565Sgshapiro				fprintf(stderr, "%s: missing command to exec\n",
24264565Sgshapiro					prg);
24364565Sgshapiro#ifndef DEBUG
24464565Sgshapiro				syslog(LOG_CRIT, "uid %d: missing command to exec", getuid());
24564565Sgshapiro#endif /* ! DEBUG */
24664565Sgshapiro				exit(EX_UNAVAILABLE);
24764565Sgshapiro			}
24838032Speter			break;
24938032Speter		}
25038032Speter
25164565Sgshapiro		/* find the end of the command name */
25264565Sgshapiro		p = strpbrk(q, " \t");
25364565Sgshapiro		if (p == NULL)
25464565Sgshapiro			cmd = &q[strlen(q)];
25564565Sgshapiro		else
25664565Sgshapiro		{
25764565Sgshapiro			*p = '\0';
25864565Sgshapiro			cmd = p;
25964565Sgshapiro		}
26064565Sgshapiro		/* search backwards for last / (allow for 0200 bit) */
26164565Sgshapiro		while (cmd > q)
26264565Sgshapiro		{
26364565Sgshapiro			if ((*--cmd & 0177) == '/')
26464565Sgshapiro			{
26564565Sgshapiro				cmd++;
26664565Sgshapiro				break;
26764565Sgshapiro			}
26864565Sgshapiro		}
26964565Sgshapiro		/* cmd now points at final component of path name */
27038032Speter
27164565Sgshapiro		/* allow a few shell builtins */
27264565Sgshapiro		if (strcmp(q, "exec") == 0 && p != NULL)
27364565Sgshapiro		{
27464565Sgshapiro			addcmd("exec ", FALSE, strlen("exec "));
27564565Sgshapiro			/* test _next_ arg */
27664565Sgshapiro			q = ++p;
27764565Sgshapiro			isexec = TRUE;
27864565Sgshapiro			continue;
27964565Sgshapiro		}
28064565Sgshapiro		else if (strcmp(q, "exit") == 0 || strcmp(q, "echo") == 0)
28164565Sgshapiro		{
28264565Sgshapiro			addcmd(cmd, FALSE, strlen(cmd));
28364565Sgshapiro			/* test following chars */
28464565Sgshapiro		}
28564565Sgshapiro		else
28664565Sgshapiro		{
28764565Sgshapiro			/*
28864565Sgshapiro			**  Check to see if the command name is legal.
28964565Sgshapiro			*/
29064565Sgshapiro			(void) strlcpy(cmdbuf, CMDDIR, sizeof cmdbuf);
29164565Sgshapiro			(void) strlcat(cmdbuf, "/", sizeof cmdbuf);
29264565Sgshapiro			(void) strlcat(cmdbuf, cmd, sizeof cmdbuf);
29364565Sgshapiro#ifdef DEBUG
29464565Sgshapiro			printf("Trying %s\n", cmdbuf);
29564565Sgshapiro#endif /* DEBUG */
29664565Sgshapiro			if (access(cmdbuf, X_OK) < 0)
29764565Sgshapiro			{
29864565Sgshapiro				/* oops....  crack attack possiblity */
29964565Sgshapiro				fprintf(stderr,
30064565Sgshapiro					"%s: %s not available for sendmail programs\n",
30164565Sgshapiro					prg, cmd);
30264565Sgshapiro				if (p != NULL)
30364565Sgshapiro					*p = ' ';
30464565Sgshapiro#ifndef DEBUG
30564565Sgshapiro				syslog(LOG_CRIT, "uid %d: attempt to use %s",
30664565Sgshapiro				       getuid(), cmd);
30764565Sgshapiro#endif /* ! DEBUG */
30864565Sgshapiro				exit(EX_UNAVAILABLE);
30964565Sgshapiro			}
31038032Speter
31164565Sgshapiro			/*
31264565Sgshapiro			**  Create the actual shell input.
31364565Sgshapiro			*/
31464565Sgshapiro
31564565Sgshapiro			addcmd(cmd, TRUE, strlen(cmd));
31664565Sgshapiro		}
31764565Sgshapiro		isexec = FALSE;
31864565Sgshapiro
31938032Speter		if (p != NULL)
32038032Speter			*p = ' ';
32164565Sgshapiro		else
32264565Sgshapiro			break;
32364565Sgshapiro
32464565Sgshapiro		r = strpbrk(p, specialbuf);
32564565Sgshapiro		if (r == NULL) {
32664565Sgshapiro			addcmd(p, FALSE, strlen(p));
32764565Sgshapiro			break;
32864565Sgshapiro		}
32964565Sgshapiro#if ALLOWSEMI
33064565Sgshapiro		if (*r == ';') {
33164565Sgshapiro			addcmd(p, FALSE,  r - p + 1);
33264565Sgshapiro			q = r + 1;
33364565Sgshapiro			continue;
33464565Sgshapiro		}
33564565Sgshapiro#endif /* ALLOWSEMI */
33664565Sgshapiro		if ((*r == '&' && *(r + 1) == '&') ||
33764565Sgshapiro		    (*r == '|' && *(r + 1) == '|'))
33864565Sgshapiro		{
33964565Sgshapiro			addcmd(p, FALSE,  r - p + 2);
34064565Sgshapiro			q = r + 2;
34164565Sgshapiro			continue;
34264565Sgshapiro		}
34364565Sgshapiro
34464565Sgshapiro		fprintf(stderr, "%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",
34764565Sgshapiro			getuid(), *r, par);
34864565Sgshapiro#endif /* ! DEBUG */
34938032Speter		exit(EX_UNAVAILABLE);
35064565Sgshapiro	}		/* end of while *q */
35164565Sgshapiro	if (isexec)
35264565Sgshapiro	{
35364565Sgshapiro		fprintf(stderr, "%s: missing command to exec\n", prg);
35464565Sgshapiro#ifndef DEBUG
35564565Sgshapiro		syslog(LOG_CRIT, "uid %d: missing command to exec", getuid());
35664565Sgshapiro#endif /* ! DEBUG */
35764565Sgshapiro		exit(EX_UNAVAILABLE);
35838032Speter	}
35964565Sgshapiro	/* make sure we created something */
36064565Sgshapiro	if (newcmdbuf[0] == '\0')
36164565Sgshapiro	{
36264565Sgshapiro		fprintf(stderr, "Usage: %s -c command\n", prg);
36364565Sgshapiro#ifndef DEBUG
36464565Sgshapiro		syslog(LOG_ERR, "usage");
36564565Sgshapiro#endif /* ! DEBUG */
36664565Sgshapiro		exit(EX_USAGE);
36764565Sgshapiro	}
36838032Speter
36938032Speter	/*
37038032Speter	**  Now invoke the shell
37138032Speter	*/
37238032Speter
37338032Speter#ifdef DEBUG
37464565Sgshapiro	printf("%s\n", newcmdbuf);
37564565Sgshapiro#endif /* DEBUG */
37664565Sgshapiro	(void) execle("/bin/sh", "/bin/sh", "-c", newcmdbuf, NULL, newenv);
37764565Sgshapiro	save_errno = errno;
37864565Sgshapiro#ifndef DEBUG
37938032Speter	syslog(LOG_CRIT, "Cannot exec /bin/sh: %m");
38064565Sgshapiro#endif /* ! DEBUG */
38164565Sgshapiro	errno = save_errno;
38238032Speter	perror("/bin/sh");
38338032Speter	exit(EX_OSFILE);
38464565Sgshapiro	/* NOTREACHED */
38564565Sgshapiro	return EX_OSFILE;
38638032Speter}
387