]> andersk Git - test.git/blob - shellinabox/privileges.c
Another iteration of changes intended to deal with MacOS X specific build
[test.git] / shellinabox / privileges.c
1 // privileges.c -- Manage process privileges
2 // Copyright (C) 2008-2010 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 #ifndef HAVE_GETRESUID
67 int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid) {
68   *ruid = getuid();
69   *euid = geteuid();
70   *suid = -1;
71   return 0;
72 }
73 #endif
74
75 #ifndef HAVE_GETRESGID
76 int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid) {
77   *rgid = getgid();
78   *egid = getegid();
79   *sgid = -1;
80   return 0;
81 }
82 #endif
83
84 #ifndef HAVE_SETRESUID
85 int setresuid(uid_t ruid, uid_t euid, uid_t suid) {
86   return setreuid(ruid, euid);
87 }
88 #endif
89
90 #ifndef HAVE_SETRESGID
91 int setresgid(gid_t rgid, gid_t egid, gid_t sgid) {
92   return setregid(rgid, egid);
93 }
94 #endif
95
96 static void removeGroupPrivileges(int showError) {
97   gid_t rg, eg, sg;
98   check(!getresgid(&rg, &eg, &sg));
99
100   // Remove all supplementary groups. Allow this command to fail. That could
101   // happen if we run as an unprivileged user.
102   setgroups(0, (gid_t *)"");
103
104   if (runAsGroup >= 0) {
105     uid_t ru, eu, su;
106     getresuid(&ru, &eu, &su);
107
108     // Try to switch the user-provided group.
109     if ((ru && runAsGroup != (int)rg) ||
110         setresgid(runAsGroup, runAsGroup, runAsGroup)) {
111       if (showError) {
112         fatal("Only privileged users can change their group memberships");
113       } else {
114         _exit(1);
115       }
116     }
117   } else {
118     if (rg) {
119       // If we were started as a set-gid binary, drop these permissions, now.
120       check(!setresgid(rg, rg, rg));
121     } else {
122       // If we are running as root, switch to "nogroup"
123       gid_t ng = getGroupId("nogroup");
124       check(!setresgid(ng, ng, ng));
125     }
126   }
127 }
128
129 void lowerPrivileges(void) {
130   uid_t r, e, g;
131   check(!getresuid(&r, &e, &g));
132
133   // Permanently lower all group permissions. We do not actually need these,
134   // as we still have "root" user privileges in our saved-uid.
135   removeGroupPrivileges(0);
136
137   // Temporarily lower user privileges. If we used to have "root" privileges,
138   // we can later still regain them.
139   setresuid(-1, -1, 0);
140
141   if (runAsUser >= 0) {
142     // Try to switch to the user-provided user id.
143     if (r && runAsUser != (int)r) {
144       fatal("Only privileged users can change their user id");
145     }
146     check(!setresuid(runAsUser, runAsUser, -1));
147   } else {
148     if (r) {
149       // If we were started as a set-uid binary, temporarily lower these
150       // permissions.
151       check(!setresuid(r, r, -1));
152     } else {
153       // If we are running as "root", temporarily switch to "nobody".
154       uid_t n = getUserId("nobody");
155       check(!setresuid(n, n, -1));
156     }
157   }
158 }
159
160 void dropPrivileges(void) {
161   uid_t r, e, s;
162   check(!getresuid(&r, &e, &s));
163
164   // Drop all group privileges.
165   removeGroupPrivileges(1);
166
167   if (runAsUser >= 0) {
168     // Try to switch to the user-provided user id.
169     if ((r && runAsUser != (int)r) ||
170         setresuid(runAsUser, runAsUser, runAsUser)) {
171       fatal("Only privileged users can change their user id.");
172     }
173   } else {
174     if (r) {
175       // If we were started as a set-uid binary, permanently drop these
176       // permissions.
177       check(!setresuid(r, r, r));
178     } else {
179       // If we are running as "root", permanently switch to "nobody".
180       uid_t n = getUserId("nobody");
181       check(!setresuid(n, n, n));
182     }
183   }
184 }
185
186 #ifndef HAVE_GETPWUID_R
187 // This is a not-thread-safe replacement for getpwuid_r()
188 #define getpwuid_r x_getpwuid_r
189 static int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, size_t buflen,
190                       struct passwd **result) {
191   if (result) {
192     *result        = NULL;
193   }
194   if (!pwd) {
195     return -1;
196   }
197   errno            = 0;
198   struct passwd *p = getpwuid(uid);
199   if (!p) {
200     return errno ? -1 : 0;
201   }
202   *pwd             = *p;
203   if (result) {
204     *result        = pwd;
205   }
206   return 0;
207 }
208 #endif
209
210 const char *getUserName(uid_t uid) {
211   struct passwd pwbuf, *pw;
212   char *buf;
213   #ifdef _SC_GETPW_R_SIZE_MAX
214   int len      = sysconf(_SC_GETPW_R_SIZE_MAX);
215   if (len <= 0) {
216     len        = 4096;
217   }
218   #else
219   int len      = 4096;
220   #endif
221   check(buf    = malloc(len));
222   char *user;
223   if (getpwuid_r(uid, &pwbuf, buf, len, &pw) || !pw) {
224     check(user = malloc(32));
225     snprintf(user, 32, "%d", uid);
226   } else {
227     check(user = strdup(pw->pw_name));
228   }
229   free(buf);
230   return user;
231 }
232
233 #ifndef HAVE_GETPWNAM_R
234 // This is a not-thread-safe replacement for getpwnam_r()
235 #define getpwnam_r x_getpwnam_r
236 static int getpwnam_r(const char *name, struct passwd *pwd, char *buf,
237                       size_t buflen, struct passwd **result) {
238   if (result) {
239     *result        = NULL;
240   }
241   if (!pwd) {
242     return -1;
243   }
244   errno            = 0;
245   struct passwd *p = getpwnam(name);
246   if (!p) {
247     return errno ? -1 : 0;
248   }
249   *pwd             = *p;
250   if (result) {
251     *result        = pwd;
252   }
253   return 0;
254 }
255 #endif
256
257 uid_t getUserId(const char *name) {
258   struct passwd pwbuf, *pw;
259   char *buf;
260   #ifdef _SC_GETPW_R_SIZE_MAX
261   int len   = sysconf(_SC_GETPW_R_SIZE_MAX);
262   if (len <= 0) {
263     len     = 4096;
264   }
265   #else
266   int len   = 4096;
267   #endif
268   check(buf = malloc(len));
269   if (getpwnam_r(name, &pwbuf, buf, len, &pw) || !pw) {
270     fatal("Cannot look up user id \"%s\"", name);
271   }
272   uid_t uid = pw->pw_uid;
273   free(buf);
274   return uid;
275 }
276
277 uid_t parseUserArg(const char *arg, const char **name) {
278   char *end;
279   errno           = 0;
280   unsigned long l = strtoul(arg, &end, 10);
281   if (errno || l > INT_MAX || *end) {
282     if (name) {
283       check(*name = strdup(arg));
284     }
285     return getUserId(arg);
286   } else {
287     if (name) {
288       *name       = getUserName((uid_t)l);
289     }
290     return (uid_t)l;
291   }
292 }
293
294 #ifndef HAVE_GETGRGID_R
295 // This is a not-thread-safe replacement for getgrgid_r()
296 #define getgrgid_r x_getgrgid_r
297 static int getgrgid_r(gid_t gid, struct group *grp, char *buf, size_t buflen,
298                       struct group **result) {
299   if (result) {
300     *result       = NULL;
301   }
302   if (!grp) {
303     return -1;
304   }
305   errno           = 0;
306   struct group *g = getgrgid(gid);
307   if (!g) {
308     return errno ? -1 : 0;
309   }
310   *grp            = *g;
311   if (result) {
312     *result       = grp;
313   }
314   return 0;
315 }
316 #endif
317
318 const char *getGroupName(gid_t gid) {
319   struct group grbuf, *gr;
320   char *buf;
321   #ifdef _SC_GETGR_R_SIZE_MAX
322   int len       = sysconf(_SC_GETGR_R_SIZE_MAX);
323   if (len <= 0) {
324     len         = 4096;
325   }
326   #else
327   int len       = 4096;
328   #endif
329   check(buf     = malloc(len));
330   char *group;
331   if (getgrgid_r(gid, &grbuf, buf, len, &gr) || !gr) {
332     check(group = malloc(32));
333     snprintf(group, 32, "%d", gid);
334   } else {
335     check(group = strdup(gr->gr_name));
336   }
337   free(buf);
338   return group;
339 }
340
341 #ifndef HAVE_GETGRNAM_R
342 // This is a not-thread-safe replacement for getgrnam_r()
343 #define getgrnam_r x_getgrnam_r
344 static int getgrnam_r(const char *name, struct group *grp, char *buf,
345                       size_t buflen, struct group **result) {
346   if (result) {
347     *result       = NULL;
348   }
349   if (!grp) {
350     return -1;
351   }
352   errno           = 0;
353   struct group *g = getgrnam(name);
354   if (!g) {
355     return errno ? -1 : 0;
356   }
357   *grp            = *g;
358   if (result) {
359     *result       = grp;
360   }
361   return 0;
362 }
363 #endif
364
365 gid_t getGroupId(const char *name) {
366   struct group grbuf, *gr;
367   char *buf;
368   #ifdef _SC_GETGR_R_SIZE_MAX
369   int gr_len      = sysconf(_SC_GETGR_R_SIZE_MAX);
370   if (gr_len <= 0) {
371     gr_len        = 4096;
372   }
373   #else
374   int gr_len      = 4096;
375   #endif
376   check(buf       = malloc(gr_len));
377   if (getgrnam_r(name, &grbuf, buf, gr_len, &gr) || !gr) {
378     // Maybe, this system does not have a "nogroup" group. Substitute the
379     // group of the "nobody" user.
380     if (!strcmp(name, "nogroup")) {
381       struct passwd pwbuf, *pw;
382       #ifdef _SC_GETPW_R_SIZE_MAX
383       int pw_len  = sysconf(_SC_GETPW_R_SIZE_MAX);
384       if (pw_len <= 0) {
385         pw_len    = 4096;
386       }
387       #else
388       int pw_len  = 4096;
389       #endif
390       if (pw_len > gr_len) {
391         check(buf = realloc(buf, pw_len));
392       }
393       if (!getpwnam_r("nobody", &pwbuf, buf, pw_len, &pw) && pw) {
394         debug("Substituting \"nobody's\" primary group for \"nogroup\"");
395         gid_t gid = pw->pw_gid;
396         free(buf);
397         return gid;
398       }
399     }
400     fatal("Cannot look up group \"%s\"", name);
401   }
402   gid_t gid = gr->gr_gid;
403   free(buf);
404   return gid;
405 }
406
407 gid_t parseGroupArg(const char *arg, const char **name) {
408   char *end;
409   errno           = 0;
410   unsigned long l = strtoul(arg, &end, 10);
411   if (errno || l > INT_MAX || *end) {
412     if (name) {
413       check(*name = strdup(arg));
414     }
415     return getGroupId(arg);
416   } else {
417     if (name) {
418       *name       = getGroupName((uid_t)l);
419     }
420     return (uid_t)l;
421   }
422 }
This page took 0.098841 seconds and 5 git commands to generate.