diff options
author | Andrew Tridgell <tridge@samba.org> | 2008-12-03 17:47:39 +1100 |
---|---|---|
committer | Andrew Tridgell <tridge@samba.org> | 2008-12-03 17:47:39 +1100 |
commit | a226d86dcec393b2cd657d5441c3041dfdf5cd8f (patch) | |
tree | 03ef7f3207607a4e5351bf50892b0a39dcf6f219 /source3/modules | |
parent | 30eff4f31b497ac94d8ee02ee2ec24bc8865ce0d (diff) | |
parent | 85b8cccab072bab263061654b677bc84826646c9 (diff) | |
download | samba-a226d86dcec393b2cd657d5441c3041dfdf5cd8f.tar.gz samba-a226d86dcec393b2cd657d5441c3041dfdf5cd8f.tar.bz2 samba-a226d86dcec393b2cd657d5441c3041dfdf5cd8f.zip |
Merge branch 'master' of ssh://git.samba.org/data/git/samba
Diffstat (limited to 'source3/modules')
-rw-r--r-- | source3/modules/gpfs.c | 92 | ||||
-rw-r--r-- | source3/modules/nfs4_acls.h | 2 | ||||
-rw-r--r-- | source3/modules/onefs.h | 52 | ||||
-rw-r--r-- | source3/modules/onefs_acl.c | 807 | ||||
-rw-r--r-- | source3/modules/vfs_acl_tdb.c | 891 | ||||
-rw-r--r-- | source3/modules/vfs_acl_xattr.c | 228 | ||||
-rw-r--r-- | source3/modules/vfs_onefs.c | 48 | ||||
-rw-r--r-- | source3/modules/vfs_readonly.c | 13 | ||||
-rw-r--r-- | source3/modules/vfs_streams_depot.c | 123 | ||||
-rw-r--r-- | source3/modules/vfs_streams_xattr.c | 339 | ||||
-rw-r--r-- | source3/modules/vfs_tsmsm.c | 2 | ||||
-rw-r--r-- | source3/modules/vfs_zfsacl.c | 83 |
12 files changed, 2522 insertions, 158 deletions
diff --git a/source3/modules/gpfs.c b/source3/modules/gpfs.c index a0d33fa33a..4e76b97ccf 100644 --- a/source3/modules/gpfs.c +++ b/source3/modules/gpfs.c @@ -24,7 +24,6 @@ #include "gpfs_gpl.h" #include "vfs_gpfs.h" -static void *libgpfs_handle = NULL; static bool gpfs_share_modes; static bool gpfs_leases; @@ -135,65 +134,64 @@ int smbd_gpfs_putacl(char *pathname, int flags, void *acl) return gpfs_putacl_fn(pathname, flags, acl); } -void init_gpfs(void) +static bool init_gpfs_function_lib(void *plibhandle_pointer, + const char *libname, + void *pfn_pointer, const char *fn_name) { - if (libgpfs_handle != NULL) { - return; - } - - libgpfs_handle = dlopen("libgpfs_gpl.so", RTLD_LAZY); - - if (libgpfs_handle == NULL) { - DEBUG(10, ("dlopen for libgpfs_gpl failed: %s\n", - strerror(errno))); - return; + bool did_open_here = false; + void **libhandle_pointer = (void **)plibhandle_pointer; + void **fn_pointer = (void **)pfn_pointer; + + if (*libhandle_pointer == NULL) { + *libhandle_pointer = dlopen(libname, RTLD_LAZY); + did_open_here = true; + } + if (*libhandle_pointer == NULL) { + DEBUG(10, ("Could not open lib %s\n", libname)); + return false; + } + + *fn_pointer = dlsym(*libhandle_pointer, fn_name); + if (*fn_pointer == NULL) { + DEBUG(10, ("Did not find symbol %s in lib %s\n", + fn_name, libname)); + if (did_open_here) { + dlclose(*libhandle_pointer); + *libhandle_pointer = NULL; + } + return false; } - DEBUG(10, ("libgpfs_gpl.so loaded\n")); - - gpfs_set_share_fn = dlsym(libgpfs_handle, "gpfs_set_share"); - if (gpfs_set_share_fn == NULL) { - DEBUG(3, ("libgpfs_gpl.so does not contain the symbol " - "'gpfs_set_share'\n")); - goto failed; - } + return true; +} - gpfs_set_lease_fn = dlsym(libgpfs_handle, "gpfs_set_lease"); - if (gpfs_set_lease_fn == NULL) { - DEBUG(3, ("libgpfs_gpl.so does not contain the symbol " - "'gpfs_set_lease'\n")); - dlclose(libgpfs_handle); +static bool init_gpfs_function(void *fn_pointer, const char *fn_name) +{ + static void *libgpfs_handle = NULL; + static void *libgpfs_gpl_handle = NULL; - goto failed; + if (init_gpfs_function_lib(&libgpfs_handle, "libgpfs.so", + fn_pointer, fn_name)) { + return true; } - - gpfs_getacl_fn = dlsym(libgpfs_handle, "gpfs_getacl"); - if (gpfs_getacl_fn == NULL) { - DEBUG(3, ("libgpfs_gpl.so does not contain the symbol " - "'gpfs_getacl'\n")); - goto failed; + if (init_gpfs_function_lib(&libgpfs_gpl_handle, "libgpfs_gpl.so", + fn_pointer, fn_name)) { + return true; } + return false; +} - gpfs_putacl_fn = dlsym(libgpfs_handle, "gpfs_putacl"); - if (gpfs_putacl_fn == NULL) { - DEBUG(3, ("libgpfs_gpl.so does not contain the symbol " - "'gpfs_putacl'\n")); - goto failed; - } +void init_gpfs(void) +{ + init_gpfs_function(&gpfs_set_share_fn, "gpfs_set_share"); + init_gpfs_function(&gpfs_set_lease_fn, "gpfs_set_lease"); + init_gpfs_function(&gpfs_getacl_fn, "gpfs_getacl"); + init_gpfs_function(&gpfs_putacl_fn, "gpfs_putacl"); gpfs_share_modes = lp_parm_bool(-1, "gpfs", "sharemodes", True); gpfs_leases = lp_parm_bool(-1, "gpfs", "leases", True); return; - -failed: - dlclose(libgpfs_handle); - /* leave libgpfs_handle != NULL around, no point - in trying twice */ - gpfs_set_share_fn = NULL; - gpfs_set_lease_fn = NULL; - gpfs_getacl_fn = NULL; - gpfs_putacl_fn = NULL; } #else diff --git a/source3/modules/nfs4_acls.h b/source3/modules/nfs4_acls.h index 0f783aa977..a227c6e0fc 100644 --- a/source3/modules/nfs4_acls.h +++ b/source3/modules/nfs4_acls.h @@ -144,7 +144,7 @@ typedef bool (*set_nfs4acl_native_fn_t)(files_struct *, SMB4ACL_T *); NTSTATUS smb_set_nt_acl_nfs4(files_struct *fsp, uint32 security_info_sent, - SEC_DESC *psd, + const SEC_DESC *psd, set_nfs4acl_native_fn_t set_nfs4_native); #endif /* __NFS4_ACLS_H__ */ diff --git a/source3/modules/onefs.h b/source3/modules/onefs.h new file mode 100644 index 0000000000..965f39509a --- /dev/null +++ b/source3/modules/onefs.h @@ -0,0 +1,52 @@ +/* + * Unix SMB/CIFS implementation. + * Support for OneFS + * + * Copyright (C) Steven Danneman, 2008 + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _ONEFS_H +#define _ONEFS_H + +/* OneFS Module smb.conf parameters and defaults */ + +/** +* Specifies when ACLs presented to Windows should be canonicalized +* into the ordering which Explorer expects. +*/ +enum onefs_acl_wire_format +{ + ACL_FORMAT_RAW, /**< Never canonicalize */ + ACL_FORMAT_WINDOWS_SD, /**< Only canonicalize synthetic ACLs */ + ACL_FORMAT_ALWAYS /**< Always canonicalize */ +}; + +const struct enum_list enum_onefs_acl_wire_format[] = { + {ACL_FORMAT_RAW, "No Format"}, + {ACL_FORMAT_WINDOWS_SD, "Format Windows SD"}, + {ACL_FORMAT_ALWAYS, "Always Format SD"}, + {-1, NULL} +}; + +#define PARM_ONEFS_TYPE "onefs" +#define PARM_ACL_WIRE_FORMAT "acl wire format" +#define PARM_ACL_WIRE_FORMAT_DEFAULT ACL_FORMAT_WINDOWS_SD +#define PARM_SIMPLE_FILE_SHARING_COMPATIBILITY_MODE "simple file sharing compatibility mode" +#define PARM_SIMPLE_FILE_SHARING_COMPATIBILITY_MODE_DEFAULT false +#define PARM_CREATOR_OWNER_GETS_FULL_CONTROL "creator owner gets full control" +#define PARM_CREATOR_OWNER_GETS_FULL_CONTROL_DEFAULT true + +#endif /* _ONEFS_H */ diff --git a/source3/modules/onefs_acl.c b/source3/modules/onefs_acl.c new file mode 100644 index 0000000000..3a692c95ab --- /dev/null +++ b/source3/modules/onefs_acl.c @@ -0,0 +1,807 @@ +/* + * Unix SMB/CIFS implementation. + * + * Support for OneFS native NTFS ACLs + * + * Copyright (C) Steven Danneman, 2008 + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#include <sys/isi_acl.h> +#include <isi_acl/isi_acl_util.h> +#include <sys/isi_oplock.h> +#include <ifs/ifs_syscalls.h> + +#include "onefs.h" + +/** + * Turn SID into UID/GID and setup a struct ifs_identity + */ +static bool +onefs_sid_to_identity(DOM_SID *sid, struct ifs_identity *id, bool is_group) +{ + enum ifs_identity_type type = IFS_ID_TYPE_LAST+1; + uid_t uid = 0; + gid_t gid = 0; + + if (!sid || sid_equal(sid, &global_sid_NULL)) + type = IFS_ID_TYPE_NULL; + else if (sid_equal(sid, &global_sid_World)) + type = IFS_ID_TYPE_EVERYONE; + else if (sid_equal(sid, &global_sid_Creator_Owner)) + type = IFS_ID_TYPE_CREATOR_OWNER; + else if (sid_equal(sid, &global_sid_Creator_Group)) + type = IFS_ID_TYPE_CREATOR_GROUP; + else if (is_group) { + if (!sid_to_gid(sid, &gid)) + return false; + type = IFS_ID_TYPE_GID; + } else { + if (sid_to_uid(sid, &uid)) + type = IFS_ID_TYPE_UID; + else if (sid_to_gid(sid, &gid)) + type = IFS_ID_TYPE_GID; + else + return false; + } + + if (aclu_initialize_identity(id, type, uid, gid, is_group)) { + DEBUG(3, ("Call to aclu_initialize_identity failed! id=%x, " + "type=%d, uid=%u, gid=%u, is_group=%d\n", + (unsigned int)id, type, uid, gid, is_group)); + return false; + } + + return true; +} + +/** + * Turn struct ifs_identity into SID + */ +static bool +onefs_identity_to_sid(struct ifs_identity *id, DOM_SID *sid) +{ + if (!id || !sid) + return false; + + if (id->type >= IFS_ID_TYPE_LAST) + return false; + + switch (id->type) { + case IFS_ID_TYPE_UID: + uid_to_sid(sid, id->id.uid); + break; + case IFS_ID_TYPE_GID: + gid_to_sid(sid, id->id.gid); + break; + case IFS_ID_TYPE_EVERYONE: + sid_copy(sid, &global_sid_World); + break; + case IFS_ID_TYPE_NULL: + sid_copy(sid, &global_sid_NULL); + break; + case IFS_ID_TYPE_CREATOR_OWNER: + sid_copy(sid, &global_sid_Creator_Owner); + break; + case IFS_ID_TYPE_CREATOR_GROUP: + sid_copy(sid, &global_sid_Creator_Group); + break; + default: + DEBUG(0, ("Unknown identity type: %d\n", id->type)); + return false; + } + + return true; +} + +/** + * Convert a SEC_ACL to a struct ifs_security_acl + */ +static bool +onefs_samba_acl_to_acl(SEC_ACL *samba_acl, struct ifs_security_acl **acl) +{ + int num_aces = 0; + struct ifs_ace *aces = NULL; + struct ifs_identity temp; + SEC_ACE *samba_aces; + int i, j; + + if ((!acl) || (!samba_acl)) + return false; + + samba_aces = samba_acl->aces; + + if (samba_acl->num_aces > 0 && samba_aces) { + /* Setup ACES */ + num_aces = samba_acl->num_aces; + aces = SMB_MALLOC_ARRAY(struct ifs_ace, num_aces); + + for (i = 0, j = 0; j < num_aces; i++, j++) { + if (!onefs_sid_to_identity(&samba_aces[j].trustee, + &temp, false)) + goto err_free; + + /* + * XXX Act like we did pre-Thai: Silently fail setting + * ACEs for BUILTIN accounts. + */ + if (temp.id.uid == -1) { + DEBUG(3, ("Silently failing to set ACE " + "because our id was == -1.\n")); + i--; + continue; + } + + if (aclu_initialize_ace(&aces[i], samba_aces[i].type, + samba_aces[i].access_mask, samba_aces[i].flags, + 0, &temp)) + goto err_free; + + if ((aces[i].trustee.type == IFS_ID_TYPE_CREATOR_OWNER || + aces[i].trustee.type == IFS_ID_TYPE_CREATOR_GROUP) && + nt4_compatible_acls()) + aces[i].flags |= IFS_ACE_FLAG_INHERIT_ONLY; + } + num_aces = i; + } + + if (aclu_initialize_acl(acl, aces, num_aces)) + goto err_free; + + /* Currently aclu_initialize_acl should copy the aces over, allowing us + * to immediately free */ + free(aces); + return true; + +err_free: + free(aces); + return false; +} + +/** + * Convert a struct ifs_security_acl to a SEC_ACL + */ +static bool +onefs_acl_to_samba_acl(struct ifs_security_acl *acl, SEC_ACL **samba_acl) +{ + SEC_ACE *samba_aces = NULL; + SEC_ACL *tmp_samba_acl = NULL; + int i, num_aces = 0; + + if (!samba_acl) + return false; + + /* NULL ACL */ + if (!acl) { + *samba_acl = NULL; + return true; + } + + /* Determine number of aces in ACL */ + if (!acl->aces) + num_aces = 0; + else + num_aces = acl->num_aces; + + /* Allocate the ace list. */ + if (num_aces > 0) { + if ((samba_aces = SMB_MALLOC_ARRAY(SEC_ACE, num_aces)) == NULL) + { + DEBUG(0, ("Unable to malloc space for %d aces.\n", + num_aces)); + return false; + } + memset(samba_aces, '\0', (num_aces) * sizeof(SEC_ACE)); + } + + for (i = 0; i < num_aces; i++) { + DOM_SID sid; + + if (!onefs_identity_to_sid(&acl->aces[i].trustee, &sid)) + goto err_free; + + init_sec_ace(&samba_aces[i], &sid, acl->aces[i].type, + acl->aces[i].access_mask, acl->aces[i].flags); + } + + if ((tmp_samba_acl = make_sec_acl(talloc_tos(), acl->revision, num_aces, + samba_aces)) == NULL) { + DEBUG(0, ("Unable to malloc space for acl.\n")); + goto err_free; + } + + *samba_acl = tmp_samba_acl; + SAFE_FREE(samba_aces); + return true; +err_free: + SAFE_FREE(samba_aces); + return false; +} + +/** + * @brief Reorder ACLs into the "correct" order for Windows Explorer. + * + * Windows Explorer expects ACLs to be in a standard order (inherited first, + * then deny, then permit.) When ACLs are composed from POSIX file permissions + * bits, they may not match these expectations, generating an annoying warning + * dialog for the user. This function will, if configured appropriately, + * reorder the ACLs for these "synthetic" (POSIX-derived) descriptors to prevent + * this. The list is changed within the security descriptor passed in. + * + * @param fsp files_struct with service configs; must not be NULL + * @param sd security descriptor being normalized; + * sd->dacl->aces is rewritten in-place, so must not be NULL + * @return true on success, errno will be set on error + * + * @bug Although Windows Explorer likes the reordering, they seem to cause + * problems with Excel and Word sending back the reordered ACLs to us and + * changing policy; see Isilon bug 30165. + */ +static bool +onefs_canon_acl(files_struct *fsp, struct ifs_security_descriptor *sd) +{ + int error = 0; + int cur; + struct ifs_ace *new_aces = NULL; + int new_aces_count = 0; + SMB_STRUCT_STAT sbuf; + + if (sd == NULL || sd->dacl == NULL || sd->dacl->num_aces == 0) + return true; + + /* + * Find out if this is a windows bit, and if the smb policy wants us to + * lie about the sd. + */ + SMB_ASSERT(fsp != NULL); + switch (lp_parm_enum(SNUM(fsp->conn), PARM_ONEFS_TYPE, + PARM_ACL_WIRE_FORMAT, enum_onefs_acl_wire_format, + PARM_ACL_WIRE_FORMAT_DEFAULT)) { + case ACL_FORMAT_RAW: + return true; + + case ACL_FORMAT_WINDOWS_SD: + error = SMB_VFS_FSTAT(fsp, &sbuf); + if (error) + return false; + + if ((sbuf.st_flags & SF_HASNTFSACL) != 0) { + DEBUG(10, ("Did not canonicalize ACLs because a " + "Windows ACL set was found for file %s\n", + fsp->fsp_name)); + return true; + } + break; + + case ACL_FORMAT_ALWAYS: + break; + + default: + SMB_ASSERT(false); + return false; + } + + new_aces = SMB_MALLOC_ARRAY(struct ifs_ace, sd->dacl->num_aces); + if (new_aces == NULL) + return false; + + /* + * By walking down the list 3 separate times, we can avoid the need + * to create multiple temp buffers and extra copies. + */ + for (cur = 0; cur < sd->dacl->num_aces; cur++) { + if (sd->dacl->aces[cur].flags & IFS_ACE_FLAG_INHERITED_ACE) + new_aces[new_aces_count++] = sd->dacl->aces[cur]; + } + + for (cur = 0; cur < sd->dacl->num_aces; cur++) { + if (!(sd->dacl->aces[cur].flags & IFS_ACE_FLAG_INHERITED_ACE) && + (sd->dacl->aces[cur].type == IFS_ACE_TYPE_ACCESS_DENIED)) + new_aces[new_aces_count++] = sd->dacl->aces[cur]; + } + + for (cur = 0; cur < sd->dacl->num_aces; cur++) { + if (!(sd->dacl->aces[cur].flags & IFS_ACE_FLAG_INHERITED_ACE) && + !(sd->dacl->aces[cur].type == IFS_ACE_TYPE_ACCESS_DENIED)) + new_aces[new_aces_count++] = sd->dacl->aces[cur]; + } + + SMB_ASSERT(new_aces_count == sd->dacl->num_aces); + DEBUG(10, ("Performed canonicalization of ACLs for file %s\n", + fsp->fsp_name)); + + /* + * At this point you would think we could just do this: + * SAFE_FREE(sd->dacl->aces); + * sd->dacl->aces = new_aces; + * However, in some cases the existing aces pointer does not point + * to the beginning of an allocated block. So we have to do a more + * expensive memcpy() + */ + memcpy(sd->dacl->aces, new_aces, + sizeof(struct ifs_ace) * new_aces_count); + + SAFE_FREE(new_aces); + return true; +} + + +/** + * This enum is a helper for onefs_fget_nt_acl() to communicate with + * onefs_init_ace(). + */ +enum mode_ident { USR, GRP, OTH }; + +/** + * Initializes an ACE for addition to a synthetic ACL. + */ +static struct ifs_ace onefs_init_ace(struct connection_struct *conn, + mode_t mode, + bool isdir, + enum mode_ident ident) +{ + struct ifs_ace result; + enum ifs_ace_rights r,w,x; + + r = isdir ? UNIX_DIRECTORY_ACCESS_R : UNIX_ACCESS_R; + w = isdir ? UNIX_DIRECTORY_ACCESS_W : UNIX_ACCESS_W; + x = isdir ? UNIX_DIRECTORY_ACCESS_X : UNIX_ACCESS_X; + + result.type = IFS_ACE_TYPE_ACCESS_ALLOWED; + result.ifs_flags = 0; + result.flags = isdir ? IFS_ACE_FLAG_CONTAINER_INHERIT : + IFS_ACE_FLAG_OBJECT_INHERIT; + result.flags |= IFS_ACE_FLAG_INHERIT_ONLY; + + switch (ident) { + case USR: + result.access_mask = + ((mode & S_IRUSR) ? r : 0 ) | + ((mode & S_IWUSR) ? w : 0 ) | + ((mode & S_IXUSR) ? x : 0 ); + if (lp_parm_bool(SNUM(conn), PARM_ONEFS_TYPE, + PARM_CREATOR_OWNER_GETS_FULL_CONTROL, + PARM_CREATOR_OWNER_GETS_FULL_CONTROL_DEFAULT)) + result.access_mask |= GENERIC_ALL_ACCESS; + result.trustee.type = IFS_ID_TYPE_CREATOR_OWNER; + break; + case GRP: + result.access_mask = + ((mode & S_IRGRP) ? r : 0 ) | + ((mode & S_IWGRP) ? w : 0 ) | + ((mode & S_IXGRP) ? x : 0 ); + result.trustee.type = IFS_ID_TYPE_CREATOR_GROUP; + break; + case OTH: + result.access_mask = + ((mode & S_IROTH) ? r : 0 ) | + ((mode & S_IWOTH) ? w : 0 ) | + ((mode & S_IXOTH) ? x : 0 ); + result.trustee.type = IFS_ID_TYPE_EVERYONE; + break; + } + + return result; +} + +/** + * This adds inheritable ACEs to the end of the DACL, with the ACEs + * being derived from the mode bits. This is useful for clients that have the + * MoveSecurityAttributes regkey set to 0 or are in Simple File Sharing Mode. + * + * On these clients, when copying files from one folder to another inside the + * same volume/share, the DACL is explicitely cleared. Without inheritable + * aces on the target folder the mode bits of the copied file are set to 000. + * + * See Isilon Bug 27990 + * + * Note: This function allocates additional memory onto sd->dacl->aces, that + * must be freed by the caller. + */ +static bool add_sfs_aces(files_struct *fsp, struct ifs_security_descriptor *sd) +{ + int error; + SMB_STRUCT_STAT sbuf; + + error = SMB_VFS_FSTAT(fsp, &sbuf); + if (error) { + DEBUG(0, ("Failed to stat %s in simple files sharing " + "compatibility mode. errno=%d\n", + fsp->fsp_name, errno)); + return false; + } + + /* Only continue if this is a synthetic ACL and a directory. */ + if (S_ISDIR(sbuf.st_mode) && (sbuf.st_flags & SF_HASNTFSACL) == 0) { + struct ifs_ace new_aces[6]; + struct ifs_ace *old_aces; + int i, num_aces_to_add = 0; + mode_t file_mode = 0, dir_mode = 0; + + /* Use existing samba logic to derive the mode bits. */ + file_mode = unix_mode(fsp->conn, 0, fsp->fsp_name, false); + dir_mode = unix_mode(fsp->conn, aDIR, fsp->fsp_name, false); + + /* Initialize ACEs. */ + new_aces[0] = onefs_init_ace(fsp->conn, file_mode, false, USR); + new_aces[1] = onefs_init_ace(fsp->conn, file_mode, false, GRP); + new_aces[2] = onefs_init_ace(fsp->conn, file_mode, false, OTH); + new_aces[3] = onefs_init_ace(fsp->conn, dir_mode, true, USR); + new_aces[4] = onefs_init_ace(fsp->conn, dir_mode, true, GRP); + new_aces[5] = onefs_init_ace(fsp->conn, dir_mode, true, OTH); + + for (i = 0; i < 6; i++) + if (new_aces[i].access_mask != 0) + num_aces_to_add++; + + /* Expand the ACEs array */ + if (num_aces_to_add != 0) { + old_aces = sd->dacl->aces; + + sd->dacl->aces = SMB_MALLOC_ARRAY(struct ifs_ace, + sd->dacl->num_aces + num_aces_to_add); + if (!sd->dacl->aces) { + DEBUG(0, ("Unable to malloc space for " + "new_aces: %d.\n", + sd->dacl->num_aces + num_aces_to_add)); + return false; + } + memcpy(sd->dacl->aces, old_aces, + sizeof(struct ifs_ace) * sd->dacl->num_aces); + + /* Add the new ACEs to the DACL. */ + for (i = 0; i < 6; i++) { + if (new_aces[i].access_mask != 0) { + sd->dacl->aces[sd->dacl->num_aces] = + new_aces[i]; + sd->dacl->num_aces++; + } + } + } + } + return true; +} + +/** + * Isilon-specific function for getting an NTFS ACL from an open file. + * + * @param[out] ppdesc SecDesc to allocate and fill in + * + * @return NTSTATUS based off errno on error + */ +NTSTATUS +onefs_fget_nt_acl(vfs_handle_struct *handle, files_struct *fsp, + uint32 security_info, SEC_DESC **ppdesc) +{ + int error; + uint32_t sd_size = 0; + size_t size = 0; + struct ifs_security_descriptor *sd = NULL; + DOM_SID owner_sid, group_sid; + DOM_SID *ownerp, *groupp; + SEC_ACL *dacl, *sacl; + SEC_DESC *pdesc; + bool alloced = false; + bool new_aces_alloced = false; + bool fopened = false; + NTSTATUS status = NT_STATUS_OK; + + *ppdesc = NULL; + + DEBUG(5, ("Getting sd for file %s. security_info=%u\n", + fsp->fsp_name, security_info)); + + if (fsp->fh->fd == -1) { + enum ifs_ace_rights desired_access = 0; + + if (security_info & (OWNER_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION)) + desired_access |= IFS_RTS_STD_READ_CONTROL; + if (security_info & SACL_SECURITY_INFORMATION) + desired_access |= IFS_RTS_SACL_ACCESS; + + if ((fsp->fh->fd = ifs_createfile(-1, + fsp->fsp_name, + desired_access, + 0, 0, + OPLOCK_NONE, + 0, NULL, 0, + NULL, 0, NULL)) == -1) { + DEBUG(0, ("Error opening file %s. errno=%d\n", + fsp->fsp_name, errno)); + status = map_nt_error_from_unix(errno); + goto out; + } + fopened = true; + } + + /* Get security descriptor */ + sd_size = 0; + do { + /* Allocate memory for get_security_descriptor */ + if (sd_size > 0) { + sd = SMB_REALLOC(sd, sd_size); + if (!sd) { + DEBUG(0, ("Unable to malloc %u bytes of space " + "for security descriptor.\n", sd_size)); + status = map_nt_error_from_unix(errno); + goto out; + } + + alloced = true; + } + + error = ifs_get_security_descriptor(fsp->fh->fd, security_info, + &sd_size, sd); + if (error && (errno != EMSGSIZE)) { + DEBUG(0, ("Failed getting size of security descriptor! " + "errno=%d\n", errno)); + status = map_nt_error_from_unix(errno); + goto out; + } + } while (error); + + DEBUG(5, ("Got sd, size=%u:\n", sd_size)); + + if (lp_parm_bool(SNUM(fsp->conn), + PARM_ONEFS_TYPE, + PARM_SIMPLE_FILE_SHARING_COMPATIBILITY_MODE, + PARM_SIMPLE_FILE_SHARING_COMPATIBILITY_MODE_DEFAULT) && + sd->dacl) { + if(!(new_aces_alloced = add_sfs_aces(fsp, sd))) + goto out; + } + + if (!(onefs_canon_acl(fsp, sd))) { + status = map_nt_error_from_unix(errno); + goto out; + } + + DEBUG(5, ("Finished canonicalizing ACL\n")); + + ownerp = NULL; + groupp = NULL; + dacl = NULL; + sacl = NULL; + + /* Copy owner into ppdesc */ + if (security_info & OWNER_SECURITY_INFORMATION) { + if (!onefs_identity_to_sid(sd->owner, &owner_sid)) { + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + ownerp = &owner_sid; + } + + /* Copy group into ppdesc */ + if (security_info & GROUP_SECURITY_INFORMATION) { + if (!onefs_identity_to_sid(sd->group, &group_sid)) { + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + groupp = &group_sid; + } + + /* Copy DACL into ppdesc */ + if (security_info & DACL_SECURITY_INFORMATION) { + if (!onefs_acl_to_samba_acl(sd->dacl, &dacl)) { + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + } + + /* Copy SACL into ppdesc */ + if (security_info & SACL_SECURITY_INFORMATION) { + if (!onefs_acl_to_samba_acl(sd->sacl, &sacl)) { + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + } + + /* AUTO_INHERIT_REQ bits are not returned over the wire so strip them + * off. Eventually we should stop storing these in the kernel + * all together. See Isilon bug 40364 */ + sd->control &= ~(IFS_SD_CTRL_DACL_AUTO_INHERIT_REQ | + IFS_SD_CTRL_SACL_AUTO_INHERIT_REQ); + + pdesc = make_sec_desc(talloc_tos(), sd->revision, sd->control, + ownerp, groupp, sacl, dacl, &size); + + if (!pdesc) { + DEBUG(0, ("Problem with make_sec_desc. Memory?\n")); + status = map_nt_error_from_unix(errno); + goto out; + } + + *ppdesc = pdesc; + + DEBUG(5, ("Finished retrieving/canonicalizing SD!\n")); + /* FALLTHROUGH */ +out: + if (alloced && sd) { + if (new_aces_alloced && sd->dacl->aces) + SAFE_FREE(sd->dacl->aces); + + SAFE_FREE(sd); + } + + if (fopened) { + close(fsp->fh->fd); + fsp->fh->fd = -1; + } + + return status; +} + +/** + * Isilon-specific function for getting an NTFS ACL from a file path. + * + * Since onefs_fget_nt_acl() needs to open a filepath if the fd is invalid, + * we just mock up a files_struct with the path and bad fd and call into it. + * + * @param[out] ppdesc SecDesc to allocate and fill in + * + * @return NTSTATUS based off errno on error + */ +NTSTATUS +onefs_get_nt_acl(vfs_handle_struct *handle, const char* name, + uint32 security_info, SEC_DESC **ppdesc) +{ + files_struct finfo; + struct fd_handle fh; + + ZERO_STRUCT(finfo); + ZERO_STRUCT(fh); + + finfo.fnum = -1; + finfo.conn = handle->conn; + finfo.fh = &fh; + finfo.fh->fd = -1; + finfo.fsp_name = CONST_DISCARD(char *, name); + + return onefs_fget_nt_acl(handle, &finfo, security_info, ppdesc); +} + +/** + * Isilon-specific function for setting an NTFS ACL on an open file. + * + * @return NT_STATUS_UNSUCCESSFUL for userspace errors, NTSTATUS based off + * errno on syscall errors + */ +NTSTATUS +onefs_fset_nt_acl(vfs_handle_struct *handle, files_struct *fsp, + uint32 security_info_sent, SEC_DESC *psd) +{ + struct ifs_security_descriptor sd = {}; + struct ifs_security_acl dacl, sacl, *daclp, *saclp; + struct ifs_identity owner, group, *ownerp, *groupp; + int fd; + bool fopened = false; + + DEBUG(5,("Setting SD on file %s.\n", fsp->fsp_name )); + + ownerp = NULL; + groupp = NULL; + daclp = NULL; + saclp = NULL; + + /* Setup owner */ + if (security_info_sent & OWNER_SECURITY_INFORMATION) { + if (!onefs_sid_to_identity(psd->owner_sid, &owner, false)) + return NT_STATUS_UNSUCCESSFUL; + + /* + * XXX Act like we did pre-Thai: Silently fail setting the + * owner to a BUILTIN account. + */ + if (owner.id.uid == -1) { + DEBUG(3, ("Silently failing to set owner because our " + "id was == -1.\n")); + security_info_sent &= ~OWNER_SECURITY_INFORMATION; + if (!security_info_sent) + return NT_STATUS_OK; + } + else + ownerp = &owner; + } + + /* Setup group */ + if (security_info_sent & GROUP_SECURITY_INFORMATION) { + if (!onefs_sid_to_identity(psd->group_sid, &group, true)) + return NT_STATUS_UNSUCCESSFUL; + + /* + * XXX Act like we did pre-Thai: Silently fail setting the + * group to a BUILTIN account. + */ + if (group.id.gid == -1) { + DEBUG(3, ("Silently failing to set group because our " + "id was == -1.\n")); + security_info_sent &= ~GROUP_SECURITY_INFORMATION; + if (!security_info_sent) + return NT_STATUS_OK; + } + else + groupp = &group; + } + + /* Setup DACL */ + if ((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl)) { + daclp = &dacl; + + if (!onefs_samba_acl_to_acl(psd->dacl, &daclp)) + return NT_STATUS_UNSUCCESSFUL; + } + + /* Setup SACL */ + if ((security_info_sent & SACL_SECURITY_INFORMATION) && (psd->sacl)) { + saclp = &sacl; + + if (!onefs_samba_acl_to_acl(psd->sacl, &saclp)) + return NT_STATUS_UNSUCCESSFUL; + } + + /* Setup ifs_security_descriptor */ + DEBUG(5,("Setting up SD\n")); + if (aclu_initialize_sd(&sd, psd->type, ownerp, groupp, + (daclp ? &daclp : NULL), (saclp ? &saclp : NULL), false)) + return NT_STATUS_UNSUCCESSFUL; + + fd = fsp->fh->fd; + if (fd == -1) { + enum ifs_ace_rights desired_access = 0; + + if (security_info_sent & + (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION)) + desired_access |= IFS_RTS_STD_WRITE_OWNER; + if (security_info_sent & DACL_SECURITY_INFORMATION) + desired_access |= IFS_RTS_STD_WRITE_DAC; + if (security_info_sent & SACL_SECURITY_INFORMATION) + desired_access |= IFS_RTS_SACL_ACCESS; + + if ((fd = ifs_createfile(-1, + fsp->fsp_name, + desired_access, + 0, 0, + OPLOCK_NONE, + 0, NULL, 0, + NULL, 0, NULL)) == -1) { + DEBUG(0, ("Error opening file %s. errno=%d\n", + fsp->fsp_name, errno)); + return map_nt_error_from_unix(errno); + } + fopened = true; + } + + errno = 0; + if (ifs_set_security_descriptor(fd, security_info_sent, &sd)) { + DEBUG(0, ("Error setting security descriptor = %d\n", errno)); + goto out; + } + + DEBUG(5, ("Security descriptor set correctly!\n")); + + /* FALLTHROUGH */ +out: + if (fopened) + close(fd); + + aclu_free_sd(&sd, false); + return errno ? map_nt_error_from_unix(errno) : NT_STATUS_OK; +} diff --git a/source3/modules/vfs_acl_tdb.c b/source3/modules/vfs_acl_tdb.c new file mode 100644 index 0000000000..915f73233d --- /dev/null +++ b/source3/modules/vfs_acl_tdb.c @@ -0,0 +1,891 @@ +/* + * Store Windows ACLs in a tdb. + * + * Copyright (C) Volker Lendecke, 2008 + * Copyright (C) Jeremy Allison, 2008 + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* NOTE: This is an experimental module, not yet finished. JRA. */ + +#include "includes.h" +#include "librpc/gen_ndr/xattr.h" +#include "librpc/gen_ndr/ndr_xattr.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_VFS + +static unsigned int ref_count; +static struct db_context *acl_db; + +/******************************************************************* + Open acl_db if not already open, increment ref count. +*******************************************************************/ + +static bool acl_tdb_init(struct db_context **pp_db) +{ + const char *dbname; + + if (acl_db) { + *pp_db = acl_db; + ref_count++; + return true; + } + + dbname = lock_path("file_ntacls.tdb"); + + if (dbname == NULL) { + errno = ENOSYS; + return false; + } + + become_root(); + *pp_db = db_open(NULL, dbname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600); + unbecome_root(); + + if (*pp_db == NULL) { +#if defined(ENOTSUP) + errno = ENOTSUP; +#else + errno = ENOSYS; +#endif + return false; + } + + ref_count++; + return true; +} + +/******************************************************************* + Lower ref count and close acl_db if zero. +*******************************************************************/ + +static void free_acl_tdb_data(void **pptr) +{ + struct db_context **pp_db = (struct db_context **)pptr; + + ref_count--; + if (ref_count == 0) { + TALLOC_FREE(*pp_db); + acl_db = NULL; + } +} + +/******************************************************************* + Fetch_lock the tdb acl record for a file +*******************************************************************/ + +static struct db_record *acl_tdb_lock(TALLOC_CTX *mem_ctx, + struct db_context *db, + const struct file_id *id) +{ + uint8 id_buf[16]; + push_file_id_16((char *)id_buf, id); + return db->fetch_locked(db, + mem_ctx, + make_tdb_data(id_buf, + sizeof(id_buf))); +} + +/******************************************************************* + Delete the tdb acl record for a file +*******************************************************************/ + +static NTSTATUS acl_tdb_delete(vfs_handle_struct *handle, + struct db_context *db, + SMB_STRUCT_STAT *psbuf) +{ + NTSTATUS status; + struct file_id id = vfs_file_id_from_sbuf(handle->conn, psbuf); + struct db_record *rec = acl_tdb_lock(talloc_tos(), db, &id); + + /* + * If rec == NULL there's not much we can do about it + */ + + if (rec == NULL) { + DEBUG(10,("acl_tdb_delete: rec == NULL\n")); + TALLOC_FREE(rec); + return NT_STATUS_OK; + } + + status = rec->delete_rec(rec); + TALLOC_FREE(rec); + return status; +} + +/******************************************************************* + Parse out a struct security_descriptor from a DATA_BLOB. +*******************************************************************/ + +static NTSTATUS parse_acl_blob(const DATA_BLOB *pblob, + uint32 security_info, + struct security_descriptor **ppdesc) +{ + TALLOC_CTX *ctx = talloc_tos(); + struct xattr_NTACL xacl; + enum ndr_err_code ndr_err; + size_t sd_size; + + ndr_err = ndr_pull_struct_blob(pblob, ctx, NULL, &xacl, + (ndr_pull_flags_fn_t)ndr_pull_xattr_NTACL); + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(5, ("parse_acl_blob: ndr_pull_xattr_NTACL failed: %s\n", + ndr_errstr(ndr_err))); + return ndr_map_error2ntstatus(ndr_err);; + } + + if (xacl.version != 2) { + return NT_STATUS_REVISION_MISMATCH; + } + + *ppdesc = make_sec_desc(ctx, SEC_DESC_REVISION, xacl.info.sd_hs->sd->type | SEC_DESC_SELF_RELATIVE, + (security_info & OWNER_SECURITY_INFORMATION) + ? xacl.info.sd_hs->sd->owner_sid : NULL, + (security_info & GROUP_SECURITY_INFORMATION) + ? xacl.info.sd_hs->sd->group_sid : NULL, + (security_info & SACL_SECURITY_INFORMATION) + ? xacl.info.sd_hs->sd->sacl : NULL, + (security_info & DACL_SECURITY_INFORMATION) + ? xacl.info.sd_hs->sd->dacl : NULL, + &sd_size); + + TALLOC_FREE(xacl.info.sd); + + return (*ppdesc != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY; +} + +/******************************************************************* + Pull a security descriptor into a DATA_BLOB from a tdb store. +*******************************************************************/ + +static NTSTATUS get_acl_blob(TALLOC_CTX *ctx, + vfs_handle_struct *handle, + files_struct *fsp, + const char *name, + DATA_BLOB *pblob) +{ + uint8 id_buf[16]; + TDB_DATA data; + struct file_id id; + struct db_context *db; + SMB_STRUCT_STAT sbuf; + + SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, + return NT_STATUS_INTERNAL_DB_CORRUPTION); + + if (fsp && fsp->fh->fd != -1) { + if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) { + return map_nt_error_from_unix(errno); + } + } else { + if (SMB_VFS_STAT(handle->conn, name, &sbuf) == -1) { + return map_nt_error_from_unix(errno); + } + } + id = vfs_file_id_from_sbuf(handle->conn, &sbuf); + + push_file_id_16((char *)id_buf, &id); + + if (db->fetch(db, + ctx, + make_tdb_data(id_buf, sizeof(id_buf)), + &data) == -1) { + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + pblob->data = data.dptr; + pblob->length = data.dsize; + + DEBUG(10,("get_acl_blob: returned %u bytes from file %s\n", + (unsigned int)data.dsize, name )); + + if (pblob->length == 0 || pblob->data == NULL) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + return NT_STATUS_OK; +} + +/******************************************************************* + Create a DATA_BLOB from a security descriptor. +*******************************************************************/ + +static NTSTATUS create_acl_blob(const struct security_descriptor *psd, DATA_BLOB *pblob) +{ + struct xattr_NTACL xacl; + struct security_descriptor_hash sd_hs; + enum ndr_err_code ndr_err; + TALLOC_CTX *ctx = talloc_tos(); + + ZERO_STRUCT(xacl); + ZERO_STRUCT(sd_hs); + + xacl.version = 2; + xacl.info.sd_hs = &sd_hs; + xacl.info.sd_hs->sd = CONST_DISCARD(struct security_descriptor *, psd); + memset(&xacl.info.sd_hs->hash[0], '\0', 16); + + ndr_err = ndr_push_struct_blob( + pblob, ctx, NULL, &xacl, + (ndr_push_flags_fn_t)ndr_push_xattr_NTACL); + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(5, ("create_acl_blob: ndr_push_xattr_NTACL failed: %s\n", + ndr_errstr(ndr_err))); + return ndr_map_error2ntstatus(ndr_err);; + } + + return NT_STATUS_OK; +} + +/******************************************************************* + Store a DATA_BLOB into a tdb record given an fsp pointer. +*******************************************************************/ + +static NTSTATUS store_acl_blob_fsp(vfs_handle_struct *handle, + files_struct *fsp, + DATA_BLOB *pblob) +{ + uint8 id_buf[16]; + struct file_id id; + SMB_STRUCT_STAT sbuf; + TDB_DATA data; + struct db_context *db; + struct db_record *rec; + + DEBUG(10,("store_acl_blob_fsp: storing blob length %u on file %s\n", + (unsigned int)pblob->length, fsp->fsp_name)); + + SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, + return NT_STATUS_INTERNAL_DB_CORRUPTION); + + if (fsp->fh->fd != -1) { + if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) { + return map_nt_error_from_unix(errno); + } + } else { + if (SMB_VFS_STAT(handle->conn, fsp->fsp_name, &sbuf) == -1) { + return map_nt_error_from_unix(errno); + } + } + id = vfs_file_id_from_sbuf(handle->conn, &sbuf); + + push_file_id_16((char *)id_buf, &id); + rec = db->fetch_locked(db, talloc_tos(), + make_tdb_data(id_buf, + sizeof(id_buf))); + if (rec == NULL) { + DEBUG(0, ("store_acl_blob_fsp_tdb: fetch_lock failed\n")); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + data.dptr = pblob->data; + data.dsize = pblob->length; + return rec->store(rec, data, 0); +} + +/******************************************************************* + Store a DATA_BLOB into a tdb record given a pathname. +*******************************************************************/ + +static NTSTATUS store_acl_blob_pathname(vfs_handle_struct *handle, + const char *fname, + DATA_BLOB *pblob) +{ + uint8 id_buf[16]; + struct file_id id; + TDB_DATA data; + SMB_STRUCT_STAT sbuf; + struct db_context *db; + struct db_record *rec; + + DEBUG(10,("store_acl_blob_pathname: storing blob " + "length %u on file %s\n", + (unsigned int)pblob->length, fname)); + + SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, + return NT_STATUS_INTERNAL_DB_CORRUPTION); + + if (SMB_VFS_STAT(handle->conn, fname, &sbuf) == -1) { + return map_nt_error_from_unix(errno); + } + + id = vfs_file_id_from_sbuf(handle->conn, &sbuf); + push_file_id_16((char *)id_buf, &id); + + rec = db->fetch_locked(db, talloc_tos(), + make_tdb_data(id_buf, + sizeof(id_buf))); + if (rec == NULL) { + DEBUG(0, ("store_acl_blob_pathname_tdb: fetch_lock failed\n")); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + data.dptr = pblob->data; + data.dsize = pblob->length; + return rec->store(rec, data, 0); +} + +/******************************************************************* + Store a DATA_BLOB into an tdb given a pathname. +*******************************************************************/ + +static NTSTATUS get_nt_acl_tdb_internal(vfs_handle_struct *handle, + files_struct *fsp, + const char *name, + uint32 security_info, + struct security_descriptor **ppdesc) +{ + TALLOC_CTX *ctx = talloc_tos(); + DATA_BLOB blob; + NTSTATUS status; + + if (fsp && name == NULL) { + name = fsp->fsp_name; + } + + DEBUG(10, ("get_nt_acl_tdb_internal: name=%s\n", name)); + + status = get_acl_blob(ctx, handle, fsp, name, &blob); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("get_acl_blob returned %s\n", nt_errstr(status))); + return status; + } + + status = parse_acl_blob(&blob, security_info, ppdesc); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("parse_acl_blob returned %s\n", + nt_errstr(status))); + return status; + } + + TALLOC_FREE(blob.data); + return status; +} + +/********************************************************************* + Create a default security descriptor for a file in case no inheritance + exists. All permissions to the owner and SYSTEM. +*********************************************************************/ + +static struct security_descriptor *default_file_sd(TALLOC_CTX *mem_ctx, + SMB_STRUCT_STAT *psbuf) +{ + struct dom_sid owner_sid, group_sid; + size_t sd_size; + struct security_ace *pace = NULL; + struct security_acl *pacl = NULL; + + uid_to_sid(&owner_sid, psbuf->st_uid); + gid_to_sid(&group_sid, psbuf->st_gid); + + pace = TALLOC_ARRAY(mem_ctx, struct security_ace, 2); + if (!pace) { + return NULL; + } + + init_sec_ace(&pace[0], &owner_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_ALL, 0); + init_sec_ace(&pace[1], &global_sid_System, SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_RIGHTS_FILE_ALL, 0); + + pacl = make_sec_acl(mem_ctx, + NT4_ACL_REVISION, + 2, + pace); + if (!pacl) { + return NULL; + } + return make_sec_desc(mem_ctx, + SECURITY_DESCRIPTOR_REVISION_1, + SEC_DESC_SELF_RELATIVE|SEC_DESC_DACL_PRESENT, + &owner_sid, + &group_sid, + NULL, + pacl, + &sd_size); +} + +/********************************************************************* +*********************************************************************/ + +static NTSTATUS inherit_new_acl(vfs_handle_struct *handle, + const char *fname, + files_struct *fsp, + bool container) +{ + TALLOC_CTX *ctx = talloc_tos(); + NTSTATUS status; + struct security_descriptor *parent_desc = NULL; + struct security_descriptor *psd = NULL; + DATA_BLOB blob; + size_t size; + char *parent_name; + + if (!parent_dirname_talloc(ctx, + fname, + &parent_name, + NULL)) { + return NT_STATUS_NO_MEMORY; + } + + DEBUG(10,("inherit_new_acl: check directory %s\n", + parent_name)); + + status = get_nt_acl_tdb_internal(handle, + NULL, + parent_name, + (OWNER_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION), + &parent_desc); + if (NT_STATUS_IS_OK(status)) { + /* Create an inherited descriptor from the parent. */ + + if (DEBUGLEVEL >= 10) { + DEBUG(10,("inherit_new_acl: parent acl is:\n")); + NDR_PRINT_DEBUG(security_descriptor, parent_desc); + } + + status = se_create_child_secdesc(ctx, + &psd, + &size, + parent_desc, + &handle->conn->server_info->ptok->user_sids[PRIMARY_USER_SID_INDEX], + &handle->conn->server_info->ptok->user_sids[PRIMARY_GROUP_SID_INDEX], + container); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (DEBUGLEVEL >= 10) { + DEBUG(10,("inherit_new_acl: child acl is:\n")); + NDR_PRINT_DEBUG(security_descriptor, psd); + } + + } else { + DEBUG(10,("inherit_new_acl: directory %s failed " + "to get acl %s\n", + parent_name, + nt_errstr(status) )); + } + + if (!psd || psd->dacl == NULL) { + SMB_STRUCT_STAT sbuf; + int ret; + + TALLOC_FREE(psd); + if (fsp && !fsp->is_directory && fsp->fh->fd != -1) { + ret = SMB_VFS_FSTAT(fsp, &sbuf); + } else { + ret = SMB_VFS_STAT(handle->conn,fname, &sbuf); + } + if (ret == -1) { + return map_nt_error_from_unix(errno); + } + psd = default_file_sd(ctx, &sbuf); + if (!psd) { + return NT_STATUS_NO_MEMORY; + } + + if (DEBUGLEVEL >= 10) { + DEBUG(10,("inherit_new_acl: default acl is:\n")); + NDR_PRINT_DEBUG(security_descriptor, psd); + } + } + + status = create_acl_blob(psd, &blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (fsp) { + return store_acl_blob_fsp(handle, fsp, &blob); + } else { + return store_acl_blob_pathname(handle, fname, &blob); + } +} + +/********************************************************************* + Check ACL on open. For new files inherit from parent directory. +*********************************************************************/ + +static int open_acl_tdb(vfs_handle_struct *handle, + const char *fname, + files_struct *fsp, + int flags, + mode_t mode) +{ + uint32_t access_granted = 0; + struct security_descriptor *pdesc = NULL; + bool file_existed = true; + NTSTATUS status = get_nt_acl_tdb_internal(handle, + NULL, + fname, + (OWNER_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION), + &pdesc); + if (NT_STATUS_IS_OK(status)) { + /* See if we can access it. */ + status = smb1_file_se_access_check(pdesc, + handle->conn->server_info->ptok, + fsp->access_mask, + &access_granted); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10,("open_acl_tdb: file %s open " + "refused with error %s\n", + fname, + nt_errstr(status) )); + errno = map_errno_from_nt_status(status); + return -1; + } + } else if (NT_STATUS_EQUAL(status,NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + file_existed = false; + } + + DEBUG(10,("open_acl_tdb: get_nt_acl_attr_internal for " + "file %s returned %s\n", + fname, + nt_errstr(status) )); + + fsp->fh->fd = SMB_VFS_NEXT_OPEN(handle, fname, fsp, flags, mode); + + if (!file_existed && fsp->fh->fd != -1) { + /* File was created. Inherit from parent directory. */ + string_set(&fsp->fsp_name, fname); + inherit_new_acl(handle, fname, fsp, false); + } + + return fsp->fh->fd; +} + +/********************************************************************* + On unlink we need to delete the tdb record (if using tdb). +*********************************************************************/ + +static int unlink_acl_tdb(vfs_handle_struct *handle, const char *path) +{ + SMB_STRUCT_STAT sbuf; + struct db_context *db; + int ret; + + SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1); + + if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) { + return -1; + } + + ret = SMB_VFS_NEXT_UNLINK(handle, path); + + if (ret == -1) { + return -1; + } + + acl_tdb_delete(handle, db, &sbuf); + return 0; +} + +/********************************************************************* + Store an inherited SD on mkdir. +*********************************************************************/ + +static int mkdir_acl_tdb(vfs_handle_struct *handle, const char *path, mode_t mode) +{ + int ret = SMB_VFS_NEXT_MKDIR(handle, path, mode); + + if (ret == -1) { + return ret; + } + /* New directory - inherit from parent. */ + inherit_new_acl(handle, path, NULL, true); + return ret; +} + +/********************************************************************* + On rmdir we need to delete the tdb record (if using tdb). +*********************************************************************/ + +static int rmdir_acl_tdb(vfs_handle_struct *handle, const char *path) +{ + + SMB_STRUCT_STAT sbuf; + struct db_context *db; + int ret; + + SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1); + + if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) { + return -1; + } + + ret = SMB_VFS_NEXT_RMDIR(handle, path); + if (ret == -1) { + return -1; + } + + acl_tdb_delete(handle, db, &sbuf); + return 0; +} + +/********************************************************************* + Fetch a security descriptor given an fsp. +*********************************************************************/ + +static NTSTATUS fget_nt_acl_tdb(vfs_handle_struct *handle, files_struct *fsp, + uint32 security_info, struct security_descriptor **ppdesc) +{ + NTSTATUS status = get_nt_acl_tdb_internal(handle, fsp, + NULL, security_info, ppdesc); + if (NT_STATUS_IS_OK(status)) { + if (DEBUGLEVEL >= 10) { + DEBUG(10,("fget_nt_acl_tdb: returning tdb sd for file %s\n", + fsp->fsp_name)); + NDR_PRINT_DEBUG(security_descriptor, *ppdesc); + } + return NT_STATUS_OK; + } + + DEBUG(10,("fget_nt_acl_tdb: failed to get tdb sd for file %s, Error %s\n", + fsp->fsp_name, + nt_errstr(status) )); + + return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, + security_info, ppdesc); +} + +/********************************************************************* + Fetch a security descriptor given a pathname. +*********************************************************************/ + +static NTSTATUS get_nt_acl_tdb(vfs_handle_struct *handle, + const char *name, uint32 security_info, struct security_descriptor **ppdesc) +{ + NTSTATUS status = get_nt_acl_tdb_internal(handle, NULL, + name, security_info, ppdesc); + if (NT_STATUS_IS_OK(status)) { + if (DEBUGLEVEL >= 10) { + DEBUG(10,("get_nt_acl_tdb: returning tdb sd for file %s\n", + name)); + NDR_PRINT_DEBUG(security_descriptor, *ppdesc); + } + return NT_STATUS_OK; + } + + DEBUG(10,("get_nt_acl_tdb: failed to get tdb sd for file %s, Error %s\n", + name, + nt_errstr(status) )); + + return SMB_VFS_NEXT_GET_NT_ACL(handle, name, + security_info, ppdesc); +} + +/********************************************************************* + Store a security descriptor given an fsp. +*********************************************************************/ + +static NTSTATUS fset_nt_acl_tdb(vfs_handle_struct *handle, files_struct *fsp, + uint32 security_info_sent, const struct security_descriptor *psd) +{ + NTSTATUS status; + DATA_BLOB blob; + + if (DEBUGLEVEL >= 10) { + DEBUG(10,("fset_nt_acl_tdb: incoming sd for file %s\n", + fsp->fsp_name)); + NDR_PRINT_DEBUG(security_descriptor, + CONST_DISCARD(struct security_descriptor *,psd)); + } + + status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* Ensure owner and group are set. */ + if (!psd->owner_sid || !psd->group_sid) { + int ret; + SMB_STRUCT_STAT sbuf; + DOM_SID owner_sid, group_sid; + struct security_descriptor *nc_psd = dup_sec_desc(talloc_tos(), psd); + + if (!nc_psd) { + return NT_STATUS_OK; + } + if (fsp->is_directory || fsp->fh->fd == -1) { + ret = SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf); + } else { + ret = SMB_VFS_FSTAT(fsp, &sbuf); + } + if (ret == -1) { + /* Lower level acl set succeeded, + * so still return OK. */ + return NT_STATUS_OK; + } + create_file_sids(&sbuf, &owner_sid, &group_sid); + /* This is safe as nc_psd is discarded at fn exit. */ + nc_psd->owner_sid = &owner_sid; + nc_psd->group_sid = &group_sid; + security_info_sent |= (OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION); + psd = nc_psd; + } + + if ((security_info_sent & DACL_SECURITY_INFORMATION) && + psd->dacl != NULL && + (psd->type & (SE_DESC_DACL_AUTO_INHERITED| + SE_DESC_DACL_AUTO_INHERIT_REQ))== + (SE_DESC_DACL_AUTO_INHERITED| + SE_DESC_DACL_AUTO_INHERIT_REQ) ) { + struct security_descriptor *new_psd = NULL; + status = append_parent_acl(fsp, psd, &new_psd); + if (!NT_STATUS_IS_OK(status)) { + /* Lower level acl set succeeded, + * so still return OK. */ + return NT_STATUS_OK; + } + psd = new_psd; + } + + if (DEBUGLEVEL >= 10) { + DEBUG(10,("fset_nt_acl_tdb: storing tdb sd for file %s\n", + fsp->fsp_name)); + NDR_PRINT_DEBUG(security_descriptor, + CONST_DISCARD(struct security_descriptor *,psd)); + } + create_acl_blob(psd, &blob); + store_acl_blob_fsp(handle, fsp, &blob); + + return NT_STATUS_OK; +} + +/******************************************************************* + Handle opening the storage tdb if so configured. +*******************************************************************/ + +static int connect_acl_tdb(struct vfs_handle_struct *handle, + const char *service, + const char *user) +{ + struct db_context *db; + int res; + + res = SMB_VFS_NEXT_CONNECT(handle, service, user); + if (res < 0) { + return res; + } + + if (!acl_tdb_init(&db)) { + SMB_VFS_NEXT_DISCONNECT(handle); + return -1; + } + + SMB_VFS_HANDLE_SET_DATA(handle, db, free_acl_tdb_data, + struct db_context, return -1); + + return 0; +} + +/********************************************************************* + Remove a Windows ACL - we're setting the underlying POSIX ACL. +*********************************************************************/ + +static int sys_acl_set_file_tdb(vfs_handle_struct *handle, + const char *path, + SMB_ACL_TYPE_T type, + SMB_ACL_T theacl) +{ + SMB_STRUCT_STAT sbuf; + struct db_context *db; + int ret; + + SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1); + + if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) { + return -1; + } + + ret = SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, + path, + type, + theacl); + if (ret == -1) { + return -1; + } + + acl_tdb_delete(handle, db, &sbuf); + return 0; +} + +/********************************************************************* + Remove a Windows ACL - we're setting the underlying POSIX ACL. +*********************************************************************/ + +static int sys_acl_set_fd_tdb(vfs_handle_struct *handle, + files_struct *fsp, + SMB_ACL_T theacl) +{ + SMB_STRUCT_STAT sbuf; + struct db_context *db; + int ret; + + SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1); + + if (fsp->is_directory || fsp->fh->fd == -1) { + ret = SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf); + } else { + ret = SMB_VFS_FSTAT(fsp, &sbuf); + } + if (ret == -1) { + return -1; + } + + ret = SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, + fsp, + theacl); + if (ret == -1) { + return -1; + } + + acl_tdb_delete(handle, db, &sbuf); + return 0; +} + +/* VFS operations structure */ + +static vfs_op_tuple skel_op_tuples[] = +{ + {SMB_VFS_OP(connect_acl_tdb), SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_TRANSPARENT}, + + {SMB_VFS_OP(mkdir_acl_tdb), SMB_VFS_OP_MKDIR, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(rmdir_acl_tdb), SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_TRANSPARENT}, + + {SMB_VFS_OP(open_acl_tdb), SMB_VFS_OP_OPEN, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(unlink_acl_tdb), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_TRANSPARENT}, + + /* NT File ACL operations */ + + {SMB_VFS_OP(fget_nt_acl_tdb),SMB_VFS_OP_FGET_NT_ACL,SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(get_nt_acl_tdb), SMB_VFS_OP_GET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(fset_nt_acl_tdb),SMB_VFS_OP_FSET_NT_ACL,SMB_VFS_LAYER_TRANSPARENT}, + + /* POSIX ACL operations. */ + {SMB_VFS_OP(sys_acl_set_file_tdb), SMB_VFS_OP_SYS_ACL_SET_FILE, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(sys_acl_set_fd_tdb), SMB_VFS_OP_SYS_ACL_SET_FD, SMB_VFS_LAYER_TRANSPARENT}, + + {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP} +}; + +NTSTATUS vfs_acl_tdb_init(void) +{ + return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "acl_tdb", skel_op_tuples); +} diff --git a/source3/modules/vfs_acl_xattr.c b/source3/modules/vfs_acl_xattr.c index 5dfe43e55b..2edb441741 100644 --- a/source3/modules/vfs_acl_xattr.c +++ b/source3/modules/vfs_acl_xattr.c @@ -27,8 +27,11 @@ #undef DBGC_CLASS #define DBGC_CLASS DBGC_VFS +/******************************************************************* + Parse out a struct security_descriptor from a DATA_BLOB. +*******************************************************************/ + static NTSTATUS parse_acl_blob(const DATA_BLOB *pblob, - const struct timespec cts, uint32 security_info, struct security_descriptor **ppdesc) { @@ -50,39 +53,15 @@ static NTSTATUS parse_acl_blob(const DATA_BLOB *pblob, return NT_STATUS_REVISION_MISMATCH; } -#if 0 - { - struct timespec ts; - /* Arg. This doesn't work. Too many activities - * change the ctime. May have to roll back to - * version 1. - */ - /* - * Check that the ctime timestamp is ealier - * than the stored timestamp. - */ - - ts = nt_time_to_unix_timespec(&xacl.info.sd_ts->last_changed); - - if (timespec_compare(&cts, &ts) > 0) { - DEBUG(5, ("parse_acl_blob: stored ACL out of date " - "(%s > %s.\n", - timestring(ctx, cts.tv_sec), - timestring(ctx, ts.tv_sec))); - return NT_STATUS_EA_CORRUPT_ERROR; - } - } -#endif - - *ppdesc = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, + *ppdesc = make_sec_desc(ctx, SEC_DESC_REVISION, xacl.info.sd_hs->sd->type | SEC_DESC_SELF_RELATIVE, (security_info & OWNER_SECURITY_INFORMATION) - ? xacl.info.sd_ts->sd->owner_sid : NULL, + ? xacl.info.sd_hs->sd->owner_sid : NULL, (security_info & GROUP_SECURITY_INFORMATION) - ? xacl.info.sd_ts->sd->group_sid : NULL, + ? xacl.info.sd_hs->sd->group_sid : NULL, (security_info & SACL_SECURITY_INFORMATION) - ? xacl.info.sd_ts->sd->sacl : NULL, + ? xacl.info.sd_hs->sd->sacl : NULL, (security_info & DACL_SECURITY_INFORMATION) - ? xacl.info.sd_ts->sd->dacl : NULL, + ? xacl.info.sd_hs->sd->dacl : NULL, &sd_size); TALLOC_FREE(xacl.info.sd); @@ -90,6 +69,10 @@ static NTSTATUS parse_acl_blob(const DATA_BLOB *pblob, return (*ppdesc != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY; } +/******************************************************************* + Pull a security descriptor into a DATA_BLOB from a xattr. +*******************************************************************/ + static NTSTATUS get_acl_blob(TALLOC_CTX *ctx, vfs_handle_struct *handle, files_struct *fsp, @@ -144,30 +127,24 @@ static NTSTATUS get_acl_blob(TALLOC_CTX *ctx, return NT_STATUS_OK; } +/******************************************************************* + Create a DATA_BLOB from a security descriptor. +*******************************************************************/ + static NTSTATUS create_acl_blob(const struct security_descriptor *psd, DATA_BLOB *pblob) { struct xattr_NTACL xacl; - struct security_descriptor_timestamp sd_ts; + struct security_descriptor_hash sd_hs; enum ndr_err_code ndr_err; TALLOC_CTX *ctx = talloc_tos(); - struct timespec curr = timespec_current(); ZERO_STRUCT(xacl); - ZERO_STRUCT(sd_ts); - - /* Horrid hack as setting an xattr changes the ctime - * on Linux. This gives a race of 1 second during - * which we would not see a POSIX ACL set. - */ - curr.tv_sec += 1; + ZERO_STRUCT(sd_hs); xacl.version = 2; - xacl.info.sd_ts = &sd_ts; - xacl.info.sd_ts->sd = CONST_DISCARD(struct security_descriptor *, psd); - unix_timespec_to_nt_time(&xacl.info.sd_ts->last_changed, curr); - - DEBUG(10, ("create_acl_blob: timestamp stored as %s\n", - timestring(ctx, curr.tv_sec) )); + xacl.info.sd_hs = &sd_hs; + xacl.info.sd_hs->sd = CONST_DISCARD(struct security_descriptor *, psd); + memset(&xacl.info.sd_hs->hash[0], '\0', 16); ndr_err = ndr_push_struct_blob( pblob, ctx, NULL, &xacl, @@ -182,7 +159,12 @@ static NTSTATUS create_acl_blob(const struct security_descriptor *psd, DATA_BLOB return NT_STATUS_OK; } -static NTSTATUS store_acl_blob_fsp(files_struct *fsp, +/******************************************************************* + Store a DATA_BLOB into an xattr given an fsp pointer. +*******************************************************************/ + +static NTSTATUS store_acl_blob_fsp(vfs_handle_struct *handle, + files_struct *fsp, DATA_BLOB *pblob) { int ret; @@ -215,10 +197,15 @@ static NTSTATUS store_acl_blob_fsp(files_struct *fsp, return NT_STATUS_OK; } -static NTSTATUS store_acl_blob_pathname(connection_struct *conn, +/******************************************************************* + Store a DATA_BLOB into an xattr given a pathname. +*******************************************************************/ + +static NTSTATUS store_acl_blob_pathname(vfs_handle_struct *handle, const char *fname, DATA_BLOB *pblob) { + connection_struct *conn = handle->conn; int ret; int saved_errno = 0; @@ -245,6 +232,9 @@ static NTSTATUS store_acl_blob_pathname(connection_struct *conn, return NT_STATUS_OK; } +/******************************************************************* + Store a DATA_BLOB into an xattr given a pathname. +*******************************************************************/ static NTSTATUS get_nt_acl_xattr_internal(vfs_handle_struct *handle, files_struct *fsp, @@ -254,7 +244,6 @@ static NTSTATUS get_nt_acl_xattr_internal(vfs_handle_struct *handle, { TALLOC_CTX *ctx = talloc_tos(); DATA_BLOB blob; - SMB_STRUCT_STAT sbuf; NTSTATUS status; if (fsp && name == NULL) { @@ -269,18 +258,7 @@ static NTSTATUS get_nt_acl_xattr_internal(vfs_handle_struct *handle, return status; } - if (fsp && fsp->fh->fd != -1) { - if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) { - return map_nt_error_from_unix(errno); - } - } else { - if (SMB_VFS_STAT(handle->conn, name, &sbuf) == -1) { - return map_nt_error_from_unix(errno); - } - } - - status = parse_acl_blob(&blob, get_ctimespec(&sbuf), - security_info, ppdesc); + status = parse_acl_blob(&blob, security_info, ppdesc); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("parse_acl_blob returned %s\n", nt_errstr(status))); @@ -326,8 +304,7 @@ static struct security_descriptor *default_file_sd(TALLOC_CTX *mem_ctx, } return make_sec_desc(mem_ctx, SECURITY_DESCRIPTOR_REVISION_1, - SEC_DESC_SELF_RELATIVE|SEC_DESC_DACL_PRESENT| - SEC_DESC_DACL_DEFAULTED, + SEC_DESC_SELF_RELATIVE|SEC_DESC_DACL_PRESENT, &owner_sid, &group_sid, NULL, @@ -364,28 +341,42 @@ static NTSTATUS inherit_new_acl(vfs_handle_struct *handle, status = get_nt_acl_xattr_internal(handle, NULL, parent_name, - DACL_SECURITY_INFORMATION, + (OWNER_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION), &parent_desc); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(10,("inherit_new_acl: directory %s failed " - "to get acl %s\n", - parent_name, - nt_errstr(status) )); - return status; - } + if (NT_STATUS_IS_OK(status)) { + /* Create an inherited descriptor from the parent. */ + + if (DEBUGLEVEL >= 10) { + DEBUG(10,("inherit_new_acl: parent acl is:\n")); + NDR_PRINT_DEBUG(security_descriptor, parent_desc); + } - /* Create an inherited descriptor from the parent. */ - status = se_create_child_secdesc(ctx, + status = se_create_child_secdesc(ctx, &psd, &size, parent_desc, &handle->conn->server_info->ptok->user_sids[PRIMARY_USER_SID_INDEX], &handle->conn->server_info->ptok->user_sids[PRIMARY_GROUP_SID_INDEX], container); - if (!NT_STATUS_IS_OK(status)) { - return status; + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (DEBUGLEVEL >= 10) { + DEBUG(10,("inherit_new_acl: child acl is:\n")); + NDR_PRINT_DEBUG(security_descriptor, psd); + } + + } else { + DEBUG(10,("inherit_new_acl: directory %s failed " + "to get acl %s\n", + parent_name, + nt_errstr(status) )); } - if (psd->dacl == NULL) { + + if (!psd || psd->dacl == NULL) { SMB_STRUCT_STAT sbuf; int ret; @@ -393,7 +384,7 @@ static NTSTATUS inherit_new_acl(vfs_handle_struct *handle, if (fsp && !fsp->is_directory && fsp->fh->fd != -1) { ret = SMB_VFS_FSTAT(fsp, &sbuf); } else { - ret = SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf); + ret = SMB_VFS_STAT(handle->conn,fname, &sbuf); } if (ret == -1) { return map_nt_error_from_unix(errno); @@ -402,6 +393,11 @@ static NTSTATUS inherit_new_acl(vfs_handle_struct *handle, if (!psd) { return NT_STATUS_NO_MEMORY; } + + if (DEBUGLEVEL >= 10) { + DEBUG(10,("inherit_new_acl: default acl is:\n")); + NDR_PRINT_DEBUG(security_descriptor, psd); + } } status = create_acl_blob(psd, &blob); @@ -409,9 +405,9 @@ static NTSTATUS inherit_new_acl(vfs_handle_struct *handle, return status; } if (fsp) { - return store_acl_blob_fsp(fsp, &blob); + return store_acl_blob_fsp(handle, fsp, &blob); } else { - return store_acl_blob_pathname(handle->conn, fname, &blob); + return store_acl_blob_pathname(handle, fname, &blob); } } @@ -481,6 +477,10 @@ static int mkdir_acl_xattr(vfs_handle_struct *handle, const char *path, mode_t m return ret; } +/********************************************************************* + Fetch a security descriptor given an fsp. +*********************************************************************/ + static NTSTATUS fget_nt_acl_xattr(vfs_handle_struct *handle, files_struct *fsp, uint32 security_info, struct security_descriptor **ppdesc) { @@ -494,10 +494,19 @@ static NTSTATUS fget_nt_acl_xattr(vfs_handle_struct *handle, files_struct *fsp, } return NT_STATUS_OK; } + + DEBUG(10,("fget_nt_acl_xattr: failed to get xattr sd for file %s, Error %s\n", + fsp->fsp_name, + nt_errstr(status) )); + return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info, ppdesc); } +/********************************************************************* + Fetch a security descriptor given a pathname. +*********************************************************************/ + static NTSTATUS get_nt_acl_xattr(vfs_handle_struct *handle, const char *name, uint32 security_info, struct security_descriptor **ppdesc) { @@ -511,10 +520,19 @@ static NTSTATUS get_nt_acl_xattr(vfs_handle_struct *handle, } return NT_STATUS_OK; } + + DEBUG(10,("get_nt_acl_xattr: failed to get xattr sd for file %s, Error %s\n", + name, + nt_errstr(status) )); + return SMB_VFS_NEXT_GET_NT_ACL(handle, name, security_info, ppdesc); } +/********************************************************************* + Store a security descriptor given an fsp. +*********************************************************************/ + static NTSTATUS fset_nt_acl_xattr(vfs_handle_struct *handle, files_struct *fsp, uint32 security_info_sent, const struct security_descriptor *psd) { @@ -584,11 +602,57 @@ static NTSTATUS fset_nt_acl_xattr(vfs_handle_struct *handle, files_struct *fsp, CONST_DISCARD(struct security_descriptor *,psd)); } create_acl_blob(psd, &blob); - store_acl_blob_fsp(fsp, &blob); + store_acl_blob_fsp(handle, fsp, &blob); return NT_STATUS_OK; } +/********************************************************************* + Remove a Windows ACL - we're setting the underlying POSIX ACL. +*********************************************************************/ + +static int sys_acl_set_file_xattr(vfs_handle_struct *handle, + const char *name, + SMB_ACL_TYPE_T type, + SMB_ACL_T theacl) +{ + int ret = SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, + name, + type, + theacl); + if (ret == -1) { + return -1; + } + + become_root(); + SMB_VFS_REMOVEXATTR(handle->conn, name, XATTR_NTACL_NAME); + unbecome_root(); + + return ret; +} + +/********************************************************************* + Remove a Windows ACL - we're setting the underlying POSIX ACL. +*********************************************************************/ + +static int sys_acl_set_fd_xattr(vfs_handle_struct *handle, + files_struct *fsp, + SMB_ACL_T theacl) +{ + int ret = SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, + fsp, + theacl); + if (ret == -1) { + return -1; + } + + become_root(); + SMB_VFS_FREMOVEXATTR(fsp, XATTR_NTACL_NAME); + unbecome_root(); + + return ret; +} + /* VFS operations structure */ static vfs_op_tuple skel_op_tuples[] = @@ -602,7 +666,11 @@ static vfs_op_tuple skel_op_tuples[] = {SMB_VFS_OP(get_nt_acl_xattr), SMB_VFS_OP_GET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT}, {SMB_VFS_OP(fset_nt_acl_xattr),SMB_VFS_OP_FSET_NT_ACL,SMB_VFS_LAYER_TRANSPARENT}, - {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP} + /* POSIX ACL operations. */ + {SMB_VFS_OP(sys_acl_set_file_xattr), SMB_VFS_OP_SYS_ACL_SET_FILE, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(sys_acl_set_fd_xattr), SMB_VFS_OP_SYS_ACL_SET_FD, SMB_VFS_LAYER_TRANSPARENT}, + + {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP} }; NTSTATUS vfs_acl_xattr_init(void) diff --git a/source3/modules/vfs_onefs.c b/source3/modules/vfs_onefs.c new file mode 100644 index 0000000000..193a986cf6 --- /dev/null +++ b/source3/modules/vfs_onefs.c @@ -0,0 +1,48 @@ +/* + * Support for OneFS + * + * Copyright (C) Tim Prouty, 2008 + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_VFS + +NTSTATUS onefs_fget_nt_acl(vfs_handle_struct *handle, files_struct *fsp, + uint32 security_info, SEC_DESC **ppdesc); + +NTSTATUS onefs_get_nt_acl(vfs_handle_struct *handle, const char* name, + uint32 security_info, SEC_DESC **ppdesc); + +NTSTATUS onefs_fset_nt_acl(vfs_handle_struct *handle, files_struct *fsp, + uint32 security_info_sent, SEC_DESC *psd); + +static vfs_op_tuple onefs_ops[] = { + {SMB_VFS_OP(onefs_fget_nt_acl), SMB_VFS_OP_FGET_NT_ACL, + SMB_VFS_LAYER_OPAQUE}, + {SMB_VFS_OP(onefs_get_nt_acl), SMB_VFS_OP_GET_NT_ACL, + SMB_VFS_LAYER_OPAQUE}, + {SMB_VFS_OP(onefs_fset_nt_acl), SMB_VFS_OP_FSET_NT_ACL, + SMB_VFS_LAYER_OPAQUE}, + {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP} +}; + +NTSTATUS vfs_onefs_init(void) +{ + return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "onefs", + onefs_ops); +} diff --git a/source3/modules/vfs_readonly.c b/source3/modules/vfs_readonly.c index d4ddf32e3a..58c83e5e1b 100644 --- a/source3/modules/vfs_readonly.c +++ b/source3/modules/vfs_readonly.c @@ -64,12 +64,25 @@ static int readonly_connect(vfs_handle_struct *handle, "period", period_def); if (period && period[0] && period[1]) { + int i; time_t current_time = time(NULL); time_t begin_period = get_date(period[0], ¤t_time); time_t end_period = get_date(period[1], ¤t_time); if ((current_time >= begin_period) && (current_time <= end_period)) { + connection_struct *conn = handle->conn; + handle->conn->read_only = True; + + /* Wipe out the VUID cache. */ + for (i=0; i< VUID_CACHE_SIZE; i++) { + struct vuid_cache_entry *ent = ent = &conn->vuid_cache.array[i]; + ent->vuid = UID_FIELD_INVALID; + TALLOC_FREE(ent->server_info); + ent->read_only = false; + ent->admin_user = false; + } + conn->vuid_cache.next_entry = 0; } return SMB_VFS_NEXT_CONNECT(handle, service, user); diff --git a/source3/modules/vfs_streams_depot.c b/source3/modules/vfs_streams_depot.c index d8c476f96f..69d34940fd 100644 --- a/source3/modules/vfs_streams_depot.c +++ b/source3/modules/vfs_streams_depot.c @@ -67,10 +67,15 @@ static uint32_t hash_fn(DATA_BLOB key) #define SAMBA_XATTR_MARKER "user.SAMBA_STREAMS" -static bool file_is_valid(vfs_handle_struct *handle, const char *path) +static bool file_is_valid(vfs_handle_struct *handle, const char *path, + bool check_valid) { char buf; + if (!check_valid) { + return true; + } + DEBUG(10, ("file_is_valid (%s) called\n", path)); if (SMB_VFS_NEXT_GETXATTR(handle, path, SAMBA_XATTR_MARKER, @@ -87,11 +92,16 @@ static bool file_is_valid(vfs_handle_struct *handle, const char *path) return true; } -static bool mark_file_valid(vfs_handle_struct *handle, const char *path) +static bool mark_file_valid(vfs_handle_struct *handle, const char *path, + bool check_valid) { char buf = '1'; int ret; + if (!check_valid) { + return true; + } + DEBUG(10, ("marking file %s as valid\n", path)); ret = SMB_VFS_NEXT_SETXATTR(handle, path, SAMBA_XATTR_MARKER, @@ -116,10 +126,22 @@ static char *stream_dir(vfs_handle_struct *handle, const char *base_path, char *id_hex; struct file_id id; uint8 id_buf[16]; + bool check_valid; + const char *rootdir; + + check_valid = lp_parm_bool(SNUM(handle->conn), + "streams_depot", "check_valid", true); + + tmp = talloc_asprintf(talloc_tos(), "%s/.streams", handle->conn->connectpath); - const char *rootdir = lp_parm_const_string( + if (tmp == NULL) { + errno = ENOMEM; + goto fail; + } + + rootdir = lp_parm_const_string( SNUM(handle->conn), "streams_depot", "directory", - handle->conn->connectpath); + tmp); if (base_sbuf == NULL) { if (SMB_VFS_NEXT_STAT(handle, base_path, &sbuf) == -1) { @@ -141,7 +163,7 @@ static char *stream_dir(vfs_handle_struct *handle, const char *base_path, first = hash & 0xff; second = (hash >> 8) & 0xff; - id_hex = hex_encode(talloc_tos(), id_buf, sizeof(id_buf)); + id_hex = hex_encode_talloc(talloc_tos(), id_buf, sizeof(id_buf)); if (id_hex == NULL) { errno = ENOMEM; @@ -166,7 +188,7 @@ static char *stream_dir(vfs_handle_struct *handle, const char *base_path, goto fail; } - if (file_is_valid(handle, base_path)) { + if (file_is_valid(handle, base_path, check_valid)) { return result; } @@ -236,7 +258,7 @@ static char *stream_dir(vfs_handle_struct *handle, const char *base_path, goto fail; } - if (!mark_file_valid(handle, base_path)) { + if (!mark_file_valid(handle, base_path, check_valid)) { goto fail; } @@ -262,6 +284,11 @@ static char *stream_name(vfs_handle_struct *handle, const char *fname, goto fail; } + /* if it's the ::$DATA stream just return the base file name */ + if (!sname) { + return base; + } + dirname = stream_dir(handle, base, NULL, create_dir); if (dirname == NULL) { @@ -401,6 +428,7 @@ static int streams_depot_open(vfs_handle_struct *handle, const char *fname, { TALLOC_CTX *frame; char *base = NULL; + char *sname = NULL; SMB_STRUCT_STAT base_sbuf; char *stream_fname; int ret = -1; @@ -412,11 +440,16 @@ static int streams_depot_open(vfs_handle_struct *handle, const char *fname, frame = talloc_stackframe(); if (!NT_STATUS_IS_OK(split_ntfs_stream_name(talloc_tos(), fname, - &base, NULL))) { + &base, &sname))) { errno = ENOMEM; goto done; } + if (!sname) { + ret = SMB_VFS_NEXT_OPEN(handle, base, fsp, flags, mode); + goto done; + } + ret = SMB_VFS_NEXT_STAT(handle, base, &base_sbuf); if (ret == -1) { @@ -478,6 +511,78 @@ static int streams_depot_unlink(vfs_handle_struct *handle, const char *fname) return SMB_VFS_NEXT_UNLINK(handle, fname); } +static int streams_depot_rename(vfs_handle_struct *handle, + const char *oldname, + const char *newname) +{ + TALLOC_CTX *frame = NULL; + int ret = -1; + bool old_is_stream; + bool new_is_stream; + char *obase = NULL; + char *osname = NULL; + char *nbase = NULL; + char *nsname = NULL; + char *ostream_fname = NULL; + char *nstream_fname = NULL; + + DEBUG(10, ("streams_depot_rename called for %s => %s\n", + oldname, newname)); + + old_is_stream = is_ntfs_stream_name(oldname); + new_is_stream = is_ntfs_stream_name(newname); + + if (!old_is_stream && !new_is_stream) { + return SMB_VFS_NEXT_RENAME(handle, oldname, newname); + } + + if (!(old_is_stream && new_is_stream)) { + errno = ENOSYS; + return -1; + } + + frame = talloc_stackframe(); + + if (!NT_STATUS_IS_OK(split_ntfs_stream_name(talloc_tos(), oldname, + &obase, &osname))) { + errno = ENOMEM; + goto done; + } + + if (!NT_STATUS_IS_OK(split_ntfs_stream_name(talloc_tos(), oldname, + &nbase, &nsname))) { + errno = ENOMEM; + goto done; + } + + /* for now don't allow renames from or to the default stream */ + if (!osname || !nsname) { + errno = ENOSYS; + goto done; + } + + if (StrCaseCmp(obase, nbase) != 0) { + errno = ENOSYS; + goto done; + } + + ostream_fname = stream_name(handle, oldname, false); + if (ostream_fname == NULL) { + return -1; + } + + nstream_fname = stream_name(handle, newname, false); + if (nstream_fname == NULL) { + return -1; + } + + ret = SMB_VFS_NEXT_RENAME(handle, ostream_fname, nstream_fname); + +done: + TALLOC_FREE(frame); + return ret; +} + static bool add_one_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams, struct stream_struct **streams, const char *name, SMB_OFF_T size, @@ -628,6 +733,8 @@ static vfs_op_tuple streams_depot_ops[] = { SMB_VFS_LAYER_TRANSPARENT}, {SMB_VFS_OP(streams_depot_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(streams_depot_rename), SMB_VFS_OP_RENAME, + SMB_VFS_LAYER_TRANSPARENT}, {SMB_VFS_OP(streams_depot_streaminfo), SMB_VFS_OP_STREAMINFO, SMB_VFS_LAYER_OPAQUE}, {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP} diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c index b74c4f7902..ecfc31970d 100644 --- a/source3/modules/vfs_streams_xattr.c +++ b/source3/modules/vfs_streams_xattr.c @@ -29,6 +29,9 @@ struct stream_io { char *base; char *xattr_name; + void *fsp_name_ptr; + files_struct *fsp; + vfs_handle_struct *handle; }; static SMB_INO_T stream_inode(const SMB_STRUCT_STAT *sbuf, const char *sname) @@ -64,14 +67,16 @@ static SMB_INO_T stream_inode(const SMB_STRUCT_STAT *sbuf, const char *sname) return result; } -static ssize_t get_xattr_size(connection_struct *conn, const char *fname, - const char *xattr_name) +static ssize_t get_xattr_size(connection_struct *conn, + files_struct *fsp, + const char *fname, + const char *xattr_name) { NTSTATUS status; struct ea_struct ea; ssize_t result; - status = get_ea_value(talloc_tos(), conn, NULL, fname, + status = get_ea_value(talloc_tos(), conn, fsp, fname, xattr_name, &ea); if (!NT_STATUS_IS_OK(status)) { @@ -83,6 +88,49 @@ static ssize_t get_xattr_size(connection_struct *conn, const char *fname, return result; } +static bool streams_xattr_recheck(struct stream_io *sio) +{ + NTSTATUS status; + char *base = NULL; + char *sname = NULL; + char *xattr_name = NULL; + + if (sio->fsp->fsp_name == sio->fsp_name_ptr) { + return true; + } + + status = split_ntfs_stream_name(talloc_tos(), sio->fsp->fsp_name, + &base, &sname); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + if (sname == NULL) { + /* how can this happen */ + errno = EINVAL; + return false; + } + + xattr_name = talloc_asprintf(talloc_tos(), "%s%s", + SAMBA_XATTR_DOSSTREAM_PREFIX, sname); + if (xattr_name == NULL) { + return false; + } + + TALLOC_FREE(sio->xattr_name); + TALLOC_FREE(sio->base); + sio->xattr_name = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(sio->handle, sio->fsp), + xattr_name); + sio->base = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(sio->handle, sio->fsp), + base); + sio->fsp_name_ptr = sio->fsp->fsp_name; + + if ((sio->xattr_name == NULL) || (sio->base == NULL)) { + return false; + } + + return true; +} static int streams_xattr_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf) @@ -92,15 +140,20 @@ static int streams_xattr_fstat(vfs_handle_struct *handle, files_struct *fsp, DEBUG(10, ("streams_xattr_fstat called for %d\n", fsp->fh->fd)); - if (io == NULL) { + if (io == NULL || fsp->base_fsp == NULL) { return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf); } - if (SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf) == -1) { + if (!streams_xattr_recheck(io)) { return -1; } - sbuf->st_size = get_xattr_size(handle->conn, io->base, io->xattr_name); + if (SMB_VFS_STAT(handle->conn, io->base, sbuf) == -1) { + return -1; + } + + sbuf->st_size = get_xattr_size(handle->conn, fsp->base_fsp, + io->base, io->xattr_name); if (sbuf->st_size == -1) { return -1; } @@ -133,6 +186,10 @@ static int streams_xattr_stat(vfs_handle_struct *handle, const char *fname, return -1; } + if (sname == NULL){ + return SMB_VFS_NEXT_STAT(handle, base, sbuf); + } + if (SMB_VFS_STAT(handle->conn, base, sbuf) == -1) { goto fail; } @@ -144,7 +201,7 @@ static int streams_xattr_stat(vfs_handle_struct *handle, const char *fname, goto fail; } - sbuf->st_size = get_xattr_size(handle->conn, base, xattr_name); + sbuf->st_size = get_xattr_size(handle->conn, NULL, base, xattr_name); if (sbuf->st_size == -1) { errno = ENOENT; goto fail; @@ -180,6 +237,10 @@ static int streams_xattr_lstat(vfs_handle_struct *handle, const char *fname, goto fail; } + if (sname == NULL){ + return SMB_VFS_NEXT_LSTAT(handle, base, sbuf); + } + if (SMB_VFS_LSTAT(handle->conn, base, sbuf) == -1) { goto fail; } @@ -191,7 +252,7 @@ static int streams_xattr_lstat(vfs_handle_struct *handle, const char *fname, goto fail; } - sbuf->st_size = get_xattr_size(handle->conn, base, xattr_name); + sbuf->st_size = get_xattr_size(handle->conn, NULL, base, xattr_name); if (sbuf->st_size == -1) { errno = ENOENT; goto fail; @@ -236,6 +297,12 @@ static int streams_xattr_open(vfs_handle_struct *handle, const char *fname, goto fail; } + if (sname == NULL) { + hostfd = SMB_VFS_NEXT_OPEN(handle, base, fsp, flags, mode); + talloc_free(frame); + return hostfd; + } + xattr_name = talloc_asprintf(talloc_tos(), "%s%s", SAMBA_XATTR_DOSSTREAM_PREFIX, sname); if (xattr_name == NULL) { @@ -300,22 +367,40 @@ static int streams_xattr_open(vfs_handle_struct *handle, const char *fname, DEBUG(10, ("creating attribute %s on file %s\n", xattr_name, base)); - if (SMB_VFS_SETXATTR( - handle->conn, base, xattr_name, - &null, sizeof(null), - flags & O_EXCL ? XATTR_CREATE : 0) == -1) { - goto fail; + if (fsp->base_fsp->fh->fd != -1) { + if (SMB_VFS_FSETXATTR( + fsp->base_fsp, xattr_name, + &null, sizeof(null), + flags & O_EXCL ? XATTR_CREATE : 0) == -1) { + goto fail; + } + } else { + if (SMB_VFS_SETXATTR( + handle->conn, base, xattr_name, + &null, sizeof(null), + flags & O_EXCL ? XATTR_CREATE : 0) == -1) { + goto fail; + } } } } if (flags & O_TRUNC) { char null = '\0'; - if (SMB_VFS_SETXATTR( - handle->conn, base, xattr_name, - &null, sizeof(null), - flags & O_EXCL ? XATTR_CREATE : 0) == -1) { - goto fail; + if (fsp->base_fsp->fh->fd != -1) { + if (SMB_VFS_FSETXATTR( + fsp->base_fsp, xattr_name, + &null, sizeof(null), + flags & O_EXCL ? XATTR_CREATE : 0) == -1) { + goto fail; + } + } else { + if (SMB_VFS_SETXATTR( + handle->conn, base, xattr_name, + &null, sizeof(null), + flags & O_EXCL ? XATTR_CREATE : 0) == -1) { + goto fail; + } } } @@ -330,6 +415,9 @@ static int streams_xattr_open(vfs_handle_struct *handle, const char *fname, xattr_name); sio->base = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp), base); + sio->fsp_name_ptr = fsp->fsp_name; + sio->handle = handle; + sio->fsp = fsp; if ((sio->xattr_name == NULL) || (sio->base == NULL)) { errno = ENOMEM; @@ -370,6 +458,10 @@ static int streams_xattr_unlink(vfs_handle_struct *handle, const char *fname) goto fail; } + if (sname == NULL){ + return SMB_VFS_NEXT_UNLINK(handle, base); + } + xattr_name = talloc_asprintf(talloc_tos(), "%s%s", SAMBA_XATTR_DOSSTREAM_PREFIX, sname); if (xattr_name == NULL) { @@ -392,6 +484,127 @@ static int streams_xattr_unlink(vfs_handle_struct *handle, const char *fname) return ret; } +static int streams_xattr_rename(vfs_handle_struct *handle, + const char *oldname, + const char *newname) +{ + NTSTATUS status; + TALLOC_CTX *frame = NULL; + char *obase; + char *ostream; + char *nbase; + char *nstream; + const char *base; + int ret = -1; + char *oxattr_name; + char *nxattr_name; + bool o_is_stream; + bool n_is_stream; + ssize_t oret; + ssize_t nret; + struct ea_struct ea; + + o_is_stream = is_ntfs_stream_name(oldname); + n_is_stream = is_ntfs_stream_name(newname); + + if (!o_is_stream && !n_is_stream) { + return SMB_VFS_NEXT_RENAME(handle, oldname, newname); + } + + if (!(o_is_stream && n_is_stream)) { + errno = ENOSYS; + goto fail; + } + + frame = talloc_stackframe(); + if (!frame) { + goto fail; + } + + status = split_ntfs_stream_name(talloc_tos(), oldname, &obase, &ostream); + if (!NT_STATUS_IS_OK(status)) { + errno = EINVAL; + goto fail; + } + + status = split_ntfs_stream_name(talloc_tos(), newname, &nbase, &nstream); + if (!NT_STATUS_IS_OK(status)) { + errno = EINVAL; + goto fail; + } + + /*TODO: maybe call SMB_VFS_NEXT_RENAME() both streams are NULL (::$DATA) */ + if (ostream == NULL) { + errno = ENOSYS; + goto fail; + } + + if (nstream == NULL) { + errno = ENOSYS; + goto fail; + } + + /* the new base should be empty */ + if (StrCaseCmp(obase, nbase) != 0) { + errno = ENOSYS; + goto fail; + } + + if (StrCaseCmp(ostream, nstream) == 0) { + goto done; + } + + base = obase; + + oxattr_name = talloc_asprintf(talloc_tos(), "%s%s", + SAMBA_XATTR_DOSSTREAM_PREFIX, ostream); + if (oxattr_name == NULL) { + errno = ENOMEM; + goto fail; + } + + nxattr_name = talloc_asprintf(talloc_tos(), "%s%s", + SAMBA_XATTR_DOSSTREAM_PREFIX, nstream); + if (nxattr_name == NULL) { + errno = ENOMEM; + goto fail; + } + + /* read the old stream */ + status = get_ea_value(talloc_tos(), handle->conn, NULL, + base, oxattr_name, &ea); + if (!NT_STATUS_IS_OK(status)) { + errno = ENOENT; + goto fail; + } + + /* (over)write the new stream */ + nret = SMB_VFS_SETXATTR(handle->conn, base, nxattr_name, + ea.value.data, ea.value.length, 0); + if (nret < 0) { + if (errno == ENOATTR) { + errno = ENOENT; + } + goto fail; + } + + /* remove the old stream */ + oret = SMB_VFS_REMOVEXATTR(handle->conn, base, oxattr_name); + if (oret < 0) { + if (errno == ENOATTR) { + errno = ENOENT; + } + goto fail; + } + + done: + errno = 0; + ret = 0; + fail: + TALLOC_FREE(frame); + return ret; +} + static NTSTATUS walk_xattr_streams(connection_struct *conn, files_struct *fsp, const char *fname, bool (*fn)(struct ea_struct *ea, @@ -579,6 +792,10 @@ static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle, return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset); } + if (!streams_xattr_recheck(sio)) { + return -1; + } + status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp, sio->base, sio->xattr_name, &ea); if (!NT_STATUS_IS_OK(status)) { @@ -603,10 +820,15 @@ static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle, memcpy(ea.value.data + offset, data, n); - ret = SMB_VFS_SETXATTR(fsp->conn, fsp->base_fsp->fsp_name, + if (fsp->base_fsp->fh->fd != -1) { + ret = SMB_VFS_FSETXATTR(fsp->base_fsp, sio->xattr_name, ea.value.data, ea.value.length, 0); - + } else { + ret = SMB_VFS_SETXATTR(fsp->conn, fsp->base_fsp->fsp_name, + sio->xattr_name, + ea.value.data, ea.value.length, 0); + } TALLOC_FREE(ea.value.data); if (ret == -1) { @@ -624,12 +846,16 @@ static ssize_t streams_xattr_pread(vfs_handle_struct *handle, (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp); struct ea_struct ea; NTSTATUS status; - size_t length, overlap; + size_t length, overlap; if (sio == NULL) { return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset); } + if (!streams_xattr_recheck(sio)) { + return -1; + } + status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp, sio->base, sio->xattr_name, &ea); if (!NT_STATUS_IS_OK(status)) { @@ -651,6 +877,73 @@ static ssize_t streams_xattr_pread(vfs_handle_struct *handle, return overlap; } +static int streams_xattr_ftruncate(struct vfs_handle_struct *handle, + struct files_struct *fsp, + SMB_OFF_T offset) +{ + int ret; + uint8 *tmp; + struct ea_struct ea; + NTSTATUS status; + struct stream_io *sio = + (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp); + + DEBUG(10, ("streams_xattr_ftruncate called for file %s offset %.0f\n", + fsp->fsp_name, + (double)offset )); + + if (sio == NULL) { + return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset); + } + + if (!streams_xattr_recheck(sio)) { + return -1; + } + + status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp, + sio->base, sio->xattr_name, &ea); + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + + tmp = TALLOC_REALLOC_ARRAY(talloc_tos(), ea.value.data, uint8, + offset + 1); + + if (tmp == NULL) { + TALLOC_FREE(ea.value.data); + errno = ENOMEM; + return -1; + } + + /* Did we expand ? */ + if (ea.value.length < offset + 1) { + memset(&tmp[ea.value.length], '\0', + offset + 1 - ea.value.length); + } + + ea.value.data = tmp; + ea.value.length = offset + 1; + ea.value.data[offset] = 0; + + if (fsp->base_fsp->fh->fd != -1) { + ret = SMB_VFS_FSETXATTR(fsp->base_fsp, + sio->xattr_name, + ea.value.data, ea.value.length, 0); + } else { + ret = SMB_VFS_SETXATTR(fsp->conn, fsp->base_fsp->fsp_name, + sio->xattr_name, + ea.value.data, ea.value.length, 0); + } + + TALLOC_FREE(ea.value.data); + + if (ret == -1) { + return -1; + } + + return 0; +} + /* VFS operations structure */ static vfs_op_tuple streams_xattr_ops[] = { @@ -672,6 +965,10 @@ static vfs_op_tuple streams_xattr_ops[] = { SMB_VFS_LAYER_TRANSPARENT}, {SMB_VFS_OP(streams_xattr_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(streams_xattr_rename), SMB_VFS_OP_RENAME, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(streams_xattr_ftruncate), SMB_VFS_OP_FTRUNCATE, + SMB_VFS_LAYER_TRANSPARENT}, {SMB_VFS_OP(streams_xattr_streaminfo), SMB_VFS_OP_STREAMINFO, SMB_VFS_LAYER_OPAQUE}, {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP} diff --git a/source3/modules/vfs_tsmsm.c b/source3/modules/vfs_tsmsm.c index ee958b1be5..6fb1d1d2d4 100644 --- a/source3/modules/vfs_tsmsm.c +++ b/source3/modules/vfs_tsmsm.c @@ -148,7 +148,7 @@ static bool tsmsm_is_offline(struct vfs_handle_struct *handle, dm_attrname_t dmname; int ret, lerrno; bool offline; - char *buf; + char *buf = NULL; size_t buflen; /* if the file has more than FILE_IS_ONLINE_RATIO of blocks available, diff --git a/source3/modules/vfs_zfsacl.c b/source3/modules/vfs_zfsacl.c index 3688b2386e..a5b0490c8d 100644 --- a/source3/modules/vfs_zfsacl.c +++ b/source3/modules/vfs_zfsacl.c @@ -212,9 +212,92 @@ static NTSTATUS zfsacl_fset_nt_acl(vfs_handle_struct *handle, return zfs_set_nt_acl(handle, fsp, security_info_sent, psd); } +/* nils.goroll@hamburg.de 2008-06-16 : + + See also + - https://bugzilla.samba.org/show_bug.cgi?id=5446 + - http://bugs.opensolaris.org/view_bug.do?bug_id=6688240 + + Solaris supports NFSv4 and ZFS ACLs through a common system call, acl(2) + with ACE_SETACL / ACE_GETACL / ACE_GETACLCNT, which is being wrapped for + use by samba in this module. + + As the acl(2) interface is identical for ZFS and for NFS, this module, + vfs_zfsacl, can not only be used for ZFS, but also for sharing NFSv4 + mounts on Solaris. + + But while "traditional" POSIX DRAFT ACLs (using acl(2) with SETACL + / GETACL / GETACLCNT) fail for ZFS, the Solaris NFS client + implemets a compatibility wrapper, which will make calls to + traditional ACL calls though vfs_solarisacl succeed. As the + compatibility wrapper's implementation is (by design) incomplete, + we want to make sure that it is never being called. + + As long as Samba does not support an exiplicit method for a module + to define conflicting vfs methods, we should override all conflicting + methods here. + + For this to work, we need to make sure that this module is initialised + *after* vfs_solarisacl + + Function declarations taken from vfs_solarisacl +*/ + +SMB_ACL_T zfsacl_fail__sys_acl_get_file(vfs_handle_struct *handle, + const char *path_p, + SMB_ACL_TYPE_T type) +{ + return (SMB_ACL_T)NULL; +} +SMB_ACL_T zfsacl_fail__sys_acl_get_fd(vfs_handle_struct *handle, + files_struct *fsp, + int fd) +{ + return (SMB_ACL_T)NULL; +} + +int zfsacl_fail__sys_acl_set_file(vfs_handle_struct *handle, + const char *name, + SMB_ACL_TYPE_T type, + SMB_ACL_T theacl) +{ + return -1; +} + +int zfsacl_fail__sys_acl_set_fd(vfs_handle_struct *handle, + files_struct *fsp, + int fd, SMB_ACL_T theacl) +{ + return -1; +} + +int zfsacl_fail__sys_acl_delete_def_file(vfs_handle_struct *handle, + const char *path) +{ + return -1; +} + /* VFS operations structure */ static vfs_op_tuple zfsacl_ops[] = { + /* invalidate conflicting VFS methods */ + {SMB_VFS_OP(zfsacl_fail__sys_acl_get_file), + SMB_VFS_OP_SYS_ACL_GET_FILE, + SMB_VFS_LAYER_OPAQUE}, + {SMB_VFS_OP(zfsacl_fail__sys_acl_get_fd), + SMB_VFS_OP_SYS_ACL_GET_FD, + SMB_VFS_LAYER_OPAQUE}, + {SMB_VFS_OP(zfsacl_fail__sys_acl_set_file), + SMB_VFS_OP_SYS_ACL_SET_FILE, + SMB_VFS_LAYER_OPAQUE}, + {SMB_VFS_OP(zfsacl_fail__sys_acl_set_fd), + SMB_VFS_OP_SYS_ACL_SET_FD, + SMB_VFS_LAYER_OPAQUE}, + {SMB_VFS_OP(zfsacl_fail__sys_acl_delete_def_file), + SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE, + SMB_VFS_LAYER_OPAQUE}, + + /* actual methods */ {SMB_VFS_OP(zfsacl_fget_nt_acl), SMB_VFS_OP_FGET_NT_ACL, SMB_VFS_LAYER_OPAQUE}, {SMB_VFS_OP(zfsacl_get_nt_acl), SMB_VFS_OP_GET_NT_ACL, |