]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/acls.c
Solaric ACL/chmod compatibility changes for onnv145+
[netatalk.git] / etc / afpd / acls.c
1 /*
2   Copyright (c) 2008,2009 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 #include <sys/acl.h>
27
28 #include <atalk/adouble.h>
29 #include <atalk/vfs.h>
30 #include <atalk/afp.h>
31 #include <atalk/util.h>
32 #include <atalk/cnid.h>
33 #include <atalk/logger.h>
34 #include <atalk/uuid.h>
35 #include <atalk/acl.h>
36
37 #include "directory.h"
38 #include "desktop.h"
39 #include "volume.h"
40 #include "fork.h"
41
42 #include "acls.h"
43 #include "acl_mappings.h"
44
45 /* for map_acl() */
46 #define SOLARIS_2_DARWIN 1
47 #define DARWIN_2_SOLARIS 2
48
49 /********************************************************
50  * Basic and helper funcs
51  ********************************************************/
52
53 /*
54   Takes a users name, uid and primary gid and checks if user is member of any group
55   Returns -1 if no or error, 0 if yes
56 */
57 static int check_group(char *name, uid_t uid, gid_t pgid, gid_t path_gid)
58 {
59     int i;
60     struct group *grp;
61
62     if (pgid == path_gid)
63         return 0;
64
65     grp = getgrgid(path_gid);
66     if (!grp)
67         return -1;
68
69     i = 0;
70     while (grp->gr_mem[i] != NULL) {
71         if ( (strcmp(grp->gr_mem[i], name)) == 0 ) {
72             LOG(log_debug, logtype_afpd, "check_group: requested user:%s is member of: %s", name, grp->gr_name);
73             return 0;
74         }
75         i++;
76     }
77
78     return -1;
79 }
80
81 /*
82   Maps ACE array from Solaris to Darwin. Darwin ACEs are stored in network byte order.
83   Return numer of mapped ACEs or -1 on error.
84   All errors while mapping (e.g. getting UUIDs from LDAP) are fatal.
85 */
86 static int map_aces_solaris_to_darwin(ace_t *aces, darwin_ace_t *darwin_aces, int ace_count)
87 {
88     int i, count = 0;
89     uint32_t flags;
90     uint32_t rights;
91     struct passwd *pwd = NULL;
92     struct group *grp = NULL;
93
94     LOG(log_debug7, logtype_afpd, "map_aces_solaris_to_darwin: parsing %d ACES", ace_count);
95
96     while(ace_count--) {
97         LOG(log_debug7, logtype_afpd, "map_aces_solaris_to_darwin: parsing ACE No. %d", ace_count + 1);
98         /* if its a ACE resulting from nfsv4 mode mapping, discard it */
99         if (aces->a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)) {
100             LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: trivial ACE");
101             aces++;
102             continue;
103         }
104
105         if ( ! (aces->a_flags & ACE_IDENTIFIER_GROUP) ) {
106             /* its a user ace */
107             LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: found user ACE with uid: %d", aces->a_who);
108             pwd = getpwuid(aces->a_who);
109             if (!pwd) {
110                 LOG(log_error, logtype_afpd, "map_aces_solaris_to_darwin: getpwuid error: %s", strerror(errno));
111                 return -1;
112             }
113             LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: uid: %d -> name: %s", aces->a_who, pwd->pw_name);
114             if ( (getuuidfromname(pwd->pw_name, UUID_USER, darwin_aces->darwin_ace_uuid)) != 0) {
115                 LOG(log_error, logtype_afpd, "map_aces_solaris_to_darwin: getuuidfromname error");
116                 return -1;
117             }
118         } else {
119             /* its a group ace */
120             LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: found group ACE with gid: %d", aces->a_who);
121             grp = getgrgid(aces->a_who);
122             if (!grp) {
123                 LOG(log_error, logtype_afpd, "map_aces_solaris_to_darwin: getgrgid error: %s", strerror(errno));
124                 return -1;
125             }
126             LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: gid: %d -> name: %s", aces->a_who, grp->gr_name);
127             if ( (getuuidfromname(grp->gr_name, UUID_GROUP, darwin_aces->darwin_ace_uuid)) != 0) {
128                 LOG(log_error, logtype_afpd, "map_aces_solaris_to_darwin: getuuidfromname error");
129                 return -1;
130             }
131         }
132
133         /* map flags */
134         if (aces->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE)
135             flags = DARWIN_ACE_FLAGS_PERMIT;
136         else if (aces->a_type == ACE_ACCESS_DENIED_ACE_TYPE)
137             flags = DARWIN_ACE_FLAGS_DENY;
138         else {          /* unsupported type */
139             aces++;
140             continue;
141         }
142         for(i=0; nfsv4_to_darwin_flags[i].from != 0; i++) {
143             if (aces->a_flags & nfsv4_to_darwin_flags[i].from)
144                 flags |= nfsv4_to_darwin_flags[i].to;
145         }
146         darwin_aces->darwin_ace_flags = htonl(flags);
147
148         /* map rights */
149         rights = 0;
150         for (i=0; nfsv4_to_darwin_rights[i].from != 0; i++) {
151             if (aces->a_access_mask & nfsv4_to_darwin_rights[i].from)
152                 rights |= nfsv4_to_darwin_rights[i].to;
153         }
154         darwin_aces->darwin_ace_rights = htonl(rights);
155
156         count++;
157         aces++;
158         darwin_aces++;
159     }
160
161     return count;
162 }
163
164 /*
165   Maps ACE array from Darwin to Solaris. Darwin ACEs are expected in network byte order.
166   Return numer of mapped ACEs or -1 on error.
167   All errors while mapping (e.g. getting UUIDs from LDAP) are fatal.
168 */
169 int map_aces_darwin_to_solaris(darwin_ace_t *darwin_aces, ace_t *nfsv4_aces, int ace_count)
170 {
171     int i, mapped_aces = 0;
172     uint32_t darwin_ace_flags;
173     uint32_t darwin_ace_rights;
174     uint16_t nfsv4_ace_flags;
175     uint32_t nfsv4_ace_rights;
176     char *name;
177     uuidtype_t uuidtype;
178     struct passwd *pwd;
179     struct group *grp;
180
181     while(ace_count--) {
182         nfsv4_ace_flags = 0;
183         nfsv4_ace_rights = 0;
184
185         /* uid/gid first */
186         if ( (getnamefromuuid(darwin_aces->darwin_ace_uuid, &name, &uuidtype)) != 0)
187             return -1;
188         if (uuidtype == UUID_USER) {
189             pwd = getpwnam(name);
190             if (!pwd) {
191                 LOG(log_error, logtype_afpd, "map_aces_darwin_to_solaris: getpwnam: %s", strerror(errno));
192                 free(name);
193                 return -1;
194             }
195             nfsv4_aces->a_who = pwd->pw_uid;
196         } else { /* hopefully UUID_GROUP*/
197             grp = getgrnam(name);
198             if (!grp) {
199                 LOG(log_error, logtype_afpd, "map_aces_darwin_to_solaris: getgrnam: %s", strerror(errno));
200                 free(name);
201                 return -1;
202             }
203             nfsv4_aces->a_who = (uid_t)(grp->gr_gid);
204             nfsv4_ace_flags |= ACE_IDENTIFIER_GROUP;
205         }
206         free(name);
207         name = NULL;
208
209         /* now type: allow/deny */
210         darwin_ace_flags = ntohl(darwin_aces->darwin_ace_flags);
211         if (darwin_ace_flags & DARWIN_ACE_FLAGS_PERMIT)
212             nfsv4_aces->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
213         else if (darwin_ace_flags & DARWIN_ACE_FLAGS_DENY)
214             nfsv4_aces->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
215         else { /* unsupported type */
216             darwin_aces++;
217             continue;
218         }
219         /* map flags */
220         for(i=0; darwin_to_nfsv4_flags[i].from != 0; i++) {
221             if (darwin_ace_flags & darwin_to_nfsv4_flags[i].from)
222                 nfsv4_ace_flags |= darwin_to_nfsv4_flags[i].to;
223         }
224
225         /* map rights */
226         darwin_ace_rights = ntohl(darwin_aces->darwin_ace_rights);
227         for (i=0; darwin_to_nfsv4_rights[i].from != 0; i++) {
228             if (darwin_ace_rights & darwin_to_nfsv4_rights[i].from)
229                 nfsv4_ace_rights |= darwin_to_nfsv4_rights[i].to;
230         }
231
232         LOG(log_debug9, logtype_afpd, "map_aces_darwin_to_solaris: ACE flags: Darwin:%08x -> NFSv4:%04x", darwin_ace_flags, nfsv4_ace_flags);
233         LOG(log_debug9, logtype_afpd, "map_aces_darwin_to_solaris: ACE rights: Darwin:%08x -> NFSv4:%08x", darwin_ace_rights, nfsv4_ace_rights);
234
235         nfsv4_aces->a_flags = nfsv4_ace_flags;
236         nfsv4_aces->a_access_mask = nfsv4_ace_rights;
237
238         mapped_aces++;
239         darwin_aces++;
240         nfsv4_aces++;
241     }
242
243     return mapped_aces;
244 }
245
246 /********************************************************
247  * 2nd level funcs
248  ********************************************************/
249
250 /*  Map between ACL styles (SOLARIS_2_DARWIN, DARWIN_2_SOLARIS).
251     Reads from 'aces' buffer, writes to 'rbuf' buffer.
252     Caller must provide buffer.
253     Darwin ACEs are read and written in network byte order.
254     Needs to know how many ACEs are in the ACL (ace_count). Ignores trivial ACEs.
255     Return no of mapped ACEs or -1 on error. */
256 static int map_acl(int type, ace_t *nfsv4_aces, darwin_ace_t *buf, int ace_count)
257 {
258     int mapped_aces;
259
260     LOG(log_debug9, logtype_afpd, "map_acl: BEGIN");
261
262     switch (type) {
263     case SOLARIS_2_DARWIN:
264         mapped_aces = map_aces_solaris_to_darwin( nfsv4_aces, buf, ace_count);
265         break;
266
267     case DARWIN_2_SOLARIS:
268         mapped_aces = map_aces_darwin_to_solaris( buf, nfsv4_aces, ace_count);
269         break;
270
271     default:
272         mapped_aces = -1;
273         break;
274     }
275
276     LOG(log_debug9, logtype_afpd, "map_acl: END");
277     return mapped_aces;
278 }
279
280 /********************************************************
281  * 1st level funcs
282  ********************************************************/
283
284
285 /* Get ACL from object omitting trivial ACEs. Map to Darwin ACL style and
286    store Darwin ACL at rbuf. Add length of ACL written to rbuf to *rbuflen.
287    Returns 0 on success, -1 on error. */
288 static int get_and_map_acl(char *name, char *rbuf, size_t *rbuflen)
289 {
290     int ace_count, mapped_aces, err;
291     ace_t *aces;
292     uint32_t *darwin_ace_count = (u_int32_t *)rbuf;
293
294     LOG(log_debug9, logtype_afpd, "get_and_map_acl: BEGIN");
295
296     /* Skip length and flags */
297     rbuf += 4;
298     *rbuf = 0;
299     rbuf += 4;
300
301     if ( (ace_count = get_nfsv4_acl(name, &aces)) == -1) {
302         LOG(log_error, logtype_afpd, "get_and_map_acl: couldnt get ACL");
303         return -1;
304     }
305
306     if ( (mapped_aces = map_acl(SOLARIS_2_DARWIN, aces, (darwin_ace_t *)rbuf, ace_count)) == -1) {
307         err = -1;
308         goto cleanup;
309     }
310     LOG(log_debug, logtype_afpd, "get_and_map_acl: mapped %d ACEs", mapped_aces);
311
312     err = 0;
313     *darwin_ace_count = htonl(mapped_aces);
314     *rbuflen += sizeof(darwin_acl_header_t) + (mapped_aces * sizeof(darwin_ace_t));
315
316 cleanup:
317     free(aces);
318
319     LOG(log_debug9, logtype_afpd, "get_and_map_acl: END");
320     return err;
321 }
322
323 /* Removes all non-trivial ACLs from object. Returns full AFPERR code. */
324 static int remove_acl_vfs(const struct vol *vol,const char *path, int dir)
325 {
326     int ret;
327
328     /* Ressource etc. first */
329     if ((ret = vol->vfs->vfs_remove_acl(vol, path, dir)) != AFP_OK)
330         return ret;
331     /* now the data fork or dir */
332     return (remove_acl(path));
333 }
334
335 /*
336   Set ACL. Subtleties:
337   - the client sends a complete list of ACEs, not only new ones. So we dont need to do
338   any combination business (one exception being 'kFileSec_Inherit': see next)
339   - client might request that we add inherited ACEs via 'kFileSec_Inherit'.
340   We will store inherited ACEs first, which is Darwins canonical order.
341   - returns AFPerror code
342 */
343 static int set_acl_vfs(const struct vol *vol, char *name, int inherit, char *ibuf)
344 {
345     int ret, i, nfsv4_ace_count, tocopy_aces_count = 0, new_aces_count = 0, trivial_ace_count = 0;
346     ace_t *old_aces, *new_aces = NULL;
347     uint16_t flags;
348     uint32_t ace_count;
349
350     LOG(log_debug9, logtype_afpd, "set_acl: BEGIN");
351
352     /*  Get no of ACEs the client put on the wire */
353     ace_count = htonl(*((uint32_t *)ibuf));
354     ibuf += 8;      /* skip ACL flags (see acls.h) */
355
356     if (inherit)
357         /* inherited + trivial ACEs */
358         flags = ACE_INHERITED_ACE | ACE_OWNER | ACE_GROUP | ACE_EVERYONE;
359     else
360         /* only trivial ACEs */
361         flags = ACE_OWNER | ACE_GROUP | ACE_EVERYONE;
362
363     /* Get existing ACL and count ACEs which have to be copied */
364     if ((nfsv4_ace_count = get_nfsv4_acl(name, &old_aces)) == -1)
365         return AFPERR_MISC;
366     for ( i=0; i < nfsv4_ace_count; i++) {
367         if (old_aces[i].a_flags & flags)
368             tocopy_aces_count++;
369     }
370
371     /* Now malloc buffer exactly sized to fit all new ACEs */
372     new_aces = malloc( (ace_count + tocopy_aces_count) * sizeof(ace_t) );
373     if (new_aces == NULL) {
374         LOG(log_error, logtype_afpd, "set_acl: malloc %s", strerror(errno));
375         ret = AFPERR_MISC;
376         goto cleanup;
377     }
378
379     /* Start building new ACL */
380
381     /* Copy local inherited ACEs. Therefore we have 'Darwin canonical order' (see chmod there):
382        inherited ACEs first. */
383     if (inherit) {
384         for (i=0; i < nfsv4_ace_count; i++) {
385             if (old_aces[i].a_flags & ACE_INHERITED_ACE) {
386                 memcpy(&new_aces[new_aces_count], &old_aces[i], sizeof(ace_t));
387                 new_aces_count++;
388             }
389         }
390     }
391     LOG(log_debug7, logtype_afpd, "set_acl: copied %d inherited ACEs", new_aces_count);
392
393     /* Now the ACEs from the client */
394     ret = map_acl(DARWIN_2_SOLARIS, &new_aces[new_aces_count], (darwin_ace_t *)ibuf, ace_count);
395     if (ret == -1) {
396         ret = AFPERR_PARAM;
397         goto cleanup;
398     }
399     new_aces_count += ace_count;
400     LOG(log_debug7, logtype_afpd, "set_acl: mapped %d ACEs from client", ace_count);
401
402     /* Now copy the trivial ACEs */
403     for (i=0; i < nfsv4_ace_count; i++) {
404         if (old_aces[i].a_flags  & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)) {
405             memcpy(&new_aces[new_aces_count], &old_aces[i], sizeof(ace_t));
406             new_aces_count++;
407             trivial_ace_count++;
408         }
409     }
410     LOG(log_debug7, logtype_afpd, "set_acl: copied %d trivial ACEs", trivial_ace_count);
411
412     /* Ressourcefork first.
413        Note: for dirs we set the same ACL on the .AppleDouble/.Parent _file_. This
414        might be strange for ACE_DELETE_CHILD and for inheritance flags. */
415     if ( (ret = vol->vfs->vfs_acl(vol, name, ACE_SETACL, new_aces_count, new_aces)) != 0) {
416         LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
417         if (errno == (EACCES | EPERM))
418             ret = AFPERR_ACCESS;
419         else if (errno == ENOENT)
420             ret = AFPERR_NOITEM;
421         else
422             ret = AFPERR_MISC;
423         goto cleanup;
424     }
425     if ( (ret = acl(name, ACE_SETACL, new_aces_count, new_aces)) != 0) {
426         LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
427         if (errno == (EACCES | EPERM))
428             ret = AFPERR_ACCESS;
429         else if (errno == ENOENT)
430             ret = AFPERR_NOITEM;
431         else
432             ret = AFPERR_MISC;
433         goto cleanup;
434     }
435
436     ret = AFP_OK;
437
438 cleanup:
439     free(old_aces);
440     free(new_aces);
441
442     LOG(log_debug9, logtype_afpd, "set_acl: END");
443     return ret;
444 }
445
446 /*
447   Checks if a given UUID has requested_rights(type darwin_ace_rights) for path.
448   Note: this gets called frequently and is a good place for optimizations !
449 */
450 static int check_acl_access(const char *path, const uuidp_t uuid, uint32_t requested_darwin_rights)
451 {
452     int                 ret, i, ace_count, dir, checkgroup;
453     char                *username = NULL; /* might be group too */
454     uuidtype_t          uuidtype;
455     uid_t               uid;
456     gid_t               pgid;
457     uint32_t            requested_rights = 0, allowed_rights = 0, denied_rights = 0;
458     ace_t               *aces;
459     struct passwd       *pwd;
460     struct stat         st;
461     int                 check_user_trivace = 0, check_group_trivace = 0;
462     uid_t               who;
463     uint16_t            flags;
464     uint16_t            type;
465     uint32_t            rights;
466
467 #ifdef DEBUG
468     LOG(log_debug9, logtype_afpd, "check_access: BEGIN. Request: %08x", requested_darwin_rights);
469 #endif
470     /* Get uid or gid from UUID */
471     if ( (getnamefromuuid(uuid, &username, &uuidtype)) != 0) {
472         LOG(log_error, logtype_afpd, "check_access: error getting name from UUID");
473         return AFPERR_PARAM;
474     }
475
476     /* File or dir */
477     if ((lstat(path, &st)) != 0) {
478         LOG(log_error, logtype_afpd, "check_access: stat: %s", strerror(errno));
479         ret = AFPERR_PARAM;
480         goto exit;
481     }
482     dir = S_ISDIR(st.st_mode);
483
484     if (uuidtype == UUID_USER) {
485         pwd = getpwnam(username);
486         if (!pwd) {
487             LOG(log_error, logtype_afpd, "check_access: getpwnam: %s", strerror(errno));
488             ret = AFPERR_MISC;
489             goto exit;
490         }
491         uid = pwd->pw_uid;
492         pgid = pwd->pw_gid;
493
494         /* If user is file/dir owner we must check the user trivial ACE */
495         if (uid == st.st_uid) {
496             LOG(log_debug, logtype_afpd, "check_access: user: %s is files owner. Must check trivial user ACE", username);
497             check_user_trivace = 1;
498         }
499
500         /* Now check if requested user is files owning group. If yes we must check the group trivial ACE */
501         if ( (check_group(username, uid, pgid, st.st_gid)) == 0) {
502             LOG(log_debug, logtype_afpd, "check_access: user: %s is in group: %d. Must check trivial group ACE", username, st.st_gid);
503             check_group_trivace = 1;
504         }
505     } else { /* hopefully UUID_GROUP*/
506         LOG(log_error, logtype_afpd, "check_access: afp_access for UUID of groups not supported!");
507 #if 0
508         grp = getgrnam(username);
509         if (!grp) {
510             LOG(log_error, logtype_afpd, "check_access: getgrnam: %s", strerror(errno));
511             return -1;
512         }
513         if (st.st_gid == grp->gr_gid )
514             check_group_trivace = 1;
515 #endif
516     }
517
518     /* Map requested rights to Solaris style. */
519     for (i=0; darwin_to_nfsv4_rights[i].from != 0; i++) {
520         if (requested_darwin_rights & darwin_to_nfsv4_rights[i].from)
521             requested_rights |= darwin_to_nfsv4_rights[i].to;
522     }
523
524     /* Get ACL from file/dir */
525     if ( (ace_count = get_nfsv4_acl(path, &aces)) == -1) {
526         LOG(log_error, logtype_afpd, "check_access: error getting ACEs");
527         ret = AFPERR_MISC;
528         goto exit;
529     }
530     if (ace_count == 0) {
531         LOG(log_debug, logtype_afpd, "check_access: 0 ACEs from get_nfsv4_acl");
532         ret = AFPERR_MISC;
533         goto exit;
534     }
535
536     /* Now check requested rights */
537     ret = AFPERR_ACCESS;
538     i = 0;
539     do { /* Loop through ACEs */
540         who = aces[i].a_who;
541         flags = aces[i].a_flags;
542         type = aces[i].a_type;
543         rights = aces[i].a_access_mask;
544
545         if (flags & ACE_INHERIT_ONLY_ACE)
546             continue;
547
548         /* Check if its a group ACE and set checkgroup to 1 if yes */
549         checkgroup = 0;
550         if ( (flags & ACE_IDENTIFIER_GROUP) && !(flags & ACE_GROUP) ) {
551             if ( (check_group(username, uid, pgid, who)) == 0)
552                 checkgroup = 1;
553             else
554                 continue;
555         }
556
557         /* Now the tricky part: decide if ACE effects our user. I'll explain:
558            if its a dedicated (non trivial) ACE for the user
559            OR
560            if its a ACE for a group we're member of
561            OR
562            if its a trivial ACE_OWNER ACE and requested UUID is the owner
563            OR
564            if its a trivial ACE_GROUP ACE and requested UUID is group
565            OR
566            if its a trivial ACE_EVERYONE ACE
567            THEN
568            process ACE */
569         if (
570             ( (who == uid) && !(flags & (ACE_TRIVIAL|ACE_IDENTIFIER_GROUP)) ) ||
571             (checkgroup) ||
572             ( (flags & ACE_OWNER) && check_user_trivace ) ||
573             ( (flags & ACE_GROUP) && check_group_trivace ) ||
574             ( flags & ACE_EVERYONE )
575             ) {
576             /* Found an applicable ACE */
577             if (type == ACE_ACCESS_ALLOWED_ACE_TYPE)
578                 allowed_rights |= rights;
579             else if (type == ACE_ACCESS_DENIED_ACE_TYPE)
580                 /* Only or to denied rights if not previously allowed !! */
581                 denied_rights |= ((!allowed_rights) & rights);
582         }
583     } while (++i < ace_count);
584
585
586     /* Darwin likes to ask for "delete_child" on dir,
587        "write_data" is actually the same, so we add that for dirs */
588     if (dir && (allowed_rights & ACE_WRITE_DATA))
589         allowed_rights |= ACE_DELETE_CHILD;
590
591     if (requested_rights & denied_rights) {
592         LOG(log_debug, logtype_afpd, "check_access: some requested right was denied:");
593         ret = AFPERR_ACCESS;
594     } else if ((requested_rights & allowed_rights) != requested_rights) {
595         LOG(log_debug, logtype_afpd, "check_access: some requested right wasn't allowed:");
596         ret = AFPERR_ACCESS;
597     } else {
598         LOG(log_debug, logtype_afpd, "check_access: all requested rights are allowed:");
599         ret = AFP_OK;
600     }
601
602     LOG(log_debug, logtype_afpd, "check_access: Requested rights: %08x, allowed_rights: %08x, denied_rights: %08x, Result: %d",
603         requested_rights, allowed_rights, denied_rights, ret);
604
605 exit:
606     free(aces);
607     free(username);
608 #ifdef DEBUG
609     LOG(log_debug9, logtype_afpd, "check_access: END");
610 #endif
611     return ret;
612 }
613
614 /********************************************************
615  * Interface
616  ********************************************************/
617
618 int afp_access(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
619 {
620     int         ret;
621     struct vol      *vol;
622     struct dir      *dir;
623     uint32_t            did, darwin_ace_rights;
624     uint16_t        vid;
625     struct path         *s_path;
626     uuidp_t             uuid;
627
628     LOG(log_debug9, logtype_afpd, "afp_access: BEGIN");
629
630     *rbuflen = 0;
631     ibuf += 2;
632
633     memcpy(&vid, ibuf, sizeof( vid ));
634     ibuf += sizeof(vid);
635     if (NULL == ( vol = getvolbyvid( vid ))) {
636         LOG(log_error, logtype_afpd, "afp_access: error getting volid:%d", vid);
637         return AFPERR_NOOBJ;
638     }
639
640     memcpy(&did, ibuf, sizeof( did ));
641     ibuf += sizeof( did );
642     if (NULL == ( dir = dirlookup( vol, did ))) {
643         LOG(log_error, logtype_afpd, "afp_access: error getting did:%d", did);
644         return afp_errno;
645     }
646
647     /* Skip bitmap */
648     ibuf += 2;
649
650     /* Store UUID address */
651     uuid = (uuidp_t)ibuf;
652     ibuf += UUID_BINSIZE;
653
654     /* Store ACE rights */
655     memcpy(&darwin_ace_rights, ibuf, 4);
656     darwin_ace_rights = ntohl(darwin_ace_rights);
657     ibuf += 4;
658
659     /* get full path and handle file/dir subtleties in netatalk code*/
660     if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
661         LOG(log_error, logtype_afpd, "afp_getacl: cname got an error!");
662         return AFPERR_NOOBJ;
663     }
664     if (!s_path->st_valid)
665         of_statdir(vol, s_path);
666     if ( s_path->st_errno != 0 ) {
667         LOG(log_error, logtype_afpd, "afp_getacl: cant stat");
668         return AFPERR_NOOBJ;
669     }
670
671     ret = check_acl_access(s_path->u_name, uuid, darwin_ace_rights);
672
673     LOG(log_debug9, logtype_afpd, "afp_access: END");
674     return ret;
675 }
676
677 int afp_getacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
678 {
679     struct vol      *vol;
680     struct dir      *dir;
681     int         ret;
682     uint32_t           did;
683     uint16_t        vid, bitmap;
684     struct path         *s_path;
685     struct passwd       *pw;
686     struct group        *gr;
687
688     LOG(log_debug9, logtype_afpd, "afp_getacl: BEGIN");
689     *rbuflen = 0;
690     ibuf += 2;
691
692     memcpy(&vid, ibuf, sizeof( vid ));
693     ibuf += sizeof(vid);
694     if (NULL == ( vol = getvolbyvid( vid ))) {
695         LOG(log_error, logtype_afpd, "afp_getacl: error getting volid:%d", vid);
696         return AFPERR_NOOBJ;
697     }
698
699     memcpy(&did, ibuf, sizeof( did ));
700     ibuf += sizeof( did );
701     if (NULL == ( dir = dirlookup( vol, did ))) {
702         LOG(log_error, logtype_afpd, "afp_getacl: error getting did:%d", did);
703         return afp_errno;
704     }
705
706     memcpy(&bitmap, ibuf, sizeof( bitmap ));
707     memcpy(rbuf, ibuf, sizeof( bitmap ));
708     bitmap = ntohs( bitmap );
709     ibuf += sizeof( bitmap );
710     rbuf += sizeof( bitmap );
711     *rbuflen += sizeof( bitmap );
712
713     /* skip maxreplysize */
714     ibuf += 4;
715
716     /* get full path and handle file/dir subtleties in netatalk code*/
717     if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
718         LOG(log_error, logtype_afpd, "afp_getacl: cname got an error!");
719         return AFPERR_NOOBJ;
720     }
721     if (!s_path->st_valid)
722         of_statdir(vol, s_path);
723     if ( s_path->st_errno != 0 ) {
724         LOG(log_error, logtype_afpd, "afp_getacl: cant stat");
725         return AFPERR_NOOBJ;
726     }
727
728     /* Shall we return owner UUID ? */
729     if (bitmap & kFileSec_UUID) {
730         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner user UUID");
731         if (NULL == (pw = getpwuid(s_path->st.st_uid)))
732             return AFPERR_MISC;
733         LOG(log_debug, logtype_afpd, "afp_getacl: got uid: %d, name: %s", s_path->st.st_uid, pw->pw_name);
734         if ((ret = getuuidfromname(pw->pw_name, UUID_USER, rbuf)) != 0)
735             return AFPERR_MISC;
736         rbuf += UUID_BINSIZE;
737         *rbuflen += UUID_BINSIZE;
738     }
739
740     /* Shall we return group UUID ? */
741     if (bitmap & kFileSec_GRPUUID) {
742         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner group UUID");
743         if (NULL == (gr = getgrgid(s_path->st.st_gid)))
744             return AFPERR_MISC;
745         LOG(log_debug, logtype_afpd, "afp_getacl: got gid: %d, name: %s", s_path->st.st_gid, gr->gr_name);
746         if ((ret = getuuidfromname(gr->gr_name, UUID_GROUP, rbuf)) != 0)
747             return AFPERR_MISC;
748         rbuf += UUID_BINSIZE;
749         *rbuflen += UUID_BINSIZE;
750     }
751
752     /* Shall we return ACL ? */
753     if (bitmap & kFileSec_ACL) {
754         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files ACL");
755         get_and_map_acl(s_path->u_name, rbuf, rbuflen);
756     }
757
758     LOG(log_debug9, logtype_afpd, "afp_getacl: END");
759     return AFP_OK;
760 }
761
762 int afp_setacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
763 {
764     struct vol      *vol;
765     struct dir      *dir;
766     int         ret;
767     uint32_t            did;
768     uint16_t        vid, bitmap;
769     struct path         *s_path;
770
771     LOG(log_debug9, logtype_afpd, "afp_setacl: BEGIN");
772     *rbuflen = 0;
773     ibuf += 2;
774
775     memcpy(&vid, ibuf, sizeof( vid ));
776     ibuf += sizeof(vid);
777     if (NULL == ( vol = getvolbyvid( vid ))) {
778         LOG(log_error, logtype_afpd, "afp_setacl: error getting volid:%d", vid);
779         return AFPERR_NOOBJ;
780     }
781
782     memcpy(&did, ibuf, sizeof( did ));
783     ibuf += sizeof( did );
784     if (NULL == ( dir = dirlookup( vol, did ))) {
785         LOG(log_error, logtype_afpd, "afp_setacl: error getting did:%d", did);
786         return afp_errno;
787     }
788
789     memcpy(&bitmap, ibuf, sizeof( bitmap ));
790     bitmap = ntohs( bitmap );
791     ibuf += sizeof( bitmap );
792
793     /* get full path and handle file/dir subtleties in netatalk code*/
794     if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
795         LOG(log_error, logtype_afpd, "afp_setacl: cname got an error!");
796         return AFPERR_NOOBJ;
797     }
798     if (!s_path->st_valid)
799         of_statdir(vol, s_path);
800     if ( s_path->st_errno != 0 ) {
801         LOG(log_error, logtype_afpd, "afp_setacl: cant stat");
802         return AFPERR_NOOBJ;
803     }
804     LOG(log_debug, logtype_afpd, "afp_setacl: unixname: %s", s_path->u_name);
805
806     /* Padding? */
807     if ((unsigned long)ibuf & 1)
808         ibuf++;
809
810     /* Start processing request */
811
812     /* Change owner: dont even try */
813     if (bitmap & kFileSec_UUID) {
814         LOG(log_debug, logtype_afpd, "afp_setacl: change owner request. discarded.");
815         ret = AFPERR_ACCESS;
816         ibuf += UUID_BINSIZE;
817     }
818
819     /* Change group: certain changes might be allowed, so try it. FIXME: not implemented yet. */
820     if (bitmap & kFileSec_UUID) {
821         LOG(log_debug, logtype_afpd, "afp_setacl: change group request. not supported this time.");
822         ret = AFPERR_PARAM;
823         ibuf += UUID_BINSIZE;
824     }
825
826     /* Remove ACL ? */
827     if (bitmap & kFileSec_REMOVEACL) {
828         LOG(log_debug, logtype_afpd, "afp_setacl: Remove ACL request.");
829         if ((ret = remove_acl_vfs(vol, s_path->u_name, S_ISDIR(s_path->st.st_mode))) != AFP_OK)
830             LOG(log_error, logtype_afpd, "afp_setacl: error from remove_acl");
831     }
832
833     /* Change ACL ? */
834     if (bitmap & kFileSec_ACL) {
835         LOG(log_debug, logtype_afpd, "afp_setacl: Change ACL request.");
836
837         /* Check if its our job to preserve inherited ACEs */
838         if (bitmap & kFileSec_Inherit)
839             ret = set_acl_vfs(vol, s_path->u_name, 1, ibuf);
840         else
841             ret = set_acl_vfs(vol, s_path->u_name, 0, ibuf);
842         if (ret == 0)
843             ret = AFP_OK;
844         else {
845             LOG(log_warning, logtype_afpd, "afp_setacl(\"%s/%s\"): error",
846                 getcwdpath(), s_path->u_name);
847             ret = AFPERR_MISC;
848         }
849     }
850
851     LOG(log_debug9, logtype_afpd, "afp_setacl: END");
852     return ret;
853 }
854
855 /*
856   unix.c/accessmode calls this: map ACL to OS 9 mode
857 */
858 void acltoownermode(char *path, struct stat *st, uid_t uid, struct maccess *ma)
859 {
860     struct passwd *pw;
861     uuid_t uuid;
862     int dir, r_ok, w_ok, x_ok;
863
864     if ( ! (AFPobj->options.flags & OPTION_UUID))
865         return;
866
867     LOG(log_maxdebug, logtype_afpd, "acltoownermode('%s')", path);
868
869     if ((pw = getpwuid(uid)) == NULL) {
870         LOG(log_error, logtype_afpd, "acltoownermode: %s", strerror(errno));
871         return;
872     }
873
874     /* We need the UUID for check_acl_access */
875     if ((getuuidfromname(pw->pw_name, UUID_USER, uuid)) != 0)
876         return;
877
878     /* These work for files and dirs */
879     r_ok = check_acl_access(path, uuid, DARWIN_ACE_READ_DATA);
880     w_ok = check_acl_access(path, uuid, (DARWIN_ACE_WRITE_DATA|DARWIN_ACE_APPEND_DATA));
881     x_ok = check_acl_access(path, uuid, DARWIN_ACE_EXECUTE);
882
883     LOG(log_debug7, logtype_afpd, "acltoownermode: ma_user before: %04o",ma->ma_user);
884     if (r_ok == 0)
885         ma->ma_user |= AR_UREAD;
886     if (w_ok == 0)
887         ma->ma_user |= AR_UWRITE;
888     if (x_ok == 0)
889         ma->ma_user |= AR_USEARCH;
890     LOG(log_debug7, logtype_afpd, "acltoownermode: ma_user after: %04o", ma->ma_user);
891
892     return;
893 }
894
895 /*
896   We're being called at the end of afp_createdir. We're (hopefully) inside dir
897   and ".AppleDouble" and ".AppleDouble/.Parent" should have already been created.
898   We then inherit any explicit ACE from "." to ".AppleDouble" and ".AppleDouble/.Parent".
899   FIXME: add to VFS layer ?
900 */
901 void addir_inherit_acl(const struct vol *vol)
902 {
903     ace_t *diraces = NULL, *adaces = NULL, *combinedaces = NULL;
904     int diracecount, adacecount;
905
906     LOG(log_debug9, logtype_afpd, "addir_inherit_acl: BEGIN");
907
908     /* Check if ACLs are enabled for the volume */
909     if (vol->v_flags & AFPVOL_ACLS) {
910
911         if ((diracecount = get_nfsv4_acl(".", &diraces)) <= 0)
912             goto cleanup;
913         /* Remove any trivial ACE from "." */
914         if ((diracecount = strip_trivial_aces(&diraces, diracecount)) <= 0)
915             goto cleanup;
916
917         /*
918           Inherit to ".AppleDouble"
919         */
920
921         if ((adacecount = get_nfsv4_acl(".AppleDouble", &adaces)) <= 0)
922             goto cleanup;
923         /* Remove any non-trivial ACE from ".AppleDouble" */
924         if ((adacecount = strip_nontrivial_aces(&adaces, adacecount)) <= 0)
925             goto cleanup;
926
927         /* Combine ACEs */
928         if ((combinedaces = concat_aces(diraces, diracecount, adaces, adacecount)) == NULL)
929             goto cleanup;
930
931         /* Now set new acl */
932         if ((acl(".AppleDouble", ACE_SETACL, diracecount + adacecount, combinedaces)) != 0)
933             LOG(log_error, logtype_afpd, "addir_inherit_acl: acl: %s", strerror(errno));
934
935         free(adaces);
936         adaces = NULL;
937         free(combinedaces);
938         combinedaces = NULL;
939
940         /*
941           Inherit to ".AppleDouble/.Parent"
942         */
943
944         if ((adacecount = get_nfsv4_acl(".AppleDouble/.Parent", &adaces)) <= 0)
945             goto cleanup;
946         if ((adacecount = strip_nontrivial_aces(&adaces, adacecount)) <= 0)
947             goto cleanup;
948
949         /* Combine ACEs */
950         if ((combinedaces = concat_aces(diraces, diracecount, adaces, adacecount)) == NULL)
951             goto cleanup;
952
953         /* Now set new acl */
954         if ((acl(".AppleDouble/.Parent", ACE_SETACL, diracecount + adacecount, combinedaces)) != 0)
955             LOG(log_error, logtype_afpd, "addir_inherit_acl: acl: %s", strerror(errno));
956
957
958     }
959
960 cleanup:
961     LOG(log_debug9, logtype_afpd, "addir_inherit_acl: END");
962
963     free(diraces);
964     free(adaces);
965     free(combinedaces);
966 }