addrutil: Re-indent.
[wk-misc.git] / webbsh.c
1 /* webbsh.c - Webbuilder Shell, a chroot command for the webbuilder account.
2  * Copyright (C) 2002 g10 Code GmbH
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2, or (at your option) any
7  * later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <assert.h>
23 #include <errno.h>
24 #include <ctype.h>
25 #include <sys/types.h>
26 #include <sys/wait.h>
27 #include <unistd.h>
28 #include <pwd.h>
29
30 #define ACCOUNT "webbuilder"
31
32 #ifdef _POSIX_OPEN_MAX
33 #define MAX_OPEN_FDS _POSIX_OPEN_MAX
34 #else
35 #define MAX_OPEN_FDS 256
36 #endif
37
38
39 static void 
40 usage (void)
41 {
42   fprintf (stderr, "usage: webbsh directory commands\n");
43   exit (1);
44 }
45
46 static void *
47 xmalloc (size_t n)
48 {
49   void *p = malloc (n);
50   if (!n)
51     {
52       fprintf (stderr, "webbsh: out of core\n");
53       exit (1);
54     }
55   return p;
56 }
57
58 static void *
59 xcalloc (size_t n, size_t m)
60 {
61   void *p = xmalloc (n*m);
62   memset (p, 0, n*m);
63   return p;
64 }
65
66
67 static void
68 drop_privileges (void)
69 {
70   if (setuid ( getuid () ) || getuid () != geteuid() || !setuid(0) )
71     { 
72       fprintf (stderr, "webbsh: failed to give up privileges: %s\n",  
73                strerror (errno));
74       exit (1);
75     }
76 }
77
78 /*  static char * */
79 /*  xstrdup (const char *s) */
80 /*  { */
81 /*    char *p = xmalloc (strlen (s)+1); */
82 /*    strcpy (p, s); */
83 /*    return p; */
84 /*  } */
85
86
87 /* Check whether any of the files below directory is suid or sgid. */ 
88 static void
89 check_for_suid (const char *directory)
90 {
91   pid_t pid;
92   int status;
93
94   if (strchr (directory, '\'') )
95     { 
96       fprintf (stderr, "webbsh: directory must not contain a `''\n");
97       exit (1);
98     }
99
100   /* we run setuid but we don't want to run find then - so lets fork. */
101   pid = fork ();
102   if ( pid == (pid_t)-1)
103     {
104       fprintf (stderr, "webbsh: fork failed: %s\n", strerror (errno));
105       exit (1);
106     }
107   
108   if (!pid)
109     { /* child */
110       FILE *fp;
111       char *cmd;
112       int esc, c, rc;
113
114       drop_privileges (); 
115       if (!geteuid () || !getuid ())
116         abort (); /* we are pretty paranoid. */
117
118       cmd = xmalloc ( strlen (directory) + 100);
119       strcpy (cmd, "/usr/bin/find '");
120       strcat (cmd, directory);
121       strcat (cmd, "' -type f  -perm +06000 -print");
122       fp = popen (cmd, "r");
123       free (cmd);
124       if (!fp)
125         {
126           fprintf (stderr, "webbsh: failed to run find utility: %s\n",
127                    strerror (errno)); 
128           exit (1);
129         }
130       esc = 0;
131       while ( (c=getc (fp)) != EOF)
132         {
133           if (!esc || esc == 2)
134             {
135               fputs ("webbsh: s[ug]id file `", stderr);
136               esc = 1;
137             }
138           if (c == '\n')
139             {
140               fputs ("' detected\n",stderr);
141               esc = 2;
142             }
143           else if (isascii (c) && isprint (c))
144             putc (c, stderr);
145           else
146             printf ("\\%02x", c );
147         }
148       if ( (rc=pclose (fp)) )
149         {
150           fprintf (stderr, "webbsh: find utility failed: rc=%d\n", rc);
151           exit (1);
152         }
153       exit (esc?1:0);
154     }
155
156   /* parent */
157   if ( waitpid (pid, &status, 0 ) == -1 || status)
158     {
159       fprintf (stderr, "webbsh: suspect files found\n");
160       exit (1);
161     }
162 }
163
164
165
166 static char **
167 build_env (void)
168 {
169   int n = 0;
170   char **envp = xcalloc (10, sizeof *envp);
171   
172   envp[n++] = "HOME=/";
173   envp[n++] = "SHELL=/bin/sh";  
174   envp[n++] = "LOGNAME=" ACCOUNT;
175   envp[n++] = "PATH=/bin:/usr/bin";
176
177   return envp;
178 }
179
180
181
182 int 
183 main (int argc, char **argv)
184 {
185   const char *directory;
186   struct passwd *pw;
187   int i, n;
188   gid_t dummy_grplist[1];
189   char **envp;
190
191   if (argc < 2)
192     usage ();
193   argc--; argv++;
194   if ( !strcmp (*argv, "--help") )
195     usage ();
196   directory = *argv;
197   argc--; argv++;
198   if (*directory != '/')
199     {
200       fprintf (stderr, "webbsh: directory must be given as absolute path\n");
201       exit (1);
202     }
203
204   pw = getpwnam (ACCOUNT);
205   if (!pw)
206     { 
207       fprintf (stderr, "webbsh: no user `%s'\n", ACCOUNT);
208       exit (1);
209     }
210   if ( getuid () != pw->pw_uid || getgid () != pw->pw_gid)
211     { 
212       fprintf (stderr, "webbsh: not run as user `%s'\n", ACCOUNT);
213       exit (1);
214     }
215
216   n = getgroups (0, dummy_grplist);
217   if ( n < 0 || n > 1)
218     { 
219       fprintf (stderr, "webbsh: user `%s' must not"
220                        " have any supplementary groups\n", ACCOUNT);
221       exit (1);
222     }
223   if ( getegid () != getgid () )
224     { 
225       fprintf (stderr, "webbsh: must not be run sgid\n");
226       exit (1);
227     }
228
229
230   /* Close all files except for the standard ones. */
231   n = sysconf (_SC_OPEN_MAX);
232   if (n < 0)
233     n = MAX_OPEN_FDS;
234   for (i=0; i < n; i++)
235     {
236       if (i == STDIN_FILENO || i == STDOUT_FILENO || i == STDERR_FILENO)
237         continue;
238       close(i);
239     }
240   errno = 0;
241     
242   if (chdir (directory))
243     { 
244       fprintf (stderr, "webbsh: chdir `%s' failed: %s\n", directory, 
245                strerror (errno));
246       exit (1);
247     }
248
249   check_for_suid (directory);
250
251   if (chroot (directory))
252     { 
253       fprintf (stderr, "webbsh: chroot `%s' failed: %s\n", directory, 
254                strerror (errno));
255       exit (1);
256     }
257
258   drop_privileges ();
259
260   if (!geteuid () || !getuid ())
261     abort (); /* we are pretty paranoid. */
262
263   if (argv[argc])
264     abort (); /* should never happen. */
265
266   envp = build_env ();
267
268   if (!argc)
269     {
270       fputs ("webbsh: no command given - would use this environment:\n", stderr);
271       for (i=0; envp[i]; i++)
272         fprintf (stderr, "%s\n", envp[i]);
273       exit (0);
274     }
275
276
277   if (execve (argv[0], argv, envp))
278     {
279       fprintf (stderr, "webbsh: failed to run `%s': %s\n", argv[0],  
280                strerror (errno));
281       exit (1);
282     }
283   abort (); /*NOTREACHED*/
284   return 0;
285 }
286
287 /*
288 Local Variables:
289 compile-command: "cc -Wall -o webbsh webbsh.c"
290 End:
291 */
292