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