capabilities: initialize supplementary groups only when doing a setuid()
[strongswan.git] / src / libstrongswan / utils / capabilities.c
1 /*
2 * Copyright (C) 2012 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 <errno.h>
21 #include <string.h>
22 #include <sys/types.h>
23 #include <pwd.h>
24 #include <grp.h>
25 #include <unistd.h>
26 #ifdef HAVE_PRCTL
27 # include <sys/prctl.h>
28 #endif /* HAVE_PRCTL */
29
30 #include <utils/debug.h>
31
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
37 #endif
38
39 typedef struct private_capabilities_t private_capabilities_t;
40
41 /**
42 * Private data of an capabilities_t object.
43 */
44 struct private_capabilities_t {
45
46 /**
47 * Public capabilities_t interface.
48 */
49 capabilities_t public;
50
51 /**
52 * user ID to switch during rights dropping
53 */
54 uid_t uid;
55
56 /**
57 * group ID to switch during rights dropping
58 */
59 gid_t gid;
60
61 /**
62 * capabilities to keep
63 */
64 #ifdef CAPABILITIES_LIBCAP
65 cap_t caps;
66 #endif /* CAPABILITIES_LIBCAP */
67 #ifdef CAPABILITIES_NATIVE
68 struct __user_cap_data_struct caps[2];
69 #endif /* CAPABILITIES_NATIVE */
70
71 #ifdef EMULATE_R_FUNCS
72 /**
73 * mutex to emulate get(pw|gr)nam_r functions
74 */
75 mutex_t *mutex;
76 #endif
77 };
78
79 METHOD(capabilities_t, keep, void,
80 private_capabilities_t *this, u_int cap)
81 {
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
88 int i = 0;
89
90 if (cap >= 32)
91 {
92 i++;
93 cap -= 32;
94 }
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 */
99 }
100
101 METHOD(capabilities_t, get_uid, uid_t,
102 private_capabilities_t *this)
103 {
104 return this->uid;
105 }
106
107 METHOD(capabilities_t, get_gid, gid_t,
108 private_capabilities_t *this)
109 {
110 return this->gid;
111 }
112
113 METHOD(capabilities_t, set_uid, void,
114 private_capabilities_t *this, uid_t uid)
115 {
116 this->uid = uid;
117 }
118
119 METHOD(capabilities_t, set_gid, void,
120 private_capabilities_t *this, gid_t gid)
121 {
122 this->gid = gid;
123 }
124
125 METHOD(capabilities_t, resolve_uid, bool,
126 private_capabilities_t *this, char *username)
127 {
128 struct passwd *pwp;
129 int err;
130
131 #ifdef HAVE_GETPWNAM_R
132 struct passwd passwd;
133 char buf[1024];
134
135 err = getpwnam_r(username, &passwd, buf, sizeof(buf), &pwp);
136 if (pwp)
137 {
138 this->uid = pwp->pw_uid;
139 }
140 #else /* HAVE GETPWNAM_R */
141 this->mutex->lock(this->mutex);
142 pwp = getpwnam(username);
143 if (pwp)
144 {
145 this->uid = pwp->pw_uid;
146 }
147 err = errno;
148 this->mutex->unlock(this->mutex);
149 #endif /* HAVE GETPWNAM_R */
150 if (pwp)
151 {
152 return TRUE;
153 }
154 DBG1(DBG_LIB, "resolving user '%s' failed: %s", username,
155 err ? strerror(err) : "user not found");
156 return FALSE;
157 }
158
159 METHOD(capabilities_t, resolve_gid, bool,
160 private_capabilities_t *this, char *groupname)
161 {
162 struct group *grp;
163 int err;
164
165 #ifdef HAVE_GETGRNAM_R
166 struct group group;
167 char buf[1024];
168
169 err = getgrnam_r(groupname, &group, buf, sizeof(buf), &grp);
170 if (grp)
171 {
172 this->gid = grp->gr_gid;
173 }
174 #else /* HAVE_GETGRNAM_R */
175 this->mutex->lock(this->mutex);
176 grp = getgrnam(groupname);
177 if (grp)
178 {
179 this->gid = grp->gr_gid;
180 }
181 err = errno;
182 this->mutex->unlock(this->mutex);
183 #endif /* HAVE_GETGRNAM_R */
184 if (grp)
185 {
186 return TRUE;
187 }
188 DBG1(DBG_LIB, "resolving user '%s' failed: %s", groupname,
189 err ? strerror(err) : "group not found");
190 return FALSE;
191 }
192
193 /**
194 * Initialize supplementary groups for unprivileged user
195 */
196 static bool init_supplementary_groups(private_capabilities_t *this)
197 {
198 struct passwd *pwp;
199 int res = -1;
200
201 #ifdef HAVE_GETPWUID_R
202 struct passwd pwd;
203 char buf[1024];
204
205 if (getpwuid_r(this->uid, &pwd, buf, sizeof(buf), &pwp) == 0 && pwp)
206 {
207 res = initgroups(pwp->pw_name, this->gid);
208 }
209 #else /* HAVE_GETPWUID_R */
210 this->mutex->lock(this->mutex);
211 pwp = getpwuid(this->uid);
212 if (pwp)
213 {
214 res = initgroups(pwp->pw_name, this->gid);
215 }
216 this->mutex->unlock(this->mutex);
217 #endif /* HAVE_GETPWUID_R */
218 return res == 0;
219 }
220
221 METHOD(capabilities_t, drop, bool,
222 private_capabilities_t *this)
223 {
224 #ifdef HAVE_PRCTL
225 prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
226 #endif
227
228 if (this->uid && !init_supplementary_groups(this))
229 {
230 DBG1(DBG_LIB, "initializing supplementary groups for %u failed",
231 this->uid);
232 return FALSE;
233 }
234 if (this->gid && setgid(this->gid) != 0)
235 {
236 DBG1(DBG_LIB, "change to unprivileged group %u failed: %s",
237 this->gid, strerror(errno));
238 return FALSE;
239 }
240 if (this->uid && setuid(this->uid) != 0)
241 {
242 DBG1(DBG_LIB, "change to unprivileged user %u failed: %s",
243 this->uid, strerror(errno));
244 return FALSE;
245 }
246
247 #ifdef CAPABILITIES_LIBCAP
248 if (cap_set_proc(this->caps) != 0)
249 {
250 DBG1(DBG_LIB, "dropping capabilities failed: %s", strerror(errno));
251 return FALSE;
252 }
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,
262 #else
263 .version = _LINUX_CAPABILITY_VERSION,
264 #endif
265 };
266 if (capset(&header, this->caps) != 0)
267 {
268 DBG1(DBG_LIB, "dropping capabilities failed: %s", strerror(errno));
269 return FALSE;
270 }
271 #endif /* CAPABILITIES_NATIVE */
272 #ifdef CAPABILITIES
273 DBG1(DBG_LIB, "dropped capabilities, running as uid %u, gid %u",
274 this->uid, this->gid);
275 #endif /* CAPABILITIES */
276 return TRUE;
277 }
278
279 METHOD(capabilities_t, destroy, void,
280 private_capabilities_t *this)
281 {
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 */
288 free(this);
289 }
290
291 /**
292 * See header
293 */
294 capabilities_t *capabilities_create()
295 {
296 private_capabilities_t *this;
297
298 INIT(this,
299 .public = {
300 .keep = _keep,
301 .get_uid = _get_uid,
302 .get_gid = _get_gid,
303 .set_uid = _set_uid,
304 .set_gid = _set_gid,
305 .resolve_uid = _resolve_uid,
306 .resolve_gid = _resolve_gid,
307 .drop = _drop,
308 .destroy = _destroy,
309 },
310 );
311
312 #ifdef CAPABILITIES
313 #ifdef CAPABILITIES_LIBCAP
314 this->caps = cap_init();
315 #endif /* CAPABILITIES_LIBCAP */
316 if (lib->leak_detective)
317 {
318 keep(this, CAP_SYS_NICE);
319 }
320 #endif /* CAPABILITIES */
321
322 #ifdef EMULATE_R_FUNCS
323 this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
324 #endif /* EMULATE_R_FUNCS */
325
326 return &this->public;
327 }