]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/acls.c
Move volume loading to libatalk
[netatalk.git] / etc / afpd / acls.c
1 /*
2   Copyright (c) 2008, 2009, 2010 Frank Lahm <franklahm@gmail.com>
3   Copyright (c) 2011 Laura Mueller <laura-mueller@uni-duesseldorf.de>
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
16 #ifdef HAVE_CONFIG_H
17 #include "config.h"
18 #endif /* HAVE_CONFIG_H */
19
20 #include <string.h>
21 #include <strings.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <grp.h>
25 #include <pwd.h>
26 #include <errno.h>
27 #ifdef HAVE_SOLARIS_ACLS
28 #include <sys/acl.h>
29 #endif
30 #ifdef HAVE_POSIX_ACLS
31 #include <sys/acl.h>
32 #endif
33 #ifdef HAVE_ACL_LIBACL_H
34 #include <acl/libacl.h>
35 #endif
36
37 #include <atalk/errchk.h>
38 #include <atalk/adouble.h>
39 #include <atalk/vfs.h>
40 #include <atalk/afp.h>
41 #include <atalk/util.h>
42 #include <atalk/cnid.h>
43 #include <atalk/logger.h>
44 #include <atalk/uuid.h>
45 #include <atalk/acl.h>
46 #include <atalk/bstrlib.h>
47 #include <atalk/bstradd.h>
48 #include <atalk/unix.h>
49 #include <atalk/netatalk_conf.h>
50
51 #include "directory.h"
52 #include "desktop.h"
53 #include "volume.h"
54 #include "fork.h"
55 #include "unix.h"
56 #include "acls.h"
57 #include "acl_mappings.h"
58 #include "auth.h"
59
60 /* for map_acl() */
61 #define SOLARIS_2_DARWIN       1
62 #define DARWIN_2_SOLARIS       2
63 #define POSIX_DEFAULT_2_DARWIN 3
64 #define POSIX_ACCESS_2_DARWIN  4
65 #define DARWIN_2_POSIX_DEFAULT 5
66 #define DARWIN_2_POSIX_ACCESS  6
67
68 #define MAP_MASK               31
69 #define IS_DIR                 32
70
71 /* bit flags for set_acl() and map_aces_darwin_to_posix() */
72 #define HAS_DEFAULT_ACL 0x01
73 #define HAS_EXT_DEFAULT_ACL 0x02
74
75 /********************************************************
76  * Solaris funcs
77  ********************************************************/
78
79 #ifdef HAVE_SOLARIS_ACLS
80
81 /*! 
82  * Compile access rights for a user to one file-system object
83  *
84  * This combines combines all access rights for a user to one fs-object and
85  * returns the result as a Darwin allowed rights ACE.
86  * This must honor trivial ACEs which are a mode_t mapping.
87  *
88  * @param path           (r) path to filesystem object
89  * @param sb             (r) struct stat of path
90  * @param result         (w) resulting Darwin allow ACE
91  *
92  * @returns                  0 or -1 on error
93  */
94 static int solaris_acl_rights(const char *path,
95                               const struct stat *sb,
96                               uint32_t *result)
97 {
98     EC_INIT;
99     int      i, ace_count, checkgroup;
100     ace_t    *aces = NULL;
101     uid_t    who;
102     uint16_t flags, type;
103     uint32_t rights, allowed_rights = 0, denied_rights = 0, darwin_rights;
104
105     /* Get ACL from file/dir */
106     EC_NEG1_LOG(ace_count = get_nfsv4_acl(path, &aces));
107
108     if (ace_count == 0)
109         goto EC_CLEANUP;
110
111     /* Now check requested rights */
112     i = 0;
113     do { /* Loop through ACEs */
114         who = aces[i].a_who;
115         flags = aces[i].a_flags;
116         type = aces[i].a_type;
117         rights = aces[i].a_access_mask;
118
119         if (flags & ACE_INHERIT_ONLY_ACE)
120             continue;
121
122         /* Now the tricky part: decide if ACE effects our user. I'll explain:
123            if its a dedicated (non trivial) ACE for the user
124            OR
125            if its a ACE for a group we're member of
126            OR
127            if its a trivial ACE_OWNER ACE and requested UUID is the owner
128            OR
129            if its a trivial ACE_GROUP ACE and requested UUID is group
130            OR
131            if its a trivial ACE_EVERYONE ACE
132            THEN
133            process ACE */
134         if (((who == uuid) && !(flags & (ACE_TRIVIAL|ACE_IDENTIFIER_GROUP)))
135             ||
136             ((flags & ACE_IDENTIFIER_GROUP) && !(flags & ACE_GROUP) && gmem(who))
137             ||
138             ((flags & ACE_OWNER) && (uuid == sb->st_uid))
139             ||
140             ((flags & ACE_GROUP) && !(uuid == sb->st_uid) && gmem(sb->st_gid))
141             ||
142             (flags & ACE_EVERYONE && !(uuid == sb->st_uid) && !gmem(sb->st_gid))
143             ) {
144             /* Found an applicable ACE */
145             if (type == ACE_ACCESS_ALLOWED_ACE_TYPE)
146                 allowed_rights |= rights;
147             else if (type == ACE_ACCESS_DENIED_ACE_TYPE)
148                 /* Only or to denied rights if not previously allowed !! */
149                 denied_rights |= ((!allowed_rights) & rights);
150         }
151     } while (++i < ace_count);
152
153
154     /* Darwin likes to ask for "delete_child" on dir,
155        "write_data" is actually the same, so we add that for dirs */
156     if (S_ISDIR(sb->st_mode) && (allowed_rights & ACE_WRITE_DATA))
157         allowed_rights |= ACE_DELETE_CHILD;
158
159     /* Remove denied from allowed rights */
160     allowed_rights &= ~denied_rights;
161
162     /* map rights */
163     darwin_rights = 0;
164     for (i=0; nfsv4_to_darwin_rights[i].from != 0; i++) {
165         if (allowed_rights & nfsv4_to_darwin_rights[i].from)
166             darwin_rights |= nfsv4_to_darwin_rights[i].to;
167     }
168
169     *result |= darwin_rights;
170
171 EC_CLEANUP:
172     if (aces) free(aces);
173
174     EC_EXIT;
175 }
176
177 /*
178   Maps ACE array from Solaris to Darwin. Darwin ACEs are stored in network byte order.
179   Return numer of mapped ACEs or -1 on error.
180   All errors while mapping (e.g. getting UUIDs from LDAP) are fatal.
181 */
182 static int map_aces_solaris_to_darwin(const ace_t *aces,
183                                       darwin_ace_t *darwin_aces,
184                                       int ace_count)
185 {
186     EC_INIT;
187     int i, count = 0;
188     uint32_t flags;
189     uint32_t rights;
190     struct passwd *pwd = NULL;
191     struct group *grp = NULL;
192
193     LOG(log_maxdebug, logtype_afpd, "map_aces_solaris_to_darwin: parsing %d ACES", ace_count);
194
195     while(ace_count--) {
196         LOG(log_maxdebug, logtype_afpd, "ACE No. %d", ace_count + 1);
197         /* if its a ACE resulting from nfsv4 mode mapping, discard it */
198         if (aces->a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)) {
199             LOG(log_debug, logtype_afpd, "trivial ACE");
200             aces++;
201             continue;
202         }
203
204         if ( ! (aces->a_flags & ACE_IDENTIFIER_GROUP) ) { /* user ace */
205             LOG(log_debug, logtype_afpd, "uid: %d", aces->a_who);
206             EC_NULL_LOG(pwd = getpwuid(aces->a_who));
207             LOG(log_debug, logtype_afpd, "uid: %d -> name: %s", aces->a_who, pwd->pw_name);
208             EC_ZERO_LOG(getuuidfromname(pwd->pw_name,
209                                         UUID_USER,
210                                         darwin_aces->darwin_ace_uuid));
211         } else { /* group ace */
212             LOG(log_debug, logtype_afpd, "gid: %d", aces->a_who);
213             EC_NULL_LOG(grp = getgrgid(aces->a_who));
214             LOG(log_debug, logtype_afpd, "gid: %d -> name: %s", aces->a_who, grp->gr_name);
215             EC_ZERO_LOG(getuuidfromname(grp->gr_name,
216                                         UUID_GROUP,
217                                         darwin_aces->darwin_ace_uuid));
218         }
219
220         /* map flags */
221         if (aces->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE)
222             flags = DARWIN_ACE_FLAGS_PERMIT;
223         else if (aces->a_type == ACE_ACCESS_DENIED_ACE_TYPE)
224             flags = DARWIN_ACE_FLAGS_DENY;
225         else {          /* unsupported type */
226             aces++;
227             continue;
228         }
229         for(i=0; nfsv4_to_darwin_flags[i].from != 0; i++) {
230             if (aces->a_flags & nfsv4_to_darwin_flags[i].from)
231                 flags |= nfsv4_to_darwin_flags[i].to;
232         }
233         darwin_aces->darwin_ace_flags = htonl(flags);
234
235         /* map rights */
236         rights = 0;
237         for (i=0; nfsv4_to_darwin_rights[i].from != 0; i++) {
238             if (aces->a_access_mask & nfsv4_to_darwin_rights[i].from)
239                 rights |= nfsv4_to_darwin_rights[i].to;
240         }
241         darwin_aces->darwin_ace_rights = htonl(rights);
242
243         count++;
244         aces++;
245         darwin_aces++;
246     }
247
248     return count;
249 EC_CLEANUP:
250     EC_EXIT;
251 }
252
253 /*
254   Maps ACE array from Darwin to Solaris. Darwin ACEs are expected in network byte order.
255   Return numer of mapped ACEs or -1 on error.
256   All errors while mapping (e.g. getting UUIDs from LDAP) are fatal.
257 */
258 static int map_aces_darwin_to_solaris(darwin_ace_t *darwin_aces,
259                                       ace_t *nfsv4_aces,
260                                       int ace_count)
261 {
262     EC_INIT;
263     int i, mapped_aces = 0;
264     uint32_t darwin_ace_flags;
265     uint32_t darwin_ace_rights;
266     uint16_t nfsv4_ace_flags;
267     uint32_t nfsv4_ace_rights;
268     char *name = NULL;
269     uuidtype_t uuidtype;
270     struct passwd *pwd;
271     struct group *grp;
272
273     while(ace_count--) {
274         nfsv4_ace_flags = 0;
275         nfsv4_ace_rights = 0;
276
277         /* uid/gid first */
278         EC_ZERO(getnamefromuuid(darwin_aces->darwin_ace_uuid, &name, &uuidtype));
279         switch (uuidtype) {
280         case UUID_USER:
281             EC_NULL_LOG(pwd = getpwnam(name));
282             nfsv4_aces->a_who = pwd->pw_uid;
283             break;
284         case UUID_GROUP:
285             EC_NULL_LOG(grp = getgrnam(name));
286             nfsv4_aces->a_who = (uid_t)(grp->gr_gid);
287             nfsv4_ace_flags |= ACE_IDENTIFIER_GROUP;
288             break;
289         default:
290             LOG(log_error, logtype_afpd, "map_aces_darwin_to_solaris: unkown uuidtype");
291             EC_FAIL;
292         }
293         free(name);
294         name = NULL;
295
296         /* now type: allow/deny */
297         darwin_ace_flags = ntohl(darwin_aces->darwin_ace_flags);
298         if (darwin_ace_flags & DARWIN_ACE_FLAGS_PERMIT)
299             nfsv4_aces->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
300         else if (darwin_ace_flags & DARWIN_ACE_FLAGS_DENY)
301             nfsv4_aces->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
302         else { /* unsupported type */
303             darwin_aces++;
304             continue;
305         }
306         /* map flags */
307         for(i=0; darwin_to_nfsv4_flags[i].from != 0; i++) {
308             if (darwin_ace_flags & darwin_to_nfsv4_flags[i].from)
309                 nfsv4_ace_flags |= darwin_to_nfsv4_flags[i].to;
310         }
311
312         /* map rights */
313         darwin_ace_rights = ntohl(darwin_aces->darwin_ace_rights);
314         for (i=0; darwin_to_nfsv4_rights[i].from != 0; i++) {
315             if (darwin_ace_rights & darwin_to_nfsv4_rights[i].from)
316                 nfsv4_ace_rights |= darwin_to_nfsv4_rights[i].to;
317         }
318
319         LOG(log_debug9, logtype_afpd,
320             "map_aces_darwin_to_solaris: ACE flags: Darwin:%08x -> NFSv4:%04x",
321             darwin_ace_flags, nfsv4_ace_flags);
322         LOG(log_debug9, logtype_afpd,
323             "map_aces_darwin_to_solaris: ACE rights: Darwin:%08x -> NFSv4:%08x",
324             darwin_ace_rights, nfsv4_ace_rights);
325
326         nfsv4_aces->a_flags = nfsv4_ace_flags;
327         nfsv4_aces->a_access_mask = nfsv4_ace_rights;
328
329         mapped_aces++;
330         darwin_aces++;
331         nfsv4_aces++;
332     }
333
334     return mapped_aces;
335 EC_CLEANUP:
336     if (name)
337         free(name);
338     EC_EXIT;
339 }
340 #endif /* HAVE_SOLARIS_ACLS */
341
342 /********************************************************
343  * POSIX 1e funcs
344  ********************************************************/
345
346 #ifdef HAVE_POSIX_ACLS
347
348 static uint32_t posix_permset_to_darwin_rights(acl_entry_t e, int is_dir)
349 {
350     EC_INIT;
351     uint32_t rights = 0;
352     acl_permset_t permset;
353
354     EC_ZERO_LOG(acl_get_permset(e, &permset));
355
356 #ifdef HAVE_ACL_GET_PERM_NP
357     if (acl_get_perm_np(permset, ACL_READ))
358 #else
359     if (acl_get_perm(permset, ACL_READ))
360 #endif
361         rights = DARWIN_ACE_READ_DATA
362             | DARWIN_ACE_READ_EXTATTRIBUTES
363             | DARWIN_ACE_READ_ATTRIBUTES
364             | DARWIN_ACE_READ_SECURITY;
365 #ifdef HAVE_ACL_GET_PERM_NP
366     if (acl_get_perm_np(permset, ACL_WRITE)) {
367 #else
368     if (acl_get_perm(permset, ACL_WRITE)) {
369 #endif
370         rights |= DARWIN_ACE_WRITE_DATA
371             | DARWIN_ACE_APPEND_DATA
372             | DARWIN_ACE_WRITE_EXTATTRIBUTES
373             | DARWIN_ACE_WRITE_ATTRIBUTES;
374         if (is_dir)
375             rights |= DARWIN_ACE_DELETE_CHILD;
376     }
377 #ifdef HAVE_ACL_GET_PERM_NP
378     if (acl_get_perm_np(permset, ACL_EXECUTE))
379 #else
380     if (acl_get_perm(permset, ACL_EXECUTE))
381 #endif
382         rights |= DARWIN_ACE_EXECUTE;
383
384 EC_CLEANUP:
385     LOG(log_maxdebug, logtype_afpd, "mapped rights: 0x%08x", rights);
386     return rights;
387 }
388
389 /*! 
390  * Compile access rights for a user to one file-system object
391  *
392  * This combines combines all access rights for a user to one fs-object and
393  * returns the result as a Darwin allowed rights ACE.
394  * This must honor trivial ACEs which are a mode_t mapping.
395  *
396  * @param path           (r) path to filesystem object
397  * @param sb             (r) struct stat of path
398  * @param result         (rw) resulting Darwin allow ACE
399  *
400  * @returns                  0 or -1 on error
401  */
402
403 static int posix_acl_rights(const AFPObj *obj,
404                             const char *path,
405                             const struct stat *sb,
406                             uint32_t *result)
407 {
408     EC_INIT;
409     int entry_id = ACL_FIRST_ENTRY;
410     uint32_t rights = 0; /* rights which do not depend on ACL_MASK */
411     uint32_t acl_rights = 0; /* rights which are subject to limitations imposed by ACL_MASK */
412     uint32_t mask_rights = 0xffffffff;
413     uid_t *uid = NULL;
414     gid_t *gid = NULL;
415     acl_t acl = NULL;
416     acl_entry_t e;
417     acl_tag_t tag;
418
419     EC_NULL_LOGSTR(acl = acl_get_file(path, ACL_TYPE_ACCESS),
420                    "acl_get_file(\"%s\"): %s", fullpathname(path), strerror(errno));
421
422     /* Iterate through all ACEs. If we apply mask_rights later there is no need to iterate twice. */
423     while (acl_get_entry(acl, entry_id, &e) == 1) {
424         entry_id = ACL_NEXT_ENTRY;
425         EC_ZERO_LOG(acl_get_tag_type(e, &tag));
426
427         switch (tag) {
428             case ACL_USER_OBJ:
429                 if (sb->st_uid == obj->uid) {
430                     LOG(log_maxdebug, logtype_afpd, "ACL_USER_OBJ: %u", sb->st_uid);
431                     rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
432                 }
433                 break;
434
435             case ACL_USER:
436                 EC_NULL_LOG(uid = (uid_t *)acl_get_qualifier(e));
437
438                 if (*uid == obj->uid) {
439                     LOG(log_maxdebug, logtype_afpd, "ACL_USER: %u", *uid);
440                     acl_rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
441                 }
442                 acl_free(uid);
443                 uid = NULL;
444                 break;
445
446             case ACL_GROUP_OBJ:
447                 if (!(sb->st_uid == obj->uid) && gmem(sb->st_gid, obj->ngroups, obj->groups)) {
448                     LOG(log_maxdebug, logtype_afpd, "ACL_GROUP_OBJ: %u", sb->st_gid);
449                     acl_rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
450                 }
451                 break;
452
453             case ACL_GROUP:
454                 EC_NULL_LOG(gid = (gid_t *)acl_get_qualifier(e));
455
456                 if (gmem(*gid, obj->ngroups, obj->groups)) {
457                     LOG(log_maxdebug, logtype_afpd, "ACL_GROUP: %u", *gid);
458                     acl_rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
459                 }
460                 acl_free(gid);
461                 gid = NULL;
462                 break;
463
464             case ACL_MASK:
465                 mask_rights = posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
466                 LOG(log_maxdebug, logtype_afpd, "maskrights: 0x%08x", mask_rights);
467                 break;
468
469             case ACL_OTHER:
470                 if (!(sb->st_uid == obj->uid) && !gmem(sb->st_gid, obj->ngroups, obj->groups)) {
471                     LOG(log_maxdebug, logtype_afpd, "ACL_OTHER");
472                     rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
473                 }
474                 break;
475
476             default:
477                 continue;
478         }
479     } /* while */
480
481     /* apply the mask and collect the rights */
482     rights |= (acl_rights & mask_rights);
483
484     *result |= rights;
485
486 EC_CLEANUP:
487     if (acl) acl_free(acl);
488     if (uid) acl_free(uid);
489     if (gid) acl_free(gid);
490     EC_EXIT;
491 }
492
493 /*!
494  * Helper function for posix_acls_to_uaperms() to convert Posix ACL permissions
495  * into access rights needed to fill ua_permissions of a FPUnixPrivs structure.
496  *
497  * @param entry     (r) Posix ACL entry
498  *
499  * @returns         access rights
500  */
501 static u_char acl_permset_to_uarights(acl_entry_t entry) {
502     acl_permset_t permset;
503     u_char rights = 0;
504
505     if (acl_get_permset(entry, &permset) == -1)
506         return rights;
507
508 #ifdef HAVE_ACL_GET_PERM_NP
509     if (acl_get_perm_np(permset, ACL_READ))
510 #else
511     if (acl_get_perm(permset, ACL_READ))
512 #endif
513         rights |= AR_UREAD;
514
515 #ifdef HAVE_ACL_GET_PERM_NP
516     if (acl_get_perm_np(permset, ACL_WRITE))
517 #else
518     if (acl_get_perm(permset, ACL_WRITE))
519 #endif
520         rights |= AR_UWRITE;
521
522 #ifdef HAVE_ACL_GET_PERM_NP
523     if (acl_get_perm_np(permset, ACL_EXECUTE))
524 #else
525     if (acl_get_perm(permset, ACL_EXECUTE))
526 #endif
527         rights |= AR_USEARCH;
528
529     return rights;
530 }
531
532 /*!
533  * Update FPUnixPrivs for a file-system object on a volume supporting ACLs
534  *
535  * Checks permissions granted by ACLS for a user to one fs-object and
536  * updates user and group permissions in given struct maccess. As OS X
537  * doesn't conform to Posix 1003.1e Draft 17 it expects proper group
538  * permissions in st_mode of struct stat even if the fs-object has an
539  * ACL_MASK entry, st_mode gets modified to properly reflect group
540  * permissions.
541  *
542  * @param path           (r) path to filesystem object
543  * @param sb             (rw) struct stat of path
544  * @param maccess        (rw) struct maccess of path
545  *
546  * @returns                  0 or -1 on error
547  */
548 static int posix_acls_to_uaperms(const AFPObj *obj, const char *path, struct stat *sb, struct maccess *ma) {
549     EC_INIT;
550
551     int entry_id = ACL_FIRST_ENTRY;
552     acl_entry_t entry;
553     acl_tag_t tag;
554     acl_t acl = NULL;
555     uid_t *uid;
556     gid_t *gid;
557     uid_t whoami = geteuid();
558
559     u_char group_rights = 0x00;
560     u_char acl_rights = 0x00;
561     u_char mask = 0xff;
562
563     EC_NULL_LOG(acl = acl_get_file(path, ACL_TYPE_ACCESS));
564
565     /* iterate through all ACEs */
566     while (acl_get_entry(acl, entry_id, &entry) == 1) {
567         entry_id = ACL_NEXT_ENTRY;
568         EC_ZERO_LOG(acl_get_tag_type(entry, &tag));
569
570         switch (tag) {
571             case ACL_USER:
572                 EC_NULL_LOG(uid = (uid_t *)acl_get_qualifier(entry));
573
574                 if (*uid == obj->uid && !(whoami == sb->st_uid)) {
575                     LOG(log_maxdebug, logtype_afpd, "ACL_USER: %u", *uid);
576                     acl_rights |= acl_permset_to_uarights(entry);
577                 }
578                 acl_free(uid);
579                 break;
580
581             case ACL_GROUP_OBJ:
582                 group_rights = acl_permset_to_uarights(entry);
583                 LOG(log_maxdebug, logtype_afpd, "ACL_GROUP_OBJ: %u", sb->st_gid);
584
585                 if (gmem(sb->st_gid, obj->ngroups, obj->groups) && !(whoami == sb->st_uid))
586                     acl_rights |= group_rights;
587                 break;
588
589             case ACL_GROUP:
590                 EC_NULL_LOG(gid = (gid_t *)acl_get_qualifier(entry));
591
592                 if (gmem(*gid, obj->ngroups, obj->groups) && !(whoami == sb->st_uid)) {
593                     LOG(log_maxdebug, logtype_afpd, "ACL_GROUP: %u", *gid);
594                     acl_rights |= acl_permset_to_uarights(entry);
595                 }
596                 acl_free(gid);
597                 break;
598
599             case ACL_MASK:
600                 mask = acl_permset_to_uarights(entry);
601                 LOG(log_maxdebug, logtype_afpd, "ACL_MASK: 0x%02x", mask);
602                 break;
603
604             default:
605                 break;
606         }
607     }
608     /* apply the mask and adjust user and group permissions */
609     ma->ma_user |= (acl_rights & mask);
610     ma->ma_group = (group_rights & mask);
611
612     /* update st_mode to properly reflect group permissions */
613     sb->st_mode &= ~S_IRWXG;
614
615     if (ma->ma_group & AR_USEARCH)
616         sb->st_mode |= S_IXGRP;
617
618     if (ma->ma_group & AR_UWRITE)
619         sb->st_mode |= S_IWGRP;
620
621     if (ma->ma_group & AR_UREAD)
622         sb->st_mode |= S_IRGRP;
623
624 EC_CLEANUP:
625     if (acl) acl_free(acl);
626
627     EC_EXIT;
628 }
629
630 /*!
631  * Add entries of one acl to another acl
632  *
633  * @param aclp   (rw) destination acl where new aces will be added
634  * @param acl    (r)  source acl where aces will be copied from
635  *
636  * @returns 0 on success, -1 on error
637  */
638 static int acl_add_acl(acl_t *aclp, const acl_t acl)
639 {
640     EC_INIT;
641     int id;
642     acl_entry_t se, de;
643
644     for (id = ACL_FIRST_ENTRY; acl_get_entry(acl, id, &se) == 1; id = ACL_NEXT_ENTRY) {
645         EC_ZERO_LOG_ERR(acl_create_entry(aclp, &de), AFPERR_MISC);
646         EC_ZERO_LOG_ERR(acl_copy_entry(de, se), AFPERR_MISC);
647     }
648
649 EC_CLEANUP:
650     EC_EXIT;
651 }
652
653 /*!
654  * Map Darwin ACE rights to POSIX 1e perm
655  *
656  * We can only map few rights:
657  *   DARWIN_ACE_READ_DATA                    -> ACL_READ
658  *   DARWIN_ACE_WRITE_DATA                   -> ACL_WRITE
659  *   DARWIN_ACE_DELETE_CHILD & (is_dir == 1) -> ACL_WRITE
660  *   DARWIN_ACE_EXECUTE                      -> ACL_EXECUTE
661  *
662  * @param entry             (rw) result of the mapping
663  * @param is_dir            (r) 1 for dirs, 0 for files
664  *
665  * @returns mapping result as acl_perm_t, -1 on error
666  */
667 static acl_perm_t map_darwin_right_to_posix_permset(uint32_t darwin_ace_rights, int is_dir)
668 {
669     acl_perm_t perm = 0;
670
671     if (darwin_ace_rights & DARWIN_ACE_READ_DATA)
672         perm |= ACL_READ;
673
674     if (darwin_ace_rights & (DARWIN_ACE_WRITE_DATA | (is_dir ? DARWIN_ACE_DELETE_CHILD : 0)))
675         perm |= ACL_WRITE;
676
677     if (darwin_ace_rights & DARWIN_ACE_EXECUTE)
678         perm |= ACL_EXECUTE;
679
680     return perm;
681 }
682
683 /*!
684  * Add a ACL_USER or ACL_GROUP permission to an ACL, extending existing ACEs
685  *
686  * Add a permission of "type" for user or group "id" to an ACL. Scan the ACL
687  * for existing permissions for this type/id, if there is one add the perm,
688  * otherwise create a new ACL entry.
689  * perm can be or'ed ACL_READ, ACL_WRITE and ACL_EXECUTE.  
690  *
691  * @param aclp     (rw) pointer to ACL
692  * @param type     (r)  acl_tag_t of ACL_USER or ACL_GROUP
693  * @param id       (r)  uid_t uid for ACL_USER, or gid casted to uid_t for ACL_GROUP
694  * @param perm     (r)  acl_perm_t permissions to add
695  *
696  * @returns 0 on success, -1 on failure
697  */
698 static int posix_acl_add_perm(acl_t *aclp, acl_tag_t type, uid_t id, acl_perm_t perm)
699 {
700     EC_INIT;
701     uid_t *eid = NULL;
702     acl_entry_t e;
703     acl_tag_t tag;
704     int entry_id = ACL_FIRST_ENTRY;
705     acl_permset_t permset;
706
707     int found = 0;
708     for ( ; (! found) && acl_get_entry(*aclp, entry_id, &e) == 1; entry_id = ACL_NEXT_ENTRY) {
709         EC_ZERO_LOG(acl_get_tag_type(e, &tag));
710         if (tag != ACL_USER && tag != ACL_GROUP)
711             continue;
712         EC_NULL_LOG(eid = (uid_t *)acl_get_qualifier(e));
713         if ((*eid == id) && (type == tag)) {
714             /* found an ACE for this type/id */
715             found = 1;
716             EC_ZERO_LOG(acl_get_permset(e, &permset));
717             EC_ZERO_LOG(acl_add_perm(permset, perm));
718         }
719
720         acl_free(eid);
721         eid = NULL;
722     }
723
724     if ( ! found) {
725         /* there was no existing ACE for this type/id */
726         EC_ZERO_LOG(acl_create_entry(aclp, &e));
727         EC_ZERO_LOG(acl_set_tag_type(e, type));
728         EC_ZERO_LOG(acl_set_qualifier(e, &id));
729         EC_ZERO_LOG(acl_get_permset(e, &permset));
730         EC_ZERO_LOG(acl_clear_perms(permset));
731         EC_ZERO_LOG(acl_add_perm(permset, perm));
732         EC_ZERO_LOG(acl_set_permset(e, permset));
733     }
734
735 EC_CLEANUP:
736     if (eid) acl_free(eid);
737
738     EC_EXIT;
739 }
740
741 /*!
742  * Map Darwin ACL to POSIX ACL.
743  *
744  * aclp must point to a acl_init'ed acl_t or an acl_t that can eg contain default ACEs.
745  * Mapping pecularities:
746  * - we create a default ace (which inherits to files and dirs) if either
747      DARWIN_ACE_FLAGS_FILE_INHERIT or DARWIN_ACE_FLAGS_DIRECTORY_INHERIT is requested
748  * - we throw away DARWIN_ACE_FLAGS_LIMIT_INHERIT (can't be mapped), thus the ACL will
749  *   not be limited
750  *
751  * @param darwin_aces        (r)  pointer to darwin_aces buffer
752  * @param def_aclp           (rw) directories: pointer to an initialized acl_t with
753                                   the default acl files: *def_aclp will be NULL
754  * @param acc_aclp           (rw) pointer to an initialized acl_t with the access acl
755  * @param ace_count          (r)  number of ACEs in darwin_aces buffer
756  * @param default_acl_flags  (rw) flags to indicate if the object has a basic default
757  *                                acl or an extended default acl.
758  *
759  * @returns 0 on success storing the result in aclp, -1 on error. default_acl_flags
760  * is set to HAS_DEFAULT_ACL|HAS_EXT_DEFAULT_ACL in case there is at least one
761  * extended default ace. Otherwise default_acl_flags is left unchanged.
762  */
763 static int map_aces_darwin_to_posix(const darwin_ace_t *darwin_aces,
764                                     acl_t *def_aclp,
765                                     acl_t *acc_aclp,
766                                     int ace_count,
767                                     uint32_t *default_acl_flags)
768 {
769     EC_INIT;
770     char *name = NULL;
771     uuidtype_t uuidtype;
772     struct passwd *pwd;
773     struct group *grp;
774     uid_t id;
775     uint32_t darwin_ace_flags, darwin_ace_rights;
776     acl_tag_t tag;
777     acl_perm_t perm;
778
779     for ( ; ace_count != 0; ace_count--, darwin_aces++) {
780         /* type: allow/deny, posix only has allow */
781         darwin_ace_flags = ntohl(darwin_aces->darwin_ace_flags);
782         if ( ! (darwin_ace_flags & DARWIN_ACE_FLAGS_PERMIT))
783             continue;
784
785         darwin_ace_rights = ntohl(darwin_aces->darwin_ace_rights);
786         perm = map_darwin_right_to_posix_permset(darwin_ace_rights, (*def_aclp != NULL));
787         if (perm == 0)
788             continue;       /* dont add empty perm */
789
790         LOG(log_debug, logtype_afpd, "map_ace: no: %u, flags: %08x, darwin: %08x, posix: %02x",
791             ace_count, darwin_ace_flags, darwin_ace_rights, perm);
792
793          /* uid/gid */
794         EC_ZERO_LOG(getnamefromuuid(darwin_aces->darwin_ace_uuid, &name, &uuidtype));
795         switch (uuidtype) {
796         case UUID_USER:
797             EC_NULL_LOG(pwd = getpwnam(name));
798             tag = ACL_USER;
799             id = pwd->pw_uid;
800             LOG(log_debug, logtype_afpd, "map_ace: name: %s, uid: %u", name, id);
801             break;
802         case UUID_GROUP:
803             EC_NULL_LOG(grp = getgrnam(name));
804             tag = ACL_GROUP;
805             id = (uid_t)(grp->gr_gid);
806             LOG(log_debug, logtype_afpd, "map_ace: name: %s, gid: %u", name, id);
807             break;
808         default:
809             continue;
810         }
811         free(name);
812         name = NULL;
813
814         if (darwin_ace_flags & DARWIN_ACE_INHERIT_CONTROL_FLAGS) {
815             if (*def_aclp == NULL) {
816                 /* ace request inheritane but we haven't got a default acl pointer */
817                 LOG(log_warning, logtype_afpd, "map_acl: unexpected ACE, flags: 0x%04x",
818                     darwin_ace_flags);
819                 EC_FAIL;
820             }
821             /* add it as default ace */
822             EC_ZERO_LOG(posix_acl_add_perm(def_aclp, tag, id, perm));
823             *default_acl_flags = (HAS_DEFAULT_ACL|HAS_EXT_DEFAULT_ACL);
824
825             if (! (darwin_ace_flags & DARWIN_ACE_FLAGS_ONLY_INHERIT))
826                 /* if it not a "inherit only" ace, it must be added as access aces too */
827                 EC_ZERO_LOG(posix_acl_add_perm(acc_aclp, tag, id, perm));
828         } else {
829             EC_ZERO_LOG(posix_acl_add_perm(acc_aclp, tag, id, perm));
830         }
831     }
832
833 EC_CLEANUP:
834     if (name)
835         free(name);
836
837     EC_EXIT;
838 }
839
840 /*
841  * Map ACEs from POSIX to Darwin.
842  * type is either POSIX_DEFAULT_2_DARWIN or POSIX_ACCESS_2_DARWIN, cf. acl_get_file.
843  * Return number of mapped ACES, -1 on error.
844  */
845 static int map_acl_posix_to_darwin(int type, const acl_t acl, darwin_ace_t *darwin_aces)
846 {
847     EC_INIT;
848     int mapped_aces = 0;
849     int entry_id = ACL_FIRST_ENTRY;
850     acl_entry_t e;
851     acl_tag_t tag;
852     uid_t *uid = NULL;
853     gid_t *gid = NULL;
854     struct passwd *pwd = NULL;
855     struct group *grp = NULL;
856     uint32_t flags;
857     uint32_t rights, maskrights = 0;
858     darwin_ace_t *saved_darwin_aces = darwin_aces;
859
860     LOG(log_maxdebug, logtype_afpd, "map_aces_posix_to_darwin(%s)",
861         (type & MAP_MASK) == POSIX_DEFAULT_2_DARWIN ?
862         "POSIX_DEFAULT_2_DARWIN" : "POSIX_ACCESS_2_DARWIN");
863
864     /* itereate through all ACEs */
865     while (acl_get_entry(acl, entry_id, &e) == 1) {
866         entry_id = ACL_NEXT_ENTRY;
867
868         /* get ACE type */
869         EC_ZERO_LOG(acl_get_tag_type(e, &tag));
870
871         /* we return user and group ACE */
872         switch (tag) {
873         case ACL_USER:
874             EC_NULL_LOG(uid = (uid_t *)acl_get_qualifier(e));
875             EC_NULL_LOG(pwd = getpwuid(*uid));
876             LOG(log_debug, logtype_afpd, "map_aces_posix_to_darwin: uid: %d -> name: %s",
877                 *uid, pwd->pw_name);
878             EC_ZERO_LOG(getuuidfromname(pwd->pw_name, UUID_USER, darwin_aces->darwin_ace_uuid));
879             acl_free(uid);
880             uid = NULL;
881             break;
882
883         case ACL_GROUP:
884             EC_NULL_LOG(gid = (gid_t *)acl_get_qualifier(e));
885             EC_NULL_LOG(grp = getgrgid(*gid));
886             LOG(log_debug, logtype_afpd, "map_aces_posix_to_darwin: gid: %d -> name: %s",
887                 *gid, grp->gr_name);
888             EC_ZERO_LOG(getuuidfromname(grp->gr_name, UUID_GROUP, darwin_aces->darwin_ace_uuid));
889             acl_free(gid);
890             gid = NULL;
891             break;
892
893         case ACL_MASK:
894             maskrights = posix_permset_to_darwin_rights(e, type & IS_DIR);
895             continue;
896
897         default:
898             continue;
899         }
900
901         /* flags */
902         flags = DARWIN_ACE_FLAGS_PERMIT;
903         if ((type & MAP_MASK) == POSIX_DEFAULT_2_DARWIN)
904             flags |= DARWIN_ACE_FLAGS_FILE_INHERIT
905                 | DARWIN_ACE_FLAGS_DIRECTORY_INHERIT
906                 | DARWIN_ACE_FLAGS_ONLY_INHERIT;
907         darwin_aces->darwin_ace_flags = htonl(flags);
908
909         /* rights */
910         rights = posix_permset_to_darwin_rights(e, type & IS_DIR);
911         darwin_aces->darwin_ace_rights = htonl(rights);
912
913         darwin_aces++;
914         mapped_aces++;
915     } /* while */
916
917     /* Loop through the mapped ACE buffer once again, applying the mask */
918     for (int i = mapped_aces; i > 0; i--) {
919         saved_darwin_aces->darwin_ace_rights &= htonl(maskrights);
920         saved_darwin_aces++;
921     }
922
923     EC_STATUS(mapped_aces);
924
925 EC_CLEANUP:
926     if (uid) acl_free(uid);
927     if (gid) acl_free(gid);
928     EC_EXIT;
929 }
930 #endif
931
932 /*
933  * Multiplex ACL mapping (SOLARIS_2_DARWIN, DARWIN_2_SOLARIS, POSIX_2_DARWIN, DARWIN_2_POSIX).
934  * Reads from 'aces' buffer, writes to 'rbuf' buffer.
935  * Caller must provide buffer.
936  * Darwin ACEs are read and written in network byte order.
937  * Needs to know how many ACEs are in the ACL (ace_count) for Solaris ACLs.
938  * Ignores trivial ACEs.
939  * Return no of mapped ACEs or -1 on error.
940  */
941 static int map_acl(int type, void *acl, darwin_ace_t *buf, int ace_count)
942 {
943     int mapped_aces;
944
945     LOG(log_debug9, logtype_afpd, "map_acl: BEGIN");
946
947     switch (type & MAP_MASK) {
948
949 #ifdef HAVE_SOLARIS_ACLS
950     case SOLARIS_2_DARWIN:
951         mapped_aces = map_aces_solaris_to_darwin( acl, buf, ace_count);
952         break;
953
954     case DARWIN_2_SOLARIS:
955         mapped_aces = map_aces_darwin_to_solaris( buf, acl, ace_count);
956         break;
957 #endif /* HAVE_SOLARIS_ACLS */
958
959 #ifdef HAVE_POSIX_ACLS
960     case POSIX_DEFAULT_2_DARWIN:
961         mapped_aces = map_acl_posix_to_darwin(type, (const acl_t)acl, buf);
962         break;
963
964     case POSIX_ACCESS_2_DARWIN:
965         mapped_aces = map_acl_posix_to_darwin(type, (const acl_t)acl, buf);
966         break;
967
968     case DARWIN_2_POSIX_DEFAULT:
969         break;
970
971     case DARWIN_2_POSIX_ACCESS:
972         break;
973 #endif /* HAVE_POSIX_ACLS */
974
975     default:
976         mapped_aces = -1;
977         break;
978     }
979
980     LOG(log_debug9, logtype_afpd, "map_acl: END");
981     return mapped_aces;
982 }
983
984 /* Get ACL from object omitting trivial ACEs. Map to Darwin ACL style and
985    store Darwin ACL at rbuf. Add length of ACL written to rbuf to *rbuflen.
986    Returns 0 on success, -1 on error. */
987 static int get_and_map_acl(char *name, char *rbuf, size_t *rbuflen)
988 {
989     EC_INIT;
990     int mapped_aces = 0;
991     int dirflag;
992     uint32_t *darwin_ace_count = (uint32_t *)rbuf;
993 #ifdef HAVE_SOLARIS_ACLS
994     int ace_count = 0;
995     ace_t *aces = NULL;
996 #endif
997 #ifdef HAVE_POSIX_ACLS
998     struct stat st;
999 #endif
1000     LOG(log_debug9, logtype_afpd, "get_and_map_acl: BEGIN");
1001
1002     /* Skip length and flags */
1003     rbuf += 4;
1004     *rbuf = 0;
1005     rbuf += 4;
1006
1007 #ifdef HAVE_SOLARIS_ACLS
1008     EC_NEG1(ace_count = get_nfsv4_acl(name, &aces));
1009     EC_NEG1(mapped_aces = map_acl(SOLARIS_2_DARWIN, aces, (darwin_ace_t *)rbuf, ace_count));
1010 #endif /* HAVE_SOLARIS_ACLS */
1011
1012 #ifdef HAVE_POSIX_ACLS
1013     acl_t defacl = NULL , accacl = NULL;
1014
1015     /* stat to check if its a dir */
1016     EC_ZERO_LOG(lstat(name, &st));
1017
1018     /* if its a dir, check for default acl too */
1019     dirflag = 0;
1020     if (S_ISDIR(st.st_mode)) {
1021         dirflag = IS_DIR;
1022         EC_NULL_LOG(defacl = acl_get_file(name, ACL_TYPE_DEFAULT));
1023         EC_NEG1(mapped_aces = map_acl(POSIX_DEFAULT_2_DARWIN | dirflag,
1024                                       defacl,
1025                                       (darwin_ace_t *)rbuf,
1026                                       0));
1027     }
1028
1029     EC_NULL_LOG(accacl = acl_get_file(name, ACL_TYPE_ACCESS));
1030
1031     int tmp;
1032     EC_NEG1(tmp = map_acl(POSIX_ACCESS_2_DARWIN | dirflag,
1033                           accacl,
1034                           (darwin_ace_t *)(rbuf + mapped_aces * sizeof(darwin_ace_t)),
1035                           0));
1036     mapped_aces += tmp;
1037 #endif /* HAVE_POSIX_ACLS */
1038
1039     LOG(log_debug, logtype_afpd, "get_and_map_acl: mapped %d ACEs", mapped_aces);
1040
1041     *darwin_ace_count = htonl(mapped_aces);
1042     *rbuflen += sizeof(darwin_acl_header_t) + (mapped_aces * sizeof(darwin_ace_t));
1043
1044     EC_STATUS(0);
1045
1046 EC_CLEANUP:
1047 #ifdef HAVE_SOLARIS_ACLS
1048     if (aces) free(aces);
1049 #endif
1050 #ifdef HAVE_POSIX_ACLS
1051     if (defacl) acl_free(defacl);
1052     if (accacl) acl_free(accacl);
1053 #endif /* HAVE_POSIX_ACLS */
1054
1055     LOG(log_debug9, logtype_afpd, "get_and_map_acl: END");
1056
1057     EC_EXIT;
1058 }
1059
1060 /* Removes all non-trivial ACLs from object. Returns full AFPERR code. */
1061 static int remove_acl(const struct vol *vol,const char *path, int dir)
1062 {
1063     int ret = AFP_OK;
1064
1065 #if (defined HAVE_SOLARIS_ACLS || defined HAVE_POSIX_ACLS)
1066     /* Ressource etc. first */
1067     if ((ret = vol->vfs->vfs_remove_acl(vol, path, dir)) != AFP_OK)
1068         return ret;
1069     /* now the data fork or dir */
1070     ret = remove_acl_vfs(path);
1071 #endif
1072     return ret;
1073 }
1074
1075 /*
1076   Set ACL. Subtleties:
1077   - the client sends a complete list of ACEs, not only new ones. So we dont need to do
1078   any combination business (one exception being 'kFileSec_Inherit': see next)
1079   - client might request that we add inherited ACEs via 'kFileSec_Inherit'.
1080   We will store inherited ACEs first, which is Darwins canonical order.
1081   - returns AFPerror code
1082 */
1083 #ifdef HAVE_SOLARIS_ACLS
1084 static int set_acl(const struct vol *vol,
1085                    char *name,
1086                    int inherit,
1087                    darwin_ace_t *daces,
1088                    uint32_t ace_count)
1089 {
1090     EC_INIT;
1091     int i, nfsv4_ace_count;
1092     int tocopy_aces_count = 0, new_aces_count = 0, trivial_ace_count = 0;
1093     ace_t *old_aces, *new_aces = NULL;
1094     uint16_t flags;
1095
1096     LOG(log_debug9, logtype_afpd, "set_acl: BEGIN");
1097
1098     if (inherit)
1099         /* inherited + trivial ACEs */
1100         flags = ACE_INHERITED_ACE | ACE_OWNER | ACE_GROUP | ACE_EVERYONE;
1101     else
1102         /* only trivial ACEs */
1103         flags = ACE_OWNER | ACE_GROUP | ACE_EVERYONE;
1104
1105     /* Get existing ACL and count ACEs which have to be copied */
1106     if ((nfsv4_ace_count = get_nfsv4_acl(name, &old_aces)) == -1)
1107         return AFPERR_MISC;
1108     for ( i=0; i < nfsv4_ace_count; i++) {
1109         if (old_aces[i].a_flags & flags)
1110             tocopy_aces_count++;
1111     }
1112
1113     /* Now malloc buffer exactly sized to fit all new ACEs */
1114     if ((new_aces = malloc((ace_count + tocopy_aces_count) * sizeof(ace_t))) == NULL) {
1115         LOG(log_error, logtype_afpd, "set_acl: malloc %s", strerror(errno));
1116         EC_STATUS(AFPERR_MISC);
1117         goto EC_CLEANUP;
1118     }
1119
1120     /* Start building new ACL */
1121
1122     /* Copy local inherited ACEs. Therefore we have 'Darwin canonical order' (see chmod there):
1123        inherited ACEs first. */
1124     if (inherit) {
1125         for (i=0; i < nfsv4_ace_count; i++) {
1126             if (old_aces[i].a_flags & ACE_INHERITED_ACE) {
1127                 memcpy(&new_aces[new_aces_count], &old_aces[i], sizeof(ace_t));
1128                 new_aces_count++;
1129             }
1130         }
1131     }
1132     LOG(log_debug7, logtype_afpd, "set_acl: copied %d inherited ACEs", new_aces_count);
1133
1134     /* Now the ACEs from the client */
1135     if ((ret = (map_acl(DARWIN_2_SOLARIS,
1136                         &new_aces[new_aces_count],
1137                         daces,
1138                         ace_count))) == -1) {
1139         EC_STATUS(AFPERR_PARAM);
1140         goto EC_CLEANUP;
1141     }
1142     new_aces_count += ret;
1143     LOG(log_debug7, logtype_afpd, "set_acl: mapped %d ACEs from client", ret);
1144
1145     /* Now copy the trivial ACEs */
1146     for (i=0; i < nfsv4_ace_count; i++) {
1147         if (old_aces[i].a_flags  & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)) {
1148             memcpy(&new_aces[new_aces_count], &old_aces[i], sizeof(ace_t));
1149             new_aces_count++;
1150             trivial_ace_count++;
1151         }
1152     }
1153     LOG(log_debug7, logtype_afpd, "set_acl: copied %d trivial ACEs", trivial_ace_count);
1154
1155     /* Ressourcefork first.
1156        Note: for dirs we set the same ACL on the .AppleDouble/.Parent _file_. This
1157        might be strange for ACE_DELETE_CHILD and for inheritance flags. */
1158     if ((ret = (vol->vfs->vfs_acl(vol, name, ACE_SETACL, new_aces_count, new_aces))) != 0) {
1159         LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
1160         if (errno == (EACCES | EPERM))
1161             EC_STATUS(AFPERR_ACCESS);
1162         else if (errno == ENOENT)
1163             EC_STATUS(AFPERR_NOITEM);
1164         else
1165             EC_STATUS(AFPERR_MISC);
1166         goto EC_CLEANUP;
1167     }
1168     if ((ret = (acl(name, ACE_SETACL, new_aces_count, new_aces))) != 0) {
1169         LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
1170         if (errno == (EACCES | EPERM))
1171             EC_STATUS(AFPERR_ACCESS);
1172         else if (errno == ENOENT)
1173             EC_STATUS(AFPERR_NOITEM);
1174         else
1175             EC_STATUS(AFPERR_MISC);
1176         goto EC_CLEANUP;
1177     }
1178
1179     EC_STATUS(AFP_OK);
1180
1181 EC_CLEANUP:
1182     if (old_aces) free(old_aces);
1183     if (new_aces) free(new_aces);
1184
1185     LOG(log_debug9, logtype_afpd, "set_acl: END");
1186     EC_EXIT;
1187 }
1188 #endif /* HAVE_SOLARIS_ACLS */
1189
1190 #ifdef HAVE_POSIX_ACLS
1191 #ifndef HAVE_ACL_FROM_MODE
1192 static acl_t acl_from_mode(mode_t mode)
1193 {
1194     acl_t acl;
1195     acl_entry_t entry;
1196     acl_permset_t permset;
1197
1198     if (!(acl = acl_init(3)))
1199         return NULL;
1200
1201     if (acl_create_entry(&acl, &entry) != 0)
1202         goto error;
1203     acl_set_tag_type(entry, ACL_USER_OBJ);
1204     acl_get_permset(entry, &permset);
1205     acl_clear_perms(permset);
1206     if (mode & S_IRUSR)
1207         acl_add_perm(permset, ACL_READ);
1208     if (mode & S_IWUSR)
1209         acl_add_perm(permset, ACL_WRITE);
1210     if (mode & S_IXUSR)
1211         acl_add_perm(permset, ACL_EXECUTE);
1212     acl_set_permset(entry, permset);
1213
1214     if (acl_create_entry(&acl, &entry) != 0)
1215         goto error;
1216     acl_set_tag_type(entry, ACL_GROUP_OBJ);
1217     acl_get_permset(entry, &permset);
1218     acl_clear_perms(permset);
1219     if (mode & S_IRGRP)
1220         acl_add_perm(permset, ACL_READ);
1221     if (mode & S_IWGRP)
1222         acl_add_perm(permset, ACL_WRITE);
1223     if (mode & S_IXGRP)
1224         acl_add_perm(permset, ACL_EXECUTE);
1225     acl_set_permset(entry, permset);
1226
1227     if (acl_create_entry(&acl, &entry) != 0)
1228         goto error;
1229     acl_set_tag_type(entry, ACL_OTHER);
1230     acl_get_permset(entry, &permset);
1231     acl_clear_perms(permset);
1232     if (mode & S_IROTH)
1233         acl_add_perm(permset, ACL_READ);
1234     if (mode & S_IWOTH)
1235         acl_add_perm(permset, ACL_WRITE);
1236     if (mode & S_IXOTH)
1237         acl_add_perm(permset, ACL_EXECUTE);
1238     acl_set_permset(entry, permset);
1239
1240     return acl;
1241
1242 error:
1243     acl_free(acl);
1244     return NULL;
1245 }
1246 #endif
1247
1248 static int set_acl(const struct vol *vol,
1249                    const char *name,
1250                    int inherit _U_,
1251                    darwin_ace_t *daces,
1252                    uint32_t ace_count)
1253 {
1254     EC_INIT;
1255     struct stat st;
1256     acl_t default_acl = NULL;
1257     acl_t access_acl = NULL;
1258     acl_entry_t entry;
1259     acl_tag_t tag;
1260     int entry_id = ACL_FIRST_ENTRY;
1261     int has_def_acl = 0;
1262     /* flags to indicate if the object has a minimal default acl and/or an extended
1263      * default acl.
1264      */
1265     uint32_t default_acl_flags = 0;
1266
1267     LOG(log_maxdebug, logtype_afpd, "set_acl: BEGIN");
1268
1269     EC_NULL_LOG_ERR(access_acl = acl_get_file(name, ACL_TYPE_ACCESS), AFPERR_MISC);
1270
1271     /* Iterate through acl and remove all extended acl entries. */
1272     while (acl_get_entry(access_acl, entry_id, &entry) == 1) {
1273         entry_id = ACL_NEXT_ENTRY;
1274         EC_ZERO_LOG(acl_get_tag_type(entry, &tag));
1275
1276         if ((tag == ACL_USER) || (tag == ACL_GROUP) || (tag == ACL_MASK)) {
1277             EC_ZERO_LOG_ERR(acl_delete_entry(access_acl, entry), AFPERR_MISC);
1278         }
1279     } /* while */
1280
1281    /* In case we are acting on a directory prepare a default acl. For files default_acl will be NULL.
1282     * If the directory already has a default acl it will be preserved.
1283     */
1284    EC_ZERO_LOG_ERR(lstat(name, &st), AFPERR_NOOBJ);
1285
1286    if (S_ISDIR(st.st_mode)) {
1287        default_acl = acl_get_file(name, ACL_TYPE_DEFAULT);
1288
1289        if (default_acl) {
1290            /* If default_acl is not empty then the dir has a default acl. */
1291            if (acl_get_entry(default_acl, ACL_FIRST_ENTRY, &entry) == 1)
1292                default_acl_flags = HAS_DEFAULT_ACL;
1293
1294            acl_free(default_acl);
1295        }
1296        default_acl = acl_dup(access_acl);
1297     }
1298     /* adds the clients aces */
1299     EC_ZERO_ERR(map_aces_darwin_to_posix(daces, &default_acl, &access_acl, ace_count, &default_acl_flags), AFPERR_MISC);
1300
1301     /* calcuate ACL mask */
1302     EC_ZERO_LOG_ERR(acl_calc_mask(&access_acl), AFPERR_MISC);
1303
1304     /* is it ok? */
1305     EC_ZERO_LOG_ERR(acl_valid(access_acl), AFPERR_MISC);
1306
1307     /* set it */
1308     EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_ACCESS, access_acl), AFPERR_MISC);
1309     EC_ZERO_LOG_ERR(vol->vfs->vfs_acl(vol, name, ACL_TYPE_ACCESS, 0, access_acl), AFPERR_MISC);
1310
1311     if (default_acl) {
1312         /* If the dir has an extended default acl it's ACL_MASK must be updated.*/
1313         if (default_acl_flags & HAS_EXT_DEFAULT_ACL)
1314             EC_ZERO_LOG_ERR(acl_calc_mask(&default_acl), AFPERR_MISC);
1315
1316         if (default_acl_flags) {
1317             EC_ZERO_LOG_ERR(acl_valid(default_acl), AFPERR_MISC);
1318             EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_DEFAULT, default_acl), AFPERR_MISC);
1319             EC_ZERO_LOG_ERR(vol->vfs->vfs_acl(vol, name, ACL_TYPE_DEFAULT, 0, default_acl), AFPERR_MISC);
1320         }
1321     }
1322
1323 EC_CLEANUP:
1324     if (access_acl) acl_free(access_acl);
1325     if (default_acl) acl_free(default_acl);
1326
1327     LOG(log_maxdebug, logtype_afpd, "set_acl: END");
1328     EC_EXIT;
1329 }
1330 #endif /* HAVE_POSIX_ACLS */
1331
1332 /*!
1333  * Checks if a given UUID has requested_rights(type darwin_ace_rights) for path.
1334  *
1335  * Note: this gets called frequently and is a good place for optimizations !
1336  *
1337  * @param vol              (r) volume
1338  * @param dir              (rw) directory
1339  * @param path             (r) path to filesystem object
1340  * @param uuid             (r) UUID of user
1341  * @param requested_rights (r) requested Darwin ACE
1342  *
1343  * @returns                    AFP result code
1344 */
1345 static int check_acl_access(const AFPObj *obj,
1346                             const struct vol *vol,
1347                             struct dir *dir,
1348                             const char *path,
1349                             const uuidp_t uuid,
1350                             uint32_t requested_rights)
1351 {
1352     int            ret;
1353     uint32_t       allowed_rights = 0;
1354     char           *username = NULL;
1355     uuidtype_t     uuidtype;
1356     struct stat    st;
1357     bstring        parent = NULL;
1358     int            is_dir;
1359
1360     LOG(log_maxdebug, logtype_afpd, "check_acl_access(dir: \"%s\", path: \"%s\", curdir: \"%s\", 0x%08x)",
1361         cfrombstr(dir->d_fullpath), path, getcwdpath(), requested_rights);
1362
1363     /* This check is not used anymore, as OS X Server seems to be ignoring too */
1364 #if 0
1365     /* Get uid or gid from UUID */
1366     EC_ZERO_ERR(getnamefromuuid(uuid, &username, &uuidtype), AFPERR_PARAM);
1367     switch (uuidtype) {
1368     case UUID_USER:
1369         break;
1370     case UUID_GROUP:
1371         LOG(log_warning, logtype_afpd, "check_acl_access: afp_access not supported for groups");
1372         EC_STATUS(AFPERR_MISC);
1373         goto EC_CLEANUP;
1374     default:
1375         EC_STATUS(AFPERR_MISC);
1376         goto EC_CLEANUP;
1377     }
1378 #endif
1379
1380     EC_ZERO_LOG_ERR(lstat(path, &st), AFPERR_PARAM);
1381
1382     is_dir = !strcmp(path, ".");
1383
1384     if (is_dir && (curdir->d_rights_cache != 0xffffffff)) {
1385         /* its a dir and the cache value is valid */
1386         allowed_rights = curdir->d_rights_cache;
1387         LOG(log_debug, logtype_afpd, "check_access: allowed rights from dircache: 0x%08x", allowed_rights);
1388     } else {
1389 #ifdef HAVE_SOLARIS_ACLS
1390         EC_ZERO_LOG(solaris_acl_rights(path, &st, &allowed_rights));
1391 #endif
1392 #ifdef HAVE_POSIX_ACLS
1393         EC_ZERO_LOG(posix_acl_rights(obj, path, &st, &allowed_rights));
1394 #endif
1395         /*
1396          * The DARWIN_ACE_DELETE right might implicitly result from write acces to the parent
1397          * directory. As it seems the 10.6 AFP client is puzzled when this right is not
1398          * allowed where a delete would succeed because the parent dir gives write perms.
1399          * So we check the parent dir for write access and set the right accordingly.
1400          * Currentyl acl2ownermode calls us with dir = NULL, because it doesn't make sense
1401          * there to do this extra check -- afaict.
1402          */
1403         if (vol && dir && (requested_rights & DARWIN_ACE_DELETE)) {
1404             int i;
1405             uint32_t parent_rights = 0;
1406
1407             if (curdir->d_did == DIRDID_ROOT_PARENT) {
1408                 /* use volume path */
1409                 EC_NULL_LOG_ERR(parent = bfromcstr(vol->v_path), AFPERR_MISC);
1410             } else {
1411                 /* build path for parent */
1412                 EC_NULL_LOG_ERR(parent = bstrcpy(curdir->d_fullpath), AFPERR_MISC);
1413                 EC_ZERO_LOG_ERR(bconchar(parent, '/'), AFPERR_MISC);
1414                 EC_ZERO_LOG_ERR(bcatcstr(parent, path), AFPERR_MISC);
1415                 EC_NEG1_LOG_ERR(i = bstrrchr(parent, '/'), AFPERR_MISC);
1416                 EC_ZERO_LOG_ERR(binsertch(parent, i, 1, 0), AFPERR_MISC);
1417             }
1418
1419             LOG(log_debug, logtype_afpd,"parent: %s", cfrombstr(parent));
1420             EC_ZERO_LOG_ERR(lstat(cfrombstr(parent), &st), AFPERR_MISC);
1421
1422 #ifdef HAVE_SOLARIS_ACLS
1423             EC_ZERO_LOG(solaris_acl_rights(cfrombstr(parent), &st, &parent_rights));
1424 #endif
1425 #ifdef HAVE_POSIX_ACLS
1426             EC_ZERO_LOG(posix_acl_rights(obj, path, &st, &parent_rights));
1427 #endif
1428             if (parent_rights & (DARWIN_ACE_WRITE_DATA | DARWIN_ACE_DELETE_CHILD))
1429                 allowed_rights |= DARWIN_ACE_DELETE; /* man, that was a lot of work! */
1430         }
1431
1432         if (is_dir) {
1433             /* Without DARWIN_ACE_DELETE set OS X 10.6 refuses to rename subdirectories in a
1434              * directory.
1435              */
1436             if (allowed_rights & DARWIN_ACE_ADD_SUBDIRECTORY)
1437                 allowed_rights |= DARWIN_ACE_DELETE;
1438
1439             curdir->d_rights_cache = allowed_rights;
1440         }
1441         LOG(log_debug, logtype_afpd, "allowed rights: 0x%08x", allowed_rights);
1442     }
1443
1444     if ((requested_rights & allowed_rights) != requested_rights) {
1445         LOG(log_debug, logtype_afpd, "some requested right wasn't allowed: 0x%08x / 0x%08x",
1446             requested_rights, allowed_rights);
1447         EC_STATUS(AFPERR_ACCESS);
1448     } else {
1449         LOG(log_debug, logtype_afpd, "all requested rights are allowed: 0x%08x",
1450             requested_rights);
1451         EC_STATUS(AFP_OK);
1452     }
1453
1454 EC_CLEANUP:
1455     if (username) free(username);
1456     if (parent) bdestroy(parent);
1457
1458     EC_EXIT;
1459 }
1460
1461 /********************************************************
1462  * Interface
1463  ********************************************************/
1464
1465 int afp_access(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1466 {
1467     int         ret;
1468     struct vol      *vol;
1469     struct dir      *dir;
1470     uint32_t            did, darwin_ace_rights;
1471     uint16_t        vid;
1472     struct path         *s_path;
1473     uuidp_t             uuid;
1474
1475     *rbuflen = 0;
1476     ibuf += 2;
1477
1478     memcpy(&vid, ibuf, sizeof( vid ));
1479     ibuf += sizeof(vid);
1480     if (NULL == ( vol = getvolbyvid( vid ))) {
1481         LOG(log_error, logtype_afpd, "afp_access: error getting volid:%d", vid);
1482         return AFPERR_NOOBJ;
1483     }
1484
1485     memcpy(&did, ibuf, sizeof( did ));
1486     ibuf += sizeof( did );
1487     if (NULL == ( dir = dirlookup( vol, did ))) {
1488         LOG(log_error, logtype_afpd, "afp_access: error getting did:%d", did);
1489         return afp_errno;
1490     }
1491
1492     /* Skip bitmap */
1493     ibuf += 2;
1494
1495     /* Store UUID address */
1496     uuid = (uuidp_t)ibuf;
1497     ibuf += UUID_BINSIZE;
1498
1499     /* Store ACE rights */
1500     memcpy(&darwin_ace_rights, ibuf, 4);
1501     darwin_ace_rights = ntohl(darwin_ace_rights);
1502     ibuf += 4;
1503
1504     /* get full path and handle file/dir subtleties in netatalk code*/
1505     if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
1506         LOG(log_error, logtype_afpd, "afp_getacl: cname got an error!");
1507         return AFPERR_NOOBJ;
1508     }
1509     if (!s_path->st_valid)
1510         of_statdir(vol, s_path);
1511     if ( s_path->st_errno != 0 ) {
1512         LOG(log_error, logtype_afpd, "afp_getacl: cant stat");
1513         return AFPERR_NOOBJ;
1514     }
1515
1516     ret = check_acl_access(obj, vol, dir, s_path->u_name, uuid, darwin_ace_rights);
1517
1518     return ret;
1519 }
1520
1521 int afp_getacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1522 {
1523     struct vol      *vol;
1524     struct dir      *dir;
1525     int         ret;
1526     uint32_t           did;
1527     uint16_t        vid, bitmap;
1528     struct path         *s_path;
1529     struct passwd       *pw;
1530     struct group        *gr;
1531
1532     LOG(log_debug9, logtype_afpd, "afp_getacl: BEGIN");
1533     *rbuflen = 0;
1534     ibuf += 2;
1535
1536     memcpy(&vid, ibuf, sizeof( vid ));
1537     ibuf += sizeof(vid);
1538     if (NULL == ( vol = getvolbyvid( vid ))) {
1539         LOG(log_error, logtype_afpd, "afp_getacl: error getting volid:%d", vid);
1540         return AFPERR_NOOBJ;
1541     }
1542
1543     memcpy(&did, ibuf, sizeof( did ));
1544     ibuf += sizeof( did );
1545     if (NULL == ( dir = dirlookup( vol, did ))) {
1546         LOG(log_error, logtype_afpd, "afp_getacl: error getting did:%d", did);
1547         return afp_errno;
1548     }
1549
1550     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1551     memcpy(rbuf, ibuf, sizeof( bitmap ));
1552     bitmap = ntohs( bitmap );
1553     ibuf += sizeof( bitmap );
1554     rbuf += sizeof( bitmap );
1555     *rbuflen += sizeof( bitmap );
1556
1557     /* skip maxreplysize */
1558     ibuf += 4;
1559
1560     /* get full path and handle file/dir subtleties in netatalk code*/
1561     if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
1562         LOG(log_error, logtype_afpd, "afp_getacl: cname got an error!");
1563         return AFPERR_NOOBJ;
1564     }
1565     if (!s_path->st_valid)
1566         of_statdir(vol, s_path);
1567     if ( s_path->st_errno != 0 ) {
1568         LOG(log_error, logtype_afpd, "afp_getacl: cant stat");
1569         return AFPERR_NOOBJ;
1570     }
1571
1572     /* Shall we return owner UUID ? */
1573     if (bitmap & kFileSec_UUID) {
1574         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner user UUID");
1575         if (NULL == (pw = getpwuid(s_path->st.st_uid))) {
1576             LOG(log_debug, logtype_afpd, "afp_getacl: local uid: %u", s_path->st.st_uid);
1577             localuuid_from_id(rbuf, UUID_USER, s_path->st.st_uid);
1578         } else {
1579             LOG(log_debug, logtype_afpd, "afp_getacl: got uid: %d, name: %s", s_path->st.st_uid, pw->pw_name);
1580             if ((ret = getuuidfromname(pw->pw_name, UUID_USER, rbuf)) != 0)
1581                 return AFPERR_MISC;
1582         }
1583         rbuf += UUID_BINSIZE;
1584         *rbuflen += UUID_BINSIZE;
1585     }
1586
1587     /* Shall we return group UUID ? */
1588     if (bitmap & kFileSec_GRPUUID) {
1589         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner group UUID");
1590         if (NULL == (gr = getgrgid(s_path->st.st_gid))) {
1591             LOG(log_debug, logtype_afpd, "afp_getacl: local gid: %u", s_path->st.st_gid);
1592             localuuid_from_id(rbuf, UUID_GROUP, s_path->st.st_gid);
1593         } else {
1594             LOG(log_debug, logtype_afpd, "afp_getacl: got gid: %d, name: %s", s_path->st.st_gid, gr->gr_name);
1595             if ((ret = getuuidfromname(gr->gr_name, UUID_GROUP, rbuf)) != 0)
1596                 return AFPERR_MISC;
1597         }
1598         rbuf += UUID_BINSIZE;
1599         *rbuflen += UUID_BINSIZE;
1600     }
1601
1602     /* Shall we return ACL ? */
1603     if (bitmap & kFileSec_ACL) {
1604         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files ACL");
1605         if (get_and_map_acl(s_path->u_name, rbuf, rbuflen) != 0) {
1606             LOG(log_error, logtype_afpd, "afp_getacl(\"%s/%s\"): mapping error",
1607                 getcwdpath(), s_path->u_name);
1608             return AFPERR_MISC;
1609         }
1610     }
1611
1612     LOG(log_debug9, logtype_afpd, "afp_getacl: END");
1613     return AFP_OK;
1614 }
1615
1616 int afp_setacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1617 {
1618     struct vol      *vol;
1619     struct dir      *dir;
1620     int         ret;
1621     uint32_t            did;
1622     uint16_t        vid, bitmap;
1623     struct path         *s_path;
1624
1625     LOG(log_debug9, logtype_afpd, "afp_setacl: BEGIN");
1626     *rbuflen = 0;
1627     ibuf += 2;
1628
1629     memcpy(&vid, ibuf, sizeof( vid ));
1630     ibuf += sizeof(vid);
1631     if (NULL == ( vol = getvolbyvid( vid ))) {
1632         LOG(log_error, logtype_afpd, "afp_setacl: error getting volid:%d", vid);
1633         return AFPERR_NOOBJ;
1634     }
1635
1636     memcpy(&did, ibuf, sizeof( did ));
1637     ibuf += sizeof( did );
1638     if (NULL == ( dir = dirlookup( vol, did ))) {
1639         LOG(log_error, logtype_afpd, "afp_setacl: error getting did:%d", did);
1640         return afp_errno;
1641     }
1642
1643     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1644     bitmap = ntohs( bitmap );
1645     ibuf += sizeof( bitmap );
1646
1647     /* get full path and handle file/dir subtleties in netatalk code*/
1648     if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
1649         LOG(log_error, logtype_afpd, "afp_setacl: cname got an error!");
1650         return AFPERR_NOOBJ;
1651     }
1652     if (!s_path->st_valid)
1653         of_statdir(vol, s_path);
1654     if ( s_path->st_errno != 0 ) {
1655         LOG(log_error, logtype_afpd, "afp_setacl: cant stat");
1656         return AFPERR_NOOBJ;
1657     }
1658     LOG(log_debug, logtype_afpd, "afp_setacl: unixname: %s", s_path->u_name);
1659
1660     /* Padding? */
1661     if ((unsigned long)ibuf & 1)
1662         ibuf++;
1663
1664     /* Start processing request */
1665
1666     /* Change owner: dont even try */
1667     if (bitmap & kFileSec_UUID) {
1668         LOG(log_note, logtype_afpd, "afp_setacl: change owner request, discarded");
1669         ret = AFPERR_ACCESS;
1670         ibuf += UUID_BINSIZE;
1671     }
1672
1673     /* Change group: certain changes might be allowed, so try it. FIXME: not implemented yet. */
1674     if (bitmap & kFileSec_UUID) {
1675         LOG(log_note, logtype_afpd, "afp_setacl: change group request, not supported");
1676         ret = AFPERR_PARAM;
1677         ibuf += UUID_BINSIZE;
1678     }
1679
1680     /* Remove ACL ? */
1681     if (bitmap & kFileSec_REMOVEACL) {
1682         LOG(log_debug, logtype_afpd, "afp_setacl: Remove ACL request.");
1683         if ((ret = remove_acl(vol, s_path->u_name, S_ISDIR(s_path->st.st_mode))) != AFP_OK)
1684             LOG(log_error, logtype_afpd, "afp_setacl: error from remove_acl");
1685     }
1686
1687     /* Change ACL ? */
1688     if (bitmap & kFileSec_ACL) {
1689         LOG(log_debug, logtype_afpd, "afp_setacl: Change ACL request.");
1690         /*  Get no of ACEs the client put on the wire */
1691         uint32_t ace_count;
1692         memcpy(&ace_count, ibuf, sizeof(uint32_t));
1693         ace_count = htonl(ace_count);
1694         ibuf += 8;      /* skip ACL flags (see acls.h) */
1695
1696         ret = set_acl(vol,
1697                       s_path->u_name,
1698                       (bitmap & kFileSec_Inherit),
1699                       (darwin_ace_t *)ibuf,
1700                       ace_count);
1701         if (ret == 0)
1702             ret = AFP_OK;
1703         else {
1704             LOG(log_warning, logtype_afpd, "afp_setacl(\"%s/%s\"): error",
1705                 getcwdpath(), s_path->u_name);
1706             ret = AFPERR_MISC;
1707         }
1708     }
1709
1710     LOG(log_debug9, logtype_afpd, "afp_setacl: END");
1711     return ret;
1712 }
1713
1714 /********************************************************************
1715  * ACL funcs interfacing with other parts
1716  ********************************************************************/
1717
1718 /*!
1719  * map ACL to user maccess
1720  *
1721  * This is the magic function that makes ACLs usable by calculating
1722  * the access granted by ACEs to the logged in user.
1723  */
1724 int acltoownermode(const AFPObj *obj, const struct vol *vol, char *path, struct stat *st, struct maccess *ma)
1725 {
1726     EC_INIT;
1727     uint32_t rights = 0;
1728
1729     if ( ! (obj->options.flags & OPTION_ACL2MACCESS)
1730          || ! (vol->v_flags & AFPVOL_ACLS))
1731          return 0;
1732
1733     LOG(log_maxdebug, logtype_afpd, "acltoownermode(\"%s/%s\", 0x%02x)",
1734         getcwdpath(), path, ma->ma_user);
1735
1736 #ifdef HAVE_SOLARIS_ACLS
1737     EC_ZERO_LOG(solaris_acl_rights(path, st, &rights));
1738
1739     LOG(log_maxdebug, logtype_afpd, "rights: 0x%08x", rights);
1740
1741     if (rights & DARWIN_ACE_READ_DATA)
1742         ma->ma_user |= AR_UREAD;
1743     if (rights & DARWIN_ACE_WRITE_DATA)
1744         ma->ma_user |= AR_UWRITE;
1745     if (rights & (DARWIN_ACE_EXECUTE | DARWIN_ACE_SEARCH))
1746         ma->ma_user |= AR_USEARCH;
1747 #endif
1748
1749 #ifdef HAVE_POSIX_ACLS
1750     EC_ZERO_LOG(posix_acls_to_uaperms(obj, path, st, ma));
1751 #endif
1752
1753     LOG(log_maxdebug, logtype_afpd, "resulting user maccess: 0x%02x group maccess: 0x%02x", ma->ma_user, ma->ma_group);
1754
1755 EC_CLEANUP:
1756     EC_EXIT;
1757 }