]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/libstrongswan/utils/capabilities.c
ce5f550b50ca4d742c0453a7e7fb247da8ef3858
[thirdparty/strongswan.git] / src / libstrongswan / utils / capabilities.c
1 /*
2 * Copyright (C) 2012-2015 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 * Copyright (C) 2012 Martin Willi
5 * Copyright (C) 2012 revosec AG
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #include "capabilities.h"
19
20 #include <utils/debug.h>
21
22 #include <errno.h>
23 #include <string.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26
27 #ifndef WIN32
28 #include <pwd.h>
29 #include <grp.h>
30 #ifdef HAVE_PRCTL
31 # include <sys/prctl.h>
32 #endif /* HAVE_PRCTL */
33
34 #if !defined(HAVE_GETPWNAM_R) || \
35 !defined(HAVE_GETGRNAM_R) || \
36 !defined(HAVE_GETPWUID_R)
37 # include <threading/mutex.h>
38 # define EMULATE_R_FUNCS
39 #endif
40 #endif /* !WIN32 */
41
42 typedef struct private_capabilities_t private_capabilities_t;
43
44 /**
45 * Private data of an capabilities_t object.
46 */
47 struct private_capabilities_t {
48
49 /**
50 * Public capabilities_t interface.
51 */
52 capabilities_t public;
53
54 /**
55 * user ID to switch during rights dropping
56 */
57 uid_t uid;
58
59 /**
60 * group ID to switch during rights dropping
61 */
62 gid_t gid;
63
64 /**
65 * capabilities to keep
66 */
67 #ifdef CAPABILITIES_LIBCAP
68 cap_t caps;
69 #endif /* CAPABILITIES_LIBCAP */
70 #ifdef CAPABILITIES_NATIVE
71 struct __user_cap_data_struct caps[2];
72 #endif /* CAPABILITIES_NATIVE */
73
74 #ifdef EMULATE_R_FUNCS
75 /**
76 * mutex to emulate get(pw|gr)nam_r functions
77 */
78 mutex_t *mutex;
79 #endif
80 };
81
82 #ifndef WIN32
83
84 /**
85 * Returns TRUE if the current process/user is member of the given group
86 */
87 static bool has_group(gid_t group)
88 {
89 gid_t *groups;
90 long ngroups, i;
91 bool found = FALSE;
92
93 if (group == getegid())
94 { /* it's unspecified if this is part of the list below or not */
95 return TRUE;
96 }
97 ngroups = sysconf(_SC_NGROUPS_MAX);
98 if (ngroups == -1)
99 {
100 DBG1(DBG_LIB, "getting groups for current process failed: %s",
101 strerror(errno));
102 return FALSE;
103 }
104 groups = calloc(ngroups + 1, sizeof(gid_t));
105 ngroups = getgroups(ngroups, groups);
106 if (ngroups == -1)
107 {
108 DBG1(DBG_LIB, "getting groups for current process failed: %s",
109 strerror(errno));
110 free(groups);
111 return FALSE;
112 }
113 for (i = 0; i < ngroups; i++)
114 {
115 if (group == groups[i])
116 {
117 found = TRUE;
118 break;
119 }
120 }
121 free(groups);
122 return found;
123 }
124
125 /**
126 * Verify that the current process has the given capability
127 */
128 static bool has_capability(private_capabilities_t *this, u_int cap,
129 bool *ignore)
130 {
131 if (cap == CAP_CHOWN)
132 { /* if new files/UNIX sockets are created they should be owned by the
133 * configured user and group. This requires a call to chown(2). But
134 * CAP_CHOWN is not always required. */
135 if (!this->uid || geteuid() == this->uid)
136 { /* if the owner does not change CAP_CHOWN is not needed */
137 if (!this->gid || has_group(this->gid))
138 { /* the same applies if the owner is a member of the group */
139 if (ignore)
140 { /* we don't have to keep this, if requested */
141 *ignore = TRUE;
142 }
143 return TRUE;
144 }
145 }
146 }
147 #ifndef CAPABILITIES
148 /* if we can't check the actual capabilities assume only root has it */
149 return geteuid() == 0;
150 #endif /* !CAPABILITIES */
151 #ifdef CAPABILITIES_LIBCAP
152 cap_flag_value_t val;
153 cap_t caps;
154 bool ok;
155
156 caps = cap_get_proc();
157 if (!caps)
158 {
159 return FALSE;
160 }
161 ok = cap_get_flag(caps, cap, CAP_PERMITTED, &val) == 0 && val == CAP_SET;
162 cap_free(caps);
163 return ok;
164 #endif /* CAPABILITIES_LIBCAP */
165 #ifdef CAPABILITIES_NATIVE
166 struct __user_cap_header_struct header = {
167 #if defined(_LINUX_CAPABILITY_VERSION_3)
168 .version = _LINUX_CAPABILITY_VERSION_3,
169 #elif defined(_LINUX_CAPABILITY_VERSION_2)
170 .version = _LINUX_CAPABILITY_VERSION_2,
171 #elif defined(_LINUX_CAPABILITY_VERSION_1)
172 .version = _LINUX_CAPABILITY_VERSION_1,
173 #else
174 .version = _LINUX_CAPABILITY_VERSION,
175 #endif
176 };
177 struct __user_cap_data_struct caps[2];
178 int i = 0;
179
180 if (cap >= 32)
181 {
182 i++;
183 cap -= 32;
184 }
185 return capget(&header, caps) == 0 && caps[i].permitted & (1 << cap);
186 #endif /* CAPABILITIES_NATIVE */
187 }
188
189 #else /* WIN32 */
190
191 /**
192 * Verify that the current process has the given capability, dummy variant
193 */
194 static bool has_capability(private_capabilities_t *this, u_int cap,
195 bool *ignore)
196 {
197 return TRUE;
198 }
199
200 #endif /* WIN32 */
201
202 /**
203 * Keep the given capability if it is held by the current process. Returns
204 * FALSE, if this is not the case.
205 */
206 static bool keep_capability(private_capabilities_t *this, u_int cap)
207 {
208 #ifdef CAPABILITIES_LIBCAP
209 cap_set_flag(this->caps, CAP_EFFECTIVE, 1, &cap, CAP_SET);
210 cap_set_flag(this->caps, CAP_INHERITABLE, 1, &cap, CAP_SET);
211 cap_set_flag(this->caps, CAP_PERMITTED, 1, &cap, CAP_SET);
212 #endif /* CAPABILITIES_LIBCAP */
213 #ifdef CAPABILITIES_NATIVE
214 int i = 0;
215
216 if (cap >= 32)
217 {
218 i++;
219 cap -= 32;
220 }
221 this->caps[i].effective |= 1 << cap;
222 this->caps[i].permitted |= 1 << cap;
223 this->caps[i].inheritable |= 1 << cap;
224 #endif /* CAPABILITIES_NATIVE */
225 return TRUE;
226 }
227
228 METHOD(capabilities_t, keep, bool,
229 private_capabilities_t *this, u_int cap)
230 {
231 bool ignore = FALSE;
232
233 if (!has_capability(this, cap, &ignore))
234 {
235 return FALSE;
236 }
237 else if (ignore)
238 { /* don't keep capabilities that are not required */
239 return TRUE;
240 }
241 return keep_capability(this, cap);
242 }
243
244 METHOD(capabilities_t, check, bool,
245 private_capabilities_t *this, u_int cap)
246 {
247 return has_capability(this, cap, NULL);
248 }
249
250 METHOD(capabilities_t, get_uid, uid_t,
251 private_capabilities_t *this)
252 {
253 #ifdef WIN32
254 return this->uid;
255 #else
256 return this->uid ?: geteuid();
257 #endif
258 }
259
260 METHOD(capabilities_t, get_gid, gid_t,
261 private_capabilities_t *this)
262 {
263 #ifdef WIN32
264 return this->gid;
265 #else
266 return this->gid ?: getegid();
267 #endif
268 }
269
270 METHOD(capabilities_t, set_uid, void,
271 private_capabilities_t *this, uid_t uid)
272 {
273 this->uid = uid;
274 }
275
276 METHOD(capabilities_t, set_gid, void,
277 private_capabilities_t *this, gid_t gid)
278 {
279 this->gid = gid;
280 }
281
282 METHOD(capabilities_t, resolve_uid, bool,
283 private_capabilities_t *this, char *username)
284 {
285 #ifndef WIN32
286 struct passwd *pwp;
287 int err;
288
289 #ifdef HAVE_GETPWNAM_R
290 struct passwd passwd;
291 size_t buflen = 1024;
292 char *buf = NULL;
293
294 while (TRUE)
295 {
296 buf = realloc(buf, buflen);
297 err = getpwnam_r(username, &passwd, buf, buflen, &pwp);
298 if (err == ERANGE)
299 {
300 buflen *= 2;
301 continue;
302 }
303 if (pwp)
304 {
305 this->uid = pwp->pw_uid;
306 }
307 break;
308 }
309 free(buf);
310 #else /* HAVE GETPWNAM_R */
311 this->mutex->lock(this->mutex);
312 pwp = getpwnam(username);
313 if (pwp)
314 {
315 this->uid = pwp->pw_uid;
316 }
317 err = errno;
318 this->mutex->unlock(this->mutex);
319 #endif /* HAVE GETPWNAM_R */
320 if (pwp)
321 {
322 return TRUE;
323 }
324 DBG1(DBG_LIB, "resolving user '%s' failed: %s", username,
325 err ? strerror(err) : "user not found");
326 #endif /* !WIN32 */
327 return FALSE;
328 }
329
330 METHOD(capabilities_t, resolve_gid, bool,
331 private_capabilities_t *this, char *groupname)
332 {
333 #ifndef WIN32
334 struct group *grp;
335 int err;
336
337 #ifdef HAVE_GETGRNAM_R
338 struct group group;
339 size_t buflen = 1024;
340 char *buf = NULL;
341
342 while (TRUE)
343 {
344 buf = realloc(buf, buflen);
345 err = getgrnam_r(groupname, &group, buf, buflen, &grp);
346 if (err == ERANGE)
347 {
348 buflen *= 2;
349 continue;
350 }
351 if (grp)
352 {
353 this->gid = grp->gr_gid;
354 }
355 break;
356 }
357 free(buf);
358 #else /* HAVE_GETGRNAM_R */
359 this->mutex->lock(this->mutex);
360 grp = getgrnam(groupname);
361 if (grp)
362 {
363 this->gid = grp->gr_gid;
364 }
365 err = errno;
366 this->mutex->unlock(this->mutex);
367 #endif /* HAVE_GETGRNAM_R */
368 if (grp)
369 {
370 return TRUE;
371 }
372 DBG1(DBG_LIB, "resolving user '%s' failed: %s", groupname,
373 err ? strerror(err) : "group not found");
374 #endif /* !WIN32 */
375 return FALSE;
376 }
377
378 #ifndef WIN32
379 /**
380 * Initialize supplementary groups for unprivileged user
381 */
382 static bool init_supplementary_groups(private_capabilities_t *this)
383 {
384 struct passwd *pwp;
385 int res = -1;
386
387 #ifdef HAVE_GETPWUID_R
388 struct passwd pwd;
389 size_t buflen = 1024;
390 char *buf = NULL;
391
392 while (TRUE)
393 {
394 buf = realloc(buf, buflen);
395 if (getpwuid_r(this->uid, &pwd, buf, buflen, &pwp) == ERANGE)
396 {
397 buflen *= 2;
398 continue;
399 }
400 if (pwp)
401 {
402 res = initgroups(pwp->pw_name, this->gid);
403 }
404 break;
405 }
406 free(buf);
407 #else /* HAVE_GETPWUID_R */
408 this->mutex->lock(this->mutex);
409 pwp = getpwuid(this->uid);
410 if (pwp)
411 {
412 res = initgroups(pwp->pw_name, this->gid);
413 }
414 this->mutex->unlock(this->mutex);
415 #endif /* HAVE_GETPWUID_R */
416 return res == 0;
417 }
418 #endif /* WIN32 */
419
420 METHOD(capabilities_t, drop, bool,
421 private_capabilities_t *this)
422 {
423 #ifndef WIN32
424 #ifdef HAVE_PRCTL
425 prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
426 #endif
427
428 if (this->uid && !init_supplementary_groups(this))
429 {
430 DBG1(DBG_LIB, "initializing supplementary groups for %u failed",
431 this->uid);
432 return FALSE;
433 }
434 if (this->gid && setgid(this->gid) != 0)
435 {
436 DBG1(DBG_LIB, "change to unprivileged group %u failed: %s",
437 this->gid, strerror(errno));
438 return FALSE;
439 }
440 if (this->uid && setuid(this->uid) != 0)
441 {
442 DBG1(DBG_LIB, "change to unprivileged user %u failed: %s",
443 this->uid, strerror(errno));
444 return FALSE;
445 }
446
447 #ifdef CAPABILITIES_LIBCAP
448 if (cap_set_proc(this->caps) != 0)
449 {
450 DBG1(DBG_LIB, "dropping capabilities failed: %s", strerror(errno));
451 return FALSE;
452 }
453 #endif /* CAPABILITIES_LIBCAP */
454 #ifdef CAPABILITIES_NATIVE
455 struct __user_cap_header_struct header = {
456 #if defined(_LINUX_CAPABILITY_VERSION_3)
457 .version = _LINUX_CAPABILITY_VERSION_3,
458 #elif defined(_LINUX_CAPABILITY_VERSION_2)
459 .version = _LINUX_CAPABILITY_VERSION_2,
460 #elif defined(_LINUX_CAPABILITY_VERSION_1)
461 .version = _LINUX_CAPABILITY_VERSION_1,
462 #else
463 .version = _LINUX_CAPABILITY_VERSION,
464 #endif
465 };
466 if (capset(&header, this->caps) != 0)
467 {
468 DBG1(DBG_LIB, "dropping capabilities failed: %s", strerror(errno));
469 return FALSE;
470 }
471 #endif /* CAPABILITIES_NATIVE */
472 #ifdef CAPABILITIES
473 DBG1(DBG_LIB, "dropped capabilities, running as uid %u, gid %u",
474 geteuid(), getegid());
475 #endif /* CAPABILITIES */
476 #endif /*!WIN32 */
477 return TRUE;
478 }
479
480 METHOD(capabilities_t, destroy, void,
481 private_capabilities_t *this)
482 {
483 #ifdef EMULATE_R_FUNCS
484 this->mutex->destroy(this->mutex);
485 #endif /* EMULATE_R_FUNCS */
486 #ifdef CAPABILITIES_LIBCAP
487 cap_free(this->caps);
488 #endif /* CAPABILITIES_LIBCAP */
489 free(this);
490 }
491
492 /**
493 * See header
494 */
495 capabilities_t *capabilities_create()
496 {
497 private_capabilities_t *this;
498
499 INIT(this,
500 .public = {
501 .keep = _keep,
502 .check = _check,
503 .get_uid = _get_uid,
504 .get_gid = _get_gid,
505 .set_uid = _set_uid,
506 .set_gid = _set_gid,
507 .resolve_uid = _resolve_uid,
508 .resolve_gid = _resolve_gid,
509 .drop = _drop,
510 .destroy = _destroy,
511 },
512 );
513
514 #ifdef CAPABILITIES_LIBCAP
515 this->caps = cap_init();
516 #endif /* CAPABILITIES_LIBCAP */
517
518 #ifdef EMULATE_R_FUNCS
519 this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
520 #endif /* EMULATE_R_FUNCS */
521
522 return &this->public;
523 }