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