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