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