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