]> gitweb.michael.orlitzky.com - apply-default-acl.git/blob - src/aclq.c
Change all return types to int.
[apply-default-acl.git] / src / aclq.c
1 #include <errno.h>
2 #include <libgen.h> /* dirname() */
3 #include <limits.h> /* PATH_MAX */
4 #include <stdbool.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10
11 /* ACLs */
12 #include <acl/libacl.h> /* acl_get_perm, not portable */
13 #include <sys/types.h>
14 #include <sys/acl.h>
15
16
17 mode_t get_mode(const char* path) {
18 if (path == NULL) {
19 errno = ENOENT;
20 return -1;
21 }
22
23 struct stat s;
24 int result = stat(path, &s);
25
26 if (result == 0) {
27 return s.st_mode;
28 }
29 else {
30 /* errno will be set already by stat() */
31 return result;
32 }
33 }
34
35 bool mode_has_perm(mode_t mode, int perm) {
36 if (mode & perm) {
37 return true;
38 }
39 else {
40 return false;
41 }
42 }
43
44
45 bool is_regular_file(const char* path) {
46 if (path == NULL) {
47 return false;
48 }
49
50 struct stat s;
51 int result = stat(path, &s);
52 if (result == 0) {
53 return S_ISREG(s.st_mode);
54 }
55 else {
56 return false;
57 }
58 }
59
60 bool is_directory(const char* path) {
61 if (path == NULL) {
62 return false;
63 }
64
65 struct stat s;
66 int result = stat(path, &s);
67 if (result == 0) {
68 return S_ISDIR(s.st_mode);
69 }
70 else {
71 return false;
72 }
73 }
74
75
76 int has_default_tag_acl(const char* path, acl_tag_t tag_type) {
77 /* Returns one if the given path has a default ACL for the supplied
78 tag, zero if it doesn't, and -1 on error. */
79 acl_t defacl = acl_get_file(path, ACL_TYPE_DEFAULT);
80
81 if (defacl == (acl_t)NULL) {
82 return 0;
83 }
84
85 acl_entry_t entry;
86 int result = acl_get_entry(defacl, ACL_FIRST_ENTRY, &entry);
87
88 while (result == 1) {
89 acl_tag_t tag = ACL_UNDEFINED_TAG;
90 int tag_result = acl_get_tag_type(entry, &tag);
91
92 if (tag_result == -1) {
93 perror("has_default_tag_acl (acl_get_tag_type)");
94 return -1;
95 }
96 else {
97 if (tag == tag_type) {
98 return 1;
99 }
100 }
101
102 result = acl_get_entry(defacl, ACL_NEXT_ENTRY, &entry);
103 }
104
105 if (result == -1) {
106 perror("has_default_tag_acl (acl_get_entry)");
107 return -1;
108 }
109
110 return 0;
111 }
112
113
114 int has_minimal_default_acl(const char* path) {
115 /* An ACL is minimal if and only if it has no mask. Return 1 if it's
116 minimal, zero if it isn't, and -1 on errors. */
117 int has_dm = has_default_tag_acl(path, ACL_MASK);
118
119 if (has_dm == 0) {
120 return 1;
121 }
122 else if (has_dm == 1) {
123 return 0;
124 }
125 else {
126 perror("has_minimal_default_acl");
127 return -1;
128 }
129 }
130
131
132 int has_default_user_obj_acl(const char* path) {
133 return has_default_tag_acl(path, ACL_USER_OBJ);
134 }
135
136 int has_default_group_obj_acl(const char* path) {
137 return has_default_tag_acl(path, ACL_GROUP_OBJ);
138 }
139
140 int has_default_other_acl(const char* path) {
141 return has_default_tag_acl(path, ACL_OTHER);
142 }
143
144
145 int get_default_tag_permset(const char* path,
146 acl_tag_t tag_type,
147 acl_permset_t* output_perms) {
148 /* Returns one if successful, zero when the ACL doesn't exist, and
149 -1 on unexpected errors. */
150 acl_t defacl = acl_get_file(path, ACL_TYPE_DEFAULT);
151
152 if (defacl == (acl_t)NULL) {
153 /* Follow the acl_foo convention of -1 == error. */
154 errno = EINVAL;
155 return 0;
156 }
157
158 acl_entry_t entry;
159 int result = acl_get_entry(defacl, ACL_FIRST_ENTRY, &entry);
160
161 while (result == 1) {
162 acl_tag_t tag = ACL_UNDEFINED_TAG;
163 int tag_result = acl_get_tag_type(entry, &tag);
164
165 if (tag_result == -1) {
166 perror("get_default_tag_permset (acl_get_tag_type)");
167 return -1;
168 }
169 else {
170 if (tag == tag_type) {
171 /* We found the right tag, now get the permset. */
172 int ps_result = acl_get_permset(entry, output_perms);
173 if (ps_result == -1) {
174 perror("get_default_tag_permset (acl_get_permset)");
175 }
176
177 if (ps_result == 0) {
178 return 1;
179 }
180 else {
181 return 0;
182 }
183 }
184 }
185
186 result = acl_get_entry(defacl, ACL_NEXT_ENTRY, &entry);
187 }
188
189 /* This catches both the initial acl_get_entry and the ones at the
190 end of the loop. */
191 if (result == -1) {
192 perror("get_default_tag_permset (acl_get_entry)");
193 return -1;
194 }
195
196 return 0;
197 }
198
199
200 int has_default_tag_perm(const char* path,
201 acl_tag_t tag,
202 acl_perm_t perm) {
203 /* Check path to see if tag has the given perm. Returns one if it
204 does, zero if it doesn't (or there's no ACL), and -1 on unexpected
205 errors. */
206
207 if (!has_default_tag_acl(path, tag)) {
208 return 0;
209 }
210
211 acl_permset_t permset;
212 bool ps_result = get_default_tag_permset(path, tag, &permset);
213
214 if (ps_result != 1) {
215 /* Failure or error. */
216 return ps_result;
217 }
218
219 int p_result = acl_get_perm(permset, perm);
220 if (p_result == -1) {
221 perror("has_default_tag_perm (acl_get_perm)");
222 }
223
224 return p_result;
225 }
226
227 int has_default_user_obj_read(const char* path) {
228 return has_default_tag_perm(path, ACL_USER_OBJ, ACL_READ);
229 }
230
231 int has_default_user_obj_write(const char* path) {
232 return has_default_tag_perm(path, ACL_USER_OBJ, ACL_WRITE);
233 }
234
235 int has_default_user_obj_execute(const char* path) {
236 return has_default_tag_perm(path, ACL_USER_OBJ, ACL_EXECUTE);
237 }
238
239 int has_default_group_obj_read(const char* path) {
240 return has_default_tag_perm(path, ACL_GROUP_OBJ, ACL_READ);
241 }
242
243 int has_default_group_obj_write(const char* path) {
244 return has_default_tag_perm(path, ACL_GROUP_OBJ, ACL_WRITE);
245 }
246
247 int has_default_group_obj_execute(const char* path) {
248 return has_default_tag_perm(path, ACL_GROUP_OBJ, ACL_EXECUTE);
249 }
250
251 int has_default_other_read(const char* path) {
252 return has_default_tag_perm(path, ACL_OTHER, ACL_READ);
253 }
254
255 int has_default_other_write(const char* path) {
256 return has_default_tag_perm(path, ACL_OTHER, ACL_WRITE);
257 }
258
259 int has_default_other_execute(const char* path) {
260 return has_default_tag_perm(path, ACL_OTHER, ACL_EXECUTE);
261 }
262
263 int has_default_mask_read(const char* path) {
264 return has_default_tag_perm(path, ACL_MASK, ACL_READ);
265 }
266
267 int has_default_mask_write(const char* path) {
268 return has_default_tag_perm(path, ACL_MASK, ACL_WRITE);
269 }
270
271 int has_default_mask_execute(const char* path) {
272 return has_default_tag_perm(path, ACL_MASK, ACL_EXECUTE);
273 }
274
275
276 int reapply_default_acl(const char* path) {
277 /* If this is a normal file or directory (i.e. that has just been
278 created), we proceed to find its parent directory which will have
279 a default ACL.
280
281 Returns one for success, zero for failure (i.e. no ACL), and -1
282 on unexpected errors. */
283 if (path == NULL) {
284 return 0;
285 }
286
287 if (!is_regular_file(path) && !is_directory(path)) {
288 return 0;
289 }
290
291 /* dirname mangles its argument */
292 char path_copy[PATH_MAX];
293 strncpy(path_copy, path, PATH_MAX-1);
294 path_copy[PATH_MAX-1] = 0;
295
296 char* parent = dirname(path_copy);
297 if (!is_directory(parent)) {
298 /* Make sure dirname() did what we think it did. */
299 return 0;
300 }
301
302 /* This is the original mode of path. We will simply add permissions
303 to it, and then later reapply the result via chmod. */
304 mode_t path_mode = get_mode(path);
305
306
307 /* If parent has a default user ACL, apply it to path via chmod. */
308 if (has_default_user_obj_read(parent)) {
309 path_mode |= S_IRUSR;
310 }
311
312 if (has_default_user_obj_write(parent)) {
313 path_mode |= S_IWUSR;
314 }
315
316 /* However, we don't want to set the execute bit on via the ACL
317 unless it was on originally. Furthermore, if the ACL denies
318 execute, we want to honor that. */
319 if (has_default_user_obj_acl(parent)) {
320 if (!has_default_user_obj_execute(parent)) {
321 /* It has a user ACL, but no user execute is granted. */
322 path_mode &= ~S_IXUSR;
323 }
324 }
325
326
327 /* Do the same thing with the other perms/ACL. */
328 if (has_default_other_read(parent)) {
329 path_mode |= S_IROTH;
330 }
331
332 if (has_default_other_write(parent)) {
333 path_mode |= S_IWOTH;
334 }
335
336 if (has_default_other_acl(parent)) {
337 if (!has_default_other_execute(parent)) {
338 /* It has an other ACL, but no other execute is granted. */
339 path_mode &= ~S_IXOTH;
340 }
341 }
342
343 if (has_minimal_default_acl(parent)) {
344 /* With a minimal ACL, we'll repeat for the group bits what we
345 already did for the owner/other bits. */
346 if (has_default_group_obj_read(parent)) {
347 path_mode |= S_IRGRP;
348 }
349
350 if (has_default_group_obj_write(parent)) {
351 path_mode |= S_IWGRP;
352 }
353
354 if (has_default_group_obj_acl(parent)) {
355 if (!has_default_group_obj_execute(parent)) {
356 /* It has a group ACL, but no group execute is granted. */
357 path_mode &= ~S_IXGRP;
358 }
359 }
360 }
361 else {
362 /* The parent has an extended ACL. Extended ACLs use the mask
363 entry. */
364
365 /* For the group bits, we'll use the ACL's mask instead of the group
366 object bits. If the default ACL had a group entry, it should
367 already have propagated (but might be masked). */
368 if (has_default_mask_read(parent)) {
369 path_mode |= S_IRGRP;
370 }
371
372 if (has_default_mask_write(parent)) {
373 path_mode |= S_IWGRP;
374 }
375
376 /* Honor the mask execute unconditionally. */
377 if (has_default_mask_execute(parent)) {
378 /* This just adds the group execute bit, and doesn't actually
379 grant group execute permissions. */
380 path_mode |= S_IXGRP;
381 }
382
383 if (!mode_has_perm(path_mode, S_IXGRP)) {
384 /* The group ACL entry should already have been inherited from the
385 default ACL. If the source was not group executable, we want to
386 modify the destination so that it is not group executable
387 either. In the presence of ACLs, the group permissions come not
388 from the mode bits, but from the group:: ACL entry. So, to do
389 this, we remove the group::x entry. */
390 if (has_default_group_obj_execute(path)) {
391 /* remove_default_group_obj_execute(path);*/
392 }
393 }
394 }
395
396 int chmod_result = chmod(path, path_mode);
397 if (chmod_result == 0) {
398 return 1;
399 }
400 else {
401 return 0;
402 }
403 }
404
405
406 int main(int argc, char* argv[]) {
407 const char* target = argv[1];
408
409 bool result = reapply_default_acl(target);
410
411 if (result) {
412 printf("Success.\n");
413 return EXIT_SUCCESS;
414 }
415 else {
416 return EXIT_FAILURE;
417 }
418 }