The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
cap.c
Go to the documentation of this file.
1/*
2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2 of the
4 * License as published by the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
10 *
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
14 */
15
16/** Functions to deal with Linux capabilities
17 *
18 * @file src/lib/util/cap.c
19 *
20 * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
21 * @copyright 2020 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
22 * @copyright 2020 The FreeRADIUS Server Project.
23 */
24RCSID("$Id: 1c0c1f48a6857d41442f86ba3d80cbebf960d6fe $")
25
26#ifdef HAVE_CAPABILITY_H
27#include <freeradius-devel/util/cap.h>
28#include <freeradius-devel/util/strerror.h>
29#include <freeradius-devel/util/syserror.h>
30#include <freeradius-devel/util/table.h>
31
32#include <pthread.h>
33
34static fr_table_num_sorted_t const cap_set_table[] = {
35 { L("effective"), CAP_EFFECTIVE },
36 { L("inherited"), CAP_INHERITABLE },
37 { L("permitted"), CAP_PERMITTED }
38};
39static size_t cap_set_table_len = NUM_ELEMENTS(cap_set_table);
40
41/** Ensure we don't loose updates, and the threads have a consistent view of the capability set
42 *
43 * This is needed because capabilities are process wide, but may be modified by multiple threads.
44 */
45static pthread_mutex_t cap_mutex = PTHREAD_MUTEX_INITIALIZER;
46
47/** Return whether a given capability is in a capabilities set
48 *
49 * @param[in] cap to query.
50 * @param[in] set One of the following sets of capabilities:
51 * - CAP_EFFECTIVE capabilities we currently have.
52 * - CAP_INHERITABLE capabilities inherited across exec.
53 * - CAP_PERMITTED capabilities we can request.
54 * @return
55 * - true if CAP_SET (enabled) in the specified set.
56 * - false if CAP_CLEAR (disabled) in the specified set.
57 */
58bool fr_cap_is_enabled(cap_value_t cap, cap_flag_t set)
59{
60 cap_t caps;
61 cap_flag_value_t state = CAP_CLEAR;
62
63 pthread_mutex_lock(&cap_mutex);
64
65 caps = cap_get_proc();
66 if (!caps) {
67 fr_strerror_printf("Failed getting process capabilities: %s", fr_syserror(errno));
68 goto done;
69 }
70
71 if (cap_get_flag(caps, cap, CAP_EFFECTIVE, &state) < 0) {
72 char *cap_name = cap_to_name(cap);
73 fr_strerror_printf("Failed getting %s %s state from working set: %s",
74 cap_name,
75 fr_table_str_by_value(cap_set_table, set, "<INVALID>"),
76 fr_syserror(errno));
77 cap_free(cap_name);
78 goto done;
79 }
80
81done:
82 pthread_mutex_unlock(&cap_mutex);
83
84 if (caps) cap_free(caps);
85
86 return (state == CAP_SET);
87}
88
89/** Add a CAP_* to the effective or inheritable set
90 *
91 * A negative return from this function should NOT always
92 * be interpreted as an error. The server MAY already be running as
93 * root, OR the system may not support CAP_*. It is almost
94 * always better to use a negative return value to print a warning
95 * message as to why CAP_* was not set.
96 *
97 * @param[in] cap to enable.
98 * @param[in] set One of the following sets of capabilities:
99 * - CAP_EFFECTIVE capabilities we currently have.
100 * - CAP_INHERITABLE capabilities inherited across exec.
101 * @return
102 * - <0 on "cannot set it"
103 * - 0 on "can set it (or it was already set)"
104 */
105int fr_cap_enable(cap_value_t cap, cap_flag_t set)
106{
107 int ret = -1;
108 cap_t caps = NULL;
109 cap_flag_value_t state;
110
111 /*
112 * This function may be called by multiple
113 * threads each binding to their own network
114 * sockets. There's no guarantee that those
115 * threads will be requesting the same
116 * capabilities at the same time, so we could
117 * suffer from a lost update problem.
118 */
119 pthread_mutex_lock(&cap_mutex);
120
121 if (set == CAP_PERMITTED) {
122 fr_strerror_const("Can't modify permitted capabilities");
123 goto done;
124 }
125
126 caps = cap_get_proc();
127 if (!caps) {
128 fr_strerror_printf("Failed getting process capabilities: %s", fr_syserror(errno));
129 goto done;
130 }
131
132 if (cap_get_flag(caps, cap, CAP_PERMITTED, &state) < 0) {
133 char *cap_name = cap_to_name(cap);
134 fr_strerror_printf("Failed getting %s permitted state: %s", cap_name, fr_syserror(errno));
135 cap_free(cap_name);
136 goto done;
137 }
138
139 /*
140 * We're not permitted to set the capability
141 */
142 if (state == CAP_CLEAR) {
143 char *cap_name = cap_to_name(cap);
144 /*
145 * Messages printed in the inverse order
146 * to the order they're printed.
147 */
148 fr_strerror_printf("Use \"setcap %s+ep <path_to_binary>\" to grant the %s capability",
149 cap_name, cap_name);
150 cap_free(cap_name);
151 goto done;
152 }
153
154 if (cap_get_flag(caps, cap, set, &state) < 0) {
155 char *cap_name = cap_to_name(cap);
156 fr_strerror_printf("Failed getting %s %s state from working set: %s",
157 cap_name,
158 fr_table_str_by_value(cap_set_table, set, "<INVALID>"),
159 fr_syserror(errno));
160 cap_free(cap_name);
161 goto done;
162 }
163
164 /*
165 * Permitted bit is high but the capability
166 * isn't in the specified set, see if we can
167 * fix that.
168 */
169 if (state == CAP_CLEAR) {
170 cap_value_t const to_set[] = {
171 cap
172 };
173
174 if (cap_set_flag(caps, set, NUM_ELEMENTS(to_set), to_set, CAP_SET) < 0) {
175 char *cap_name = cap_to_name(cap);
176 fr_strerror_printf("Failed setting %s %s state in working set: %s",
177 cap_name,
178 fr_table_str_by_value(cap_set_table, set, "<INVALID>"),
179 fr_syserror(errno));
180 cap_free(cap_name);
181 goto done;
182 }
183
184 if (cap_set_proc(caps) < 0) {
185 char *cap_name = cap_to_name(cap);
186 fr_strerror_printf("Failed setting %s %s state: %s",
187 cap_name,
188 fr_table_str_by_value(cap_set_table, set, "<INVALID>"),
189 fr_syserror(errno));
190 cap_free(cap_name);
191 goto done;
192 }
193
194 ret = 0;
195 /*
196 * It's already in the effective set
197 */
198 } else if (state == CAP_SET) {
199 ret = 0;
200 }
201
202done:
203 pthread_mutex_unlock(&cap_mutex);
204
205 if (caps) cap_free(caps);
206
207 return ret;
208}
209
210/** Remove a CAP_* from the permitted, effective or inheritable set
211 *
212 * @param[in] cap to disable.
213 * @param[in] set One of the following sets of capabilities:
214 * - CAP_EFFECTIVE capabilities we currently have.
215 * - CAP_INHERITABLE capabilities inherited across exec.
216 * - CAP_PERMITTED capabilities we can request.
217 * @return
218 * - <0 on "cannot unset it"
219 * - 0 on "unset it (or it was already set)"
220 */
221int fr_cap_disable(cap_value_t cap, cap_flag_t set)
222{
223 int ret = -1;
224 cap_t caps;
225 cap_flag_value_t state;
226
227 /*
228 * This function may be called by multiple
229 * threads each binding to their own network
230 * sockets. There's no guarantee that those
231 * threads will be requesting the same
232 * capabilities at the same time, so we could
233 * suffer from a lost update problem.
234 */
235 pthread_mutex_lock(&cap_mutex);
236
237 caps = cap_get_proc();
238 if (!caps) {
239 fr_strerror_printf("Failed getting process capabilities: %s", fr_syserror(errno));
240 goto done;
241 }
242
243 if (cap_get_flag(caps, cap, set, &state) < 0) {
244 char *cap_name = cap_to_name(cap);
245 fr_strerror_printf("Failed getting %s %s state from working set: %s",
246 cap_name,
247 fr_table_str_by_value(cap_set_table, set, "<INVALID>"),
248 fr_syserror(errno));
249 cap_free(cap_name);
250 goto done;
251 }
252
253 if (state == CAP_SET) {
254 if (cap_clear_flag(caps, set) < 0) {
255 char *cap_name = cap_to_name(cap);
256 fr_strerror_printf("Failed clearing %s %s state in working set: %s",
257 cap_name,
258 fr_table_str_by_value(cap_set_table, set, "<INVALID>"),
259 fr_syserror(errno));
260 cap_free(cap_name);
261 goto done;
262 }
263
264 if (cap_set_proc(caps) < 0) {
265 char *cap_name = cap_to_name(cap);
266 fr_strerror_printf("Failed setting %s %s state: %s",
267 cap_name,
268 fr_table_str_by_value(cap_set_table, set, "<INVALID>"),
269 fr_syserror(errno));
270 cap_free(cap_name);
271 goto done;
272 }
273
274 ret = 0;
275 } else {
276 ret = 0;
277 }
278
279done:
280 pthread_mutex_unlock(&cap_mutex);
281
282 if (caps) cap_free(caps);
283
284 return ret;
285}
286
287/** Snapshot the processes' current capability set, printing it to a string
288 *
289 * @param[in] ctx Where to allocate the string.
290 * @param[out] out The string containing the capabilities.
291 */
292ssize_t fr_cap_set_to_str(TALLOC_CTX *ctx, char **out)
293{
294 cap_t caps = NULL;
295 char *tmp;
296 ssize_t slen;
297
298 caps = cap_get_proc();
299 if (unlikely(!caps)) {
300 fr_strerror_printf("Failed retrieving process capabilities: %s", fr_syserror(errno));
301 return -1;
302 }
303 tmp = cap_to_text(caps, &slen);
304 cap_free(caps);
305 if (unlikely(!tmp)) {
306 fr_strerror_printf("Failed converting capabilities to string: %s", fr_syserror(errno));
307 return -1;
308 }
309
310 *out = talloc_bstrndup(ctx, tmp, slen);
311 cap_free(tmp);
312
313 return slen;
314}
315#endif /* HAVE_CAPABILITY_H */
#define RCSID(id)
Definition build.h:485
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define unlikely(_x)
Definition build.h:383
#define NUM_ELEMENTS(_t)
Definition build.h:339
long int ssize_t
static bool done
Definition radclient.c:81
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:560
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
static size_t char ** out
Definition value.h:1012