]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/acls.c
Fix for not shown ACLs for when filesyem uid or gid couldn't be resolved because...
[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             LOG(log_debug, logtype_afpd, "afp_getacl: local uid: %u", s_path->st.st_uid);
733             localuuid_from_id(rbuf, UUID_USER, s_path->st.st_uid);
734         } else {
735             LOG(log_debug, logtype_afpd, "afp_getacl: got uid: %d, name: %s", s_path->st.st_uid, pw->pw_name);
736             if ((ret = getuuidfromname(pw->pw_name, UUID_USER, rbuf)) != 0)
737                 return AFPERR_MISC;
738         }
739         rbuf += UUID_BINSIZE;
740         *rbuflen += UUID_BINSIZE;
741     }
742
743     /* Shall we return group UUID ? */
744     if (bitmap & kFileSec_GRPUUID) {
745         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner group UUID");
746         if (NULL == (gr = getgrgid(s_path->st.st_gid))) {
747             LOG(log_debug, logtype_afpd, "afp_getacl: local gid: %u", s_path->st.st_gid);
748             localuuid_from_id(rbuf, UUID_GROUP, s_path->st.st_gid);
749         } else {
750             LOG(log_debug, logtype_afpd, "afp_getacl: got gid: %d, name: %s", s_path->st.st_gid, gr->gr_name);
751             if ((ret = getuuidfromname(gr->gr_name, UUID_GROUP, rbuf)) != 0)
752                 return AFPERR_MISC;
753         }
754         rbuf += UUID_BINSIZE;
755         *rbuflen += UUID_BINSIZE;
756     }
757
758     /* Shall we return ACL ? */
759     if (bitmap & kFileSec_ACL) {
760         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files ACL");
761         get_and_map_acl(s_path->u_name, rbuf, rbuflen);
762     }
763
764     LOG(log_debug9, logtype_afpd, "afp_getacl: END");
765     return AFP_OK;
766 }
767
768 int afp_setacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
769 {
770     struct vol      *vol;
771     struct dir      *dir;
772     int         ret;
773     uint32_t            did;
774     uint16_t        vid, bitmap;
775     struct path         *s_path;
776
777     LOG(log_debug9, logtype_afpd, "afp_setacl: BEGIN");
778     *rbuflen = 0;
779     ibuf += 2;
780
781     memcpy(&vid, ibuf, sizeof( vid ));
782     ibuf += sizeof(vid);
783     if (NULL == ( vol = getvolbyvid( vid ))) {
784         LOG(log_error, logtype_afpd, "afp_setacl: error getting volid:%d", vid);
785         return AFPERR_NOOBJ;
786     }
787
788     memcpy(&did, ibuf, sizeof( did ));
789     ibuf += sizeof( did );
790     if (NULL == ( dir = dirlookup( vol, did ))) {
791         LOG(log_error, logtype_afpd, "afp_setacl: error getting did:%d", did);
792         return afp_errno;
793     }
794
795     memcpy(&bitmap, ibuf, sizeof( bitmap ));
796     bitmap = ntohs( bitmap );
797     ibuf += sizeof( bitmap );
798
799     /* get full path and handle file/dir subtleties in netatalk code*/
800     if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
801         LOG(log_error, logtype_afpd, "afp_setacl: cname got an error!");
802         return AFPERR_NOOBJ;
803     }
804     if (!s_path->st_valid)
805         of_statdir(vol, s_path);
806     if ( s_path->st_errno != 0 ) {
807         LOG(log_error, logtype_afpd, "afp_setacl: cant stat");
808         return AFPERR_NOOBJ;
809     }
810     LOG(log_debug, logtype_afpd, "afp_setacl: unixname: %s", s_path->u_name);
811
812     /* Padding? */
813     if ((unsigned long)ibuf & 1)
814         ibuf++;
815
816     /* Start processing request */
817
818     /* Change owner: dont even try */
819     if (bitmap & kFileSec_UUID) {
820         LOG(log_debug, logtype_afpd, "afp_setacl: change owner request. discarded.");
821         ret = AFPERR_ACCESS;
822         ibuf += UUID_BINSIZE;
823     }
824
825     /* Change group: certain changes might be allowed, so try it. FIXME: not implemented yet. */
826     if (bitmap & kFileSec_UUID) {
827         LOG(log_debug, logtype_afpd, "afp_setacl: change group request. not supported this time.");
828         ret = AFPERR_PARAM;
829         ibuf += UUID_BINSIZE;
830     }
831
832     /* Remove ACL ? */
833     if (bitmap & kFileSec_REMOVEACL) {
834         LOG(log_debug, logtype_afpd, "afp_setacl: Remove ACL request.");
835         if ((ret = remove_acl_vfs(vol, s_path->u_name, S_ISDIR(s_path->st.st_mode))) != AFP_OK)
836             LOG(log_error, logtype_afpd, "afp_setacl: error from remove_acl");
837     }
838
839     /* Change ACL ? */
840     if (bitmap & kFileSec_ACL) {
841         LOG(log_debug, logtype_afpd, "afp_setacl: Change ACL request.");
842
843         /* Check if its our job to preserve inherited ACEs */
844         if (bitmap & kFileSec_Inherit)
845             ret = set_acl_vfs(vol, s_path->u_name, 1, ibuf);
846         else
847             ret = set_acl_vfs(vol, s_path->u_name, 0, ibuf);
848         if (ret == 0)
849             ret = AFP_OK;
850         else {
851             LOG(log_warning, logtype_afpd, "afp_setacl(\"%s/%s\"): error",
852                 getcwdpath(), s_path->u_name);
853             ret = AFPERR_MISC;
854         }
855     }
856
857     LOG(log_debug9, logtype_afpd, "afp_setacl: END");
858     return ret;
859 }
860
861 /*
862   unix.c/accessmode calls this: map ACL to OS 9 mode
863 */
864 void acltoownermode(char *path, struct stat *st, uid_t uid, struct maccess *ma)
865 {
866     struct passwd *pw;
867     uuid_t uuid;
868     int dir, r_ok, w_ok, x_ok;
869
870     if ( ! (AFPobj->options.flags & OPTION_UUID))
871         return;
872
873     LOG(log_maxdebug, logtype_afpd, "acltoownermode('%s')", path);
874
875     if ((pw = getpwuid(uid)) == NULL) {
876         LOG(log_error, logtype_afpd, "acltoownermode: %s", strerror(errno));
877         return;
878     }
879
880     /* We need the UUID for check_acl_access */
881     if ((getuuidfromname(pw->pw_name, UUID_USER, uuid)) != 0)
882         return;
883
884     /* These work for files and dirs */
885     r_ok = check_acl_access(path, uuid, DARWIN_ACE_READ_DATA);
886     w_ok = check_acl_access(path, uuid, (DARWIN_ACE_WRITE_DATA|DARWIN_ACE_APPEND_DATA));
887     x_ok = check_acl_access(path, uuid, DARWIN_ACE_EXECUTE);
888
889     LOG(log_debug7, logtype_afpd, "acltoownermode: ma_user before: %04o",ma->ma_user);
890     if (r_ok == 0)
891         ma->ma_user |= AR_UREAD;
892     if (w_ok == 0)
893         ma->ma_user |= AR_UWRITE;
894     if (x_ok == 0)
895         ma->ma_user |= AR_USEARCH;
896     LOG(log_debug7, logtype_afpd, "acltoownermode: ma_user after: %04o", ma->ma_user);
897
898     return;
899 }
900
901 /*
902   We're being called at the end of afp_createdir. We're (hopefully) inside dir
903   and ".AppleDouble" and ".AppleDouble/.Parent" should have already been created.
904   We then inherit any explicit ACE from "." to ".AppleDouble" and ".AppleDouble/.Parent".
905   FIXME: add to VFS layer ?
906 */
907 void addir_inherit_acl(const struct vol *vol)
908 {
909     ace_t *diraces = NULL, *adaces = NULL, *combinedaces = NULL;
910     int diracecount, adacecount;
911
912     LOG(log_debug9, logtype_afpd, "addir_inherit_acl: BEGIN");
913
914     /* Check if ACLs are enabled for the volume */
915     if (vol->v_flags & AFPVOL_ACLS) {
916
917         if ((diracecount = get_nfsv4_acl(".", &diraces)) <= 0)
918             goto cleanup;
919         /* Remove any trivial ACE from "." */
920         if ((diracecount = strip_trivial_aces(&diraces, diracecount)) <= 0)
921             goto cleanup;
922
923         /*
924           Inherit to ".AppleDouble"
925         */
926
927         if ((adacecount = get_nfsv4_acl(".AppleDouble", &adaces)) <= 0)
928             goto cleanup;
929         /* Remove any non-trivial ACE from ".AppleDouble" */
930         if ((adacecount = strip_nontrivial_aces(&adaces, adacecount)) <= 0)
931             goto cleanup;
932
933         /* Combine ACEs */
934         if ((combinedaces = concat_aces(diraces, diracecount, adaces, adacecount)) == NULL)
935             goto cleanup;
936
937         /* Now set new acl */
938         if ((acl(".AppleDouble", ACE_SETACL, diracecount + adacecount, combinedaces)) != 0)
939             LOG(log_error, logtype_afpd, "addir_inherit_acl: acl: %s", strerror(errno));
940
941         free(adaces);
942         adaces = NULL;
943         free(combinedaces);
944         combinedaces = NULL;
945
946         /*
947           Inherit to ".AppleDouble/.Parent"
948         */
949
950         if ((adacecount = get_nfsv4_acl(".AppleDouble/.Parent", &adaces)) <= 0)
951             goto cleanup;
952         if ((adacecount = strip_nontrivial_aces(&adaces, adacecount)) <= 0)
953             goto cleanup;
954
955         /* Combine ACEs */
956         if ((combinedaces = concat_aces(diraces, diracecount, adaces, adacecount)) == NULL)
957             goto cleanup;
958
959         /* Now set new acl */
960         if ((acl(".AppleDouble/.Parent", ACE_SETACL, diracecount + adacecount, combinedaces)) != 0)
961             LOG(log_error, logtype_afpd, "addir_inherit_acl: acl: %s", strerror(errno));
962
963
964     }
965
966 cleanup:
967     LOG(log_debug9, logtype_afpd, "addir_inherit_acl: END");
968
969     free(diraces);
970     free(adaces);
971     free(combinedaces);
972 }