The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
perm.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Implementation of filed semaphores that release on exit
18 *
19 * @file src/lib/util/perm.c
20 *
21 * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
22 */
23RCSID("$Id: 1a431f74bce697b00b0ce7c263da395624d09d30 $")
24
25#include <freeradius-devel/util/perm.h>
26#include <freeradius-devel/util/strerror.h>
27#include <freeradius-devel/util/syserror.h>
28
29/** Convert mode_t into humanly readable permissions flags
30 *
31 * @author Jonathan Leffler.
32 *
33 * @param mode to convert.
34 * @param out Where to write the string to, must be exactly 10 bytes long.
35 */
36char const *fr_perm_mode_to_str(char out[static 10], mode_t mode)
37{
38 static char const *rwx[] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"};
39
40 strcpy(&out[0], rwx[(mode >> 6) & 0x07]);
41 strcpy(&out[3], rwx[(mode >> 3) & 0x07]);
42 strcpy(&out[6], rwx[(mode & 7)]);
43 if (mode & S_ISUID) out[2] = (mode & 0100) ? 's' : 'S';
44 if (mode & S_ISGID) out[5] = (mode & 0010) ? 's' : 'l';
45 if (mode & S_ISVTX) out[8] = (mode & 0100) ? 't' : 'T';
46 out[9] = '\0';
47
48 return out;
49}
50
51char const *fr_perm_mode_to_oct(char out[static 5], mode_t mode)
52{
53 out[0] = '0' + ((mode >> 9) & 0x07);
54 out[1] = '0' + ((mode >> 6) & 0x07);
55 out[2] = '0' + ((mode >> 3) & 0x07);
56 out[3] = '0' + (mode & 0x07);
57 out[4] = '\0';
58
59 return out;
60}
61
62int fr_perm_mode_from_str(mode_t *out, char const *str)
63{
64 char const *p;
65 mode_t mode;
66 int shift;
67
68 /*
69 * Octal strings.
70 */
71 if (*str == '0') {
72 unsigned long value;
73 char *end = NULL;
74
75 /*
76 * Might as well be quick about it.
77 */
78 if (strlen(str) != 4) goto fail;
79
80 value = strtoul(str, &end, 8);
81 if (*end || (value == ULONG_MAX)) {
82 fail:
83 fr_strerror_const("Invalid octal permissions string");
84 return -1;
85 }
86
87 mode = value;
88 goto check;
89 }
90
91 mode = 0;
92 shift = 0;
93
94
95 p = str;
96
97redo:
98 /*
99 * g=foo,u=foo
100 */
101 if (*p == 'g') {
102 if (p[1] != '=') {
103 expected_set:
104 fr_strerror_printf("Invalid permission character '%c', expected '='", *p);
105 return -1;
106 }
107
108 shift = 3;
109 p += 2;
110
111 } else if (*p == 'u') {
112 if (p[1] != '=') goto expected_set;
113
114 shift = 0;
115 p += 2;
116
117 } else if (*p == 'o') {
118 if (p[1] != '=') goto expected_set;
119
120 shift = 6;
121 p += 2;
122
123 } /* else 'rwx' is implicitly "u=rwx" */
124
125 /*
126 * Parse permissions in any order.
127 */
128 for (/* nothing */ ; *p != '\0'; p++) {
129 mode_t mask = 0;
130
131 switch (*p) {
132 case 'r':
133 mask = S_IRUSR;
134 break;
135
136 case 'w':
137 mask = S_IWUSR;
138 break;
139
140 case 'x':
141 mask = S_IXUSR;
142 break;
143
144 case ',':
145 p++;
146
147 if (!*p) {
148 fr_strerror_const("Unexpected end of string after ','");
149 return -1;
150 }
151
152 /*
153 * Alphabetical is OK.
154 */
155 if (isalpha((uint8_t) *p)) goto redo;
156
157 /*
158 * Non-alphabetical is bad.
159 */
161
162 default:
163 fr_strerror_printf("Invalid permission character '%c', expected one of 'rwx'", *p);
164 return -1;
165 }
166
167 mask >>= shift;
168
169 if ((mode & mask) != 0) {
170 fr_strerror_printf("Permission '%c' is duplicated", *p);
171 return -1;
172 }
173
174 mode |= mask;
175 }
176
177check:
178 /*
179 * Some attempt at security is a good idea.
180 */
181 if ((mode & S_IWOTH) != 0) {
182 fr_strerror_const("Invalid permissions, files cannot be world writeable");
183 return -1;
184 }
185
186 *out = mode;
187 return 0;
188}
189
190DIAG_OFF(cast-align)
191
192/** Resolve a uid to a passwd entry
193 *
194 * Resolves a uid to a passwd entry. The memory to hold the
195 * passwd entry is talloced under ctx, and must be freed when no
196 * longer required.
197 *
198 * @param ctx to allocate passwd entry in.
199 * @param out Where to write pointer to entry.
200 * @param uid to resolve.
201 * @return
202 * - 0 on success.
203 * - -1 on failure.
204 */
205int fr_perm_getpwuid(TALLOC_CTX *ctx, struct passwd **out, uid_t uid)
206{
207 static size_t len;
208 uint8_t *buff;
209 int ret;
210
211 *out = NULL;
212
213 /*
214 * We assume this won't change between calls,
215 * and that the value is the same, so races don't
216 * matter.
217 */
218 if (len == 0) {
219#ifdef _SC_GETPW_R_SIZE_MAX
220 long int sc_len;
221
222 sc_len = sysconf(_SC_GETPW_R_SIZE_MAX);
223 if (sc_len <= 0) sc_len = 1024;
224 len = (size_t)sc_len;
225#else
226 len = 1024;
227#endif
228 }
229
230 buff = talloc_array(ctx, uint8_t, sizeof(struct passwd) + len);
231 if (!buff) return -1;
232
233 /*
234 * In some cases we may need to dynamically
235 * grow the string buffer.
236 */
237 while ((ret = getpwuid_r(uid, (struct passwd *)buff, (char *)(buff + sizeof(struct passwd)),
238 talloc_array_length(buff) - sizeof(struct passwd), out)) == ERANGE) {
239 buff = talloc_realloc_size(ctx, buff, talloc_array_length(buff) * 2);
240 }
241
242 if ((ret != 0) || !*out) {
243 fr_strerror_printf("%s", (errno != 0) ? fr_syserror(ret) : "Non-existent user");
245 errno = ret;
246 return -1;
247 }
248
249 talloc_set_type(buff, struct passwd);
250 *out = (struct passwd *)buff;
251
252 return 0;
253}
254
255/** Resolve a username to a passwd entry
256 *
257 * Resolves a username to a passwd entry. The memory to hold the
258 * passwd entry is talloced under ctx, and must be freed when no
259 * longer required.
260 *
261 * @param ctx to allocate passwd entry in.
262 * @param out Where to write pointer to entry.
263 * @param name to resolve.
264 * @return
265 * - 0 on success.
266 * - -1 on failure.
267 */
268int fr_perm_getpwnam(TALLOC_CTX *ctx, struct passwd **out, char const *name)
269{
270 static size_t len;
271 uint8_t *buff;
272 int ret;
273
274 *out = NULL;
275
276 /*
277 * We assume this won't change between calls,
278 * and that the value is the same, so races don't
279 * matter.
280 */
281 if (len == 0) {
282#ifdef _SC_GETPW_R_SIZE_MAX
283 long int sc_len;
284
285 sc_len = sysconf(_SC_GETPW_R_SIZE_MAX);
286 if (sc_len <= 0) sc_len = 1024;
287 len = (size_t)sc_len;
288#else
289 sc_len = 1024;
290#endif
291 }
292
293 buff = talloc_array(ctx, uint8_t, sizeof(struct passwd) + len);
294 if (!buff) return -1;
295
296 /*
297 * In some cases we may need to dynamically
298 * grow the string buffer.
299 */
300 while ((ret = getpwnam_r(name, (struct passwd *)buff, (char *)(buff + sizeof(struct passwd)),
301 talloc_array_length(buff) - sizeof(struct passwd), out)) == ERANGE) {
302 buff = talloc_realloc_size(ctx, buff, talloc_array_length(buff) * 2);
303 }
304
305 if ((ret != 0) || !*out) {
306 fr_strerror_printf("%s", (errno != 0) ? fr_syserror(ret) : "Non-existent user");
308 errno = ret;
309 return -1;
310 }
311
312 talloc_set_type(buff, struct passwd);
313 *out = (struct passwd *)buff;
314
315 return 0;
316}
317
318/** Resolve a gid to a group database entry
319 *
320 * Resolves a gid to a group database entry. The memory to hold the
321 * group entry is talloced under ctx, and must be freed when no
322 * longer required.
323 *
324 * @param ctx to allocate passwd entry in.
325 * @param out Where to write pointer to entry.
326 * @param gid to resolve.
327 * @return
328 * - 0 on success.
329 * - -1 on failure.
330 */
331int fr_perm_getgrgid(TALLOC_CTX *ctx, struct group **out, gid_t gid)
332{
333 static size_t len;
334 uint8_t *buff;
335 int ret;
336
337 *out = NULL;
338
339 /*
340 * We assume this won't change between calls,
341 * and that the value is the same, so races don't
342 * matter.
343 */
344 if (len == 0) {
345#ifdef _SC_GETGR_R_SIZE_MAX
346 long int sc_len;
347
348 sc_len = sysconf(_SC_GETGR_R_SIZE_MAX);
349 if (sc_len <= 0) sc_len = 1024;
350 len = (size_t)sc_len;
351#else
352 sc_len = 1024;
353#endif
354 }
355
356 buff = talloc_array(ctx, uint8_t, sizeof(struct group) + len);
357 if (!buff) return -1;
358
359 /*
360 * In some cases we may need to dynamically
361 * grow the string buffer.
362 */
363 while ((ret = getgrgid_r(gid, (struct group *)buff, (char *)(buff + sizeof(struct group)),
364 talloc_array_length(buff) - sizeof(struct group), out)) == ERANGE) {
365 buff = talloc_realloc_size(ctx, buff, talloc_array_length(buff) * 2);
366 }
367
368 if ((ret != 0) || !*out) {
369 fr_strerror_printf("%s", (ret != 0) ? fr_syserror(ret) : "Non-existent group");
371 errno = ret;
372 return -1;
373 }
374
375 talloc_set_type(buff, struct group);
376 *out = (struct group *)buff;
377
378 return 0;
379}
380
381/** Resolve a group name to a group database entry
382 *
383 * Resolves a group name to a group database entry.
384 * The memory to hold the group entry is talloced under ctx,
385 * and must be freed when no longer required.
386 *
387 * @param ctx to allocate passwd entry in.
388 * @param out Where to write pointer to entry.
389 * @param name to resolve.
390 * @return
391 * - 0 on success.
392 * - -1 on failure.
393 */
394int fr_perm_getgrnam(TALLOC_CTX *ctx, struct group **out, char const *name)
395{
396 static size_t len;
397 uint8_t *buff;
398 int ret;
399
400 *out = NULL;
401
402 /*
403 * We assume this won't change between calls,
404 * and that the value is the same, so races don't
405 * matter.
406 */
407 if (len == 0) {
408#ifdef _SC_GETGR_R_SIZE_MAX
409 long int sc_len;
410
411 sc_len = sysconf(_SC_GETGR_R_SIZE_MAX);
412 if (sc_len <= 0) sc_len = 1024;
413 len = (size_t)sc_len;
414#else
415 len = 1024;
416#endif
417 }
418
419 buff = talloc_array(ctx, uint8_t, sizeof(struct group) + len);
420 if (!buff) return -1;
421
422 /*
423 * In some cases we may need to dynamically
424 * grow the string buffer.
425 */
426 while ((ret = getgrnam_r(name, (struct group *)buff, (char *)(buff + sizeof(struct group)),
427 talloc_array_length(buff) - sizeof(struct group), out)) == ERANGE) {
428 buff = talloc_realloc_size(ctx, buff, talloc_array_length(buff) * 2);
429 }
430
431 if ((ret != 0) || !*out) {
432 fr_strerror_printf("%s", (ret != 0) ? fr_syserror(ret) : "Non-existent group");
434 errno = ret;
435 return -1;
436 }
437
438 talloc_set_type(buff, struct group);
439 *out = (struct group *)buff;
440
441 return 0;
442}
443
444/** Resolve a user name to a GID
445 *
446 * @param[in] ctx TALLOC_CTX for temporary allocations.
447 * @param[in] out where to write gid.
448 * @param[in] name of user.
449 * @return
450 * - 0 on success.
451 * - -1 on failure.
452 */
453int fr_perm_uid_from_str(TALLOC_CTX *ctx, uid_t *out, char const *name)
454{
455 int ret;
456 struct passwd *result;
457
458 ret = fr_perm_getpwnam(ctx, &result, name);
459 if (ret < 0) return -1;
460
461 *out = result->pw_uid;
462 talloc_free(result);
463 return 0;
464}
465
466/** Resolve a group name to a GID
467 *
468 * @param[in] ctx TALLOC_CTX for temporary allocations.
469 * @param[in] out where to write gid.
470 * @param[in] name of group.
471 * @return
472 * - 0 on success.
473 * - -1 on failure.
474 */
475int fr_perm_gid_from_str(TALLOC_CTX *ctx, gid_t *out, char const *name)
476{
477 int ret;
478 struct group *result;
479
480 ret = fr_perm_getgrnam(ctx, &result, name);
481 if (ret < 0) return -1;
482
483 *out = result->gr_gid;
484 talloc_free(result);
485 return 0;
486}
487
488/** Print uid to a string
489 *
490 * @param ctx TALLOC_CTX for temporary allocations.
491 * @param uid to resolve.
492 * @return
493 * - 0 on success.
494 * - -1 on failure.
495 */
496char *fr_perm_uid_to_str(TALLOC_CTX *ctx, uid_t uid)
497{
498 struct passwd *result;
499 char *out;
500
501 if (fr_perm_getpwuid(ctx, &result, uid) < 0) return NULL;
502 out = talloc_strdup(ctx, result->pw_name);
503 talloc_free(result);
504
505 return out;
506}
507
508/** Print gid to a string
509 *
510 * @param ctx TALLOC_CTX for temporary allocations.
511 * @param gid to resolve.
512 * @return
513 * - 0 on success.
514 * - -1 on failure.
515 */
516char *fr_perm_gid_to_str(TALLOC_CTX *ctx, uid_t gid){
517 struct group *result;
518 char *out;
519
520 if (fr_perm_getgrgid(ctx, &result, gid) < 0) return NULL;
521 out = talloc_strdup(ctx, result->gr_name);
522 talloc_free(result);
523
524 return out;
525}
526
527/** Write a file access error to the fr_strerror buffer, including euid/egid
528 *
529 * @note retrieve error with fr_strerror()
530 *
531 * @param num Usually errno, unless the error is returned by the function.
532 */
534{
535 char const *error;
536 struct passwd *user = NULL;
537 struct group *group = NULL;
538
539 error = fr_syserror(num);
540
541 if (fr_perm_getpwuid(NULL, &user, geteuid()) < 0) goto finish;
542 if (fr_perm_getgrgid(NULL, &group, getegid()) < 0) goto finish;
543
544 fr_strerror_printf("Effective user/group - %s:%s: %s", user->pw_name, group->gr_name, error);
545finish:
546 talloc_free(user);
547 talloc_free(group);
548}
strcpy(log_entry->msg, buffer)
#define RCSID(id)
Definition build.h:506
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:343
#define DIAG_OFF(_x)
Definition build.h:480
Test enumeration values.
Definition dict_test.h:92
talloc_free(hp)
unsigned char uint8_t
unsigned int mode_t
unsigned long int size_t
int fr_perm_getgrnam(TALLOC_CTX *ctx, struct group **out, char const *name)
Resolve a group name to a group database entry.
Definition perm.c:394
char * fr_perm_uid_to_str(TALLOC_CTX *ctx, uid_t uid)
Print uid to a string.
Definition perm.c:496
char const * fr_perm_mode_to_str(char out[static 10], mode_t mode)
Convert mode_t into humanly readable permissions flags.
Definition perm.c:36
char const * fr_perm_mode_to_oct(char out[static 5], mode_t mode)
Definition perm.c:51
int fr_perm_mode_from_str(mode_t *out, char const *str)
Definition perm.c:62
char * fr_perm_gid_to_str(TALLOC_CTX *ctx, uid_t gid)
Print gid to a string.
Definition perm.c:516
int fr_perm_getgrgid(TALLOC_CTX *ctx, struct group **out, gid_t gid)
Resolve a gid to a group database entry.
Definition perm.c:331
int fr_perm_getpwnam(TALLOC_CTX *ctx, struct passwd **out, char const *name)
Resolve a username to a passwd entry.
Definition perm.c:268
int fr_perm_uid_from_str(TALLOC_CTX *ctx, uid_t *out, char const *name)
Resolve a user name to a GID.
Definition perm.c:453
int fr_perm_getpwuid(TALLOC_CTX *ctx, struct passwd **out, uid_t uid)
Resolve a uid to a passwd entry.
Definition perm.c:205
int fr_perm_gid_from_str(TALLOC_CTX *ctx, gid_t *out, char const *name)
Resolve a group name to a GID.
Definition perm.c:475
void fr_perm_file_error(int num)
Write a file access error to the fr_strerror buffer, including euid/egid.
Definition perm.c:533
static uint32_t mask
Definition rbmonkey.c:39
static char const * name
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:41
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
#define talloc_strdup(_ctx, _str)
Definition talloc.h:142
#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:1030