]> gitweb.michael.orlitzky.com - apply-default-acl.git/blob - src/libadacl.c
libadacl: update failure return value docs for apply_default_acl().
[apply-default-acl.git] / src / libadacl.c
1 /**
2 * @file libadacl.c
3 *
4 * @brief The adacl (apply default acl) shared library.
5 *
6 */
7
8 /* Enables get_current_dir_name() in unistd.h */
9 #define _GNU_SOURCE
10
11 #include <errno.h> /* ELOOP, EINVAL, etc. */
12 #include <fcntl.h> /* openat() */
13 #include <libgen.h> /* basename(), dirname() */
14 #include <limits.h> /* PATH_MAX */
15 #include <stdbool.h> /* the "bool" type */
16 #include <stdio.h> /* perror(), snprintf() */
17 #include <stdlib.h> /* free() */
18 #include <string.h> /* strdup() */
19 #include <sys/stat.h> /* fstat() */
20 #include <unistd.h> /* get_current_dir_name() */
21
22 /* ACLs */
23 #include <acl/libacl.h> /* acl_get_perm, not portable */
24 #include <sys/acl.h> /* all other acl_foo functions */
25
26 #include "libadacl.h"
27
28
29 /**
30 * @brief The recursive portion of the @c safe_open function, used to
31 * open a file descriptor in a symlink-safe way when combined with
32 * the @c O_NOFOLLOW flag.
33 *
34 * @param at_fd
35 * A file descriptor relative to which @c pathname will be opened.
36 *
37 * @param pathname
38 * The path to the file/directory/whatever whose descriptor you want.
39 *
40 * @return a file descriptor for @c pathname if everything goes well,
41 * and @c OPEN_ERROR if not.
42 */
43 int safe_open_ex(int at_fd, char* pathname, int flags) {
44 if (pathname != NULL && strlen(pathname) == 0) {
45 /* Oops, went one level to deep with nothing to do. */
46 return at_fd;
47 }
48
49 char* firstslash = strchr(pathname, '/');
50 if (firstslash == NULL) {
51 /* No more slashes, this is the base case. */
52 int r = openat(at_fd, pathname, flags);
53 return r;
54 }
55
56 /* Temporarily disable the slash, so that the subsequent call to
57 openat() opens only the next directory (and doesn't recurse). */
58 *firstslash = '\0';
59 int fd = safe_open_ex(at_fd, pathname, flags);
60 if (fd == OPEN_ERROR) {
61 if (errno != ELOOP) {
62 /* Don't output anything if we ignore a symlink */
63 perror("safe_open_ex (safe_open_ex)");
64 }
65 return OPEN_ERROR;
66 }
67
68 /* The ++ is safe because there needs to be at least a null byte
69 after the first slash, even if it's the last real character in
70 the string. */
71 int result = safe_open_ex(fd, firstslash+1, flags);
72 if (close(fd) == CLOSE_ERROR) {
73 perror("safe_open_ex (close)");
74 return OPEN_ERROR;
75 }
76 return result;
77 }
78
79
80 /**
81 * @brief A version of @c open that is completely symlink-safe when
82 * used with the @c O_NOFOLLOW flag.
83 *
84 * The @c openat function exists to ensure that you can anchor one
85 * path to a particular directory while opening it; however, if you
86 * open "b/c/d" relative to "/a", then even the @c openat function will
87 * still follow symlinks in the "b" component. This can be exploited
88 * by an attacker to make you open the wrong path.
89 *
90 * To avoid that problem, this function uses a recursive
91 * implementation that opens every path from the root, one level at a
92 * time. So "a" is opened relative to "/", and then "b" is opened
93 * relative to "/a", and then "c" is opened relative to "/a/b",
94 * etc. When the @c O_NOFOLLOW flag is used, this approach ensures
95 * that no symlinks in any component are followed.
96 *
97 * @param pathname
98 * The path to the file/directory/whatever whose descriptor you want.
99 *
100 * @return a file descriptor for @c pathname if everything goes well,
101 * and @c OPEN_ERROR if not.
102 */
103 int safe_open(const char* pathname, int flags) {
104 if (pathname == NULL || strlen(pathname) == 0 || pathname[0] == '\0') {
105 errno = EINVAL;
106 perror("safe_open (args)");
107 return OPEN_ERROR;
108 }
109
110 char abspath[PATH_MAX];
111 int snprintf_result = 0;
112 if (strchr(pathname, '/') == pathname) {
113 /* pathname is already absolute; just copy it. */
114 snprintf_result = snprintf(abspath, PATH_MAX, "%s", pathname);
115 }
116 else {
117 /* Concatenate the current working directory and pathname into an
118 * absolute path. We use realpath() ONLY on the cwd part, and not
119 * on the pathname part, because realpath() resolves symlinks. And
120 * the whole point of all this crap is to avoid following symlinks
121 * in the pathname.
122 *
123 * Using realpath() on the cwd lets us operate on relative paths
124 * while we're sitting in a directory that happens to have a
125 * symlink in it; for example: cd /var/run && apply-default-acl foo.
126 */
127 char* cwd = get_current_dir_name();
128 if (cwd == NULL) {
129 perror("safe_open (get_current_dir_name)");
130 return OPEN_ERROR;
131 }
132
133 char abs_cwd[PATH_MAX];
134 if (realpath(cwd, abs_cwd) == NULL) {
135 perror("safe_open (realpath)");
136 free(cwd);
137 return OPEN_ERROR;
138 }
139 snprintf_result = snprintf(abspath, PATH_MAX, "%s/%s", abs_cwd, pathname);
140 free(cwd);
141 }
142 if (snprintf_result == SNPRINTF_ERROR || snprintf_result > PATH_MAX) {
143 perror("safe_open (snprintf)");
144 return OPEN_ERROR;
145 }
146
147 int fd = open("/", flags);
148 if (strcmp(abspath, "/") == 0) {
149 return fd;
150 }
151
152 int result = safe_open_ex(fd, abspath+1, flags);
153 if (close(fd) == CLOSE_ERROR) {
154 perror("safe_open (close)");
155 return OPEN_ERROR;
156 }
157 return result;
158 }
159
160
161
162
163 /**
164 * @brief Update (or create) an entry in an @b minimal ACL.
165 *
166 * This function will not work if @c aclp contains extended
167 * entries. This is fine for our purposes, since we call @c wipe_acls
168 * on each path before applying the default to it.
169 *
170 * The assumption that there are no extended entries makes things much
171 * simpler. For example, we only have to update the @c ACL_USER_OBJ,
172 * @c ACL_GROUP_OBJ, and @c ACL_OTHER entries -- all others can simply
173 * be created anew. This means we don't have to fool around comparing
174 * named-user/group entries.
175 *
176 * @param aclp
177 * A pointer to the acl_t structure whose entry we want to modify.
178 *
179 * @param entry
180 * The new entry. If @c entry contains a user/group/other entry, we
181 * update the existing one. Otherwise we create a new entry.
182 *
183 * @return If there is an unexpected library error, @c ACL_ERROR is
184 * returned. Otherwise, @c ACL_SUCCESS.
185 *
186 */
187 int acl_set_entry(acl_t* aclp, acl_entry_t entry) {
188
189 acl_tag_t entry_tag;
190 if (acl_get_tag_type(entry, &entry_tag) == ACL_ERROR) {
191 perror("acl_set_entry (acl_get_tag_type)");
192 return ACL_ERROR;
193 }
194
195 acl_permset_t entry_permset;
196 if (acl_get_permset(entry, &entry_permset) == ACL_ERROR) {
197 perror("acl_set_entry (acl_get_permset)");
198 return ACL_ERROR;
199 }
200
201 acl_entry_t existing_entry;
202 /* Loop through the given ACL looking for matching entries. */
203 int result = acl_get_entry(*aclp, ACL_FIRST_ENTRY, &existing_entry);
204
205 while (result == ACL_SUCCESS) {
206 acl_tag_t existing_tag = ACL_UNDEFINED_TAG;
207
208 if (acl_get_tag_type(existing_entry, &existing_tag) == ACL_ERROR) {
209 perror("set_acl_tag_permset (acl_get_tag_type)");
210 return ACL_ERROR;
211 }
212
213 if (existing_tag == entry_tag) {
214 if (entry_tag == ACL_USER_OBJ ||
215 entry_tag == ACL_GROUP_OBJ ||
216 entry_tag == ACL_OTHER) {
217 /* Only update for these three since all other tags will have
218 been wiped. These three are guaranteed to exist, so if we
219 match one of them, we're allowed to return ACL_SUCCESS
220 below and bypass the rest of the function. */
221 acl_permset_t existing_permset;
222 if (acl_get_permset(existing_entry, &existing_permset) == ACL_ERROR) {
223 perror("acl_set_entry (acl_get_permset)");
224 return ACL_ERROR;
225 }
226
227 if (acl_set_permset(existing_entry, entry_permset) == ACL_ERROR) {
228 perror("acl_set_entry (acl_set_permset)");
229 return ACL_ERROR;
230 }
231
232 return ACL_SUCCESS;
233 }
234
235 }
236
237 result = acl_get_entry(*aclp, ACL_NEXT_ENTRY, &existing_entry);
238 }
239
240 /* This catches both the initial acl_get_entry and the ones at the
241 end of the loop. */
242 if (result == ACL_ERROR) {
243 perror("acl_set_entry (acl_get_entry)");
244 return ACL_ERROR;
245 }
246
247 /* If we've made it this far, we need to add a new entry to the
248 ACL. */
249 acl_entry_t new_entry;
250
251 /* The acl_create_entry() function can allocate new memory and/or
252 * change the location of the ACL structure entirely. When that
253 * happens, the value pointed to by aclp is updated, which means
254 * that a new acl_t gets "passed out" to our caller, eventually to
255 * be fed to acl_free(). In other words, we should still be freeing
256 * the right thing, even if the value pointed to by aclp changes.
257 */
258 if (acl_create_entry(aclp, &new_entry) == ACL_ERROR) {
259 perror("acl_set_entry (acl_create_entry)");
260 return ACL_ERROR;
261 }
262
263 if (acl_set_tag_type(new_entry, entry_tag) == ACL_ERROR) {
264 perror("acl_set_entry (acl_set_tag_type)");
265 return ACL_ERROR;
266 }
267
268 if (acl_set_permset(new_entry, entry_permset) == ACL_ERROR) {
269 perror("acl_set_entry (acl_set_permset)");
270 return ACL_ERROR;
271 }
272
273 if (entry_tag == ACL_USER || entry_tag == ACL_GROUP) {
274 /* We need to set the qualifier too. */
275 void* entry_qual = acl_get_qualifier(entry);
276 if (entry_qual == (void*)NULL) {
277 perror("acl_set_entry (acl_get_qualifier)");
278 return ACL_ERROR;
279 }
280
281 if (acl_set_qualifier(new_entry, entry_qual) == ACL_ERROR) {
282 perror("acl_set_entry (acl_set_qualifier)");
283 return ACL_ERROR;
284 }
285 }
286
287 return ACL_SUCCESS;
288 }
289
290
291
292 /**
293 * @brief Determine the number of entries in the given ACL.
294 *
295 * @param acl
296 * The ACL to inspect.
297 *
298 * @return Either the non-negative number of entries in @c acl, or
299 * @c ACL_ERROR on error.
300 */
301 int acl_entry_count(acl_t acl) {
302
303 acl_entry_t entry;
304 int entry_count = 0;
305 int result = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
306
307 while (result == ACL_SUCCESS) {
308 entry_count++;
309 result = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
310 }
311
312 if (result == ACL_ERROR) {
313 perror("acl_entry_count (acl_get_entry)");
314 return ACL_ERROR;
315 }
316
317 return entry_count;
318 }
319
320
321
322 /**
323 * @brief Determine whether or not the given ACL is minimal.
324 *
325 * An ACL is minimal if it has fewer than four entries.
326 *
327 * @param acl
328 * The ACL whose minimality is in question.
329 *
330 * @return
331 * - @c ACL_SUCCESS - @c acl is minimal
332 * - @c ACL_FAILURE - @c acl is not minimal
333 * - @c ACL_ERROR - Unexpected library error
334 */
335 int acl_is_minimal(acl_t acl) {
336
337 int ec = acl_entry_count(acl);
338
339 if (ec == ACL_ERROR) {
340 perror("acl_is_minimal (acl_entry_count)");
341 return ACL_ERROR;
342 }
343
344 if (ec < 4) {
345 return ACL_SUCCESS;
346 }
347 else {
348 return ACL_FAILURE;
349 }
350 }
351
352
353
354 /**
355 * @brief Determine whether the given ACL's mask denies execute.
356 *
357 * @param acl
358 * The ACL whose mask we want to check.
359 *
360 * @return
361 * - @c ACL_SUCCESS - The @c acl has a mask which denies execute.
362 * - @c ACL_FAILURE - The @c acl has a mask which does not deny execute.
363 * - @c ACL_ERROR - Unexpected library error.
364 */
365 int acl_execute_masked(acl_t acl) {
366
367 acl_entry_t entry;
368 int ge_result = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
369
370 while (ge_result == ACL_SUCCESS) {
371 acl_tag_t tag = ACL_UNDEFINED_TAG;
372
373 if (acl_get_tag_type(entry, &tag) == ACL_ERROR) {
374 perror("acl_execute_masked (acl_get_tag_type)");
375 return ACL_ERROR;
376 }
377
378 if (tag == ACL_MASK) {
379 /* This is the mask entry, get its permissions, and see if
380 execute is specified. */
381 acl_permset_t permset;
382
383 if (acl_get_permset(entry, &permset) == ACL_ERROR) {
384 perror("acl_execute_masked (acl_get_permset)");
385 return ACL_ERROR;
386 }
387
388 int gp_result = acl_get_perm(permset, ACL_EXECUTE);
389 if (gp_result == ACL_ERROR) {
390 perror("acl_execute_masked (acl_get_perm)");
391 return ACL_ERROR;
392 }
393
394 if (gp_result == ACL_FAILURE) {
395 /* No execute bit set in the mask; execute not allowed. */
396 return ACL_SUCCESS;
397 }
398 }
399
400 ge_result = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
401 }
402
403 return ACL_FAILURE;
404 }
405
406
407
408 /**
409 * @brief Determine whether @c fd is executable by anyone.
410 *
411 *
412 * This is used as part of the heuristic to determine whether or not
413 * we should mask the execute bit when inheriting an ACL. If @c fd
414 * describes a file, we check the @a effective permissions, contrary
415 * to what setfacl does.
416 *
417 * @param fd
418 * The file descriptor to check.
419 *
420 * @param sp
421 * A pointer to a stat structure for @c fd.
422 *
423 * @return
424 * - @c ACL_SUCCESS - Someone has effective execute permissions on @c fd.
425 * - @c ACL_FAILURE - Nobody can execute @c fd.
426 * - @c ACL_ERROR - Unexpected library error.
427 */
428 int any_can_execute(int fd, const struct stat* sp) {
429 acl_t acl = acl_get_fd(fd);
430
431 if (acl == (acl_t)NULL) {
432 perror("any_can_execute (acl_get_file)");
433 return ACL_ERROR;
434 }
435
436 /* Our return value. */
437 int result = ACL_FAILURE;
438
439 if (acl_is_minimal(acl)) {
440 if (sp->st_mode & (S_IXUSR | S_IXOTH | S_IXGRP)) {
441 result = ACL_SUCCESS;
442 goto cleanup;
443 }
444 else {
445 result = ACL_FAILURE;
446 goto cleanup;
447 }
448 }
449
450 acl_entry_t entry;
451 int ge_result = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
452
453 while (ge_result == ACL_SUCCESS) {
454 /* The first thing we do is check to see if this is a mask
455 entry. If it is, we skip it entirely. */
456 acl_tag_t tag = ACL_UNDEFINED_TAG;
457
458 if (acl_get_tag_type(entry, &tag) == ACL_ERROR) {
459 perror("any_can_execute_or (acl_get_tag_type)");
460 result = ACL_ERROR;
461 goto cleanup;
462 }
463
464 if (tag == ACL_MASK) {
465 ge_result = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
466 continue;
467 }
468
469 /* Ok, so it's not a mask entry. Check the execute perms. */
470 acl_permset_t permset;
471
472 if (acl_get_permset(entry, &permset) == ACL_ERROR) {
473 perror("any_can_execute_or (acl_get_permset)");
474 result = ACL_ERROR;
475 goto cleanup;
476 }
477
478 int gp_result = acl_get_perm(permset, ACL_EXECUTE);
479 if (gp_result == ACL_ERROR) {
480 perror("any_can_execute (acl_get_perm)");
481 result = ACL_ERROR;
482 goto cleanup;
483 }
484
485 if (gp_result == ACL_SUCCESS) {
486 /* Only return ACL_SUCCESS if this execute bit is not masked. */
487 if (acl_execute_masked(acl) != ACL_SUCCESS) {
488 result = ACL_SUCCESS;
489 goto cleanup;
490 }
491 }
492
493 ge_result = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
494 }
495
496 if (ge_result == ACL_ERROR) {
497 perror("any_can_execute (acl_get_entry)");
498 result = ACL_ERROR;
499 goto cleanup;
500 }
501
502 cleanup:
503 acl_free(acl);
504 return result;
505 }
506
507
508
509 /**
510 * @brief Set @c acl as the default ACL on @c path.
511 *
512 * This overwrites any existing default ACL on @c path. If @c path is
513 * not a directory, we return ACL_ERROR and @c errno is set.
514 *
515 * @param path
516 * The target directory whose ACL we wish to replace or create.
517 *
518 * @param acl
519 * The ACL to set as default on @c path.
520 *
521 * @return
522 * - @c ACL_SUCCESS - The default ACL was assigned successfully.
523 * - @c ACL_ERROR - Unexpected library error.
524 */
525 int assign_default_acl(const char* path, acl_t acl) {
526
527 if (path == NULL) {
528 errno = EINVAL;
529 perror("assign_default_acl (args)");
530 return ACL_ERROR;
531 }
532
533 /* Our return value; success unless something bad happens. */
534 int result = ACL_SUCCESS;
535 acl_t path_acl = acl_dup(acl);
536
537 if (path_acl == (acl_t)NULL) {
538 perror("assign_default_acl (acl_dup)");
539 return ACL_ERROR; /* Nothing to clean up in this case. */
540 }
541
542 if (acl_set_file(path, ACL_TYPE_DEFAULT, path_acl) == ACL_ERROR) {
543 perror("assign_default_acl (acl_set_file)");
544 result = ACL_ERROR;
545 }
546
547 acl_free(path_acl);
548 return result;
549 }
550
551
552
553 /**
554 * @brief Remove all @c ACL_TYPE_ACCESS entries from the given file
555 * descriptor, leaving the UNIX permission bits.
556 *
557 * @param fd
558 * The file descriptor whose ACLs we want to wipe.
559 *
560 * @return
561 * - @c ACL_SUCCESS - The ACLs were wiped successfully, or none
562 * existed in the first place.
563 * - @c ACL_ERROR - Unexpected library error.
564 */
565 int wipe_acls(int fd) {
566 /* Initialize an empty ACL, and then overwrite the one on "fd" with it. */
567 acl_t empty_acl = acl_init(0);
568
569 if (empty_acl == (acl_t)NULL) {
570 perror("wipe_acls (acl_init)");
571 return ACL_ERROR;
572 }
573
574 if (acl_set_fd(fd, empty_acl) == ACL_ERROR) {
575 perror("wipe_acls (acl_set_fd)");
576 acl_free(empty_acl);
577 return ACL_ERROR;
578 }
579
580 acl_free(empty_acl);
581 return ACL_SUCCESS;
582 }
583
584
585
586 /**
587 * @brief Apply parent default ACL to a path.
588 *
589 * This overwrites any existing ACLs on @c path.
590 *
591 * @param path
592 * The path whose ACL we would like to reset to its default.
593 *
594 * @param sp
595 * A pointer to a stat structure for @c path, or @c NULL if you don't
596 * have one handy.
597 *
598 * @param no_exec_mask
599 * The value (either true or false) of the --no-exec-mask flag.
600 *
601 * @return
602 * - @c ACL_SUCCESS - The parent default ACL was inherited successfully.
603 * - @c ACL_FAILURE - If symlinks or hard links are encountered.
604 * - @c ACL_ERROR - Unexpected library error.
605 */
606 int apply_default_acl_ex(const char* path,
607 const struct stat* sp,
608 bool no_exec_mask) {
609
610 if (path == NULL) {
611 errno = EINVAL;
612 perror("apply_default_acl_ex (args)");
613 return ACL_ERROR;
614 }
615
616 /* Define these next three variables here because we may have to
617 * jump to the cleanup routine which expects them to exist.
618 */
619
620 /* Our return value. */
621 int result = ACL_SUCCESS;
622
623 /* The default ACL on path's parent directory */
624 acl_t defacl = (acl_t)NULL;
625
626 /* The file descriptor corresponding to "path" */
627 int fd = 0;
628
629 /* Get the parent directory of "path" with dirname(), which happens
630 * to murder its argument and necessitates a path_copy.
631 */
632 char* path_copy = strdup(path);
633 if (path_copy == NULL) {
634 perror("apply_default_acl_ex (strdup)");
635 return ACL_ERROR;
636 }
637 char* parent = dirname(path_copy);
638
639 fd = safe_open(path, O_NOFOLLOW);
640 if (fd == OPEN_ERROR) {
641 if (errno == ELOOP) {
642 result = ACL_FAILURE; /* hit a symlink */
643 goto cleanup;
644 }
645 else {
646 perror("apply_default_acl_ex (open fd)");
647 result = ACL_ERROR;
648 goto cleanup;
649 }
650 }
651
652
653 /* Refuse to operate on hard links, which can be abused by an
654 * attacker to trick us into changing the ACL on a file we didn't
655 * intend to; namely the "target" of the hard link. There is TOCTOU
656 * race condition here, but the window is as small as possible
657 * between when we open the file descriptor (look above) and when we
658 * fstat it.
659 *
660 * Note: we only need to call fstat ourselves if we weren't passed a
661 * valid pointer to a stat structure (nftw does that).
662 */
663 if (sp == NULL) {
664 struct stat s;
665 if (fstat(fd, &s) == STAT_ERROR) {
666 perror("apply_default_acl_ex (fstat)");
667 goto cleanup;
668 }
669
670 sp = &s;
671 }
672
673 if (!S_ISDIR(sp->st_mode)) {
674 /* If it's not a directory, make sure it's a regular,
675 non-hard-linked file. */
676 if (!S_ISREG(sp->st_mode) || sp->st_nlink != 1) {
677 result = ACL_FAILURE;
678 goto cleanup;
679 }
680 }
681
682
683 /* Default to not masking the exec bit; i.e. applying the default
684 ACL literally. If --no-exec-mask was not specified, then we try
685 to "guess" whether or not to mask the exec bit. This behavior
686 is modeled after the capital 'X' perms of setfacl. */
687 bool allow_exec = true;
688
689 if (!no_exec_mask) {
690 /* Never mask the execute bit on directories. */
691 int ace_result = any_can_execute(fd,sp) || S_ISDIR(sp->st_mode);
692
693 if (ace_result == ACL_ERROR) {
694 perror("apply_default_acl_ex (any_can_execute)");
695 result = ACL_ERROR;
696 goto cleanup;
697 }
698
699 allow_exec = (bool)ace_result;
700 }
701
702 defacl = acl_get_file(parent, ACL_TYPE_DEFAULT);
703
704 if (defacl == (acl_t)NULL) {
705 perror("apply_default_acl_ex (acl_get_file)");
706 result = ACL_ERROR;
707 goto cleanup;
708 }
709
710 if (wipe_acls(fd) == ACL_ERROR) {
711 perror("apply_default_acl_ex (wipe_acls)");
712 result = ACL_ERROR;
713 goto cleanup;
714 }
715
716 /* Do this after wipe_acls(), otherwise we'll overwrite the wiped
717 ACL with this one. */
718 acl_t acl = acl_get_fd(fd);
719 if (acl == (acl_t)NULL) {
720 perror("apply_default_acl_ex (acl_get_fd)");
721 result = ACL_ERROR;
722 goto cleanup;
723 }
724
725 /* If it's a directory, inherit the parent's default. We sure hope
726 * that "path" still points to the same thing that "fd" and this
727 * "sp" describe. If not, we may wind up trying to set a default ACL
728 * on a file, and this will throw an error. I guess that's what we
729 * want to do?
730 */
731 if (S_ISDIR(sp->st_mode) && assign_default_acl(path, defacl) == ACL_ERROR) {
732 perror("apply_default_acl_ex (assign_default_acl)");
733 result = ACL_ERROR;
734 goto cleanup;
735 }
736
737 acl_entry_t entry;
738 int ge_result = acl_get_entry(defacl, ACL_FIRST_ENTRY, &entry);
739
740 while (ge_result == ACL_SUCCESS) {
741 acl_tag_t tag = ACL_UNDEFINED_TAG;
742
743 if (acl_get_tag_type(entry, &tag) == ACL_ERROR) {
744 perror("apply_default_acl_ex (acl_get_tag_type)");
745 result = ACL_ERROR;
746 goto cleanup;
747 }
748
749
750 /* We've got an entry/tag from the default ACL. Get its permset. */
751 acl_permset_t permset;
752 if (acl_get_permset(entry, &permset) == ACL_ERROR) {
753 perror("apply_default_acl_ex (acl_get_permset)");
754 result = ACL_ERROR;
755 goto cleanup;
756 }
757
758 /* If this is a default mask, fix it up. */
759 if (tag == ACL_MASK ||
760 tag == ACL_USER_OBJ ||
761 tag == ACL_GROUP_OBJ ||
762 tag == ACL_OTHER) {
763
764 if (!allow_exec) {
765 /* The mask doesn't affect acl_user_obj, acl_group_obj (in
766 minimal ACLs) or acl_other entries, so if execute should be
767 masked, we have to do it manually. */
768 if (acl_delete_perm(permset, ACL_EXECUTE) == ACL_ERROR) {
769 perror("apply_default_acl_ex (acl_delete_perm)");
770 result = ACL_ERROR;
771 goto cleanup;
772 }
773
774 if (acl_set_permset(entry, permset) == ACL_ERROR) {
775 perror("apply_default_acl_ex (acl_set_permset)");
776 result = ACL_ERROR;
777 goto cleanup;
778 }
779 }
780 }
781
782 /* Finally, add the permset to the access ACL. It's actually
783 * important that we pass in the address of "acl" here, and not
784 * "acl" itself. Why? The call to acl_create_entry() within
785 * acl_set_entry() can allocate new memory for the entry.
786 * Sometimes that can be done in-place, in which case everything
787 * is cool and the new memory gets released when we call
788 * acl_free(acl).
789 *
790 * But occasionally, the whole ACL structure will have to be moved
791 * in order to allocate the extra space. When that happens,
792 * acl_create_entry() modifies the pointer it was passed (in this
793 * case, &acl) to point to the new location. We want to call
794 * acl_free() on the new location, and since acl_free() gets
795 * called right here, we need acl_create_entry() to update the
796 * value of "acl". To do that, it needs the address of "acl".
797 */
798 if (acl_set_entry(&acl, entry) == ACL_ERROR) {
799 perror("apply_default_acl_ex (acl_set_entry)");
800 result = ACL_ERROR;
801 goto cleanup;
802 }
803
804 ge_result = acl_get_entry(defacl, ACL_NEXT_ENTRY, &entry);
805 }
806
807 /* Catches the first acl_get_entry as well as the ones at the end of
808 the loop. */
809 if (ge_result == ACL_ERROR) {
810 perror("apply_default_acl_ex (acl_get_entry)");
811 result = ACL_ERROR;
812 goto cleanup;
813 }
814
815 if (acl_set_fd(fd, acl) == ACL_ERROR) {
816 perror("apply_default_acl_ex (acl_set_fd)");
817 result = ACL_ERROR;
818 goto cleanup;
819 }
820
821 cleanup:
822 free(path_copy);
823 if (defacl != (acl_t)NULL) {
824 acl_free(defacl);
825 }
826 if (fd >= 0 && close(fd) == CLOSE_ERROR) {
827 perror("apply_default_acl_ex (close)");
828 result = ACL_ERROR;
829 }
830 return result;
831 }
832
833
834
835 /**
836 * @brief The friendly interface to @c apply_default_acl_ex.
837 *
838 * The @c apply_default_acl_ex function holds the real implementation
839 * of this function, but it takes a weird second argument that most
840 * people won't care about (a stat structure). But, we use that
841 * argument for the recursive mode of the CLI, so it's there.
842 *
843 * If you don't have a stat structure for your @c path, use this instead.
844 *
845 * @param path
846 * The path whose ACL we would like to reset to its default.
847 *
848 * @param no_exec_mask
849 * The value (either true or false) of the --no-exec-mask flag.
850 *
851 * @return
852 * - @c ACL_SUCCESS - The parent default ACL was inherited successfully.
853 * - @c ACL_FAILURE - If symlinks or hard links are encountered.
854 * or the parent of @c path is not a directory.
855 * - @c ACL_ERROR - Unexpected library error.
856 */
857 int apply_default_acl(const char* path, bool no_exec_mask) {
858 return apply_default_acl_ex(path, NULL, no_exec_mask);
859 }