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: f396b80d37df6781361d0f2e963ad20101793c8d $")
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#include <freeradius-devel/util/talloc.h>
32
33#include <pthread.h>
34
35static fr_table_num_sorted_t const cap_set_table[] = {
36 { L("effective"), CAP_EFFECTIVE },
37 { L("inherited"), CAP_INHERITABLE },
38 { L("permitted"), CAP_PERMITTED }
39};
40static size_t cap_set_table_len = NUM_ELEMENTS(cap_set_table);
41
42/** Ensure we don't loose updates, and the threads have a consistent view of the capability set
43 *
44 * This is needed because capabilities are process wide, but may be modified by multiple threads.
45 */
46static pthread_mutex_t cap_mutex = PTHREAD_MUTEX_INITIALIZER;
47
48/** Return whether a given capability is in a capabilities set
49 *
50 * @param[in] cap to query.
51 * @param[in] set One of the following sets of capabilities:
52 * - CAP_EFFECTIVE capabilities we currently have.
53 * - CAP_INHERITABLE capabilities inherited across exec.
54 * - CAP_PERMITTED capabilities we can request.
55 * @return
56 * - true if CAP_SET (enabled) in the specified set.
57 * - false if CAP_CLEAR (disabled) in the specified set.
58 */
59bool fr_cap_is_enabled(cap_value_t cap, cap_flag_t set)
60{
61 cap_t caps;
62 cap_flag_value_t state = CAP_CLEAR;
63
64 pthread_mutex_lock(&cap_mutex);
65
66 caps = cap_get_proc();
67 if (!caps) {
68 fr_strerror_printf("Failed getting process capabilities: %s", fr_syserror(errno));
69 goto done;
70 }
71
72 if (cap_get_flag(caps, cap, CAP_EFFECTIVE, &state) < 0) {
73 char *cap_name = cap_to_name(cap);
74 fr_strerror_printf("Failed getting %s %s state from working set: %s",
75 cap_name,
76 fr_table_str_by_value(cap_set_table, set, "<INVALID>"),
77 fr_syserror(errno));
78 cap_free(cap_name);
79 goto done;
80 }
81
82done:
83 pthread_mutex_unlock(&cap_mutex);
84
85 if (caps) cap_free(caps);
86
87 return (state == CAP_SET);
88}
89
90/** Add a CAP_* to the effective or inheritable set
91 *
92 * A negative return from this function should NOT always
93 * be interpreted as an error. The server MAY already be running as
94 * root, OR the system may not support CAP_*. It is almost
95 * always better to use a negative return value to print a warning
96 * message as to why CAP_* was not set.
97 *
98 * @param[in] cap to enable.
99 * @param[in] set One of the following sets of capabilities:
100 * - CAP_EFFECTIVE capabilities we currently have.
101 * - CAP_INHERITABLE capabilities inherited across exec.
102 * @return
103 * - <0 on "cannot set it"
104 * - 0 on "can set it (or it was already set)"
105 */
106int fr_cap_enable(cap_value_t cap, cap_flag_t set)
107{
108 int ret = -1;
109 cap_t caps = NULL;
110 cap_flag_value_t state;
111
112 /*
113 * This function may be called by multiple
114 * threads each binding to their own network
115 * sockets. There's no guarantee that those
116 * threads will be requesting the same
117 * capabilities at the same time, so we could
118 * suffer from a lost update problem.
119 */
120 pthread_mutex_lock(&cap_mutex);
121
122 if (set == CAP_PERMITTED) {
123 fr_strerror_const("Can't modify permitted capabilities");
124 goto done;
125 }
126
127 caps = cap_get_proc();
128 if (!caps) {
129 fr_strerror_printf("Failed getting process capabilities: %s", fr_syserror(errno));
130 goto done;
131 }
132
133 if (cap_get_flag(caps, cap, CAP_PERMITTED, &state) < 0) {
134 char *cap_name = cap_to_name(cap);
135 fr_strerror_printf("Failed getting %s permitted state: %s", cap_name, fr_syserror(errno));
136 cap_free(cap_name);
137 goto done;
138 }
139
140 /*
141 * We're not permitted to set the capability
142 */
143 if (state == CAP_CLEAR) {
144 char *cap_name = cap_to_name(cap);
145 /*
146 * Messages printed in the inverse order
147 * to the order they're printed.
148 */
149 fr_strerror_printf("Use \"setcap %s+ep <path_to_binary>\" to grant the %s capability",
150 cap_name, cap_name);
151 cap_free(cap_name);
152 goto done;
153 }
154
155 if (cap_get_flag(caps, cap, set, &state) < 0) {
156 char *cap_name = cap_to_name(cap);
157 fr_strerror_printf("Failed getting %s %s state from working set: %s",
158 cap_name,
159 fr_table_str_by_value(cap_set_table, set, "<INVALID>"),
160 fr_syserror(errno));
161 cap_free(cap_name);
162 goto done;
163 }
164
165 /*
166 * Permitted bit is high but the capability
167 * isn't in the specified set, see if we can
168 * fix that.
169 */
170 if (state == CAP_CLEAR) {
171 cap_value_t const to_set[] = {
172 cap
173 };
174
175 if (cap_set_flag(caps, set, NUM_ELEMENTS(to_set), to_set, CAP_SET) < 0) {
176 char *cap_name = cap_to_name(cap);
177 fr_strerror_printf("Failed setting %s %s state in working set: %s",
178 cap_name,
179 fr_table_str_by_value(cap_set_table, set, "<INVALID>"),
180 fr_syserror(errno));
181 cap_free(cap_name);
182 goto done;
183 }
184
185 if (cap_set_proc(caps) < 0) {
186 char *cap_name = cap_to_name(cap);
187 fr_strerror_printf("Failed setting %s %s state: %s",
188 cap_name,
189 fr_table_str_by_value(cap_set_table, set, "<INVALID>"),
190 fr_syserror(errno));
191 cap_free(cap_name);
192 goto done;
193 }
194
195 ret = 0;
196 /*
197 * It's already in the effective set
198 */
199 } else if (state == CAP_SET) {
200 ret = 0;
201 }
202
203done:
204 pthread_mutex_unlock(&cap_mutex);
205
206 if (caps) cap_free(caps);
207
208 return ret;
209}
210
211/** Remove a CAP_* from the permitted, effective or inheritable set
212 *
213 * @param[in] cap to disable.
214 * @param[in] set One of the following sets of capabilities:
215 * - CAP_EFFECTIVE capabilities we currently have.
216 * - CAP_INHERITABLE capabilities inherited across exec.
217 * - CAP_PERMITTED capabilities we can request.
218 * @return
219 * - <0 on "cannot unset it"
220 * - 0 on "unset it (or it was already set)"
221 */
222int fr_cap_disable(cap_value_t cap, cap_flag_t set)
223{
224 int ret = -1;
225 cap_t caps;
226 cap_flag_value_t state;
227
228 /*
229 * This function may be called by multiple
230 * threads each binding to their own network
231 * sockets. There's no guarantee that those
232 * threads will be requesting the same
233 * capabilities at the same time, so we could
234 * suffer from a lost update problem.
235 */
236 pthread_mutex_lock(&cap_mutex);
237
238 caps = cap_get_proc();
239 if (!caps) {
240 fr_strerror_printf("Failed getting process capabilities: %s", fr_syserror(errno));
241 goto done;
242 }
243
244 if (cap_get_flag(caps, cap, set, &state) < 0) {
245 char *cap_name = cap_to_name(cap);
246 fr_strerror_printf("Failed getting %s %s state from working set: %s",
247 cap_name,
248 fr_table_str_by_value(cap_set_table, set, "<INVALID>"),
249 fr_syserror(errno));
250 cap_free(cap_name);
251 goto done;
252 }
253
254 if (state == CAP_SET) {
255 if (cap_clear_flag(caps, set) < 0) {
256 char *cap_name = cap_to_name(cap);
257 fr_strerror_printf("Failed clearing %s %s state in working set: %s",
258 cap_name,
259 fr_table_str_by_value(cap_set_table, set, "<INVALID>"),
260 fr_syserror(errno));
261 cap_free(cap_name);
262 goto done;
263 }
264
265 if (cap_set_proc(caps) < 0) {
266 char *cap_name = cap_to_name(cap);
267 fr_strerror_printf("Failed setting %s %s state: %s",
268 cap_name,
269 fr_table_str_by_value(cap_set_table, set, "<INVALID>"),
270 fr_syserror(errno));
271 cap_free(cap_name);
272 goto done;
273 }
274
275 ret = 0;
276 } else {
277 ret = 0;
278 }
279
280done:
281 pthread_mutex_unlock(&cap_mutex);
282
283 if (caps) cap_free(caps);
284
285 return ret;
286}
287
288/** Snapshot the processes' current capability set, printing it to a string
289 *
290 * @param[in] ctx Where to allocate the string.
291 * @param[out] out The string containing the capabilities.
292 */
293ssize_t fr_cap_set_to_str(TALLOC_CTX *ctx, char **out)
294{
295 cap_t caps = NULL;
296 char *tmp;
297 ssize_t slen;
298
299 caps = cap_get_proc();
300 if (unlikely(!caps)) {
301 fr_strerror_printf("Failed retrieving process capabilities: %s", fr_syserror(errno));
302 return -1;
303 }
304 tmp = cap_to_text(caps, &slen);
305 cap_free(caps);
306 if (unlikely(!tmp)) {
307 fr_strerror_printf("Failed converting capabilities to string: %s", fr_syserror(errno));
308 return -1;
309 }
310
311 *out = talloc_bstrndup(ctx, tmp, slen);
312 cap_free(tmp);
313
314 return slen;
315}
316#endif /* HAVE_CAPABILITY_H */
#define RCSID(id)
Definition build.h:483
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define unlikely(_x)
Definition build.h:381
#define NUM_ELEMENTS(_t)
Definition build.h:337
long int ssize_t
static bool done
Definition radclient.c:80
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:564
#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:997