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