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