]> arthur.barton.de Git - netatalk.git/blobdiff - etc/afpd/acls.c
Merge from branch-2-1
[netatalk.git] / etc / afpd / acls.c
index a8a4da73c08120c4e1061d9d4e65c0637886de97..76d87c12645e60e618e28256df65b5faaa6acc97 100644 (file)
@@ -50,6 +50,7 @@
 #include "unix.h"
 #include "acls.h"
 #include "acl_mappings.h"
+#include "auth.h"
 
 /* for map_acl() */
 #define SOLARIS_2_DARWIN       1
  *
  * @param path           (r) path to filesystem object
  * @param sb             (r) struct stat of path
- * @param pwd            (r) struct passwd of user
  * @param result         (w) resulting Darwin allow ACE
  *
  * @returns                  0 or -1 on error
  */
 static int solaris_acl_rights(const char *path,
                               const struct stat *sb,
-                              const struct passwd *pwd,
                               uint32_t *result)
 {
     EC_INIT;
@@ -125,11 +124,11 @@ static int solaris_acl_rights(const char *path,
            if its a trivial ACE_EVERYONE ACE
            THEN
            process ACE */
-        if (((who == pwd->pw_uid) && !(flags & (ACE_TRIVIAL|ACE_IDENTIFIER_GROUP)))
+        if (((who == uuid) && !(flags & (ACE_TRIVIAL|ACE_IDENTIFIER_GROUP)))
             ||
             ((flags & ACE_IDENTIFIER_GROUP) && !(flags & ACE_GROUP) && gmem(who))
             ||
-            ((flags & ACE_OWNER) && (pwd->pw_uid == sb->st_uid))
+            ((flags & ACE_OWNER) && (uuid == sb->st_uid))
             ||
             ((flags & ACE_GROUP) && gmem(sb->st_gid))
             ||
@@ -337,6 +336,133 @@ EC_CLEANUP:
 
 #ifdef HAVE_POSIX_ACLS
 
+static uint32_t posix_permset_to_darwin_rights(acl_entry_t e, int is_dir)
+{
+    EC_INIT;
+    uint32_t rights = 0;
+    acl_permset_t permset;
+
+    EC_ZERO_LOG(acl_get_permset(e, &permset));
+
+    if (acl_get_perm(permset, ACL_READ))
+        rights = DARWIN_ACE_READ_DATA
+            | DARWIN_ACE_READ_EXTATTRIBUTES
+            | DARWIN_ACE_READ_ATTRIBUTES
+            | DARWIN_ACE_READ_SECURITY;
+    if (acl_get_perm(permset, ACL_WRITE)) {
+        rights |= DARWIN_ACE_WRITE_DATA
+            | DARWIN_ACE_APPEND_DATA
+            | DARWIN_ACE_WRITE_EXTATTRIBUTES
+            | DARWIN_ACE_WRITE_ATTRIBUTES;
+        if (is_dir)
+            rights |= DARWIN_ACE_DELETE_CHILD;
+    }
+    if (acl_get_perm(permset, ACL_EXECUTE))
+        rights |= DARWIN_ACE_EXECUTE;
+
+EC_CLEANUP:
+    LOG(log_maxdebug, logtype_afpd, "mapped rights: 0x%08x", rights);
+    return rights;
+}
+
+/*! 
+ * Compile access rights for a user to one file-system object
+ *
+ * This combines combines all access rights for a user to one fs-object and
+ * returns the result as a Darwin allowed rights ACE.
+ * This must honor trivial ACEs which are a mode_t mapping.
+ *
+ * @param path           (r) path to filesystem object
+ * @param sb             (r) struct stat of path
+ * @param result         (rw) resulting Darwin allow ACE
+ *
+ * @returns                  0 or -1 on error
+ */
+static int posix_acl_rights(const char *path,
+                            const struct stat *sb,
+                            uint32_t *result)
+{
+    EC_INIT;
+    int havemask = 0;
+    int entry_id = ACL_FIRST_ENTRY;
+    uint32_t rights = 0, maskrights = 0;
+    uid_t *uid = NULL;
+    gid_t *gid = NULL;
+    acl_t acl = NULL;
+    acl_entry_t e;
+    acl_tag_t tag;
+
+    EC_NULL_LOG(acl = acl_get_file(path, ACL_TYPE_ACCESS));
+
+    /* 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;
+    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 (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:
+            LOG(log_maxdebug, logtype_afpd, "ACL_OTHER");
+            rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+            break;
+        default:
+            continue;
+        }
+    } /* while */
+
+    *result |= rights;
+
+EC_CLEANUP:
+    if (acl) acl_free(acl);
+    if (uid) acl_free(uid);
+    if (gid) acl_free(gid);
+    EC_EXIT;
+}
+
 /*!
  * Add entries of one acl to another acl
  *
@@ -553,17 +679,15 @@ static int map_acl_posix_to_darwin(int type, const acl_t acl, darwin_ace_t *darw
 {
     EC_INIT;
     int mapped_aces = 0;
-    int havemask = 0;
     int entry_id = ACL_FIRST_ENTRY;
     acl_entry_t e;
     acl_tag_t tag;
-    acl_permset_t permset, mask;
     uid_t *uid = NULL;
     gid_t *gid = NULL;
     struct passwd *pwd = NULL;
     struct group *grp = NULL;
     uint32_t flags;
-    uint32_t rights;
+    uint32_t rights, maskrights = 0;
     darwin_ace_t *saved_darwin_aces = darwin_aces;
 
     LOG(log_maxdebug, logtype_afpd, "map_aces_posix_to_darwin(%s)",
@@ -599,10 +723,8 @@ static int map_acl_posix_to_darwin(int type, const acl_t acl, darwin_ace_t *darw
             gid = NULL;
             break;
 
-            /* store mask so we can apply it later in a second loop */
         case ACL_MASK:
-            EC_ZERO_LOG(acl_get_permset(e, &mask));
-            havemask = 1;
+            maskrights = posix_permset_to_darwin_rights(e, type & IS_DIR);
             continue;
 
         default:
@@ -618,55 +740,19 @@ static int map_acl_posix_to_darwin(int type, const acl_t acl, darwin_ace_t *darw
         darwin_aces->darwin_ace_flags = htonl(flags);
 
         /* rights */
-        EC_ZERO_LOG(acl_get_permset(e, &permset));
-
-        rights = 0;
-        if (acl_get_perm(permset, ACL_READ))
-            rights = DARWIN_ACE_READ_DATA
-                | DARWIN_ACE_READ_EXTATTRIBUTES
-                | DARWIN_ACE_READ_ATTRIBUTES
-                | DARWIN_ACE_READ_SECURITY;
-        if (acl_get_perm(permset, ACL_WRITE)) {
-            rights |= DARWIN_ACE_WRITE_DATA
-                | DARWIN_ACE_APPEND_DATA
-                | DARWIN_ACE_WRITE_EXTATTRIBUTES
-                | DARWIN_ACE_WRITE_ATTRIBUTES;
-            if ((type & ~MAP_MASK) == IS_DIR)
-                rights |= DARWIN_ACE_DELETE;
-        }
-        if (acl_get_perm(permset, ACL_EXECUTE))
-            rights |= DARWIN_ACE_EXECUTE;
-
+        rights = posix_permset_to_darwin_rights(e, type & IS_DIR);
         darwin_aces->darwin_ace_rights = htonl(rights);
 
         darwin_aces++;
         mapped_aces++;
     } /* while */
 
-    if (havemask) {
-        /* Loop through the mapped ACE buffer once again, applying the mask */
-        /* Map the mask to Darwin ACE rights first */
-        rights = 0;
-        if (acl_get_perm(mask, ACL_READ))
-            rights = DARWIN_ACE_READ_DATA
-                | DARWIN_ACE_READ_EXTATTRIBUTES
-                | DARWIN_ACE_READ_ATTRIBUTES
-                | DARWIN_ACE_READ_SECURITY;
-        if (acl_get_perm(mask, ACL_WRITE)) {
-            rights |= DARWIN_ACE_WRITE_DATA
-                | DARWIN_ACE_APPEND_DATA
-                | DARWIN_ACE_WRITE_EXTATTRIBUTES
-                | DARWIN_ACE_WRITE_ATTRIBUTES;
-            if ((type & ~MAP_MASK) == IS_DIR)
-                rights |= DARWIN_ACE_DELETE;
-        }
-        if (acl_get_perm(mask, ACL_EXECUTE))
-            rights |= DARWIN_ACE_EXECUTE;
-        for (int i = mapped_aces; i > 0; i--) {
-            saved_darwin_aces->darwin_ace_rights &= htonl(rights);
-            saved_darwin_aces++;
-        }
+    /* Loop through the mapped ACE buffer once again, applying the mask */
+    for (int i = mapped_aces; i > 0; i--) {
+        saved_darwin_aces->darwin_ace_rights &= htonl(maskrights);
+        saved_darwin_aces++;
     }
+
     EC_STATUS(mapped_aces);
 
 EC_CLEANUP:
@@ -760,7 +846,7 @@ static int get_and_map_acl(char *name, char *rbuf, size_t *rbuflen)
     acl_t defacl = NULL , accacl = NULL;
 
     /* stat to check if its a dir */
-    EC_ZERO_LOG(stat(name, &st));
+    EC_ZERO_LOG(lstat(name, &st));
 
     /* if its a dir, check for default acl too */
     dirflag = 0;
@@ -1009,7 +1095,6 @@ static int check_acl_access(const struct vol *vol,
     uint32_t       allowed_rights = 0;
     char           *username = NULL;
     uuidtype_t     uuidtype;
-    struct passwd  *pwd;
     struct stat    st;
     bstring        parent = NULL;
 
@@ -1020,6 +1105,8 @@ static int check_acl_access(const struct vol *vol,
     EC_ZERO_LOG_ERR(lstat(path, &st), AFPERR_PARAM);
 
     switch (uuidtype) {
+    case UUID_USER:
+        break;
     case UUID_GROUP:
         LOG(log_warning, logtype_afpd, "check_access: afp_access not supported for groups");
         EC_STATUS(AFPERR_MISC);
@@ -1031,12 +1118,11 @@ static int check_acl_access(const struct vol *vol,
         goto EC_CLEANUP;
     }
 
-    EC_NULL_LOG_ERR(pwd = getpwnam(username), AFPERR_MISC);
-
 #ifdef HAVE_SOLARIS_ACLS
-    EC_ZERO_LOG(solaris_acl_rights(path, &st, pwd, &allowed_rights));
+    EC_ZERO_LOG(solaris_acl_rights(path, &st, &allowed_rights));
 #endif
 #ifdef HAVE_POSIX_ACLS
+    EC_ZERO_LOG(posix_acl_rights(path, &st, &allowed_rights));
 #endif
 
     LOG(log_debug, logtype_afpd, "allowed rights: 0x%08x", allowed_rights);
@@ -1069,9 +1155,10 @@ static int check_acl_access(const struct vol *vol,
         EC_ZERO_LOG_ERR(lstat(cfrombstr(parent), &st), AFPERR_MISC);
 
 #ifdef HAVE_SOLARIS_ACLS
-        EC_ZERO_LOG(solaris_acl_rights(cfrombstr(parent), &st, pwd, &parent_rights));
+        EC_ZERO_LOG(solaris_acl_rights(cfrombstr(parent), &st, &parent_rights));
 #endif
 #ifdef HAVE_POSIX_ACLS
+    EC_ZERO_LOG(posix_acl_rights(path, &st, &allowed_rights));
 #endif
         if (parent_rights & (DARWIN_ACE_WRITE_DATA | DARWIN_ACE_DELETE_CHILD))
             allowed_rights |= DARWIN_ACE_DELETE; /* man, that was a lot of work! */
@@ -1232,7 +1319,11 @@ int afp_getacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
     /* 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);
+        if (get_and_map_acl(s_path->u_name, rbuf, rbuflen) != 0) {
+            LOG(log_error, logtype_afpd, "afp_getacl(\"%s/%s\"): mapping error",
+                getcwdpath(), s_path->u_name);
+            return AFPERR_MISC;
+        }
     }
 
     LOG(log_debug9, logtype_afpd, "afp_getacl: END");
@@ -1337,106 +1428,81 @@ int afp_setacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
     return ret;
 }
 
-/*
-  unix.c/accessmode calls this: map ACL to OS 9 mode
-*/
-int acltoownermode(char *path, struct stat *st, uid_t uid, struct maccess *ma)
+/********************************************************************
+ * ACL funcs interfacing with other parts
+ ********************************************************************/
+
+/*!
+ * map ACL to user maccess
+ *
+ * This is the magic function that makes ACLs usable by calculating
+ * the access granted by ACEs to the logged in user.
+ */
+int acltoownermode(char *path, struct stat *st, struct maccess *ma)
 {
     EC_INIT;
-    struct passwd *pw;
     uint32_t rights = 0;
 
-    if ( ! (AFPobj->options.flags & OPTION_UUID)
-         ||
-         ! (AFPobj->options.flags & OPTION_ACL2MACCESS))
-        return 0;
+    if ( ! (AFPobj->options.flags & OPTION_ACL2MACCESS)
+         || (current_vol == NULL)
+         || ! (current_vol->v_flags & AFPVOL_ACLS))
+         return 0;
 
-    LOG(log_maxdebug, logtype_afpd, "acltoownermode('%s')", path);
-
-    EC_NULL_LOG(pw = getpwuid(uid));
+    LOG(log_maxdebug, logtype_afpd, "acltoownermode(\"%s/%s\", 0x%02x)",
+        getcwdpath(), path, ma->ma_user);
 
 #ifdef HAVE_SOLARIS_ACLS
-    EC_ZERO_LOG(solaris_acl_rights(path, st, pw, &rights));
+    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_debug, logtype_afpd, "rights: 0x%08x", rights);
+    LOG(log_maxdebug, logtype_afpd, "rights: 0x%08x", rights);
 
-    LOG(log_maxdebug, logtype_afpd, "acltoownermode: ma_user before: %04o",ma->ma_user);
     if (rights & DARWIN_ACE_READ_DATA)
         ma->ma_user |= AR_UREAD;
     if (rights & DARWIN_ACE_WRITE_DATA)
         ma->ma_user |= AR_UWRITE;
     if (rights & (DARWIN_ACE_EXECUTE | DARWIN_ACE_SEARCH))
         ma->ma_user |= AR_USEARCH;
-    LOG(log_maxdebug, logtype_afpd, "acltoownermode: ma_user after: %04o", ma->ma_user);
+
+    LOG(log_maxdebug, logtype_afpd, "resulting user maccess: 0x%02x", ma->ma_user);
 
 EC_CLEANUP:
     EC_EXIT;
 }
 
-/*
-  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 ?
-*/
-int createdir_inherit_acl(const struct vol *vol)
+/*!
+ * Check whether a volume supports ACLs
+ *
+ * @param vol  (r) volume
+ *
+ * @returns        0 if not, 1 if yes
+ */
+int check_vol_acl_support(const struct vol *vol)
 {
-    EC_INIT;
-#ifdef HAVE_SOLARIS_ACLS
-    ace_t *diraces = NULL, *adaces = NULL, *combinedaces = NULL;
-    int diracecount, adacecount;
-#endif
-#ifdef HAVE_POSIX_ACLS
-    acl_t def_acl = NULL;
-    acl_t acc_acl = NULL;
-#endif
-    LOG(log_debug9, logtype_afpd, "addir_inherit_acl: BEGIN");
+    int ret = 1;
 
-    /* Check if ACLs are enabled for the volume */
-    if (vol->v_flags & AFPVOL_ACLS) {
 #ifdef HAVE_SOLARIS_ACLS
-        /* Get directory ACL */
-        EC_NEG1_LOG(diracecount = get_nfsv4_acl(".", &diraces));
-        EC_NEG1_LOG(diracecount = strip_trivial_aces(&diraces, diracecount));
-
-        /* Inherit to .AppleDouble directory */
-        EC_NEG1_LOG(adacecount = get_nfsv4_acl(".AppleDouble", &adaces));
-        EC_NEG1_LOG(adacecount = strip_nontrivial_aces(&adaces, adacecount));
-        EC_NULL_LOG(combinedaces = concat_aces(diraces, diracecount, adaces, adacecount));
-        EC_ZERO_LOG(acl(".AppleDouble", ACE_SETACL, diracecount + adacecount, combinedaces));
-        free(adaces);
-        adaces = NULL;
-        free(combinedaces);
-        combinedaces = NULL;
-
-        /* Inherit to ".AppleDouble/.Parent" */
-        EC_NEG1_LOG(adacecount = get_nfsv4_acl(".AppleDouble/.Parent", &adaces));
-        EC_NEG1_LOG(adacecount = strip_nontrivial_aces(&adaces, adacecount));
-        EC_NULL_LOG(combinedaces = concat_aces(diraces, diracecount, adaces, adacecount));
-        EC_ZERO_LOG(acl(".AppleDouble/.Parent",
-                        ACE_SETACL,
-                        diracecount + adacecount,
-                        combinedaces));
+    ace_t *aces = NULL;
+    if (get_nfsv4_acl(vol->v_path, &aces) == -1)
+        ret = 0;
 #endif
 #ifdef HAVE_POSIX_ACLS
+    acl_t acl = NULL;
+    if ((acl = acl_get_file(vol->v_path, ACL_TYPE_ACCESS)) == NULL)
+        ret = 0;
 #endif
-    }
-
-    LOG(log_debug9, logtype_afpd, "addir_inherit_acl: END");
 
-EC_CLEANUP:
 #ifdef HAVE_SOLARIS_ACLS
-    if (diraces) free(diraces);
-    if (adaces) free(adaces);
-    if (combinedaces) free(combinedaces);
+    if (aces) free(aces);
 #endif
 #ifdef HAVE_POSIX_ACLS
-    acl_free(acc_acl);
-    acl_free(def_acl);
-#endif
-    EC_EXIT;
-}
+    if (acl) acl_free(acl);
+#endif /* HAVE_POSIX_ACLS */
 
+    LOG(log_debug, logtype_afpd, "Volume \"%s\" ACL support: %s",
+        vol->v_path, ret ? "yes" : "no");
+    return ret;
+}