]> arthur.barton.de Git - netatalk.git/commitdiff
Initial checkin of ACLs patch after_acls
authorfranklahm <franklahm>
Mon, 2 Feb 2009 11:55:00 +0000 (11:55 +0000)
committerfranklahm <franklahm>
Mon, 2 Feb 2009 11:55:00 +0000 (11:55 +0000)
36 files changed:
configure.in
contrib/Makefile.am
contrib/acltests/.cvsignore [new file with mode: 0644]
contrib/acltests/Makefile.am [new file with mode: 0644]
contrib/acltests/uuidtest.c [new file with mode: 0644]
doc/README.acls [new file with mode: 0644]
etc/afpd/Makefile.am
etc/afpd/acl_mappings.h [new file with mode: 0644]
etc/afpd/acls.c [new file with mode: 0644]
etc/afpd/acls.h [new file with mode: 0644]
etc/afpd/afp_config.c
etc/afpd/afp_vfs.h
etc/afpd/auth.c
etc/afpd/auth.h
etc/afpd/directory.c
etc/afpd/globals.h
etc/afpd/status.c
etc/afpd/switch.c
etc/afpd/unix.c
etc/afpd/vfs_adouble.c
etc/afpd/volume.c
etc/afpd/volume.h
include/atalk/afp.h
include/atalk/ldapconfig.h [new file with mode: 0644]
include/atalk/uuid.h [new file with mode: 0644]
libatalk/Makefile.am
libatalk/acl/.cvsignore [new file with mode: 0644]
libatalk/acl/Makefile.am [new file with mode: 0644]
libatalk/acl/acl.c [new file with mode: 0644]
libatalk/acl/aclldap.h [new file with mode: 0644]
libatalk/acl/cache.c [new file with mode: 0644]
libatalk/acl/cache.h [new file with mode: 0644]
libatalk/acl/ldap.c [new file with mode: 0644]
libatalk/acl/ldap_config.c [new file with mode: 0644]
libatalk/acl/uuid.c [new file with mode: 0644]
macros/summary.m4

index 1334f6e4af6c3acbefc98568d9cc41e07bbfbc3a..47336919dd92acc9b3d79fc6783dc23d9773e24f 100644 (file)
@@ -1,4 +1,4 @@
-dnl $Id: configure.in,v 1.209 2008-12-23 07:37:44 didg Exp $
+dnl $Id: configure.in,v 1.210 2009-02-02 11:55:00 franklahm Exp $
 dnl configure.in for netatalk
 
 AC_INIT(etc/afpd/main.c)
@@ -1004,6 +1004,43 @@ AC_ARG_ENABLE(overwrite,
 )
 AC_MSG_RESULT([$OVERWRITE_CONFIG])
 
+dnl --------------------- check for ACL support
+neta_cv_nfsv4acl="no"
+AC_MSG_CHECKING([if NFSv4 ACL Support should be enabled])
+AC_ARG_ENABLE(nfsv4acls,
+       [  --enable-nfsv4acls      enable NFSv4 ACL Support],[
+       if test x"$enableval" = x"yes"; then
+          AC_MSG_RESULT([yes])
+          neta_cv_nfsv4acl="yes"
+       else
+          AC_MSG_RESULT([no])
+       fi],[
+          AC_MSG_RESULT([no])
+       ]
+)
+if test x$neta_cv_nfsv4acl = xyes; then
+       AC_CHECK_HEADER([ldap.h],,[
+               AC_MSG_ERROR([ACL Support need the LDAP client headers not found.])
+               neta_cv_nfsv4acl=no
+               ]
+       )
+       AC_CHECK_LIB(ldap,ldap_init,neta_cv_nfsv4acl=yes,neta_cv_nfsv4acl=no)
+fi
+if test x$neta_cv_nfsv4acl = xyes; then
+       AC_CHECK_HEADER([sys/acl.h],[
+               AC_DEFINE([HAVE_NFSv4_ACLS], 1, [Enable ACL code])
+               AC_MSG_NOTICE([Enabling ACL support])
+               ],
+               neta_cv_nfsv4acl=no
+       )
+fi
+if test x$neta_cv_nfsv4acl = xyes; then
+       LIBATALK_ACLS="acl/libacl.la"
+else
+       LIBATALK_ACLS=""
+fi
+AC_SUBST(LIBATALK_ACLS)
+
 dnl --------------------- last minute substitutions
 
 AC_SUBST(LIBS)
@@ -1015,6 +1052,7 @@ AM_CONDITIONAL(COMPILE_TIMELORD, test x$compile_timelord = xyes)
 AM_CONDITIONAL(COMPILE_A2BOOT, test x$compile_a2boot = xyes)
 AM_CONDITIONAL(HAVE_LIBGCRYPT, test x$neta_cv_have_libgcrypt = xyes)
 AM_CONDITIONAL(HAVE_OPENSSL, test x$neta_cv_have_openssl = xyes)
+AM_CONDITIONAL(USE_NFSv4_ACLS, test x$neta_cv_nfsv4acl = xyes)
 AM_CONDITIONAL(USE_DHX, test x$neta_cv_compile_dhx = xyes)
 AM_CONDITIONAL(USE_DHX2, test x$neta_cv_compile_dhx2 = xyes)
 AM_CONDITIONAL(USE_RANDNUM, test x$neta_cv_have_openssl = xyes)
