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