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