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