|
1 /* |
|
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 #include <string.h> |
|
54 |
|
55 /* CONFIGURATION SECTION */ |
|
56 |
|
57 #ifndef FULL_PATH /* so that this can be specified from the Makefile */ |
|
58 /* Uncomment the following line: |
|
59 #define FULL_PATH "/full/path/of/script" |
|
60 * Then comment out the #error line. */ |
|
61 #error "You must define FULL_PATH somewhere" |
|
62 #endif |
|
63 #ifndef UMASK |
|
64 #define UMASK 077 |
|
65 #endif |
|
66 |
|
67 /* END OF CONFIGURATION SECTION */ |
|
68 |
|
69 #if defined(__STDC__) && defined(__sgi) |
|
70 #define environ _environ |
|
71 #endif |
|
72 |
|
73 /* don't change def_IFS */ |
|
74 char def_IFS[] = "IFS= \t\n"; |
|
75 /* you may want to change def_PATH, but you should really change it in */ |
|
76 /* your script */ |
|
77 #ifdef __sgi |
|
78 char def_PATH[] = "PATH=/usr/bsd:/usr/bin:/bin:/usr/local/bin:/usr/sbin"; |
|
79 #else |
|
80 char def_PATH[] = "PATH=/usr/ucb:/usr/bin:/bin:/usr/local/bin"; |
|
81 #endif |
|
82 /* don't change def_CDPATH */ |
|
83 char def_CDPATH[] = "CDPATH=."; |
|
84 /* don't change def_ENV */ |
|
85 char def_ENV[] = "ENV=:"; |
|
86 |
|
87 /* |
|
88 This function changes all environment variables that start with LD_ |
|
89 into variables that start with XD_. This is important since we |
|
90 don't want the script that is executed to use any funny shared |
|
91 libraries. |
|
92 |
|
93 The other changes to the environment are, strictly speaking, not |
|
94 needed here. They can safely be done in the script. They are done |
|
95 here because we don't trust the script writer (just like the script |
|
96 writer shouldn't trust the user of the script). |
|
97 If IFS is set in the environment, set it to space,tab,newline. |
|
98 If CDPATH is set in the environment, set it to ``.''. |
|
99 Set PATH to a reasonable default. |
|
100 */ |
|
101 void |
|
102 clean_environ(void) |
|
103 { |
|
104 char **p; |
|
105 extern char **environ; |
|
106 |
|
107 for (p = environ; *p; p++) { |
|
108 if (strncmp(*p, "LD_", 3) == 0) |
|
109 **p = 'X'; |
|
110 else if (strncmp(*p, "_RLD", 4) == 0) |
|
111 **p = 'X'; |
|
112 else if (strncmp(*p, "PYTHON", 6) == 0) |
|
113 **p = 'X'; |
|
114 else if (strncmp(*p, "IFS=", 4) == 0) |
|
115 *p = def_IFS; |
|
116 else if (strncmp(*p, "CDPATH=", 7) == 0) |
|
117 *p = def_CDPATH; |
|
118 else if (strncmp(*p, "ENV=", 4) == 0) |
|
119 *p = def_ENV; |
|
120 } |
|
121 putenv(def_PATH); |
|
122 } |
|
123 |
|
124 int |
|
125 main(int argc, char **argv) |
|
126 { |
|
127 struct stat statb; |
|
128 gid_t egid = getegid(); |
|
129 uid_t euid = geteuid(); |
|
130 |
|
131 /* |
|
132 Sanity check #1. |
|
133 This check should be made compile-time, but that's not possible. |
|
134 If you're sure that you specified a full path name for FULL_PATH, |
|
135 you can omit this check. |
|
136 */ |
|
137 if (FULL_PATH[0] != '/') { |
|
138 fprintf(stderr, "%s: %s is not a full path name\n", argv[0], |
|
139 FULL_PATH); |
|
140 fprintf(stderr, "You can only use this wrapper if you\n"); |
|
141 fprintf(stderr, "compile it with an absolute path.\n"); |
|
142 exit(1); |
|
143 } |
|
144 |
|
145 /* |
|
146 Sanity check #2. |
|
147 Check that the owner of the script is equal to either the |
|
148 effective uid or the super user. |
|
149 */ |
|
150 if (stat(FULL_PATH, &statb) < 0) { |
|
151 perror("stat"); |
|
152 exit(1); |
|
153 } |
|
154 if (statb.st_uid != 0 && statb.st_uid != euid) { |
|
155 fprintf(stderr, "%s: %s has the wrong owner\n", argv[0], |
|
156 FULL_PATH); |
|
157 fprintf(stderr, "The script should be owned by root,\n"); |
|
158 fprintf(stderr, "and shouldn't be writeable by anyone.\n"); |
|
159 exit(1); |
|
160 } |
|
161 |
|
162 if (setregid(egid, egid) < 0) |
|
163 perror("setregid"); |
|
164 if (setreuid(euid, euid) < 0) |
|
165 perror("setreuid"); |
|
166 |
|
167 clean_environ(); |
|
168 |
|
169 umask(UMASK); |
|
170 |
|
171 while (**argv == '-') /* don't let argv[0] start with '-' */ |
|
172 (*argv)++; |
|
173 execv(FULL_PATH, argv); |
|
174 fprintf(stderr, "%s: could not execute the script\n", argv[0]); |
|
175 exit(1); |
|
176 } |