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