]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/acls.c
Merge symlink branch
[netatalk.git] / etc / afpd / acls.c
1 /*
2   $Id: acls.c,v 1.8 2010-02-10 14:05:37 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     /* Now check requested rights */
640     ret = AFPERR_ACCESS;
641     i = 0;
642     do { /* Loop through ACEs */
643         who = aces[i].a_who;
644         flags = aces[i].a_flags;
645         type = aces[i].a_type;
646         rights = aces[i].a_access_mask;
647
648         if (flags & ACE_INHERIT_ONLY_ACE)
649             continue;
650
651         /* Check if its a group ACE and set checkgroup to 1 if yes */
652         checkgroup = 0;
653         if ( (flags & ACE_IDENTIFIER_GROUP) && !(flags & ACE_GROUP) ) {
654             if ( (check_group(username, uid, pgid, who)) == 0)
655                 checkgroup = 1;
656             else
657                 continue;
658         }
659
660         /* Now the tricky part: decide if ACE effects our user. I'll explain:
661            if its a dedicated (non trivial) ACE for the user
662            OR
663            if its a ACE for a group we're member of
664            OR
665            if its a trivial ACE_OWNER ACE and requested UUID is the owner
666            OR
667            if its a trivial ACE_GROUP ACE and requested UUID is group
668            OR
669            if its a trivial ACE_EVERYONE ACE
670            THEN
671            process ACE */
672         if (
673             ( (who == uid) && !(flags & (ACE_TRIVIAL|ACE_IDENTIFIER_GROUP)) ) ||
674             (checkgroup) ||
675             ( (flags & ACE_OWNER) && check_user_trivace ) ||
676             ( (flags & ACE_GROUP) && check_group_trivace ) ||
677             ( flags & ACE_EVERYONE )
678             ) {
679             /* Found an applicable ACE */
680             if (type == ACE_ACCESS_ALLOWED_ACE_TYPE)
681                 allowed_rights |= rights;
682             else if (type == ACE_ACCESS_DENIED_ACE_TYPE)
683                 /* Only or to denied rights if not previously allowed !! */
684                 denied_rights |= ((!allowed_rights) & rights);
685         }
686     } while (++i < ace_count);
687
688
689     /* Darwin likes to ask for "delete_child" on dir,
690        "write_data" is actually the same, so we add that for dirs */
691     if (dir && (allowed_rights & ACE_WRITE_DATA))
692         allowed_rights |= ACE_DELETE_CHILD;
693
694     if (requested_rights & denied_rights) {
695         LOG(log_debug, logtype_afpd, "check_access: some requested right was denied:");
696         ret = AFPERR_ACCESS;
697     } else if ((requested_rights & allowed_rights) != requested_rights) {
698         LOG(log_debug, logtype_afpd, "check_access: some requested right wasn't allowed:");
699         ret = AFPERR_ACCESS;
700     } else {
701         LOG(log_debug, logtype_afpd, "check_access: all requested rights are allowed:");
702         ret = AFP_OK;
703     }
704
705     LOG(log_debug, logtype_afpd, "check_access: Requested rights: %08x, allowed_rights: %08x, denied_rights: %08x, Result: %d",
706         requested_rights, allowed_rights, denied_rights, ret);
707
708 exit:
709     free(aces);
710     free(username);
711 #ifdef DEBUG
712     LOG(log_debug9, logtype_afpd, "check_access: END");
713 #endif
714     return ret;
715 }
716
717 /********************************************************
718  * Interface
719  ********************************************************/
720
721 int afp_access(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
722 {
723     int         ret;
724     struct vol      *vol;
725     struct dir      *dir;
726     uint32_t            did, darwin_ace_rights;
727     uint16_t        vid;
728     struct path         *s_path;
729     uuidp_t             uuid;
730
731     LOG(log_debug9, logtype_afpd, "afp_access: BEGIN");
732
733     *rbuflen = 0;
734     ibuf += 2;
735
736     memcpy(&vid, ibuf, sizeof( vid ));
737     ibuf += sizeof(vid);
738     if (NULL == ( vol = getvolbyvid( vid ))) {
739         LOG(log_error, logtype_afpd, "afp_access: error getting volid:%d", vid);
740         return AFPERR_NOOBJ;
741     }
742
743     memcpy(&did, ibuf, sizeof( did ));
744     ibuf += sizeof( did );
745     if (NULL == ( dir = dirlookup( vol, did ))) {
746         LOG(log_error, logtype_afpd, "afp_access: error getting did:%d", did);
747         return afp_errno;
748     }
749
750     /* Skip bitmap */
751     ibuf += 2;
752
753     /* Store UUID address */
754     uuid = (uuidp_t)ibuf;
755     ibuf += UUID_BINSIZE;
756
757     /* Store ACE rights */
758     memcpy(&darwin_ace_rights, ibuf, 4);
759     darwin_ace_rights = ntohl(darwin_ace_rights);
760     ibuf += 4;
761
762     /* get full path and handle file/dir subtleties in netatalk code*/
763     if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
764         LOG(log_error, logtype_afpd, "afp_getacl: cname got an error!");
765         return AFPERR_NOOBJ;
766     }
767     if (!s_path->st_valid)
768         of_statdir(vol, s_path);
769     if ( s_path->st_errno != 0 ) {
770         LOG(log_error, logtype_afpd, "afp_getacl: cant stat");
771         return AFPERR_NOOBJ;
772     }
773
774     ret = check_acl_access(s_path->u_name, uuid, darwin_ace_rights);
775
776     LOG(log_debug9, logtype_afpd, "afp_access: END");
777     return ret;
778 }
779
780 int afp_getacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
781 {
782     struct vol      *vol;
783     struct dir      *dir;
784     int         ret;
785     uint32_t           did;
786     uint16_t        vid, bitmap;
787     struct path         *s_path;
788     struct passwd       *pw;
789     struct group        *gr;
790
791     LOG(log_debug9, logtype_afpd, "afp_getacl: BEGIN");
792     *rbuflen = 0;
793     ibuf += 2;
794
795     memcpy(&vid, ibuf, sizeof( vid ));
796     ibuf += sizeof(vid);
797     if (NULL == ( vol = getvolbyvid( vid ))) {
798         LOG(log_error, logtype_afpd, "afp_getacl: error getting volid:%d", vid);
799         return AFPERR_NOOBJ;
800     }
801
802     memcpy(&did, ibuf, sizeof( did ));
803     ibuf += sizeof( did );
804     if (NULL == ( dir = dirlookup( vol, did ))) {
805         LOG(log_error, logtype_afpd, "afp_getacl: error getting did:%d", did);
806         return afp_errno;
807     }
808
809     memcpy(&bitmap, ibuf, sizeof( bitmap ));
810     memcpy(rbuf, ibuf, sizeof( bitmap ));
811     bitmap = ntohs( bitmap );
812     ibuf += sizeof( bitmap );
813     rbuf += sizeof( bitmap );
814     *rbuflen += sizeof( bitmap );
815
816     /* skip maxreplysize */
817     ibuf += 4;
818
819     /* get full path and handle file/dir subtleties in netatalk code*/
820     if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
821         LOG(log_error, logtype_afpd, "afp_getacl: cname got an error!");
822         return AFPERR_NOOBJ;
823     }
824     if (!s_path->st_valid)
825         of_statdir(vol, s_path);
826     if ( s_path->st_errno != 0 ) {
827         LOG(log_error, logtype_afpd, "afp_getacl: cant stat");
828         return AFPERR_NOOBJ;
829     }
830
831     /* Shall we return owner UUID ? */
832     if (bitmap & kFileSec_UUID) {
833         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner user UUID");
834         if (NULL == (pw = getpwuid(s_path->st.st_uid)))
835             return AFPERR_MISC;
836         LOG(log_debug, logtype_afpd, "afp_getacl: got uid: %d, name: %s", s_path->st.st_uid, pw->pw_name);
837         if ((ret = getuuidfromname(pw->pw_name, UUID_USER, rbuf)) != 0)
838             return AFPERR_MISC;
839         rbuf += UUID_BINSIZE;
840         *rbuflen += UUID_BINSIZE;
841     }
842
843     /* Shall we return group UUID ? */
844     if (bitmap & kFileSec_GRPUUID) {
845         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner group UUID");
846         if (NULL == (gr = getgrgid(s_path->st.st_gid)))
847             return AFPERR_MISC;
848         LOG(log_debug, logtype_afpd, "afp_getacl: got gid: %d, name: %s", s_path->st.st_gid, gr->gr_name);
849         if ((ret = getuuidfromname(gr->gr_name, UUID_GROUP, rbuf)) != 0)
850             return AFPERR_MISC;
851         rbuf += UUID_BINSIZE;
852         *rbuflen += UUID_BINSIZE;
853     }
854
855     /* Shall we return ACL ? */
856     if (bitmap & kFileSec_ACL) {
857         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files ACL");
858         get_and_map_acl(s_path->u_name, rbuf, rbuflen);
859     }
860
861     LOG(log_debug9, logtype_afpd, "afp_getacl: END");
862     return AFP_OK;
863 }
864
865 int afp_setacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
866 {
867     struct vol      *vol;
868     struct dir      *dir;
869     int         ret;
870     uint32_t            did;
871     uint16_t        vid, bitmap;
872     struct path         *s_path;
873
874     LOG(log_debug9, logtype_afpd, "afp_setacl: BEGIN");
875     *rbuflen = 0;
876     ibuf += 2;
877
878     memcpy(&vid, ibuf, sizeof( vid ));
879     ibuf += sizeof(vid);
880     if (NULL == ( vol = getvolbyvid( vid ))) {
881         LOG(log_error, logtype_afpd, "afp_setacl: error getting volid:%d", vid);
882         return AFPERR_NOOBJ;
883     }
884
885     memcpy(&did, ibuf, sizeof( did ));
886     ibuf += sizeof( did );
887     if (NULL == ( dir = dirlookup( vol, did ))) {
888         LOG(log_error, logtype_afpd, "afp_setacl: error getting did:%d", did);
889         return afp_errno;
890     }
891
892     memcpy(&bitmap, ibuf, sizeof( bitmap ));
893     bitmap = ntohs( bitmap );
894     ibuf += sizeof( bitmap );
895
896     /* get full path and handle file/dir subtleties in netatalk code*/
897     if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
898         LOG(log_error, logtype_afpd, "afp_setacl: cname got an error!");
899         return AFPERR_NOOBJ;
900     }
901     if (!s_path->st_valid)
902         of_statdir(vol, s_path);
903     if ( s_path->st_errno != 0 ) {
904         LOG(log_error, logtype_afpd, "afp_setacl: cant stat");
905         return AFPERR_NOOBJ;
906     }
907     LOG(log_debug, logtype_afpd, "afp_setacl: unixname: %s", s_path->u_name);
908
909     /* Padding? */
910     if ((unsigned long)ibuf & 1)
911         ibuf++;
912
913     /* Start processing request */
914
915     /* Change owner: dont even try */
916     if (bitmap & kFileSec_UUID) {
917         LOG(log_debug, logtype_afpd, "afp_setacl: change owner request. discarded.");
918         ret = AFPERR_ACCESS;
919         ibuf += UUID_BINSIZE;
920     }
921
922     /* Change group: certain changes might be allowed, so try it. FIXME: not implemented yet. */
923     if (bitmap & kFileSec_UUID) {
924         LOG(log_debug, logtype_afpd, "afp_setacl: change group request. not supported this time.");
925         ret = AFPERR_PARAM;
926         ibuf += UUID_BINSIZE;
927     }
928
929     /* Remove ACL ? */
930     if (bitmap & kFileSec_REMOVEACL) {
931         LOG(log_debug, logtype_afpd, "afp_setacl: Remove ACL request.");
932         if ((ret = remove_acl_vfs(vol, s_path->u_name, S_ISDIR(s_path->st.st_mode))) != AFP_OK)
933             LOG(log_error, logtype_afpd, "afp_setacl: error from remove_acl");
934     }
935
936     /* Change ACL ? */
937     if (bitmap & kFileSec_ACL) {
938         LOG(log_debug, logtype_afpd, "afp_setacl: Change ACL request.");
939
940         /* Check if its our job to preserve inherited ACEs */
941         if (bitmap & kFileSec_Inherit)
942             ret = set_acl_vfs(vol, s_path->u_name, 1, ibuf);
943         else
944             ret = set_acl_vfs(vol, s_path->u_name, 0, ibuf);
945         if (ret == 0)
946             ret = AFP_OK;
947         else
948             ret = AFPERR_MISC;
949     }
950
951     LOG(log_debug9, logtype_afpd, "afp_setacl: END");
952     return ret;
953 }
954
955 /*
956   unix.c/accessmode calls this: map ACL to OS 9 mode
957 */
958 void acltoownermode(char *path, struct stat *st, uid_t uid, struct maccess *ma)
959 {
960     struct passwd *pw;
961     uuid_t uuid;
962     int dir, r_ok, w_ok, x_ok;
963
964     if ((pw = getpwuid(uid)) == NULL) {
965         LOG(log_error, logtype_afpd, "acltoownermode: %s", strerror(errno));
966         return;
967     }
968
969     /* We need the UUID for check_acl_access */
970     if ((getuuidfromname(pw->pw_name, UUID_USER, uuid)) != 0)
971         return;
972
973     /* These work for files and dirs */
974     r_ok = check_acl_access(path, uuid, DARWIN_ACE_READ_DATA);
975     w_ok = check_acl_access(path, uuid, (DARWIN_ACE_WRITE_DATA|DARWIN_ACE_APPEND_DATA));
976     x_ok = check_acl_access(path, uuid, DARWIN_ACE_EXECUTE);
977
978     LOG(log_debug7, logtype_afpd, "acltoownermode: ma_user before: %04o",ma->ma_user);
979     if (r_ok == 0)
980         ma->ma_user |= AR_UREAD;
981     if (w_ok == 0)
982         ma->ma_user |= AR_UWRITE;
983     if (x_ok == 0)
984         ma->ma_user |= AR_USEARCH;
985     LOG(log_debug7, logtype_afpd, "acltoownermode: ma_user after: %04o", ma->ma_user);
986
987     return;
988 }
989
990 /*
991   We're being called at the end of afp_createdir. We're (hopefully) inside dir
992   and ".AppleDouble" and ".AppleDouble/.Parent" should have already been created.
993   We then inherit any explicit ACE from "." to ".AppleDouble" and ".AppleDouble/.Parent".
994   FIXME: add to VFS layer ?
995 */
996 void addir_inherit_acl(const struct vol *vol)
997 {
998     ace_t *diraces = NULL, *adaces = NULL, *combinedaces = NULL;
999     int diracecount, adacecount;
1000
1001     LOG(log_debug9, logtype_afpd, "addir_inherit_acl: BEGIN");
1002
1003     /* Check if ACLs are enabled for the volume */
1004     if (vol->v_flags & AFPVOL_ACLS) {
1005
1006         if ((diracecount = get_nfsv4_acl(".", &diraces)) <= 0)
1007             goto cleanup;
1008         /* Remove any trivial ACE from "." */
1009         if ((diracecount = strip_trivial_aces(&diraces, diracecount)) <= 0)
1010             goto cleanup;
1011
1012         /*
1013           Inherit to ".AppleDouble"
1014         */
1015
1016         if ((adacecount = get_nfsv4_acl(".AppleDouble", &adaces)) <= 0)
1017             goto cleanup;
1018         /* Remove any non-trivial ACE from ".AppleDouble" */
1019         if ((adacecount = strip_nontrivial_aces(&adaces, adacecount)) <= 0)
1020             goto cleanup;
1021
1022         /* Combine ACEs */
1023         if ((combinedaces = concat_aces(diraces, diracecount, adaces, adacecount)) == NULL)
1024             goto cleanup;
1025
1026         /* Now set new acl */
1027         if ((acl(".AppleDouble", ACE_SETACL, diracecount + adacecount, combinedaces)) != 0)
1028             LOG(log_error, logtype_afpd, "addir_inherit_acl: acl: %s", strerror(errno));
1029
1030         free(adaces);
1031         adaces = NULL;
1032         free(combinedaces);
1033         combinedaces = NULL;
1034
1035         /*
1036           Inherit to ".AppleDouble/.Parent"
1037         */
1038
1039         if ((adacecount = get_nfsv4_acl(".AppleDouble/.Parent", &adaces)) <= 0)
1040             goto cleanup;
1041         if ((adacecount = strip_nontrivial_aces(&adaces, adacecount)) <= 0)
1042             goto cleanup;
1043
1044         /* Combine ACEs */
1045         if ((combinedaces = concat_aces(diraces, diracecount, adaces, adacecount)) == NULL)
1046             goto cleanup;
1047
1048         /* Now set new acl */
1049         if ((acl(".AppleDouble/.Parent", ACE_SETACL, diracecount + adacecount, combinedaces)) != 0)
1050             LOG(log_error, logtype_afpd, "addir_inherit_acl: acl: %s", strerror(errno));
1051
1052
1053     }
1054
1055 cleanup:
1056     LOG(log_debug9, logtype_afpd, "addir_inherit_acl: END");
1057
1058     free(diraces);
1059     free(adaces);
1060     free(combinedaces);
1061 }