]> arthur.barton.de Git - netatalk.git/commitdiff
Enhanced POSIX ACL mapping semantics from Laura Mueller <laura-mueller@uni-duesseldor...
authorFrank Lahm <franklahm@googlemail.com>
Thu, 13 Oct 2011 15:02:44 +0000 (17:02 +0200)
committerFrank Lahm <franklahm@googlemail.com>
Thu, 13 Oct 2011 15:02:44 +0000 (17:02 +0200)
etc/afpd/acls.c
etc/afpd/directory.c
etc/afpd/filedir.c
etc/afpd/unix.c
include/atalk/acl.h
libatalk/acl/unix.c
libatalk/vfs/acl.c
libatalk/vfs/unix.c
libatalk/vfs/vfs.c

index e955cb5867d9c00497d249fae6d4326346fb5a08..60c95a5166bbc4bee3a505ef69098ca2a12bb97e 100644 (file)
@@ -1,5 +1,6 @@
 /*
   Copyright (c) 2008, 2009, 2010 Frank Lahm <franklahm@gmail.com>
+  Copyright (c) 2011 Laura Mueller <laura-mueller@uni-duesseldorf.de>
 
   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
 #define MAP_MASK               31
 #define IS_DIR                 32
 
+/* bit flags for set_acl() and map_aces_darwin_to_posix() */
+#define HAS_DEFAULT_ACL 0x01
+#define HAS_EXT_DEFAULT_ACL 0x02
+
 /********************************************************
  * Solaris funcs
  ********************************************************/
@@ -397,9 +402,10 @@ static int posix_acl_rights(const char *path,
                             uint32_t *result)
 {
     EC_INIT;
-    int havemask = 0;
     int entry_id = ACL_FIRST_ENTRY;
-    uint32_t rights = 0, maskrights = 0;
+    uint32_t rights = 0; /* rights which do not depend on ACL_MASK */
+    uint32_t acl_rights = 0; /* rights which are subject to limitations imposed by ACL_MASK */
+    uint32_t mask_rights = 0xffffffff;
     uid_t *uid = NULL;
     gid_t *gid = NULL;
     acl_t acl = NULL;
@@ -409,68 +415,68 @@ static int posix_acl_rights(const char *path,
     EC_NULL_LOGSTR(acl = acl_get_file(path, ACL_TYPE_ACCESS),
                    "acl_get_file(\"%s\"): %s", fullpathname(path), strerror(errno));
 
-    /* itereate through all ACEs to get the mask */
-    while (!havemask && acl_get_entry(acl, entry_id, &e) == 1) {
-        entry_id = ACL_NEXT_ENTRY;
-        EC_ZERO_LOG(acl_get_tag_type(e, &tag));
-        switch (tag) {
-        case ACL_MASK:
-            maskrights = posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
-            LOG(log_maxdebug, logtype_afpd, "maskrights: 0x%08x", maskrights);
-            havemask = 1;
-            break;
-        default:
-            continue;
-        }
-    }
-
-    /* itereate through all ACEs */
-    entry_id = ACL_FIRST_ENTRY;
+    /* Iterate through all ACEs. If we apply mask_rights later there is no need to iterate twice. */
     while (acl_get_entry(acl, entry_id, &e) == 1) {
         entry_id = ACL_NEXT_ENTRY;
         EC_ZERO_LOG(acl_get_tag_type(e, &tag));
+
         switch (tag) {
-        case ACL_USER:
-            EC_NULL_LOG(uid = (uid_t *)acl_get_qualifier(e));
-            if (*uid == uuid) {
-                LOG(log_maxdebug, logtype_afpd, "ACL_USER: %u", *uid);
-                rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
-            }
-            acl_free(uid);
-            uid = NULL;
-            break;
-        case ACL_USER_OBJ:
-            if (sb->st_uid == uuid) {
-                LOG(log_maxdebug, logtype_afpd, "ACL_USER_OBJ: %u", sb->st_uid);
-                rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
-            }
-            break;
-        case ACL_GROUP:
-            EC_NULL_LOG(gid = (gid_t *)acl_get_qualifier(e));
-            if (gmem(*gid)) {
-                LOG(log_maxdebug, logtype_afpd, "ACL_GROUP: %u", *gid);
-                rights |= (posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode)) & maskrights);
-            }
-            acl_free(gid);
-            gid = NULL;
-            break;
-        case ACL_GROUP_OBJ:
-            if (!(sb->st_uid == uuid) && gmem(sb->st_gid)) {
-                LOG(log_maxdebug, logtype_afpd, "ACL_GROUP_OBJ: %u", sb->st_gid);
-                rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));            
-            }
-            break;
-        case ACL_OTHER:
-            if (!(sb->st_uid == uuid) && !gmem(sb->st_gid)) {
-                LOG(log_maxdebug, logtype_afpd, "ACL_OTHER");
-                rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
-            }
-            break;
-        default:
-            continue;
+            case ACL_USER_OBJ:
+                if (sb->st_uid == uuid) {
+                    LOG(log_maxdebug, logtype_afpd, "ACL_USER_OBJ: %u", sb->st_uid);
+                    rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+                }
+                break;
+
+            case ACL_USER:
+                EC_NULL_LOG(uid = (uid_t *)acl_get_qualifier(e));
+
+                if (*uid == uuid) {
+                    LOG(log_maxdebug, logtype_afpd, "ACL_USER: %u", *uid);
+                    acl_rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+                }
+                acl_free(uid);
+                uid = NULL;
+                break;
+
+            case ACL_GROUP_OBJ:
+                if (!(sb->st_uid == uuid) && gmem(sb->st_gid)) {
+                    LOG(log_maxdebug, logtype_afpd, "ACL_GROUP_OBJ: %u", sb->st_gid);
+                    acl_rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+                }
+                break;
+
+            case ACL_GROUP:
+                EC_NULL_LOG(gid = (gid_t *)acl_get_qualifier(e));
+
+                if (gmem(*gid)) {
+                    LOG(log_maxdebug, logtype_afpd, "ACL_GROUP: %u", *gid);
+                    acl_rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+                }
+                acl_free(gid);
+                gid = NULL;
+                break;
+
+            case ACL_MASK:
+                mask_rights = posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+                LOG(log_maxdebug, logtype_afpd, "maskrights: 0x%08x", mask_rights);
+                break;
+
+            case ACL_OTHER:
+                if (!(sb->st_uid == uuid) && !gmem(sb->st_gid)) {
+                    LOG(log_maxdebug, logtype_afpd, "ACL_OTHER");
+                    rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+                }
+                break;
+
+            default:
+                continue;
         }
     } /* while */
 
