]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blob - src/misc-progs/setuid.c
misc-progs: Remove own copy of strlcat.
[people/pmueller/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 /* General routine to initialise a setuid root program, and put the
119 * environment in a known state. Returns 1 on success, if initsetuid() returns
120 * 0 then you should exit(1) immediately, DON'T attempt to recover from the
121 * error */
122 int initsetuid(void)
123 {
124 int fds,i;
125 struct stat st;
126 struct rlimit rlim;
127
128 /* Prevent signal tricks by ignoring all except SIGKILL and SIGCHILD */
129 for( i = 0; i < NSIG; i++ ) {
130 if( i != SIGKILL && i != SIGCHLD )
131 signal(i, SIG_IGN);
132 }
133
134 /* dump all non-standard file descriptors (a full descriptor table could
135 * lead to DoS by preventing us opening files) */
136 if ((fds = getdtablesize()) == -1) fds = OPEN_MAX;
137 for( i = 3; i < fds; i++ ) close(i);
138
139 /* check stdin, stdout & stderr are open before going any further */
140 for( i = 0; i < 3; i++ )
141 if( fstat(i, &st) == -1 && ((errno != EBADF) || (close(i), open("/dev/null", O_RDWR, 0)) != i ))
142 return 0;
143
144 /* disable core dumps in case we're processing sensitive information */
145 rlim.rlim_cur = rlim.rlim_max = 0;
146 if(setrlimit(RLIMIT_CORE, &rlim))
147 { perror("Couldn't disable core dumps"); return 0; }
148
149 /* drop any supplementary groups, set uid & gid to root */
150 if (setgroups(0, NULL)) { perror("Couldn't clear group list"); return 0; }
151 if (setgid(0)) { perror("Couldn't setgid(0)"); return 0; }
152 if (setuid(0)) { perror("Couldn't setuid(0)"); return 0; }
153
154 return 1;
155 }