smrsh.c revision 38081
138032Speter/*
238032Speter * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
338032Speter * Copyright (c) 1993 Eric P. Allman.  All rights reserved.
438032Speter * Copyright (c) 1993
538032Speter *	The Regents of the University of California.  All rights reserved.
638032Speter *
738032Speter * By using this file, you agree to the terms and conditions set
838032Speter * forth in the LICENSE file which can be found at the top level of
938032Speter * the sendmail distribution.
1038032Speter *
1138032Speter */
1238032Speter
1338032Speter#ifndef lint
1438032Speterstatic char sccsid[] = "@(#)smrsh.c	8.11 (Berkeley) 5/19/98";
1538032Speter#endif /* not lint */
1638032Speter
1738032Speter/*
1838032Speter**  SMRSH -- sendmail restricted shell
1938032Speter**
2038032Speter**	This is a patch to get around the prog mailer bugs in most
2138032Speter**	versions of sendmail.
2238032Speter**
2338032Speter**	Use this in place of /bin/sh in the "prog" mailer definition
2438032Speter**	in your sendmail.cf file.  You then create CMDDIR (owned by
2538032Speter**	root, mode 755) and put links to any programs you want
2638032Speter**	available to prog mailers in that directory.  This should
2738032Speter**	include things like "vacation" and "procmail", but not "sed"
2838032Speter**	or "sh".
2938032Speter**
3038032Speter**	Leading pathnames are stripped from program names so that
3138032Speter**	existing .forward files that reference things like
3238081Speter**	"/usr/bin/vacation" will continue to work.
3338032Speter**
3438032Speter**	The following characters are completely illegal:
3538032Speter**		<  >  |  ^  ;  &  $  `  (  ) \n \r
3638032Speter**	This is more restrictive than strictly necessary.
3738032Speter**
3838032Speter**	To use this, edit /etc/sendmail.cf, search for ^Mprog, and
3938081Speter**	change P=/bin/sh to P=/usr/libexec/smrsh, where this compiled
4038081Speter**	binary is installed /usr/libexec/smrsh.
4138032Speter**
4238032Speter**	This can be used on any version of sendmail.
4338032Speter**
4438032Speter**	In loving memory of RTM.  11/02/93.
4538032Speter*/
4638032Speter
4738032Speter#include <unistd.h>
4838032Speter#include <stdio.h>
4938032Speter#include <sys/file.h>
5038032Speter#include <string.h>
5138032Speter#include <ctype.h>
5238032Speter#ifdef EX_OK
5338032Speter# undef EX_OK
5438032Speter#endif
5538032Speter#include <sysexits.h>
5638032Speter#include <syslog.h>
5738032Speter#include <stdlib.h>
5838032Speter
5938032Speter/* directory in which all commands must reside */
6038032Speter#ifndef CMDDIR
6138081Speter# define CMDDIR		"/usr/libexec/sm.bin"
6238032Speter#endif
6338032Speter
6438032Speter/* characters disallowed in the shell "-c" argument */
6538032Speter#define SPECIALS	"<|>^();&`$\r\n"
6638032Speter
6738032Speter/* default search path */
6838032Speter#ifndef PATH
6938081Speter# define PATH		"/bin:/usr/bin"
7038032Speter#endif
7138032Speter
7238032Speterint
7338032Spetermain(argc, argv)
7438032Speter	int argc;
7538032Speter	char **argv;
7638032Speter{
7738032Speter	register char *p;
7838032Speter	register char *q;
7938032Speter	register char *cmd;
8038032Speter	int i;
8138032Speter	char *newenv[2];
8238032Speter	char cmdbuf[1000];
8338032Speter	char pathbuf[1000];
8438032Speter
8538032Speter#ifndef LOG_MAIL
8638032Speter	openlog("smrsh", 0);
8738032Speter#else
8838032Speter	openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL);
8938032Speter#endif
9038032Speter
9138032Speter	strcpy(pathbuf, "PATH=");
9238032Speter	strcat(pathbuf, PATH);
9338032Speter	newenv[0] = pathbuf;
9438032Speter	newenv[1] = NULL;
9538032Speter
9638032Speter	/*
9738032Speter	**  Do basic argv usage checking
9838032Speter	*/
9938032Speter
10038032Speter	if (argc != 3 || strcmp(argv[1], "-c") != 0)
10138032Speter	{
10238032Speter		fprintf(stderr, "Usage: %s -c command\n", argv[0]);
10338032Speter		syslog(LOG_ERR, "usage");
10438032Speter		exit(EX_USAGE);
10538032Speter	}
10638032Speter
10738032Speter	/*
10838032Speter	**  Disallow special shell syntax.  This is overly restrictive,
10938032Speter	**  but it should shut down all attacks.
11038032Speter	**  Be sure to include 8-bit versions, since many shells strip
11138032Speter	**  the address to 7 bits before checking.
11238032Speter	*/
11338032Speter
11438032Speter	strcpy(cmdbuf, SPECIALS);
11538032Speter	for (p = cmdbuf; *p != '\0'; p++)
11638032Speter		*p |= '\200';
11738032Speter	strcat(cmdbuf, SPECIALS);
11838032Speter	p = strpbrk(argv[2], cmdbuf);
11938032Speter	if (p != NULL)
12038032Speter	{
12138032Speter		fprintf(stderr, "%s: cannot use %c in command\n",
12238032Speter			argv[0], *p);
12338032Speter		syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s",
12438032Speter			getuid(), *p, argv[2]);
12538032Speter		exit(EX_UNAVAILABLE);
12638032Speter	}
12738032Speter
12838032Speter	/*
12938032Speter	**  Do a quick sanity check on command line length.
13038032Speter	*/
13138032Speter
13238032Speter	i = strlen(argv[2]);
13338032Speter	if (i > (sizeof cmdbuf - sizeof CMDDIR - 2))
13438032Speter	{
13538032Speter		fprintf(stderr, "%s: command too long: %s\n", argv[0], argv[2]);
13638032Speter		syslog(LOG_WARNING, "command too long: %.40s", argv[2]);
13738032Speter		exit(EX_UNAVAILABLE);
13838032Speter	}
13938032Speter
14038032Speter	/*
14138032Speter	**  Strip off a leading pathname on the command name.  For
14238032Speter	**  example, change /usr/ucb/vacation to vacation.
14338032Speter	*/
14438032Speter
14538032Speter	/* strip leading spaces */
14638032Speter	for (q = argv[2]; *q != '\0' && isascii(*q) && isspace(*q); )
14738032Speter		q++;
14838032Speter
14938032Speter	/* find the end of the command name */
15038032Speter	p = strpbrk(q, " \t");
15138032Speter	if (p == NULL)
15238032Speter		cmd = &q[strlen(q)];
15338032Speter	else
15438032Speter	{
15538032Speter		*p = '\0';
15638032Speter		cmd = p;
15738032Speter	}
15838032Speter
15938032Speter	/* search backwards for last / (allow for 0200 bit) */
16038032Speter	while (cmd > q)
16138032Speter	{
16238032Speter		if ((*--cmd & 0177) == '/')
16338032Speter		{
16438032Speter			cmd++;
16538032Speter			break;
16638032Speter		}
16738032Speter	}
16838032Speter
16938032Speter	/* cmd now points at final component of path name */
17038032Speter
17138032Speter	/*
17238032Speter	**  Check to see if the command name is legal.
17338032Speter	*/
17438032Speter
17538032Speter	(void) strcpy(cmdbuf, CMDDIR);
17638032Speter	(void) strcat(cmdbuf, "/");
17738032Speter	(void) strcat(cmdbuf, cmd);
17838032Speter#ifdef DEBUG
17938032Speter	printf("Trying %s\n", cmdbuf);
18038032Speter#endif
18138032Speter	if (access(cmdbuf, X_OK) < 0)
18238032Speter	{
18338032Speter		/* oops....  crack attack possiblity */
18438032Speter		fprintf(stderr, "%s: %s not available for sendmail programs\n",
18538032Speter			argv[0], cmd);
18638032Speter		if (p != NULL)
18738032Speter			*p = ' ';
18838032Speter		syslog(LOG_CRIT, "uid %d: attempt to use %s", getuid(), cmd);
18938032Speter		exit(EX_UNAVAILABLE);
19038032Speter	}
19138032Speter	if (p != NULL)
19238032Speter		*p = ' ';
19338032Speter
19438032Speter	/*
19538032Speter	**  Create the actual shell input.
19638032Speter	*/
19738032Speter
19838032Speter	strcpy(cmdbuf, CMDDIR);
19938032Speter	strcat(cmdbuf, "/");
20038032Speter	strcat(cmdbuf, cmd);
20138032Speter
20238032Speter	/*
20338032Speter	**  Now invoke the shell
20438032Speter	*/
20538032Speter
20638032Speter#ifdef DEBUG
20738032Speter	printf("%s\n", cmdbuf);
20838032Speter#endif
20938032Speter	execle("/bin/sh", "/bin/sh", "-c", cmdbuf, NULL, newenv);
21038032Speter	syslog(LOG_CRIT, "Cannot exec /bin/sh: %m");
21138032Speter	perror("/bin/sh");
21238032Speter	exit(EX_OSFILE);
21338032Speter}
214