@@ -1055,6 +1093,7 @@ AC_OUTPUT([Makefile
        bin/uniconv/Makefile
        config/Makefile
        contrib/Makefile
+       contrib/acltests/Makefile
        contrib/macusers/Makefile
        contrib/macusers/macusers
        contrib/nu/Makefile
@@ -1086,6 +1125,7 @@ AC_OUTPUT([Makefile
        include/Makefile
        include/atalk/Makefile
        libatalk/Makefile
+       libatalk/acl/Makefile
        libatalk/adouble/Makefile
        libatalk/asp/Makefile
        libatalk/atp/Makefile
@@ -1127,4 +1167,3 @@ AC_OUTPUT([Makefile
 
 AC_NETATALK_LIBS_SUMMARY
 AC_NETATALK_CONFIG_SUMMARY
-
index 633257c3df66f778a1cfc7ea6a554ace9a27ab45..47379aba9973fc532ddfeed07b6e54612b409d3c 100644 (file)
@@ -12,6 +12,6 @@ else
 A2BOOT =
 endif
 
-SUBDIRS = macusers nu printing shell_utils ${TIMELORD} ${A2BOOT}
+SUBDIRS = macusers nu printing shell_utils ${TIMELORD} ${A2BOOT} acltests
 
 EXTRA_DIST = ICDumpSuffixMap
diff --git a/contrib/acltests/.cvsignore b/contrib/acltests/.cvsignore
new file mode 100644 (file)
index 0000000..4bebe07
--- /dev/null
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+.deps
+.libs
+uuid.log
+uuidtest
diff --git a/contrib/acltests/Makefile.am b/contrib/acltests/Makefile.am
new file mode 100644 (file)
index 0000000..75eba00
--- /dev/null
@@ -0,0 +1,10 @@
+pkgconfdir = @PKGCONFDIR@
+
+if USE_NFSv4_ACLS
+bin_PROGRAMS = uuidtest
+
+uuidtest_SOURCES = uuidtest.c
+uuidtest_LDADD =  $(top_builddir)/libatalk/libatalk.la
+
+AM_CFLAGS = @CFLAGS@ -D_PATH_ACL_LDAPCONF=\"$(pkgconfdir)/ldap.conf\"
+endif
diff --git a/contrib/acltests/uuidtest.c b/contrib/acltests/uuidtest.c
new file mode 100644 (file)
index 0000000..fd4d208
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+   $Id: uuidtest.c,v 1.1 2009-02-02 11:55:00 franklahm Exp $
+   Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifdef HAVE_NFSv4_ACLS
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ldap.h>
+
+#include <atalk/ldapconfig.h>
+#include <atalk/uuid.h>
+#include <atalk/logger.h>
+
+#define STRNCMP(a, R, b, l) (strncmp(a,b,l) R 0)
+
+int main( int argc, char **argv)
+{
+    int ret, i;
+    uuid_t uuid;
+    uuidtype_t type;
+    char *uuidstring = NULL;
+    char *name = NULL;
+
+    if (argc <  2) {
+       printf("Usage: uuidtest <name> | -u<UUID> [<name> ... | -u<UUID> ...] \n");
+       return -1;
+    }
+    
+    set_processname("uuidtest");
+    log_init();
+    log_setup("uuid.log", log_maxdebug, logtype_default, logoption_cons);
+
+
+    /* Parse ldap.conf */
+    printf("Start parsing ldap.conf\n");
+    acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
+    printf("Finished parsing ldap.conf\n");
+    if (ldap_config_valid) {
+       if (ldap_auth_method == LDAP_AUTH_NONE)
+           printf("ldap.conf is ok. Using anonymous bind.\n");
+       else if (ldap_auth_method == LDAP_AUTH_SIMPLE)
+           printf("ldap.conf is ok. Using simple bind.\n");
+       else {
+           ldap_config_valid = 0;
+           printf("ldap.conf want SASL which is not yet supported.\n");        
+       }
+    } else {
+       printf("ldap.conf is not ok.\n");
+       return 1;
+    }
+
+    for (i=1; i+1 <= argc; i++) {
+       if (STRNCMP("-u", == , argv[i], 2)) {
+           printf("Searching uuid: %s\n", argv[i]+2);
+           uuid_string2bin(argv[i]+2, uuid);
+            ret = getnamefromuuid( uuid, &name, &type);
+            if (ret == 0) {
+                printf("Got user: %s for uuid: %s, type:%d\n", name, argv[i]+2, type);
+                free(uuidstring);
+            } else
+                printf("uuid %s not found.\n", argv[i]+2);
+       } else {
+           printf("Searching user: %s\n", argv[i]);
+           ret = getuuidfromname( argv[i], UUID_USER, uuid);
+           if (ret == 0) {
+               uuid_bin2string( uuid, &uuidstring);
+               printf("Got uuid: %s for name: %s, type: USER\n", uuidstring, argv[i]);
+               free(uuidstring);
+           } else {
+               ret = getuuidfromname( argv[i], UUID_GROUP, uuid);
+               if (ret == 0) {
+                   uuid_bin2string( uuid, &uuidstring);
+                   printf("Got uuid: %s for name: %s, type: GROUP\n", uuidstring, argv[i]);
+                   free(uuidstring);
+               }
+               else
+                   printf("User %s not found.\n", argv[i]);
+           }
+       }
+    }
+
+    return 0;
+}
+
+#endif /* HAVE_NFSv4_ACLS */
diff --git a/doc/README.acls b/doc/README.acls
new file mode 100644 (file)
index 0000000..266b74b
--- /dev/null
@@ -0,0 +1,94 @@
+
+                 ACLs - Konfiguration and Infos vor Developpers
+                 ==============================================
+
+ACL support for AFP is implemented with NFSv4 ACLs. Few filesystems and fewer OSes support
+these. At the time of implementation its only provided with ZFS on Solaris, Opensolaris and
+derived distributions.
+
+  Configuration
+  -------------
+
+In order to be able to support ACLs, the following things have to be configured:
+
+1. ZFS Volumes
+2. Authentication Domain
+3. Netatalk Volumes
+
+  1. ZFS Volumes:
+
+     You MUST configure two ACL parameters for any volume you want to use with Netatalk:
+
+        aclinherit = passthrough
+        aclmode = passthrough
+
+     For an explanation of what these parameters mean and how to apply them see, your hosts
+     ZFS documentation (e.g. man zfs).
+
+  2. Authentication Domain
+
+     Your server and the clients must be part of a security association where identity data
+     is coming from a common source. ACLs in Darwin are based on UUIDs and so is the ACL
+     specification in AFP 3.2. Therefor your source of identity data has to provide an
+     attribute for every user and group where a UUID is stored as a ASCII string.
+
+     In other words:
+      - you need an Open Directory Server or an LDAP server where you store UUIDs in some
+       attribute
+      - your clients must be configured to use this server 
+      - your server should be configured to use this server via nsswitch and PAM. This
+       however is not a strict requirement: 
+       if you create duplicates of every LDAP/OD user and group with identic attributes
+       (name, uid, gid) in your local data store (/etc/[passwd|group]) things will work
+
+             *  as long as user/group names/ids in the filesystem are equal  *
+             *         to their counterparts in the LDAP/OD datastore        *
+
+      - configure Netatalk via ldap.conf so that Netatalk is able to retrieve the UUID
+       for users and groups via LDAP search queries
+
+  3. Netatalk Volumes
+
+     Finally you can add "options:acls" to your volume defintions to add ACL support.
+     In case your volume basedir doesn't grant read permissions via mode (like: 0700 root:adm)
+     but only via ACLs, you MUST add the "nostat" option to the volume defintion.
+
+  Implemantation Notes
+  --------------------
+
+Some implementation details that are buried in the code are worthwhile to be documented.
+
+1. Darwin ACEs vs NFSv4 ACEs
+2. .AppleDouble VFS integration
+
+  1. Darwin ACEs vs NFSv4 ACEs
+
+     Basically as far as implementing AFP support is concerned they're equivalent.
+     Subtleties arise at other places:
+
+     FPAccess
+
+       The AFP client frequently checks the (DARWIN_)ACE_DELETE_CHILD right. This is most
+       often not explicitly granted via an ACE. Therefor the client would get an no access
+        error. The client in turn then declares the object in question read only.
+       Thus we have to the check the mode for every directory and add ACE_DELETE_CHILD if
+       the requestor has write permissions.
+
+     FPGetFileDirParms
+
+       10.5 does not only use unix mode and FPAccess for permission check, but also OS 9
+       access bits from FPGetFileDirParms. Thus we have to adjust the Access Rights bitmap
+       user bits by including any ACL rigths.
+
+  2. .AppleDouble VFS integration
+
+     FPSetACL sets ACLs on files and dirs. Our implementation also sets the same ACL on the
+     .AppleDouble file for files and on the .AppleDouble dir itself for dirs.
+
+     Thereafter ACLs for created files is taken care of by ACLs own inheritance rules.
+
+     For dirs on the other hand whe have to make sure that any ACL the dir inherits is
+     copied verbatim to its .AppleDouble dir. 
+
+
+                                                                    January 2009, Frank Lahm
\ No newline at end of file
index 764b8475e77a73c4741c61fdb2eca39b6c089ba4..a893b36c665d9a3b86b4cbe9d02ad5f90d5f912e 100644 (file)
@@ -10,12 +10,16 @@ afpd_SOURCES = unix.c ofork.c main.c switch.c auth.c volume.c directory.c \
         afp_config.c nfsquota.c quota.c uam.c afs.c uid.c afp_util.c \
          catsearch.c afprun.c vfs_adouble.c hash.c
 
+if USE_NFSv4_ACLS
+afpd_SOURCES += acls.c
+endif
+
 afpd_LDADD =  $(top_builddir)/libatalk/cnid/libcnid.la $(top_builddir)/libatalk/libatalk.la
 afpd_LDFLAGS = -export-dynamic
 
 noinst_HEADERS = auth.h afp_config.h desktop.h directory.h file.h \
         filedir.h fork.h globals.h icon.h mangle.h misc.h status.h switch.h \
-        uam_auth.h uid.h unix.h volume.h afp_vfs.h hash.h
+        uam_auth.h uid.h unix.h volume.h afp_vfs.h hash.h acls.h
 
 LIBS = @LIBS@ @QUOTA_LIBS@ @SLP_LIBS@ @WRAP_LIBS@ @LIBADD_DL@
 
@@ -26,5 +30,6 @@ AM_CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/sys \
         -D_PATH_AFPDPWFILE=\"$(pkgconfdir)/afppasswd\" \
         -D_PATH_AFPDCONF=\"$(pkgconfdir)/afpd.conf\" \
         -D_PATH_AFPDUAMPATH=\"$(UAMS_PATH)/\" \
+        -D_PATH_ACL_LDAPCONF=\"$(pkgconfdir)/ldap.conf\" \
         -DAPPLCNAME \
         -DSERVERTEXT=\"$(SERVERTEXT)/\"
diff --git a/etc/afpd/acl_mappings.h b/etc/afpd/acl_mappings.h
new file mode 100644 (file)
index 0000000..d1ddbc1
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+   $Id: acl_mappings.h,v 1.1 2009-02-02 11:55:00 franklahm Exp $
+   Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+ */
+
+#ifndef ACL_MAPPINGS
+#define ACL_MAPPINGS
+
+#include <sys/acl.h>
+#include "acls.h"
+
+/* 
+ * Stuff for mapping between ACL implementations
+ */
+
+struct ace_rights_map {
+    u_int32_t from;
+    u_int32_t to;
+};
+
+struct ace_rights_map nfsv4_to_darwin_rights[] = {
+    {ACE_READ_DATA,         DARWIN_ACE_READ_DATA},
+    {ACE_WRITE_DATA,        DARWIN_ACE_WRITE_DATA},
+    {ACE_APPEND_DATA,       DARWIN_ACE_APPEND_DATA},
+    {ACE_READ_NAMED_ATTRS,  DARWIN_ACE_READ_EXTATTRIBUTES},
+    {ACE_WRITE_NAMED_ATTRS, DARWIN_ACE_WRITE_EXTATTRIBUTES},
+    {ACE_EXECUTE,           DARWIN_ACE_EXECUTE},
+    {ACE_DELETE_CHILD,      DARWIN_ACE_DELETE_CHILD},
+    {ACE_READ_ATTRIBUTES,   DARWIN_ACE_READ_ATTRIBUTES},
+    {ACE_WRITE_ATTRIBUTES,  DARWIN_ACE_WRITE_ATTRIBUTES},
+    {ACE_DELETE,            DARWIN_ACE_DELETE},
+    {ACE_READ_ACL,          DARWIN_ACE_READ_SECURITY},
+    {ACE_WRITE_ACL,         DARWIN_ACE_WRITE_SECURITY},
+    {ACE_WRITE_OWNER,       DARWIN_ACE_TAKE_OWNERSHIP},
+    {0,0}
+};
+
+struct ace_rights_map darwin_to_nfsv4_rights[] = {
+    {DARWIN_ACE_READ_DATA,           ACE_READ_DATA},
+    {DARWIN_ACE_WRITE_DATA,          ACE_WRITE_DATA},
+    {DARWIN_ACE_APPEND_DATA,         ACE_APPEND_DATA},
+    {DARWIN_ACE_READ_EXTATTRIBUTES,  ACE_READ_NAMED_ATTRS},
+    {DARWIN_ACE_WRITE_EXTATTRIBUTES, ACE_WRITE_NAMED_ATTRS},
+    {DARWIN_ACE_EXECUTE,             ACE_EXECUTE},
+    {DARWIN_ACE_DELETE_CHILD,        ACE_DELETE_CHILD},
+    {DARWIN_ACE_READ_ATTRIBUTES,     ACE_READ_ATTRIBUTES},
+    {DARWIN_ACE_WRITE_ATTRIBUTES,    ACE_WRITE_ATTRIBUTES},
+    {DARWIN_ACE_DELETE,              ACE_DELETE},
+    {DARWIN_ACE_READ_SECURITY,       ACE_READ_ACL},
+    {DARWIN_ACE_WRITE_SECURITY,      ACE_WRITE_ACL},
+    {DARWIN_ACE_TAKE_OWNERSHIP,      ACE_WRITE_OWNER},
+    {0,0}
+};
+
+struct nfsv4_to_darwin_flags_map {
+    u_int16_t from;
+    u_int32_t to;
+};
+
+struct nfsv4_to_darwin_flags_map nfsv4_to_darwin_flags[] = {
+    {ACE_FILE_INHERIT_ACE,         DARWIN_ACE_FLAGS_FILE_INHERIT},
+    {ACE_DIRECTORY_INHERIT_ACE,    DARWIN_ACE_FLAGS_DIRECTORY_INHERIT},
+    {ACE_NO_PROPAGATE_INHERIT_ACE, DARWIN_ACE_FLAGS_LIMIT_INHERIT},
+    {ACE_INHERIT_ONLY_ACE,         DARWIN_ACE_FLAGS_ONLY_INHERIT},
+    {ACE_INHERITED_ACE,            DARWIN_ACE_FLAGS_INHERITED},
+    {0,0}
+};
+
+struct darwin_to_nfsv4_flags_map {
+    u_int32_t from;
+    u_int16_t to;
+};
+
+struct darwin_to_nfsv4_flags_map darwin_to_nfsv4_flags[] = {
+    {DARWIN_ACE_FLAGS_FILE_INHERIT,      ACE_FILE_INHERIT_ACE},
+    {DARWIN_ACE_FLAGS_DIRECTORY_INHERIT, ACE_DIRECTORY_INHERIT_ACE},
+    {DARWIN_ACE_FLAGS_LIMIT_INHERIT,     ACE_NO_PROPAGATE_INHERIT_ACE},
+    {DARWIN_ACE_FLAGS_ONLY_INHERIT,      ACE_INHERIT_ONLY_ACE},
+    {DARWIN_ACE_FLAGS_INHERITED,         ACE_INHERITED_ACE},
+    {0,0}
+};
+
+#endif /* ACL_MAPPINGS */
diff --git a/etc/afpd/acls.c b/etc/afpd/acls.c
new file mode 100644 (file)
index 0000000..00e82f3
--- /dev/null
@@ -0,0 +1,1165 @@
+/*
+   $Id: acls.c,v 1.1 2009-02-02 11:55:00 franklahm Exp $
+   Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <grp.h>
+#include <pwd.h>
+#include <errno.h>
+#include <sys/acl.h>
+
+#include <atalk/adouble.h>
+#include <atalk/afp.h>
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/uuid.h>
+
+#include "directory.h"
+#include "desktop.h"
+#include "volume.h"
+#include "fork.h"
+
+#include "acls.h"
+#include "acl_mappings.h"
+
+/* for map_acl() */
+#define SOLARIS_2_DARWIN 1
+#define DARWIN_2_SOLARIS 2
+
+/********************************************************
+ * Basic and helper funcs
+ ********************************************************/
+
+static void hexdump(char *o, void *buf, size_t l) {
+    int count = 0, len;
+    unsigned char *p = (unsigned char *)buf;
+
+    while (l--) {
+        if ((count % 16) == 0) {
+            len = sprintf(o, "\n%p: ", p);
+           o += len;
+       }
+       len = sprintf(o, "%02x ", *p);
+       o += len;
+       count++;
+        p++;
+    }
+    sprintf(o, "\n");
+}
+
+
+/* Get ACL. Allocates storage as needed. Caller must free.
+ * Returns no of ACEs or -1 on error.  */
+static int get_nfsv4_acl(const char *name, ace_t **retAces)
+{
+    int ace_count = -1;
+    ace_t *aces;
+
+    *retAces = NULL;
+    ace_count = acl(name, ACE_GETACLCNT, 0, NULL);
+    if (ace_count <= 0) {
+       LOG(log_error, logtype_afpd, "get_nfsv4_acl: acl(ACE_GETACLCNT) error");
+        return -1;
+    }
+
+    aces = malloc(ace_count * sizeof(ace_t));
+    if (aces == NULL) {
+       LOG(log_error, logtype_afpd, "get_nfsv4_acl: malloc error");
+       return -1;
+    }
+
+    if ( (acl(name, ACE_GETACL, ace_count, aces)) == -1 ) {
+       LOG(log_error, logtype_afpd, "get_nfsv4_acl: acl(ACE_GETACL) error");
+       free(aces);
+       return -1;
+    }
+
+    LOG(log_debug9, logtype_afpd, "get_nfsv4_acl: file: %s -> No. of ACEs: %d", name, ace_count);
+    *retAces = aces;
+
+    return ace_count;
+}
+
+/*
+  Takes a users name, uid and primary gid and checks if user is member of any group
+    Returns -1 if no or error, 0 if yes
+*/
+static int check_group(char *name, uid_t uid, gid_t pgid, gid_t path_gid)
+{
+    int i;
+    struct group *grp; 
+
+    if (pgid == path_gid)
+       return 0;
+
+    grp = getgrgid(path_gid);
+    if (!grp)
+       return -1;
+
+    i = 0;
+    while (grp->gr_mem[i] != NULL) {
+       if ( (strcmp(grp->gr_mem[i], name)) == 0 ) {
+           LOG(log_debug, logtype_afpd, "check_group: requested user:%s is member of: %s", name, grp->gr_name);
+           return 0;
+       }
+       i++;
+    }
+    
+    return -1;
+}
+
+/* 
+   Remove any trivial ACE "in-place". Returns no of non-trivial ACEs
+*/
+static int strip_trivial_aces(ace_t **saces, int sacecount)
+{
+    int i,j;
+    int nontrivaces = 0;
+    ace_t *aces = *saces;
+    ace_t *new_aces;
+
+    /* Count non-trivial ACEs */
+    for (i=0; i < sacecount; ) {
+       if ( ! (aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)))
+           nontrivaces++;
+       i++;
+    }
+    /* malloc buffer for new ACL */
+    if ((new_aces = malloc(nontrivaces * sizeof(ace_t))) == NULL) {
+       LOG(log_error, logtype_afpd, "strip_trivial_aces: malloc %s", strerror(errno));
+       return -1;
+    }
+
+    /* Copy non-trivial ACEs */
+    for (i=0, j=0; i < sacecount; ) {
+       if ( ! (aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE))) {
+           memcpy(&new_aces[j], &aces[i], sizeof(ace_t));
+           j++;
+       }
+       i++;
+    }
+
+    free(aces);
+    *saces = new_aces;
+
+    LOG(log_debug7, logtype_afpd, "strip_trivial_aces: non-trivial ACEs: %d", nontrivaces);
+
+    return nontrivaces;
+}
+
+/* 
+   Remove non-trivial ACEs "in-place". Returns no of trivial ACEs.
+ */
+static int strip_nontrivial_aces(ace_t **saces, int sacecount)
+{
+    int i,j;
+    int trivaces = 0;
+    ace_t *aces = *saces;
+    ace_t *new_aces;
+
+    /* Count trivial ACEs */
+    for (i=0; i < sacecount; ) {
+       if ((aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)))
+           trivaces++;
+       i++;
+    }
+    /* malloc buffer for new ACL */
+    if ((new_aces = malloc(trivaces * sizeof(ace_t))) == NULL) {
+       LOG(log_error, logtype_afpd, "strip_nontrivial_aces: malloc %s", strerror(errno));
+       return -1;
+    }
+
+    /* Copy trivial ACEs */
+    for (i=0, j=0; i < sacecount; ) {
+       if ((aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE))) {
+           memcpy(&new_aces[j], &aces[i], sizeof(ace_t));
+           j++;
+       }
+       i++;
+    }
+    /* Free old ACEs */
+    free(aces);
+    *saces = new_aces;
+
+    LOG(log_debug7, logtype_afpd, "strip_nontrivial_aces: trivial ACEs: %d", trivaces);
+
+    return trivaces;
+}
+
+/* 
+   Concatenate ACEs
+ */
+static ace_t *concat_aces(ace_t *aces1, int ace1count, ace_t *aces2, int ace2count)
+{
+    ace_t *new_aces;
+    int i, j;
+
+    /* malloc buffer for new ACL */
+    if ((new_aces = malloc((ace1count + ace2count) * sizeof(ace_t))) == NULL) {
+       LOG(log_error, logtype_afpd, "combine_aces: malloc %s", strerror(errno));
+       return NULL;
+    }
+
+    /* Copy ACEs from buf1 */
+    for (i=0; i < ace1count; ) {
+       memcpy(&new_aces[i], &aces1[i], sizeof(ace_t));
+       i++;
+    }
+
+    j = i;
+
+    /* Copy ACEs from buf2 */
+    for (i=0; i < ace2count; ) {
+       memcpy(&new_aces[j], &aces2[i], sizeof(ace_t));
+       i++;
+       j++;
+    }
+    return new_aces;
+}
+
+
+/* 
+   Maps ACE array from Solaris to Darwin. Darwin ACEs are stored in network byte order.
+   Return numer of mapped ACEs or -1 on error.
+   All errors while mapping (e.g. getting UUIDs from LDAP) are fatal.
+ */
+static int map_aces_solaris_to_darwin(ace_t *aces, darwin_ace_t *darwin_aces, int ace_count)
+{
+    int i, count = 0;
+    uint32_t flags;
+    uint32_t rights;
+    struct passwd *pwd = NULL;
+    struct group *grp = NULL;
+
+    LOG(log_debug7, logtype_afpd, "map_aces_solaris_to_darwin: parsing %d ACES", ace_count);
+
+    while(ace_count--) {
+       LOG(log_debug7, logtype_afpd, "map_aces_solaris_to_darwin: parsing ACE No. %d", ace_count + 1);
+       /* if its a ACE resulting from nfsv4 mode mapping, discard it */
+       if (aces->a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)) {
+           LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: trivial ACE");
+           aces++;
+           continue;
+       }
+
+       if ( ! (aces->a_flags & ACE_IDENTIFIER_GROUP) ) {
+           /* its a user ace */
+           LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: found user ACE with uid: %d", aces->a_who);
+           pwd = getpwuid(aces->a_who);
+           if (!pwd) {
+               LOG(log_error, logtype_afpd, "map_aces_solaris_to_darwin: getpwuid error: %s", strerror(errno));
+               return -1;
+           }
+           LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: uid: %d -> name: %s", aces->a_who, pwd->pw_name); 
+           if ( (getuuidfromname(pwd->pw_name, UUID_USER, darwin_aces->darwin_ace_uuid)) != 0) {
+               LOG(log_error, logtype_afpd, "map_aces_solaris_to_darwin: getuuidfromname error");
+               return -1;
+           }
+       } else { 
+           /* its a group ace */
+           LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: found group ACE with gid: %d", aces->a_who);
+           grp = getgrgid(aces->a_who);
+           if (!grp) {
+               LOG(log_error, logtype_afpd, "map_aces_solaris_to_darwin: getgrgid error: %s", strerror(errno));
+               return -1;
+           }
+           LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: gid: %d -> name: %s", aces->a_who, grp->gr_name);
+           if ( (getuuidfromname(grp->gr_name, UUID_GROUP, darwin_aces->darwin_ace_uuid)) != 0) {
+               LOG(log_error, logtype_afpd, "map_aces_solaris_to_darwin: getuuidfromname error");
+               return -1;
+           }
+       }
+
+       /* map flags */
+       if (aces->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE)
+           flags = DARWIN_ACE_FLAGS_PERMIT;
+       else if (aces->a_type == ACE_ACCESS_DENIED_ACE_TYPE)
+           flags = DARWIN_ACE_FLAGS_DENY;
+       else {                  /* unsupported type */
+           aces++;
+           continue;
+       }
+       for(i=0; nfsv4_to_darwin_flags[i].from != 0; i++) {
+           if (aces->a_flags & nfsv4_to_darwin_flags[i].from)
+               flags |= nfsv4_to_darwin_flags[i].to;
+       }
+       darwin_aces->darwin_ace_flags = htonl(flags);
+
+       /* map rights */
+       rights = 0;
+       for (i=0; nfsv4_to_darwin_rights[i].from != 0; i++) {
+           if (aces->a_access_mask & nfsv4_to_darwin_rights[i].from)
+               rights |= nfsv4_to_darwin_rights[i].to;
+       }
+       darwin_aces->darwin_ace_rights = htonl(rights);
+
+       count++;
+       aces++;
+       darwin_aces++;
+    }
+
+    return count;
+}
+
+/* 
+   Maps ACE array from Darwin to Solaris. Darwin ACEs are expected in network byte order.
+   Return numer of mapped ACEs or -1 on error.
+   All errors while mapping (e.g. getting UUIDs from LDAP) are fatal.
+ */
+int map_aces_darwin_to_solaris(darwin_ace_t *darwin_aces, ace_t *nfsv4_aces, int ace_count)
+{
+    int i, mapped_aces = 0;
+    uint32_t darwin_ace_flags;
+    uint32_t darwin_ace_rights;
+    uint16_t nfsv4_ace_flags;
+    uint32_t nfsv4_ace_rights;
+    char *name;
+    uuidtype_t uuidtype;
+    struct passwd *pwd;
+    struct group *grp;
+
+    while(ace_count--) {
+       nfsv4_ace_flags = 0;
+       nfsv4_ace_rights = 0;
+
+       /* uid/gid first */
+       if ( (getnamefromuuid(darwin_aces->darwin_ace_uuid, &name, &uuidtype)) != 0)
+           return -1;
+       if (uuidtype == UUID_USER) {
+           pwd = getpwnam(name);
+           if (!pwd) {
+               LOG(log_error, logtype_afpd, "map_aces_darwin_to_solaris: getpwnam: %s", strerror(errno));
+               return -1;
+           }
+           nfsv4_aces->a_who = pwd->pw_uid;
+       } else { /* hopefully UUID_GROUP*/
+           grp = getgrnam(name);
+           if (!grp) {
+               LOG(log_error, logtype_afpd, "map_aces_darwin_to_solaris: getgrnam: %s", strerror(errno));
+               return -1;
+           }
+           nfsv4_aces->a_who = (uid_t)(grp->gr_gid);
+           nfsv4_ace_flags |= ACE_IDENTIFIER_GROUP;
+       }
+       free(name);
+       name = NULL;
+
+       /* now type: allow/deny */
+       darwin_ace_flags = ntohl(darwin_aces->darwin_ace_flags);
+       if (darwin_ace_flags & DARWIN_ACE_FLAGS_PERMIT)
+           nfsv4_aces->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+       else if (darwin_ace_flags & DARWIN_ACE_FLAGS_DENY)
+           nfsv4_aces->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
+       else { /* unsupported type */
+           darwin_aces++;
+           continue;
+       }
+       /* map flags */
+       for(i=0; darwin_to_nfsv4_flags[i].from != 0; i++) {
+           if (darwin_ace_flags & darwin_to_nfsv4_flags[i].from)
+               nfsv4_ace_flags |= darwin_to_nfsv4_flags[i].to;
+       }
+
+       /* map rights */
+       darwin_ace_rights = ntohl(darwin_aces->darwin_ace_rights);
+       for (i=0; darwin_to_nfsv4_rights[i].from != 0; i++) {
+           if (darwin_ace_rights & darwin_to_nfsv4_rights[i].from)
+               nfsv4_ace_rights |= darwin_to_nfsv4_rights[i].to;
+       }
+
+       LOG(log_debug9, logtype_afpd, "map_aces_darwin_to_solaris: ACE flags: Darwin:%08x -> NFSv4:%04x", darwin_ace_flags, nfsv4_ace_flags);
+       LOG(log_debug9, logtype_afpd, "map_aces_darwin_to_solaris: ACE rights: Darwin:%08x -> NFSv4:%08x", darwin_ace_rights, nfsv4_ace_rights);
+
+       nfsv4_aces->a_flags = nfsv4_ace_flags;
+       nfsv4_aces->a_access_mask = nfsv4_ace_rights;
+
+       mapped_aces++;
+       darwin_aces++;
+       nfsv4_aces++;
+    }
+
+    return mapped_aces;
+}
+
+/********************************************************
+ * 2nd level funcs
+ ********************************************************/
+
+/*  Map between ACL styles (SOLARIS_2_DARWIN, DARWIN_2_SOLARIS).
+    Reads from 'aces' buffer, writes to 'rbuf' buffer.
+    Caller must provide buffer.
+    Darwin ACEs are read and written in network byte order.
+    Needs to know how many ACEs are in the ACL (ace_count). Ignores trivial ACEs.
+    Return no of mapped ACEs or -1 on error. */
+static int map_acl(int type, ace_t *nfsv4_aces, darwin_ace_t *buf, int ace_count)
+{
+    int mapped_aces;
+
+    LOG(log_debug9, logtype_afpd, "map_acl: BEGIN");
+    
+    switch (type) {
+    case SOLARIS_2_DARWIN:
+       mapped_aces = map_aces_solaris_to_darwin( nfsv4_aces, buf, ace_count);
+       break;
+
+    case DARWIN_2_SOLARIS:
+       mapped_aces = map_aces_darwin_to_solaris( buf, nfsv4_aces, ace_count);
+       break;
+
+    default:
+       mapped_aces = -1;
+       break;
+    }
+
+    LOG(log_debug9, logtype_afpd, "map_acl: END");
+    return mapped_aces;
+}
+
+/********************************************************
+ * 1st level funcs
+ ********************************************************/
+
+
+/* Get ACL from object omitting trivial ACEs. Map to Darwin ACL style and 
+   store Darwin ACL at rbuf. Add length of ACL written to rbuf to *rbuflen.
+   Returns 0 on success, -1 on error. */
+static int get_and_map_acl(char *name, char *rbuf, int *rbuflen)
+{
+    int ace_count, mapped_aces, err;
+    ace_t *aces;
+    uint32_t *darwin_ace_count = (u_int32_t *)rbuf;
+
+    LOG(log_debug9, logtype_afpd, "get_and_map_acl: BEGIN");
+
+    /* Skip length and flags */
+    rbuf += 4;
+    *rbuf = 0;
+    rbuf += 4;
+
+    if ( (ace_count = get_nfsv4_acl(name, &aces)) == -1) {
+       LOG(log_error, logtype_afpd, "get_and_map_acl: couldnt get ACL");
+       return -1;
+    }
+
+    if ( (mapped_aces = map_acl(SOLARIS_2_DARWIN, aces, (darwin_ace_t *)rbuf, ace_count)) == -1) {
+       err = -1;
+       goto cleanup;
+    }
+    LOG(log_debug, logtype_afpd, "get_and_map_acl: mapped %d ACEs", mapped_aces);
+
+    err = 0;
+    *darwin_ace_count = htonl(mapped_aces);
+    *rbuflen += sizeof(darwin_acl_header_t) + (mapped_aces * sizeof(darwin_ace_t));
+
+cleanup:    
+    free(aces);
+
+    LOG(log_debug9, logtype_afpd, "get_and_map_acl: END");    
+    return err;
+}
+
+/* Removes all non-trivial ACLs from object. Returns full AFPERR code. */
+int remove_acl(const char *name)
+{
+    int ret,i, ace_count, trivial_aces, new_aces_count;
+    ace_t *old_aces = NULL;
+    ace_t *new_aces = NULL;
+
+    LOG(log_debug9, logtype_afpd, "remove_acl: BEGIN");
+
+    /* Get existing ACL and count trivial ACEs */
+    if ((ace_count = get_nfsv4_acl(name, &old_aces)) == -1)
+       return AFPERR_MISC;
+    trivial_aces = 0;
+    for ( i=0; i < ace_count; i++) {
+       if (old_aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE))
+           trivial_aces++;
+    }
+
+    /* malloc buffer for new ACL */
+    if ((new_aces = malloc(trivial_aces * sizeof(ace_t))) == NULL) {
+       LOG(log_error, logtype_afpd, "remove_acl: malloc %s", strerror(errno));
+       ret = AFPERR_MISC;
+       goto exit;
+    }
+
+    /* Now copy the trivial ACEs */
+    new_aces_count = 0;
+    for (i=0; i < ace_count; i++) {
+       if (old_aces[i].a_flags  & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)) {
+           memcpy(&new_aces[new_aces_count], &old_aces[i], sizeof(ace_t));
+           new_aces_count++;
+       }
+    }
+    
+    if ( (acl(name, ACE_SETACL, trivial_aces, new_aces)) == 0)
+       ret = AFP_OK;
+    else {
+       LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
+       if (errno == (EACCES | EPERM))
+           ret = AFPERR_ACCESS;
+       else if (errno == ENOENT)
+           ret = AFPERR_NOITEM;
+       else
+           ret = AFPERR_MISC;
+    }
+
+exit:
+    free(old_aces);
+    free(new_aces);
+
+    LOG(log_debug9, logtype_afpd, "remove_acl: END");
+    return ret;
+}
+
+/* Removes all non-trivial ACLs from object. Returns full AFPERR code. */
+static int remove_acl_vfs(const struct vol *vol,const char *path, int dir)
+{
+    int ret;
+
+    /* Ressource etc. first */
+    if ((ret = vol->vfs->rf_remove_acl(vol, path, dir)) != AFP_OK)
+       return ret;
+    /* now the data fork or dir */
+    return (remove_acl(path));
+}
+
+/* 
+   Set ACL. Subtleties:
+   - the client sends a complete list of ACEs, not only new ones. So we dont need to do
+     any combination business (one exception being 'kFileSec_Inherit': see next)
+   - client might request that we add inherited ACEs via 'kFileSec_Inherit'.
+     We will store inherited ACEs first, which is Darwins canonical order.
+   - returns AFPerror code
+*/
+static int set_acl_vfs(const struct vol *vol, char *name, int inherit, char *ibuf)
+{
+    int ret, i, nfsv4_ace_count, tocopy_aces_count = 0, new_aces_count = 0, trivial_ace_count = 0;
+    ace_t *old_aces, *new_aces = NULL;
+    uint16_t flags;
+    uint32_t ace_count;
+
+    LOG(log_debug9, logtype_afpd, "set_acl: BEGIN");
+
+    /*  Get no of ACEs the client put on the wire */
+    ace_count = htonl(*((uint32_t *)ibuf));
+    ibuf += 8;                 /* skip ACL flags (see acls.h) */
+
+    if (inherit)
+       /* inherited + trivial ACEs */
+       flags = ACE_INHERITED_ACE | ACE_OWNER | ACE_GROUP | ACE_EVERYONE;
+    else
+       /* only trivial ACEs */
+       flags = ACE_OWNER | ACE_GROUP | ACE_EVERYONE;
+
+    /* Get existing ACL and count ACEs which have to be copied */
+    if ((nfsv4_ace_count = get_nfsv4_acl(name, &old_aces)) == -1)
+       return AFPERR_MISC;
+    for ( i=0; i < nfsv4_ace_count; i++) {
+       if (old_aces[i].a_flags & flags)
+           tocopy_aces_count++;
+    }
+
+    /* Now malloc buffer exactly sized to fit all new ACEs */
+    new_aces = malloc( (ace_count + tocopy_aces_count) * sizeof(ace_t) );
+    if (new_aces == NULL) {
+       LOG(log_error, logtype_afpd, "set_acl: malloc %s", strerror(errno));
+       ret = AFPERR_MISC;
+       goto cleanup;
+    }
+
+    /* Start building new ACL */
+
+    /* Copy local inherited ACEs. Therefore we have 'Darwin canonical order' (see chmod there):
+       inherited ACEs first. */
+    if (inherit) {
+       for (i=0; i < nfsv4_ace_count; i++) {
+           if (old_aces[i].a_flags & ACE_INHERITED_ACE) {
+               memcpy(&new_aces[new_aces_count], &old_aces[i], sizeof(ace_t));
+               new_aces_count++;
+           }
+       }
+    }
+    LOG(log_debug7, logtype_afpd, "set_acl: copied %d inherited ACEs", new_aces_count);
+
+    /* Now the ACEs from the client */
+    ret = map_acl(DARWIN_2_SOLARIS, &new_aces[new_aces_count], (darwin_ace_t *)ibuf, ace_count);
+    if (ret == -1) {
+       ret = AFPERR_PARAM;
+       goto cleanup;
+    }
+    new_aces_count += ace_count;
+    LOG(log_debug7, logtype_afpd, "set_acl: mapped %d ACEs from client", ace_count);
+
+    /* Now copy the trivial ACEs */
+    for (i=0; i < nfsv4_ace_count; i++) {
+       if (old_aces[i].a_flags  & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)) {
+           memcpy(&new_aces[new_aces_count], &old_aces[i], sizeof(ace_t));
+           new_aces_count++;
+           trivial_ace_count++;
+       }
+    }
+    LOG(log_debug7, logtype_afpd, "set_acl: copied %d trivial ACEs", trivial_ace_count);
+
+    /* Ressourcefork first.
+       Note: for dirs we set the same ACL on the .AppleDouble/.Parent _file_. This
+       might be strange for ACE_DELETE_CHILD and for inheritance flags. */
+    if ( (ret = vol->vfs->rf_acl(vol, name, ACE_SETACL, new_aces_count, new_aces)) != 0) {
+       LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
+       if (errno == (EACCES | EPERM))
+           ret = AFPERR_ACCESS;
+       else if (errno == ENOENT)
+           ret = AFPERR_NOITEM;
+       else
+           ret = AFPERR_MISC;
+       goto cleanup;
+    }
+    if ( (ret = acl(name, ACE_SETACL, new_aces_count, new_aces)) != 0) {
+       LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
+       if (errno == (EACCES | EPERM))
+           ret = AFPERR_ACCESS;
+       else if (errno == ENOENT)
+           ret = AFPERR_NOITEM;
+       else
+           ret = AFPERR_MISC;
+       goto cleanup;
+    }
+
+    ret = AFP_OK;
+
+cleanup:
+    free(old_aces);
+    free(new_aces);
+
+    LOG(log_debug9, logtype_afpd, "set_acl: END");
+    return ret;
+}
+
+/* 
+   Checks if a given UUID has requested_rights(type darwin_ace_rights) for path.
+   Note: this gets called frequently and is a good place for optimizations !
+ */
+static int check_acl_access(const char *path, const uuidp_t uuid, uint32_t requested_darwin_rights)
+{
+    int                 ret, i, ace_count, dir, checkgroup;
+    char                *username; /* might be group too */
+    uuidtype_t          uuidtype;
+    uid_t               uid;
+    gid_t               pgid;
+    uint32_t            requested_rights = 0, allowed_rights = 0, denied_rights = 0;
+    ace_t               *aces;
+    struct passwd       *pwd;
+    struct stat         st;
+    int                 check_user_trivace = 0, check_group_trivace = 0;
+    uid_t               who;
+    uint16_t            flags;
+    uint16_t            type;
+    uint32_t            rights;
+
+#ifdef DEBUG
+    LOG(log_debug9, logtype_afpd, "check_access: BEGIN. Request: %08x", requested_darwin_rights);
+#endif
+    /* Get uid or gid from UUID */
+    if ( (getnamefromuuid(uuid, &username, &uuidtype)) != 0) {
+       LOG(log_error, logtype_afpd, "check_access: error getting name from UUID");     
+       return AFPERR_PARAM;
+    }
+
+    /* File or dir */
+    if ((stat(path, &st)) != 0) {
+       LOG(log_error, logtype_afpd, "check_access: stat: %s", strerror(errno));
+       ret = AFPERR_PARAM;
+       goto exit;
+    }
+    dir = S_ISDIR(st.st_mode);
+
+    if (uuidtype == UUID_USER) {
+       pwd = getpwnam(username);
+       if (!pwd) {
+           LOG(log_error, logtype_afpd, "check_access: getpwnam: %s", strerror(errno));
+           ret = AFPERR_MISC;
+           goto exit;
+       }
+       uid = pwd->pw_uid;
+       pgid = pwd->pw_gid;
+
+       /* If user is file/dir owner we must check the user trivial ACE */
+       if (uid == st.st_uid) {
+           LOG(log_debug, logtype_afpd, "check_access: user: %s is files owner. Must check trivial user ACE", username);
+           check_user_trivace = 1;
+       }
+
+       /* Now check if requested user is files owning group. If yes we must check the group trivial ACE */
+       if ( (check_group(username, uid, pgid, st.st_gid)) == 0) {
+           LOG(log_debug, logtype_afpd, "check_access: user: %s is in group: %d. Must check trivial group ACE", username, st.st_gid);
+           check_group_trivace = 1;
+       }
+    } else { /* hopefully UUID_GROUP*/
+           LOG(log_error, logtype_afpd, "check_access: afp_access for UUID of groups not supported!");
+#if 0
+       grp = getgrnam(username);
+       if (!grp) {
+           LOG(log_error, logtype_afpd, "check_access: getgrnam: %s", strerror(errno));
+           return -1;
+       }
+       if (st.st_gid == grp->gr_gid )
+           check_group_trivace = 1;
+#endif
+    }
+
+    /* Map requested rights to Solaris style. */
+    for (i=0; darwin_to_nfsv4_rights[i].from != 0; i++) {
+       if (requested_darwin_rights & darwin_to_nfsv4_rights[i].from)
+           requested_rights |= darwin_to_nfsv4_rights[i].to;
+    }
+
+    /* Get ACL from file/dir */
+    if ( (ace_count = get_nfsv4_acl(path, &aces)) == -1) {
+       LOG(log_error, logtype_afpd, "check_access: error getting ACEs");
+       ret = AFPERR_MISC;
+       goto exit;
+    }
+    /* Now check requested rights */
+    ret = AFPERR_ACCESS;
+    i = 0;
+    do { /* Loop through ACEs */
+       who = aces[i].a_who;
+       flags = aces[i].a_flags;
+       type = aces[i].a_type;
+       rights = aces[i].a_access_mask;
+       
+       if (flags & ACE_INHERIT_ONLY_ACE)
+           continue;
+
+       /* Check if its a group ACE and set checkgroup to 1 if yes */
+       checkgroup = 0;
+       if ( (flags & ACE_IDENTIFIER_GROUP) && !(flags & ACE_GROUP) ) {
+           if ( (check_group(username, uid, pgid, who)) == 0)
+               checkgroup = 1;
+           else
+               continue;
+       }
+
+       /* Now the tricky part: decide if ACE effects our user. I'll explain:
+          if its a dedicated (non trivial) ACE for the user
+            OR
+          if its a ACE for a group we're member of
+            OR
+          if its a trivial ACE_OWNER ACE and requested UUID is the owner
+            OR
+          if its a trivial ACE_GROUP ACE and requested UUID is group
+            OR
+          if its a trivial ACE_EVERYONE ACE
+            THEN
+          process ACE */
+       if (
+           ( (who == uid) && !(flags & (ACE_TRIVIAL|ACE_IDENTIFIER_GROUP)) ) ||
+           (checkgroup) ||
+           ( (flags & ACE_OWNER) && check_user_trivace ) ||
+           ( (flags & ACE_GROUP) && check_group_trivace ) ||
+           ( flags & ACE_EVERYONE )
+          ) {
+           /* Found an applicable ACE */
+           if (type == ACE_ACCESS_ALLOWED_ACE_TYPE)
+               allowed_rights |= rights;
+           else if (type == ACE_ACCESS_DENIED_ACE_TYPE)
+               /* Only or to denied rights if not previously allowed !! */
+               denied_rights |= ((!allowed_rights) & rights);
+       }
+    } while (++i < ace_count);
+
+
+    /* Darwin likes to ask for "delete_child" on dir,
+       "write_data" is actually the same, so we add that for dirs */
+    if (dir && (allowed_rights & ACE_WRITE_DATA))
+       allowed_rights |= ACE_DELETE_CHILD;
+
+    if (requested_rights & denied_rights) {
+       LOG(log_debug, logtype_afpd, "check_access: some requested right was denied:");
+       ret = AFPERR_ACCESS;
+    } else if ((requested_rights & allowed_rights) != requested_rights) {
+       LOG(log_debug, logtype_afpd, "check_access: some requested right wasn't allowed:");
+       ret = AFPERR_ACCESS;
+    } else {
+       LOG(log_debug, logtype_afpd, "check_access: all requested rights are allowed:");
+       ret = AFP_OK;
+    }
+
+    LOG(log_debug, logtype_afpd, "check_access: Requested rights: %08x, allowed_rights: %08x, denied_rights: %08x, Result: %d",
+       requested_rights, allowed_rights, denied_rights, ret);
+
+exit:
+    free(aces);
+    free(username);
+#ifdef DEBUG
+    LOG(log_debug9, logtype_afpd, "check_access: END");
+#endif
+    return ret;
+}
+
+/******************************************************** 
+ * Interface
+ ********************************************************/
+
+int afp_access(AFPObj *obj, char *ibuf, int ibuflen _U_, char *rbuf _U_, int *rbuflen)
+{
+    int                        ret;
+    struct vol         *vol;
+    struct dir         *dir;
+    uint32_t            did, darwin_ace_rights;
+    uint16_t           vid;
+    struct path         *s_path;
+    uuidp_t             uuid;
+
+    LOG(log_debug9, logtype_afpd, "afp_access: BEGIN");
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy(&vid, ibuf, sizeof( vid ));
+    ibuf += sizeof(vid);
+    if (NULL == ( vol = getvolbyvid( vid ))) {
+       LOG(log_error, logtype_afpd, "afp_access: error getting volid:%d", vid);
+        return AFPERR_NOOBJ;
+    }
+
+    memcpy(&did, ibuf, sizeof( did ));
+    ibuf += sizeof( did );
+    if (NULL == ( dir = dirlookup( vol, did ))) {
+       LOG(log_error, logtype_afpd, "afp_access: error getting did:%d", did);
+       return afp_errno;    
+    }
+
+    /* Skip bitmap */
+    ibuf += 2;
+
+    /* Store UUID address */
+    uuid = (uuidp_t)ibuf;
+    ibuf += UUID_BINSIZE;
+
+    /* Store ACE rights */
+    memcpy(&darwin_ace_rights, ibuf, 4);
+    darwin_ace_rights = ntohl(darwin_ace_rights); 
+    ibuf += 4;
+
+    /* get full path and handle file/dir subtleties in netatalk code*/
+    if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
+       LOG(log_error, logtype_afpd, "afp_getacl: cname got an error!");
+       return AFPERR_NOOBJ;    
+    }
+    if (!s_path->st_valid)
+        of_statdir(vol, s_path);
+    if ( s_path->st_errno != 0 ) {
+       LOG(log_error, logtype_afpd, "afp_getacl: cant stat");  
+        return AFPERR_NOOBJ;
+    }
+
+    ret = check_acl_access(s_path->u_name, uuid, darwin_ace_rights);
+
+    LOG(log_debug9, logtype_afpd, "afp_access: END");
+    return ret;
+}
+
+int afp_getacl(AFPObj *obj, char *ibuf, int ibuflen _U_, char *rbuf _U_, int *rbuflen)
+{
+    struct vol         *vol;
+    struct dir         *dir;
+    int                        ret;
+    uint32_t           did;
+    uint16_t           vid, bitmap;
+    struct path         *s_path;
+    struct passwd       *pw;
+    struct group        *gr;
+
+    LOG(log_debug9, logtype_afpd, "afp_getacl: BEGIN");
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy(&vid, ibuf, sizeof( vid ));
+    ibuf += sizeof(vid);
+    if (NULL == ( vol = getvolbyvid( vid ))) {
+       LOG(log_error, logtype_afpd, "afp_getacl: error getting volid:%d", vid);
+        return AFPERR_NOOBJ;
+    }
+
+    memcpy(&did, ibuf, sizeof( did ));
+    ibuf += sizeof( did );
+    if (NULL == ( dir = dirlookup( vol, did ))) {
+       LOG(log_error, logtype_afpd, "afp_getacl: error getting did:%d", did);
+       return afp_errno;    
+    }
+
+    memcpy(&bitmap, ibuf, sizeof( bitmap ));
+    memcpy(rbuf, ibuf, sizeof( bitmap ));    
+    bitmap = ntohs( bitmap );
+    ibuf += sizeof( bitmap );
+    rbuf += sizeof( bitmap );
+    *rbuflen += sizeof( bitmap );
+
+    /* skip maxreplysize */
+    ibuf += 4;
+
+    /* get full path and handle file/dir subtleties in netatalk code*/
+    if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
+       LOG(log_error, logtype_afpd, "afp_getacl: cname got an error!");
+       return AFPERR_NOOBJ;    
+    }
+    if (!s_path->st_valid)
+        of_statdir(vol, s_path);
+    if ( s_path->st_errno != 0 ) {
+       LOG(log_error, logtype_afpd, "afp_getacl: cant stat");  
+        return AFPERR_NOOBJ;
+    }
+
+    /* Shall we return owner UUID ? */
+    if (bitmap & kFileSec_UUID) {
+       LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner user UUID");
+       if (NULL == (pw = getpwuid(s_path->st.st_uid)))
+           return AFPERR_MISC;
+       LOG(log_debug, logtype_afpd, "afp_getacl: got uid: %d, name: %s", s_path->st.st_uid, pw->pw_name);
+       if ((ret = getuuidfromname(pw->pw_name, UUID_USER, rbuf)) != 0)
+           return AFPERR_MISC;
+       rbuf += UUID_BINSIZE;
+       *rbuflen += UUID_BINSIZE;
+    }    
+
+    /* Shall we return group UUID ? */
+    if (bitmap & kFileSec_GRPUUID) {
+       LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner group UUID");
+       if (NULL == (gr = getgrgid(s_path->st.st_gid)))
+           return AFPERR_MISC;
+       LOG(log_debug, logtype_afpd, "afp_getacl: got gid: %d, name: %s", s_path->st.st_gid, gr->gr_name);
+       if ((ret = getuuidfromname(gr->gr_name, UUID_GROUP, rbuf)) != 0)
+           return AFPERR_MISC;
+       rbuf += UUID_BINSIZE;
+       *rbuflen += UUID_BINSIZE;
+    }    
+
+    /* Shall we return ACL ? */
+    if (bitmap & kFileSec_ACL) { 
+       LOG(log_debug, logtype_afpd, "afp_getacl: client requested files ACL");
+       get_and_map_acl(s_path->u_name, rbuf, rbuflen);
+    }
+
+    LOG(log_debug9, logtype_afpd, "afp_getacl: END");
+    return AFP_OK;
+}
+
+int afp_setacl(AFPObj *obj, char *ibuf, int ibuflen _U_, char *rbuf _U_, int *rbuflen)
+{
+    struct vol         *vol;
+    struct dir         *dir;
+    int                        ret;
+    uint32_t            did;
+    uint16_t           vid, bitmap;
+    struct path         *s_path;
+
+    LOG(log_debug9, logtype_afpd, "afp_setacl: BEGIN");
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy(&vid, ibuf, sizeof( vid ));
+    ibuf += sizeof(vid);
+    if (NULL == ( vol = getvolbyvid( vid ))) {
+       LOG(log_error, logtype_afpd, "afp_setacl: error getting volid:%d", vid);
+        return AFPERR_NOOBJ;
+    }
+
+    memcpy(&did, ibuf, sizeof( did ));
+    ibuf += sizeof( did );
+    if (NULL == ( dir = dirlookup( vol, did ))) {
+       LOG(log_error, logtype_afpd, "afp_setacl: error getting did:%d", did);
+       return afp_errno;    
+    }
+
+    memcpy(&bitmap, ibuf, sizeof( bitmap ));
+    bitmap = ntohs( bitmap );
+    ibuf += sizeof( bitmap );
+
+    /* get full path and handle file/dir subtleties in netatalk code*/
+    if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
+       LOG(log_error, logtype_afpd, "afp_setacl: cname got an error!");
+       return AFPERR_NOOBJ;    
+    }
+    if (!s_path->st_valid)
+        of_statdir(vol, s_path);
+    if ( s_path->st_errno != 0 ) {
+       LOG(log_error, logtype_afpd, "afp_setacl: cant stat");  
+        return AFPERR_NOOBJ;
+    }
+    LOG(log_debug, logtype_afpd, "afp_setacl: unixname: %s", s_path->u_name);
+
+    /* Padding? */
+    if ((unsigned long)ibuf & 1)
+       ibuf++;
+
+    /* Start processing request */
+
+    /* Change owner: dont even try */
+    if (bitmap & kFileSec_UUID) {
+       LOG(log_debug, logtype_afpd, "afp_setacl: change owner request. discarded.");
+       ret = AFPERR_ACCESS;
+       ibuf += UUID_BINSIZE;
+    }
+
+    /* Change group: certain changes might be allowed, so try it. FIXME: not implemented yet. */
+    if (bitmap & kFileSec_UUID) {
+       LOG(log_debug, logtype_afpd, "afp_setacl: change group request. not supported this time.");
+       ret = AFPERR_PARAM;
+       ibuf += UUID_BINSIZE;
+    }
+
+    /* Remove ACL ? */
+    if (bitmap & kFileSec_REMOVEACL) {
+       LOG(log_debug, logtype_afpd, "afp_setacl: Remove ACL request.");
+       if ((ret = remove_acl_vfs(vol, s_path->u_name, S_ISDIR(s_path->st.st_mode))) != AFP_OK)
+           LOG(log_error, logtype_afpd, "afp_setacl: error from remove_acl");
+    }
+
+    /* Change ACL ? */
+    if (bitmap & kFileSec_ACL) {
+       LOG(log_debug, logtype_afpd, "afp_setacl: Change ACL request.");
+       
+       /* Check if its our job to preserve inherited ACEs */
+       if (bitmap & kFileSec_Inherit)
+           ret = set_acl_vfs(vol, s_path->u_name, 1, ibuf);
+       else
+           ret = set_acl_vfs(vol, s_path->u_name, 0, ibuf);
+       if (ret == 0)
+           ret = AFP_OK;
+       else
+           ret = AFPERR_MISC;
+    }
+
+    LOG(log_debug9, logtype_afpd, "afp_setacl: END");
+    return ret;
+}
+
+/* 
+   unix.c/accessmode calls this: map ACL to OS 9 mode
+ */
+void acltoownermode(char *path, struct stat *st, uid_t uid, struct maccess *ma)
+{
+    struct passwd *pw;
+    uuid_t uuid;
+    int dir, r_ok, w_ok, x_ok;
+
+    dir = S_ISDIR(st->st_mode);
+
+    if ((pw = getpwuid(uid)) == NULL) {
+       LOG(log_error, logtype_afpd, "acltoownermode: %s", strerror(errno));
+       return;
+    }
+
+    /* We need the UUID for check_acl_access */
+    if ((getuuidfromname(pw->pw_name, UUID_USER, uuid)) != 0)
+       return;
+
+    /* These work for files and dirs */
+    r_ok = check_acl_access(path, uuid, DARWIN_ACE_READ_DATA);
+    w_ok = check_acl_access(path, uuid, (DARWIN_ACE_WRITE_DATA|DARWIN_ACE_APPEND_DATA));
+    x_ok = check_acl_access(path, uuid, DARWIN_ACE_EXECUTE);
+
+    LOG(log_error, logtype_afpd, "acltoownermode: ma_user before: %04o",ma->ma_user);
+    if (r_ok == 0)
+       ma->ma_user |= AR_UREAD;
+    if (w_ok == 0)
+       ma->ma_user |= AR_UWRITE;
+    if (x_ok == 0)
+       ma->ma_user |= AR_USEARCH;
+    LOG(log_error, logtype_afpd, "acltoownermode: ma_user after: %04o", ma->ma_user);
+
+    return;
+}
+
+/* 
+   We're being called at the end of afp_createdir. We're (hopefully) inside dir
+   and ".AppleDouble" and ".AppleDouble/.Parent" should have already been created.
+   We then inherit any explicit ACE from "." to ".AppleDouble" and ".AppleDouble/.Parent".
+   FIXME: add to VFS layer ?
+ */
+void addir_inherit_acl(const struct vol *vol)
+{
+    ace_t *diraces = NULL, *adaces = NULL, *combinedaces = NULL;
+    int diracecount, adacecount;
+
+    LOG(log_debug9, logtype_afpd, "addir_inherit_acl: BEGIN");
+
+    /* Check if ACLs are enabled for the volume */
+    if (vol->v_flags & AFPVOL_ACLS) {
+
+       if ((diracecount = get_nfsv4_acl(".", &diraces)) <= 0)
+           goto cleanup;
+       /* Remove any trivial ACE from "." */
+       if ((diracecount = strip_trivial_aces(&diraces, diracecount)) <= 0)
+           goto cleanup;
+
+       /* 
+          Inherit to ".AppleDouble"
+        */
+
+       if ((adacecount = get_nfsv4_acl(".AppleDouble", &adaces)) <= 0)
+           goto cleanup;
+       /* Remove any non-trivial ACE from ".AppleDouble" */
+       if ((adacecount = strip_nontrivial_aces(&adaces, adacecount)) <= 0)
+           goto cleanup;
+
+       /* Combine ACEs */
+       if ((combinedaces = concat_aces(diraces, diracecount, adaces, adacecount)) == NULL)
+           goto cleanup;
+
+       /* Now set new acl */
+       if ((acl(".AppleDouble", ACE_SETACL, diracecount + adacecount, combinedaces)) != 0)
+           LOG(log_error, logtype_afpd, "addir_inherit_acl: acl: %s", strerror(errno));
+
+       free(adaces);
+       adaces = NULL;
+       free(combinedaces);
+       combinedaces = NULL;
+
+       /* 
+          Inherit to ".AppleDouble/.Parent"
+        */
+
+       if ((adacecount = get_nfsv4_acl(".AppleDouble/.Parent", &adaces)) <= 0)
+           goto cleanup;
+       if ((adacecount = strip_nontrivial_aces(&adaces, adacecount)) <= 0)
+           goto cleanup;
+
+       /* Combine ACEs */
+       if ((combinedaces = concat_aces(diraces, diracecount, adaces, adacecount)) == NULL)
+           goto cleanup;
+
+       /* Now set new acl */
+       if ((acl(".AppleDouble/.Parent", ACE_SETACL, diracecount + adacecount, combinedaces)) != 0)
+           LOG(log_error, logtype_afpd, "addir_inherit_acl: acl: %s", strerror(errno));
+
+
+    }
+
+cleanup:
+    LOG(log_debug9, logtype_afpd, "addir_inherit_acl: END");
+
+    free(diraces);
+    free(adaces);
+    free(combinedaces);
+}
diff --git a/etc/afpd/acls.h b/etc/afpd/acls.h
new file mode 100644 (file)
index 0000000..5e0087d
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+   $Id: acls.h,v 1.1 2009-02-02 11:55:00 franklahm Exp $
+   Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+ */
+
+#ifndef AFPD_ACLS_H 
+#define AFPD_ACLS_H
+
+#include <sys/acl.h>
+#include <atalk/uuid.h>                /* for uuid_t */
+
+/*
+ * This is what Apple says about ACL flags in sys/kauth.h:
+ *
+ * <Apple> The low 16 bits of the flags field are reserved for filesystem
+ * internal use and must be preserved by all APIs.  This includes
+ * round-tripping flags through user-space interfaces.
+ * The high 16 bits of the flags are used to store attributes and
+ * to request specific handling of the ACL. </Apple>
+ * 
+ * The constants are included for reference. We DONT expect them on
+ * the wire! We will ignore and spoil em.
+ */
+
+/* Some stuff for the handling of NFSv4 ACLs */
+#define ACE_TRIVIAL (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)
+
+/* FPGet|Set Bitmap */
+enum {
+    kFileSec_UUID      = (1<<0),
+    kFileSec_GRPUUID   = (1<<1),
+    kFileSec_ACL       = (1<<2),
+    kFileSec_REMOVEACL = (1<<3),
+    kFileSec_Inherit   = (1<<4)
+};
+
+/* ACL Flags */
+#define DARWIN_ACL_FLAGS_PRIVATE       (0xffff)
+/* inheritance will be deferred until the first rename operation */
+#define KAUTH_ACL_DEFER_INHERIT (1<<16)
+/* this ACL must not be overwritten as part of an inheritance operation */
+#define KAUTH_ACL_NO_INHERIT (1<<17)
+
+/* ACE Flags */
+#define DARWIN_ACE_FLAGS_KINDMASK           0xf
+#define DARWIN_ACE_FLAGS_PERMIT             (1<<0)
+#define DARWIN_ACE_FLAGS_DENY               (1<<1)
+#define DARWIN_ACE_FLAGS_INHERITED          (1<<4)
+#define DARWIN_ACE_FLAGS_FILE_INHERIT       (1<<5)
+#define DARWIN_ACE_FLAGS_DIRECTORY_INHERIT  (1<<6)
+#define DARWIN_ACE_FLAGS_LIMIT_INHERIT      (1<<7)
+#define DARWIN_ACE_FLAGS_ONLY_INHERIT       (1<<8)
+
+/* All flag bits controlling ACE inheritance */
+#define DARWIN_ACE_INHERIT_CONTROL_FLAGS \
+       (DARWIN_ACE_FLAGS_FILE_INHERIT |\
+        DARWIN_ACE_FLAGS_DIRECTORY_INHERIT |\
+        DARWIN_ACE_FLAGS_LIMIT_INHERIT |\
+        DARWIN_ACE_FLAGS_ONLY_INHERIT)
+
+/* ACE Rights */
+#define DARWIN_ACE_READ_DATA           0x00000002
+#define DARWIN_ACE_LIST_DIRECTORY      0x00000002
+#define DARWIN_ACE_WRITE_DATA          0x00000004
+#define DARWIN_ACE_ADD_FILE            0x00000004
+#define DARWIN_ACE_EXECUTE             0x00000008
+#define DARWIN_ACE_SEARCH              0x00000008
+#define DARWIN_ACE_DELETE              0x00000010
+#define DARWIN_ACE_APPEND_DATA         0x00000020
+#define DARWIN_ACE_ADD_SUBDIRECTORY    0x00000020
+#define DARWIN_ACE_DELETE_CHILD        0x00000040
+#define DARWIN_ACE_READ_ATTRIBUTES     0x00000080
+#define DARWIN_ACE_WRITE_ATTRIBUTES    0x00000100
+#define DARWIN_ACE_READ_EXTATTRIBUTES  0x00000200
+#define DARWIN_ACE_WRITE_EXTATTRIBUTES 0x00000400
+#define DARWIN_ACE_READ_SECURITY       0x00000800
+#define DARWIN_ACE_WRITE_SECURITY      0x00001000
+#define DARWIN_ACE_TAKE_OWNERSHIP      0x00002000
+
+/* Access Control List Entry (ACE) */
+typedef struct {
+    uuid_t      darwin_ace_uuid;
+    uint32_t    darwin_ace_flags;
+    uint32_t    darwin_ace_rights;
+} darwin_ace_t;
+
+/* Access Control List */
+typedef struct {
+    uint32_t darwin_acl_count;
+    uint32_t darwin_acl_flags;
+} darwin_acl_header_t;
+
+/* FP functions */
+extern int afp_access(AFPObj *, char *, int, char *, int *);
+extern int afp_getacl(AFPObj *, char *, int, char *, int *);
+extern int afp_setacl(AFPObj *, char *, int, char *, int *);
+
+/* Parse ldap.conf */
+extern int acl_ldap_readconfig(char *name);
+
+#endif
index 2e25df4caaa9a722bd129462d1b83057627239c3..b4631e8cbf24d3c97f3e646c3df6ac3549dd3972 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: afp_config.c,v 1.25 2009-01-20 04:31:10 didg Exp $
+ * $Id: afp_config.c,v 1.26 2009-02-02 11:55:00 franklahm Exp $
  *
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
  * All Rights Reserved.  See COPYRIGHT.
