]> gitweb.michael.orlitzky.com - apply-default-acl.git/blob - src/apply-default-acl.c
Define a few more error constants to make the code more readable.
[apply-default-acl.git] / src / apply-default-acl.c
1 /**
2 * @file apply-default-acl.c
3 *
4 * @brief The entire implementation.
5 *
6 */
7
8 /* On Linux, ftw.h needs this special voodoo to work. */
9 #define _XOPEN_SOURCE 500
10 #define _GNU_SOURCE
11
12 #include <errno.h>
13 #include <fcntl.h> /* AT_FOO constants */
14 #include <ftw.h> /* nftw() et al. */
15 #include <getopt.h>
16 #include <libgen.h> /* basename(), dirname() */
17 #include <limits.h> /* PATH_MAX */
18 #include <stdbool.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24
25 /* ACLs */
26 #include <acl/libacl.h> /* acl_get_perm, not portable */
27 #include <sys/types.h>
28 #include <sys/acl.h>
29
30 /* Most of the libacl functions return 1 for success, 0 for failure,
31 and -1 on error */
32 #define ACL_ERROR -1
33 #define ACL_FAILURE 0
34 #define ACL_SUCCESS 1
35
36 /* Even though most other library functions reliably return -1 for
37 * error, it feels a little wrong to re-use the ACL_ERROR constant.
38 */
39 #define CLOSE_ERROR -1
40 #define NFTW_ERROR -1
41 #define OPEN_ERROR -1
42 #define SNPRINTF_ERROR -1
43 #define STAT_ERROR -1
44
45 int safe_open_ex(int at_fd, char* pathname, int flags) {
46 if (pathname != NULL && strlen(pathname) == 0) {
47 /* Oops, went one level to deep with nothing to do. */
48 return at_fd;
49 }
50
51 char* firstslash = strchr(pathname, '/');
52 if (firstslash == NULL) {
53 /* No more slashes, this is the base case. */
54 int r = openat(at_fd, pathname, flags);
55 return r;
56 }
57
58 /* Temporarily disable the slash, so that the subsequent call to
59 openat() opens only the next directory (and doesn't recurse). */
60 *firstslash = '\0';
61 int fd = safe_open_ex(at_fd, pathname, flags);
62 if (fd == OPEN_ERROR) {
63 if (errno != ELOOP) {
64 /* Don't output anything if we ignore a symlink */
65 perror("safe_open_ex (safe_open_ex)");
66 }
67 return OPEN_ERROR;
68 }
69
70 /* The ++ is safe because there needs to be at least a null byte
71 after the first slash, even if it's the last real character in
72 the string. */
73 int result = safe_open_ex(fd, firstslash+1, flags);
74 if (close(fd) == CLOSE_ERROR) {
75 perror("safe_open_ex (close)");
76 return OPEN_ERROR;
77 }
78 return result;
79 }
80
81
82 int safe_open(const char* pathname, int flags) {
83 if (pathname == NULL || strlen(pathname) == 0 || pathname[0] == '\0') {
84 /* error? */
85 return OPEN_ERROR;
86 }
87
88 char abspath[PATH_MAX];
89 int snprintf_result = 0;
90 if (strchr(pathname, '/') == pathname) {
91 /* pathname is already absolute; just copy it. */
92 snprintf_result = snprintf(abspath, PATH_MAX, "%s", pathname);
93 }
94 else {
95 /* Concatenate the current working directory and pathname into an
96 * absolute path. We use realpath() ONLY on the cwd part, and not
97 * on the pathname part, because realpath() resolves symlinks. And
98 * the whole point of all this crap is to avoid following symlinks
99 * in the pathname.
100 *
101 * Using realpath() on the cwd lets us operate on relative paths
102 * while we're sitting in a directory that happens to have a
103 * symlink in it; for example: cd /var/run && apply-default-acl foo.
104 */
105 char* cwd = get_current_dir_name();
106 if (cwd == NULL) {
107 perror("safe_open (get_current_dir_name)");
108 return OPEN_ERROR;
109 }
110
111 char abs_cwd[PATH_MAX];
112 if (realpath(cwd, abs_cwd) == NULL) {
113 perror("safe_open (realpath)");
114 free(cwd);
115 return OPEN_ERROR;
116 }
117 snprintf_result = snprintf(abspath, PATH_MAX, "%s/%s", abs_cwd, pathname);
118 free(cwd);
119 }
120 if (snprintf_result == SNPRINTF_ERROR || snprintf_result > PATH_MAX) {
121 perror("safe_open (snprintf)");
122 return OPEN_ERROR;
123 }
124
125 int fd = open("/", flags);
126 if (strcmp(abspath, "/") == 0) {
127 return fd;
128 }
129
130 int result = safe_open_ex(fd, abspath+1, flags);
131 if (close(fd) == CLOSE_ERROR) {
132 perror("safe_open (close)");
133 return OPEN_ERROR;
134 }
135 return result;
136 }
137
138
139
140 /**
141 * @brief Determine whether or not the given path is accessible.
142 *
143 * @param path
144 * The path to test.
145 *
146 * @return true if @c path is accessible to the current effective
147 * user/group, false otherwise.
148 */
149 bool path_accessible(const char* path) {
150 if (path == NULL) {
151 return false;
152 }
153
154 /* Test for access using the effective user and group rather than
155 the real one. */
156 int flags = AT_EACCESS;
157
158 /* Don't follow symlinks when checking for a path's existence,
159 since we won't follow them to set its ACLs either. */
160 flags |= AT_SYMLINK_NOFOLLOW;
161
162 /* If the path is relative, interpret it relative to the current
163 working directory (just like the access() system call). */
164 if (faccessat(AT_FDCWD, path, F_OK, flags) == 0) {
165 return true;
166 }
167 else {
168 return false;
169 }
170 }
171
172
173
174 /**
175 * @brief Update (or create) an entry in an @b minimal ACL.
176 *
177 * This function will not work if @c aclp contains extended
178 * entries. This is fine for our purposes, since we call @c wipe_acls
179 * on each path before applying the default to it.
180 *
181 * The assumption that there are no extended entries makes things much
182 * simpler. For example, we only have to update the @c ACL_USER_OBJ,
183 * @c ACL_GROUP_OBJ, and @c ACL_OTHER entries -- all others can simply
184 * be created anew. This means we don't have to fool around comparing
185 * named-user/group entries.
186 *
187 * @param aclp
188 * A pointer to the acl_t structure whose entry we want to modify.
189 *
190 * @param entry
191 * The new entry. If @c entry contains a user/group/other entry, we
192 * update the existing one. Otherwise we create a new entry.
193 *
194 * @return If there is an unexpected library error, @c ACL_ERROR is
195 * returned. Otherwise, @c ACL_SUCCESS.
196 *
197 */
198 int acl_set_entry(acl_t* aclp, acl_entry_t entry) {
199
200 acl_tag_t entry_tag;
201 if (acl_get_tag_type(entry, &entry_tag) == ACL_ERROR) {
202 perror("acl_set_entry (acl_get_tag_type)");
203 return ACL_ERROR;
204 }
205
206 acl_permset_t entry_permset;
207 if (acl_get_permset(entry, &entry_permset) == ACL_ERROR) {
208 perror("acl_set_entry (acl_get_permset)");
209 return ACL_ERROR;
210 }
211
212 acl_entry_t existing_entry;
213 /* Loop through the given ACL looking for matching entries. */
214 int result = acl_get_entry(*aclp, ACL_FIRST_ENTRY, &existing_entry);
215
216 while (result == ACL_SUCCESS) {
217 acl_tag_t existing_tag = ACL_UNDEFINED_TAG;
218
219 if (acl_get_tag_type(existing_entry, &existing_tag) == ACL_ERROR) {
220 perror("set_acl_tag_permset (acl_get_tag_type)");
221 return ACL_ERROR;
222 }
223
224 if (existing_tag == entry_tag) {
225 if (entry_tag == ACL_USER_OBJ ||
226 entry_tag == ACL_GROUP_OBJ ||
227 entry_tag == ACL_OTHER) {
228 /* Only update for these three since all other tags will have
229 been wiped. These three are guaranteed to exist, so if we
230 match one of them, we're allowed to return ACL_SUCCESS
231 below and bypass the rest of the function. */
232 acl_permset_t existing_permset;
233 if (acl_get_permset(existing_entry, &existing_permset) == ACL_ERROR) {
234 perror("acl_set_entry (acl_get_permset)");
235 return ACL_ERROR;
236 }
237
238 if (acl_set_permset(existing_entry, entry_permset) == ACL_ERROR) {
239 perror("acl_set_entry (acl_set_permset)");
240 return ACL_ERROR;
241 }
242
243 return ACL_SUCCESS;
244 }
245
246 }
247
248 result = acl_get_entry(*aclp, ACL_NEXT_ENTRY, &existing_entry);
249 }
250
251 /* This catches both the initial acl_get_entry and the ones at the
252 end of the loop. */
253 if (result == ACL_ERROR) {
254 perror("acl_set_entry (acl_get_entry)");
255 return ACL_ERROR;
256 }
257
258 /* If we've made it this far, we need to add a new entry to the
259 ACL. */
260 acl_entry_t new_entry;
261
262 /* The acl_create_entry() function can allocate new memory and/or
263 * change the location of the ACL structure entirely. When that
264 * happens, the value pointed to by aclp is updated, which means
265 * that a new acl_t gets "passed out" to our caller, eventually to
266 * be fed to acl_free(). In other words, we should still be freeing
267 * the right thing, even if the value pointed to by aclp changes.
268 */
269 if (acl_create_entry(aclp, &new_entry) == ACL_ERROR) {
270 perror("acl_set_entry (acl_create_entry)");
271 return ACL_ERROR;
272 }
273
274 if (acl_set_tag_type(new_entry, entry_tag) == ACL_ERROR) {
275 perror("acl_set_entry (acl_set_tag_type)");
276 return ACL_ERROR;
277 }
278
279 if (acl_set_permset(new_entry, entry_permset) == ACL_ERROR) {
280 perror("acl_set_entry (acl_set_permset)");
281 return ACL_ERROR;
282 }
283
284 if (entry_tag == ACL_USER || entry_tag == ACL_GROUP) {
285 /* We need to set the qualifier too. */
286 void* entry_qual = acl_get_qualifier(entry);
287 if (entry_qual == (void*)NULL) {
288 perror("acl_set_entry (acl_get_qualifier)");
289 return ACL_ERROR;
290 }
291
292 if (acl_set_qualifier(new_entry, entry_qual) == ACL_ERROR) {
293 perror("acl_set_entry (acl_set_qualifier)");
294 return ACL_ERROR;
295 }
296 }
297
298 return ACL_SUCCESS;
299 }
300
301
302
303 /**
304 * @brief Determine the number of entries in the given ACL.
305 *
306 * @param acl
307 * The ACL to inspect.
308 *
309 * @return Either the non-negative number of entries in @c acl, or
310 * @c ACL_ERROR on error.
311 */
312 int acl_entry_count(acl_t acl) {
313
314 acl_entry_t entry;
315 int entry_count = 0;
316 int result = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
317
318 while (result == ACL_SUCCESS) {
319 entry_count++;
320 result = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
321 }
322
323 if (result == ACL_ERROR) {
324 perror("acl_entry_count (acl_get_entry)");
325 return ACL_ERROR;
326 }
327
328 return entry_count;
329 }
330
331
332
333 /**
334 * @brief Determine whether or not the given ACL is minimal.
335 *
336 * An ACL is minimal if it has fewer than four entries.
337 *
338 * @param acl
339 * The ACL whose minimality is in question.
340 *
341 * @return
342 * - @c ACL_SUCCESS - @c acl is minimal
343 * - @c ACL_FAILURE - @c acl is not minimal
344 * - @c ACL_ERROR - Unexpected library error
345 */
346 int acl_is_minimal(acl_t acl) {
347
348 int ec = acl_entry_count(acl);
349
350 if (ec == ACL_ERROR) {
351 perror("acl_is_minimal (acl_entry_count)");
352 return ACL_ERROR;
353 }
354
355 if (ec < 4) {
356 return ACL_SUCCESS;
357 }
358 else {
359 return ACL_FAILURE;
360 }
361 }
362
363
364
365 /**
366 * @brief Determine whether the given ACL's mask denies execute.
367 *
368 * @param acl
369 * The ACL whose mask we want to check.
370 *
371 * @return
372 * - @c ACL_SUCCESS - The @c acl has a mask which denies execute.
373 * - @c ACL_FAILURE - The @c acl has a mask which does not deny execute.
374 * - @c ACL_ERROR - Unexpected library error.
375 */
376 int acl_execute_masked(acl_t acl) {
377
378 acl_entry_t entry;
379 int ge_result = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
380
381 while (ge_result == ACL_SUCCESS) {
382 acl_tag_t tag = ACL_UNDEFINED_TAG;
383
384 if (acl_get_tag_type(entry, &tag) == ACL_ERROR) {
385 perror("acl_execute_masked (acl_get_tag_type)");
386 return ACL_ERROR;
387 }
388
389 if (tag == ACL_MASK) {
390 /* This is the mask entry, get its permissions, and see if
391 execute is specified. */
392 acl_permset_t permset;
393
394 if (acl_get_permset(entry, &permset) == ACL_ERROR) {
395 perror("acl_execute_masked (acl_get_permset)");
396 return ACL_ERROR;
397 }
398
399 int gp_result = acl_get_perm(permset, ACL_EXECUTE);
400 if (gp_result == ACL_ERROR) {
401 perror("acl_execute_masked (acl_get_perm)");
402 return ACL_ERROR;
403 }
404
405 if (gp_result == ACL_FAILURE) {
406 /* No execute bit set in the mask; execute not allowed. */
407 return ACL_SUCCESS;
408 }
409 }
410
411 ge_result = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
412 }
413
414 return ACL_FAILURE;
415 }
416
417
418
419 /**
420 * @brief Determine whether @c fd is executable by anyone.
421 *
422 *
423 * This is used as part of the heuristic to determine whether or not
424 * we should mask the execute bit when inheriting an ACL. If @c fd
425 * describes a file, we check the @a effective permissions, contrary
426 * to what setfacl does.
427 *
428 * @param fd
429 * The file descriptor to check.
430 *
431 * @param sp
432 * A pointer to a stat structure for @c fd.
433 *
434 * @return
435 * - @c ACL_SUCCESS - Someone has effective execute permissions on @c fd.
436 * - @c ACL_FAILURE - Nobody can execute @c fd.
437 * - @c ACL_ERROR - Unexpected library error.
438 */
439 int any_can_execute(int fd, const struct stat* sp) {
440 acl_t acl = acl_get_fd(fd);
441
442 if (acl == (acl_t)NULL) {
443 perror("any_can_execute (acl_get_file)");
444 return ACL_ERROR;
445 }
446
447 /* Our return value. */
448 int result = ACL_FAILURE;
449
450 if (acl_is_minimal(acl)) {
451 if (sp->st_mode & (S_IXUSR | S_IXOTH | S_IXGRP)) {
452 result = ACL_SUCCESS;
453 goto cleanup;
454 }
455 else {
456 result = ACL_FAILURE;
457 goto cleanup;
458 }
459 }
460
461 acl_entry_t entry;
462 int ge_result = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
463
464 while (ge_result == ACL_SUCCESS) {
465 /* The first thing we do is check to see if this is a mask
466 entry. If it is, we skip it entirely. */
467 acl_tag_t tag = ACL_UNDEFINED_TAG;
468
469 if (acl_get_tag_type(entry, &tag) == ACL_ERROR) {
470 perror("any_can_execute_or (acl_get_tag_type)");
471 result = ACL_ERROR;
472 goto cleanup;
473 }
474
475 if (tag == ACL_MASK) {
476 ge_result = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
477 continue;
478 }
479
480 /* Ok, so it's not a mask entry. Check the execute perms. */
481 acl_permset_t permset;
482
483 if (acl_get_permset(entry, &permset) == ACL_ERROR) {
484 perror("any_can_execute_or (acl_get_permset)");
485 result = ACL_ERROR;
486 goto cleanup;
487 }
488
489 int gp_result = acl_get_perm(permset, ACL_EXECUTE);
490 if (gp_result == ACL_ERROR) {
491 perror("any_can_execute (acl_get_perm)");
492 result = ACL_ERROR;
493 goto cleanup;
494 }
495
496 if (gp_result == ACL_SUCCESS) {
497 /* Only return ACL_SUCCESS if this execute bit is not masked. */
498 if (acl_execute_masked(acl) != ACL_SUCCESS) {
499 result = ACL_SUCCESS;
500 goto cleanup;
501 }
502 }
503
504 ge_result = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
505 }
506
507 if (ge_result == ACL_ERROR) {
508 perror("any_can_execute (acl_get_entry)");
509 result = ACL_ERROR;
510 goto cleanup;
511 }
512
513 cleanup:
514 acl_free(acl);
515 return result;
516 }
517
518
519
520 /**
521 * @brief Set @c acl as the default ACL on @c path.
522 *
523 * This overwrites any existing default ACL on @c path. If @c path is
524 * not a directory, we return ACL_ERROR and @c errno is set.
525 *
526 * @param path
527 * The target directory whose ACL we wish to replace or create.
528 *
529 * @param acl
530 * The ACL to set as default on @c path.
531 *
532 * @return
533 * - @c ACL_SUCCESS - The default ACL was assigned successfully.
534 * - @c ACL_ERROR - Unexpected library error.
535 */
536 int assign_default_acl(const char* path, acl_t acl) {
537
538 if (path == NULL) {
539 errno = EINVAL;
540 perror("assign_default_acl (args)");
541 return ACL_ERROR;
542 }
543
544 /* Our return value; success unless something bad happens. */
545 int result = ACL_SUCCESS;
546 acl_t path_acl = acl_dup(acl);
547
548 if (path_acl == (acl_t)NULL) {
549 perror("assign_default_acl (acl_dup)");
550 return ACL_ERROR; /* Nothing to clean up in this case. */
551 }
552
553 if (acl_set_file(path, ACL_TYPE_DEFAULT, path_acl) == ACL_ERROR) {
554 perror("assign_default_acl (acl_set_file)");
555 result = ACL_ERROR;
556 }
557
558 acl_free(path_acl);
559 return result;
560 }
561
562
563
564 /**
565 * @brief Remove all @c ACL_TYPE_ACCESS entries from the given file
566 * descriptor, leaving the UNIX permission bits.
567 *
568 * @param fd
569 * The file descriptor whose ACLs we want to wipe.
570 *
571 * @return
572 * - @c ACL_SUCCESS - The ACLs were wiped successfully, or none
573 * existed in the first place.
574 * - @c ACL_ERROR - Unexpected library error.
575 */
576 int wipe_acls(int fd) {
577 /* Initialize an empty ACL, and then overwrite the one on "fd" with it. */
578 acl_t empty_acl = acl_init(0);
579
580 if (empty_acl == (acl_t)NULL) {
581 perror("wipe_acls (acl_init)");
582 return ACL_ERROR;
583 }
584
585 if (acl_set_fd(fd, empty_acl) == ACL_ERROR) {
586 perror("wipe_acls (acl_set_fd)");
587 acl_free(empty_acl);
588 return ACL_ERROR;
589 }
590
591 acl_free(empty_acl);
592 return ACL_SUCCESS;
593 }
594
595
596
597 /**
598 * @brief Apply parent default ACL to a path.
599 *
600 * This overwrites any existing ACLs on @c path.
601 *
602 * @param path
603 * The path whose ACL we would like to reset to its default.
604 *
605 * @param sp
606 * A pointer to a stat structure for @c path, or @c NULL if you don't
607 * have one handy.
608 *
609 * @param no_exec_mask
610 * The value (either true or false) of the --no-exec-mask flag.
611 *
612 * @return
613 * - @c ACL_SUCCESS - The parent default ACL was inherited successfully.
614 * - @c ACL_FAILURE - The target path is not a regular file/directory,
615 * or the parent of @c path is not a directory.
616 * - @c ACL_ERROR - Unexpected library error.
617 */
618 int apply_default_acl(const char* path,
619 const struct stat* sp,
620 bool no_exec_mask) {
621
622 if (path == NULL) {
623 errno = EINVAL;
624 perror("apply_default_acl (args)");
625 return ACL_ERROR;
626 }
627
628 /* Define these next three variables here because we may have to
629 * jump to the cleanup routine which expects them to exist.
630 */
631
632 /* Our return value. */
633 int result = ACL_SUCCESS;
634
635 /* The default ACL on path's parent directory */
636 acl_t defacl = (acl_t)NULL;
637
638 /* The file descriptor corresponding to "path" */
639 int fd = 0;
640
641 /* Get the parent directory of "path" with dirname(), which happens
642 * to murder its argument and necessitates a path_copy.
643 */
644 char* path_copy = strdup(path);
645 if (path_copy == NULL) {
646 perror("apply_default_acl (strdup)");
647 return ACL_ERROR;
648 }
649 char* parent = dirname(path_copy);
650
651 fd = safe_open(path, O_NOFOLLOW);
652 if (fd == OPEN_ERROR) {
653 if (errno == ELOOP) {
654 result = ACL_FAILURE; /* hit a symlink */
655 goto cleanup;
656 }
657 else {
658 perror("apply_default_acl (open fd)");
659 result = ACL_ERROR;
660 goto cleanup;
661 }
662 }
663
664
665 /* Refuse to operate on hard links, which can be abused by an
666 * attacker to trick us into changing the ACL on a file we didn't
667 * intend to; namely the "target" of the hard link. There is TOCTOU
668 * race condition here, but the window is as small as possible
669 * between when we open the file descriptor (look above) and when we
670 * fstat it.
671 *
672 * Note: we only need to call fstat ourselves if we weren't passed a
673 * valid pointer to a stat structure (nftw does that).
674 */
675 if (sp == NULL) {
676 struct stat s;
677 if (fstat(fd, &s) == STAT_ERROR) {
678 perror("apply_default_acl (fstat)");
679 goto cleanup;
680 }
681
682 sp = &s;
683 }
684
685 if (!S_ISDIR(sp->st_mode)) {
686 /* If it's not a directory, make sure it's a regular,
687 non-hard-linked file. */
688 if (!S_ISREG(sp->st_mode) || sp->st_nlink != 1) {
689 result = ACL_FAILURE;
690 goto cleanup;
691 }
692 }
693
694
695 /* Default to not masking the exec bit; i.e. applying the default
696 ACL literally. If --no-exec-mask was not specified, then we try
697 to "guess" whether or not to mask the exec bit. This behavior
698 is modeled after the capital 'X' perms of setfacl. */
699 bool allow_exec = true;
700
701 if (!no_exec_mask) {
702 /* Never mask the execute bit on directories. */
703 int ace_result = any_can_execute(fd,sp) || S_ISDIR(sp->st_mode);
704
705 if (ace_result == ACL_ERROR) {
706 perror("apply_default_acl (any_can_execute)");
707 result = ACL_ERROR;
708 goto cleanup;
709 }
710
711 allow_exec = (bool)ace_result;
712 }
713
714 defacl = acl_get_file(parent, ACL_TYPE_DEFAULT);
715
716 if (defacl == (acl_t)NULL) {
717 perror("apply_default_acl (acl_get_file)");
718 result = ACL_ERROR;
719 goto cleanup;
720 }
721
722 if (wipe_acls(fd) == ACL_ERROR) {
723 perror("apply_default_acl (wipe_acls)");
724 result = ACL_ERROR;
725 goto cleanup;
726 }
727
728 /* Do this after wipe_acls(), otherwise we'll overwrite the wiped
729 ACL with this one. */
730 acl_t acl = acl_get_fd(fd);
731 if (acl == (acl_t)NULL) {
732 perror("apply_default_acl (acl_get_fd)");
733 result = ACL_ERROR;
734 goto cleanup;
735 }
736
737 /* If it's a directory, inherit the parent's default. We sure hope
738 * that "path" still points to the same thing that "fd" and this
739 * "sp" describe. If not, we may wind up trying to set a default ACL
740 * on a file, and this will throw an error. I guess that's what we
741 * want to do?
742 */
743 if (S_ISDIR(sp->st_mode) && assign_default_acl(path, defacl) == ACL_ERROR) {
744 perror("apply_default_acl (assign_default_acl)");
745 result = ACL_ERROR;
746 goto cleanup;
747 }
748
749 acl_entry_t entry;
750 int ge_result = acl_get_entry(defacl, ACL_FIRST_ENTRY, &entry);
751
752 while (ge_result == ACL_SUCCESS) {
753 acl_tag_t tag = ACL_UNDEFINED_TAG;
754
755 if (acl_get_tag_type(entry, &tag) == ACL_ERROR) {
756 perror("apply_default_acl (acl_get_tag_type)");
757 result = ACL_ERROR;
758 goto cleanup;
759 }
760
761
762 /* We've got an entry/tag from the default ACL. Get its permset. */
763 acl_permset_t permset;
764 if (acl_get_permset(entry, &permset) == ACL_ERROR) {
765 perror("apply_default_acl (acl_get_permset)");
766 result = ACL_ERROR;
767 goto cleanup;
768 }
769
770 /* If this is a default mask, fix it up. */
771 if (tag == ACL_MASK ||
772 tag == ACL_USER_OBJ ||
773 tag == ACL_GROUP_OBJ ||
774 tag == ACL_OTHER) {
775
776 if (!allow_exec) {
777 /* The mask doesn't affect acl_user_obj, acl_group_obj (in
778 minimal ACLs) or acl_other entries, so if execute should be
779 masked, we have to do it manually. */
780 if (acl_delete_perm(permset, ACL_EXECUTE) == ACL_ERROR) {
781 perror("apply_default_acl (acl_delete_perm)");
782 result = ACL_ERROR;
783 goto cleanup;
784 }
785
786 if (acl_set_permset(entry, permset) == ACL_ERROR) {
787 perror("apply_default_acl (acl_set_permset)");
788 result = ACL_ERROR;
789 goto cleanup;
790 }
791 }
792 }
793
794 /* Finally, add the permset to the access ACL. It's actually
795 * important that we pass in the address of "acl" here, and not
796 * "acl" itself. Why? The call to acl_create_entry() within
797 * acl_set_entry() can allocate new memory for the entry.
798 * Sometimes that can be done in-place, in which case everything
799 * is cool and the new memory gets released when we call
800 * acl_free(acl).
801 *
802 * But occasionally, the whole ACL structure will have to be moved
803 * in order to allocate the extra space. When that happens,
804 * acl_create_entry() modifies the pointer it was passed (in this
805 * case, &acl) to point to the new location. We want to call
806 * acl_free() on the new location, and since acl_free() gets
807 * called right here, we need acl_create_entry() to update the
808 * value of "acl". To do that, it needs the address of "acl".
809 */
810 if (acl_set_entry(&acl, entry) == ACL_ERROR) {
811 perror("apply_default_acl (acl_set_entry)");
812 result = ACL_ERROR;
813 goto cleanup;
814 }
815
816 ge_result = acl_get_entry(defacl, ACL_NEXT_ENTRY, &entry);
817 }
818
819 /* Catches the first acl_get_entry as well as the ones at the end of
820 the loop. */
821 if (ge_result == ACL_ERROR) {
822 perror("apply_default_acl (acl_get_entry)");
823 result = ACL_ERROR;
824 goto cleanup;
825 }
826
827 if (acl_set_fd(fd, acl) == ACL_ERROR) {
828 perror("apply_default_acl (acl_set_fd)");
829 result = ACL_ERROR;
830 goto cleanup;
831 }
832
833 cleanup:
834 free(path_copy);
835 if (defacl != (acl_t)NULL) {
836 acl_free(defacl);
837 }
838 if (fd >= 0 && close(fd) == CLOSE_ERROR) {
839 perror("apply_default_acl (close)");
840 result = ACL_ERROR;
841 }
842 return result;
843 }
844
845
846
847 /**
848 * @brief Display program usage information.
849 *
850 * @param program_name
851 * The program name to use in the output.
852 *
853 */
854 void usage(const char* program_name) {
855 printf("Apply any applicable default ACLs to the given files or "
856 "directories.\n\n");
857 printf("Usage: %s [flags] <target1> [<target2> [ <target3>...]]\n\n",
858 program_name);
859 printf("Flags:\n");
860 printf(" -h, --help Print this help message\n");
861 printf(" -r, --recursive Act on any given directories recursively\n");
862 printf(" -x, --no-exec-mask Apply execute permissions unconditionally\n");
863
864 return;
865 }
866
867
868 /**
869 * @brief Wrapper around @c apply_default_acl() for use with @c nftw().
870 *
871 * For parameter information, see the @c nftw man page.
872 *
873 * @return If the ACL was applied to @c target successfully, we return
874 * @c FTW_CONTINUE to signal to @ nftw() that we should proceed onto
875 * the next file or directory. Otherwise, we return @c FTW_STOP to
876 * signal failure.
877 *
878 */
879 int apply_default_acl_nftw(const char *target,
880 const struct stat *sp,
881 int info,
882 struct FTW *ftw) {
883
884 if (apply_default_acl(target, sp, false)) {
885 return FTW_CONTINUE;
886 }
887 else {
888 return FTW_STOP;
889 }
890 }
891
892
893
894 /**
895 * @brief Wrapper around @c apply_default_acl() for use with @c nftw().
896 *
897 * This is identical to @c apply_default_acl_nftw(), except it passes
898 * @c true to @c apply_default_acl() as its no_exec_mask argument.
899 *
900 */
901 int apply_default_acl_nftw_x(const char *target,
902 const struct stat *sp,
903 int info,
904 struct FTW *ftw) {
905
906 if (apply_default_acl(target, sp, true)) {
907 return FTW_CONTINUE;
908 }
909 else {
910 return FTW_STOP;
911 }
912 }
913
914
915
916 /**
917 * @brief Recursive version of @c apply_default_acl().
918 *
919 * If @c target is a directory, we use @c nftw() to call @c
920 * apply_default_acl() recursively on all of its children. Otherwise,
921 * we just delegate to @c apply_default_acl().
922 *
923 * We ignore symlinks for consistency with chmod -r.
924 *
925 * @param target
926 * The root (path) of the recursive application.
927 *
928 * @param no_exec_mask
929 * The value (either true or false) of the --no-exec-mask flag.
930 *
931 * @return
932 * If @c target is not a directory, we return the result of
933 * calling @c apply_default_acl() on @c target. Otherwise, we convert
934 * the return value of @c nftw(). If @c nftw() succeeds (returns 0),
935 * then we return @c true. Otherwise, we return @c false.
936 * \n\n
937 * If there is an error, it will be reported via @c perror, but
938 * we still return @c false.
939 */
940 bool apply_default_acl_recursive(const char *target, bool no_exec_mask) {
941 int max_levels = 256;
942 int flags = FTW_PHYS; /* Don't follow links. */
943
944 /* There are two separate functions that could be passed to
945 nftw(). One passes no_exec_mask = true to apply_default_acl(),
946 and the other passes no_exec_mask = false. Since the function we
947 pass to nftw() cannot have parameters, we have to create separate
948 options and make the decision here. */
949 int (*fn)(const char *, const struct stat *, int, struct FTW *) = NULL;
950 fn = no_exec_mask ? apply_default_acl_nftw_x : apply_default_acl_nftw;
951
952 int nftw_result = nftw(target, fn, max_levels, flags);
953
954 if (nftw_result == 0) {
955 /* Success */
956 return true;
957 }
958
959 /* nftw will return NFTW_ERROR on error, or if the supplied function
960 * (apply_default_acl_nftw) returns a non-zero result, nftw will
961 * return that.
962 */
963 if (nftw_result == NFTW_ERROR) {
964 perror("apply_default_acl_recursive (nftw)");
965 }
966
967 return false;
968 }
969
970
971
972 /**
973 * @brief Call apply_default_acl (possibly recursively) on each
974 * command-line argument.
975 *
976 * @return Either @c EXIT_FAILURE or @c EXIT_SUCCESS. If everything
977 * goes as expected, we return @c EXIT_SUCCESS. Otherwise, we return
978 * @c EXIT_FAILURE.
979 */
980 int main(int argc, char* argv[]) {
981
982 if (argc < 2) {
983 usage(argv[0]);
984 return EXIT_FAILURE;
985 }
986
987 bool recursive = false;
988 bool no_exec_mask = false;
989
990 struct option long_options[] = {
991 /* These options set a flag. */
992 {"help", no_argument, NULL, 'h'},
993 {"recursive", no_argument, NULL, 'r'},
994 {"no-exec-mask", no_argument, NULL, 'x'},
995 {NULL, 0, NULL, 0}
996 };
997
998 int opt = 0;
999
1000 while ((opt = getopt_long(argc, argv, "hrx", long_options, NULL)) != -1) {
1001 switch (opt) {
1002 case 'h':
1003 usage(argv[0]);
1004 return EXIT_SUCCESS;
1005 case 'r':
1006 recursive = true;
1007 break;
1008 case 'x':
1009 no_exec_mask = true;
1010 break;
1011 default:
1012 usage(argv[0]);
1013 return EXIT_FAILURE;
1014 }
1015 }
1016
1017 int result = EXIT_SUCCESS;
1018
1019 int arg_index = 1;
1020 for (arg_index = optind; arg_index < argc; arg_index++) {
1021 const char* target = argv[arg_index];
1022 bool reapp_result = false;
1023
1024 /* Make sure we can access the given path before we go out of our
1025 * way to please it. Doing this check outside of
1026 * apply_default_acl() lets us spit out a better error message for
1027 * typos, too.
1028 */
1029 if (!path_accessible(target)) {
1030 fprintf(stderr, "%s: %s: No such file or directory\n", argv[0], target);
1031 result = EXIT_FAILURE;
1032 continue;
1033 }
1034
1035 if (recursive) {
1036 reapp_result = apply_default_acl_recursive(target, no_exec_mask);
1037 }
1038 else {
1039 /* It's either a normal file, or we're not operating recursively. */
1040 reapp_result = apply_default_acl(target, NULL, no_exec_mask);
1041 }
1042
1043 if (!reapp_result) {
1044 result = EXIT_FAILURE;
1045 }
1046 }
1047
1048 return result;
1049 }