]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/acls.c
37128fd3b291de7458bcadeefacfef8b0d754ec2
[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_PRINT(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_PRINT(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_PRINT(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_PRINT(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_PRINT(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_PRINT(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_PRINT(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_PRINT(pwd = getpwnam(name));
312             nfsv4_aces->a_who = pwd->pw_uid;
313         } else { /* hopefully UUID_GROUP*/
314             EC_NULL_PRINT(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_PRINT(acl_create_entry(aclp, &e));
434
435          /* uid/gid */
436         EC_ZERO_PRINT(getnamefromuuid(darwin_aces->darwin_ace_uuid, &name, &uuidtype));
437         if (uuidtype == UUID_USER) {
438             EC_ZERO_PRINT(pwd = getpwnam(name));
439             tag = ACL_USER_OBJ;
440             id = pwd->pw_uid;
441         } else { /* hopefully UUID_GROUP*/
442             EC_ZERO_PRINT(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_PRINT(acl_set_tag_type(e, tag));
450         EC_ZERO_PRINT(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_PRINT(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_PRINT(acl_get_tag_type(e, &tag));
497
498         /* we return user and group ACE */
499         switch (tag) {
500         case ACL_USER:
501             EC_NULL_PRINT(uid = (uid_t *)acl_get_qualifier(e));
502             EC_NULL_PRINT(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_PRINT(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_PRINT(gid = (gid_t *)acl_get_qualifier(e));
512             EC_NULL_PRINT(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_PRINT(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_PRINT(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_PRINT(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     int ace_count = 0;
652     int mapped_aces = 0;
653     int err, dirflag;
654     uint32_t *darwin_ace_count = (u_int32_t *)rbuf;
655 #ifdef HAVE_SOLARIS_ACLS
656     ace_t *aces;
657 #endif
658 #ifdef HAVE_POSIX_ACLS
659     struct stat st;
660 #endif
661     LOG(log_debug9, logtype_afpd, "get_and_map_acl: BEGIN");
662
663     /* Skip length and flags */
664     rbuf += 4;
665     *rbuf = 0;
666     rbuf += 4;
667
668 #ifdef HAVE_SOLARIS_ACLS
669     if ( (ace_count = get_nfsv4_acl(name, &aces)) == -1) {
670         LOG(log_error, logtype_afpd, "get_and_map_acl: couldnt get ACL");
671         return -1;
672     }
673
674     if ( (mapped_aces = map_acl(SOLARIS_2_DARWIN, aces, (darwin_ace_t *)rbuf, ace_count)) == -1) {
675         err = -1;
676         goto cleanup;
677     }
678 #endif /* HAVE_SOLARIS_ACLS */
679
680 #ifdef HAVE_POSIX_ACLS
681     acl_t defacl = NULL , accacl = NULL;
682
683     /* stat to check if its a dir */
684     if (stat(name, &st) != 0) {
685         LOG(log_error, logtype_afpd, "get_and_map_acl: stat: %s", strerror(errno));
686         err = -1;
687         goto cleanup;
688     }
689
690     /* if its a dir, check for default acl too */
691     dirflag = 0;
692     if (S_ISDIR(st.st_mode)) {
693         dirflag = IS_DIR;
694         if ((defacl = acl_get_file(name, ACL_TYPE_DEFAULT)) == NULL) {
695             LOG(log_error, logtype_afpd, "get_and_map_acl: couldnt get default ACL");
696             err = -1;
697             goto cleanup;
698         }        
699         if ((mapped_aces = map_acl(POSIX_DEFAULT_2_DARWIN | dirflag,
700                                    defacl,
701                                    (darwin_ace_t *)rbuf,
702                                    0)) == -1) {
703             err = -1;
704             goto cleanup;
705         }
706     }
707
708     if ((accacl = acl_get_file(name, ACL_TYPE_ACCESS)) == NULL) {
709         LOG(log_error, logtype_afpd, "get_and_map_acl: couldnt get access ACL");
710         err = -1;
711         goto cleanup;
712     }
713
714     int tmp;
715     if ((tmp = map_acl(POSIX_ACCESS_2_DARWIN | dirflag,
716                       accacl,
717                       (darwin_ace_t *)(rbuf + mapped_aces * sizeof(darwin_ace_t)),
718                        0)) == -1) {
719         err = -1;
720         goto cleanup;
721     }
722     mapped_aces += tmp;
723 #endif /* HAVE_POSIX_ACLS */
724
725     LOG(log_debug, logtype_afpd, "get_and_map_acl: mapped %d ACEs", mapped_aces);
726
727     err = 0;
728     *darwin_ace_count = htonl(mapped_aces);
729     *rbuflen += sizeof(darwin_acl_header_t) + (mapped_aces * sizeof(darwin_ace_t));
730
731 cleanup:
732 #ifdef HAVE_SOLARIS_ACLS
733     free(aces);
734 #endif
735 #ifdef HAVE_POSIX_ACLS
736     if (defacl) acl_free(defacl);
737     if (accacl) acl_free(accacl);
738 #endif /* HAVE_POSIX_ACLS */
739
740     LOG(log_debug9, logtype_afpd, "get_and_map_acl: END");
741     return err;
742 }
743
744 /* Removes all non-trivial ACLs from object. Returns full AFPERR code. */
745 static int remove_acl(const struct vol *vol,const char *path, int dir)
746 {
747     int ret = AFP_OK;
748
749 #ifdef HAVE_SOLARIS_ACLS
750     /* Ressource etc. first */
751     if ((ret = vol->vfs->vfs_remove_acl(vol, path, dir)) != AFP_OK)
752         return ret;
753     /* now the data fork or dir */
754     ret = remove_acl_vfs(path);
755 #endif
756     return ret;
757 }
758
759 /*
760   Set ACL. Subtleties:
761   - the client sends a complete list of ACEs, not only new ones. So we dont need to do
762   any combination business (one exception being 'kFileSec_Inherit': see next)
763   - client might request that we add inherited ACEs via 'kFileSec_Inherit'.
764   We will store inherited ACEs first, which is Darwins canonical order.
765   - returns AFPerror code
766 */
767 #ifdef HAVE_SOLARIS_ACLS
768 static int set_acl(const struct vol *vol, char *name, int inherit, char *ibuf)
769 {
770     int ret, i, nfsv4_ace_count;
771     int tocopy_aces_count = 0, new_aces_count = 0, trivial_ace_count = 0;
772     ace_t *old_aces, *new_aces = NULL;
773     uint16_t flags;
774     uint32_t ace_count;
775
776     LOG(log_debug9, logtype_afpd, "set_acl: BEGIN");
777
778      /*  Get no of ACEs the client put on the wire */
779     ace_count = htonl(*((uint32_t *)ibuf));
780     ibuf += 8;      /* skip ACL flags (see acls.h) */
781
782     if (inherit)
783         /* inherited + trivial ACEs */
784         flags = ACE_INHERITED_ACE | ACE_OWNER | ACE_GROUP | ACE_EVERYONE;
785     else
786         /* only trivial ACEs */
787         flags = ACE_OWNER | ACE_GROUP | ACE_EVERYONE;
788
789     /* Get existing ACL and count ACEs which have to be copied */
790     if ((nfsv4_ace_count = get_nfsv4_acl(name, &old_aces)) == -1)
791         return AFPERR_MISC;
792     for ( i=0; i < nfsv4_ace_count; i++) {
793         if (old_aces[i].a_flags & flags)
794             tocopy_aces_count++;
795     }
796
797     /* Now malloc buffer exactly sized to fit all new ACEs */
798     new_aces = malloc( (ace_count + tocopy_aces_count) * sizeof(ace_t) );
799     if (new_aces == NULL) {
800         LOG(log_error, logtype_afpd, "set_acl: malloc %s", strerror(errno));
801         ret = AFPERR_MISC;
802         goto cleanup;
803     }
804
805     /* Start building new ACL */
806
807     /* Copy local inherited ACEs. Therefore we have 'Darwin canonical order' (see chmod there):
808        inherited ACEs first. */
809     if (inherit) {
810         for (i=0; i < nfsv4_ace_count; i++) {
811             if (old_aces[i].a_flags & ACE_INHERITED_ACE) {
812                 memcpy(&new_aces[new_aces_count], &old_aces[i], sizeof(ace_t));
813                 new_aces_count++;
814             }
815         }
816     }
817     LOG(log_debug7, logtype_afpd, "set_acl: copied %d inherited ACEs", new_aces_count);
818
819     /* Now the ACEs from the client */
820     ret = map_acl(DARWIN_2_SOLARIS, &new_aces[new_aces_count], (darwin_ace_t *)ibuf, ace_count);
821     if (ret == -1) {
822         ret = AFPERR_PARAM;
823         goto cleanup;
824     }
825     new_aces_count += ace_count;
826     LOG(log_debug7, logtype_afpd, "set_acl: mapped %d ACEs from client", ace_count);
827
828     /* Now copy the trivial ACEs */
829     for (i=0; i < nfsv4_ace_count; i++) {
830         if (old_aces[i].a_flags  & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)) {
831             memcpy(&new_aces[new_aces_count], &old_aces[i], sizeof(ace_t));
832             new_aces_count++;
833             trivial_ace_count++;
834         }
835     }
836     LOG(log_debug7, logtype_afpd, "set_acl: copied %d trivial ACEs", trivial_ace_count);
837
838     /* Ressourcefork first.
839        Note: for dirs we set the same ACL on the .AppleDouble/.Parent _file_. This
840        might be strange for ACE_DELETE_CHILD and for inheritance flags. */
841     if ( (ret = vol->vfs->vfs_acl(vol, name, ACE_SETACL, new_aces_count, new_aces)) != 0) {
842         LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
843         if (errno == (EACCES | EPERM))
844             ret = AFPERR_ACCESS;
845         else if (errno == ENOENT)
846             ret = AFPERR_NOITEM;
847         else
848             ret = AFPERR_MISC;
849         goto cleanup;
850     }
851     if ( (ret = acl(name, ACE_SETACL, new_aces_count, new_aces)) != 0) {
852         LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
853         if (errno == (EACCES | EPERM))
854             ret = AFPERR_ACCESS;
855         else if (errno == ENOENT)
856             ret = AFPERR_NOITEM;
857         else
858             ret = AFPERR_MISC;
859         goto cleanup;
860     }
861
862     ret = AFP_OK;
863
864 cleanup:
865     free(old_aces);
866     free(new_aces);
867
868     LOG(log_debug9, logtype_afpd, "set_acl: END");
869     return ret;
870 }
871 #endif /* HAVE_SOLARIS_ACLS */
872
873 #ifdef HAVE_POSIX_ACLS
874 static int set_acl(const struct vol *vol, char *name, int inherit, char *ibuf)
875 {
876     int ret = AFP_OK;
877
878     LOG(log_maxdebug, logtype_afpd, "set_acl: BEGIN");
879
880     /*  Get no of ACEs the client put on the wire */
881     uint32_t ace_count = htonl(*((uint32_t *)ibuf));
882     ibuf += 8;      /* skip ACL flags (see acls.h) */
883
884     acl_t acl = NULL;
885     if (inherit) {
886         /* get default ACEs */
887         struct stat st;
888         if (stat(name, &st) != 0) {
889             LOG(log_error, logtype_afpd, "set_acl: stat: %s", strerror(errno));
890             ret = AFPERR_NOOBJ;
891             goto exit;
892         }
893         if (S_ISDIR(st.st_mode)) {
894             if ((acl = acl_get_file(name, ACL_TYPE_DEFAULT)) == NULL) {
895                 LOG(log_error, logtype_afpd, "set_acl: acl_get_file error: %s",
896                     strerror(errno));
897                 ret = AFPERR_MISC;
898                 goto exit;
899             }
900         }
901     }
902
903     /*  */
904     if (acl == NULL && (acl = acl_init(0)) == NULL) {
905         LOG(log_error, logtype_afpd, "set_acl: acl_get_file error: %s",
906             strerror(errno));
907         ret = AFPERR_MISC;
908         goto exit;
909     }
910     /* acl now can take the additional ACEs as requested by the client */
911
912     while (ace_count) {
913         
914     }
915
916 exit:
917     if (acl)
918         acl_free(acl);
919     LOG(log_maxdebug, logtype_afpd, "set_acl: END: %u", ret);
920     return ret;
921 }
922 #endif /* HAVE_POSIX_ACLS */
923
924 /*
925   Checks if a given UUID has requested_rights(type darwin_ace_rights) for path.
926   Note: this gets called frequently and is a good place for optimizations !
927 */
928 #ifdef HAVE_SOLARIS_ACLS
929 static int check_acl_access(const char *path, const uuidp_t uuid, uint32_t requested_darwin_rights)
930 {
931     int                 ret, i, ace_count, dir, checkgroup;
932     char                *username = NULL; /* might be group too */
933     uuidtype_t          uuidtype;
934     uid_t               uid;
935     gid_t               pgid;
936     uint32_t            requested_rights = 0, allowed_rights = 0, denied_rights = 0;
937     ace_t               *aces;
938     struct passwd       *pwd;
939     struct stat         st;
940     int                 check_user_trivace = 0, check_group_trivace = 0;
941     uid_t               who;
942     uint16_t            flags;
943     uint16_t            type;
944     uint32_t            rights;
945
946 #ifdef DEBUG
947     LOG(log_debug9, logtype_afpd, "check_access: BEGIN. Request: %08x", requested_darwin_rights);
948 #endif
949     /* Get uid or gid from UUID */
950     if ( (getnamefromuuid(uuid, &username, &uuidtype)) != 0) {
951         LOG(log_error, logtype_afpd, "check_access: error getting name from UUID");
952         return AFPERR_PARAM;
953     }
954
955     /* File or dir */
956     if ((lstat(path, &st)) != 0) {
957         LOG(log_error, logtype_afpd, "check_access: stat: %s", strerror(errno));
958         ret = AFPERR_PARAM;
959         goto exit;
960     }
961     dir = S_ISDIR(st.st_mode);
962
963     if (uuidtype == UUID_USER) {
964         pwd = getpwnam(username);
965         if (!pwd) {
966             LOG(log_error, logtype_afpd, "check_access: getpwnam: %s", strerror(errno));
967             ret = AFPERR_MISC;
968             goto exit;
969         }
970         uid = pwd->pw_uid;
971         pgid = pwd->pw_gid;
972
973         /* If user is file/dir owner we must check the user trivial ACE */
974         if (uid == st.st_uid) {
975             LOG(log_debug, logtype_afpd, "check_access: user: %s is files owner. Must check trivial user ACE", username);
976             check_user_trivace = 1;
977         }
978
979         /* Now check if requested user is files owning group. If yes we must check the group trivial ACE */
980         if ( (check_group(username, uid, pgid, st.st_gid)) == 0) {
981             LOG(log_debug, logtype_afpd, "check_access: user: %s is in group: %d. Must check trivial group ACE", username, st.st_gid);
982             check_group_trivace = 1;
983         }
984     } else { /* hopefully UUID_GROUP*/
985         LOG(log_error, logtype_afpd, "check_access: afp_access for UUID of groups not supported!");
986 #if 0
987         grp = getgrnam(username);
988         if (!grp) {
989             LOG(log_error, logtype_afpd, "check_access: getgrnam: %s", strerror(errno));
990             return -1;
991         }
992         if (st.st_gid == grp->gr_gid )
993             check_group_trivace = 1;
994 #endif
995     }
996
997     /* Map requested rights to Solaris style. */
998     for (i=0; darwin_to_nfsv4_rights[i].from != 0; i++) {
999         if (requested_darwin_rights & darwin_to_nfsv4_rights[i].from)
1000             requested_rights |= darwin_to_nfsv4_rights[i].to;
1001     }
1002
1003     /* Get ACL from file/dir */
1004     if ( (ace_count = get_nfsv4_acl(path, &aces)) == -1) {
1005         LOG(log_error, logtype_afpd, "check_access: error getting ACEs");
1006         ret = AFPERR_MISC;
1007         goto exit;
1008     }
1009     if (ace_count == 0) {
1010         LOG(log_debug, logtype_afpd, "check_access: 0 ACEs from get_nfsv4_acl");
1011         ret = AFPERR_MISC;
1012         goto exit;
1013     }
1014
1015     /* Now check requested rights */
1016     ret = AFPERR_ACCESS;
1017     i = 0;
1018     do { /* Loop through ACEs */
1019         who = aces[i].a_who;
1020         flags = aces[i].a_flags;
1021         type = aces[i].a_type;
1022         rights = aces[i].a_access_mask;
1023
1024         if (flags & ACE_INHERIT_ONLY_ACE)
1025             continue;
1026
1027         /* Check if its a group ACE and set checkgroup to 1 if yes */
1028         checkgroup = 0;
1029         if ( (flags & ACE_IDENTIFIER_GROUP) && !(flags & ACE_GROUP) ) {
1030             if ( (check_group(username, uid, pgid, who)) == 0)
1031                 checkgroup = 1;
1032             else
1033                 continue;
1034         }
1035
1036         /* Now the tricky part: decide if ACE effects our user. I'll explain:
1037            if its a dedicated (non trivial) ACE for the user
1038            OR
1039            if its a ACE for a group we're member of
1040            OR
1041            if its a trivial ACE_OWNER ACE and requested UUID is the owner
1042            OR
1043            if its a trivial ACE_GROUP ACE and requested UUID is group
1044            OR
1045            if its a trivial ACE_EVERYONE ACE
1046            THEN
1047            process ACE */
1048         if (
1049             ( (who == uid) && !(flags & (ACE_TRIVIAL|ACE_IDENTIFIER_GROUP)) ) ||
1050             (checkgroup) ||
1051             ( (flags & ACE_OWNER) && check_user_trivace ) ||
1052             ( (flags & ACE_GROUP) && check_group_trivace ) ||
1053             ( flags & ACE_EVERYONE )
1054             ) {
1055             /* Found an applicable ACE */
1056             if (type == ACE_ACCESS_ALLOWED_ACE_TYPE)
1057                 allowed_rights |= rights;
1058             else if (type == ACE_ACCESS_DENIED_ACE_TYPE)
1059                 /* Only or to denied rights if not previously allowed !! */
1060                 denied_rights |= ((!allowed_rights) & rights);
1061         }
1062     } while (++i < ace_count);
1063
1064
1065     /* Darwin likes to ask for "delete_child" on dir,
1066        "write_data" is actually the same, so we add that for dirs */
1067     if (dir && (allowed_rights & ACE_WRITE_DATA))
1068         allowed_rights |= ACE_DELETE_CHILD;
1069
1070     if (requested_rights & denied_rights) {
1071         LOG(log_debug, logtype_afpd, "check_access: some requested right was denied:");
1072         ret = AFPERR_ACCESS;
1073     } else if ((requested_rights & allowed_rights) != requested_rights) {
1074         LOG(log_debug, logtype_afpd, "check_access: some requested right wasn't allowed:");
1075         ret = AFPERR_ACCESS;
1076     } else {
1077         LOG(log_debug, logtype_afpd, "check_access: all requested rights are allowed:");
1078         ret = AFP_OK;
1079     }
1080
1081     LOG(log_debug, logtype_afpd, "check_access: Requested rights: %08x, allowed_rights: %08x, denied_rights: %08x, Result: %d",
1082         requested_rights, allowed_rights, denied_rights, ret);
1083
1084 exit:
1085     free(aces);
1086     free(username);
1087 #ifdef DEBUG
1088     LOG(log_debug9, logtype_afpd, "check_access: END");
1089 #endif
1090     return ret;
1091 }
1092 #endif /* HAVE_SOLARIS_ACLS */
1093
1094 #ifdef HAVE_POSIX_ACLS
1095 static int check_acl_access(const char *path, const uuidp_t uuid, uint32_t requested_darwin_rights)
1096 {
1097     /*
1098      * FIXME: for OS X >= 10.6 it seems fp_access isn't called anymore, instead
1099      * the client just tries to perform any action, relying on the server
1100      * to enforce permission (which the OS does for us), returning appropiate
1101      * error codes in case the action failed.
1102      * So to summarize: I think it's safe to not implement this function and
1103      * just always return AFP_OK.
1104      */
1105     return AFP_OK;
1106 }
1107 #endif /* HAVE_POSIX_ACLS */
1108
1109 /********************************************************
1110  * Interface
1111  ********************************************************/
1112
1113 int afp_access(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1114 {
1115     int         ret;
1116     struct vol      *vol;
1117     struct dir      *dir;
1118     uint32_t            did, darwin_ace_rights;
1119     uint16_t        vid;
1120     struct path         *s_path;
1121     uuidp_t             uuid;
1122
1123     LOG(log_debug9, logtype_afpd, "afp_access: BEGIN");
1124
1125     *rbuflen = 0;
1126     ibuf += 2;
1127
1128     memcpy(&vid, ibuf, sizeof( vid ));
1129     ibuf += sizeof(vid);
1130     if (NULL == ( vol = getvolbyvid( vid ))) {
1131         LOG(log_error, logtype_afpd, "afp_access: error getting volid:%d", vid);
1132         return AFPERR_NOOBJ;
1133     }
1134
1135     memcpy(&did, ibuf, sizeof( did ));
1136     ibuf += sizeof( did );
1137     if (NULL == ( dir = dirlookup( vol, did ))) {
1138         LOG(log_error, logtype_afpd, "afp_access: error getting did:%d", did);
1139         return afp_errno;
1140     }
1141
1142     /* Skip bitmap */
1143     ibuf += 2;
1144
1145     /* Store UUID address */
1146     uuid = (uuidp_t)ibuf;
1147     ibuf += UUID_BINSIZE;
1148
1149     /* Store ACE rights */
1150     memcpy(&darwin_ace_rights, ibuf, 4);
1151     darwin_ace_rights = ntohl(darwin_ace_rights);
1152     ibuf += 4;
1153
1154     /* get full path and handle file/dir subtleties in netatalk code*/
1155     if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
1156         LOG(log_error, logtype_afpd, "afp_getacl: cname got an error!");
1157         return AFPERR_NOOBJ;
1158     }
1159     if (!s_path->st_valid)
1160         of_statdir(vol, s_path);
1161     if ( s_path->st_errno != 0 ) {
1162         LOG(log_error, logtype_afpd, "afp_getacl: cant stat");
1163         return AFPERR_NOOBJ;
1164     }
1165
1166     ret = check_acl_access(s_path->u_name, uuid, darwin_ace_rights);
1167
1168     LOG(log_debug9, logtype_afpd, "afp_access: END");
1169     return ret;
1170 }
1171
1172 int afp_getacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1173 {
1174     struct vol      *vol;
1175     struct dir      *dir;
1176     int         ret;
1177     uint32_t           did;
1178     uint16_t        vid, bitmap;
1179     struct path         *s_path;
1180     struct passwd       *pw;
1181     struct group        *gr;
1182
1183     LOG(log_debug9, logtype_afpd, "afp_getacl: BEGIN");
1184     *rbuflen = 0;
1185     ibuf += 2;
1186
1187     memcpy(&vid, ibuf, sizeof( vid ));
1188     ibuf += sizeof(vid);
1189     if (NULL == ( vol = getvolbyvid( vid ))) {
1190         LOG(log_error, logtype_afpd, "afp_getacl: error getting volid:%d", vid);
1191         return AFPERR_NOOBJ;
1192     }
1193
1194     memcpy(&did, ibuf, sizeof( did ));
1195     ibuf += sizeof( did );
1196     if (NULL == ( dir = dirlookup( vol, did ))) {
1197         LOG(log_error, logtype_afpd, "afp_getacl: error getting did:%d", did);
1198         return afp_errno;
1199     }
1200
1201     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1202     memcpy(rbuf, ibuf, sizeof( bitmap ));
1203     bitmap = ntohs( bitmap );
1204     ibuf += sizeof( bitmap );
1205     rbuf += sizeof( bitmap );
1206     *rbuflen += sizeof( bitmap );
1207
1208     /* skip maxreplysize */
1209     ibuf += 4;
1210
1211     /* get full path and handle file/dir subtleties in netatalk code*/
1212     if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
1213         LOG(log_error, logtype_afpd, "afp_getacl: cname got an error!");
1214         return AFPERR_NOOBJ;
1215     }
1216     if (!s_path->st_valid)
1217         of_statdir(vol, s_path);
1218     if ( s_path->st_errno != 0 ) {
1219         LOG(log_error, logtype_afpd, "afp_getacl: cant stat");
1220         return AFPERR_NOOBJ;
1221     }
1222
1223     /* Shall we return owner UUID ? */
1224     if (bitmap & kFileSec_UUID) {
1225         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner user UUID");
1226         if (NULL == (pw = getpwuid(s_path->st.st_uid)))
1227             return AFPERR_MISC;
1228         LOG(log_debug, logtype_afpd, "afp_getacl: got uid: %d, name: %s", s_path->st.st_uid, pw->pw_name);
1229         if ((ret = getuuidfromname(pw->pw_name, UUID_USER, rbuf)) != 0)
1230             return AFPERR_MISC;
1231         rbuf += UUID_BINSIZE;
1232         *rbuflen += UUID_BINSIZE;
1233     }
1234
1235     /* Shall we return group UUID ? */
1236     if (bitmap & kFileSec_GRPUUID) {
1237         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner group UUID");
1238         if (NULL == (gr = getgrgid(s_path->st.st_gid)))
1239             return AFPERR_MISC;
1240         LOG(log_debug, logtype_afpd, "afp_getacl: got gid: %d, name: %s", s_path->st.st_gid, gr->gr_name);
1241         if ((ret = getuuidfromname(gr->gr_name, UUID_GROUP, rbuf)) != 0)
1242             return AFPERR_MISC;
1243         rbuf += UUID_BINSIZE;
1244         *rbuflen += UUID_BINSIZE;
1245     }
1246
1247     /* Shall we return ACL ? */
1248     if (bitmap & kFileSec_ACL) {
1249         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files ACL");
1250         get_and_map_acl(s_path->u_name, rbuf, rbuflen);
1251     }
1252
1253     LOG(log_debug9, logtype_afpd, "afp_getacl: END");
1254     return AFP_OK;
1255 }
1256
1257 int afp_setacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1258 {
1259     struct vol      *vol;
1260     struct dir      *dir;
1261     int         ret;
1262     uint32_t            did;
1263     uint16_t        vid, bitmap;
1264     struct path         *s_path;
1265
1266     LOG(log_debug9, logtype_afpd, "afp_setacl: BEGIN");
1267     *rbuflen = 0;
1268     ibuf += 2;
1269
1270     memcpy(&vid, ibuf, sizeof( vid ));
1271     ibuf += sizeof(vid);
1272     if (NULL == ( vol = getvolbyvid( vid ))) {
1273         LOG(log_error, logtype_afpd, "afp_setacl: error getting volid:%d", vid);
1274         return AFPERR_NOOBJ;
1275     }
1276
1277     memcpy(&did, ibuf, sizeof( did ));
1278     ibuf += sizeof( did );
1279     if (NULL == ( dir = dirlookup( vol, did ))) {
1280         LOG(log_error, logtype_afpd, "afp_setacl: error getting did:%d", did);
1281         return afp_errno;
1282     }
1283
1284     memcpy(&bitmap, ibuf, sizeof( bitmap ));
1285     bitmap = ntohs( bitmap );
1286     ibuf += sizeof( bitmap );
1287
1288     /* get full path and handle file/dir subtleties in netatalk code*/
1289     if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
1290         LOG(log_error, logtype_afpd, "afp_setacl: cname got an error!");
1291         return AFPERR_NOOBJ;
1292     }
1293     if (!s_path->st_valid)
1294         of_statdir(vol, s_path);
1295     if ( s_path->st_errno != 0 ) {
1296         LOG(log_error, logtype_afpd, "afp_setacl: cant stat");
1297         return AFPERR_NOOBJ;
1298     }
1299     LOG(log_debug, logtype_afpd, "afp_setacl: unixname: %s", s_path->u_name);
1300
1301     /* Padding? */
1302     if ((unsigned long)ibuf & 1)
1303         ibuf++;
1304
1305     /* Start processing request */
1306
1307     /* Change owner: dont even try */
1308     if (bitmap & kFileSec_UUID) {
1309         LOG(log_debug, logtype_afpd, "afp_setacl: change owner request. discarded.");
1310         ret = AFPERR_ACCESS;
1311         ibuf += UUID_BINSIZE;
1312     }
1313
1314     /* Change group: certain changes might be allowed, so try it. FIXME: not implemented yet. */
1315     if (bitmap & kFileSec_UUID) {
1316         LOG(log_debug, logtype_afpd, "afp_setacl: change group request. not supported this time.");
1317         ret = AFPERR_PARAM;
1318         ibuf += UUID_BINSIZE;
1319     }
1320
1321     /* Remove ACL ? */
1322     if (bitmap & kFileSec_REMOVEACL) {
1323         LOG(log_debug, logtype_afpd, "afp_setacl: Remove ACL request.");
1324         if ((ret = remove_acl(vol, s_path->u_name, S_ISDIR(s_path->st.st_mode))) != AFP_OK)
1325             LOG(log_error, logtype_afpd, "afp_setacl: error from remove_acl");
1326     }
1327
1328     /* Change ACL ? */
1329     if (bitmap & kFileSec_ACL) {
1330         LOG(log_debug, logtype_afpd, "afp_setacl: Change ACL request.");
1331
1332         /* Check if its our job to preserve inherited ACEs */
1333         if (bitmap & kFileSec_Inherit)
1334             ret = set_acl(vol, s_path->u_name, 1, ibuf);
1335         else
1336             ret = set_acl(vol, s_path->u_name, 0, ibuf);
1337         if (ret == 0)
1338             ret = AFP_OK;
1339         else {
1340             LOG(log_warning, logtype_afpd, "afp_setacl(\"%s/%s\"): error",
1341                 getcwdpath(), s_path->u_name);
1342             ret = AFPERR_MISC;
1343         }
1344     }
1345
1346     LOG(log_debug9, logtype_afpd, "afp_setacl: END");
1347     return ret;
1348 }
1349
1350 /*
1351   unix.c/accessmode calls this: map ACL to OS 9 mode
1352 */
1353 void acltoownermode(char *path, struct stat *st, uid_t uid, struct maccess *ma)
1354 {
1355     struct passwd *pw;
1356     atalk_uuid_t uuid;
1357     int r_ok, w_ok, x_ok;
1358
1359     if ( ! (AFPobj->options.flags & OPTION_UUID) || ! (AFPobj->options.flags & OPTION_ACL2OS9MODE))
1360         return;
1361
1362     LOG(log_maxdebug, logtype_afpd, "acltoownermode('%s')", path);
1363
1364     if ((pw = getpwuid(uid)) == NULL) {
1365         LOG(log_error, logtype_afpd, "acltoownermode: %s", strerror(errno));
1366         return;
1367     }
1368
1369     /* We need the UUID for check_acl_access */
1370     if ((getuuidfromname(pw->pw_name, UUID_USER, uuid)) != 0)
1371         return;
1372
1373     /* These work for files and dirs */
1374     r_ok = check_acl_access(path, uuid, DARWIN_ACE_READ_DATA);
1375     w_ok = check_acl_access(path, uuid, (DARWIN_ACE_WRITE_DATA|DARWIN_ACE_APPEND_DATA));
1376     x_ok = check_acl_access(path, uuid, DARWIN_ACE_EXECUTE);
1377
1378     LOG(log_debug7, logtype_afpd, "acltoownermode: ma_user before: %04o",ma->ma_user);
1379     if (r_ok == 0)
1380         ma->ma_user |= AR_UREAD;
1381     if (w_ok == 0)
1382         ma->ma_user |= AR_UWRITE;
1383     if (x_ok == 0)
1384         ma->ma_user |= AR_USEARCH;
1385     LOG(log_debug7, logtype_afpd, "acltoownermode: ma_user after: %04o", ma->ma_user);
1386
1387     return;
1388 }
1389
1390 /*
1391   We're being called at the end of afp_createdir. We're (hopefully) inside dir
1392   and ".AppleDouble" and ".AppleDouble/.Parent" should have already been created.
1393   We then inherit any explicit ACE from "." to ".AppleDouble" and ".AppleDouble/.Parent".
1394   FIXME: add to VFS layer ?
1395 */
1396 #ifdef HAVE_SOLARIS_ACLS
1397 void addir_inherit_acl(const struct vol *vol)
1398 {
1399     ace_t *diraces = NULL, *adaces = NULL, *combinedaces = NULL;
1400     int diracecount, adacecount;
1401
1402     LOG(log_debug9, logtype_afpd, "addir_inherit_acl: BEGIN");
1403
1404     /* Check if ACLs are enabled for the volume */
1405     if (vol->v_flags & AFPVOL_ACLS) {
1406
1407         if ((diracecount = get_nfsv4_acl(".", &diraces)) <= 0)
1408             goto cleanup;
1409         /* Remove any trivial ACE from "." */
1410         if ((diracecount = strip_trivial_aces(&diraces, diracecount)) <= 0)
1411             goto cleanup;
1412
1413         /*
1414           Inherit to ".AppleDouble"
1415         */
1416
1417         if ((adacecount = get_nfsv4_acl(".AppleDouble", &adaces)) <= 0)
1418             goto cleanup;
1419         /* Remove any non-trivial ACE from ".AppleDouble" */
1420         if ((adacecount = strip_nontrivial_aces(&adaces, adacecount)) <= 0)
1421             goto cleanup;
1422
1423         /* Combine ACEs */
1424         if ((combinedaces = concat_aces(diraces, diracecount, adaces, adacecount)) == NULL)
1425             goto cleanup;
1426
1427         /* Now set new acl */
1428         if ((acl(".AppleDouble", ACE_SETACL, diracecount + adacecount, combinedaces)) != 0)
1429             LOG(log_error, logtype_afpd, "addir_inherit_acl: acl: %s", strerror(errno));
1430
1431         free(adaces);
1432         adaces = NULL;
1433         free(combinedaces);
1434         combinedaces = NULL;
1435
1436         /*
1437           Inherit to ".AppleDouble/.Parent"
1438         */
1439
1440         if ((adacecount = get_nfsv4_acl(".AppleDouble/.Parent", &adaces)) <= 0)
1441             goto cleanup;
1442         if ((adacecount = strip_nontrivial_aces(&adaces, adacecount)) <= 0)
1443             goto cleanup;
1444
1445         /* Combine ACEs */
1446         if ((combinedaces = concat_aces(diraces, diracecount, adaces, adacecount)) == NULL)
1447             goto cleanup;
1448
1449         /* Now set new acl */
1450         if ((acl(".AppleDouble/.Parent", ACE_SETACL, diracecount + adacecount, combinedaces)) != 0)
1451             LOG(log_error, logtype_afpd, "addir_inherit_acl: acl: %s", strerror(errno));
1452
1453
1454     }
1455
1456 cleanup:
1457     LOG(log_debug9, logtype_afpd, "addir_inherit_acl: END");
1458
1459     free(diraces);
1460     free(adaces);
1461     free(combinedaces);
1462 }
1463 #endif /* HAVE_SOLARIS_ACLS */
1464
1465 #ifdef HAVE_POSIX_ACLS
1466 void addir_inherit_acl(const struct vol *vol)
1467 {
1468     return;
1469 }
1470 #endif /* HAVE_POSIX_ACLS */