@@ -50,6 +50,9 @@ char *strchr (), *strrchr ();
 #ifdef USE_SRVLOC
 #include <slp.h>
 #endif /* USE_SRVLOC */
+#ifdef HAVE_NFSv4_ACLS
+#include <atalk/ldapconfig.h>
+#endif
 
 #include "globals.h"
 #include "afp_config.h"
@@ -587,7 +590,22 @@ AFPConfig *configinit(struct afp_options *cmdline)
     fclose(fp);
 
     if (!have_option)
-        return AFPConfigInit(cmdline, cmdline);
+        first = AFPConfigInit(cmdline, cmdline);
+
+#ifdef HAVE_NFSv4_ACLS
+    /* Parse ldap.conf */
+    LOG(log_debug, logtype_afpd, "Start parsing ldap.conf");
+    acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
+    LOG(log_debug, logtype_afpd, "Finished parsing ldap.conf");
+
+    /* If we have a good LDAP config, enable UUID option for all apfd's */
+    config = first;
+    while(config) {
+       if (ldap_config_valid)
+           config->obj.options.flags |= OPTION_UUID;
+       config = config->next;
+    }
+#endif
 
     return first;
 }
index e9ec5c364b3846f38928afd8928d9f9c04cd7401..5c5f75168c9d3f4206739ae3c9be5cb3beaed493 100644 (file)
 #ifndef _AFP_VFS_H
 #define _AFP_VFS_H
 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifdef HAVE_NFSv4_ACLS
