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