1 /*
2 * ProFTPD - FTP server daemon
3 * Copyright (c) 2004-2017 The ProFTPD Project team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 *
19 * As a special exemption, the ProFTPD Project team and other respective
20 * copyright holders give permission to link this program with OpenSSL, and
21 * distribute the resulting executable, without including the source code
22 * for OpenSSL in the source distribution.
23 */
24
25 /* POSIX ACL checking code (aka POSIX.1e hell)
26 */
27
28 #include "conf.h"
29
30 #define MOD_FACL_VERSION "mod_facl/0.6"
31
32 /* Make sure the version of proftpd is as necessary. */
33 #if PROFTPD_VERSION_NUMBER < 0x0001030101
34 # error "ProFTPD 1.3.1rc1 or later required"
35 #endif
36
37 module facl_module;
38
39 static int facl_engine = TRUE;
40 static const char *trace_channel = "facl";
41
42 #ifdef HAVE_POSIX_ACL
43
44 #ifdef HAVE_SYS_ACL_H
45 # include <sys/acl.h>
46 #endif
47
48 /* This header appears on Linux. */
49 #ifdef HAVE_ACL_LIBACL_H
50 # include <acl/libacl.h>
51 #endif
52
is_errno_eperm(int xerrno)53 static int is_errno_eperm(int xerrno) {
54 if (xerrno == EPERM)
55 return 1;
56
57 #ifdef EOPNOTSUPP
58 if (xerrno == EOPNOTSUPP)
59 return 1;
60 #endif /* !EOPNOTSUPP */
61
62 return 0;
63 }
64
facl_access(pr_fs_t * fs,const char * path,int mode,uid_t uid,gid_t gid,array_header * suppl_gids)65 static int facl_access(pr_fs_t *fs, const char *path, int mode, uid_t uid,
66 gid_t gid, array_header *suppl_gids) {
67 struct stat st;
68
69 pr_fs_clear_cache2(path);
70 if (pr_fsio_stat(path, &st) < 0) {
71 return -1;
72 }
73
74 return pr_fs_have_access(&st, mode, uid, gid, suppl_gids);
75 }
76
facl_faccess(pr_fh_t * fh,int mode,uid_t uid,gid_t gid,array_header * suppl_gids)77 static int facl_faccess(pr_fh_t *fh, int mode, uid_t uid, gid_t gid,
78 array_header *suppl_gids) {
79 return facl_access(fh->fh_fs, fh->fh_path, mode, uid, gid, suppl_gids);
80 }
81
82 #if defined(HAVE_BSD_POSIX_ACL) || \
83 defined(HAVE_LINUX_POSIX_ACL)
get_facl_perm_for_mode(int mode)84 static acl_perm_t get_facl_perm_for_mode(int mode) {
85 acl_perm_t res;
86
87 memset(&res, 0, sizeof(acl_perm_t));
88
89 if (mode & R_OK) {
90 res |= ACL_READ;
91 }
92
93 if (mode & W_OK) {
94 res |= ACL_WRITE;
95 }
96
97 if (mode & X_OK) {
98 res |= ACL_EXECUTE;
99 }
100
101 return res;
102 }
103
check_bsd_facl(pool * p,const char * path,int mode,void * acl,int nents,struct stat * st,uid_t uid,gid_t gid,array_header * suppl_gids)104 static int check_bsd_facl(pool *p, const char *path, int mode, void *acl,
105 int nents, struct stat *st, uid_t uid, gid_t gid,
106 array_header *suppl_gids) {
107 register unsigned int i;
108 int have_access_entry = FALSE, res = -1;
109 pool *acl_pool;
110 char *acl_text;
111 acl_t facl = acl;
112 acl_entry_t ae;
113 acl_tag_t ae_type;
114 acl_entry_t acl_user_entry = NULL;
115 acl_entry_t acl_group_entry = NULL;
116 acl_entry_t acl_other_entry = NULL;
117 acl_entry_t acl_mask_entry = NULL;
118 array_header *acl_groups;
119 array_header *acl_users;
120
121 acl_text = acl_to_text(facl, NULL);
122 if (acl_text != NULL) {
123 pr_trace_msg(trace_channel, 8,
124 "checking path '%s', ACL '%s'", path, acl_text);
125 acl_free(acl_text);
126 }
127
128 /* Iterate through all of the ACL entries, sorting them for later
129 * checking.
130 */
131 res = acl_get_entry(facl, ACL_FIRST_ENTRY, &ae);
132 if (res < 0) {
133 pr_trace_msg(trace_channel, 8,
134 "unable to retrieve first ACL entry for '%s': [%d] %s", path,
135 errno, strerror(errno));
136 errno = EACCES;
137 return -1;
138 }
139
140 if (res == 0) {
141 pr_trace_msg(trace_channel, 3, "ill-formed ACL for '%s' has no entries",
142 path);
143 errno = EACCES;
144 return -1;
145 }
146
147 acl_pool = make_sub_pool(p);
148 pr_pool_tag(acl_pool, "BSD/Linux ACL pool");
149
150 acl_groups = make_array(acl_pool, 1, sizeof(acl_entry_t));
151 acl_users = make_array(acl_pool, 1, sizeof(acl_entry_t));
152
153 while (res > 0) {
154 pr_signals_handle();
155
156 if (acl_get_tag_type(ae, &ae_type) < 0) {
157 pr_trace_msg(trace_channel, 5,
158 "error retrieving type of ACL entry for '%s': %s", path,
159 strerror(errno));
160 res = acl_get_entry(facl, ACL_NEXT_ENTRY, &ae);
161 continue;
162 }
163
164 if (ae_type & ACL_USER_OBJ) {
165 acl_user_entry = ae;
166
167 } else if (ae_type & ACL_USER) {
168 acl_entry_t *ae_dup = push_array(acl_users);
169 *ae_dup = ae;
170
171 } else if (ae_type & ACL_GROUP_OBJ) {
172 acl_group_entry = ae;
173
174 } else if (ae_type & ACL_GROUP) {
175 acl_entry_t *ae_dup = push_array(acl_groups);
176 *ae_dup = ae;
177
178 } else if (ae_type & ACL_OTHER) {
179 acl_other_entry = ae;
180
181 } else if (ae_type & ACL_MASK) {
182 acl_mask_entry = ae;
183 }
184
185 res = acl_get_entry(facl, ACL_NEXT_ENTRY, &ae);
186 }
187 ae = NULL;
188
189 /* Select the ACL entry that determines access. */
190 res = -1;
191
192 /* 1. If the given user ID matches the file owner, use that entry for
193 * access.
194 */
195 if (uid == st->st_uid) {
196 /* Check the acl_user_entry for access. */
197 ae = acl_user_entry;
198 ae_type = ACL_USER_OBJ;
199 have_access_entry = TRUE;
200
201 pr_trace_msg(trace_channel, 10, "user ID %s matches ACL owner user ID",
202 pr_uid2str(NULL, uid));
203 }
204
205 /* 2. If not matched above, and if the given user ID matches one of the
206 * named user entries, use that entry for access.
207 */
208 for (i = 0; !have_access_entry && i < acl_users->nelts; i++) {
209 acl_entry_t e = ((acl_entry_t *) acl_users->elts)[i];
210
211 if (uid == *((uid_t *) acl_get_qualifier(e))) {
212
213 /* Check this entry for access. Note that it'll need to
214 * be modified by the mask, if any, later.
215 */
216 ae = e;
217 ae_type = ACL_USER;
218 have_access_entry = TRUE;
219
220 pr_trace_msg(trace_channel, 10,
221 "user ID %s matches ACL allowed users list", pr_uid2str(NULL, uid));
222
223 break;
224 }
225 }
226
227 /* 3. If not matched above, and if one of the group IDs matches the
228 * group owner entry, and the group owner entry contains the
229 * requested permissions, use that entry for access.
230 */
231 if (!have_access_entry &&
232 gid == st->st_gid) {
233 int ret;
234
235 /* Check the acl_group_entry for access. First though, we need to
236 * see if the acl_group_entry contains the requested permissions.
237 */
238 acl_permset_t perms;
239 if (acl_get_permset(acl_group_entry, &perms) < 0) {
240 pr_trace_msg(trace_channel, 5, "error retrieving permission set: %s",
241 strerror(errno));
242 }
243
244 # if defined(HAVE_BSD_POSIX_ACL)
245 ret = acl_get_perm_np(perms, get_facl_perm_for_mode(mode));
246 # elif defined(HAVE_LINUX_POSIX_ACL)
247 ret = acl_get_perm(perms, get_facl_perm_for_mode(mode));
248 # endif
249 if (ret == 1) {
250 ae = acl_group_entry;
251 ae_type = ACL_GROUP_OBJ;
252 have_access_entry = TRUE;
253
254 pr_trace_msg(trace_channel, 10,
255 "primary group ID %s matches ACL owner group ID",
256 pr_gid2str(NULL, gid));
257
258 } else if (ret < 0) {
259 int xerrno = errno;
260
261 pr_trace_msg(trace_channel, 5,
262 "error checking permissions in permission set: %s", strerror(xerrno));
263 errno = xerrno;
264 }
265 }
266
267 if (suppl_gids) {
268 for (i = 0; !have_access_entry && i < suppl_gids->nelts; i++) {
269 gid_t suppl_gid = ((gid_t *) suppl_gids->elts)[i];
270
271 if (suppl_gid == st->st_gid) {
272 int ret;
273
274 /* Check the acl_group_entry for access. First though, we need to
275 * see if the acl_group_entry contains the requested permissions.
276 */
277 acl_permset_t perms;
278 if (acl_get_permset(acl_group_entry, &perms) < 0) {
279 pr_trace_msg(trace_channel, 5, "error retrieving permission set: %s",
280 strerror(errno));
281 }
282
283 # if defined(HAVE_BSD_POSIX_ACL)
284 ret = acl_get_perm_np(perms, get_facl_perm_for_mode(mode));
285 # elif defined(HAVE_LINUX_POSIX_ACL)
286 ret = acl_get_perm(perms, get_facl_perm_for_mode(mode));
287 # endif
288 if (ret == 1) {
289 ae = acl_group_entry;
290 ae_type = ACL_GROUP_OBJ;
291 have_access_entry = TRUE;
292
293 pr_trace_msg(trace_channel, 10,
294 "supplemental group ID %s matches ACL owner group ID",
295 pr_gid2str(NULL, suppl_gid));
296
297 break;
298
299 } else if (ret < 0) {
300 int xerrno = errno;
301
302 pr_trace_msg(trace_channel, 5,
303 "error checking permissions in permission set: %s",
304 strerror(xerrno));
305 errno = xerrno;
306 }
307 }
308 }
309 }
310
311 /* 4. If not matched above, and if one of the group IDs matches one
312 * of the named group entries, and that entry contains the requested
313 * permissions, use that entry for access.
314 */
315 for (i = 0; !have_access_entry && i < acl_groups->nelts; i++) {
316 acl_entry_t e = ((acl_entry_t *) acl_groups->elts)[i];
317
318 if (gid == *((gid_t *) acl_get_qualifier(e))) {
319 int ret;
320
321 /* Check this entry for access. Note that it'll need to
322 * be modified by the mask, if any, later.
323 */
324 acl_permset_t perms;
325 if (acl_get_permset(e, &perms) < 0) {
326 pr_trace_msg(trace_channel, 5, "error retrieving permission set: %s",
327 strerror(errno));
328 }
329
330 # if defined(HAVE_BSD_POSIX_ACL)
331 ret = acl_get_perm_np(perms, get_facl_perm_for_mode(mode));
332 # elif defined(HAVE_LINUX_POSIX_ACL)
333 ret = acl_get_perm(perms, get_facl_perm_for_mode(mode));
334 # endif
335 if (ret == 1) {
336 ae = e;
337 ae_type = ACL_GROUP;
338 have_access_entry = TRUE;
339
340 pr_trace_msg(trace_channel, 10,
341 "primary group ID %s matches ACL allowed groups list",
342 pr_gid2str(NULL, gid));
343
344 break;
345
346 } else if (ret < 0) {
347 int xerrno = errno;
348
349 pr_trace_msg(trace_channel, 5,
350 "error checking permissions in permission set: %s", strerror(errno));
351 errno = xerrno;
352 }
353 }
354
355 if (suppl_gids) {
356 register unsigned int j;
357
358 for (j = 0; !have_access_entry && j < suppl_gids->nelts; j++) {
359 gid_t suppl_gid = ((gid_t *) suppl_gids->elts)[j];
360
361 if (suppl_gid == *((gid_t *) acl_get_qualifier(e))) {
362 int ret;
363
364 /* Check this entry for access. Note that it'll need to
365 * be modified by the mask, if any, later.
366 */
367 acl_permset_t perms;
368 if (acl_get_permset(e, &perms) < 0) {
369 pr_trace_msg(trace_channel, 5,
370 "error retrieving permission set: %s", strerror(errno));
371 }
372
373 # if defined(HAVE_BSD_POSIX_ACL)
374 ret = acl_get_perm_np(perms, get_facl_perm_for_mode(mode));
375 # elif defined(HAVE_LINUX_POSIX_ACL)
376 ret = acl_get_perm(perms, get_facl_perm_for_mode(mode));
377 # endif
378 if (ret == 1) {
379 ae = e;
380 ae_type = ACL_GROUP;
381 have_access_entry = TRUE;
382
383 pr_trace_msg(trace_channel, 10,
384 "supplemental group ID %s matches ACL allowed groups list",
385 pr_gid2str(NULL, suppl_gid));
386
387 break;
388
389 } else if (ret < 0) {
390 int xerrno = errno;
391
392 pr_trace_msg(trace_channel, 5,
393 "error checking permissions in permission set: %s",
394 strerror(xerrno));
395 errno = xerrno;
396 }
397 }
398 }
399 }
400 }
401
402 /* 5. If not matched above, and if one of the group IDs matches
403 * the group owner or any of the named group entries, but neither
404 * the group owner entry nor any of the named group entries contains
405 * the requested permissions, access is denied.
406 */
407
408 /* XXX implement this condition properly */
409
410 /* 6. If not matched above, the other entry determines access.
411 */
412 if (!have_access_entry) {
413 ae = acl_other_entry;
414 ae_type = ACL_OTHER;
415 have_access_entry = TRUE;
416
417 pr_trace_msg(trace_channel, 10, "using ACL 'other' list");
418 }
419
420 /* Access determination:
421 *
422 * If either the user owner entry or other entry were used, and the
423 * entry contains the requested permissions, access is permitted.
424 *
425 * Otherwise, if the selected entry and the mask entry both contain
426 * the requested permissions (or there is no mask entry), access is
427 * permitted.
428 *
429 * Otherwise, access is denied.
430 */
431 switch (ae_type) {
432 case ACL_USER_OBJ:
433 case ACL_OTHER: {
434 int ret;
435
436 acl_permset_t perms;
437 if (acl_get_permset(ae, &perms) < 0) {
438 pr_trace_msg(trace_channel, 5, "error retrieving permission set: %s",
439 strerror(errno));
440 }
441
442 # if defined(HAVE_BSD_POSIX_ACL)
443 ret = acl_get_perm_np(perms, get_facl_perm_for_mode(mode));
444 # elif defined(HAVE_LINUX_POSIX_ACL)
445 ret = acl_get_perm(perms, get_facl_perm_for_mode(mode));
446 # endif
447 if (ret == 1) {
448 res = 0;
449
450 } else if (ret < 0) {
451 int xerrno = errno;
452
453 pr_trace_msg(trace_channel, 5,
454 "error checking permissions in permission set: %s", strerror(xerrno));
455 errno = xerrno;
456 }
457
458 break;
459 }
460
461 default: {
462 int ret1, ret2;
463 acl_permset_t ent_perms, mask_perms;
464
465 if (acl_get_permset(ae, &ent_perms) < 0) {
466 pr_trace_msg(trace_channel, 5, "error retrieving permission set: %s",
467 strerror(errno));
468 }
469
470 # if defined(HAVE_BSD_POSIX_ACL)
471 ret1 = acl_get_perm_np(ent_perms, get_facl_perm_for_mode(mode));
472 # elif defined(HAVE_LINUX_POSIX_ACL)
473 ret1 = acl_get_perm(ent_perms, get_facl_perm_for_mode(mode));
474 # endif
475
476 if (acl_mask_entry != NULL) {
477 if (acl_get_permset(acl_mask_entry, &mask_perms) < 0) {
478 pr_trace_msg(trace_channel, 5,
479 "error retrieving mask permission set: %s", strerror(errno));
480 }
481
482 # if defined(HAVE_BSD_POSIX_ACL)
483 ret2 = acl_get_perm_np(mask_perms, get_facl_perm_for_mode(mode));
484 # elif defined(HAVE_LINUX_POSIX_ACL)
485 ret2 = acl_get_perm(mask_perms, get_facl_perm_for_mode(mode));
486 # endif
487
488 } else {
489 /* If there is no mask entry, then access should be granted. */
490 ret2 = 1;
491 }
492
493 if (ret1 == 1 && ret2 == 1) {
494 res = 0;
495
496 } else {
497 if (ret1 < 0) {
498 int xerrno = errno;
499
500 pr_trace_msg(trace_channel, 5,
501 "error checking permissions in entry permission set: %s",
502 strerror(xerrno));
503 errno = xerrno;
504 }
505
506 if (ret2 < 0) {
507 int xerrno = errno;
508
509 pr_trace_msg(trace_channel, 5,
510 "error checking permissions in mask permission set: %s",
511 strerror(xerrno));
512 errno = xerrno;
513 }
514 }
515
516 break;
517 }
518 }
519
520 destroy_pool(acl_pool);
521
522 if (res < 0) {
523 pr_trace_msg(trace_channel, 3,
524 "returning EACCES for path '%s', user ID %s", path,
525 pr_uid2str(NULL, uid));
526 errno = EACCES;
527 }
528
529 return res;
530 }
531 #endif /* BSD/Linux POSIX ACL */
532
533 #if defined(HAVE_MACOSX_POSIX_ACL)
get_facl_perm_for_mode(int mode)534 static acl_perm_t get_facl_perm_for_mode(int mode) {
535 acl_perm_t res;
536
537 memset(&res, 0, sizeof(acl_perm_t));
538
539 if (mode & R_OK) {
540 res |= ACL_READ_DATA;
541 }
542
543 if (mode & W_OK) {
544 res |= ACL_WRITE_DATA;
545 }
546
547 if (mode & X_OK) {
548 res |= ACL_EXECUTE;
549 }
550
551 return res;
552 }
553
check_macosx_facl(pool * p,const char * path,int mode,void * acl,int nents,struct stat * st,uid_t uid,gid_t gid,array_header * suppl_gids)554 static int check_macosx_facl(pool *p, const char *path, int mode, void *acl,
555 int nents, struct stat *st, uid_t uid, gid_t gid,
556 array_header *suppl_gids) {
557 int have_access = FALSE, res = -1;
558 char *acl_text;
559 acl_t facl = acl;
560 acl_entry_t ae;
561
562 acl_text = acl_to_text(facl, NULL);
563 if (acl_text != NULL) {
564 pr_trace_msg(trace_channel, 8,
565 "checking path '%s', ACL '%s'", path, acl_text);
566 acl_free(acl_text);
567 }
568
569 /* Iterate through all of the ACL entries, sorting them for later
570 * checking.
571 */
572 res = acl_get_entry(facl, ACL_FIRST_ENTRY, &ae);
573 if (res < 0) {
574 pr_trace_msg(trace_channel, 8,
575 "unable to retrieve first ACL entry for '%s': [%d] %s", path,
576 errno, strerror(errno));
577 errno = EACCES;
578 return -1;
579 }
580
581 if (res == 0) {
582 pr_trace_msg(trace_channel, 3, "ill-formed ACL for '%s' has no entries",
583 path);
584 errno = EACCES;
585 return -1;
586 }
587
588 while (res > 0) {
589 acl_tag_t ae_type;
590
591 pr_signals_handle();
592
593 if (acl_get_tag_type(ae, &ae_type) < 0) {
594 pr_trace_msg(trace_channel, 5,
595 "error retrieving type of ACL entry for '%s': %s", path,
596 strerror(errno));
597 res = acl_get_entry(facl, ACL_NEXT_ENTRY, &ae);
598 continue;
599 }
600
601 if (ae_type & ACL_TYPE_EXTENDED) {
602 int ret;
603
604 acl_permset_t perms;
605 if (acl_get_permset(ae, &perms) < 0) {
606 pr_trace_msg(trace_channel, 5, "error retrieving permission set: %s",
607 strerror(errno));
608 }
609
610 ret = acl_get_perm_np(perms, get_facl_perm_for_mode(mode));
611 if (ret == 1) {
612 have_access = TRUE;
613 break;
614
615 } else if (ret < 0) {
616 int xerrno = errno;
617
618 pr_trace_msg(trace_channel, 5,
619 "error checking permissions in permission set: %s", strerror(xerrno));
620 errno = xerrno;
621 }
622 }
623
624 res = acl_get_entry(facl, ACL_NEXT_ENTRY, &ae);
625 }
626
627 if (!have_access) {
628 pr_trace_msg(trace_channel, 3,
629 "returning EACCES for path '%s', user ID %s", path,
630 pr_uid2str(NULL, uid));
631 errno = EACCES;
632 return -1;
633 }
634
635 return 0;
636 }
637 #endif /* MacOSX POSIX ACL */
638
639 #if defined(HAVE_SOLARIS_POSIX_ACL)
check_solaris_facl(pool * p,const char * path,int mode,void * acl,int nents,struct stat * st,uid_t uid,gid_t gid,array_header * suppl_gids)640 static int check_solaris_facl(pool *p, const char *path, int mode, void *acl,
641 int nents, struct stat *st, uid_t uid, gid_t gid,
642 array_header *suppl_gids) {
643 register unsigned int i;
644 int have_access_entry = FALSE, have_mask_entry = FALSE, idx, res = -1;
645 pool *acl_pool;
646 aclent_t *acls = acl;
647 aclent_t ae;
648 int ae_type = 0;
649 aclent_t acl_user_entry;
650 aclent_t acl_group_entry;
651 aclent_t acl_other_entry;
652 aclent_t acl_mask_entry;
653 array_header *acl_groups;
654 array_header *acl_users;
655
656 /* In the absence of any clear documentation, I'll assume that
657 * Solaris ACLs follow the same selection and checking algorithm
658 * as do BSD and Linux.
659 */
660
661 res = aclcheck(acls, nents, &idx);
662 switch (res) {
663 case 0:
664 break;
665
666 case GRP_ERROR:
667 pr_trace_msg(trace_channel, 3, "ill-formed ACL for '%s': %s",
668 path, "too many GROUP entries");
669 errno = EACCES;
670 return -1;
671
672 case USER_ERROR:
673 pr_trace_msg(trace_channel, 3, "ill-formed ACL for '%s': %s",
674 path, "too many USER entries");
675 errno = EACCES;
676 return -1;
677
678 case OTHER_ERROR:
679 pr_trace_msg(trace_channel, 3, "ill-formed ACL for '%s': %s",
680 path, "too many OTHER entries");
681 errno = EACCES;
682 return -1;
683
684 case CLASS_ERROR:
685 pr_trace_msg(trace_channel, 3, "ill-formed ACL for '%s': %s",
686 path, "too many CLASS entries");
687 errno = EACCES;
688 return -1;
689
690 case DUPLICATE_ERROR:
691 pr_trace_msg(trace_channel, 3, "ill-formed ACL for '%s': %s",
692 path, "duplicate entries");
693 errno = EACCES;
694 return -1;
695
696 case MISS_ERROR:
697 pr_trace_msg(trace_channel, 3, "ill-formed ACL for '%s': %s",
698 path, "missing required entry");
699 errno = EACCES;
700 return -1;
701
702 case MEM_ERROR:
703 pr_trace_msg(trace_channel, 3, "ill-formed ACL for '%s': %s",
704 path, "Out of memory!");
705 errno = EACCES;
706 return -1;
707
708 case ENTRY_ERROR:
709 pr_trace_msg(trace_channel, 3, "ill-formed ACL for '%s': %s",
710 path, "invalid entry type");
711 errno = EACCES;
712 return -1;
713 }
714
715 /* Iterate through all of the ACL entries, sorting them for later
716 * checking.
717 */
718
719 acl_pool = make_sub_pool(p);
720 pr_pool_tag(acl_pool, "Solaris ACL pool");
721
722 acl_groups = make_array(acl_pool, 1, sizeof(aclent_t));
723 acl_users = make_array(acl_pool, 1, sizeof(aclent_t));
724
725 for (i = 0; i < nents; i++) {
726 if (acls[i].a_type & USER_OBJ) {
727 memcpy(&acl_user_entry, &(acls[i]), sizeof(aclent_t));
728
729 } else if (acls[i].a_type & USER) {
730 aclent_t *ae_dup = push_array(acl_users);
731 memcpy(ae_dup, &(acls[i]), sizeof(aclent_t));
732
733 } else if (acls[i].a_type & GROUP_OBJ) {
734 memcpy(&acl_group_entry, &(acls[i]), sizeof(aclent_t));
735
736 } else if (acls[i].a_type & GROUP) {
737 aclent_t *ae_dup = push_array(acl_groups);
738 memcpy(ae_dup, &(acls[i]), sizeof(aclent_t));
739
740 } else if (acls[i].a_type & OTHER_OBJ) {
741 memcpy(&acl_other_entry, &(acls[i]), sizeof(aclent_t));
742
743 } else if (acls[i].a_type & CLASS_OBJ) {
744 memcpy(&acl_mask_entry, &(acls[i]), sizeof(aclent_t));
745 have_mask_entry = TRUE;
746 }
747 }
748
749 /* Select the ACL entry that determines access. */
750 res = -1;
751
752 /* 1. If the given user ID matches the file owner, use that entry for
753 * access.
754 */
755 if (uid == st->st_uid) {
756 /* Check the acl_user_entry for access. */
757 memcpy(&ae, &acl_user_entry, sizeof(aclent_t));
758 ae_type = USER_OBJ;
759 have_access_entry = TRUE;
760
761 pr_trace_msg(trace_channel, 10, "user ID %s matches ACL owner user ID",
762 pr_uid2str(NULL, uid));
763 }
764
765 /* 2. If not matched above, and f the given user ID matches one of the
766 * named user entries, use that entry for access.
767 */
768 for (i = 0; !have_access_entry && i < acl_users->nelts; i++) {
769 aclent_t e;
770 memcpy(&e, &(((aclent_t *) acl_users->elts)[i]), sizeof(aclent_t));
771
772 if (uid == e.a_id) {
773
774 /* Check this entry for access. Note that it'll need to
775 * be modified by the mask, if any, later.
776 */
777 memcpy(&ae, &e, sizeof(aclent_t));
778 ae_type = USER;
779 have_access_entry = TRUE;
780
781 pr_trace_msg(trace_channel, 10,
782 "user ID %s matches ACL allowed users list", pr_uid2str(NULL, uid));
783
784 break;
785 }
786 }
787
788 /* 3. If not matched above, and if one of the group IDs matches the
789 * group owner entry, and the group owner entry contains the
790 * requested permissions, use that entry for access.
791 */
792 if (!have_access_entry &&
793 gid == st->st_gid) {
794
795 /* Check the acl_group_entry for access. First though, we need to
796 * see if the acl_group_entry contains the requested permissions.
797 */
798 if (acl_group_entry.a_perm & mode) {
799 memcpy(&ae, &acl_group_entry, sizeof(aclent_t));
800 ae_type = GROUP_OBJ;
801 have_access_entry = TRUE;
802
803 pr_trace_msg(trace_channel, 10,
804 "primary group ID %s matches ACL owner group ID",
805 pr_gid2str(NULL, gid));
806 }
807 }
808
809 if (suppl_gids) {
810 for (i = 0; !have_access_entry && i < suppl_gids->nelts; i++) {
811 gid_t suppl_gid = ((gid_t *) suppl_gids->elts)[i];
812
813 if (suppl_gid == st->st_gid) {
814 /* Check the acl_group_entry for access. First though, we need to
815 * see if the acl_group_entry contains the requested permissions.
816 */
817 if (acl_group_entry.a_perm & mode) {
818 memcpy(&ae, &acl_group_entry, sizeof(aclent_t));
819 ae_type = GROUP_OBJ;
820 have_access_entry = TRUE;
821
822 pr_trace_msg(trace_channel, 10,
823 "supplemental group ID %s matches ACL owner group ID",
824 pr_gid2str(NULL, suppl_gid));
825
826 break;
827 }
828 }
829 }
830 }
831
832 /* 4. If not matched above, and if one of the group IDs matches one
833 * of the named group entries, and that entry contains the requested
834 * permissions, use that entry for access.
835 */
836 for (i = 0; !have_access_entry && i < acl_groups->nelts; i++) {
837 aclent_t e;
838 memcpy(&e, &(((aclent_t *) acl_groups->elts)[i]), sizeof(aclent_t));
839
840 if (gid == e.a_id) {
841
842 /* Check this entry for access. Note that it'll need to
843 * be modified by the mask, if any, later.
844 */
845 if (e.a_perm & mode) {
846 memcpy(&ae, &e, sizeof(aclent_t));
847 ae_type = GROUP;
848 have_access_entry = TRUE;
849
850 pr_trace_msg(trace_channel, 10,
851 "primary group ID %s matches ACL allowed groups list",
852 pr_gid2str(NULL, gid));
853
854 break;
855 }
856 }
857
858 if (suppl_gids) {
859 register unsigned int j;
860
861 for (j = 0; !have_access_entry && j < suppl_gids->nelts; j++) {
862 gid_t suppl_gid = ((gid_t *) suppl_gids->elts)[j];
863
864 if (suppl_gid == e.a_id) {
865 /* Check this entry for access. Note that it'll need to
866 * be modified by the mask, if any, later.
867 */
868 if (e.a_perm & mode) {
869 memcpy(&ae, &e, sizeof(aclent_t));
870 ae_type = GROUP;
871 have_access_entry = TRUE;
872
873 pr_trace_msg(trace_channel, 10,
874 "supplemental group ID %s matches ACL allowed groups list",
875 pr_gid2str(NULL, suppl_gid));
876
877 break;
878 }
879 }
880 }
881 }
882 }
883
884 /* 5. If not matched above, and if one of the group IDs matches
885 * the group owner or any of the named group entries, but neither
886 * the group owner entry nor any of the named group entries contains
887 * the requested permissions, access is denied.
888 */
889
890 /* XXX implement this condition properly */
891
892 /* 6. If not matched above, the other entry determines access.
893 */
894 if (!have_access_entry) {
895 memcpy(&ae, &acl_other_entry, sizeof(aclent_t));
896 ae_type = OTHER_OBJ;
897 have_access_entry = TRUE;
898
899 pr_trace_msg(trace_channel, 10, "using ACL 'other' list");
900 }
901
902 /* Access determination:
903 *
904 * If either the user owner entry or other entry were used, and the
905 * entry contains the requested permissions, access is permitted.
906 *
907 * Otherwise, if the selected entry and the mask entry both contain
908 * the requested permissions (or there is no mask entry), access is
909 * permitted.
910 *
911 * Otherwise, access is denied.
912 */
913 switch (ae_type) {
914 case USER_OBJ:
915 case OTHER_OBJ:
916 if (ae.a_perm & mode) {
917 res = 0;
918 }
919 break;
920
921 default:
922 if (have_mask_entry) {
923 if ((ae.a_perm & mode) &&
924 (acl_mask_entry.a_perm & mode)) {
925 res = 0;
926 }
927
928 } else {
929
930 /* If there is no mask entry, then access should be granted. */
931 if (ae.a_perm & mode) {
932 res = 0;
933 }
934 }
935
936 break;
937 }
938
939 destroy_pool(acl_pool);
940
941 if (res < 0) {
942 pr_trace_msg(trace_channel, 3,
943 "returning EACCES for path '%s', user ID %s", path,
944 pr_uid2str(NULL, uid));
945 errno = EACCES;
946 }
947
948 return res;
949 }
950 #endif /* Solaris POSIX ACL */
951
check_facl(pool * p,const char * path,int mode,void * acl,int nents,struct stat * st,uid_t uid,gid_t gid,array_header * suppl_gids)952 static int check_facl(pool *p, const char *path, int mode, void *acl, int nents,
953 struct stat *st, uid_t uid, gid_t gid, array_header *suppl_gids) {
954 int res = -1;
955
956 # if defined(HAVE_BSD_POSIX_ACL) || \
957 defined(HAVE_LINUX_POSIX_ACL)
958 res = check_bsd_facl(p, path, mode, acl, nents, st, uid, gid, suppl_gids);
959
960 # elif defined(HAVE_MACOSX_POSIX_ACL)
961 res = check_macosx_facl(p, path, mode, acl, nents, st, uid, gid, suppl_gids);
962
963 # elif defined(HAVE_SOLARIS_POSIX_ACL)
964 res = check_solaris_facl(p, path, mode, acl, nents, st, uid, gid, suppl_gids);
965 # endif
966
967 return res;
968 }
969
970 # if defined(PR_USE_FACL)
971
972 /* FSIO handlers
973 */
974
facl_fsio_access(pr_fs_t * fs,const char * path,int mode,uid_t uid,gid_t gid,array_header * suppl_gids)975 static int facl_fsio_access(pr_fs_t *fs, const char *path, int mode,
976 uid_t uid, gid_t gid, array_header *suppl_gids) {
977 int nents = 0, res, xerrno;
978 struct stat st;
979 void *acls;
980 pool *tmp_pool = NULL;
981
982 pr_fs_clear_cache2(path);
983 if (pr_fsio_stat(path, &st) < 0) {
984 return -1;
985 }
986
987 /* Look up the acl for this path. */
988 # if defined(HAVE_BSD_POSIX_ACL) || \
989 defined(HAVE_LINUX_POSIX_ACL) || \
990 defined(HAVE_MACOSX_POSIX_ACL)
991 acls = acl_get_file(path, ACL_TYPE_ACCESS);
992 if (acls == NULL) {
993 xerrno = errno;
994
995 pr_trace_msg(trace_channel, 5, "unable to retrieve ACL for '%s': [%d] %s",
996 path, xerrno, strerror(xerrno));
997
998 if (is_errno_eperm(xerrno)) {
999 pr_trace_msg(trace_channel, 3, "ACL retrieval operation not supported "
1000 "for '%s', falling back to normal access check", path);
1001
1002 if (facl_access(fs, path, mode, uid, gid, suppl_gids) < 0) {
1003 xerrno = errno;
1004
1005 pr_trace_msg(trace_channel, 6, "normal access check for '%s' "
1006 "failed: %s", path, strerror(xerrno));
1007 errno = xerrno;
1008 return -1;
1009 }
1010
1011 return 0;
1012 }
1013
1014 errno = xerrno;
1015 return -1;
1016 }
1017
1018 # elif defined(HAVE_SOLARIS_POSIX_ACL)
1019
1020 nents = acl(path, GETACLCNT, 0, NULL);
1021 if (nents < 0) {
1022 xerrno = errno;
1023
1024 pr_trace_msg(trace_channel, 5,
1025 "unable to retrieve ACL count for '%s': [%d] %s", path, xerrno,
1026 strerror(xerrno));
1027
1028 if (is_errno_eperm(xerrno)) {
1029 pr_trace_msg(trace_channel, 3, "ACL retrieval operation not supported "
1030 "for '%s', falling back to normal access check", path);
1031
1032 if (facl_access(fs, path, mode, uid, gid, suppl_gids) < 0) {
1033 xerrno = errno;
1034
1035 pr_trace_msg(trace_channel, 6, "normal access check for '%s' "
1036 "failed: %s", path, strerror(xerrno));
1037 errno = xerrno;
1038 return -1;
1039 }
1040
1041 return 0;
1042 }
1043
1044 errno = xerrno;
1045 return -1;
1046 }
1047
1048 pr_trace_msg(trace_channel, 10,
1049 "acl(2) returned %d ACL entries for path '%s'", nents, path);
1050
1051 if (tmp_pool == NULL) {
1052 tmp_pool = make_sub_pool(fs->fs_pool);
1053 pr_pool_tag(tmp_pool, "mod_facl access(2) pool");
1054 }
1055
1056 acls = pcalloc(tmp_pool, nents * sizeof(aclent_t));
1057
1058 nents = acl(path, GETACL, nents, acls);
1059 if (nents < 0) {
1060 xerrno = errno;
1061
1062 destroy_pool(tmp_pool);
1063 tmp_pool = NULL;
1064
1065 pr_trace_msg(trace_channel, 5,
1066 "unable to retrieve ACL for '%s': [%d] %s", path, xerrno,
1067 strerror(xerrno));
1068
1069 if (is_errno_eperm(xerrno)) {
1070 pr_trace_msg(trace_channel, 3, "ACL retrieval operation not supported "
1071 "for '%s', falling back to normal access check", path);
1072
1073 if (facl_access(fs, path, mode, uid, gid, suppl_gids) < 0) {
1074 xerrno = errno;
1075
1076 pr_trace_msg(trace_channel, 6, "normal access check for '%s' "
1077 "failed: %s", path, strerror(xerrno));
1078 errno = xerrno;
1079 return -1;
1080 }
1081
1082 return 0;
1083 }
1084
1085 errno = xerrno;
1086 return -1;
1087 }
1088 # endif
1089
1090 if (tmp_pool == NULL) {
1091 tmp_pool = make_sub_pool(fs->fs_pool);
1092 pr_pool_tag(tmp_pool, "mod_facl access(2) pool");
1093 }
1094
1095 res = check_facl(tmp_pool, path, mode, acls, nents, &st, uid, gid,
1096 suppl_gids);
1097 xerrno = errno;
1098
1099 # if defined(HAVE_BSD_POSIX_ACL) || \
1100 defined(HAVE_LINUX_POSIX_ACL) || \
1101 defined(HAVE_MACOSX_POSIX_ACL)
1102 acl_free(acls);
1103 # endif
1104 destroy_pool(tmp_pool);
1105
1106 errno = xerrno;
1107 return res;
1108 }
1109
facl_fsio_faccess(pr_fh_t * fh,int mode,uid_t uid,gid_t gid,array_header * suppl_gids)1110 static int facl_fsio_faccess(pr_fh_t *fh, int mode, uid_t uid, gid_t gid,
1111 array_header *suppl_gids) {
1112 int nents = 0, res, xerrno;
1113 struct stat st;
1114 void *acls;
1115 pool *tmp_pool = NULL;
1116
1117 if (pr_fsio_fstat(fh, &st) < 0) {
1118 return -1;
1119 }
1120
1121 /* Look up the acl for this fd. */
1122 # if defined(HAVE_BSD_POSIX_ACL) || \
1123 defined(HAVE_LINUX_POSIX_ACL) || \
1124 defined(HAVE_MACOSX_POSIX_ACL)
1125 acls = acl_get_fd(PR_FH_FD(fh));
1126 if (acls == NULL) {
1127 xerrno = errno;
1128
1129 pr_trace_msg(trace_channel, 10,
1130 "unable to retrieve ACL for '%s': [%d] %s", fh->fh_path, xerrno,
1131 strerror(xerrno));
1132
1133 if (is_errno_eperm(xerrno)) {
1134 pr_trace_msg(trace_channel, 3, "ACL retrieval operation not supported "
1135 "for '%s', falling back to normal access check", fh->fh_path);
1136
1137 if (facl_faccess(fh, mode, uid, gid, suppl_gids) < 0) {
1138 xerrno = errno;
1139
1140 pr_trace_msg(trace_channel, 6, "normal access check for '%s' "
1141 "failed: %s", fh->fh_path, strerror(xerrno));
1142 errno = xerrno;
1143 return -1;
1144 }
1145
1146 return 0;
1147 }
1148
1149 errno = xerrno;
1150 return -1;
1151 }
1152
1153 # elif defined(HAVE_SOLARIS_POSIX_ACL)
1154
1155 nents = facl(PR_FH_FD(fh), GETACLCNT, 0, NULL);
1156 if (nents < 0) {
1157 xerrno = errno;
1158
1159 pr_trace_msg(trace_channel, 10,
1160 "unable to retrieve ACL count for '%s': [%d] %s", fh->fh_path,
1161 xerrno, strerror(xerrno));
1162
1163 if (is_errno_eperm(xerrno)) {
1164 pr_trace_msg(trace_channel, 3, "ACL retrieval operation not supported "
1165 "for '%s', falling back to normal access check", fh->fh_path);
1166
1167 if (facl_faccess(fh, mode, uid, gid, suppl_gids) < 0) {
1168 xerrno = errno;
1169
1170 pr_trace_msg(trace_channel, 6, "normal access check for '%s' "
1171 "failed: %s", fh->fh_path, strerror(xerrno));
1172 errno = xerrno;
1173 return -1;
1174 }
1175
1176 return 0;
1177 }
1178
1179 errno = xerrno;
1180 return -1;
1181 }
1182
1183 if (tmp_pool == NULL) {
1184 tmp_pool = make_sub_pool(fh->fh_fs->fs_pool);
1185 pr_pool_tag(tmp_pool, "mod_facl faccess(2) pool");
1186 }
1187
1188 acls = pcalloc(tmp_pool, nents * sizeof(aclent_t));
1189
1190 nents = facl(PR_FH_FD(fh), GETACL, nents, acls);
1191 if (nents < 0) {
1192 xerrno = errno;
1193
1194 destroy_pool(tmp_pool);
1195 tmp_pool = NULL;
1196
1197 pr_trace_msg(trace_channel, 10,
1198 "unable to retrieve ACL for '%s': [%d] %s", fh->fh_path, xerrno,
1199 strerror(xerrno));
1200
1201 if (is_errno_eperm(xerrno)) {
1202 pr_trace_msg(trace_channel, 3, "ACL retrieval operation not supported "
1203 "for '%s', falling back to normal access check", fh->fh_path);
1204
1205 if (facl_faccess(fh, mode, uid, gid, suppl_gids) < 0) {
1206 xerrno = errno;
1207
1208 pr_trace_msg(trace_channel, 6, "normal access check for '%s' "
1209 "failed: %s", fh->fh_path, strerror(xerrno));
1210 errno = xerrno;
1211 return -1;
1212 }
1213
1214 return 0;
1215 }
1216
1217 errno = xerrno;
1218 return -1;
1219 }
1220 # endif
1221
1222 if (tmp_pool == NULL) {
1223 tmp_pool = make_sub_pool(fh->fh_fs->fs_pool);
1224 pr_pool_tag(tmp_pool, "mod_facl faccess(2) pool");
1225 }
1226
1227 res = check_facl(tmp_pool, fh->fh_path, mode, acls, nents, &st, uid, gid,
1228 suppl_gids);
1229 xerrno = errno;
1230
1231 # if defined(HAVE_BSD_POSIX_ACL) || \
1232 defined(HAVE_LINUX_POSIX_ACL) || \
1233 defined(HAVE_MACOSX_POSIX_ACL)
1234 acl_free(acls);
1235 # endif
1236 destroy_pool(tmp_pool);
1237
1238 errno = xerrno;
1239 return res;
1240 }
1241 # endif /* !PR_USE_FACL */
1242 #endif /* HAVE_POSIX_ACL */
1243
1244 /* Configuration handlers
1245 */
1246
1247 /* usage: FACLEngine on|off */
set_faclengine(cmd_rec * cmd)1248 MODRET set_faclengine(cmd_rec *cmd) {
1249 int engine = -1;
1250
1251 CHECK_ARGS(cmd, 1);
1252 CHECK_CONF(cmd, CONF_ROOT);
1253
1254 engine = get_boolean(cmd, 1);
1255 if (engine == -1) {
1256 CONF_ERROR(cmd, "expected Boolean parameter");
1257 }
1258
1259 facl_engine = engine;
1260 return PR_HANDLED(cmd);
1261 }
1262
1263 /* Event listeners
1264 */
1265
unmount_facl(void)1266 static void unmount_facl(void) {
1267 pr_fs_t *fs;
1268
1269 fs = pr_unmount_fs("/", "facl");
1270 if (fs != NULL) {
1271 destroy_pool(fs->fs_pool);
1272 fs->fs_pool = NULL;
1273 return;
1274 }
1275
1276 if (errno != ENOENT) {
1277 pr_log_debug(DEBUG0, MOD_FACL_VERSION
1278 ": error unmounting 'facl' FS: %s", strerror(errno));
1279 }
1280 }
1281
1282 #if defined(PR_SHARED_MODULE) && \
1283 defined(PR_USE_FACL) && \
1284 defined(HAVE_POSIX_ACL)
facl_mod_unload_ev(const void * event_data,void * user_data)1285 static void facl_mod_unload_ev(const void *event_data, void *user_data) {
1286 if (strcmp("mod_facl.c", (const char *) event_data) == 0) {
1287 pr_event_unregister(&facl_module, NULL, NULL);
1288 unmount_facl();
1289 }
1290 }
1291 #endif /* !PR_SHARED_MODULE */
1292
facl_postparse_ev(const void * event_data,void * user_data)1293 static void facl_postparse_ev(const void *event_data, void *user_data) {
1294 #if defined(PR_USE_FACL) && \
1295 defined(HAVE_POSIX_ACL)
1296 pr_fs_t *fs;
1297 #endif /* PR_USE_FACL and HAVE_POSIX_ACL */
1298
1299 if (facl_engine == FALSE) {
1300 return;
1301 }
1302
1303 #if defined(PR_USE_FACL) && \
1304 defined(HAVE_POSIX_ACL)
1305 fs = pr_register_fs(permanent_pool, "facl", "/");
1306 if (fs == NULL) {
1307 int xerrno = errno;
1308
1309 pr_log_pri(PR_LOG_WARNING,
1310 MOD_FACL_VERSION ": error registering 'facl' FS: %s", strerror(xerrno));
1311 pr_session_disconnect(&facl_module, PR_SESS_DISCONNECT_BY_APPLICATION,
1312 NULL);
1313 }
1314 pr_log_debug(DEBUG6, MOD_FACL_VERSION ": registered 'facl' FS");
1315
1316 /* Ensure that our ACL-checking handlers are used. */
1317 fs->access = facl_fsio_access;
1318 fs->faccess = facl_fsio_faccess;
1319 #endif /* PR_USE_FACL and HAVE_POSIX_ACL */
1320 }
1321
facl_restart_ev(const void * event_data,void * user_data)1322 static void facl_restart_ev(const void *event_data, void *user_data) {
1323 if (facl_engine == FALSE) {
1324 return;
1325 }
1326
1327 unmount_facl();
1328 }
1329
1330 /* Initialization routines
1331 */
1332
facl_init(void)1333 static int facl_init(void) {
1334 #if defined(PR_USE_FACL) && \
1335 defined(HAVE_POSIX_ACL)
1336 # if defined(PR_SHARED_MODULE)
1337 pr_event_register(&facl_module, "core.module-unload", facl_mod_unload_ev,
1338 NULL);
1339 # endif /* !PR_SHARED_MODULE */
1340 #endif /* PR_USE_FACL and HAVE_POSIX_ACL */
1341 pr_event_register(&facl_module, "core.postparse", facl_postparse_ev, NULL);
1342 pr_event_register(&facl_module, "core.restart", facl_restart_ev, NULL);
1343
1344 return 0;
1345 }
1346
1347 /* Module Tables
1348 */
1349
1350 static conftable facl_conftab[] = {
1351 { "FACLEngine", set_faclengine, NULL },
1352 { NULL }
1353 };
1354
1355 module facl_module = {
1356 /* Always NULL */
1357 NULL, NULL,
1358
1359 /* Module API version */
1360 0x20,
1361
1362 /* Module name */
1363 "facl",
1364
1365 /* Module configuration directive handlers */
1366 facl_conftab,
1367
1368 /* Module command handlers */
1369 NULL,
1370
1371 /* Module authentication handlers */
1372 NULL,
1373
1374 /* Module initialization */
1375 facl_init,
1376
1377 /* Session initialization */
1378 NULL,
1379
1380 /* Module version */
1381 MOD_FACL_VERSION
1382 };
1383