+#include <sys/acl.h>
+#endif
+
 #include <atalk/adouble.h>
 struct vol;
 
@@ -41,7 +49,10 @@ struct vfs_ops {
 
     int (*rf_deletefile)(const struct vol *, const char * );
     int (*rf_renamefile)(const struct vol *, const char *oldpath, const char *newpath);
-
+#ifdef HAVE_NFSv4_ACLS
+    int (*rf_acl)(const struct vol *, const char *path, int cmd, int count, ace_t *aces);
+    int (*rf_remove_acl)(const struct vol *, const char *path, int dir);
+#endif
 };
 
 void initvol_vfs(struct vol *vol);
index a9755f11d70c6a0387b5a467c9e8771384697dfb..aeac8db07ba869190e0f64029c4598017b764aea 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: auth.c,v 1.53 2008-05-23 06:35:49 didg Exp $
+ * $Id: auth.c,v 1.54 2009-02-02 11:55:00 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -30,6 +30,7 @@
 #include <grp.h>
 #include <atalk/logger.h>
 #include <atalk/server_ipc.h>
+#include <atalk/uuid.h>
 
 #ifdef TRU64
 #include <netdb.h>
@@ -45,8 +46,10 @@ extern void afp_get_cmdline( int *ac, char ***av );
 #include "uam_auth.h"
 #include "switch.h"
 #include "status.h"
