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