diff --git a/source/modules/vfs_zfsacl.c b/source/modules/vfs_zfsacl.c index b44ac48..6836bdc 100644 --- a/source/modules/vfs_zfsacl.c +++ b/source/modules/vfs_zfsacl.c @@ -2,6 +2,8 @@ * Convert ZFS/NFSv4 acls to NT acls and vice versa. * * Copyright (C) Jiri Sasek, 2007 + * Copyright (C) Rafal Szczesniak, 2009 + * * based on the foobar.c module which is copyrighted by Volker Lendecke * * Many thanks to Axel Apitz for help to fix the special ace's handling @@ -21,7 +23,7 @@ * along with this program; if not, see . * */ - + #include "includes.h" #include "nfs4_acls.h" @@ -30,6 +32,245 @@ #define ZFSACL_MODULE_NAME "zfsacl" +struct ace_params { + uint16_t a_type; + uint16_t a_flags; +}; + +struct ace_params nt_acl_order[] = { + { ACE_ACCESS_DENIED_ACE_TYPE, ACE_OWNER }, + { ACE_ACCESS_DENIED_ACE_TYPE, 0 /* "ACE_IDENTIFIER_USER" */ }, + { ACE_ACCESS_DENIED_ACE_TYPE, ACE_GROUP | ACE_IDENTIFIER_GROUP }, + { ACE_ACCESS_DENIED_ACE_TYPE, ACE_IDENTIFIER_GROUP }, + { ACE_ACCESS_DENIED_ACE_TYPE, ACE_EVERYONE }, + { ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_OWNER }, + { ACE_ACCESS_ALLOWED_ACE_TYPE, 0 /* "ACE_IDENTIFIER_USER" */ }, + { ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_GROUP | ACE_IDENTIFIER_GROUP }, + { ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_IDENTIFIER_GROUP }, + { ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_EVERYONE } +}; + + +struct ace_params zfs_acl_order[] = { + { ACE_ACCESS_DENIED_ACE_TYPE, ACE_OWNER }, + { ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_OWNER }, + { ACE_ACCESS_DENIED_ACE_TYPE, 0 /* "ACE_IDENTIFIER_USER" */ }, + { ACE_ACCESS_ALLOWED_ACE_TYPE, 0 /* "ACE_IDENTIFIER_USER" */ }, + { ACE_ACCESS_DENIED_ACE_TYPE, ACE_GROUP | ACE_IDENTIFIER_GROUP }, + { ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_GROUP | ACE_IDENTIFIER_GROUP }, + { ACE_ACCESS_DENIED_ACE_TYPE, ACE_IDENTIFIER_GROUP }, + { ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_IDENTIFIER_GROUP }, + { ACE_ACCESS_DENIED_ACE_TYPE, ACE_EVERYONE }, + { ACE_ACCESS_ALLOWED_ACE_TYPE, ACE_EVERYONE } +}; + + +struct ace_entry { + ace_t acebuf; + struct ace_entry *prev; + struct ace_entry *next; +}; + + +static NTSTATUS ace_entry_append(struct ace_entry **list, + const struct ace_entry *entry) +{ + NTSTATUS status = NT_STATUS_OK; + struct ace_entry *l = NULL; + struct ace_entry *e = NULL; + TALLOC_CTX *mem_ctx = NULL; + + if (list == NULL && + entry == NULL) { + status = NT_STATUS_INVALID_PARAMETER; + goto done; + } + + l = *list; + + mem_ctx = talloc_tos(); + e = talloc_size(mem_ctx, sizeof(struct ace_entry)); + if (e == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + e->acebuf = entry->acebuf; + e->prev = NULL; + e->next = NULL; + + DLIST_ADD_END(l, e, struct ace_entry*); + *list = l; + +done: + return status; +} + + +static NTSTATUS ace_entry_insert(struct ace_entry **list, + const struct ace_entry *entry, + int position) +{ + NTSTATUS status = NT_STATUS_OK; + struct ace_entry *l = NULL; + struct ace_entry *prev = NULL; + struct ace_entry *e = NULL; + TALLOC_CTX *mem_ctx = NULL; + int pos = 0; + + if (list == NULL || + entry == NULL) { + status = NT_STATUS_INVALID_PARAMETER; + goto done; + } + + l = *list; + + mem_ctx = talloc_tos(); + e = talloc_size(mem_ctx, sizeof(struct ace_entry)); + if (e == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + e->acebuf = entry->acebuf; + e->prev = NULL; + e->next = NULL; + + while (l) { + if (pos == position) { + e->prev = prev; + e->next = l; + l->prev = e; + + if (prev) { + prev->next = e; + } + + goto done; + } + + prev = l; + l = l->next; + pos++; + } + +done: + if (l == NULL || + prev == NULL) { + *list = e; + } + + return status; +} + + +static int ace_entries_count(struct ace_entry *list) +{ + int count = 0; + + while (list) { + count++; + list = list->next; + } + + return count; +} + + +static int ace_entry_buf_to_list(ace_t *acebuf, + int naces, + struct ace_entry **list) +{ + NTSTATUS status = NT_STATUS_OK; + int i = 0; + struct ace_entry *l = NULL; + struct ace_entry e; + + if (acebuf == NULL || + naces == 0 || + list == NULL) { + goto done; + } + + for (i = 0; i < naces; i++) { + e.acebuf = acebuf[i]; + e.next = NULL; + + status = ace_entry_append(&l, &e); + if (!NT_STATUS_IS_OK(status)) goto done; + } + + *list = l; + +done: + return i; +} + + +static int ace_entry_list_to_buf(struct ace_entry *list, + ace_t **acebuf) +{ + int count = 0; + ace_t *aces = NULL; + TALLOC_CTX *mem_ctx = NULL; + int i = 0; + + if (list == NULL || + acebuf == NULL) { + goto done; + } + + mem_ctx = talloc_tos(); + + count = ace_entries_count(list); + aces = talloc_size(NULL, sizeof(ace_t) * count); + if (aces == NULL) { + count = 0; + goto done; + } + + for (i = 0; i < count && list; i++) { + aces[i] = list->acebuf; + list = list->next; + } + +done: + *acebuf = aces; + + return count; +} + + +static void ace_entry_list_free(struct ace_entry *list) +{ + struct ace_entry *prev = NULL; + + while (list) { + prev = list; + list = list->next; + + talloc_free(prev); + } +} + + +static void dump_acl(const char *desc, const char *fname, + ace_t *acebuf, int naces) +{ + int i = 0; + + DEBUG(10, ("%s [%s] (number of ACEs = %d)\n", desc, fname, naces)); + + for (i = 0; i < naces; i++) { + DEBUGADD(9, ("ace[%02d]: a_who = %d, a_access_mask = 0x%08x, a_flags = 0x%04x, " + "a_type = 0x%04x\n", i, (int)acebuf[i].a_who, + acebuf[i].a_access_mask, acebuf[i].a_flags, + acebuf[i].a_type)); + } +} + + /* zfs_get_nt_acl() * read the local file's acls and return it in NT form * using the NFSv4 format conversion @@ -38,7 +279,7 @@ static NTSTATUS zfs_get_nt_acl_common(const char *name, uint32 security_info, SMB4ACL_T **ppacl) { - int naces, i; + int naces, i, j; ace_t *acebuf; SMB4ACL_T *pacl; TALLOC_CTX *mem_ctx; @@ -55,6 +296,7 @@ static NTSTATUS zfs_get_nt_acl_common(const char *name, } return map_nt_error_from_unix(errno); } + /* allocate the field of ZFS aces */ mem_ctx = talloc_tos(); acebuf = (ace_t *) talloc_size(mem_ctx, sizeof(ace_t)*naces); @@ -62,50 +304,85 @@ static NTSTATUS zfs_get_nt_acl_common(const char *name, return NT_STATUS_NO_MEMORY; } /* read the aces into the field */ - if(acl(name, ACE_GETACL, naces, acebuf) < 0) { + if (acl(name, ACE_GETACL, naces, acebuf) < 0) { DEBUG(9, ("acl(ACE_GETACL, %s): %s ", name, strerror(errno))); return map_nt_error_from_unix(errno); } + /* create SMB4ACL data */ if((pacl = smb_create_smb4acl()) == NULL) { return NT_STATUS_NO_MEMORY; } - for(i=0; ia_type || + (acebuf[i].a_flags & flags_mask) != ace_parms->a_flags) { + continue; + } + + DEBUGADD(9, ("ACE[%02d]: a_who = %d, a_access_mask = 0x%08x, a_flags = 0x%04x, " + "a_type = 0x%04x\n", i, (int)acebuf[i].a_who, acebuf[i].a_access_mask, + acebuf[i].a_flags, acebuf[i].a_type)); + + aceprop.aceType = (uint32) acebuf[i].a_type; + aceprop.aceFlags = (uint32) acebuf[i].a_flags; + aceprop.aceMask = (uint32) acebuf[i].a_access_mask; + aceprop.who.id = (uint32) acebuf[i].a_who; + + if(aceprop.aceFlags & ACE_OWNER) { + aceprop.flags = SMB_ACE4_ID_SPECIAL; + aceprop.who.special_id = SMB_ACE4_WHO_OWNER; + } else if(aceprop.aceFlags & ACE_GROUP) { + aceprop.flags = SMB_ACE4_ID_SPECIAL; + aceprop.who.special_id = SMB_ACE4_WHO_GROUP; + } else if(aceprop.aceFlags & ACE_EVERYONE) { + aceprop.flags = SMB_ACE4_ID_SPECIAL; + aceprop.who.special_id = SMB_ACE4_WHO_EVERYONE; + } else { + aceprop.flags = 0; + } + + if(smb_add_ace4(pacl, &aceprop) == NULL) { + return NT_STATUS_NO_MEMORY; + } } - if(smb_add_ace4(pacl, &aceprop) == NULL) - return NT_STATUS_NO_MEMORY; } *ppacl = pacl; return NT_STATUS_OK; } + /* call-back function processing the NT acl -> ZFS acl using NFSv4 conv. */ static bool zfs_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl) { - int naces = smb_get_naces(smbacl), i; - ace_t *acebuf; - SMB4ACE_T *smbace; - TALLOC_CTX *mem_ctx; + int naces = smb_get_naces(smbacl); + int res_naces = 0; + ace_t *acebuf = NULL; + ace_t *res_acebuf = NULL; + SMB4ACE_T *smbace = NULL; + TALLOC_CTX *mem_ctx = NULL; + NTSTATUS status = NT_STATUS_OK; + bool ret = True; + struct ace_entry *new_ace_list = NULL; + struct ace_entry *ne = NULL; /* new entry */ + struct ace_entry *res_ace_list = NULL; + struct ace_entry *pe = NULL; /* previous entry */ + struct ace_entry *re; /* resulting entry */ + int pos = 0; + int i = 0; + int j = 0; /* allocate the field of ZFS aces */ mem_ctx = talloc_tos(); @@ -114,10 +391,11 @@ static bool zfs_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl) errno = ENOMEM; return False; } + /* handle all aces */ - for(smbace = smb_first_ace4(smbacl), i = 0; - smbace!=NULL; - smbace = smb_next_ace4(smbace), i++) { + for (smbace = smb_first_ace4(smbacl), i = 0; + smbace!=NULL; + smbace = smb_next_ace4(smbace), i++) { SMB_ACE4PROP_T *aceprop = smb_get_ace4(smbace); acebuf[i].a_type = aceprop->aceType; @@ -140,12 +418,112 @@ static bool zfs_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl) aceprop->who.special_id)); continue; /* don't add it !!! */ } + + /* Either owner, group or everyone are a_who = -1 + and marked with corresponding flag + acebuf[i].a_who = -1; */ } } SMB_ASSERT(i == naces); + /* dump current acl */ + dump_acl("Dumping new ACL to be written to ", fsp->fsp_name, + acebuf, naces); + + /* create list of entries in new ACL */ + if (ace_entry_buf_to_list(acebuf, naces, &new_ace_list) != naces) { + DEBUG(9, ("Created ACE list length doesn't match number of entries " + "in new acebuf")); + ret = False; + goto cleanup; + } + + DEBUG(9, ("Reordering ACL\n")); + + for (j = 0; j < sizeof(zfs_acl_order)/sizeof(zfs_acl_order[0]); j++) { + struct ace_params *ace_parms = &zfs_acl_order[j]; + struct ace_entry entry; + uint16_t flags_mask; + + memset(&entry, 0, sizeof(entry)); + flags_mask = ACE_OWNER | ACE_GROUP | ACE_EVERYONE | ACE_IDENTIFIER_GROUP; + + ne = new_ace_list; + while (ne) { + if (ne->acebuf.a_type != ace_parms->a_type || + (ne->acebuf.a_flags & flags_mask) != ace_parms->a_flags) { + ne = ne->next; + continue; + } + + DEBUGADD(9, ("ACE[%02d]: a_who = %d, a_access_mask = 0x%08x, a_flags = 0x%04x, " + "a_type = 0x%04x\n", i, (int)ne->acebuf.a_who, + ne->acebuf.a_access_mask, ne->acebuf.a_flags, + ne->acebuf.a_type)); + + entry.acebuf.a_who = ne->acebuf.a_who; + entry.acebuf.a_type = ne->acebuf.a_type; + entry.acebuf.a_flags = ne->acebuf.a_flags; + entry.acebuf.a_access_mask = ne->acebuf.a_access_mask; + + status = ace_entry_append(&res_ace_list, &entry); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(9, ("Error when creating ZFS ACL from NT ACL")); + ret = False; + goto cleanup; + } + + ne = ne->next; + } + } + + pos = 0; + re = res_ace_list; + pe = NULL; + + /* Make sure each ALLOW entry has a preceding DENY */ + while (re) { + struct ace_entry entry; + memset(&entry, 0, sizeof(entry)); + + if (re->acebuf.a_type == ACE_ACCESS_ALLOWED_ACE_TYPE && + (pe == NULL || + !(pe->acebuf.a_type == ACE_ACCESS_DENIED_ACE_TYPE && + pe->acebuf.a_who == re->acebuf.a_who && + pe->acebuf.a_flags == re->acebuf.a_flags))) { + + /* Create a DENY entry complementary to existing ALLOW */ + entry.acebuf.a_who = re->acebuf.a_who; + entry.acebuf.a_flags = re->acebuf.a_flags; + entry.acebuf.a_type = ACE_ACCESS_DENIED_ACE_TYPE; + entry.acebuf.a_access_mask = 0; + + status = ace_entry_insert(&res_ace_list, &entry, pos); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(9, ("Error when canonicalising ZFS ACL")); + ret = False; + goto cleanup; + } + } + + pos++; + pe = re; + re = re->next; + } + + /* create ace_t buffer with resulting ACEs */ + res_naces = ace_entry_list_to_buf(res_ace_list, &res_acebuf); + if (res_naces < 0) { + DEBUG(9, ("Failed to convert ACE list to buffer")); + ret = False; + goto cleanup; + } + + dump_acl("Dumping resulting ACL to be written to file", fsp->fsp_name, + res_acebuf, res_naces); + /* store acl */ - if(acl(fsp->fsp_name, ACE_SETACL, naces, acebuf)) { + if(acl(fsp->fsp_name, ACE_SETACL, res_naces, res_acebuf)) { if(errno == ENOSYS) { DEBUG(9, ("acl(ACE_SETACL, %s): Operation is not " "supported on the filesystem where the file " @@ -154,12 +532,32 @@ static bool zfs_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl) DEBUG(9, ("acl(ACE_SETACL, %s): %s ", fsp->fsp_name, strerror(errno))); } - return 0; + + ret = False; + goto cleanup; + } + +cleanup: + if (new_ace_list) { + ace_entry_list_free(new_ace_list); + } + + if (res_ace_list) { + ace_entry_list_free(res_ace_list); + } + + if (acebuf) { + talloc_free(acebuf); + } + + if (res_acebuf) { + talloc_free(res_acebuf); } - return True; + return ret; } + /* zfs_set_nt_acl() * set the local file's acls obtaining it in NT form * using the NFSv4 format conversion