-
 #include "fork.h"
+#ifdef HAVE_NFSv4_ACLS
+#include "acls.h"
+#endif
 
 int    afp_version = 11;
 static int afp_version_index;
@@ -188,6 +191,13 @@ static int set_auth_switch(int expired)
         afp_switch = postauth_switch;
         switch (afp_version) {
         case 32:
+#ifdef HAVE_NFSv4_ACLS
+           uam_afpserver_action(21, UAM_AFPSERVER_POSTAUTH, afp_mapid, NULL);
+           uam_afpserver_action(22, UAM_AFPSERVER_POSTAUTH, afp_mapname, NULL);
+           uam_afpserver_action(73, UAM_AFPSERVER_POSTAUTH, afp_getacl, NULL);
+           uam_afpserver_action(74, UAM_AFPSERVER_POSTAUTH, afp_setacl, NULL);
+           uam_afpserver_action(75, UAM_AFPSERVER_POSTAUTH, afp_access, NULL);
+#endif
         case 31:
            uam_afpserver_action(AFP_ENUMERATE_EXT2, UAM_AFPSERVER_POSTAUTH, afp_enumerate_ext2, NULL); 
         case 30:
@@ -919,9 +929,14 @@ AFPObj  *obj _U_;
 char   *ibuf, *rbuf;
 int    ibuflen _U_, *rbuflen;
 {
+    int ret;
     u_int8_t  thisuser;
     u_int32_t id;
     u_int16_t bitmap;
+    uuid_t uuid;
+    char *uuidstring;
+
+    LOG(log_debug, logtype_afpd, "begin afp_getuserinfo:");
 
     *rbuflen = 0;
     ibuf++;
@@ -958,7 +973,26 @@ int        ibuflen _U_, *rbuflen;
         *rbuflen += sizeof(id);
     }
 
+#ifdef HAVE_NFSv4_ACLS
+    if (bitmap & USERIBIT_UUID) {
+       if ( ! (obj->options.flags & OPTION_UUID))
+           return AFPERR_BITMAP;
+       LOG(log_debug, logtype_afpd, "afp_getuserinfo: get UUID for \'%s\'", obj->username);
+       ret = getuuidfromname( obj->username, UUID_USER, uuid);
+       if (ret != 0) {
+           LOG(log_info, logtype_afpd, "afp_getuserinfo: error getting UUID !");
+           return AFPERR_NOITEM;
+       }
+       uuid_bin2string( uuid, &uuidstring);
+       LOG(log_debug, logtype_afpd, "afp_getuserinfo: got UUID: %s", uuidstring);
+       free(uuidstring);
+       memcpy(rbuf, uuid, UUID_BINSIZE);
+       rbuf += UUID_BINSIZE;
+       *rbuflen += UUID_BINSIZE;
+    }
+#endif
     return AFP_OK;
+    LOG(log_debug, logtype_afpd, "END afp_getuserinfo:");
 }
 
 #define UAM_LIST(type) (((type) == UAM_SERVER_LOGIN || (type) == UAM_SERVER_LOGIN_EXT) ? &uam_login : \
index 803786b3870314efbe2613fb35e7839c5a9af72b..a473757da317d39fdde3f5e18af051ead7abd6dd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: auth.h,v 1.6 2005-04-28 20:49:40 bfernhomberg Exp $
+ * $Id: auth.h,v 1.7 2009-02-02 11:55:00 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -26,7 +26,8 @@ struct afp_versions {
 /* for GetUserInfo */
 #define USERIBIT_USER  (1 << 0)
 #define USERIBIT_GROUP (1 << 1)
-#define USERIBIT_ALL   (USERIBIT_USER | USERIBIT_GROUP)
+#define USERIBIT_UUID  (1 << 2)
+#define USERIBIT_ALL   (USERIBIT_USER | USERIBIT_GROUP | USERIBIT_UUID)
 
 extern uid_t    uuid;
 #if defined( sun ) && !defined( __svr4__ ) || defined( ultrix )
index a108561e06ca26ffc38051ed44f6cbfe225df90a..642eb9ebf35db9de62a6c578a102245c8958187e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: directory.c,v 1.90 2009-01-30 04:57:42 didg Exp $
+ * $Id: directory.c,v 1.91 2009-02-02 11:55:00 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -44,6 +44,7 @@ char *strchr (), *strrchr ();
 #include <atalk/util.h>
 #include <atalk/cnid.h>
 #include <atalk/logger.h>
+#include <atalk/uuid.h>
 
 #include "directory.h"
 #include "desktop.h"
@@ -55,6 +56,10 @@ char *strchr (), *strrchr ();
 #include "unix.h"
 #include "mangle.h"
 
+#ifdef HAVE_NFSv4_ACLS
+extern void addir_inherit_acl(const struct vol *vol);
+#endif 
+
 struct dir     *curdir;
 int             afp_errno;
 
@@ -2267,6 +2272,11 @@ int      ibuflen _U_, *rbuflen;
     ad_close_metadata( &ad);
 
 createdir_done:
+#ifdef HAVE_NFSv4_ACLS
+    /* FIXME: are we really inside the created dir? */
+    addir_inherit_acl(vol);
+#endif
+
     memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
     *rbuflen = sizeof( u_int32_t );
     setvoltime(obj, vol );
@@ -2456,49 +2466,93 @@ int     ibuflen _U_, *rbuflen;
     u_int32_t           id;
     int                        len, sfunc;
     int         utf8 = 0;
+    uuidtype_t          type;
+
+    LOG(log_debug, logtype_afpd, "afp_mapid: BEGIN");
     
     ibuf++;
     sfunc = (unsigned char) *ibuf++;
-    memcpy( &id, ibuf, sizeof( id ));
-
-    id = ntohl(id);
     *rbuflen = 0;
 
-    if (sfunc == 3 || sfunc == 4) {
+
+    if (sfunc >= 3 && sfunc <= 6) {
         if (afp_version < 30) {
             return( AFPERR_PARAM );
         }
         utf8 = 1;
     }
-    if ( id != 0 ) {
+
         switch ( sfunc ) {
         case 1 :
         case 3 :/* unicode */
+       memcpy( &id, ibuf, sizeof( id ));
+       id = ntohl(id);
+       if ( id != 0 ) {
             if (( pw = getpwuid( id )) == NULL ) {
                 return( AFPERR_NOITEM );
             }
            len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
                                             pw->pw_name, strlen(pw->pw_name), &name);
+       } else {
+           len = 0;
+           name = NULL;
+       }
             break;
-
         case 2 :
         case 4 : /* unicode */
+       memcpy( &id, ibuf, sizeof( id ));
+       id = ntohl(id);
+       if ( id != 0 ) {
             if (NULL == ( gr = (struct group *)getgrgid( id ))) {
                 return( AFPERR_NOITEM );
             }
            len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
                                             gr->gr_name, strlen(gr->gr_name), &name);
+       } else {
+           len = 0;
+           name = NULL;
+       }
             break;
-
+#ifdef HAVE_NFSv4_ACLS
+    case 5 : /* username -> UUID  */
+    case 6 : /* groupname -> UUID */
+       if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
+           return AFPERR_PARAM;
+       LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
+       len = getnamefromuuid( ibuf, &name, &type);
+       if (len != 0)           /* its a error code, not len */
+           return AFPERR_NOITEM;
+       if (type == UUID_USER) {
+           if (( pw = getpwnam( name )) == NULL )
+               return( AFPERR_NOITEM );
+           LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
+           id = htonl(UUID_USER);
+           memcpy( rbuf, &id, sizeof( id ));
+           id = htonl( pw->pw_uid);
+           rbuf += sizeof( id );
+           memcpy( rbuf, &id, sizeof( id ));
+           rbuf += sizeof( id );
+           *rbuflen = 2 * sizeof( id );
+       } else {                /* type == UUID_GROUP */
+            if (( gr = getgrnam( name )) == NULL )
+                return( AFPERR_NOITEM );
+           LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
+           id = htonl(UUID_GROUP);
+           memcpy( rbuf, &id, sizeof( id ));
+           rbuf += sizeof( id );
+           id = htonl( gr->gr_gid);
+           memcpy( rbuf, &id, sizeof( id ));
+           rbuf += sizeof( id );
+           *rbuflen = 2 * sizeof( id );
+       }
+       break;
+#endif
         default :
             return( AFPERR_PARAM );
         }
+
         len = strlen( name );
 
-    } else {
-        len = 0;
-        name = NULL;
-    }
     if (utf8) {
         u_int16_t tp = htons(len);
         memcpy(rbuf, &tp, sizeof(tp));
@@ -2528,10 +2582,14 @@ int     ibuflen _U_, *rbuflen;
     int             len, sfunc;
     u_int32_t       id;
     u_int16_t       ulen;
+    char            *uuidstring;
+
+    LOG(log_debug, logtype_afpd, "afp_mapname: BEGIN");
 
     ibuf++;
     sfunc = (unsigned char) *ibuf++;
     *rbuflen = 0;
+    LOG(log_debug, logtype_afpd, "afp_mapname: sfunc: %d, afp_version: %d", sfunc, afp_version);
     switch ( sfunc ) {
     case 1 : 
     case 2 : /* unicode */
@@ -2541,18 +2599,31 @@ int     ibuflen _U_, *rbuflen;
         memcpy(&ulen, ibuf, sizeof(ulen));
         len = ntohs(ulen);
         ibuf += 2;
+       LOG(log_debug, logtype_afpd, "afp_mapname: alive");
         break;
     case 3 :
     case 4 :
         len = (unsigned char) *ibuf++;
         break;
+#ifdef HAVE_NFSv4_ACLS
+    case 5 : /* username -> UUID  */
+    case 6 : /* groupname -> UUID */
+        if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
+            return AFPERR_PARAM;
+        memcpy(&ulen, ibuf, sizeof(ulen));
+        len = ntohs(ulen);
+        ibuf += 2;
+        break;
+#endif
     default :
         return( AFPERR_PARAM );
     }
 
     ibuf[ len ] = '\0';
 
-    if ( len != 0 ) {
+    if ( len == 0 )
+       return AFPERR_PARAM;
+    else {
         switch ( sfunc ) {
         case 1 : /* unicode */
         case 3 :
@@ -2560,22 +2631,39 @@ int     ibuflen _U_, *rbuflen;
                 return( AFPERR_NOITEM );
             }
             id = pw->pw_uid;
+           id = htonl(id);
+           memcpy( rbuf, &id, sizeof( id ));
+           *rbuflen = sizeof( id );
             break;
 
         case 2 : /* unicode */
         case 4 :
+           LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s",ibuf);
             if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
                 return( AFPERR_NOITEM );
             }
             id = gr->gr_gid;
-            break;
-        }
-    } else {
-        id = 0;
-    }
+           LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s -> id: %d",ibuf, id);
     id = htonl(id);
     memcpy( rbuf, &id, sizeof( id ));
     *rbuflen = sizeof( id );
