--- 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 +