2 * Copyright (C) 2012-2015 Tobias Brunner
3 * Copyright (C) 2012 Martin Willi
5 * Copyright (C) secunet Security Networks AG
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>.
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
18 #include "capabilities.h"
20 #include <utils/debug.h>
24 #include <sys/types.h>
31 # include <sys/prctl.h>
32 #endif /* HAVE_PRCTL */
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
42 typedef struct private_capabilities_t private_capabilities_t
;
45 * Private data of an capabilities_t object.
47 struct private_capabilities_t
{
50 * Public capabilities_t interface.
52 capabilities_t
public;
55 * user ID to switch during rights dropping
60 * group ID to switch during rights dropping
65 * capabilities to keep
67 #ifdef CAPABILITIES_LIBCAP
69 #endif /* CAPABILITIES_LIBCAP */
70 #ifdef CAPABILITIES_NATIVE
71 struct __user_cap_data_struct caps
[2];
72 #endif /* CAPABILITIES_NATIVE */
74 #ifdef EMULATE_R_FUNCS
76 * mutex to emulate get(pw|gr)nam_r functions
85 * Returns TRUE if the current process/user is member of the given group
87 static bool has_group(gid_t group
)
93 if (group
== getegid())
94 { /* it's unspecified if this is part of the list below or not */
97 ngroups
= sysconf(_SC_NGROUPS_MAX
);
100 DBG1(DBG_LIB
, "getting groups for current process failed: %s",
104 groups
= calloc(ngroups
+ 1, sizeof(gid_t
));
105 ngroups
= getgroups(ngroups
, groups
);
108 DBG1(DBG_LIB
, "getting groups for current process failed: %s",
113 for (i
= 0; i
< ngroups
; i
++)
115 if (group
== groups
[i
])
126 * Verify that the current process has the given capability
128 static bool has_capability(private_capabilities_t
*this, u_int cap
,
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 */
140 { /* we don't have to keep this, if requested */
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
;
156 caps
= cap_get_proc();
161 ok
= cap_get_flag(caps
, cap
, CAP_PERMITTED
, &val
) == 0 && val
== CAP_SET
;
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
,
174 .version
= _LINUX_CAPABILITY_VERSION
,
177 struct __user_cap_data_struct caps
[2];
185 return capget(&header
, caps
) == 0 && caps
[i
].permitted
& (1 << cap
);
186 #endif /* CAPABILITIES_NATIVE */
192 * Verify that the current process has the given capability, dummy variant
194 static bool has_capability(private_capabilities_t
*this, u_int cap
,
203 * Keep the given capability if it is held by the current process. Returns
204 * FALSE, if this is not the case.
206 static bool keep_capability(private_capabilities_t
*this, u_int cap
)
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
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 */
228 METHOD(capabilities_t
, keep
, bool,
229 private_capabilities_t
*this, u_int cap
)
233 if (!has_capability(this, cap
, &ignore
))
238 { /* don't keep capabilities that are not required */
241 return keep_capability(this, cap
);
244 METHOD(capabilities_t
, check
, bool,
245 private_capabilities_t
*this, u_int cap
)
247 return has_capability(this, cap
, NULL
);
250 METHOD(capabilities_t
, get_uid
, uid_t
,
251 private_capabilities_t
*this)
256 return this->uid
?: geteuid();
260 METHOD(capabilities_t
, get_gid
, gid_t
,
261 private_capabilities_t
*this)
266 return this->gid
?: getegid();
270 METHOD(capabilities_t
, set_uid
, void,
271 private_capabilities_t
*this, uid_t uid
)
276 METHOD(capabilities_t
, set_gid
, void,
277 private_capabilities_t
*this, gid_t gid
)
282 METHOD(capabilities_t
, resolve_uid
, bool,
283 private_capabilities_t
*this, char *username
)
289 #ifdef HAVE_GETPWNAM_R
290 struct passwd passwd
;
291 size_t buflen
= 1024;
296 buf
= realloc(buf
, buflen
);
297 err
= getpwnam_r(username
, &passwd
, buf
, buflen
, &pwp
);
305 this->uid
= pwp
->pw_uid
;
310 #else /* HAVE GETPWNAM_R */
311 this->mutex
->lock(this->mutex
);
312 pwp
= getpwnam(username
);
315 this->uid
= pwp
->pw_uid
;
318 this->mutex
->unlock(this->mutex
);
319 #endif /* HAVE GETPWNAM_R */
324 DBG1(DBG_LIB
, "resolving user '%s' failed: %s", username
,
325 err
? strerror(err
) : "user not found");
330 METHOD(capabilities_t
, resolve_gid
, bool,
331 private_capabilities_t
*this, char *groupname
)
337 #ifdef HAVE_GETGRNAM_R
339 size_t buflen
= 1024;
344 buf
= realloc(buf
, buflen
);
345 err
= getgrnam_r(groupname
, &group
, buf
, buflen
, &grp
);
353 this->gid
= grp
->gr_gid
;
358 #else /* HAVE_GETGRNAM_R */
359 this->mutex
->lock(this->mutex
);
360 grp
= getgrnam(groupname
);
363 this->gid
= grp
->gr_gid
;
366 this->mutex
->unlock(this->mutex
);
367 #endif /* HAVE_GETGRNAM_R */
372 DBG1(DBG_LIB
, "resolving user '%s' failed: %s", groupname
,
373 err
? strerror(err
) : "group not found");
380 * Initialize supplementary groups for unprivileged user
382 static bool init_supplementary_groups(private_capabilities_t
*this)
387 #ifdef HAVE_GETPWUID_R
389 size_t buflen
= 1024;
394 buf
= realloc(buf
, buflen
);
395 if (getpwuid_r(this->uid
, &pwd
, buf
, buflen
, &pwp
) == ERANGE
)
402 res
= initgroups(pwp
->pw_name
, this->gid
);
407 #else /* HAVE_GETPWUID_R */
408 this->mutex
->lock(this->mutex
);
409 pwp
= getpwuid(this->uid
);
412 res
= initgroups(pwp
->pw_name
, this->gid
);
414 this->mutex
->unlock(this->mutex
);
415 #endif /* HAVE_GETPWUID_R */
420 METHOD(capabilities_t
, drop
, bool,
421 private_capabilities_t
*this)
425 if (has_capability(this, CAP_SETPCAP
, NULL
))
427 prctl(PR_SET_KEEPCAPS
, 1, 0, 0, 0);
431 if (this->uid
&& !init_supplementary_groups(this))
433 DBG1(DBG_LIB
, "initializing supplementary groups for %u failed",
437 if (this->gid
&& setgid(this->gid
) != 0)
439 DBG1(DBG_LIB
, "change to unprivileged group %u failed: %s",
440 this->gid
, strerror(errno
));
443 if (this->uid
&& setuid(this->uid
) != 0)
445 DBG1(DBG_LIB
, "change to unprivileged user %u failed: %s",
446 this->uid
, strerror(errno
));
450 #ifdef CAPABILITIES_LIBCAP
451 if (cap_set_proc(this->caps
) != 0)
453 DBG1(DBG_LIB
, "dropping capabilities failed: %s", strerror(errno
));
456 #endif /* CAPABILITIES_LIBCAP */
457 #ifdef CAPABILITIES_NATIVE
458 struct __user_cap_header_struct header
= {
459 #if defined(_LINUX_CAPABILITY_VERSION_3)
460 .version
= _LINUX_CAPABILITY_VERSION_3
,
461 #elif defined(_LINUX_CAPABILITY_VERSION_2)
462 .version
= _LINUX_CAPABILITY_VERSION_2
,
463 #elif defined(_LINUX_CAPABILITY_VERSION_1)
464 .version
= _LINUX_CAPABILITY_VERSION_1
,
466 .version
= _LINUX_CAPABILITY_VERSION
,
469 if (capset(&header
, this->caps
) != 0)
471 DBG1(DBG_LIB
, "dropping capabilities failed: %s", strerror(errno
));
474 #endif /* CAPABILITIES_NATIVE */
476 DBG1(DBG_LIB
, "dropped capabilities, running as uid %u, gid %u",
477 geteuid(), getegid());
478 #endif /* CAPABILITIES */
483 METHOD(capabilities_t
, destroy
, void,
484 private_capabilities_t
*this)
486 #ifdef EMULATE_R_FUNCS
487 this->mutex
->destroy(this->mutex
);
488 #endif /* EMULATE_R_FUNCS */
489 #ifdef CAPABILITIES_LIBCAP
490 cap_free(this->caps
);
491 #endif /* CAPABILITIES_LIBCAP */
498 capabilities_t
*capabilities_create()
500 private_capabilities_t
*this;
510 .resolve_uid
= _resolve_uid
,
511 .resolve_gid
= _resolve_gid
,
517 #ifdef CAPABILITIES_LIBCAP
518 this->caps
= cap_init();
519 #endif /* CAPABILITIES_LIBCAP */
521 #ifdef EMULATE_R_FUNCS
522 this->mutex
= mutex_create(MUTEX_TYPE_DEFAULT
);
523 #endif /* EMULATE_R_FUNCS */
525 return &this->public;