+           break;
+#ifdef HAVE_NFSv4_ACLS
+       case 5 :                /* username -> UUID */
+           LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);    
+           if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
+               return AFPERR_NOITEM;
+           *rbuflen = UUID_BINSIZE;
+           break;
+       case 6 :                /* groupname -> UUID */
+           LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);    
+           if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf))
+               return AFPERR_NOITEM;
+           *rbuflen = UUID_BINSIZE;
+           break;
+#endif
+        }
+    }
     return( AFP_OK );
 }
 
index c6b741ff6a54f58970b4248d27d3a4f1d6678ac5..531c62e6f26608888429acc07f391057332063b3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: globals.h,v 1.24 2008-12-03 18:35:44 didg Exp $
+ * $Id: globals.h,v 1.25 2009-02-02 11:55:00 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -36,6 +36,7 @@
 #define OPTION_CUSTOMICON    (1 << 4)
 #define OPTION_NOSLP         (1 << 5)
 #define OPTION_ANNOUNCESSH   (1 << 6)
+#define OPTION_UUID          (1 << 7)
 
 #ifdef FORCE_UIDGID
 /* set up a structure for this */
index f92c14d1fb08e3a9db2374874ba97df859000a90..0d842e562329e85c95ce71b8d28ae0a29dd58294 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: status.c,v 1.18 2006-09-18 01:06:44 didg Exp $
+ * $Id: status.c,v 1.19 2009-02-02 11:55:01 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -65,6 +65,9 @@ static void status_flags(char *data, const int notif, const int ipok,
     status |= AFPSRVRINFO_SRVRDIR; /* AFP 3.1 specs says we need to specify this, but may set the count to 0 */
     /* We don't set the UTF8 name flag here, we don't know whether we have enough space ... */
 
+    if (flags & OPTION_UUID)   /* 05122008 FIXME: can we set AFPSRVRINFO_UUID here ? see AFPSRVRINFO_SRVRDIR*/
+       status |= AFPSRVRINFO_UUID;
+
     status = htons(status);
     memcpy(data + AFPSTATUS_FLAGOFF, &status, sizeof(status));
 }
@@ -525,7 +528,8 @@ void status_init(AFPConfig *aspconfig, AFPConfig *dsiconfig,
     status_flags(status, options->server_notif, options->fqdn ||
                  (dsiconfig && dsi->server.sin_addr.s_addr),
                  options->passwdbits, 
-                (options->k5service && options->k5realm && options->fqdn));
+                (options->k5service && options->k5realm && options->fqdn), 
+                options->flags);
     /* returns offset to signature offset */
     c = status_server(status, options->server ? options->server :
                       options->hostname, options);
index 48febcf6a2fcc70b7375d23fb23660ee7e197884..fd9770841910f6da250aaa334c8371281bcc81e6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: switch.c,v 1.14 2008-05-23 06:35:49 didg Exp $
+ * $Id: switch.c,v 1.15 2009-02-02 11:55:01 franklahm Exp $
  *
  * Copyright (c) 1990,1991 Regents of The University of Michigan.
  * All Rights Reserved.
@@ -46,6 +46,9 @@
 #include "filedir.h"
 #include "status.h"
 #include "misc.h"
+#ifdef HAVE_NFSv4_ACLS
+#include "acls.h"
+#endif
 
 static int afp_null(obj, ibuf, ibuflen, rbuf, rbuflen )
 AFPObj  *obj _U_;
index 240bfd2a338ebe5e0c4c54677bdb48d9a2cd0db9..1ad282f227f08f0049a0ed9d233b9e5e5975e58a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: unix.c,v 1.51 2009-01-30 04:57:42 didg Exp $
+ * $Id: unix.c,v 1.52 2009-02-02 11:55:01 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -45,6 +45,11 @@ char *strchr (), *strrchr ();
 #include "unix.h"
 #include "fork.h"
 
+#ifdef HAVE_NFSv4_ACLS
+extern void acltoownermode(char *path, struct stat *st,uid_t uid, struct maccess *ma);
+#endif
+
+
 /*
  * Get the free space on a partition.
  */
@@ -169,7 +174,6 @@ mode_t mode;
  *
  * Note: the previous method, using access(), does not work correctly
  * over NFS.
- * FIXME what about ACL?
  *
  * dir parameter is used by AFS
  */
@@ -189,6 +193,10 @@ struct stat     sb;
         st = &sb;
     }
     utommode( st, ma );
+#ifdef HAVE_NFSv4_ACLS
+    /* 10.5 Finder looks at OS 9 mode, so we must do some mapping */
+    acltoownermode( path, st, uuid, ma);
+#endif
 }
 
 int gmem( gid )
index 8e9768c6688c594cf9a4c152400c2f1542a3a882..82df36edfae4000a7fb0531481369b9cc388ad95 100644 (file)
 #include "volume.h"
 #include "unix.h"
 
+#ifdef HAVE_NFSv4_ACLS
+extern int remove_acl(const char *name);
+#endif
+
 struct perm {
     uid_t uid;
     gid_t gid;
@@ -650,6 +654,54 @@ int RF_renamefile_adouble(const struct vol *vol, const char *src, const char *ds
        return 0;
 }
 
+#ifdef HAVE_NFSv4_ACLS
+static int RF_acl(const struct vol *vol, const char *path, int cmd, int count, ace_t *aces)
+{
+    static char buf[ MAXPATHLEN + 1];
+    struct stat st;
+
+    if ((stat(path, &st)) != 0)
+       return -1;
+    if (S_ISDIR(st.st_mode)) {
+       if ((snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path)) < 0)
+           return -1;
+       /* set acl on .AppleDouble dir first */
+       if ((acl(buf, cmd, count, aces)) != 0)
+           return -1;
+       /* now set ACL on ressource fork */
+       if ((acl(vol->vfs->ad_path(path, ADFLAGS_DIR), cmd, count, aces)) != 0)
+           return -1;
+    } else
+       /* set ACL on ressource fork */
+       if ((acl(vol->vfs->ad_path(path, ADFLAGS_HF), cmd, count, aces)) != 0)
+           return -1;
+
+    return 0;
+}
+
+static int RF_remove_acl(const struct vol *vol, const char *path, int dir)
+{
+    int ret;
+    static char buf[ MAXPATHLEN + 1];
+
+    if (dir) {
+       if ((snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path)) < 0)
+           return AFPERR_MISC;
+       /* remove ACL from .AppleDouble/.Parent first */
+       if ((ret = remove_acl(vol->vfs->ad_path(path, ADFLAGS_DIR))) != AFP_OK)
+           return ret;
+       /* now remove from .AppleDouble dir */
+       if ((ret = remove_acl(buf)) != AFP_OK)
+           return ret;
+    } else
+       /* remove ACL from ressource fork */
+       if ((ret = remove_acl(vol->vfs->ad_path(path, ADFLAGS_HF))) != AFP_OK)
+           return ret;
+
+    return AFP_OK;
+}
+#endif
+
 struct vfs_ops netatalk_adouble = {
     /* ad_path:           */ ad_path,
     /* validupath:        */ validupath_adouble,
@@ -662,6 +714,10 @@ struct vfs_ops netatalk_adouble = {
     /* rf_setdirowner:    */ RF_setdirowner_adouble,
     /* rf_deletefile:     */ RF_deletefile_adouble,
     /* rf_renamefile:     */ RF_renamefile_adouble,
+#ifdef HAVE_NFSv4_ACLS
+    /* rf_acl:            */ RF_acl,
+    /* rf_remove_acl      */ RF_remove_acl
+#endif
 };
 
 /* =======================================
index 0ebb8d5b982ad4b1069870311e3cff898634ffaa..b84ac2a1fc12be3ed73bb3fedd8ff81e1f2771b1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: volume.c,v 1.78 2009-02-01 07:11:56 didg Exp $
+ * $Id: volume.c,v 1.79 2009-02-02 11:55:01 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -174,6 +174,7 @@ static const _vol_opt_name vol_opt_names[] = {
     {AFPVOL_NODEV,      "NODEV"},       /* always use 0 for device number in cnid calls */
     {AFPVOL_EILSEQ,     "ILLEGALSEQ"},     /* encode illegal sequence */
     {AFPVOL_CACHE,      "CACHEID"},     /* Use adouble v2 CNID caching, default don't use it */
+    {AFPVOL_ACLS,       "ACLS"},        /* Vol supports ACLs */
     {0, NULL}
 };
 
@@ -482,6 +483,8 @@ static void volset(struct vol_option *options, struct vol_option *save,
                options[VOLOPT_ROOTPREEXEC].i_value = 1;
             else if (strcasecmp(p, "upriv") == 0)
                 options[VOLOPT_FLAGS].i_value |= AFPVOL_UNIX_PRIV;
+            else if (strcasecmp(p, "acls") == 0)
+                options[VOLOPT_FLAGS].i_value |= AFPVOL_ACLS;
             else if (strcasecmp(p, "nodev") == 0)
                 options[VOLOPT_FLAGS].i_value |= AFPVOL_NODEV;
             else if (strcasecmp(p, "illegalseq") == 0)
@@ -1318,6 +1321,10 @@ int              *buflen;
                if (vol->v_flags & AFPVOL_UNIX_PRIV)
                    ashort |= VOLPBIT_ATTR_UNIXPRIV;
             }
+            if (afp_version >= 32) {
+               if (vol->v_flags & AFPVOL_ACLS)
+                   ashort |= VOLPBIT_ATTR_ACLS;
+            }
             ashort = htons(ashort);
             memcpy(data, &ashort, sizeof( ashort ));
             data += sizeof( ashort );
index 23fd19086e9f6d86efe1c23541a949fe7b8071a6..52d244828a31dea1027e2ad072a279ca2f28bacd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: volume.h,v 1.26 2009-01-30 04:57:42 didg Exp $
+ * $Id: volume.h,v 1.27 2009-02-02 11:55:01 franklahm Exp $
  *
  * Copyright (c) 1990,1994 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -130,6 +130,7 @@ this is going away. */
 
 #define AFPVOL_CACHE     (1 << 21)   /* Use adouble v2 CNID caching, default don't use it */
 #define AFPVOL_INV_DOTS  (1 << 22)   /* dots files are invisible */
+#define AFPVOL_ACLS      (1 << 25)   /* Volume supports ACLS */
 
 /* FPGetSrvrParms options */
 #define AFPSRVR_CONFIGINFO     (1 << 0)
@@ -163,7 +164,7 @@ int wincheck(const struct vol *vol, const char *path);
 #define VOLPBIT_ATTR_UNIXPRIV     (1 << 5)
 #define VOLPBIT_ATTR_UTF8         (1 << 6)
 #define VOLPBIT_ATTR_NONETUID     (1 << 7)
-
+#define VOLPBIT_ATTR_ACLS         (1 << 11)
 #define VOLPBIT_ATTR   0
 #define VOLPBIT_SIG    1
 #define VOLPBIT_CDATE  2
index 581d759f61e1cf3cdf7fbe32b0c3636c309bdfcc..c7e8a556a06df77fb01735aca90e370f05190db9 100644 (file)
@@ -55,7 +55,7 @@ typedef u_int16_t AFPUserBytes;
 #define AFPSRVRINFO_SRVRDIR      (1<<8)  /* supports directories service */ 
 
 #define AFPSRVRINFO_SRVUTF8      (1<<9)  /* supports UTF8 names AFP 3.1 */ 
-
+#define AFPSRVRINFO_UUID         (1<<10) /* supports UUIDs */
 #define AFPSRVRINFO_FASTBOZO    (1<<15) /* fast copying */
 
 #define AFP_OK         0
diff --git a/include/atalk/ldapconfig.h b/include/atalk/ldapconfig.h
new file mode 100644 (file)
index 0000000..ca14e97
--- /dev/null
@@ -0,0 +1,42 @@
+#ifdef HAVE_NFSv4_ACLS
+
+#ifndef LDAPCONFIG_H
+#define LDAPCONFIG_H
+
+/* One function does the whole job */
+extern int acl_ldap_readconfig(char *name);
+
+/* These are the prefvalues */
+extern char *ldap_server;
+extern int  ldap_auth_method;
+extern char *ldap_auth_dn;
+extern char *ldap_auth_pw;
+extern char *ldap_userbase;
+extern char *ldap_groupbase;
+extern char *ldap_uuid_attr;
+extern char *ldap_name_attr;
+extern char *ldap_group_attr;
+extern char *ldap_uid_attr;
+
+struct ldap_pref {
+    void *pref;
+    char *name;
+    int strorint;
+    int intfromarray;
+    int valid;
+};
+
+struct pref_array {
+    char *pref;
+    char *valuestring;
+    int  value;
+};
+
+/* For parsing */
+extern struct ldap_pref ldap_prefs[];
+extern struct pref_array prefs_array[];
+extern int ldap_config_valid;
+
+#endif
+
+#endif
diff --git a/include/atalk/uuid.h b/include/atalk/uuid.h
new file mode 100644 (file)
index 0000000..30c14eb
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+   $Id: uuid.h,v 1.1 2009-02-02 11:55:01 franklahm Exp $
+   Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+ */
+
+#ifndef AFP_UUID_H
+#define AFP_UUID_H
+
+#define UUID_BINSIZE 16
+#define UUID_STRINGSIZE 36
+
+typedef char *uuidp_t;
+typedef char uuid_t[UUID_BINSIZE];
+
+typedef enum {UUID_USER = 1, UUID_GROUP} uuidtype_t;
+extern char *uuidtype[];
+
+/* afp_options.c needs these. defined in libatalk/ldap.c */
+extern char *ldap_host;
+extern int  ldap_auth_method;
+extern char *ldap_auth_dn;
+extern char *ldap_auth_pw;
+extern char *ldap_userbase;
+extern char *ldap_groupbase;
+extern char *ldap_uuid_attr;
+extern char *ldap_name_attr;
+extern char *ldap_group_attr;
+extern char *ldap_uid_attr;
+
+/******************************************************** 
+ * Interface
+ ********************************************************/
+
+/*
+ *   name: give me his name
+ *   type: and type (UUID_USER or UUID_GROUP)
+ *   uuid: and I'll try to return you his uuid
+ * returns 0 on success !=0 on errror
+ */  
+extern int getuuidfromname( const char *name, uuidtype_t type, uuidp_t uuid);
+
+/* 
+ *   uuidp: give me a pointer to a uuid
+ *   name: and I'll allocate a buf with his name and store a pointer to buf
+ *   type: returns USER or GROUP
+ * return 0 on success !=0 on errror
+ */
+extern int getnamefromuuid( uuidp_t uuidp, char **name, uuidtype_t *type);
+
+/* 
+ * convert 16 byte binary uuid to neat ascii represantation including dashes
+ * string is allocated and pointer returned. caller must freee.
+ */
+extern int uuid_bin2string( uuidp_t uuidp, char **uuidstring);
+
+
+/* 
+ * convert ascii string that can include dashes to binary uuid.
+ * caller must provide a buffer.
+ */
+extern void uuid_string2bin( const char *uuidstring, uuidp_t uuid);
+
+#endif /* AFP_UUID_H */
index 0432e2575159a48500cc0360440ccac452462576..de222108ea9d0feabcf42e8e065251d8810ec9e0 100644 (file)
@@ -1,10 +1,13 @@
+
 # Makefile.am for libatalk/
 
-SUBDIRS = adouble asp atp compat cnid dsi nbp netddp util tdb unicode
+SUBDIRS = adouble asp atp compat cnid dsi nbp netddp util tdb unicode acl
 
 lib_LTLIBRARIES = libatalk.la
 
-LIBATALK_DEPS = \
+libatalk_la_SOURCES = dummy.c
+
+libatalk_la_LIBADD  = \
        adouble/libadouble.la   \
        asp/libasp.la           \
        atp/libatp.la           \
@@ -14,9 +17,19 @@ LIBATALK_DEPS = \
        netddp/libnetddp.la     \
        util/libutil.la         \
         tdb/libtdb.la           \
-       unicode/libunicode.la
+       unicode/libunicode.la @LIBATALK_ACLS@
+
+libatalk_la_DEPENDENCIES = \
+       adouble/libadouble.la   \
+       asp/libasp.la           \
+       atp/libatp.la           \
+       compat/libcompat.la     \
+       dsi/libdsi.la           \
+       nbp/libnbp.la           \
+       netddp/libnetddp.la     \
+       util/libutil.la         \
+        tdb/libtdb.la           \
+       unicode/libunicode.la @LIBATALK_ACLS@
 
-libatalk_la_SOURCES = dummy.c
-libatalk_la_LIBADD  = $(LIBATALK_DEPS)
 libatalk_la_LDFLAGS = -static
 
