-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)
)
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)
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)
bin/uniconv/Makefile
config/Makefile
contrib/Makefile
+ contrib/acltests/Makefile
contrib/macusers/Makefile
contrib/macusers/macusers
contrib/nu/Makefile
include/Makefile
include/atalk/Makefile
libatalk/Makefile
+ libatalk/acl/Makefile
libatalk/adouble/Makefile
libatalk/asp/Makefile
libatalk/atp/Makefile
AC_NETATALK_LIBS_SUMMARY
AC_NETATALK_CONFIG_SUMMARY
-
A2BOOT =
endif
-SUBDIRS = macusers nu printing shell_utils ${TIMELORD} ${A2BOOT}
+SUBDIRS = macusers nu printing shell_utils ${TIMELORD} ${A2BOOT} acltests
EXTRA_DIST = ICDumpSuffixMap
--- /dev/null
+Makefile
+Makefile.in
+.deps
+.libs
+uuid.log
+uuidtest
--- /dev/null
+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
--- /dev/null
+/*
+ $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 */
--- /dev/null
+
+ 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
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@
-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)/\"
--- /dev/null
+/*
+ $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 */
--- /dev/null
+/*
+ $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);
+}
--- /dev/null
+/*
+ $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
/*
- * $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.
#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"
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;
}
#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;
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);
/*
- * $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.
#include <grp.h>
#include <atalk/logger.h>
#include <atalk/server_ipc.h>
+#include <atalk/uuid.h>
#ifdef TRU64
#include <netdb.h>
#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;
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:
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++;
*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 : \
/*
- * $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.
/* 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 )
/*
- * $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.
#include <atalk/util.h>
#include <atalk/cnid.h>
#include <atalk/logger.h>
+#include <atalk/uuid.h>
#include "directory.h"
#include "desktop.h"
#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;
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 );
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));
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 */
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 :
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 );
}
/*
- * $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.
#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 */
/*
- * $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.
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));
}
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);
/*
- * $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.
#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_;
/*
- * $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.
#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.
*/
*
* Note: the previous method, using access(), does not work correctly
* over NFS.
- * FIXME what about ACL?
*
* dir parameter is used by AFS
*/
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 )
#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;
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,
/* 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
};
/* =======================================
/*
- * $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.
{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}
};
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)
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 );
/*
- * $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.
#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)
#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
#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
--- /dev/null
+#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
--- /dev/null
+/*
+ $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 */
+
# 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 \
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
--- /dev/null
+Makefile
+Makefile.in
+.deps
+.libs
+*.lo
+*.la
--- /dev/null
+# 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
+
--- /dev/null
+/*
+ $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>
+
--- /dev/null
+/*
+ $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 */
--- /dev/null
+/*
+ $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;
+}
--- /dev/null
+/*
+ $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 */
--- /dev/null
+/*
+ $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;
+}
+
--- /dev/null
+/*
+ $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
--- /dev/null
+/*
+ $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;
+}
-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], [
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])