src/server/daemon/acl.c

changeset 63
66442f81f823
parent 54
3a1d5a52adfc
child 66
74babc0082b7
--- a/src/server/daemon/acl.c	Thu May 09 19:41:11 2013 +0200
+++ b/src/server/daemon/acl.c	Sat May 11 13:28:26 2013 +0200
@@ -29,6 +29,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "../util/util.h"
 #include "../util/pool.h"
 #include "../safs/auth.h"
 #include "acl.h"
@@ -122,6 +123,10 @@
 }
 
 void acl_set_error_status(Session *sn, Request *rq, ACLList *acl, User *user) {
+    if(sn == NULL || rq == NULL) {
+        return;
+    }
+    
     if(!user) {
         char *value = NULL;
         if(acl->authprompt) {
@@ -159,7 +164,7 @@
     User *user = acllist_getuser(sn, rq, list);
     
     // evalutate all ACLs
-    ACLList *acl = acl_evallist(list, user, access_mask);
+    ACLList *acl = acl_evallist(list, user, access_mask, NULL);
     if(acl) {
         acl_set_error_status(sn, rq, acl, user);
         // TODO: don't free the user here
@@ -177,16 +182,29 @@
     return REQ_PROCEED;
 }
 
-ACLList* acl_evallist(ACLListHandle *list, User *user, int access_mask) {
+ACLList* acl_evallist(
+        ACLListHandle *list,
+        User *user,
+        int access_mask,
+        ACLList **externacl)
+{
     if(!list) {
         return NULL;
     }
+    if(externacl) {
+        *externacl = NULL;
+    }
     
     // evaluate each acl until one denies access
     ACLListElm *elm = list->listhead;
     while(elm) {
         ACLList *acl = elm->acl;
-        if(!acl->check(acl, user, access_mask)) {
+        if(acl->isextern) {
+            // set externacl to the first external acl
+            if(externacl && *externacl == NULL) {
+                *externacl = acl;
+            }
+        } else if(!acl->check(acl, user, access_mask)) {
             // the acl denies access
             return acl;
         } 
@@ -266,3 +284,269 @@
     
     return allow; // allow is 0, if no ace set it to 1
 }
+
+
+/* filesystem acl functions */
+
+#if defined (__SVR4) && defined (__sun)
+
+#include <sys/acl.h>
+
+int solaris_acl_check(
+        char *path,
+        struct stat *s,
+        uint32_t mask,
+        uid_t uid,
+        gid_t gid);
+int solaris_acl_affects_user(
+        ace_t *ace,
+        uid_t uid,
+        gid_t gid,
+        uid_t owner,
+        gid_t owninggroup);
+
+int fs_acl_check(SysACL *acl, User *user, char *path, uint32_t access_mask) {
+    sstr_t p;
+    if(path[0] != '/') {
+        size_t n = 128;
+        char *cwd = malloc(n);
+        while(!getcwd(cwd, n)) {
+            if(errno == ERANGE) {
+                n *= 2;
+                cwd = realloc(cwd, n);
+            } else {
+                free(cwd);
+                return 0;
+            }
+        }
+        sstr_t wd = sstr(cwd);
+        sstr_t pp = sstr(path);
+        p.length = wd.length + pp.length + 1;
+        p.ptr = malloc(p.length + 1);
+        p = sstrncat(3, p, wd, sstrn("/", 1), pp);
+        p.ptr[p.length] = '\0';
+    } else {
+        p = sstrdup(sstr(path));
+    }
+    if(p.ptr[p.length-1] == '/') {
+        p.ptr[p.length-1] = 0;
+        p.length--;
+    }
+    
+    // get uid/gid
+    struct passwd pw;
+    if(user) {
+        char *pwbuf = malloc(DEF_PWBUF);
+        if(pwbuf == NULL) {
+            free(p.ptr);
+            return 0;
+        }
+        if(!util_getpwnam(user->name, &pw, pwbuf, DEF_PWBUF)) {
+            free(pwbuf);
+            free(p.ptr);
+            return 0;
+        }
+        free(pwbuf);
+        acl->user_uid = pw.pw_uid;
+        acl->user_gid = pw.pw_gid;
+    } else {
+        acl->user_uid = -1;
+        acl->user_gid = -1;
+    }
+    
+    // translate access_mask
+    uint32_t mask = 0;
+    if((access_mask & ACL_READ_DATA) == ACL_READ_DATA) {
+        mask |= ACE_READ_DATA;
+    }
+    if((access_mask & ACL_WRITE_DATA) == ACL_WRITE_DATA) {
+        mask |= ACE_WRITE_DATA;
+    }
+    if((access_mask & ACL_ADD_FILE) == ACL_ADD_FILE) {
+        mask |= ACE_ADD_FILE;
+    }
+    if((access_mask & ACL_READ_XATTR) == ACL_READ_XATTR) {
+        mask |= ACE_READ_NAMED_ATTRS;
+    }
+    if((access_mask & ACL_WRITE_XATTR) == ACL_WRITE_XATTR) {
+        mask |= ACE_WRITE_NAMED_ATTRS;
+    }
+    if((access_mask & ACL_EXECUTE) == ACL_EXECUTE) {
+        mask |= ACE_EXECUTE;
+    }
+    if((access_mask & ACL_DELETE) == ACL_DELETE) {
+        mask |= ACE_DELETE_CHILD;
+    }
+    if((access_mask & ACL_READ_ATTRIBUTES) == ACL_READ_ATTRIBUTES) {
+        mask |= ACE_READ_ATTRIBUTES;
+    }
+    if((access_mask & ACL_WRITE_ATTRIBUTES) == ACL_WRITE_ATTRIBUTES) {
+        mask |= ACE_WRITE_ATTRIBUTES;
+    }
+    if((access_mask & ACL_LIST) == ACL_LIST) {
+        mask |= ACE_LIST_DIRECTORY;
+    }
+    if((access_mask & ACL_READ_ACL) == ACL_READ_ACL) {
+        mask |= ACE_READ_ACL;
+    }
+    if((access_mask & ACL_WRITE_ACL) == ACL_WRITE_ACL) {
+        mask |= ACE_WRITE_ACL;
+    }
+    if((access_mask & ACL_WRITE_OWNER) == ACL_WRITE_OWNER) {
+        mask |= ACE_WRITE_OWNER;
+    }
+    if((access_mask & ACL_SYNCHRONIZE) == ACL_SYNCHRONIZE) {
+        mask |= ACE_SYNCHRONIZE;
+    }
+    
+    /*
+     * If the vfs wants to create new files, path does not name an existing
+     * file. In this case, we check if the user has the ACE_ADD_FILE
+     * permission for the parent directory
+     */
+    struct stat s;
+    if(stat(p.ptr, &s)) {
+        if(errno != ENOENT) {
+            perror("fs_acl_check: stat");
+            free(p.ptr);
+            return 0;
+        } else {
+            mask = ACE_ADD_FILE;
+            p = util_path_remove_last(p);
+            if(stat(p.ptr, &s)) {
+                free(p.ptr);
+                return 0;
+            }
+        }
+    }
+    
+    /*
+     * perform a acl check for the path and each parent directory
+     * we don't check the file system root
+     * 
+     * after the first check, we check only search permission for the
+     * directories
+     */
+    if(!solaris_acl_check(p.ptr, &s, mask, pw.pw_uid, pw.pw_gid)) {
+        free(p.ptr);
+        return 0;
+    }
+    
+    p = util_path_remove_last(p);
+    mask = ACE_LIST_DIRECTORY;
+    while(p.length > 1) {
+        if(stat(p.ptr, &s)) {
+            free(p.ptr);
+            return 0;
+        }
+        if(!solaris_acl_check(p.ptr, &s, mask, pw.pw_uid, pw.pw_gid)) {
+            free(p.ptr);
+            return 0;
+        }
+        
+        // cut the last file name from the path
+        p = util_path_remove_last(p);
+    }
+    
+    
+    return 1;
+}
+
+int solaris_acl_check(
+        char *path,
+        struct stat *s,
+        uint32_t mask,
+        uid_t uid,
+        gid_t gid)
+{
+    //printf("solaris_acl_check %s\n", path);
+    
+    int nace = acl(path, ACE_GETACLCNT, 0, NULL);
+    if(nace == -1) {
+        perror("acl: ACE_GETACLCNT");
+        // TODO: log error
+        return 0;
+    }
+    ace_t *aces = calloc(nace, sizeof(ace_t));
+    if(acl(path, ACE_GETACL, nace, aces) == 1) {
+        perror("acl: ACE_GETACL");
+        // TODO: log error
+        free(aces);
+        return 0;
+    }
+    
+    int allow = 0;
+    uint32_t allowed_access = 0;
+    for(int i=0;i<nace;i++) {
+        ace_t ace = aces[i];
+        if(solaris_acl_affects_user(&ace, uid, gid, s->st_uid, s->st_gid)) {
+            if(ace.a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) {
+                // add all new access rights 
+                allowed_access |= (mask & ace.a_access_mask);
+                // check if we have all requested rights
+                if((allowed_access & mask) == mask) {
+                    allow = 1;
+                    break;
+                }
+            } else if(ace.a_type == ACE_ACCESS_DENIED_ACE_TYPE) {
+                // ACL_TYPE_DENIED
+                
+                if((ace.a_access_mask & mask) != 0) {
+                    // access denied
+                    break;
+                }
+            }
+        }
+    }
+    
+    free(aces);
+    
+    //printf("return %d\n", allow);
+    return allow;
+}
+
+int solaris_acl_affects_user(
+        ace_t *ace,
+        uid_t uid,
+        gid_t gid,
+        uid_t owner,
+        gid_t owninggroup)
+{
+    /*
+     * mostly the same as wsacl_affects_user
+     */
+    
+    int check_access = 0;
+    
+    if((ace->a_flags & ACE_OWNER) == ACE_OWNER) {
+        if(uid == owner) {
+            check_access = 1;
+        }
+    } else if((ace->a_flags & ACE_GROUP) == ACE_GROUP) {
+        if(gid == owninggroup) {
+            check_access = 1;
+        }
+    } else if((ace->a_flags & ACE_EVERYONE) == ACE_EVERYONE) {
+       check_access = 1;
+    } else if(ace->a_who != -1 && uid != 0) {
+        // this ace is defined for a named user or group
+        if((ace->a_flags & ACE_IDENTIFIER_GROUP) == ACE_IDENTIFIER_GROUP) {
+            // TODO: check all groups
+            if(ace->a_who == gid) {
+                // the user is in the group
+                check_access = 1;
+            }
+        } else {
+            if(ace->a_who == uid) {
+                check_access = 1;
+            }
+        }
+    }
+    
+    return check_access;
+}
+
+
+
+#endif
+

mercurial