diff --git a/libatalk/acl/.cvsignore b/libatalk/acl/.cvsignore
new file mode 100644 (file)
index 0000000..6e5ca7e
--- /dev/null
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+.deps
+.libs
+*.lo
+*.la
diff --git a/libatalk/acl/Makefile.am b/libatalk/acl/Makefile.am
new file mode 100644 (file)
index 0000000..8dbebe0
--- /dev/null
@@ -0,0 +1,17 @@
+# Makefile.am for libatalk/acl/
+
+noinst_HEADERS = aclldap.h cache.h
+
+if USE_NFSv4_ACLS
+
+noinst_LTLIBRARIES = libacl.la
+libacl_la_SOURCES = \
+       acl.c           \
+       ldap.c          \
+       uuid.c          \
+       cache.c         \
+       ldap_config.c
+libacl_la_LDFLAGS = -lldap
+
+endif
+
diff --git a/libatalk/acl/acl.c b/libatalk/acl/acl.c
new file mode 100644 (file)
index 0000000..6cc87bb
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+   $Id: acl.c,v 1.1 2009-02-02 11:55:01 franklahm Exp $
+   Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atalk/logger.h>
+#include <atalk/afp.h>
+
diff --git a/libatalk/acl/aclldap.h b/libatalk/acl/aclldap.h
new file mode 100644 (file)
index 0000000..085ed0b
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+   $Id: aclldap.h,v 1.1 2009-02-02 11:55:01 franklahm Exp $
+   Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+ */
+
+#ifndef ACLLDAP_H
+#define ACLLDAP_H
+
+#include <atalk/uuid.h>                /* just for uuidtype_t*/
+
+/******************************************************** 
+ * Interface
+ ********************************************************/
+
+/* 
+ *   name: give me his name
+ *   type: and type of USER or GROUP
+ *   uuid_string: returns pointer to allocated string
+ * returns 0 on success !=0 on errror  
+ */
+extern int ldap_getuuidfromname( const char *name, uuidtype_t type, char **uuid_string);
+
+/* 
+ *   uuipd: give me his uuid
+ *   name:  returns pointer to allocated string
+ *   type:  returns type: USER or GROUP
+ * returns 0 on success !=0 on errror
+ */
+extern int ldap_getnamefromuuid( uuidp_t uuidp, char **name, uuidtype_t *type); 
+
+#endif /* ACLLDAP_H */
diff --git a/libatalk/acl/cache.c b/libatalk/acl/cache.c
new file mode 100644 (file)
index 0000000..e84076a
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+   $Id: cache.c,v 1.1 2009-02-02 11:55:01 franklahm Exp $
+   Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#include <atalk/logger.h>
+#include <atalk/afp.h>
+#include <atalk/uuid.h>
+#include "cache.h"
+
+typedef struct cacheduser {
+    unsigned long uid;         /* for future use */
+    uuidtype_t type;
+    uuidp_t uuid;
+    char *name;
+    time_t creationtime;
+    struct cacheduser *prev;
+    struct cacheduser *next;
+} cacheduser_t;
+
+cacheduser_t *namecache[256];  /* indexed by hash of name */
+cacheduser_t *uuidcache[256];  /* indexed by hash of uuid */
+
+/********************************************************
+ * helper function
+ ********************************************************/
+
+static int dumpcache() {
+    int i;
+    int ret = 0;
+    cacheduser_t *entry;
+    char *uuidstring = NULL;
+    char timestr[200];
+    struct tm *tmp = NULL;
+
+    for ( i=0 ; i<256; i++) {
+       if ((entry = namecache[i]) != NULL) {
+           do {
+               uuid_bin2string(entry->uuid, &uuidstring);
+               tmp = localtime(&entry->creationtime);
+               if (tmp == NULL)
+                   continue;
+               if (strftime(timestr, 200, "%c", tmp) == 0)
+                   continue;
+               LOG(log_debug9, logtype_default, "namecache{%d}]: name:%s, uuid:%s, cached: %s", i, entry->name, uuidstring, timestr);
+               free(uuidstring);
+           } while ((entry = entry->next) != NULL);
+       }
+    }
+
+    for ( i=0; i<256; i++) {
+        if ((entry = uuidcache[i]) != NULL) {
+            do {
+                uuid_bin2string(entry->uuid, &uuidstring);
+                tmp = localtime(&entry->creationtime);
+                if (tmp == NULL)
+                    continue;
+                if (strftime(timestr, 200, "%c", tmp) == 0)
+                    continue;
+                LOG(log_debug9, logtype_default, "uuidcache{%d}: uuid:%s, name:%s, type:%d, cached: %s", i, uuidstring, entry->name, entry->type,timestr);
+               free(uuidstring);
+            } while ((entry = entry->next) != NULL);
+        }
+    }
+
+    return ret;
+}
+
+/* hash string it into unsigned char */
+static unsigned char hashstring(unsigned char *str) {
+    unsigned long hash = 5381;
+    unsigned char index;
+    int c;
+    while ((c = *str++) != 0)
+       hash = ((hash << 5) + hash) ^ c; /* (hash * 33) ^ c */
+
+    index = 85 ^ (hash & 0xff);
+    while ((hash = hash >> 8) != 0)
+       index ^= (hash & 0xff);
+    
+    return index;
+}
+
+/* hash uuid_t into unsigned char */
+static unsigned char hashuuid(uuidp_t uuid) {
+    unsigned char index = 83;
+    int i;
+
+    for (i=0; i<16; i++) {
+       index ^= uuid[i];
+       index += uuid[i];
+    }
+    return index;
+}
+
+/******************************************************** 
+ * Interface
+ ********************************************************/
+
+int add_cachebyname( const char *inname, const uuidp_t inuuid, const uuidtype_t type, const unsigned long uid _U_) {
+    int ret = 0;
+    char *name = NULL;
+    uuidp_t uuid;
+    cacheduser_t *cacheduser = NULL;
+    cacheduser_t *entry;
+    unsigned char hash;
+
+    /* allocate mem and copy values */
+    name = malloc(strlen(inname)+1);
+    if (!name) {
+       LOG(log_error, logtype_default, "add_cachebyname: mallor error");
+       ret = -1;
+       goto cleanup;
+    }
+    
+    uuid = malloc(UUID_BINSIZE);
+    if (!uuid) {
+       LOG(log_error, logtype_default, "add_cachebyname: mallor error");
+       ret = -1;
+       goto cleanup;
+    }
+
+    cacheduser = malloc(sizeof(cacheduser_t));
+    if (!cacheduser) {
+       LOG(log_error, logtype_default, "add_cachebyname: mallor error");
+       ret = -1;
+       goto cleanup;
+    }
+
+    strcpy(name, inname);
+    memcpy(uuid, inuuid, UUID_BINSIZE);
+
+    /* fill in the cacheduser */
+    cacheduser->name = name;
+    cacheduser->uuid = uuid;
+    cacheduser->type = type;
+    cacheduser->creationtime = time(NULL);
+    cacheduser->prev = NULL;
+    cacheduser->next = NULL;
+
+    /* get hash */
+    hash = hashstring((unsigned char *)name);
+
+    /* insert cache entry into cache array */
+    if (namecache[hash] == NULL) { /* this queue is empty */
+       namecache[hash] = cacheduser;
+    } else {                   /* queue is not empty, search end of queue*/
+       entry = namecache[hash];
+       while( entry->next != NULL)
+           entry = entry->next;
+       cacheduser->prev = entry;
+       entry->next = cacheduser;
+    }
+
+cleanup:
+    if (ret != 0) {
+       if (name)
+           free(name);
+       if (uuid)
+           free(uuid);
+       if (cacheduser)
+           free(cacheduser);
+    }
+    return ret;
+}
+
+int search_cachebyname( const char *name, uuidtype_t type, uuidp_t uuid) {
+    int ret;
+    unsigned char hash;
+    cacheduser_t *entry;
+    time_t tim;
+
+    hash = hashstring((unsigned char *)name);
+
+    if (! namecache[hash])
+       return -1;
+
+    entry = namecache[hash];
+    while (entry) {
+       ret = strcmp(entry->name, name);
+       if (ret == 0 && type == entry->type) {
+           /* found, now check if expired */
+           tim = time(NULL);
+           if ((tim - entry->creationtime) > CACHESECONDS) {
+               /* remove item */
+               if (entry->prev) /* 2nd to last in queue */
+                   entry->prev->next = entry->next;
+               else            /* queue head */
+                   namecache[hash] = entry->next;
+               free(entry->name);
+               free(entry->uuid);
+               free(entry);
+               return -1;
+           } else {
+               memcpy(uuid, entry->uuid, UUID_BINSIZE);
+                   return 0;
+           }
+       }
+       entry = entry->next;
+    }
+    return -1;
+}
+
+int search_cachebyuuid( uuidp_t uuidp, char **name, uuidtype_t *type) {
+    int ret;
+    unsigned char hash;
+    cacheduser_t *entry;
+    time_t tim;
+
+    hash = hashuuid(uuidp);
+
+    if (! uuidcache[hash])
+        return -1;
+
+    entry = uuidcache[hash];
+    while (entry) {
+        ret = memcmp(entry->uuid, uuidp, UUID_BINSIZE);
+        if (ret == 0) {
+            tim = time(NULL);
+            if ((tim - entry->creationtime) > CACHESECONDS) {
+                LOG(log_info, logtype_default, "search_cachebyuuid: expired: name:\'%s\' in queue {%d}", entry->name, hash);
+                if (entry->prev)
+                    entry->prev->next = entry->next;
+                else
+                    uuidcache[hash] = entry->next;
+                free(entry->name);
+                free(entry->uuid);
+                free(entry);
+                return -1;
+            } else {
+               *name = malloc(strlen(entry->name)+1);
+               strcpy(*name, entry->name);
+               *type = entry->type;
+                return 0;
+            }
+        }
+        entry = entry->next;
+    }
+
+    return -1;
+}
+
+int add_cachebyuuid( uuidp_t inuuid, const char *inname, uuidtype_t type, const unsigned long uid _U_) {
+    int ret = 0;
+    char *name = NULL;
+    uuidp_t uuid;
+    cacheduser_t *cacheduser = NULL;
+    cacheduser_t *entry;
+    unsigned char hash;
+
+    /* allocate mem and copy values */
+    name = malloc(strlen(inname)+1);
+    if (!name) {
+       LOG(log_error, logtype_default, "add_cachebyuuid: mallor error");
+       ret = -1;
+       goto cleanup;
+    }
+    
+    uuid = malloc(UUID_BINSIZE);
+    if (!uuid) {
+       LOG(log_error, logtype_default, "add_cachebyuuid: mallor error");
+       ret = -1;
+       goto cleanup;
+    }
+
+    cacheduser = malloc(sizeof(cacheduser_t));
+    if (!cacheduser) {
+       LOG(log_error, logtype_default, "add_cachebyuuid: mallor error");
+       ret = -1;
+       goto cleanup;
+    }
+
+    strcpy(name, inname);
+    memcpy(uuid, inuuid, UUID_BINSIZE);
+
+    /* fill in the cacheduser */
+    cacheduser->name = name;
+    cacheduser->type = type;
+    cacheduser->uuid = uuid;
+    cacheduser->creationtime = time(NULL);
+    cacheduser->prev = NULL;
+    cacheduser->next = NULL;
+
+    /* get hash */
+    hash = hashuuid(uuid);
+
+    /* insert cache entry into cache array */
+    if (uuidcache[hash] == NULL) { /* this queue is empty */
+       uuidcache[hash] = cacheduser;
+    } else {                   /* queue is not empty, search end of queue*/
+       entry = uuidcache[hash];
+       while( entry->next != NULL)
+           entry = entry->next;
+       cacheduser->prev = entry;
+       entry->next = cacheduser;
+    }
+
+cleanup:
+    if (ret != 0) {
+       if (name)
+           free(name);
+       if (uuid)
+           free(uuid);
+       if (cacheduser)
+           free(cacheduser);
+    }
+    return ret;
+}
diff --git a/libatalk/acl/cache.h b/libatalk/acl/cache.h
new file mode 100644 (file)
index 0000000..7db8ecd
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+   $Id: cache.h,v 1.1 2009-02-02 11:55:01 franklahm Exp $
+   Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+ */
+
+#ifndef LDAPCACHE_H
+#define LDAPCACHE_H
+
+/* 
+ * We need to cache all LDAP querie results, they just take too long.
+ * We do hashing with chaining. Two caches are needed:
+ * 1) name -> uuid, indexed by a hash(f(): hashstring) of the name
+ * 2) uuid -> name, indexed by a hash of the uuid(f(): hashuuid)
+ * Both hash funcs result in a value 0-255 with which we index a array.
+ * We malloc and free all elements as needed.
+ * The cache caches for CACHESECONDS.
+ */
+
+#define CACHESECONDS 600
+
+/******************************************************** 
+ * Interface
+ ********************************************************/
+
+/* 
+ *   name: search for this name
+ *   type: of type USER or GROUP
+ *   uuid: if found copies uuid into this buffer
+ * returns 0 on success, !=0 if not found or on errors
+ */
+extern int search_cachebyname( const char *name, uuidtype_t type, uuidp_t uuid);
+
+/* 
+ *   inname: name
+ *   inuuid: uuid
+ *   type: USER or GROUP
+ *   (uid: unused)
+ * returns 0 on success, !=0 on memory errors
+ */
+extern int add_cachebyname( const char *inname, const uuidp_t inuuid, const uuidtype_t type, const unsigned long uid);
+
+/* same as above but for the uuid cache */
+extern int search_cachebyuuid( uuidp_t uuidp, char **name, uuidtype_t *type);
+extern int add_cachebyuuid( uuidp_t inuuid, const char *inname, uuidtype_t type, const unsigned long uid);
+
+#endif /* LDAPCACHE_H */
diff --git a/libatalk/acl/ldap.c b/libatalk/acl/ldap.c
new file mode 100644 (file)
index 0000000..a78f2a5
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+   $Id: ldap.c,v 1.1 2009-02-02 11:55:01 franklahm Exp $
+   Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <string.h>
+#include <errno.h>
+#include <ldap.h>
+
+#include <atalk/logger.h>
+#include <atalk/afp.h>
+#include <atalk/uuid.h>
+#include <atalk/ldapconfig.h>  /* For struct ldap_pref */
+
+typedef enum {
+    KEEPALIVE = 1
+} ldapcon_t;
+
+/********************************************************
+ * LDAP config stuff. Filled by etc/afpd/acl_config.c
+ ********************************************************/
+int ldap_config_valid;
+
+char *ldap_server;
+int  ldap_auth_method;
+char *ldap_auth_dn;
+char *ldap_auth_pw;
+char *ldap_userbase;
+char *ldap_groupbase;
+char *ldap_uuid_attr;
+char *ldap_name_attr;
+char *ldap_group_attr;
+char *ldap_uid_attr;
+
+struct ldap_pref ldap_prefs[] = {
+    {&ldap_server,     "ldap_server",      0, 0, -1},
+    {&ldap_auth_method,"ldap_auth_method", 1, 1, -1},
+    {&ldap_auth_dn,    "ldap_auth_dn",     0, 0,  0},
+    {&ldap_auth_pw,    "ldap_auth_pw",     0, 0,  0},
+    {&ldap_userbase,   "ldap_userbase",    0, 0, -1},
+    {&ldap_groupbase,  "ldap_groupbase",   0, 0, -1},
+    {&ldap_uuid_attr,  "ldap_uuid_attr",   0, 0, -1},
+    {&ldap_name_attr,  "ldap_name_attr",   0, 0, -1},
+    {&ldap_group_attr, "ldap_group_attr",  0, 0, -1},
+    {&ldap_uid_attr,   "ldap_uid_attr",    0, 0,  0},
+    {NULL,             NULL,               0, 0, -1}
+};
+
+struct pref_array prefs_array[] = {
+    {"ldap_auth_method", "none",   LDAP_AUTH_NONE},
+    {"ldap_auth_method", "simple", LDAP_AUTH_SIMPLE},
+    {"ldap_auth_method", "sasl",   LDAP_AUTH_SASL},
+    {NULL,               NULL,     0}
+};
+
+/********************************************************
+ * Static helper function
+ ********************************************************/
+
+/* 
+ * ldap_getattr_fromfilter_withbase_scope():
+ *   conflags: KEEPALIVE
+ *   scope: LDAP_SCOPE_BASE, LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
+ *   result: return unique search result here, allocated here, caller must free
+ *
+ * All connection managment to the LDAP server is done here. Just set KEEPALIVE if you know
+ * you will be dispatching more than one search in a row, then don't set it with the last search.
+ * You MUST dispatch the queries timely, otherwise the LDAP handle might timeout.
+ */
+static int ldap_getattr_fromfilter_withbase_scope( const char *searchbase, 
+                                                  const char *filter, 
+                                                  char *attributes[], 
+                                                  int scope,
+                                                  ldapcon_t conflags,
+                                                  char **result) {
+    int ret = 0;
+    int ldaperr;
+    int desired_version  = LDAP_VERSION3;
+    static int ldapconnected = 0;
+    static LDAP *ld     = NULL;
+    LDAPMessage* msg    = NULL;
+    LDAPMessage* entry  = NULL;
+
+    char **attribute_values;
+    struct timeval timeout;
+
+//    LOG(log_debug, logtype_afpd,"ldap_getattr_fromfilter_withbase_scope: BEGIN");
+
+    timeout.tv_sec = 3;
+    timeout.tv_usec = 0;
+
+    /* init LDAP if necessary */
+    if (!ldapconnected) {
+//     LOG(log_debug, logtype_default, "ldap_getattr_fromfilter_withbase_scope: LDAP server: \'%s\'", ldap_server);
+       if ((ld = ldap_init(ldap_server, LDAP_PORT)) == NULL ) {
+           LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_init error");
+           return -1;
+       }
+       if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &desired_version) != 0) {
+           /* LDAP_OPT_SUCCESS is not in the proposed standard, so we check for 0
+              http://tools.ietf.org/id/draft-ietf-ldapext-ldap-c-api-05.txt */
+           LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_set_option failed!");
+           ret = -1;
+           goto cleanup;
+       }
+    }
+
+    /* connect */
+    if (!ldapconnected) {
+       if (LDAP_AUTH_NONE == ldap_auth_method) {
+           if (ldap_bind_s(ld, "", "", LDAP_AUTH_SIMPLE) != LDAP_SUCCESS ) {
+               LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_bind failed!");
+               LOG(log_error, logtype_default, "ldap_auth_method: \'%d\'", ldap_auth_method);
+               return -1;
+           }
+           ldapconnected = 1;
+
+       } else if (LDAP_AUTH_SIMPLE == ldap_auth_method) {
+           if (ldap_bind_s(ld, ldap_auth_dn, ldap_auth_pw, ldap_auth_method) != LDAP_SUCCESS ) {
+               LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_bind failed!");
+               LOG(log_error, logtype_default, "ldap_auth_dn: \'%s\', ldap_auth_pw: \'%s\', ldap_auth_method: \'%d\'",
+                   ldap_auth_dn, ldap_auth_pw, ldap_auth_method);
+               return -1;
+           }
+           ldapconnected = 1;
+       }
+    }
+
+//    LOG(log_debug, logtype_afpd,"LDAP start search: base: %s, filter: %s, attr: %s", searchbase, filter, attributes[0]);
+
+    /* start LDAP search */
+    ldaperr = ldap_search_st(ld, searchbase, scope, filter, attributes, 0, &timeout, &msg);
+    if (ldaperr != LDAP_SUCCESS) {
+       LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_search_st failed: %s", ldap_err2string(ldaperr));
+        ret = -1;
+        goto cleanup;
+    }
+
+    /* parse search result */
+//    LOG(log_debug, logtype_default, "ldap_getuuidfromname: got %d entries from ldap search", ldap_count_entries(ld, msg));
+    if (ldap_count_entries(ld, msg) != 1) {
+        ret = -1;
+        goto cleanup;
+    }
+    entry = ldap_first_entry(ld, msg);
+    if (entry == NULL) {
+       LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: error in ldap_first_entry");
+        ret = -1;
+        goto cleanup;
+    }
+    attribute_values = ldap_get_values(ld, entry, attributes[0]);
+    if (attribute_values == NULL) {
+       LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: error in ldap_get_values");
+        ret = -1;
+        goto cleanup;
+    }
+
+//    LOG(log_debug, logtype_afpd,"LDAP Search result: %s: %s", attributes[0], attribute_values[0]);
+
+    /* allocate place for uuid as string */
+    *result = calloc( 1, strlen(attribute_values[0]) + 1);
+    if (*result == NULL) {
+        LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: %s: error calloc'ing",strerror(errno));
+        ret = -1;
+        goto cleanup;
+    }
+    /* get value */
+    strcpy( *result, attribute_values[0]);
+    ldap_value_free(attribute_values);
+
+    /* FIXME: is there another way to free entry ? */
+    while (entry != NULL)
+       entry = ldap_next_entry(ld, entry);
+
+cleanup:
+    if(msg)
+       ldap_msgfree(msg);
+    if(ld) {
+       if ((ldapconnected && !(conflags & KEEPALIVE)) || (*result != NULL)) {
+           ldapconnected = 0;  /* regardless of unbind errors */
+//         LOG(log_debug, logtype_default,"LDAP unbind!");
+           if (ldap_unbind_s(ld) != 0) {
+               LOG(log_error, logtype_default, "ldap_unbind_s: %s\n", ldap_err2string(ldaperr));
+               return -1;
+           }
+       }
+    }
+    return ret;
+}
+
+/******************************************************** 
+ * Interface
+ ********************************************************/
+
+int ldap_getuuidfromname( const char *name, uuidtype_t type, char **uuid_string) {
+    int ret;
+    int len;
+    char filter[256];           /* this should really be enough. we dont want to malloc everything! */
+    char *attributes[]  = { ldap_uuid_attr, NULL};
+    char *ldap_attr;
+
+    /* make filter */
+    if (type == UUID_GROUP)
+       ldap_attr = ldap_group_attr;
+    else /* type hopefully == UUID_USER */
+       ldap_attr = ldap_name_attr;
+    len = snprintf( filter, 256, "%s=%s", ldap_attr, name);
+    if (len >= 256 || len == -1) {
+       LOG(log_error, logtype_default, "ldap_getnamefromuuid: filter error:%d, \"%s\"", len, filter);
+       return -1;
+    }    
+
+
+    if (type == UUID_GROUP) {
+       ret = ldap_getattr_fromfilter_withbase_scope( ldap_groupbase, filter, attributes, LDAP_SCOPE_ONELEVEL, KEEPALIVE, uuid_string);
+    } else  { /* type hopefully == UUID_USER */
+       ret = ldap_getattr_fromfilter_withbase_scope( ldap_userbase, filter, attributes, LDAP_SCOPE_ONELEVEL, 0, uuid_string);
+    }
+    return ret;
+}
+
+int ldap_getnamefromuuid( char *uuidstr, char **name, uuidtype_t *type) {
+    int ret;
+    int len;
+    char filter[256];          /* this should really be enough. we dont want to malloc everything! */
+    char *attributes[]  = { NULL, NULL};
+
+    /* make filter */
+    len = snprintf( filter, 256, "%s=%s", ldap_uuid_attr, uuidstr);
+    if (len >= 256 || len == -1) {
+       LOG(log_error, logtype_default, "ldap_getnamefromuuid: filter overflow:%d, \"%s\"", len, filter);
+       return -1;
+    }    
+    /* search groups first. group acls are probably used more often */
+    attributes[0] = ldap_group_attr;
+    ret = ldap_getattr_fromfilter_withbase_scope( ldap_groupbase, filter, attributes, LDAP_SCOPE_ONELEVEL, KEEPALIVE, name);
+    if (ret == 0) {
+       *type = UUID_GROUP;
+       return 0;
+    }
+    attributes[0] = ldap_name_attr;
+    ret = ldap_getattr_fromfilter_withbase_scope( ldap_userbase, filter, attributes, LDAP_SCOPE_ONELEVEL, 0, name);
+    if (ret == 0) {
+       *type = UUID_USER;
+       return 0;
+    }
+
+    return ret;
+}
+
diff --git a/libatalk/acl/ldap_config.c b/libatalk/acl/ldap_config.c
new file mode 100644 (file)
index 0000000..546cbbe
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+   $Id: ldap_config.c,v 1.1 2009-02-02 11:55:01 franklahm Exp $
+   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifdef HAVE_NFSv4_ACLS
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <ldap.h>
+
+#include <atalk/ldapconfig.h>
+#include <atalk/logger.h>
+
+#define LINESIZE 1024
+
+/* Parse one line. Return result in pref and val */
+static int getpref(char *buf, char **R_pref, char **R_val)
+{
+    char *p, *pref, *val;
+
+    /* a little pre-processing to get rid of spaces and end-of-lines */
+    p = buf;
+    while (p && isspace(*p))
+       p++;
+    if (!p || (*p == '\0'))
+       return -1;
+    
+    if ((val = strchr(p, '=')) == NULL)
+       return -1;
+    while ((*val == '=') || (*val == ' '))
+       val++;
+    if ((val = strtok(val, " \n")) == NULL)
+       return -1;
+    if ((val = strdup(val)) == NULL)
+       return -1;
+    if ((pref = strtok(p, " =")) == NULL)
+       return -1;
+
+    *R_pref = pref;
+    *R_val = val;
+    return 0;
+}
+
+/* Parse the ldap.conf file */
+int acl_ldap_readconfig(char *name)
+{
+    int i, j;
+    FILE *f;
+    char buf[LINESIZE];
+    char *pref, *val;
+
+    f = fopen(name,"r");
+    if (!f) {
+       perror("fopen");
+       return -1;
+    }
+
+    while (!feof(f)) {
+       /* read a line from file */
+       if (!fgets(buf, LINESIZE, f) || buf[0] == '#')
+           continue;
+
+       /* parse and return pref and value */
+       if ((getpref(buf, &pref, &val)) != 0)
+           continue;
+
+       i = 0;
+       /* now see if its a correct pref */
+       while(ldap_prefs[i].pref != NULL) {
+           if ((strcmp(ldap_prefs[i].name, pref)) == 0) {
+               /* ok, found a valid pref */
+
+               /* check if we have pre-defined values */
+               if (0 == ldap_prefs[i].intfromarray) {
+                   /* no, its just a string */
+                   ldap_prefs[i].valid = 0;
+                   if (0 == ldap_prefs[i].strorint)
+                       /* store string as string */
+                       *((char **)(ldap_prefs[i].pref)) = val;
+                   else
+                       /* store as int */
+                       *((int *)(ldap_prefs[i].pref)) = atoi(val);
+               } else {
+                   /* ok, we have string to int mapping for this pref
+                      eg. "none", "simple", "sasl" map to 0, 128, 129 */
+                   j = 0;
+                   while(prefs_array[j].pref != NULL) {
+                       if (((strcmp(prefs_array[j].pref, pref)) == 0) &&
+                           ((strcmp(prefs_array[j].valuestring, val)) == 0)) {
+                           ldap_prefs[i].valid = 0;
+                           *((int *)(ldap_prefs[i].pref)) = prefs_array[j].value;
+                       }
+                       j++;
+                   } /* while j*/
+               } /* else */
+           }
+           i++;
+       } /* while i */
+    }  /*  EOF */
+
+    /* check if the config is sane and complete */
+    i = 0;
+    ldap_config_valid = 1;
+
+    while(ldap_prefs[i].pref != NULL) {
+       if ( ldap_prefs[i].valid != 0) {
+           ldap_config_valid = 0;
+           break;
+       }
+       i++;
+    }
+
+    if (ldap_config_valid) {
+       if (ldap_auth_method == LDAP_AUTH_NONE)
+           LOG(log_debug, logtype_afpd,"ldappref: Pref is ok. Using anonymous bind.");
+       else if (ldap_auth_method == LDAP_AUTH_SIMPLE)
+           LOG(log_debug, logtype_afpd,"ldappref: Pref is ok. Using simple bind.");
+       else {
+           ldap_config_valid = 0;
+           LOG(log_error, logtype_afpd,"ldappref: Pref not ok. SASL not yet supported.");      
+       }
+    } else
+       LOG(log_error, logtype_afpd,"ldappref: Pref is not ok.");       
+    fclose(f);
+    return 0;
+}
+#endif
diff --git a/libatalk/acl/uuid.c b/libatalk/acl/uuid.c
new file mode 100644 (file)
index 0000000..700120b
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+   $Id: uuid.c,v 1.1 2009-02-02 11:55:01 franklahm Exp $
+   Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <atalk/logger.h>
+#include <atalk/afp.h>
+#include <atalk/uuid.h>
+
+#include "aclldap.h"
+#include "cache.h"
+
+char *uuidtype[] = {"NULL","USER", "GROUP"};
+
+/********************************************************
+ * Public helper function
+ ********************************************************/
+
+void uuid_string2bin( const char *uuidstring, uuidp_t uuid) {
+    int nibble = 1;
+    int i = 0;
+    unsigned char c, val = 0;
+
+    while (*uuidstring) {
+       c = *uuidstring;
+       if (c == '-') {
+           uuidstring++;
+           continue;
+       }
+       else if (c <= '9')              /* 0-9 */
+           c -= '0';
+       else if (c <= 'F')      /* A-F */
+           c -= 'A' - 10;
+       else if (c <= 'f')      /* a-f */
+           c-= 'a' - 10;
+
+       if (nibble)
+           val = c * 16;
+       else
+           uuid[i++] = val + c;
+
+       nibble ^= 1;
+       uuidstring++;
+    }
+
+}
+
+int uuid_bin2string( uuidp_t uuid, char **uuidstring) {
+    char ascii[16] = { "0123456789abcdef" };
+    int nibble = 1;
+    int i = 0;
+    unsigned char c;
+    char *s;
+
+    *uuidstring = calloc(1, UUID_STRINGSIZE + 1);
+    if (*uuidstring == NULL) {
+        LOG(log_error, logtype_default, "uuid_bin2string: %s: error calloc'ing",strerror(errno));
+        return -1;
+    }
+    s = *uuidstring;
+
+    while (i < UUID_STRINGSIZE) {
+       c = *uuid;
+       if (nibble)
+           c = c >> 4;
+       else {
+           c &= 0x0f;
+           uuid++;
+       }
+       s[i] = ascii[c];
+       nibble ^= 1;
+       i++;
+       if (i==8 || i==13 || i==18 || i==23)
+           s[i++] = '-';
+    }
+    return 0;
+}
+
+/******************************************************** 
+ * Interface
+ ********************************************************/
+
+int getuuidfromname( const char *name, uuidtype_t type, uuidp_t uuid) {
+    int ret = 0;
+    char *uuid_string = NULL;
+
+    ret = search_cachebyname( name, type, uuid);
+    if (ret == 0) {            /* found in cache */
+       uuid_bin2string( uuid, &uuid_string);
+       LOG(log_debug, logtype_afpd, "getuuidfromname{cache}: name: %s, type: %s -> UUID: %s",name, uuidtype[type], uuid_string);
+    } else  {                   /* if not found in cache */
+       ret = ldap_getuuidfromname( name, type, &uuid_string);
+       if (ret != 0) {
+           LOG(log_error, logtype_afpd, "getuuidfromname: no result from ldap_getuuidfromname");
+           goto cleanup;
+       }
+       uuid_string2bin( uuid_string, uuid);
+       add_cachebyname( name, uuid, type, 0);
+       LOG(log_debug, logtype_afpd, "getuuidfromname{LDAP}: name: %s, type: %s -> UUID: %s",name, uuidtype[type], uuid_string);
+    }
+
+cleanup:
+    free(uuid_string);
+    return ret;
+}
+
+int getnamefromuuid( uuidp_t uuidp, char **name, uuidtype_t *type) {
+    int ret;
+    char *uuid_string = NULL;
+    
+    ret = search_cachebyuuid( uuidp, name, type);
+    if (ret == 0) {            /* found in cache */
+#ifdef DEBUG
+       uuid_bin2string( uuidp, &uuid_string);
+       LOG(log_debug, logtype_afpd, "getnamefromuuid{cache}: UUID: %s -> name: %s, type:%s", uuid_string, *name, uuidtype[*type]);
+#endif
+    } else  {                   /* if not found in cache */
+       uuid_bin2string( uuidp, &uuid_string);
+       ret = ldap_getnamefromuuid( uuid_string, name, type);
+       if (ret != 0) {
+           LOG(log_error, logtype_afpd, "getnamefromuuid: no result from ldap_getuuidfromname");
+           goto cleanup;
+       }
+       add_cachebyuuid( uuidp, *name, *type, 0);
+       LOG(log_debug, logtype_afpd, "getnamefromuuid{LDAP}: UUID: %s -> name: %s, type:%s",uuid_string, *name, uuidtype[*type]);
+    }
+
+cleanup:
+    free(uuid_string);
+    return ret;
+}
index 3f32834e80325e94b0b32b707419d7b3d4fcd0b3..87eee7f2cc4811bb618aadac302dd0edde91e4be 100644 (file)
@@ -1,4 +1,4 @@
-dnl $Id: summary.m4,v 1.3 2008-11-22 12:07:26 didg Exp $
+dnl $Id: summary.m4,v 1.4 2009-02-02 11:55:01 franklahm Exp $
 dnl Autoconf macros, display configure summary
 
 AC_DEFUN([AC_NETATALK_CONFIG_SUMMARY], [
@@ -60,6 +60,7 @@ dnl   fi
        AC_MSG_RESULT([         dropbox kludge:         $netatalk_cv_dropkludge])
        AC_MSG_RESULT([         force volume uid/gid:   $netatalk_cv_force_uidgid])
        AC_MSG_RESULT([         Apple 2 boot support:   $compile_a2boot])
+       AC_MSG_RESULT([         ACL support:            $neta_cv_nfsv4acl])
        if test x"$use_pam_so" = x"yes" -a x"$netatalk_cv_install_pam" = x"no"; then
                AC_MSG_RESULT([])
                AC_MSG_WARN([ PAM support was configured for your system, but the netatalk PAM configuration file])