The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
perm.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 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: 6d1c02a5ae3ea9e98b85743130ba13552614262b $")
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
190/** Resolve a uid to a passwd entry
191 *
192 * Resolves a uid to a passwd entry. The memory to hold the
193 * passwd entry is talloced under ctx, and must be freed when no
194 * longer required.
195 *
196 * @param ctx to allocate passwd entry in.
197 * @param out Where to write pointer to entry.
198 * @param uid to resolve.
199 * @return
200 * - 0 on success.
201 * - -1 on failure.
202 */
203int fr_perm_getpwuid(TALLOC_CTX *ctx, struct passwd **out, uid_t uid)
204{
205 static size_t len;
206 uint8_t *buff;
207 int ret;
208
209 *out = NULL;
210
211 /*
212 * We assume this won't change between calls,
213 * and that the value is the same, so races don't
214 * matter.
215 */
216 if (len == 0) {
217#ifdef _SC_GETPW_R_SIZE_MAX
218 long int sc_len;
219
220 sc_len = sysconf(_SC_GETPW_R_SIZE_MAX);
221 if (sc_len <= 0) sc_len = 1024;
222 len = (size_t)sc_len;
223#else
224 len = 1024;
225#endif
226 }
227
228 buff = talloc_array(ctx, uint8_t, sizeof(struct passwd) + len);
229 if (!buff) return -1;
230
231 /*
232 * In some cases we may need to dynamically
233 * grow the string buffer.
234 */
235 while ((ret = getpwuid_r(uid, (struct passwd *)buff, (char *)(buff + sizeof(struct passwd)),
236 talloc_array_length(buff) - sizeof(struct passwd), out)) == ERANGE) {
237 buff = talloc_realloc_size(ctx, buff, talloc_array_length(buff) * 2);
238 }
239
240 if ((ret != 0) || !*out) {
241 fr_strerror_printf("%s", (errno != 0) ? fr_syserror(ret) : "Non-existent user");
243 errno = ret;
244 return -1;
245 }
246
247 talloc_set_type(buff, struct passwd);
248 *out = (struct passwd *)buff;
249
250 return 0;
251}
252
253/** Resolve a username to a passwd entry
254 *
255 * Resolves a username to a passwd entry. The memory to hold the
256 * passwd entry is talloced under ctx, and must be freed when no
257 * longer required.
258 *
259 * @param ctx to allocate passwd entry in.
260 * @param out Where to write pointer to entry.
261 * @param name to resolve.
262 * @return
263 * - 0 on success.
264 * - -1 on failure.
265 */
266int fr_perm_getpwnam(TALLOC_CTX *ctx, struct passwd **out, char const *name)
267{
268 static size_t len;
269 uint8_t *buff;
270 int ret;
271
272 *out = NULL;
273
274 /*
275 * We assume this won't change between calls,
276 * and that the value is the same, so races don't
277 * matter.
278 */
279 if (len == 0) {
280#ifdef _SC_GETPW_R_SIZE_MAX
281 long int sc_len;
282
283 sc_len = sysconf(_SC_GETPW_R_SIZE_MAX);
284 if (sc_len <= 0) sc_len = 1024;
285 len = (size_t)sc_len;
286#else
287 sc_len = 1024;
288#endif
289 }
290
291 buff = talloc_array(ctx, uint8_t, sizeof(struct passwd) + len);
292 if (!buff) return -1;
293
294 /*
295 * In some cases we may need to dynamically
296 * grow the string buffer.
297 */
298 while ((ret = getpwnam_r(name, (struct passwd *)buff, (char *)(buff + sizeof(struct passwd)),
299 talloc_array_length(buff) - sizeof(struct passwd), out)) == ERANGE) {
300 buff = talloc_realloc_size(ctx, buff, talloc_array_length(buff) * 2);
301 }
302
303 if ((ret != 0) || !*out) {
304 fr_strerror_printf("%s", (errno != 0) ? fr_syserror(ret) : "Non-existent user");
306 errno = ret;
307 return -1;
308 }
309
310 talloc_set_type(buff, struct passwd);
311 *out = (struct passwd *)buff;
312
313 return 0;
314}
315
316/** Resolve a gid to a group database entry
317 *
318 * Resolves a gid to a group database entry. The memory to hold the
319 * group entry is talloced under ctx, and must be freed when no
320 * longer required.
321 *
322 * @param ctx to allocate passwd entry in.
323 * @param out Where to write pointer to entry.
324 * @param gid to resolve.
325 * @return
326 * - 0 on success.
327 * - -1 on failure.
328 */
329int fr_perm_getgrgid(TALLOC_CTX *ctx, struct group **out, gid_t gid)
330{
331 static size_t len;
332 uint8_t *buff;
333 int ret;
334
335 *out = NULL;
336
337 /*
338 * We assume this won't change between calls,
339 * and that the value is the same, so races don't
340 * matter.
341 */
342 if (len == 0) {
343#ifdef _SC_GETGR_R_SIZE_MAX
344 long int sc_len;
345
346 sc_len = sysconf(_SC_GETGR_R_SIZE_MAX);
347 if (sc_len <= 0) sc_len = 1024;
348 len = (size_t)sc_len;
349#else
350 sc_len = 1024;
351#endif
352 }
353
354 buff = talloc_array(ctx, uint8_t, sizeof(struct group) + len);
355 if (!buff) return -1;
356
357 /*
358 * In some cases we may need to dynamically
359 * grow the string buffer.
360 */
361 while ((ret = getgrgid_r(gid, (struct group *)buff, (char *)(buff + sizeof(struct group)),
362 talloc_array_length(buff) - sizeof(struct group), out)) == ERANGE) {
363 buff = talloc_realloc_size(ctx, buff, talloc_array_length(buff) * 2);
364 }
365
366 if ((ret != 0) || !*out) {
367 fr_strerror_printf("%s", (ret != 0) ? fr_syserror(ret) : "Non-existent group");
369 errno = ret;
370 return -1;
371 }
372
373 talloc_set_type(buff, struct group);
374 *out = (struct group *)buff;
375
376 return 0;
377}
378
379/** Resolve a group name to a group database entry
380 *
381 * Resolves a group name to a group database entry.
382 * The memory to hold the group entry is talloced under ctx,
383 * and must be freed when no longer required.
384 *
385 * @param ctx to allocate passwd entry in.
386 * @param out Where to write pointer to entry.
387 * @param name to resolve.
388 * @return
389 * - 0 on success.
390 * - -1 on failure.
391 */
392int fr_perm_getgrnam(TALLOC_CTX *ctx, struct group **out, char const *name)
393{
394 static size_t len;
395 uint8_t *buff;
396 int ret;
397
398 *out = NULL;
399
400 /*
401 * We assume this won't change between calls,
402 * and that the value is the same, so races don't
403 * matter.
404 */
405 if (len == 0) {
406#ifdef _SC_GETGR_R_SIZE_MAX
407 long int sc_len;
408
409 sc_len = sysconf(_SC_GETGR_R_SIZE_MAX);
410 if (sc_len <= 0) sc_len = 1024;
411 len = (size_t)sc_len;
412#else
413 len = 1024;
414#endif
415 }
416
417 buff = talloc_array(ctx, uint8_t, sizeof(struct group) + len);
418 if (!buff) return -1;
419
420 /*
421 * In some cases we may need to dynamically
422 * grow the string buffer.
423 */
424 while ((ret = getgrnam_r(name, (struct group *)buff, (char *)(buff + sizeof(struct group)),
425 talloc_array_length(buff) - sizeof(struct group), out)) == ERANGE) {
426 buff = talloc_realloc_size(ctx, buff, talloc_array_length(buff) * 2);
427 }
428
429 if ((ret != 0) || !*out) {
430 fr_strerror_printf("%s", (ret != 0) ? fr_syserror(ret) : "Non-existent group");
432 errno = ret;
433 return -1;
434 }
435
436 talloc_set_type(buff, struct group);
437 *out = (struct group *)buff;
438
439 return 0;
440}
441
442/** Resolve a user name to a GID
443 *
444 * @param[in] ctx TALLOC_CTX for temporary allocations.
445 * @param[in] out where to write gid.
446 * @param[in] name of user.
447 * @return
448 * - 0 on success.
449 * - -1 on failure.
450 */
451int fr_perm_uid_from_str(TALLOC_CTX *ctx, uid_t *out, char const *name)
452{
453 int ret;
454 struct passwd *result;
455
456 ret = fr_perm_getpwnam(ctx, &result, name);
457 if (ret < 0) return -1;
458
459 *out = result->pw_uid;
460 talloc_free(result);
461 return 0;
462}
463
464/** Resolve a group name to a GID
465 *
466 * @param[in] ctx TALLOC_CTX for temporary allocations.
467 * @param[in] out where to write gid.
468 * @param[in] name of group.
469 * @return
470 * - 0 on success.
471 * - -1 on failure.
472 */
473int fr_perm_gid_from_str(TALLOC_CTX *ctx, gid_t *out, char const *name)
474{
475 int ret;
476 struct group *result;
477
478 ret = fr_perm_getgrnam(ctx, &result, name);
479 if (ret < 0) return -1;
480
481 *out = result->gr_gid;
482 talloc_free(result);
483 return 0;
484}
485
486/** Print uid to a string
487 *
488 * @param ctx TALLOC_CTX for temporary allocations.
489 * @param uid to resolve.
490 * @return
491 * - 0 on success.
492 * - -1 on failure.
493 */
494char *fr_perm_uid_to_str(TALLOC_CTX *ctx, uid_t uid)
495{
496 struct passwd *result;
497 char *out;
498
499 if (fr_perm_getpwuid(ctx, &result, uid) < 0) return NULL;
500 out = talloc_typed_strdup(ctx, result->pw_name);
501 talloc_free(result);
502
503 return out;
504}
505
506/** Print gid to a string
507 *
508 * @param ctx TALLOC_CTX for temporary allocations.
509 * @param gid to resolve.
510 * @return
511 * - 0 on success.
512 * - -1 on failure.
513 */
514char *fr_perm_gid_to_str(TALLOC_CTX *ctx, uid_t gid){
515 struct group *result;
516 char *out;
517
518 if (fr_perm_getgrgid(ctx, &result, gid) < 0) return NULL;
519 out = talloc_typed_strdup(ctx, result->gr_name);
520 talloc_free(result);
521
522 return out;
523}
524
525/** Write a file access error to the fr_strerror buffer, including euid/egid
526 *
527 * @note retrieve error with fr_strerror()
528 *
529 * @param num Usually errno, unless the error is returned by the function.
530 */
532{
533 char const *error;
534 struct passwd *user = NULL;
535 struct group *group = NULL;
536
537 error = fr_syserror(num);
538
539 if (fr_perm_getpwuid(NULL, &user, geteuid()) < 0) goto finish;
540 if (fr_perm_getgrgid(NULL, &group, getegid()) < 0) goto finish;
541
542 fr_strerror_printf("Effective user/group - %s:%s: %s", user->pw_name, group->gr_name, error);
543finish:
544 talloc_free(user);
545 talloc_free(group);
546}
strcpy(log_entry->msg, buffer)
#define RCSID(id)
Definition build.h:483
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:322
Test enumeration values.
Definition dict_test.h:92
talloc_free(reap)
unsigned char uint8_t
unsigned int mode_t
unsigned long int size_t
#define check(_handle, _len_p)
Definition bio.c:44
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:392
char * fr_perm_uid_to_str(TALLOC_CTX *ctx, uid_t uid)
Print uid to a string.
Definition perm.c:494
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:514
int fr_perm_getgrgid(TALLOC_CTX *ctx, struct group **out, gid_t gid)
Resolve a gid to a group database entry.
Definition perm.c:329
int fr_perm_getpwnam(TALLOC_CTX *ctx, struct passwd **out, char const *name)
Resolve a username to a passwd entry.
Definition perm.c:266
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:451
int fr_perm_getpwuid(TALLOC_CTX *ctx, struct passwd **out, uid_t uid)
Resolve a uid to a passwd entry.
Definition perm.c:203
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:473
void fr_perm_file_error(int num)
Write a file access error to the fr_strerror buffer, including euid/egid.
Definition perm.c:531
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
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition talloc.c:445
#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