]> andersk Git - test.git/blob - shellinabox/privileges.c
61268a248f504289bb751f6632c50088facfe60e
[test.git] / shellinabox / privileges.c
1 // privileges.c -- Manage process privileges
2 // Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License version 2 as
6 // published by the Free Software Foundation.
7 //
8 // This program 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 along
14 // with this program; if not, write to the Free Software Foundation, Inc.,
15 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 //
17 // In addition to these license terms, the author grants the following
18 // additional rights:
19 //
20 // If you modify this program, or any covered work, by linking or
21 // combining it with the OpenSSL project's OpenSSL library (or a
22 // modified version of that library), containing parts covered by the
23 // terms of the OpenSSL or SSLeay licenses, the author
24 // grants you additional permission to convey the resulting work.
25 // Corresponding Source for a non-source form of such a combination
26 // shall include the source code for the parts of OpenSSL used as well
27 // as that of the covered work.
28 //
29 // You may at your option choose to remove this additional permission from
30 // the work, or from any part of it.
31 //
32 // It is possible to build this program in a way that it loads OpenSSL
33 // libraries at run-time. If doing so, the following notices are required
34 // by the OpenSSL and SSLeay licenses:
35 //
36 // This product includes software developed by the OpenSSL Project
37 // for use in the OpenSSL Toolkit. (http://www.openssl.org/)
38 //
39 // This product includes cryptographic software written by Eric Young
40 // (eay@cryptsoft.com)
41 //
42 //
43 // The most up-to-date version of this program is always available from
44 // http://shellinabox.com
45
46 #define _GNU_SOURCE
47 #include "config.h"
48
49 #include <errno.h>
50 #include <grp.h>
51 #include <limits.h>
52 #include <pwd.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <sys/types.h>
57 #include <unistd.h>
58
59 #include "shellinabox/privileges.h"
60 #include "logging/logging.h"
61
62 int   runAsUser  = -1;
63 int   runAsGroup = -1;
64
65
66 static void removeGroupPrivileges(int showError) {
67   gid_t rg, eg, sg;
68   check(!getresgid(&rg, &eg, &sg));
69
70   // Remove all supplementary groups. Allow this command to fail. That could
71   // happen if we run as an unprivileged user.
72   setgroups(0, (gid_t *)"");
73
74   if (runAsGroup >= 0) {
75     uid_t ru, eu, su;
76     getresuid(&ru, &eu, &su);
77
78     // Try to switch the user-provided group.
79     if ((ru && runAsGroup != rg) ||
80         setresgid(runAsGroup, runAsGroup, runAsGroup)) {
81       if (showError) {
82         fatal("Only privileged users can change their group memberships");
83       } else {
84         _exit(1);
85       }
86     }
87   } else {
88     if (rg) {
89       // If we were started as a set-gid binary, drop these permissions, now.
90       check(!setresgid(rg, rg, rg));
91     } else {
92       // If we are running as root, switch to "nogroup"
93       gid_t ng = getGroupId("nogroup");
94       check(!setresgid(ng, ng, ng));
95     }
96   }
97 }
98
99 void lowerPrivileges(void) {
100   uid_t r, e, g;
101   check(!getresuid(&r, &e, &g));
102
103   // Permanently lower all group permissions. We do not actually need these,
104   // as we still have "root" user privileges in our saved-uid.
105   removeGroupPrivileges(0);
106
107   // Temporarily lower user privileges. If we used to have "root" privileges,
108   // we can later still regain them.
109   setresuid(-1, -1, 0);
110
111   if (runAsUser >= 0) {
112     // Try to switch to the user-provided user id.
113     if (r && runAsUser != r) {
114       fatal("Only privileged users can change their user id");
115     }
116     check(!setresuid(runAsUser, runAsUser, -1));
117   } else {
118     if (r) {
119       // If we were started as a set-uid binary, temporarily lower these
120       // permissions.
121       check(!setresuid(r, r, -1));
122     } else {
123       // If we are running as "root", temporarily switch to "nobody".
124       uid_t n = getUserId("nobody");
125       check(!setresuid(n, n, -1));
126     }
127   }
128 }
129
130 void dropPrivileges(void) {
131   uid_t r, e, s;
132   check(!getresuid(&r, &e, &s));
133
134   // Drop all group privileges.
135   removeGroupPrivileges(1);
136
137   if (runAsUser >= 0) {
138     // Try to switch to the user-provided user id.
139     if ((r && runAsUser != r) ||
140         setresuid(runAsUser, runAsUser, runAsUser)) {
141       fatal("Only privileged users can change their user id.");
142     }
143   } else {
144     if (r) {
145       // If we were started as a set-uid binary, permanently drop these
146       // permissions.
147       check(!setresuid(r, r, r));
148     } else {
149       // If we are running as "root", permanently switch to "nobody".
150       uid_t n = getUserId("nobody");
151       check(!setresuid(n, n, n));
152     }
153   }
154 }
155
156 #ifndef HAVE_GETPWUID_R
157 // This is a not-thread-safe replacement for getpwuid_r()
158 #define getpwuid_r x_getpwuid_r
159 static int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, size_t buflen,
160                       struct passwd **result) {
161   if (result) {
162     *result        = NULL;
163   }
164   if (!pwd) {
165     return -1;
166   }
167   errno            = 0;
168   struct passwd *p = getpwuid(uid);
169   if (!p) {
170     return errno ? -1 : 0;
171   }
172   *pwd             = *p;
173   if (result) {
174     *result        = pwd;
175   }
176   return 0;
177 }
178 #endif
179
180 const char *getUserName(uid_t uid) {
181   struct passwd pwbuf, *pw;
182   char *buf;
183   #ifdef _SC_GETPW_R_SIZE_MAX
184   int len      = sysconf(_SC_GETPW_R_SIZE_MAX);
185   if (len <= 0) {
186     len        = 4096;
187   }
188   #else
189   int len      = 4096;
190   #endif
191   check(buf    = malloc(len));
192   char *user;
193   if (getpwuid_r(uid, &pwbuf, buf, len, &pw) || !pw) {
194     check(user = malloc(32));
195     snprintf(user, 32, "%d", uid);
196   } else {
197     check(user = strdup(pw->pw_name));
198   }
199   free(buf);
200   return user;
201 }
202
203 #ifndef HAVE_GETPWNAM_R
204 // This is a not-thread-safe replacement for getpwnam_r()
205 #define getpwnam_r x_getpwnam_r
206 static int getpwnam_r(const char *name, struct passwd *pwd, char *buf,
207                       size_t buflen, struct passwd **result) {
208   if (result) {
209     *result        = NULL;
210   }
211   if (!pwd) {
212     return -1;
213   }
214   errno            = 0;
215   struct passwd *p = getpwnam(name);
216   if (!p) {
217     return errno ? -1 : 0;
218   }
219   *pwd             = *p;
220   if (result) {
221     *result        = pwd;
222   }
223   return 0;
224 }
225 #endif
226
227 uid_t getUserId(const char *name) {
228   struct passwd pwbuf, *pw;
229   char *buf;
230   #ifdef _SC_GETPW_R_SIZE_MAX
231   int len   = sysconf(_SC_GETPW_R_SIZE_MAX);
232   if (len <= 0) {
233     len     = 4096;
234   }
235   #else
236   int len   = 4096;
237   #endif
238   check(buf = malloc(len));
239   if (getpwnam_r(name, &pwbuf, buf, len, &pw) || !pw) {
240     fatal("Cannot look up user id \"%s\"", name);
241   }
242   uid_t uid = pw->pw_uid;
243   free(buf);
244   return uid;
245 }
246
247 uid_t parseUser(const char *arg, const char **name) {
248   char *end;
249   errno           = 0;
250   unsigned long l = strtoul(arg, &end, 10);
251   if (errno || l > INT_MAX || *end) {
252     if (name) {
253       check(*name = strdup(arg));
254     }
255     return getUserId(arg);
256   } else {
257     if (name) {
258       *name       = getUserName((uid_t)l);
259     }
260     return (uid_t)l;
261   }
262 }
263
264 #ifndef HAVE_GETGRGID_R
265 // This is a not-thread-safe replacement for getgrgid_r()
266 #define getgrgid_r x_getgrgid_r
267 static int getgrgid_r(gid_t gid, struct group *grp, char *buf, size_t buflen,
268                       struct group **result) {
269   if (result) {
270     *result       = NULL;
271   }
272   if (!grp) {
273     return -1;
274   }
275   errno           = 0;
276   struct group *g = getgrgid(gid);
277   if (!g) {
278     return errno ? -1 : 0;
279   }
280   *grp            = *g;
281   if (result) {
282     *result       = grp;
283   }
284   return 0;
285 }
286 #endif
287
288 const char *getGroupName(gid_t gid) {
289   struct group grbuf, *gr;
290   char *buf;
291   #ifdef _SC_GETGR_R_SIZE_MAX
292   int len       = sysconf(_SC_GETGR_R_SIZE_MAX);
293   if (len <= 0) {
294     len         = 4096;
295   }
296   #else
297   int len       = 4096;
298   #endif
299   check(buf     = malloc(len));
300   char *group;
301   if (getgrgid_r(gid, &grbuf, buf, len, &gr) || !gr) {
302     check(group = malloc(32));
303     snprintf(group, 32, "%d", gid);
304   } else {
305     check(group = strdup(gr->gr_name));
306   }
307   free(buf);
308   return group;
309 }
310
311 #ifndef HAVE_GETGRNAM_R
312 // This is a not-thread-safe replacement for getgrnam_r()
313 #define getgrnam_r x_getgrnam_r
314 static int getgrnam_r(const char *name, struct group *grp, char *buf,
315                       size_t buflen, struct group **result) {
316   if (result) {
317     *result       = NULL;
318   }
319   if (!grp) {
320     return -1;
321   }
322   errno           = 0;
323   struct group *g = getgrnam(name);
324   if (!g) {
325     return errno ? -1 : 0;
326   }
327   *grp            = *g;
328   if (result) {
329     *result       = grp;
330   }
331   return 0;
332 }
333 #endif
334
335 gid_t getGroupId(const char *name) {
336   struct group grbuf, *gr;
337   char *buf;
338   #ifdef _SC_GETGR_R_SIZE_MAX
339   int gr_len      = sysconf(_SC_GETGR_R_SIZE_MAX);
340   if (gr_len <= 0) {
341     gr_len        = 4096;
342   }
343   #else
344   int gr_len      = 4096;
345   #endif
346   check(buf       = malloc(gr_len));
347   if (getgrnam_r(name, &grbuf, buf, gr_len, &gr) || !gr) {
348     // Maybe, this system does not have a "nogroup" group. Substitute the
349     // group of the "nobody" user.
350     if (!strcmp(name, "nogroup")) {
351       struct passwd pwbuf, *pw;
352       #ifdef _SC_GETPW_R_SIZE_MAX
353       int pw_len  = sysconf(_SC_GETPW_R_SIZE_MAX);
354       if (pw_len <= 0) {
355         pw_len    = 4096;
356       }
357       #else
358       int pw_len  = 4096;
359       #endif
360       if (pw_len > gr_len) {
361         check(buf = realloc(buf, pw_len));
362       }
363       if (!getpwnam_r("nobody", &pwbuf, buf, pw_len, &pw) && pw) {
364         debug("Substituting \"nobody's\" primary group for \"nogroup\"");
365         gid_t gid = pw->pw_gid;
366         free(buf);
367         return gid;
368       }
369     }
370     fatal("Cannot look up group \"%s\"", name);
371   }
372   gid_t gid = gr->gr_gid;
373   free(buf);
374   return gid;
375 }
376
377 gid_t parseGroup(const char *arg, const char **name) {
378   char *end;
379   errno           = 0;
380   unsigned long l = strtoul(arg, &end, 10);
381   if (errno || l > INT_MAX || *end) {
382     if (name) {
383       check(*name = strdup(arg));
384     }
385     return getGroupId(arg);
386   } else {
387     if (name) {
388       *name       = getGroupName((uid_t)l);
389     }
390     return (uid_t)l;
391   }
392 }
This page took 0.051898 seconds and 3 git commands to generate.