]> git.ipfire.org Git - ipfire-2.x.git/blob - src/misc-progs/setuid.c
Merge remote-tracking branch 'origin/next' into thirteen
[ipfire-2.x.git] / src / misc-progs / setuid.c
1 /* This file is part of the IPCop Firewall.
2 *
3 * IPCop is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * IPCop is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with IPCop; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 * Copyright (C) 2003-04-22 Robert Kerr <rkerr@go.to>
18 *
19 * $Id: setuid.c,v 1.2.2.1 2005/11/18 14:51:43 franck78 Exp $
20 *
21 */
22
23 #include <stdio.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <sys/types.h>
29 #include <limits.h>
30 #include <sys/time.h>
31 #include <sys/resource.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <grp.h>
35 #include <signal.h>
36 #include <sys/wait.h>
37 #include <glob.h>
38 #include "setuid.h"
39
40 #ifndef OPEN_MAX
41 #define OPEN_MAX 256
42 #endif
43
44 /* Trusted environment for executing commands */
45 char * trusted_env[4]={
46 "PATH=/usr/bin:/usr/sbin:/sbin:/bin",
47 "SHELL=/bin/sh",
48 "TERM=dumb",
49 NULL};
50
51 /* Spawns a child process that uses /bin/sh to interpret a command.
52 * This is much the same in use and purpose as system(), yet as it uses execve
53 * to pass a trusted environment it's immune to attacks based upon changing
54 * IFS, ENV, BASH_ENV and other such variables.
55 * Note this does NOT guard against any other attacks, inparticular you MUST
56 * validate the command you are passing. If the command is formed from user
57 * input be sure to check this input is what you expect. Nasty things can
58 * happen if a user can inject ; or `` into your command for example */
59 int safe_system(char* command)
60 {
61 return system_core( command, 0, 0, "safe_system" );
62 }
63
64 /* Much like safe_system but lets you specify a non-root uid and gid to run
65 * the command as */
66 int unpriv_system(char* command, uid_t uid, gid_t gid)
67 {
68 return system_core(command, uid, gid, "unpriv_system" );
69 }
70
71 int system_core(char* command, uid_t uid, gid_t gid, char *error)
72 {
73 int pid, status;
74
75 if(!command)
76 return 1;
77
78 switch( pid = fork() )
79 {
80 case -1:
81 return -1;
82 case 0: /* child */
83 {
84 char * argv[4];
85 if (gid && setgid(gid))
86 {
87 fprintf(stderr, "%s: ", error);
88 perror("Couldn't setgid");
89 exit(127);
90 }
91 if (uid && setuid(uid))
92 {
93 fprintf(stderr, "%s: ", error);
94 perror("Couldn't setuid");
95 exit(127);
96 }
97 argv[0] = "sh";
98 argv[1] = "-c";
99 argv[2] = command;
100 argv[3] = NULL;
101 execve("/bin/sh", argv, trusted_env);
102 fprintf(stderr, "%s: ", error);
103 perror("execve failed");
104 exit(127);
105 }
106 default: /* parent */
107 do {
108 if( waitpid(pid, &status, 0) == -1 ) {
109 if( errno != EINTR )
110 return -1;
111 } else
112 return status;
113 } while (1);
114 }
115
116 }
117
118 /* BSD style safe strcat; from the secure programming cookbook */
119 size_t strlcat(char *dst, const char *src, size_t len) {
120 char *dstptr = dst;
121 size_t dstlen, tocopy = len;
122 const char *srcptr = src;
123
124 while (tocopy-- && *dstptr) dstptr++;
125 dstlen = dstptr - dst;
126 if (!(tocopy = len - dstlen)) return (dstlen + strlen(src));
127 while (*srcptr) {
128 if (tocopy != 1) {
129 *dstptr++ = *srcptr;
130 tocopy--;
131 }
132 srcptr++;
133 }
134 *dstptr = 0;
135
136 return (dstlen + (srcptr - src));
137 }
138
139 /* General routine to initialise a setuid root program, and put the
140 * environment in a known state. Returns 1 on success, if initsetuid() returns
141 * 0 then you should exit(1) immediately, DON'T attempt to recover from the
142 * error */
143 int initsetuid(void)
144 {
145 int fds,i;
146 struct stat st;
147 struct rlimit rlim;
148
149 /* Prevent signal tricks by ignoring all except SIGKILL and SIGCHILD */
150 for( i = 0; i < NSIG; i++ ) {
151 if( i != SIGKILL && i != SIGCHLD )
152 signal(i, SIG_IGN);
153 }
154
155 /* dump all non-standard file descriptors (a full descriptor table could
156 * lead to DoS by preventing us opening files) */
157 if ((fds = getdtablesize()) == -1) fds = OPEN_MAX;
158 for( i = 3; i < fds; i++ ) close(i);
159
160 /* check stdin, stdout & stderr are open before going any further */
161 for( i = 0; i < 3; i++ )
162 if( fstat(i, &st) == -1 && ((errno != EBADF) || (close(i), open("/dev/null", O_RDWR, 0)) != i ))
163 return 0;
164
165 /* disable core dumps in case we're processing sensitive information */
166 rlim.rlim_cur = rlim.rlim_max = 0;
167 if(setrlimit(RLIMIT_CORE, &rlim))
168 { perror("Couldn't disable core dumps"); return 0; }
169
170 /* drop any supplementary groups, set uid & gid to root */
171 if (setgroups(0, NULL)) { perror("Couldn't clear group list"); return 0; }
172 if (setgid(0)) { perror("Couldn't setgid(0)"); return 0; }
173 if (setuid(0)) { perror("Couldn't setuid(0)"); return 0; }
174
175 return 1;
176 }
177
178 /* check whether a file exists */
179 int file_exists(const char *fname) {
180 struct stat st;
181 stat(fname, &st);
182 return S_ISREG(st.st_mode) ? 1 : 0;
183 }
184
185 /* check whether a file exists. fname is wildcard eg: file_exists (/tmp/foo*) */
186 int file_exists_w(const char *fname)
187 {
188 /* do a quick check first */
189 struct stat st;
190 stat(fname, &st);
191 if (S_ISREG(st.st_mode))
192 return 1;
193
194 /* check for possible wild cards in name */
195 glob_t globbuf;
196 int retval=0;
197 if (glob(fname, GLOB_ERR, NULL, &globbuf)==0) {
198 if (globbuf.gl_pathc>0) {
199 retval=1;
200 }
201 }
202 globfree(&globbuf);
203 return retval;
204 }