The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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  */
24 RCSID("$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 
35 static 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 };
40 static 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  */
46 static 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  */
59 bool 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 
82 done:
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  */
106 int 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 
203 done:
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  */
222 int 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 
280 done:
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  */
293 ssize_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:481
#define L(_str)
Helper for initialising arrays of string literals.
Definition: build.h:207
#define unlikely(_x)
Definition: build.h:379
#define NUM_ELEMENTS(_t)
Definition: build.h:335
long int ssize_t
Definition: merged_model.c:24
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