+    /* apply the mask and collect the rights */
+    rights |= (acl_rights & mask_rights);
+
     *result |= rights;
 
 EC_CLEANUP:
@@ -480,6 +486,142 @@ EC_CLEANUP:
     EC_EXIT;
 }
 
+/*!
+ * Helper function for posix_acls_to_uaperms() to convert Posix ACL permissions
+ * into access rights needed to fill ua_permissions of a FPUnixPrivs structure.
+ *
+ * @param entry     (r) Posix ACL entry
+ *
+ * @returns         access rights
+ */
+static u_char acl_permset_to_uarights(acl_entry_t entry) {
+    acl_permset_t permset;
+    u_char rights = 0;
+
+    if (acl_get_permset(entry, &permset) == -1)
+        return rights;
+
+#ifdef HAVE_ACL_GET_PERM_NP
+    if (acl_get_perm_np(permset, ACL_READ))
+#else
+    if (acl_get_perm(permset, ACL_READ))
+#endif
+        rights |= AR_UREAD;
+
+#ifdef HAVE_ACL_GET_PERM_NP
+    if (acl_get_perm_np(permset, ACL_WRITE))
+#else
+    if (acl_get_perm(permset, ACL_WRITE))
+#endif
+        rights |= AR_UWRITE;
+
+#ifdef HAVE_ACL_GET_PERM_NP
+    if (acl_get_perm_np(permset, ACL_EXECUTE))
+#else
+    if (acl_get_perm(permset, ACL_EXECUTE))
+#endif
+        rights |= AR_USEARCH;
+
+    return rights;
+}
+
+/*!
+ * Update FPUnixPrivs for a file-system object on a volume supporting ACLs
+ *
+ * Checks permissions granted by ACLS for a user to one fs-object and
+ * updates user and group permissions in given struct maccess. As OS X
+ * doesn't conform to Posix 1003.1e Draft 17 it expects proper group
+ * permissions in st_mode of struct stat even if the fs-object has an
+ * ACL_MASK entry, st_mode gets modified to properly reflect group
+ * permissions.
+ *
+ * @param path           (r) path to filesystem object
+ * @param sb             (rw) struct stat of path
+ * @param maccess        (rw) struct maccess of path
+ *
+ * @returns                  0 or -1 on error
+ */
+static int posix_acls_to_uaperms(const char *path, struct stat *sb, struct maccess *ma) {
+    EC_INIT;
+
+    int entry_id = ACL_FIRST_ENTRY;
+    acl_entry_t entry;
+    acl_tag_t tag;
+    acl_t acl = NULL;
+    uid_t *uid;
+    gid_t *gid;
+
+    u_char group_rights = 0x00;
+    u_char acl_rights = 0x00;
+    u_char mask = 0xff;
+
+    EC_NULL_LOG(acl = acl_get_file(path, ACL_TYPE_ACCESS));
+
+    /* iterate through all ACEs */
+    while (acl_get_entry(acl, entry_id, &entry) == 1) {
+        entry_id = ACL_NEXT_ENTRY;
+        EC_ZERO_LOG(acl_get_tag_type(entry, &tag));
+
+        switch (tag) {
+            case ACL_USER:
+                EC_NULL_LOG(uid = (uid_t *)acl_get_qualifier(entry));
+
+                if (*uid == uuid) {
+                    LOG(log_maxdebug, logtype_afpd, "ACL_USER: %u", *uid);
+                    acl_rights |= acl_permset_to_uarights(entry);
+                }
+                acl_free(uid);
+                break;
+
+            case ACL_GROUP_OBJ:
+                group_rights = acl_permset_to_uarights(entry);
+                LOG(log_maxdebug, logtype_afpd, "ACL_GROUP_OBJ: %u", sb->st_gid);
+
+                if (gmem(sb->st_gid))
+                    acl_rights |= group_rights;
+                break;
+
+            case ACL_GROUP:
+                EC_NULL_LOG(gid = (gid_t *)acl_get_qualifier(entry));
+
+                if (gmem(*gid)) {
+                    LOG(log_maxdebug, logtype_afpd, "ACL_GROUP: %u", *gid);
+                    acl_rights |= acl_permset_to_uarights(entry);
+                }
+                acl_free(gid);
+                break;
+
+            case ACL_MASK:
+                mask = acl_permset_to_uarights(entry);
+                LOG(log_maxdebug, logtype_afpd, "ACL_MASK: 0x%02x", mask);
+                break;
+
+            default:
+                break;
+        }
+    }
+    /* apply the mask and adjust user and group permissions */
+    ma->ma_user |= (acl_rights & mask);
+    ma->ma_group = (group_rights & mask);
+
+    /* update st_mode to properly reflect group permissions */
+    sb->st_mode &= ~S_IRWXG;
+
+    if (ma->ma_group & AR_USEARCH)
+        sb->st_mode |= S_IXGRP;
+
+    if (ma->ma_group & AR_UWRITE)
+        sb->st_mode |= S_IWGRP;
+
+    if (ma->ma_group & AR_UREAD)
+        sb->st_mode |= S_IRGRP;
+
+EC_CLEANUP:
+    if (acl) acl_free(acl);
+
+    EC_EXIT;
+}
+
 /*!
  * Add entries of one acl to another acl
  *
@@ -524,7 +666,7 @@ static acl_perm_t map_darwin_right_to_posix_permset(uint32_t darwin_ace_rights,
     if (darwin_ace_rights & DARWIN_ACE_READ_DATA)
         perm |= ACL_READ;
 
-    if (darwin_ace_rights & (DARWIN_ACE_WRITE_DATA | (DARWIN_ACE_DELETE_CHILD & is_dir)))
+    if (darwin_ace_rights & (DARWIN_ACE_WRITE_DATA | (is_dir ? DARWIN_ACE_DELETE_CHILD : 0)))
         perm |= ACL_WRITE;
 
     if (darwin_ace_rights & DARWIN_ACE_EXECUTE)
@@ -584,7 +726,7 @@ static int posix_acl_add_perm(acl_t *aclp, acl_tag_t type, uid_t id, acl_perm_t
         EC_ZERO_LOG(acl_add_perm(permset, perm));
         EC_ZERO_LOG(acl_set_permset(e, permset));
     }
-    
+
 EC_CLEANUP:
     if (eid) acl_free(eid);
 
@@ -601,18 +743,23 @@ EC_CLEANUP:
  * - we throw away DARWIN_ACE_FLAGS_LIMIT_INHERIT (can't be mapped), thus the ACL will
  *   not be limited
  *
- * @param darwin_aces  (r)  pointer to darwin_aces buffer
- * @param def_aclp     (rw) directories: pointer to an initialized acl_t with the default acl
- *                          files: *def_aclp will be NULL
- * @param acc_aclp     (rw) pointer to an initialized acl_t with the access acl
- * @param ace_count    (r)  number of ACEs in darwin_aces buffer
+ * @param darwin_aces        (r)  pointer to darwin_aces buffer
+ * @param def_aclp           (rw) directories: pointer to an initialized acl_t with
+                                  the default acl files: *def_aclp will be NULL
+ * @param acc_aclp           (rw) pointer to an initialized acl_t with the access acl
+ * @param ace_count          (r)  number of ACEs in darwin_aces buffer
+ * @param default_acl_flags  (rw) flags to indicate if the object has a basic default
+ *                                acl or an extended default acl.
  *
- * @returns 0 on success storing the result in aclp, -1 on error.
+ * @returns 0 on success storing the result in aclp, -1 on error. default_acl_flags
+ * is set to HAS_DEFAULT_ACL|HAS_EXT_DEFAULT_ACL in case there is at least one
+ * extended default ace. Otherwise default_acl_flags is left unchanged.
  */
 static int map_aces_darwin_to_posix(const darwin_ace_t *darwin_aces,
                                     acl_t *def_aclp,
                                     acl_t *acc_aclp,
-                                    int ace_count)
+                                    int ace_count,
+                                    uint32_t *default_acl_flags)
 {
     EC_INIT;
     char *name = NULL;
@@ -668,7 +815,7 @@ static int map_aces_darwin_to_posix(const darwin_ace_t *darwin_aces,
             }
             /* add it as default ace */
             EC_ZERO_LOG(posix_acl_add_perm(def_aclp, tag, id, perm));
-
+            *default_acl_flags = (HAS_DEFAULT_ACL|HAS_EXT_DEFAULT_ACL);
 
             if (! (darwin_ace_flags & DARWIN_ACE_FLAGS_ONLY_INHERIT))
                 /* if it not a "inherit only" ace, it must be added as access aces too */
@@ -1100,44 +1247,77 @@ static int set_acl(const struct vol *vol,
                    uint32_t ace_count)
 {
     EC_INIT;
-    acl_t def_acl = NULL;
-    acl_t acc_acl = NULL;
+    struct stat st;
+    acl_t default_acl = NULL;
+    acl_t access_acl = NULL;
+    acl_entry_t entry;
+    acl_tag_t tag;
+    int entry_id = ACL_FIRST_ENTRY;
+    int has_def_acl = 0;
+    /* flags to indicate if the object has a minimal default acl and/or an extended
+     * default acl.
+     */
+    uint32_t default_acl_flags = 0;
 
     LOG(log_maxdebug, logtype_afpd, "set_acl: BEGIN");
 
-    struct stat st;
-    EC_ZERO_LOG_ERR(lstat(name, &st), AFPERR_NOOBJ);
+    EC_NULL_LOG_ERR(access_acl = acl_get_file(name, ACL_TYPE_ACCESS), AFPERR_MISC);
 
-    /* seed default ACL with access ACL */
-    if (S_ISDIR(st.st_mode))
-        EC_NULL_LOG_ERR(def_acl = acl_get_file(name, ACL_TYPE_ACCESS), AFPERR_MISC);
+    /* Iterate through acl and remove all extended acl entries. */
+    while (acl_get_entry(access_acl, entry_id, &entry) == 1) {
+        entry_id = ACL_NEXT_ENTRY;
+        EC_ZERO_LOG(acl_get_tag_type(entry, &tag));
+
+        if ((tag == ACL_USER) || (tag == ACL_GROUP) || (tag == ACL_MASK)) {
+            EC_ZERO_LOG_ERR(acl_delete_entry(access_acl, entry), AFPERR_MISC);
+        }
+    } /* while */
 
-    /* for files def_acl will be NULL */
+   /* In case we are acting on a directory prepare a default acl. For files default_acl will be NULL.
+    * If the directory already has a default acl it will be preserved.
+    */
+   EC_ZERO_LOG_ERR(lstat(name, &st), AFPERR_NOOBJ);
 
-    /* create access acl from mode */
-    EC_NULL_LOG_ERR(acc_acl = acl_from_mode(st.st_mode), AFPERR_MISC);
+   if (S_ISDIR(st.st_mode)) {
+       default_acl = acl_get_file(name, ACL_TYPE_DEFAULT);
 
+       if (default_acl) {
+           /* If default_acl is not empty then the dir has a default acl. */
+           if (acl_get_entry(default_acl, ACL_FIRST_ENTRY, &entry) == 1)
+               default_acl_flags = HAS_DEFAULT_ACL;
+
+           acl_free(default_acl);
+       }
+       default_acl = acl_dup(access_acl);
+    }
     /* adds the clients aces */
-    EC_ZERO_ERR(map_aces_darwin_to_posix(daces, &def_acl, &acc_acl, ace_count), AFPERR_MISC);
+    EC_ZERO_ERR(map_aces_darwin_to_posix(daces, &default_acl, &access_acl, ace_count, &default_acl_flags), AFPERR_MISC);
 
     /* calcuate ACL mask */
-    EC_ZERO_LOG_ERR(acl_calc_mask(&acc_acl), AFPERR_MISC);
+    EC_ZERO_LOG_ERR(acl_calc_mask(&access_acl), AFPERR_MISC);
 
     /* is it ok? */
-    EC_ZERO_LOG_ERR(acl_valid(acc_acl), AFPERR_MISC);
+    EC_ZERO_LOG_ERR(acl_valid(access_acl), AFPERR_MISC);
 
     /* set it */
-    EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_ACCESS, acc_acl), AFPERR_MISC);
-    EC_ZERO_LOG_ERR(vol->vfs->vfs_acl(vol, name, ACL_TYPE_ACCESS, 0, acc_acl), AFPERR_MISC);
-
-    if (def_acl) {
-        EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_DEFAULT, def_acl), AFPERR_MISC);
-        EC_ZERO_LOG_ERR(vol->vfs->vfs_acl(vol, name, ACL_TYPE_DEFAULT, 0, def_acl), AFPERR_MISC);
+    EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_ACCESS, access_acl), AFPERR_MISC);
+    EC_ZERO_LOG_ERR(vol->vfs->vfs_acl(vol, name, ACL_TYPE_ACCESS, 0, access_acl), AFPERR_MISC);
+
+    if (default_acl) {
+        /* If the dir has an extended default acl it's ACL_MASK must be updated.*/
+        if (default_acl_flags & HAS_EXT_DEFAULT_ACL)
+            EC_ZERO_LOG_ERR(acl_calc_mask(&default_acl), AFPERR_MISC);
+
+        if (default_acl_flags) {
+            EC_ZERO_LOG_ERR(acl_valid(default_acl), AFPERR_MISC);
+            EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_DEFAULT, default_acl), AFPERR_MISC);
+            EC_ZERO_LOG_ERR(vol->vfs->vfs_acl(vol, name, ACL_TYPE_DEFAULT, 0, default_acl), AFPERR_MISC);
+        }
     }
 
 EC_CLEANUP:
-    acl_free(acc_acl);
-    acl_free(def_acl);
+    if (access_acl) acl_free(access_acl);
+    if (default_acl) acl_free(default_acl);
 
     LOG(log_maxdebug, logtype_afpd, "set_acl: END");
     EC_EXIT;
@@ -1550,10 +1730,6 @@ int acltoownermode(char *path, struct stat *st, struct maccess *ma)
 
 #ifdef HAVE_SOLARIS_ACLS
     EC_ZERO_LOG(solaris_acl_rights(path, st, &rights));
-#endif
-#ifdef HAVE_POSIX_ACLS
-    EC_ZERO_LOG(posix_acl_rights(path, st, &rights));
-#endif
 
     LOG(log_maxdebug, logtype_afpd, "rights: 0x%08x", rights);
 
@@ -1563,8 +1739,13 @@ int acltoownermode(char *path, struct stat *st, struct maccess *ma)
         ma->ma_user |= AR_UWRITE;
     if (rights & (DARWIN_ACE_EXECUTE | DARWIN_ACE_SEARCH))
         ma->ma_user |= AR_USEARCH;
+#endif
+
+#ifdef HAVE_POSIX_ACLS
+    EC_ZERO_LOG(posix_acls_to_uaperms(path, st, ma));
+#endif
 
