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