]> andersk Git - test.git/blame - shellinabox/privileges.c
Another iteration of changes intended to deal with MacOS X specific build
[test.git] / shellinabox / privileges.c
CommitLineData
7460295f 1// privileges.c -- Manage process privileges
bc83b450 2// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
7460295f
MG
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
bdd01e84 47#include "config.h"
7460295f
MG
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
62int runAsUser = -1;
63int runAsGroup = -1;
7460295f
MG
64
65
242e6c5b
MG
66#ifndef HAVE_GETRESUID
67int 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
76int 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
85int setresuid(uid_t ruid, uid_t euid, uid_t suid) {
86 return setreuid(ruid, euid);
87}
88#endif
89
90#ifndef HAVE_SETRESGID
91int setresgid(gid_t rgid, gid_t egid, gid_t sgid) {
92 return setregid(rgid, egid);
93}
94#endif
95
d1edcc0e
MG
96static void removeGroupPrivileges(int showError) {
97 gid_t rg, eg, sg;
98 check(!getresgid(&rg, &eg, &sg));
99
7460295f
MG
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 *)"");
d1edcc0e 103
7460295f 104 if (runAsGroup >= 0) {
d1edcc0e
MG
105 uid_t ru, eu, su;
106 getresuid(&ru, &eu, &su);
107
7460295f 108 // Try to switch the user-provided group.
bc83b450 109 if ((ru && runAsGroup != (int)rg) ||
d1edcc0e
MG
110 setresgid(runAsGroup, runAsGroup, runAsGroup)) {
111 if (showError) {
7460295f 112 fatal("Only privileged users can change their group memberships");
d1edcc0e
MG
113 } else {
114 _exit(1);
7460295f
MG
115 }
116 }
117 } else {
d1edcc0e 118 if (rg) {
7460295f 119 // If we were started as a set-gid binary, drop these permissions, now.
d1edcc0e 120 check(!setresgid(rg, rg, rg));
7460295f
MG
121 } else {
122 // If we are running as root, switch to "nogroup"
d1edcc0e
MG
123 gid_t ng = getGroupId("nogroup");
124 check(!setresgid(ng, ng, ng));
7460295f
MG
125 }
126 }
127}
128
129void lowerPrivileges(void) {
d1edcc0e
MG
130 uid_t r, e, g;
131 check(!getresuid(&r, &e, &g));
132
7460295f
MG
133 // Permanently lower all group permissions. We do not actually need these,
134 // as we still have "root" user privileges in our saved-uid.
d1edcc0e 135 removeGroupPrivileges(0);
7460295f
MG
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.
bc83b450 143 if (r && runAsUser != (int)r) {
d1edcc0e
MG
144 fatal("Only privileged users can change their user id");
145 }
7460295f
MG
146 check(!setresuid(runAsUser, runAsUser, -1));
147 } else {
7460295f
MG
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
160void dropPrivileges(void) {
d1edcc0e
MG
161 uid_t r, e, s;
162 check(!getresuid(&r, &e, &s));
163
7460295f 164 // Drop all group privileges.
d1edcc0e 165 removeGroupPrivileges(1);
7460295f
MG
166
167 if (runAsUser >= 0) {
168 // Try to switch to the user-provided user id.
bc83b450 169 if ((r && runAsUser != (int)r) ||
d1edcc0e 170 setresuid(runAsUser, runAsUser, runAsUser)) {
7460295f
MG
171 fatal("Only privileged users can change their user id.");
172 }
173 } else {
7460295f
MG
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
572ac014
MG
186#ifndef HAVE_GETPWUID_R
187// This is a not-thread-safe replacement for getpwuid_r()
188#define getpwuid_r x_getpwuid_r
189static 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
7460295f
MG
210const char *getUserName(uid_t uid) {
211 struct passwd pwbuf, *pw;
212 char *buf;
572ac014 213 #ifdef _SC_GETPW_R_SIZE_MAX
7460295f 214 int len = sysconf(_SC_GETPW_R_SIZE_MAX);
29135474
MG
215 if (len <= 0) {
216 len = 4096;
217 }
572ac014
MG
218 #else
219 int len = 4096;
220 #endif
7460295f
MG
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
572ac014
MG
233#ifndef HAVE_GETPWNAM_R
234// This is a not-thread-safe replacement for getpwnam_r()
235#define getpwnam_r x_getpwnam_r
236static 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
7460295f
MG
257uid_t getUserId(const char *name) {
258 struct passwd pwbuf, *pw;
259 char *buf;
572ac014 260 #ifdef _SC_GETPW_R_SIZE_MAX
29135474
MG
261 int len = sysconf(_SC_GETPW_R_SIZE_MAX);
262 if (len <= 0) {
263 len = 4096;
264 }
572ac014
MG
265 #else
266 int len = 4096;
267 #endif
29135474 268 check(buf = malloc(len));
7460295f
MG
269 if (getpwnam_r(name, &pwbuf, buf, len, &pw) || !pw) {
270 fatal("Cannot look up user id \"%s\"", name);
271 }
29135474 272 uid_t uid = pw->pw_uid;
7460295f
MG
273 free(buf);
274 return uid;
275}
276
4aec9144 277uid_t parseUserArg(const char *arg, const char **name) {
7460295f
MG
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
572ac014
MG
294#ifndef HAVE_GETGRGID_R
295// This is a not-thread-safe replacement for getgrgid_r()
296#define getgrgid_r x_getgrgid_r
297static 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
7460295f
MG
318const char *getGroupName(gid_t gid) {
319 struct group grbuf, *gr;
320 char *buf;
572ac014 321 #ifdef _SC_GETGR_R_SIZE_MAX
7460295f 322 int len = sysconf(_SC_GETGR_R_SIZE_MAX);
29135474
MG
323 if (len <= 0) {
324 len = 4096;
325 }
572ac014
MG
326 #else
327 int len = 4096;
328 #endif
7460295f
MG
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
572ac014
MG
341#ifndef HAVE_GETGRNAM_R
342// This is a not-thread-safe replacement for getgrnam_r()
343#define getgrnam_r x_getgrnam_r
344static 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
7460295f
MG
365gid_t getGroupId(const char *name) {
366 struct group grbuf, *gr;
367 char *buf;
572ac014 368 #ifdef _SC_GETGR_R_SIZE_MAX
8bcba5ef
MG
369 int gr_len = sysconf(_SC_GETGR_R_SIZE_MAX);
370 if (gr_len <= 0) {
371 gr_len = 4096;
29135474 372 }
572ac014
MG
373 #else
374 int gr_len = 4096;
375 #endif
8bcba5ef
MG
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;
572ac014 382 #ifdef _SC_GETPW_R_SIZE_MAX
8bcba5ef
MG
383 int pw_len = sysconf(_SC_GETPW_R_SIZE_MAX);
384 if (pw_len <= 0) {
385 pw_len = 4096;
386 }
572ac014
MG
387 #else
388 int pw_len = 4096;
389 #endif
8bcba5ef
MG
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 }
7460295f
MG
400 fatal("Cannot look up group \"%s\"", name);
401 }
402 gid_t gid = gr->gr_gid;
403 free(buf);
404 return gid;
405}
406
4aec9144 407gid_t parseGroupArg(const char *arg, const char **name) {
7460295f
MG
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 1.779162 seconds and 5 git commands to generate.