2 * Copyright (C) 2012 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 * Copyright (C) 2012 Martin Willi
5 * Copyright (C) 2012 revosec 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"
22 #include <sys/types.h>
27 # include <sys/prctl.h>
28 #endif /* HAVE_PRCTL */
30 #include <utils/debug.h>
32 #if !defined(HAVE_GETPWNAM_R) || \
33 !defined(HAVE_GETGRNAM_R) || \
34 !defined(HAVE_GETPWUID_R)
35 # include <threading/mutex.h>
36 # define EMULATE_R_FUNCS
39 typedef struct private_capabilities_t private_capabilities_t
;
42 * Private data of an capabilities_t object.
44 struct private_capabilities_t
{
47 * Public capabilities_t interface.
49 capabilities_t
public;
52 * user ID to switch during rights dropping
57 * group ID to switch during rights dropping
62 * capabilities to keep
64 #ifdef CAPABILITIES_LIBCAP
66 #endif /* CAPABILITIES_LIBCAP */
67 #ifdef CAPABILITIES_NATIVE
68 struct __user_cap_data_struct caps
[2];
69 #endif /* CAPABILITIES_NATIVE */
71 #ifdef EMULATE_R_FUNCS
73 * mutex to emulate get(pw|gr)nam_r functions
79 METHOD(capabilities_t
, keep
, void,
80 private_capabilities_t
*this, u_int cap
)
82 #ifdef CAPABILITIES_LIBCAP
83 cap_set_flag(this->caps
, CAP_EFFECTIVE
, 1, &cap
, CAP_SET
);
84 cap_set_flag(this->caps
, CAP_INHERITABLE
, 1, &cap
, CAP_SET
);
85 cap_set_flag(this->caps
, CAP_PERMITTED
, 1, &cap
, CAP_SET
);
86 #endif /* CAPABILITIES_LIBCAP */
87 #ifdef CAPABILITIES_NATIVE
95 this->caps
[i
].effective
|= 1 << cap
;
96 this->caps
[i
].permitted
|= 1 << cap
;
97 this->caps
[i
].inheritable
|= 1 << cap
;
98 #endif /* CAPABILITIES_NATIVE */
101 METHOD(capabilities_t
, get_uid
, uid_t
,
102 private_capabilities_t
*this)
107 METHOD(capabilities_t
, get_gid
, gid_t
,
108 private_capabilities_t
*this)
113 METHOD(capabilities_t
, set_uid
, void,
114 private_capabilities_t
*this, uid_t uid
)
119 METHOD(capabilities_t
, set_gid
, void,
120 private_capabilities_t
*this, gid_t gid
)
125 METHOD(capabilities_t
, resolve_uid
, bool,
126 private_capabilities_t
*this, char *username
)
131 #ifdef HAVE_GETPWNAM_R
132 struct passwd passwd
;
135 err
= getpwnam_r(username
, &passwd
, buf
, sizeof(buf
), &pwp
);
138 this->uid
= pwp
->pw_uid
;
140 #else /* HAVE GETPWNAM_R */
141 this->mutex
->lock(this->mutex
);
142 pwp
= getpwnam(username
);
145 this->uid
= pwp
->pw_uid
;
148 this->mutex
->unlock(this->mutex
);
149 #endif /* HAVE GETPWNAM_R */
154 DBG1(DBG_LIB
, "resolving user '%s' failed: %s", username
,
155 err ?
strerror(err
) : "user not found");
159 METHOD(capabilities_t
, resolve_gid
, bool,
160 private_capabilities_t
*this, char *groupname
)
165 #ifdef HAVE_GETGRNAM_R
169 err
= getgrnam_r(groupname
, &group
, buf
, sizeof(buf
), &grp
);
172 this->gid
= grp
->gr_gid
;
174 #else /* HAVE_GETGRNAM_R */
175 this->mutex
->lock(this->mutex
);
176 grp
= getgrnam(groupname
);
179 this->gid
= grp
->gr_gid
;
182 this->mutex
->unlock(this->mutex
);
183 #endif /* HAVE_GETGRNAM_R */
188 DBG1(DBG_LIB
, "resolving user '%s' failed: %s", groupname
,
189 err ?
strerror(err
) : "group not found");
194 * Initialize supplementary groups for unprivileged user
196 static bool init_supplementary_groups(private_capabilities_t
*this)
201 #ifdef HAVE_GETPWUID_R
205 if (getpwuid_r(this->uid
, &pwd
, buf
, sizeof(buf
), &pwp
) == 0 && pwp
)
207 res
= initgroups(pwp
->pw_name
, this->gid
);
209 #else /* HAVE_GETPWUID_R */
210 this->mutex
->lock(this->mutex
);
211 pwp
= getpwuid(this->uid
);
214 res
= initgroups(pwp
->pw_name
, this->gid
);
216 this->mutex
->unlock(this->mutex
);
217 #endif /* HAVE_GETPWUID_R */
221 METHOD(capabilities_t
, drop
, bool,
222 private_capabilities_t
*this)
225 prctl(PR_SET_KEEPCAPS
, 1, 0, 0, 0);
228 if (!init_supplementary_groups(this))
230 DBG1(DBG_LIB
, "initializing supplementary groups for %u failed",
234 if (this->gid
&& setgid(this->gid
) != 0)
236 DBG1(DBG_LIB
, "change to unprivileged group %u failed: %s",
237 this->gid
, strerror(errno
));
240 if (this->uid
&& setuid(this->uid
) != 0)
242 DBG1(DBG_LIB
, "change to unprivileged user %u failed: %s",
243 this->uid
, strerror(errno
));
247 #ifdef CAPABILITIES_LIBCAP
248 if (cap_set_proc(this->caps
) != 0)
250 DBG1(DBG_LIB
, "dropping capabilities failed: %s", strerror(errno
));
253 #endif /* CAPABILITIES_LIBCAP */
254 #ifdef CAPABILITIES_NATIVE
255 struct __user_cap_header_struct header
= {
256 #if defined(_LINUX_CAPABILITY_VERSION_3)
257 .version
= _LINUX_CAPABILITY_VERSION_3
,
258 #elif defined(_LINUX_CAPABILITY_VERSION_2)
259 .version
= _LINUX_CAPABILITY_VERSION_2
,
260 #elif defined(_LINUX_CAPABILITY_VERSION_1)
261 .version
= _LINUX_CAPABILITY_VERSION_1
,
263 .version
= _LINUX_CAPABILITY_VERSION
,
266 if (capset(&header
, this->caps
) != 0)
268 DBG1(DBG_LIB
, "dropping capabilities failed: %s", strerror(errno
));
271 #endif /* CAPABILITIES_NATIVE */
273 DBG1(DBG_LIB
, "dropped capabilities, running as uid %u, gid %u",
274 this->uid
, this->gid
);
275 #endif /* CAPABILITIES */
279 METHOD(capabilities_t
, destroy
, void,
280 private_capabilities_t
*this)
282 #ifdef EMULATE_R_FUNCS
283 this->mutex
->destroy(this->mutex
);
284 #endif /* EMULATE_R_FUNCS */
285 #ifdef CAPABILITIES_LIBCAP
286 cap_free(this->caps
);
287 #endif /* CAPABILITIES_LIBCAP */
294 capabilities_t
*capabilities_create()
296 private_capabilities_t
*this;
305 .resolve_uid
= _resolve_uid
,
306 .resolve_gid
= _resolve_gid
,
313 #ifdef CAPABILITIES_LIBCAP
314 this->caps
= cap_init();
315 #endif /* CAPABILITIES_LIBCAP */
316 if (lib
->leak_detective
)
318 keep(this, CAP_SYS_NICE
);
320 #endif /* CAPABILITIES */
322 #ifdef EMULATE_R_FUNCS
323 this->mutex
= mutex_create(MUTEX_TYPE_DEFAULT
);
324 #endif /* EMULATE_R_FUNCS */
326 return &this->public;