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