-    LOG(log_maxdebug, logtype_afpd, "resulting user maccess: 0x%02x", ma->ma_user);
+    LOG(log_maxdebug, logtype_afpd, "resulting user maccess: 0x%02x group maccess: 0x%02x", ma->ma_user, ma->ma_group);
 
 EC_CLEANUP:
     EC_EXIT;
index 3d10cf6bb4e80025a576e479a7088fc063c4b7e4..be52d36d1350d833f53c5c1a8448f7d1e400865d 100644 (file)
@@ -1664,6 +1664,9 @@ int getdirparams(const struct vol *vol,
             break;
 
         case DIRPBIT_UNIXPR :
+            /* accessmode may change st_mode with ACLs */
+            accessmode( upath, &ma, dir, st);
+
             aint = htonl(st->st_uid);
             memcpy( data, &aint, sizeof( aint ));
             data += sizeof( aint );
@@ -1676,8 +1679,6 @@ int getdirparams(const struct vol *vol,
             memcpy( data, &aint, sizeof( aint ));
             data += sizeof( aint );
 
-            accessmode( upath, &ma, dir , st);
-
             *data++ = ma.ma_user;
             *data++ = ma.ma_world;
             *data++ = ma.ma_group;
index 16c329ab72d6b38c7580d416b8fe6c1e57ed88b5..6fe3c855b2d32d007432a9623ffa2cb2f0b6f8f8 100644 (file)
@@ -101,7 +101,7 @@ int matchfile2dirperms(
                     upath, strerror(errno));
                 ret = AFPERR_ACCESS;
             }
-            else if ((!S_ISLNK(st->st_mode)) && (chmod(upath,(st.st_mode&~default_options.umask)| S_IRGRP| S_IROTH) < 0))
+            else if ((!S_ISLNK(st->st_mode)) && (chmod_acl(upath,(st.st_mode&~default_options.umask)| S_IRGRP| S_IROTH) < 0))
             {
                 LOG(log_error, logtype_afpd,
                     "matchfile2dirperms(%s): Error adding file read permissions: %s",
@@ -115,7 +115,7 @@ int matchfile2dirperms(
                     adpath, strerror(errno));
                 ret = AFPERR_ACCESS;
             }
-            else if (chmod(adpath, (st.st_mode&~default_options.umask)| S_IRGRP| S_IROTH) < 0)
+            else if (chmod_acl(adpath, (st.st_mode&~default_options.umask)| S_IRGRP| S_IROTH) < 0)
             {
                 LOG(log_error, logtype_afpd,
                     "matchfile2dirperms(%s):  Error adding AD file read permissions: %s",
index f604d79b8258a44ccdb5b228d18c543f40bda77c..0e444bcfffe36d712c7a6a9de7887a8cda497b05 100644 (file)
@@ -283,17 +283,17 @@ int setdeskmode(const mode_t mode)
             }
 
             if (S_ISDIR(st.st_mode)) {
-                if ( chmod( modbuf,  (DIRBITS | mode) & ~default_options.umask ) < 0 && errno != EPERM ) {
+                if ( chmod_acl( modbuf,  (DIRBITS | mode) & ~default_options.umask ) < 0 && errno != EPERM ) {
                      LOG(log_error, logtype_afpd, "setdeskmode: chmod %s: %s",fullpathname(modbuf), strerror(errno) );
                 }
-            } else if ( chmod( modbuf,  mode & ~(default_options.umask | EXEC_MODE) ) < 0 && errno != EPERM ) {
+            } else if ( chmod_acl( modbuf,  mode & ~(default_options.umask | EXEC_MODE) ) < 0 && errno != EPERM ) {
                 LOG(log_error, logtype_afpd, "setdeskmode: chmod %s: %s",fullpathname(modbuf), strerror(errno) );
             }
 
         }
         closedir( sub );
         /* XXX: need to preserve special modes */
-        if ( chmod( deskp->d_name,  (DIRBITS | mode) & ~default_options.umask ) < 0 && errno != EPERM ) {
+        if ( chmod_acl( deskp->d_name,  (DIRBITS | mode) & ~default_options.umask ) < 0 && errno != EPERM ) {
             LOG(log_error, logtype_afpd, "setdeskmode: chmod %s: %s",fullpathname(deskp->d_name), strerror(errno) );
         }
     }
@@ -303,7 +303,7 @@ int setdeskmode(const mode_t mode)
         return -1;
     }
     /* XXX: need to preserve special modes */
-    if ( chmod( ".AppleDesktop",  (DIRBITS | mode) & ~default_options.umask ) < 0 && errno != EPERM ) {
+    if ( chmod_acl( ".AppleDesktop",  (DIRBITS | mode) & ~default_options.umask ) < 0 && errno != EPERM ) {
         LOG(log_error, logtype_afpd, "setdeskmode: chmod %s: %s", fullpathname(".AppleDesktop"),strerror(errno) );
     }
     return( 0 );
index 53d10b19aa011383c1d8f40788f4984332e70b18..95aa0753401bb3385013192c3ea620a1fdd327b1 100644 (file)
 
 #ifdef HAVE_SOLARIS_ACLS
 #include <sys/acl.h>
-#endif  /* HAVE_SOLARIS_ACLS */
 
-#ifdef HAVE_POSIX_ACLS
-#include <sys/types.h>
-#include <sys/acl.h>
-#endif /* HAVE_POSIX_ACLS */
+#define chmod_acl nfsv4_chmod
 
-#ifdef HAVE_SOLARIS_ACLS
-#define chmod nfsv4_chmod
 extern int get_nfsv4_acl(const char *name, ace_t **retAces);
 extern int strip_trivial_aces(ace_t **saces, int sacecount);
 extern int strip_nontrivial_aces(ace_t **saces, int sacecount);
 extern ace_t *concat_aces(ace_t *aces1, int ace1count, ace_t *aces2, int ace2count);
 extern int nfsv4_chmod(char *name, mode_t mode);
-#endif /* HAVE_SOLARIS_ACLS */
+
+#endif  /* HAVE_SOLARIS_ACLS */
+
+#ifdef HAVE_POSIX_ACLS
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/acl.h>
+
+#define chmod_acl posix_chmod
+#define fchmod_acl posix_fchmod
+
+extern int posix_chmod(const char *name, mode_t mode);
+extern int posix_fchmod(int fd, mode_t mode);
+
+#endif /* HAVE_POSIX_ACLS */
 
 extern int remove_acl_vfs(const char *name);
 
+#else /* HAVE_ACLS=no */
+
+#define chmod_acl chmod
+
 #endif /* HAVE_ACLS */
 
 #endif /* ATALK_ACL_H */
index f72d9aaef7e3f01d96143de53cf20af517b259b4..31027abe581da133c0de503b479c2ccb301142ff 100644 (file)
@@ -1,5 +1,6 @@
 /*
   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+  Copyright (c) 2011 Laura Mueller <laura-mueller@uni-duesseldorf.de>
 
   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
@@ -16,7 +17,7 @@
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
-#ifdef HAVE_SOLARIS_ACLS
+#ifdef HAVE_ACLS
 
 #include <unistd.h>
 #include <sys/types.h>
@@ -33,6 +34,8 @@
 #include <atalk/util.h>
 #include <atalk/acl.h>
 
+#ifdef HAVE_SOLARIS_ACLS
+
 /* Get ACL. Allocates storage as needed. Caller must free.
  * Returns no of ACEs or -1 on error.  */
 int get_nfsv4_acl(const char *name, ace_t **retAces)
@@ -227,9 +230,6 @@ int nfsv4_chmod(char *name, mode_t mode)
     if ((noaces = strip_trivial_aces(&oacl, noaces)) == -1) /* (2) */
         goto exit;
 
-#ifdef chmod
-#undef chmod
-#endif
     if (chmod(name, mode) != 0) /* (3) */
         goto exit;
 
@@ -258,3 +258,229 @@ exit:
 }
 
 #endif /* HAVE_SOLARIS_ACLS */
+
+#ifdef HAVE_POSIX_ACLS
+
+/* This is a workaround for chmod() on filestystems supporting Posix 1003.1e draft 17
+ * compliant ACLs. For objects with extented ACLs, eg objects with an ACL_MASK entry,
+ * chmod() manipulates ACL_MASK instead of ACL_GROUP_OBJ. As OS X isn't aware of
+ * this behavior calling FPSetFileDirParms may lead to unpredictable results. For
+ * more information see section 23.1.2 of Posix 1003.1e draft 17.
+ *
+ * posix_chmod() accepts the same arguments as chmod() and returns 0 in case of
+ * success or -1 in case something went wrong.
+ */
+
+#define SEARCH_GROUP_OBJ 0x01
+#define SEARCH_MASK 0x02
+
+int posix_chmod(const char *name, mode_t mode) {
+    int ret = 0;
+    int entry_id = ACL_FIRST_ENTRY;
+    acl_entry_t entry;
+    acl_entry_t group_entry;
+    acl_tag_t tag;
+    acl_t acl;
+    u_char not_found = (SEARCH_GROUP_OBJ|SEARCH_MASK); /* used as flags */
+
+    LOG(log_maxdebug, logtype_afpd, "posix_chmod: %s mode: 0x%08x", name, mode);
+
+    /* Call chmod() first because there might be some special bits to be set which
+     * aren't related to access control.
+     */
+    ret = chmod(name, mode);
+
+    if (ret)
+       goto done;
+
+    /* Check if the underlying filesystem supports ACLs. */
+    acl = acl_get_file(name, ACL_TYPE_ACCESS);
+
+    if (acl) {
+        /* There is no need to keep iterating once we have found ACL_GROUP_OBJ and ACL_MASK. */
+        while ((acl_get_entry(acl, entry_id, &entry) == 1) && not_found) {
+            entry_id = ACL_NEXT_ENTRY;
+
+            ret = acl_get_tag_type(entry, &tag);
+
+           if (ret) {
+                LOG(log_error, logtype_afpd, "posix_chmod: Failed to get tag type.");
+                goto cleanup;
+           }
+
+            switch (tag) {
+                case ACL_GROUP_OBJ:
+                    group_entry = entry;
+                    not_found &= ~SEARCH_GROUP_OBJ;
+                    break;
+
+                case ACL_MASK:
+                    not_found &= ~SEARCH_MASK;
+                    break;
+
+                default:
+                    break;
+            }
+        }
+        if (!not_found) {
+            /* The filesystem object has extented ACLs. We have to update ACL_GROUP_OBJ
+             * with the group permissions.
+             */
+           acl_permset_t permset;
+            acl_perm_t perm = 0;
+
+            ret = acl_get_permset(group_entry, &permset);
+
+            if (ret) {
+                LOG(log_error, logtype_afpd, "posix_chmod: Can't get permset.");
+                goto cleanup;
+            }
+            ret = acl_clear_perms(permset);
+
+            if (ret)
+                goto cleanup;
+
+            if (mode & S_IXGRP)
+                perm |= ACL_EXECUTE;
+
+            if (mode & S_IWGRP)
+                perm |= ACL_WRITE;
+
+            if (mode & S_IRGRP)
+                perm |= ACL_READ;
+
+            ret = acl_add_perm(permset, perm);
+
+            if (ret)
+                goto cleanup;
+
+            ret = acl_set_permset(group_entry, permset);
+
+            if (ret) {
+                LOG(log_error, logtype_afpd, "posix_chmod: Can't set permset.");
+                goto cleanup;
+            }
+            /* also update ACL_MASK */
+            ret = acl_calc_mask(&acl);
+
+           if (ret) {
+                LOG(log_error, logtype_afpd, "posix_chmod: acl_calc_mask failed.");
+               goto cleanup;
+            }
+           ret = acl_set_file(name, ACL_TYPE_ACCESS, acl);
+        }
+cleanup:
+        acl_free(acl);
+    }
+done:
+    LOG(log_maxdebug, logtype_afpd, "posix_chmod: %d", ret);
+    return ret;
+}
+
+/*
+ * posix_fchmod() accepts the same arguments as fchmod() and returns 0 in case of
+ * success or -1 in case something went wrong.
+ */
+int posix_fchmod(int fd, mode_t mode) {
+    int ret = 0;
+    int entry_id = ACL_FIRST_ENTRY;
+    acl_entry_t entry;
+    acl_entry_t group_entry;
+    acl_tag_t tag;
+    acl_t acl;
+    u_char not_found = (SEARCH_GROUP_OBJ|SEARCH_MASK); /* used as flags */
+
+    /* Call chmod() first because there might be some special bits to be set which
+     * aren't related to access control.
+     */
+    ret = fchmod(fd, mode);
+
+    if (ret)
+        goto done;
+
+    /* Check if the underlying filesystem supports ACLs. */
+    acl = acl_get_fd(fd);
+
+    if (acl) {
+        /* There is no need to keep iterating once we have found ACL_GROUP_OBJ and ACL_MASK. */
+        while ((acl_get_entry(acl, entry_id, &entry) == 1) && not_found) {
+            entry_id = ACL_NEXT_ENTRY;
+
+            ret = acl_get_tag_type(entry, &tag);
+
+            if (ret) {
+                LOG(log_error, logtype_afpd, "posix_fchmod: Failed to get tag type.");
+                goto cleanup;
+            }
+
+            switch (tag) {
+                case ACL_GROUP_OBJ:
+                    group_entry = entry;
+                    not_found &= ~SEARCH_GROUP_OBJ;
+                    break;
+
+                case ACL_MASK:
+                    not_found &= ~SEARCH_MASK;
+                    break;
+
+                default:
+                    break;
+            }
+        }
+        if (!not_found) {
+            /* The filesystem object has extented ACLs. We have to update ACL_GROUP_OBJ
+             * with the group permissions.
+             */
+            acl_permset_t permset;
+            acl_perm_t perm = 0;
+
+            ret = acl_get_permset(group_entry, &permset);
+
+            if (ret) {
+                LOG(log_error, logtype_afpd, "posix_fchmod: Can't get permset.");
+                goto cleanup;
+            }
+            ret = acl_clear_perms(permset);
+
+            if (ret)
+                goto cleanup;
+
+            if (mode & S_IXGRP)
+                perm |= ACL_EXECUTE;
+
+            if (mode & S_IWGRP)
+                perm |= ACL_WRITE;
+
+            if (mode & S_IRGRP)
+                perm |= ACL_READ;
+
+            ret = acl_add_perm(permset, perm);
+
+            if (ret)
+                goto cleanup;
+
+            ret = acl_set_permset(group_entry, permset);
+
+            if (ret) {
+                LOG(log_error, logtype_afpd, "posix_fchmod: Can't set permset.");
+                goto cleanup;
+            }
+            /* also update ACL_MASK */
+            ret = acl_calc_mask(&acl);
+
+            if (ret) {
+                LOG(log_error, logtype_afpd, "posix_fchmod: acl_calc_mask failed.");
+                goto cleanup;
+            }
+            ret = acl_set_fd(fd, acl);
+        }
+cleanup:
+        acl_free(acl);
+    }
+done:
+    return ret;
+}
+
+#endif /* HAVE_POSIX_ACLS */
+
+#endif /* HAVE_ACLS */
index 1396ce34c809d98e5083f1b96ba16fe49180f266..c936f178d79bbd8bbbafc8c0269a07275ac550a7 100644 (file)
@@ -90,7 +90,7 @@ exit:
 
 #ifdef HAVE_POSIX_ACLS
 /*!
- * Remove any ACL_USER, ACL_GROUP or ACL_TYPE_DEFAULT ACEs from an object
+ * Remove any ACL_USER, ACL_GROUP, ACL_MASK or ACL_TYPE_DEFAULT ACEs from an object
  *
  * @param name  (r) filesystem object name
  *
@@ -116,14 +116,15 @@ int remove_acl_vfs(const char *name)
         acl = NULL;
     }
 
-    /* Now get ACL and remove ACL_USER or ACL_GROUP entries, then re-set the ACL again */
+    /* Now get ACL and remove ACL_MASK, ACL_USER or ACL_GROUP entries, then re-set
+     * the ACL again. acl_calc_mask() must not be called because there is no need
+     * for an ACL_MASK entry in a basic ACL. */
     EC_NULL_LOG_ERR(acl = acl_get_file(name, ACL_TYPE_ACCESS), AFPERR_MISC);
     for ( ; acl_get_entry(acl, entry_id, &e) == 1; entry_id = ACL_NEXT_ENTRY) {
         EC_ZERO_LOG_ERR(acl_get_tag_type(e, &tag), AFPERR_MISC);
-        if (tag == ACL_USER || tag == ACL_GROUP)
+        if (tag == ACL_USER || tag == ACL_GROUP || tag == ACL_MASK)
             EC_ZERO_LOG_ERR(acl_delete_entry(acl, e), AFPERR_MISC);
     }
-    EC_ZERO_LOG_ERR(acl_calc_mask(&acl), AFPERR_MISC);
     EC_ZERO_LOG_ERR(acl_valid(acl), AFPERR_MISC);
     EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_ACCESS, acl), AFPERR_MISC);
 
