blob: b49438a1c6705f80fe70fb10d97a94d73291bbbf [file] [log] [blame]
Guido van Rossumffa257d1995-07-20 21:57:15 +00001/*
2 Template for a setuid program that calls a script.
3
4 The script should be in an unwritable directory and should itself
5 be unwritable. In fact all parent directories up to the root
6 should be unwritable. The script must not be setuid, that's what
7 this program is for.
8
9 This is a template program. You need to fill in the name of the
10 script that must be executed. This is done by changing the
11 definition of FULL_PATH below.
12
13 There are also some rules that should be adhered to when writing
14 the script itself.
15
16 The first and most important rule is to never, ever trust that the
17 user of the program will behave properly. Program defensively.
18 Check your arguments for reasonableness. If the user is allowed to
19 create files, check the names of the files. If the program depends
20 on argv[0] for the action it should perform, check it.
21
22 Assuming the script is a Bourne shell script, the first line of the
23 script should be
24 #!/bin/sh -
25 The - is important, don't omit it. If you're using esh, the first
26 line should be
27 #!/usr/local/bin/esh -f
28 and for ksh, the first line should be
29 #!/usr/local/bin/ksh -p
30 The script should then set the variable IFS to the string
31 consisting of <space>, <tab>, and <newline>. After this (*not*
32 before!), the PATH variable should be set to a reasonable value and
33 exported. Do not expect the PATH to have a reasonable value, so do
34 not trust the old value of PATH. You should then set the umask of
35 the program by calling
36 umask 077 # or 022 if you want the files to be readable
37 If you plan to change directories, you should either unset CDPATH
38 or set it to a good value. Setting CDPATH to just ``.'' (dot) is a
39 good idea.
40 If, for some reason, you want to use csh, the first line should be
41 #!/bin/csh -fb
42 You should then set the path variable to something reasonable,
43 without trusting the inherited path. Here too, you should set the
44 umask using the command
45 umask 077 # or 022 if you want the files to be readable
46*/
47
48#include <unistd.h>
49#include <stdlib.h>
50#include <stdio.h>
51#include <sys/types.h>
52#include <sys/stat.h>
53
54/* CONFIGURATION SECTION */
55
56#ifndef FULL_PATH /* so that this can be specified from the Makefile */
57#define FULL_PATH "/full/path/of/script"
58#endif
59#ifndef UMASK
60#define UMASK 077
61#endif
62
63/* END OF CONFIGURATION SECTION */
64
65#if defined(__STDC__) && defined(__sgi)
66#define environ _environ
67#endif
68
69/* don't change def_IFS */
70char def_IFS[] = "IFS= \t\n";
71/* you may want to change def_PATH, but you should really change it in */
72/* your script */
73#ifdef __sgi
74char def_PATH[] = "PATH=/usr/bsd:/usr/bin:/bin:/usr/local/bin:/usr/sbin";
75#else
76char def_PATH[] = "PATH=/usr/ucb:/usr/bin:/bin:/usr/local/bin";
77#endif
78/* don't change def_CDPATH */
79char def_CDPATH[] = "CDPATH=.";
80/* don't change def_ENV */
81char def_ENV[] = "ENV=:";
82
83/*
84 This function changes all environment variables that start with LD_
85 into variables that start with XD_. This is important since we
86 don't want the script that is executed to use any funny shared
87 libraries.
88
89 The other changes to the environment are, strictly speaking, not
90 needed here. They can safely be done in the script. They are done
91 here because we don't trust the script writer (just like the script
92 writer shouldn't trust the user of the script).
93 If IFS is set in the environment, set it to space,tab,newline.
94 If CDPATH is set in the environment, set it to ``.''.
95 Set PATH to a reasonable default.
96*/
97void
98clean_environ(void)
99{
100 char **p;
101 extern char **environ;
102
103 for (p = environ; *p; p++) {
104 if (strncmp(*p, "LD_", 3) == 0)
105 **p = 'X';
106 else if (strncmp(*p, "_RLD", 4) == 0)
107 **p = 'X';
Guido van Rossum80eb3c01997-03-11 18:24:21 +0000108 else if (strncmp(*p, "PYTHON", 6) == 0)
109 **p = 'X';
Guido van Rossumffa257d1995-07-20 21:57:15 +0000110 else if (strncmp(*p, "IFS=", 4) == 0)
111 *p = def_IFS;
112 else if (strncmp(*p, "CDPATH=", 7) == 0)
113 *p = def_CDPATH;
114 else if (strncmp(*p, "ENV=", 4) == 0)
115 *p = def_ENV;
116 }
117 putenv(def_PATH);
118}
119
120int
121main(int argc, char **argv)
122{
123 struct stat statb;
124 gid_t egid = getegid();
125 uid_t euid = geteuid();
126
127 /*
128 Sanity check #1.
129 This check should be made compile-time, but that's not possible.
130 If you're sure that you specified a full path name for FULL_PATH,
131 you can omit this check.
132 */
133 if (FULL_PATH[0] != '/') {
134 fprintf(stderr, "%s: %s is not a full path name\n", argv[0],
135 FULL_PATH);
136 fprintf(stderr, "Tell this program's maintainer that s\\he ");
137 fprintf(stderr, "fouled up some simple rules\n");
138 fprintf(stderr, "pretty badly.\n");
139 exit(1);
140 }
141
142 /*
143 Sanity check #2.
144 Check that the owner of the script is equal to either the
145 effective uid or the super user.
146 */
147 if (stat(FULL_PATH, &statb) < 0) {
148 perror("stat");
149 exit(1);
150 }
151 if (statb.st_uid != 0 && statb.st_uid != euid) {
152 fprintf(stderr, "%s: %s has the wrong owner\n", argv[0],
153 FULL_PATH);
154 fprintf(stderr, "Tell this program's maintainer that the ");
155 fprintf(stderr, "script should be owned by him/herself or the\n");
156 fprintf(stderr, "superuser.\n");
157 exit(1);
158 }
159
160 if (setregid(egid, egid) < 0)
161 perror("setregid");
162 if (setreuid(euid, euid) < 0)
163 perror("setreuid");
164
165 clean_environ();
166
167 umask(UMASK);
168
169 while (**argv == '-') /* don't let argv[0] start with '-' */
170 (*argv)++;
171 execv(FULL_PATH, argv);
172 fprintf(stderr, "%s: could not execute the script\n", argv[0]);
173 exit(1);
174}