xref: /freebsd/contrib/sendmail/smrsh/smrsh.c (revision 7f3dea244c40159a41ab22da77a434d7c5b5e85a)
1 /*
2  * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3  * Copyright (c) 1993 Eric P. Allman.  All rights reserved.
4  * Copyright (c) 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * By using this file, you agree to the terms and conditions set
8  * forth in the LICENSE file which can be found at the top level of
9  * the sendmail distribution.
10  *
11  */
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)smrsh.c	8.11 (Berkeley) 5/19/1998";
15 #endif /* not lint */
16 
17 /*
18 **  SMRSH -- sendmail restricted shell
19 **
20 **	This is a patch to get around the prog mailer bugs in most
21 **	versions of sendmail.
22 **
23 **	Use this in place of /bin/sh in the "prog" mailer definition
24 **	in your sendmail.cf file.  You then create CMDDIR (owned by
25 **	root, mode 755) and put links to any programs you want
26 **	available to prog mailers in that directory.  This should
27 **	include things like "vacation" and "procmail", but not "sed"
28 **	or "sh".
29 **
30 **	Leading pathnames are stripped from program names so that
31 **	existing .forward files that reference things like
32 **	"/usr/bin/vacation" will continue to work.
33 **
34 **	The following characters are completely illegal:
35 **		<  >  |  ^  ;  &  $  `  (  ) \n \r
36 **	This is more restrictive than strictly necessary.
37 **
38 **	To use this, edit /etc/sendmail.cf, search for ^Mprog, and
39 **	change P=/bin/sh to P=/usr/libexec/smrsh, where this compiled
40 **	binary is installed /usr/libexec/smrsh.
41 **
42 **	This can be used on any version of sendmail.
43 **
44 **	In loving memory of RTM.  11/02/93.
45 */
46 
47 #include <unistd.h>
48 #include <stdio.h>
49 #include <sys/file.h>
50 #include <string.h>
51 #include <ctype.h>
52 #ifdef EX_OK
53 # undef EX_OK
54 #endif
55 #include <sysexits.h>
56 #include <syslog.h>
57 #include <stdlib.h>
58 
59 /* directory in which all commands must reside */
60 #ifndef CMDDIR
61 # define CMDDIR		"/usr/libexec/sm.bin"
62 #endif
63 
64 /* characters disallowed in the shell "-c" argument */
65 #define SPECIALS	"<|>^();&`$\r\n"
66 
67 /* default search path */
68 #ifndef PATH
69 # define PATH		"/bin:/usr/bin"
70 #endif
71 
72 int
73 main(argc, argv)
74 	int argc;
75 	char **argv;
76 {
77 	register char *p;
78 	register char *q;
79 	register char *cmd;
80 	int i;
81 	char *newenv[2];
82 	char cmdbuf[1000];
83 	char pathbuf[1000];
84 
85 #ifndef LOG_MAIL
86 	openlog("smrsh", 0);
87 #else
88 	openlog("smrsh", LOG_ODELAY|LOG_CONS, LOG_MAIL);
89 #endif
90 
91 	strcpy(pathbuf, "PATH=");
92 	strcat(pathbuf, PATH);
93 	newenv[0] = pathbuf;
94 	newenv[1] = NULL;
95 
96 	/*
97 	**  Do basic argv usage checking
98 	*/
99 
100 	if (argc != 3 || strcmp(argv[1], "-c") != 0)
101 	{
102 		fprintf(stderr, "Usage: %s -c command\n", argv[0]);
103 		syslog(LOG_ERR, "usage");
104 		exit(EX_USAGE);
105 	}
106 
107 	/*
108 	**  Disallow special shell syntax.  This is overly restrictive,
109 	**  but it should shut down all attacks.
110 	**  Be sure to include 8-bit versions, since many shells strip
111 	**  the address to 7 bits before checking.
112 	*/
113 
114 	strcpy(cmdbuf, SPECIALS);
115 	for (p = cmdbuf; *p != '\0'; p++)
116 		*p |= '\200';
117 	strcat(cmdbuf, SPECIALS);
118 	p = strpbrk(argv[2], cmdbuf);
119 	if (p != NULL)
120 	{
121 		fprintf(stderr, "%s: cannot use %c in command\n",
122 			argv[0], *p);
123 		syslog(LOG_CRIT, "uid %d: attempt to use %c in command: %s",
124 			getuid(), *p, argv[2]);
125 		exit(EX_UNAVAILABLE);
126 	}
127 
128 	/*
129 	**  Do a quick sanity check on command line length.
130 	*/
131 
132 	i = strlen(argv[2]);
133 	if (i > (sizeof cmdbuf - sizeof CMDDIR - 2))
134 	{
135 		fprintf(stderr, "%s: command too long: %s\n", argv[0], argv[2]);
136 		syslog(LOG_WARNING, "command too long: %.40s", argv[2]);
137 		exit(EX_UNAVAILABLE);
138 	}
139 
140 	/*
141 	**  Strip off a leading pathname on the command name.  For
142 	**  example, change /usr/ucb/vacation to vacation.
143 	*/
144 
145 	/* strip leading spaces */
146 	for (q = argv[2]; *q != '\0' && isascii(*q) && isspace(*q); )
147 		q++;
148 
149 	/* find the end of the command name */
150 	p = strpbrk(q, " \t");
151 	if (p == NULL)
152 		cmd = &q[strlen(q)];
153 	else
154 	{
155 		*p = '\0';
156 		cmd = p;
157 	}
158 
159 	/* search backwards for last / (allow for 0200 bit) */
160 	while (cmd > q)
161 	{
162 		if ((*--cmd & 0177) == '/')
163 		{
164 			cmd++;
165 			break;
166 		}
167 	}
168 
169 	/* cmd now points at final component of path name */
170 
171 	/*
172 	**  Check to see if the command name is legal.
173 	*/
174 
175 	(void) strcpy(cmdbuf, CMDDIR);
176 	(void) strcat(cmdbuf, "/");
177 	(void) strcat(cmdbuf, cmd);
178 #ifdef DEBUG
179 	printf("Trying %s\n", cmdbuf);
180 #endif
181 	if (access(cmdbuf, X_OK) < 0)
182 	{
183 		/* oops....  crack attack possiblity */
184 		fprintf(stderr, "%s: %s not available for sendmail programs\n",
185 			argv[0], cmd);
186 		if (p != NULL)
187 			*p = ' ';
188 		syslog(LOG_CRIT, "uid %d: attempt to use %s", getuid(), cmd);
189 		exit(EX_UNAVAILABLE);
190 	}
191 	if (p != NULL)
192 		*p = ' ';
193 
194 	/*
195 	**  Create the actual shell input.
196 	*/
197 
198 	strcpy(cmdbuf, CMDDIR);
199 	strcat(cmdbuf, "/");
200 	strcat(cmdbuf, cmd);
201 
202 	/*
203 	**  Now invoke the shell
204 	*/
205 
206 #ifdef DEBUG
207 	printf("%s\n", cmdbuf);
208 #endif
209 	execle("/bin/sh", "/bin/sh", "-c", cmdbuf, NULL, newenv);
210 	syslog(LOG_CRIT, "Cannot exec /bin/sh: %m");
211 	perror("/bin/sh");
212 	exit(EX_OSFILE);
213 }
214