index cc053c91b35dd6935cbf28f60ba5682bc3555a23..89c2ba1b26e74d93e7aa8f8192f14c8629ea1f75 100644 (file)
@@ -44,7 +44,7 @@ int stickydirmode(const char *name, const mode_t mode, const int dropbox, const
             if ( seteuid(0) < 0) {
                 LOG(log_error, logtype_afpd, "stickydirmode: unable to seteuid root: %s", strerror(errno));
             }
-            if ( (retval=chmod( name, ( (DIRBITS | mode | S_ISVTX) & ~v_umask) )) < 0) {
+            if ( (retval=chmod_acl( name, ( (DIRBITS | mode | S_ISVTX) & ~v_umask) )) < 0) {
                 LOG(log_error, logtype_afpd, "stickydirmode: chmod \"%s\": %s", fullpathname(name), strerror(errno) );
             } else {
                 LOG(log_debug, logtype_afpd, "stickydirmode: chmod \"%s\": %s", fullpathname(name), strerror(retval) );
@@ -59,7 +59,7 @@ int stickydirmode(const char *name, const mode_t mode, const int dropbox, const
      *  Ignore EPERM errors:  We may be dealing with a directory that is
      *  group writable, in which case chmod will fail.
      */
-    if ( (chmod( name, (DIRBITS | mode) & ~v_umask ) < 0) && errno != EPERM &&
+    if ( (chmod_acl( name, (DIRBITS | mode) & ~v_umask ) < 0) && errno != EPERM &&
          !(errno == ENOENT && (dropbox & AFPVOL_NOADOUBLE)) )
     {
         LOG(log_error, logtype_afpd, "stickydirmode: chmod \"%s\": %s", fullpathname(name), strerror(errno) );
@@ -92,7 +92,7 @@ int setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask)
     
     mode |= st->st_mode & ~mask; /* keep other bits from previous mode */
 
-    if ( chmod( name,  mode & ~v_umask ) < 0 && errno != EPERM ) {
+    if ( chmod_acl( name,  mode & ~v_umask ) < 0 && errno != EPERM ) {
         return -1;
     }
     return 0;
index 3156afe33191433b0ac43cf7db77d1cb4d05e43e..0999b878a8c37c800660e3ffb6945893e78623fe 100644 (file)
@@ -599,12 +599,12 @@ static int ads_setfilmode(const char * name, mode_t mode, struct stat *st, mode_
     if ((dir_mode & (S_IRGRP | S_IWGRP )))
         dir_mode |= S_IXGRP;
     if ((dir_mode & (S_IROTH | S_IWOTH )))
-        dir_mode |= S_IXOTH;   
-    
+        dir_mode |= S_IXOTH;
+
        /* change folder */
        dir_mode |= DIRBITS;
     if (dir_rx_set(dir_mode)) {
-        if (chmod( name,  dir_mode ) < 0)
+        if (chmod_acl( name,  dir_mode ) < 0)
             return -1;
     }
     param.st = st;
@@ -613,7 +613,7 @@ static int ads_setfilmode(const char * name, mode_t mode, struct stat *st, mode_
         return -1;
 
     if (!dir_rx_set(dir_mode)) {
-        if (chmod( name,  dir_mode ) < 0)
+        if (chmod_acl( name,  dir_mode ) < 0)
             return -1;
     }