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