/*
Unix SMB/CIFS implementation.
SMB NT Security Descriptor / Unix permission conversion.
- Copyright (C) Jeremy Allison 1994-2000.
+ Copyright (C) Jeremy Allison 1994-2009.
Copyright (C) Andreas Gruenbacher 2002.
+ Copyright (C) Simo Sorce <idra@samba.org> 2009.
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
*/
#include "includes.h"
+#include "../libcli/security/security.h"
-extern struct current_user current_user;
extern const struct generic_mapping file_generic_mapping;
#undef DBGC_CLASS
struct canon_ace *next, *prev;
SMB_ACL_TAG_T type;
mode_t perms; /* Only use S_I(R|W|X)USR mode bits here. */
- DOM_SID trustee;
+ struct dom_sid trustee;
enum ace_owner owner_type;
enum ace_attribute attr;
posix_id unix_ug;
- bool inherited;
+ uint8_t ace_flags; /* From windows ACE entry. */
} canon_ace;
#define ALL_ACE_PERMS (S_IRUSR|S_IWUSR|S_IXUSR)
/*
* EA format of user.SAMBA_PAI (Samba_Posix_Acl_Interitance)
- * attribute on disk.
+ * attribute on disk - version 1.
+ * All values are little endian.
*
- * | 1 | 1 | 2 | 2 | ....
+ * | 1 | 1 | 2 | 2 | ....
* +------+------+-------------+---------------------+-------------+--------------------+
* | vers | flag | num_entries | num_default_entries | ..entries.. | default_entries... |
* +------+------+-------------+---------------------+-------------+--------------------+
+ *
+ * Entry format is :
+ *
+ * | 1 | 4 |
+ * +------+-------------------+
+ * | value| uid/gid or world |
+ * | type | value |
+ * +------+-------------------+
+ *
+ * Version 2 format. Stores extra Windows metadata about an ACL.
+ *
+ * | 1 | 2 | 2 | 2 | ....
+ * +------+----------+-------------+---------------------+-------------+--------------------+
+ * | vers | ace | num_entries | num_default_entries | ..entries.. | default_entries... |
+ * | 2 | type | | | | |
+ * +------+----------+-------------+---------------------+-------------+--------------------+
+ *
+ * Entry format is :
+ *
+ * | 1 | 1 | 4 |
+ * +------+------+-------------------+
+ * | ace | value| uid/gid or world |
+ * | flag | type | value |
+ * +------+-------------------+------+
+ *
*/
-#define PAI_VERSION_OFFSET 0
-#define PAI_FLAG_OFFSET 1
-#define PAI_NUM_ENTRIES_OFFSET 2
-#define PAI_NUM_DEFAULT_ENTRIES_OFFSET 4
-#define PAI_ENTRIES_BASE 6
+#define PAI_VERSION_OFFSET 0
+
+#define PAI_V1_FLAG_OFFSET 1
+#define PAI_V1_NUM_ENTRIES_OFFSET 2
+#define PAI_V1_NUM_DEFAULT_ENTRIES_OFFSET 4
+#define PAI_V1_ENTRIES_BASE 6
+#define PAI_V1_ACL_FLAG_PROTECTED 0x1
+#define PAI_V1_ENTRY_LENGTH 5
-#define PAI_VERSION 1
-#define PAI_ACL_FLAG_PROTECTED 0x1
-#define PAI_ENTRY_LENGTH 5
+#define PAI_V1_VERSION 1
+
+#define PAI_V2_TYPE_OFFSET 1
+#define PAI_V2_NUM_ENTRIES_OFFSET 3
+#define PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET 5
+#define PAI_V2_ENTRIES_BASE 7
+#define PAI_V2_ENTRY_LENGTH 6
+
+#define PAI_V2_VERSION 2
/*
* In memory format of user.SAMBA_PAI attribute.
struct pai_entry {
struct pai_entry *next, *prev;
+ uint8_t ace_flags;
enum ace_owner owner_type;
posix_id unix_ug;
};
struct pai_val {
- bool pai_protected;
+ uint16_t sd_type;
unsigned int num_entries;
struct pai_entry *entry_list;
unsigned int num_def_entries;
Return a uint32 of the pai_entry principal.
************************************************************************/
-static uint32 get_pai_entry_val(struct pai_entry *paie)
+static uint32_t get_pai_entry_val(struct pai_entry *paie)
{
switch (paie->owner_type) {
case UID_ACE:
DEBUG(10,("get_pai_entry_val: uid = %u\n", (unsigned int)paie->unix_ug.uid ));
- return (uint32)paie->unix_ug.uid;
+ return (uint32_t)paie->unix_ug.uid;
case GID_ACE:
DEBUG(10,("get_pai_entry_val: gid = %u\n", (unsigned int)paie->unix_ug.gid ));
- return (uint32)paie->unix_ug.gid;
+ return (uint32_t)paie->unix_ug.gid;
case WORLD_ACE:
default:
DEBUG(10,("get_pai_entry_val: world ace\n"));
- return (uint32)-1;
+ return (uint32_t)-1;
}
}
Return a uint32 of the entry principal.
************************************************************************/
-static uint32 get_entry_val(canon_ace *ace_entry)
+static uint32_t get_entry_val(canon_ace *ace_entry)
{
switch (ace_entry->owner_type) {
case UID_ACE:
DEBUG(10,("get_entry_val: uid = %u\n", (unsigned int)ace_entry->unix_ug.uid ));
- return (uint32)ace_entry->unix_ug.uid;
+ return (uint32_t)ace_entry->unix_ug.uid;
case GID_ACE:
DEBUG(10,("get_entry_val: gid = %u\n", (unsigned int)ace_entry->unix_ug.gid ));
- return (uint32)ace_entry->unix_ug.gid;
+ return (uint32_t)ace_entry->unix_ug.gid;
case WORLD_ACE:
default:
DEBUG(10,("get_entry_val: world ace\n"));
- return (uint32)-1;
+ return (uint32_t)-1;
}
}
/************************************************************************
- Count the inherited entries.
+ Create the on-disk format (always v2 now). Caller must free.
************************************************************************/
-static unsigned int num_inherited_entries(canon_ace *ace_list)
-{
- unsigned int num_entries = 0;
-
- for (; ace_list; ace_list = ace_list->next)
- if (ace_list->inherited)
- num_entries++;
- return num_entries;
-}
-
-/************************************************************************
- Create the on-disk format. Caller must free.
-************************************************************************/
-
-static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, bool pai_protected, size_t *store_size)
+static char *create_pai_buf_v2(canon_ace *file_ace_list,
+ canon_ace *dir_ace_list,
+ uint16_t sd_type,
+ size_t *store_size)
{
char *pai_buf = NULL;
canon_ace *ace_list = NULL;
char *entry_offset = NULL;
unsigned int num_entries = 0;
unsigned int num_def_entries = 0;
+ unsigned int i;
- for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next)
- if (ace_list->inherited)
- num_entries++;
+ for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) {
+ num_entries++;
+ }
- for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next)
- if (ace_list->inherited)
- num_def_entries++;
+ for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next) {
+ num_def_entries++;
+ }
- DEBUG(10,("create_pai_buf: num_entries = %u, num_def_entries = %u\n", num_entries, num_def_entries ));
+ DEBUG(10,("create_pai_buf_v2: num_entries = %u, num_def_entries = %u\n", num_entries, num_def_entries ));
- *store_size = PAI_ENTRIES_BASE + ((num_entries + num_def_entries)*PAI_ENTRY_LENGTH);
+ *store_size = PAI_V2_ENTRIES_BASE +
+ ((num_entries + num_def_entries)*PAI_V2_ENTRY_LENGTH);
pai_buf = (char *)SMB_MALLOC(*store_size);
if (!pai_buf) {
}
/* Set up the header. */
- memset(pai_buf, '\0', PAI_ENTRIES_BASE);
- SCVAL(pai_buf,PAI_VERSION_OFFSET,PAI_VERSION);
- SCVAL(pai_buf,PAI_FLAG_OFFSET,(pai_protected ? PAI_ACL_FLAG_PROTECTED : 0));
- SSVAL(pai_buf,PAI_NUM_ENTRIES_OFFSET,num_entries);
- SSVAL(pai_buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET,num_def_entries);
+ memset(pai_buf, '\0', PAI_V2_ENTRIES_BASE);
+ SCVAL(pai_buf,PAI_VERSION_OFFSET,PAI_V2_VERSION);
+ SSVAL(pai_buf,PAI_V2_TYPE_OFFSET, sd_type);
+ SSVAL(pai_buf,PAI_V2_NUM_ENTRIES_OFFSET,num_entries);
+ SSVAL(pai_buf,PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET,num_def_entries);
- entry_offset = pai_buf + PAI_ENTRIES_BASE;
+ DEBUG(10,("create_pai_buf_v2: sd_type = 0x%x\n",
+ (unsigned int)sd_type ));
- for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) {
- if (ace_list->inherited) {
- uint8 type_val = (unsigned char)ace_list->owner_type;
- uint32 entry_val = get_entry_val(ace_list);
+ entry_offset = pai_buf + PAI_V2_ENTRIES_BASE;
- SCVAL(entry_offset,0,type_val);
- SIVAL(entry_offset,1,entry_val);
- entry_offset += PAI_ENTRY_LENGTH;
- }
+ i = 0;
+ for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) {
+ uint8_t type_val = (uint8_t)ace_list->owner_type;
+ uint32_t entry_val = get_entry_val(ace_list);
+
+ SCVAL(entry_offset,0,ace_list->ace_flags);
+ SCVAL(entry_offset,1,type_val);
+ SIVAL(entry_offset,2,entry_val);
+ DEBUG(10,("create_pai_buf_v2: entry %u [0x%x] [0x%x] [0x%x]\n",
+ i,
+ (unsigned int)ace_list->ace_flags,
+ (unsigned int)type_val,
+ (unsigned int)entry_val ));
+ i++;
+ entry_offset += PAI_V2_ENTRY_LENGTH;
}
for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next) {
- if (ace_list->inherited) {
- uint8 type_val = (unsigned char)ace_list->owner_type;
- uint32 entry_val = get_entry_val(ace_list);
-
- SCVAL(entry_offset,0,type_val);
- SIVAL(entry_offset,1,entry_val);
- entry_offset += PAI_ENTRY_LENGTH;
- }
+ uint8_t type_val = (uint8_t)ace_list->owner_type;
+ uint32_t entry_val = get_entry_val(ace_list);
+
+ SCVAL(entry_offset,0,ace_list->ace_flags);
+ SCVAL(entry_offset,1,type_val);
+ SIVAL(entry_offset,2,entry_val);
+ DEBUG(10,("create_pai_buf_v2: entry %u [0x%x] [0x%x] [0x%x]\n",
+ i,
+ (unsigned int)ace_list->ace_flags,
+ (unsigned int)type_val,
+ (unsigned int)entry_val ));
+ i++;
+ entry_offset += PAI_V2_ENTRY_LENGTH;
}
return pai_buf;
Store the user.SAMBA_PAI attribute on disk.
************************************************************************/
-static void store_inheritance_attributes(files_struct *fsp, canon_ace *file_ace_list,
- canon_ace *dir_ace_list, bool pai_protected)
+static void store_inheritance_attributes(files_struct *fsp,
+ canon_ace *file_ace_list,
+ canon_ace *dir_ace_list,
+ uint16_t sd_type)
{
int ret;
size_t store_size;
char *pai_buf;
- if (!lp_map_acl_inherit(SNUM(fsp->conn)))
- return;
-
- /*
- * Don't store if this ACL isn't protected and
- * none of the entries in it are marked as inherited.
- */
-
- if (!pai_protected && num_inherited_entries(file_ace_list) == 0 && num_inherited_entries(dir_ace_list) == 0) {
- /* Instead just remove the attribute if it exists. */
- if (fsp->fh->fd != -1)
- SMB_VFS_FREMOVEXATTR(fsp, SAMBA_POSIX_INHERITANCE_EA_NAME);
- else
- SMB_VFS_REMOVEXATTR(fsp->conn, fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME);
+ if (!lp_map_acl_inherit(SNUM(fsp->conn))) {
return;
}
- pai_buf = create_pai_buf(file_ace_list, dir_ace_list, pai_protected, &store_size);
+ pai_buf = create_pai_buf_v2(file_ace_list, dir_ace_list,
+ sd_type, &store_size);
- if (fsp->fh->fd != -1)
+ if (fsp->fh->fd != -1) {
ret = SMB_VFS_FSETXATTR(fsp, SAMBA_POSIX_INHERITANCE_EA_NAME,
pai_buf, store_size, 0);
- else
- ret = SMB_VFS_SETXATTR(fsp->conn,fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME,
- pai_buf, store_size, 0);
+ } else {
+ ret = SMB_VFS_SETXATTR(fsp->conn, fsp->fsp_name->base_name,
+ SAMBA_POSIX_INHERITANCE_EA_NAME,
+ pai_buf, store_size, 0);
+ }
SAFE_FREE(pai_buf);
- DEBUG(10,("store_inheritance_attribute:%s for file %s\n", pai_protected ? " (protected)" : "", fsp->fsp_name));
- if (ret == -1 && !no_acl_syscall_error(errno))
+ DEBUG(10,("store_inheritance_attribute: type 0x%x for file %s\n",
+ (unsigned int)sd_type,
+ fsp_str_dbg(fsp)));
+
+ if (ret == -1 && !no_acl_syscall_error(errno)) {
DEBUG(1,("store_inheritance_attribute: Error %s\n", strerror(errno) ));
+ }
}
/************************************************************************
}
/************************************************************************
- Was this ACL protected ?
+ Get any stored ACE flags.
************************************************************************/
-static bool get_protected_flag(struct pai_val *pal)
-{
- if (!pal)
- return False;
- return pal->pai_protected;
-}
-
-/************************************************************************
- Was this ACE inherited ?
-************************************************************************/
-
-static bool get_inherited_flag(struct pai_val *pal, canon_ace *ace_entry, bool default_ace)
+static uint16_t get_pai_flags(struct pai_val *pal, canon_ace *ace_entry, bool default_ace)
{
struct pai_entry *paie;
- if (!pal)
- return False;
+ if (!pal) {
+ return 0;
+ }
/* If the entry exists it is inherited. */
for (paie = (default_ace ? pal->def_entry_list : pal->entry_list); paie; paie = paie->next) {
if (ace_entry->owner_type == paie->owner_type &&
get_entry_val(ace_entry) == get_pai_entry_val(paie))
- return True;
+ return paie->ace_flags;
}
- return False;
+ return 0;
}
/************************************************************************
- Ensure an attribute just read is valid.
+ Ensure an attribute just read is valid - v1.
************************************************************************/
-static bool check_pai_ok(char *pai_buf, size_t pai_buf_data_size)
+static bool check_pai_ok_v1(const char *pai_buf, size_t pai_buf_data_size)
{
uint16 num_entries;
uint16 num_def_entries;
- if (pai_buf_data_size < PAI_ENTRIES_BASE) {
+ if (pai_buf_data_size < PAI_V1_ENTRIES_BASE) {
/* Corrupted - too small. */
- return False;
+ return false;
}
- if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_VERSION)
- return False;
+ if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_V1_VERSION) {
+ return false;
+ }
- num_entries = SVAL(pai_buf,PAI_NUM_ENTRIES_OFFSET);
- num_def_entries = SVAL(pai_buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET);
+ num_entries = SVAL(pai_buf,PAI_V1_NUM_ENTRIES_OFFSET);
+ num_def_entries = SVAL(pai_buf,PAI_V1_NUM_DEFAULT_ENTRIES_OFFSET);
/* Check the entry lists match. */
/* Each entry is 5 bytes (type plus 4 bytes of uid or gid). */
- if (((num_entries + num_def_entries)*PAI_ENTRY_LENGTH) + PAI_ENTRIES_BASE != pai_buf_data_size)
- return False;
+ if (((num_entries + num_def_entries)*PAI_V1_ENTRY_LENGTH) +
+ PAI_V1_ENTRIES_BASE != pai_buf_data_size) {
+ return false;
+ }
- return True;
+ return true;
}
+/************************************************************************
+ Ensure an attribute just read is valid - v2.
+************************************************************************/
+
+static bool check_pai_ok_v2(const char *pai_buf, size_t pai_buf_data_size)
+{
+ uint16 num_entries;
+ uint16 num_def_entries;
+
+ if (pai_buf_data_size < PAI_V2_ENTRIES_BASE) {
+ /* Corrupted - too small. */
+ return false;
+ }
+
+ if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_V2_VERSION) {
+ return false;
+ }
+
+ num_entries = SVAL(pai_buf,PAI_V2_NUM_ENTRIES_OFFSET);
+ num_def_entries = SVAL(pai_buf,PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET);
+
+ /* Check the entry lists match. */
+ /* Each entry is 6 bytes (flags + type + 4 bytes of uid or gid). */
+
+ if (((num_entries + num_def_entries)*PAI_V2_ENTRY_LENGTH) +
+ PAI_V2_ENTRIES_BASE != pai_buf_data_size) {
+ return false;
+ }
+
+ return true;
+}
/************************************************************************
- Convert to in-memory format.
+ Decode the owner.
************************************************************************/
-static struct pai_val *create_pai_val(char *buf, size_t size)
+static bool get_pai_owner_type(struct pai_entry *paie, const char *entry_offset)
+{
+ paie->owner_type = (enum ace_owner)CVAL(entry_offset,0);
+ switch( paie->owner_type) {
+ case UID_ACE:
+ paie->unix_ug.uid = (uid_t)IVAL(entry_offset,1);
+ DEBUG(10,("get_pai_owner_type: uid = %u\n",
+ (unsigned int)paie->unix_ug.uid ));
+ break;
+ case GID_ACE:
+ paie->unix_ug.gid = (gid_t)IVAL(entry_offset,1);
+ DEBUG(10,("get_pai_owner_type: gid = %u\n",
+ (unsigned int)paie->unix_ug.gid ));
+ break;
+ case WORLD_ACE:
+ paie->unix_ug.world = -1;
+ DEBUG(10,("get_pai_owner_type: world ace\n"));
+ break;
+ default:
+ DEBUG(10,("get_pai_owner_type: unknown type %u\n",
+ (unsigned int)paie->owner_type ));
+ return false;
+ }
+ return true;
+}
+
+/************************************************************************
+ Process v2 entries.
+************************************************************************/
+
+static const char *create_pai_v1_entries(struct pai_val *paiv,
+ const char *entry_offset,
+ bool def_entry)
{
- char *entry_offset;
- struct pai_val *paiv = NULL;
int i;
- if (!check_pai_ok(buf, size))
+ for (i = 0; i < paiv->num_entries; i++) {
+ struct pai_entry *paie = SMB_MALLOC_P(struct pai_entry);
+ if (!paie) {
+ return NULL;
+ }
+
+ paie->ace_flags = SEC_ACE_FLAG_INHERITED_ACE;
+ if (!get_pai_owner_type(paie, entry_offset)) {
+ SAFE_FREE(paie);
+ return NULL;
+ }
+
+ if (!def_entry) {
+ DLIST_ADD(paiv->entry_list, paie);
+ } else {
+ DLIST_ADD(paiv->def_entry_list, paie);
+ }
+ entry_offset += PAI_V1_ENTRY_LENGTH;
+ }
+ return entry_offset;
+}
+
+/************************************************************************
+ Convert to in-memory format from version 1.
+************************************************************************/
+
+static struct pai_val *create_pai_val_v1(const char *buf, size_t size)
+{
+ const char *entry_offset;
+ struct pai_val *paiv = NULL;
+
+ if (!check_pai_ok_v1(buf, size)) {
return NULL;
+ }
paiv = SMB_MALLOC_P(struct pai_val);
- if (!paiv)
+ if (!paiv) {
return NULL;
+ }
memset(paiv, '\0', sizeof(struct pai_val));
- paiv->pai_protected = (CVAL(buf,PAI_FLAG_OFFSET) == PAI_ACL_FLAG_PROTECTED);
+ paiv->sd_type = (CVAL(buf,PAI_V1_FLAG_OFFSET) == PAI_V1_ACL_FLAG_PROTECTED) ?
+ SEC_DESC_DACL_PROTECTED : 0;
- paiv->num_entries = SVAL(buf,PAI_NUM_ENTRIES_OFFSET);
- paiv->num_def_entries = SVAL(buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET);
+ paiv->num_entries = SVAL(buf,PAI_V1_NUM_ENTRIES_OFFSET);
+ paiv->num_def_entries = SVAL(buf,PAI_V1_NUM_DEFAULT_ENTRIES_OFFSET);
- entry_offset = buf + PAI_ENTRIES_BASE;
+ entry_offset = buf + PAI_V1_ENTRIES_BASE;
- DEBUG(10,("create_pai_val:%s num_entries = %u, num_def_entries = %u\n",
- paiv->pai_protected ? " (pai_protected)" : "", paiv->num_entries, paiv->num_def_entries ));
+ DEBUG(10,("create_pai_val: num_entries = %u, num_def_entries = %u\n",
+ paiv->num_entries, paiv->num_def_entries ));
- for (i = 0; i < paiv->num_entries; i++) {
- struct pai_entry *paie;
+ entry_offset = create_pai_v1_entries(paiv, entry_offset, false);
+ if (entry_offset == NULL) {
+ free_inherited_info(paiv);
+ return NULL;
+ }
+ entry_offset = create_pai_v1_entries(paiv, entry_offset, true);
+ if (entry_offset == NULL) {
+ free_inherited_info(paiv);
+ return NULL;
+ }
+
+ return paiv;
+}
- paie = SMB_MALLOC_P(struct pai_entry);
+/************************************************************************
+ Process v2 entries.
+************************************************************************/
+
+static const char *create_pai_v2_entries(struct pai_val *paiv,
+ unsigned int num_entries,
+ const char *entry_offset,
+ bool def_entry)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_entries; i++) {
+ struct pai_entry *paie = SMB_MALLOC_P(struct pai_entry);
if (!paie) {
- free_inherited_info(paiv);
return NULL;
}
- paie->owner_type = (enum ace_owner)CVAL(entry_offset,0);
- switch( paie->owner_type) {
- case UID_ACE:
- paie->unix_ug.uid = (uid_t)IVAL(entry_offset,1);
- DEBUG(10,("create_pai_val: uid = %u\n", (unsigned int)paie->unix_ug.uid ));
- break;
- case GID_ACE:
- paie->unix_ug.gid = (gid_t)IVAL(entry_offset,1);
- DEBUG(10,("create_pai_val: gid = %u\n", (unsigned int)paie->unix_ug.gid ));
- break;
- case WORLD_ACE:
- paie->unix_ug.world = -1;
- DEBUG(10,("create_pai_val: world ace\n"));
- break;
- default:
- free_inherited_info(paiv);
- return NULL;
+ paie->ace_flags = CVAL(entry_offset,0);
+
+ if (!get_pai_owner_type(paie, entry_offset+1)) {
+ SAFE_FREE(paie);
+ return NULL;
+ }
+ if (!def_entry) {
+ DLIST_ADD(paiv->entry_list, paie);
+ } else {
+ DLIST_ADD(paiv->def_entry_list, paie);
}
- entry_offset += PAI_ENTRY_LENGTH;
- DLIST_ADD(paiv->entry_list, paie);
+ entry_offset += PAI_V2_ENTRY_LENGTH;
}
+ return entry_offset;
+}
- for (i = 0; i < paiv->num_def_entries; i++) {
- struct pai_entry *paie;
+/************************************************************************
+ Convert to in-memory format from version 2.
+************************************************************************/
- paie = SMB_MALLOC_P(struct pai_entry);
- if (!paie) {
- free_inherited_info(paiv);
- return NULL;
- }
+static struct pai_val *create_pai_val_v2(const char *buf, size_t size)
+{
+ const char *entry_offset;
+ struct pai_val *paiv = NULL;
- paie->owner_type = (enum ace_owner)CVAL(entry_offset,0);
- switch( paie->owner_type) {
- case UID_ACE:
- paie->unix_ug.uid = (uid_t)IVAL(entry_offset,1);
- DEBUG(10,("create_pai_val: (def) uid = %u\n", (unsigned int)paie->unix_ug.uid ));
- break;
- case GID_ACE:
- paie->unix_ug.gid = (gid_t)IVAL(entry_offset,1);
- DEBUG(10,("create_pai_val: (def) gid = %u\n", (unsigned int)paie->unix_ug.gid ));
- break;
- case WORLD_ACE:
- paie->unix_ug.world = -1;
- DEBUG(10,("create_pai_val: (def) world ace\n"));
- break;
- default:
- free_inherited_info(paiv);
- return NULL;
- }
- entry_offset += PAI_ENTRY_LENGTH;
- DLIST_ADD(paiv->def_entry_list, paie);
+ if (!check_pai_ok_v2(buf, size)) {
+ return NULL;
+ }
+
+ paiv = SMB_MALLOC_P(struct pai_val);
+ if (!paiv) {
+ return NULL;
+ }
+
+ memset(paiv, '\0', sizeof(struct pai_val));
+
+ paiv->sd_type = SVAL(buf,PAI_V2_TYPE_OFFSET);
+
+ paiv->num_entries = SVAL(buf,PAI_V2_NUM_ENTRIES_OFFSET);
+ paiv->num_def_entries = SVAL(buf,PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET);
+
+ entry_offset = buf + PAI_V2_ENTRIES_BASE;
+
+ DEBUG(10,("create_pai_val_v2: sd_type = 0x%x num_entries = %u, num_def_entries = %u\n",
+ (unsigned int)paiv->sd_type,
+ paiv->num_entries, paiv->num_def_entries ));
+
+ entry_offset = create_pai_v2_entries(paiv, paiv->num_entries,
+ entry_offset, false);
+ if (entry_offset == NULL) {
+ free_inherited_info(paiv);
+ return NULL;
+ }
+ entry_offset = create_pai_v2_entries(paiv, paiv->num_def_entries,
+ entry_offset, true);
+ if (entry_offset == NULL) {
+ free_inherited_info(paiv);
+ return NULL;
}
return paiv;
}
+/************************************************************************
+ Convert to in-memory format - from either version 1 or 2.
+************************************************************************/
+
+static struct pai_val *create_pai_val(const char *buf, size_t size)
+{
+ if (size < 1) {
+ return NULL;
+ }
+ if (CVAL(buf,PAI_VERSION_OFFSET) == PAI_V1_VERSION) {
+ return create_pai_val_v1(buf, size);
+ } else if (CVAL(buf,PAI_VERSION_OFFSET) == PAI_V2_VERSION) {
+ return create_pai_val_v2(buf, size);
+ } else {
+ return NULL;
+ }
+}
+
/************************************************************************
Load the user.SAMBA_PAI attribute.
************************************************************************/
struct pai_val *paiv = NULL;
ssize_t ret;
- if (!lp_map_acl_inherit(SNUM(fsp->conn)))
+ if (!lp_map_acl_inherit(SNUM(fsp->conn))) {
return NULL;
+ }
- if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL)
+ if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL) {
return NULL;
+ }
do {
- if (fsp->fh->fd != -1)
+ if (fsp->fh->fd != -1) {
ret = SMB_VFS_FGETXATTR(fsp, SAMBA_POSIX_INHERITANCE_EA_NAME,
pai_buf, pai_buf_size);
- else
- ret = SMB_VFS_GETXATTR(fsp->conn,fsp->fsp_name,SAMBA_POSIX_INHERITANCE_EA_NAME,
- pai_buf, pai_buf_size);
+ } else {
+ ret = SMB_VFS_GETXATTR(fsp->conn,
+ fsp->fsp_name->base_name,
+ SAMBA_POSIX_INHERITANCE_EA_NAME,
+ pai_buf, pai_buf_size);
+ }
if (ret == -1) {
if (errno != ERANGE) {
}
} while (ret == -1);
- DEBUG(10,("load_inherited_info: ret = %lu for file %s\n", (unsigned long)ret, fsp->fsp_name));
+ DEBUG(10,("load_inherited_info: ret = %lu for file %s\n",
+ (unsigned long)ret, fsp_str_dbg(fsp)));
if (ret == -1) {
/* No attribute or not supported. */
paiv = create_pai_val(pai_buf, ret);
- if (paiv && paiv->pai_protected)
- DEBUG(10,("load_inherited_info: ACL is protected for file %s\n", fsp->fsp_name));
+ if (paiv) {
+ DEBUG(10,("load_inherited_info: ACL type is 0x%x for file %s\n",
+ (unsigned int)paiv->sd_type, fsp_str_dbg(fsp)));
+ }
SAFE_FREE(pai_buf);
return paiv;
paiv = create_pai_val(pai_buf, ret);
- if (paiv && paiv->pai_protected) {
- DEBUG(10,("load_inherited_info: ACL is protected for file %s\n", fname));
+ if (paiv) {
+ DEBUG(10,("load_inherited_info: ACL type 0x%x for file %s\n",
+ (unsigned int)paiv->sd_type,
+ fname));
}
SAFE_FREE(pai_buf);
Count a linked list of canonical ACE entries.
****************************************************************************/
-static size_t count_canon_ace_list( canon_ace *list_head )
+static size_t count_canon_ace_list( canon_ace *l_head )
{
size_t count = 0;
canon_ace *ace;
- for (ace = list_head; ace; ace = ace->next)
+ for (ace = l_head; ace; ace = ace->next)
count++;
return count;
Free a linked list of canonical ACE entries.
****************************************************************************/
-static void free_canon_ace_list( canon_ace *list_head )
+static void free_canon_ace_list( canon_ace *l_head )
{
canon_ace *list, *next;
- for (list = list_head; list; list = next) {
+ for (list = l_head; list; list = next) {
next = list->next;
- DLIST_REMOVE(list_head, list);
+ DLIST_REMOVE(l_head, list);
SAFE_FREE(list);
}
}
dbgtext( "MASK " );
break;
}
- if (pace->inherited)
- dbgtext( "(inherited) ");
+
+ dbgtext( "ace_flags = 0x%x ", (unsigned int)pace->ace_flags);
dbgtext( "perms ");
dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
Function to create owner and group SIDs from a SMB_STRUCT_STAT.
****************************************************************************/
-void create_file_sids(const SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
+void create_file_sids(const SMB_STRUCT_STAT *psbuf, struct dom_sid *powner_sid, struct dom_sid *pgroup_sid)
{
- uid_to_sid( powner_sid, psbuf->st_uid );
- gid_to_sid( pgroup_sid, psbuf->st_gid );
-}
-
-/****************************************************************************
- Is the identity in two ACEs equal ? Check both SID and uid/gid.
-****************************************************************************/
-
-static bool identity_in_ace_equal(canon_ace *ace1, canon_ace *ace2)
-{
- if (sid_equal(&ace1->trustee, &ace2->trustee)) {
- return True;
- }
- if (ace1->owner_type == ace2->owner_type) {
- if (ace1->owner_type == UID_ACE &&
- ace1->unix_ug.uid == ace2->unix_ug.uid) {
- return True;
- } else if (ace1->owner_type == GID_ACE &&
- ace1->unix_ug.gid == ace2->unix_ug.gid) {
- return True;
- }
- }
- return False;
+ uid_to_sid( powner_sid, psbuf->st_ex_uid );
+ gid_to_sid( pgroup_sid, psbuf->st_ex_gid );
}
/****************************************************************************
if the permissions become zero, delete the deny if the permissions are non zero.
****************************************************************************/
-static void merge_aces( canon_ace **pp_list_head )
+static void merge_aces( canon_ace **pp_list_head, bool dir_acl)
{
- canon_ace *list_head = *pp_list_head;
+ canon_ace *l_head = *pp_list_head;
canon_ace *curr_ace_outer;
canon_ace *curr_ace_outer_next;
* with identical SIDs.
*/
- for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
+ for (curr_ace_outer = l_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
canon_ace *curr_ace;
canon_ace *curr_ace_next;
curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
+ bool can_merge = false;
curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
- if (identity_in_ace_equal(curr_ace, curr_ace_outer) &&
- (curr_ace->attr == curr_ace_outer->attr)) {
+ /* For file ACLs we can merge if the SIDs and ALLOW/DENY
+ * types are the same. For directory acls we must also
+ * ensure the POSIX ACL types are the same. */
+ if (!dir_acl) {
+ can_merge = (dom_sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
+ (curr_ace->attr == curr_ace_outer->attr));
+ } else {
+ can_merge = (dom_sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
+ (curr_ace->type == curr_ace_outer->type) &&
+ (curr_ace->attr == curr_ace_outer->attr));
+ }
+
+ if (can_merge) {
if( DEBUGLVL( 10 )) {
dbgtext("merge_aces: Merging ACE's\n");
print_canon_ace( curr_ace_outer, 0);
/* Merge two allow or two deny ACE's. */
+ /* Theoretically we shouldn't merge a dir ACE if
+ * one ACE has the CI flag set, and the other
+ * ACE has the OI flag set, but this is rare
+ * enough we can ignore it. */
+
curr_ace_outer->perms |= curr_ace->perms;
- DLIST_REMOVE(list_head, curr_ace);
+ curr_ace_outer->ace_flags |= curr_ace->ace_flags;
+ DLIST_REMOVE(l_head, curr_ace);
SAFE_FREE(curr_ace);
curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
}
* appears only once in the list.
*/
- for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
+ for (curr_ace_outer = l_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
canon_ace *curr_ace;
canon_ace *curr_ace_next;
* we've put on the ACL, we know the deny must be the first one.
*/
- if (identity_in_ace_equal(curr_ace, curr_ace_outer) &&
+ if (dom_sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
(curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
if( DEBUGLVL( 10 )) {
* The deny overrides the allow. Remove the allow.
*/
- DLIST_REMOVE(list_head, curr_ace);
+ DLIST_REMOVE(l_head, curr_ace);
SAFE_FREE(curr_ace);
curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
* before we can get to an allow ace.
*/
- DLIST_REMOVE(list_head, curr_ace_outer);
+ DLIST_REMOVE(l_head, curr_ace_outer);
SAFE_FREE(curr_ace_outer);
break;
}
/* We may have modified the list. */
- *pp_list_head = list_head;
+ *pp_list_head = l_head;
}
/****************************************************************************
not get. Deny entries are implicit on get with ace->perms = 0.
****************************************************************************/
-static uint32_t map_canon_ace_perms(int snum,
+uint32_t map_canon_ace_perms(int snum,
enum security_ace_type *pacl_type,
mode_t perms,
bool directory_ace)
}
}
+ if ((perms & S_IWUSR) && lp_dos_filemode(snum)) {
+ nt_mask |= (SEC_STD_WRITE_DAC|SEC_STD_WRITE_OWNER|DELETE_ACCESS);
+ }
+
DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
(unsigned int)perms, (unsigned int)nt_mask ));
}
/****************************************************************************
- Unpack a SEC_DESC into a UNIX owner and group.
+ Unpack a struct security_descriptor into a UNIX owner and group.
****************************************************************************/
-NTSTATUS unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, const SEC_DESC *psd)
+NTSTATUS unpack_nt_owners(struct connection_struct *conn,
+ uid_t *puser, gid_t *pgrp,
+ uint32 security_info_sent, const struct
+ security_descriptor *psd)
{
- DOM_SID owner_sid;
- DOM_SID grp_sid;
+ struct dom_sid owner_sid;
+ struct dom_sid grp_sid;
*puser = (uid_t)-1;
*pgrp = (gid_t)-1;
* This may be a group chown only set.
*/
- if (security_info_sent & OWNER_SECURITY_INFORMATION) {
+ if (security_info_sent & SECINFO_OWNER) {
sid_copy(&owner_sid, psd->owner_sid);
if (!sid_to_uid(&owner_sid, puser)) {
- if (lp_force_unknown_acl_user(snum)) {
+ if (lp_force_unknown_acl_user(SNUM(conn))) {
/* this allows take ownership to work
* reasonably */
- *puser = current_user.ut.uid;
+ *puser = get_current_uid(conn);
} else {
DEBUG(3,("unpack_nt_owners: unable to validate"
" owner sid for %s\n",
* This may be an owner chown only set.
*/
- if (security_info_sent & GROUP_SECURITY_INFORMATION) {
+ if (security_info_sent & SECINFO_GROUP) {
sid_copy(&grp_sid, psd->group_sid);
if (!sid_to_gid( &grp_sid, pgrp)) {
- if (lp_force_unknown_acl_user(snum)) {
+ if (lp_force_unknown_acl_user(SNUM(conn))) {
/* this allows take group ownership to work
* reasonably */
- *pgrp = current_user.ut.gid;
+ *pgrp = get_current_gid(conn);
} else {
DEBUG(3,("unpack_nt_owners: unable to validate"
" group sid.\n"));
expensive and will need optimisation. A *lot* of optimisation :-). JRA.
****************************************************************************/
-static bool uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
+static bool uid_entry_in_group(connection_struct *conn, canon_ace *uid_ace, canon_ace *group_ace )
{
const char *u_name = NULL;
/* "Everyone" always matches every uid. */
- if (sid_equal(&group_ace->trustee, &global_sid_World))
+ if (dom_sid_equal(&group_ace->trustee, &global_sid_World))
return True;
- /* Assume that the current user is in the current group (force group) */
+ /*
+ * if it's the current user, we already have the unix token
+ * and don't need to do the complex user_in_group_sid() call
+ */
+ if (uid_ace->unix_ug.uid == get_current_uid(conn)) {
+ const struct security_unix_token *curr_utok = NULL;
+ size_t i;
- if (uid_ace->unix_ug.uid == current_user.ut.uid && group_ace->unix_ug.gid == current_user.ut.gid)
- return True;
+ if (group_ace->unix_ug.gid == get_current_gid(conn)) {
+ return True;
+ }
+
+ curr_utok = get_current_utok(conn);
+ for (i=0; i < curr_utok->ngroups; i++) {
+ if (group_ace->unix_ug.gid == curr_utok->groups[i]) {
+ return True;
+ }
+ }
+ }
/* u_name talloc'ed off tos. */
u_name = uidtoname(uid_ace->unix_ug.uid);
if (!u_name) {
return False;
}
+
+ /*
+ * user_in_group_sid() uses create_token_from_username()
+ * which creates an artificial NT token given just a username,
+ * so this is not reliable for users from foreign domains
+ * exported by winbindd!
+ */
return user_in_group_sid(u_name, &group_ace->trustee);
}
type.
****************************************************************************/
-static bool ensure_canon_entry_valid(canon_ace **pp_ace,
+static bool ensure_canon_entry_valid(connection_struct *conn, canon_ace **pp_ace,
const struct share_params *params,
const bool is_directory,
- const DOM_SID *pfile_owner_sid,
- const DOM_SID *pfile_grp_sid,
+ const struct dom_sid *pfile_owner_sid,
+ const struct dom_sid *pfile_grp_sid,
const SMB_STRUCT_STAT *pst,
bool setting_acl)
{
ZERO_STRUCTP(pace);
pace->type = SMB_ACL_USER_OBJ;
pace->owner_type = UID_ACE;
- pace->unix_ug.uid = pst->st_uid;
+ pace->unix_ug.uid = pst->st_ex_uid;
pace->trustee = *pfile_owner_sid;
pace->attr = ALLOW_ACE;
for (pace_iter = *pp_ace; pace_iter; pace_iter = pace_iter->next) {
if (pace_iter->type == SMB_ACL_GROUP_OBJ || pace_iter->type == SMB_ACL_GROUP) {
- if (uid_entry_in_group(pace, pace_iter)) {
+ if (uid_entry_in_group(conn, pace, pace_iter)) {
pace->perms |= pace_iter->perms;
group_matched = True;
}
apply_default_perms(params, is_directory, pace, S_IRUSR);
} else {
- pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
+ pace->perms = unix_perms_to_acl_perms(pst->st_ex_mode, S_IRUSR, S_IWUSR, S_IXUSR);
}
DLIST_ADD(*pp_ace, pace);
ZERO_STRUCTP(pace);
pace->type = SMB_ACL_GROUP_OBJ;
pace->owner_type = GID_ACE;
- pace->unix_ug.uid = pst->st_gid;
+ pace->unix_ug.uid = pst->st_ex_gid;
pace->trustee = *pfile_grp_sid;
pace->attr = ALLOW_ACE;
if (setting_acl) {
pace->perms = 0;
apply_default_perms(params, is_directory, pace, S_IRGRP);
} else {
- pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
+ pace->perms = unix_perms_to_acl_perms(pst->st_ex_mode, S_IRGRP, S_IWGRP, S_IXGRP);
}
DLIST_ADD(*pp_ace, pace);
pace->perms = 0;
apply_default_perms(params, is_directory, pace, S_IROTH);
} else
- pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
+ pace->perms = unix_perms_to_acl_perms(pst->st_ex_mode, S_IROTH, S_IWOTH, S_IXOTH);
DLIST_ADD(*pp_ace, pace);
}
file owner or the owning group, and map these to SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ.
****************************************************************************/
-static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID *pfile_grp_sid)
+static void check_owning_objs(canon_ace *ace, struct dom_sid *pfile_owner_sid, struct dom_sid *pfile_grp_sid)
{
bool got_user_obj, got_group_obj;
canon_ace *current_ace;
for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
if (!got_user_obj && current_ace->owner_type == UID_ACE &&
- sid_equal(¤t_ace->trustee, pfile_owner_sid)) {
+ dom_sid_equal(¤t_ace->trustee, pfile_owner_sid)) {
current_ace->type = SMB_ACL_USER_OBJ;
got_user_obj = True;
}
if (!got_group_obj && current_ace->owner_type == GID_ACE &&
- sid_equal(¤t_ace->trustee, pfile_grp_sid)) {
+ dom_sid_equal(¤t_ace->trustee, pfile_grp_sid)) {
current_ace->type = SMB_ACL_GROUP_OBJ;
got_group_obj = True;
}
}
/****************************************************************************
- Unpack a SEC_DESC into two canonical ace lists.
+ If an ACE entry is SMB_ACL_USER_OBJ and not CREATOR_OWNER, map to SMB_ACL_USER.
+ If an ACE entry is SMB_ACL_GROUP_OBJ and not CREATOR_GROUP, map to SMB_ACL_GROUP
+****************************************************************************/
+
+static bool dup_owning_ace(canon_ace *dir_ace, canon_ace *ace)
+{
+ /* dir ace must be followings.
+ SMB_ACL_USER_OBJ : trustee(CREATOR_OWNER) -> Posix ACL d:u::perm
+ SMB_ACL_USER : not trustee -> Posix ACL u:user:perm
+ SMB_ACL_USER_OBJ : trustee -> convert to SMB_ACL_USER : trustee
+ Posix ACL u:trustee:perm
+
+ SMB_ACL_GROUP_OBJ: trustee(CREATOR_GROUP) -> Posix ACL d:g::perm
+ SMB_ACL_GROUP : not trustee -> Posix ACL g:group:perm
+ SMB_ACL_GROUP_OBJ: trustee -> convert to SMB_ACL_GROUP : trustee
+ Posix ACL g:trustee:perm
+ */
+
+ if (ace->type == SMB_ACL_USER_OBJ &&
+ !(dom_sid_equal(&ace->trustee, &global_sid_Creator_Owner))) {
+ canon_ace *dup_ace = dup_canon_ace(ace);
+
+ if (dup_ace == NULL) {
+ return false;
+ }
+ dup_ace->type = SMB_ACL_USER;
+ DLIST_ADD_END(dir_ace, dup_ace, canon_ace *);
+ }
+
+ if (ace->type == SMB_ACL_GROUP_OBJ &&
+ !(dom_sid_equal(&ace->trustee, &global_sid_Creator_Group))) {
+ canon_ace *dup_ace = dup_canon_ace(ace);
+
+ if (dup_ace == NULL) {
+ return false;
+ }
+ dup_ace->type = SMB_ACL_GROUP;
+ DLIST_ADD_END(dir_ace, dup_ace, canon_ace *);
+ }
+
+ return true;
+}
+
+/****************************************************************************
+ Unpack a struct security_descriptor into two canonical ace lists.
****************************************************************************/
static bool create_canon_ace_lists(files_struct *fsp,
- SMB_STRUCT_STAT *pst,
- DOM_SID *pfile_owner_sid,
- DOM_SID *pfile_grp_sid,
+ const SMB_STRUCT_STAT *pst,
+ struct dom_sid *pfile_owner_sid,
+ struct dom_sid *pfile_grp_sid,
canon_ace **ppfile_ace,
canon_ace **ppdir_ace,
- const SEC_ACL *dacl)
+ const struct security_acl *dacl)
{
bool all_aces_are_inherit_only = (fsp->is_directory ? True : False);
canon_ace *file_ace = NULL;
*/
for(i = 0; i < dacl->num_aces; i++) {
- SEC_ACE *psa = &dacl->aces[i];
+ struct security_ace *psa = &dacl->aces[i];
if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
*/
for(i = 0; i < dacl->num_aces; i++) {
- SEC_ACE *psa1 = &dacl->aces[i];
+ struct security_ace *psa1 = &dacl->aces[i];
for (j = i + 1; j < dacl->num_aces; j++) {
- SEC_ACE *psa2 = &dacl->aces[j];
+ struct security_ace *psa2 = &dacl->aces[j];
if (psa1->access_mask != psa2->access_mask)
continue;
- if (!sid_equal(&psa1->trustee, &psa2->trustee))
+ if (!dom_sid_equal(&psa1->trustee, &psa2->trustee))
continue;
/*
}
for(i = 0; i < dacl->num_aces; i++) {
- SEC_ACE *psa = &dacl->aces[i];
+ struct security_ace *psa = &dacl->aces[i];
/*
- * Create a cannon_ace entry representing this NT DACL ACE.
+ * Create a canon_ace entry representing this NT DACL ACE.
*/
if ((current_ace = SMB_MALLOC_P(canon_ace)) == NULL) {
* Note what kind of a POSIX ACL this should map to.
*/
- if( sid_equal(¤t_ace->trustee, &global_sid_World)) {
+ if( dom_sid_equal(¤t_ace->trustee, &global_sid_World)) {
current_ace->owner_type = WORLD_ACE;
current_ace->unix_ug.world = -1;
current_ace->type = SMB_ACL_OTHER;
- } else if (sid_equal(¤t_ace->trustee, &global_sid_Creator_Owner)) {
+ } else if (dom_sid_equal(¤t_ace->trustee, &global_sid_Creator_Owner)) {
current_ace->owner_type = UID_ACE;
- current_ace->unix_ug.uid = pst->st_uid;
+ current_ace->unix_ug.uid = pst->st_ex_uid;
current_ace->type = SMB_ACL_USER_OBJ;
/*
* The Creator Owner entry only specifies inheritable permissions,
* never access permissions. WinNT doesn't always set the ACE to
- *INHERIT_ONLY, though.
+ * INHERIT_ONLY, though.
*/
- if (nt4_compatible_acls())
- psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
- } else if (sid_equal(¤t_ace->trustee, &global_sid_Creator_Group)) {
+ psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+
+ } else if (dom_sid_equal(¤t_ace->trustee, &global_sid_Creator_Group)) {
current_ace->owner_type = GID_ACE;
- current_ace->unix_ug.gid = pst->st_gid;
+ current_ace->unix_ug.gid = pst->st_ex_gid;
current_ace->type = SMB_ACL_GROUP_OBJ;
/*
* The Creator Group entry only specifies inheritable permissions,
* never access permissions. WinNT doesn't always set the ACE to
- *INHERIT_ONLY, though.
+ * INHERIT_ONLY, though.
*/
- if (nt4_compatible_acls())
- psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+ psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
} else if (sid_to_uid( ¤t_ace->trustee, ¤t_ace->unix_ug.uid)) {
current_ace->owner_type = UID_ACE;
/* If it's the owning user, this is a user_obj, not
* a user. */
- if (current_ace->unix_ug.uid == pst->st_uid) {
+ if (current_ace->unix_ug.uid == pst->st_ex_uid) {
current_ace->type = SMB_ACL_USER_OBJ;
} else {
current_ace->type = SMB_ACL_USER;
current_ace->owner_type = GID_ACE;
/* If it's the primary group, this is a group_obj, not
* a group. */
- if (current_ace->unix_ug.gid == pst->st_gid) {
+ if (current_ace->unix_ug.gid == pst->st_ex_gid) {
current_ace->type = SMB_ACL_GROUP_OBJ;
} else {
current_ace->type = SMB_ACL_GROUP;
continue;
}
+ if (lp_force_unknown_acl_user(SNUM(fsp->conn))) {
+ DEBUG(10, ("create_canon_ace_lists: ignoring "
+ "unknown or foreign SID %s\n",
+ sid_string_dbg(&psa->trustee)));
+ SAFE_FREE(current_ace);
+ continue;
+ }
+
free_canon_ace_list(file_ace);
free_canon_ace_list(dir_ace);
DEBUG(0, ("create_canon_ace_lists: unable to map SID "
current_ace->perms |= map_nt_perms( &psa->access_mask, S_IRUSR);
current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
- current_ace->inherited = ((psa->flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False);
+
+ /* Store the ace_flag. */
+ current_ace->ace_flags = psa->flags;
/*
* Now add the created ace to either the file list, the directory
got_dir_allow = True;
if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
- DEBUG(0,("create_canon_ace_lists: malformed ACL in inheritable ACL ! \
-Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
+ DEBUG(0,("create_canon_ace_lists: "
+ "malformed ACL in "
+ "inheritable ACL! Deny entry "
+ "after Allow entry. Failing "
+ "to set on file %s.\n",
+ fsp_str_dbg(fsp)));
free_canon_ace_list(file_ace);
free_canon_ace_list(dir_ace);
return False;
print_canon_ace( current_ace, 0);
}
+ /*
+ * We have a lossy mapping: directory ACE entries
+ * CREATOR_OWNER ------\
+ * (map to) +---> SMB_ACL_USER_OBJ
+ * owning sid ------/
+ *
+ * CREATOR_GROUP ------\
+ * (map to) +---> SMB_ACL_GROUP_OBJ
+ * primary group sid --/
+ *
+ * on set. And on read of a directory ACL
+ *
+ * SMB_ACL_USER_OBJ ----> CREATOR_OWNER
+ * SMB_ACL_GROUP_OBJ ---> CREATOR_GROUP.
+ *
+ * Deal with this on set by duplicating
+ * owning sid and primary group sid ACE
+ * entries into the directory ACL.
+ * Fix from Tsukasa Hamano <hamano@osstech.co.jp>.
+ */
+
+ if (!dup_owning_ace(dir_ace, current_ace)) {
+ DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
+ free_canon_ace_list(file_ace);
+ free_canon_ace_list(dir_ace);
+ return false;
+ }
+
/*
* If this is not an inherit only ACE we need to add a duplicate
* to the file acl.
* pointer is now owned by the dir_ace list.
*/
current_ace = dup_ace;
+ /* We've essentially split this ace into two,
+ * and added the ace with inheritance request
+ * bits to the directory ACL. Drop those bits for
+ * the ACE we're adding to the file list. */
+ current_ace->ace_flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|
+ SEC_ACE_FLAG_CONTAINER_INHERIT|
+ SEC_ACE_FLAG_INHERIT_ONLY);
} else {
/*
* We must not free current_ace here as its
got_file_allow = True;
if ((current_ace->attr == DENY_ACE) && got_file_allow) {
- DEBUG(0,("create_canon_ace_lists: malformed ACL in file ACL ! \
-Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
+ DEBUG(0,("create_canon_ace_lists: malformed "
+ "ACL in file ACL ! Deny entry after "
+ "Allow entry. Failing to set on file "
+ "%s.\n", fsp_str_dbg(fsp)));
free_canon_ace_list(file_ace);
free_canon_ace_list(dir_ace);
return False;
allow entries.
****************************************************************************/
-static void process_deny_list( canon_ace **pp_ace_list )
+static void process_deny_list(connection_struct *conn, canon_ace **pp_ace_list )
{
canon_ace *ace_list = *pp_ace_list;
canon_ace *curr_ace = NULL;
continue;
}
- if (!sid_equal(&curr_ace->trustee, &global_sid_World))
+ if (!dom_sid_equal(&curr_ace->trustee, &global_sid_World))
continue;
/* JRATEST - assert. */
* list at this point including this entry.
*/
- canon_ace *prev_entry = curr_ace->prev;
+ canon_ace *prev_entry = DLIST_PREV(curr_ace);
free_canon_ace_list( curr_ace );
if (prev_entry)
- prev_entry->next = NULL;
+ DLIST_REMOVE(ace_list, prev_entry);
else {
/* We deleted the entire list. */
ace_list = NULL;
if (allow_ace_p->owner_type == UID_ACE)
continue;
- if (uid_entry_in_group( curr_ace, allow_ace_p))
+ if (uid_entry_in_group(conn, curr_ace, allow_ace_p))
new_perms |= allow_ace_p->perms;
}
/* Mask off the deny group perms. */
- if (uid_entry_in_group( allow_ace_p, curr_ace))
+ if (uid_entry_in_group(conn, allow_ace_p, curr_ace))
allow_ace_p->perms &= ~curr_ace->perms;
}
/* OR in the group perms. */
- if (uid_entry_in_group( curr_ace, allow_ace_p))
+ if (uid_entry_in_group(conn, curr_ace, allow_ace_p))
curr_ace->perms |= allow_ace_p->perms;
}
}
int snum = SNUM(fsp->conn);
mode_t and_bits = (mode_t)0;
mode_t or_bits = (mode_t)0;
- mode_t mode = interitable_mode
- ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name,
- NULL )
- : S_IRUSR;
+ mode_t mode;
+
+ if (interitable_mode) {
+ mode = unix_mode(fsp->conn, FILE_ATTRIBUTE_ARCHIVE,
+ fsp->fsp_name, NULL);
+ } else {
+ mode = S_IRUSR;
+ }
if (fsp->is_directory)
mode |= (S_IWUSR|S_IXUSR);
}
/****************************************************************************
- Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
+ Unpack a struct security_descriptor into two canonical ace lists. We don't depend on this
succeeding.
****************************************************************************/
static bool unpack_canon_ace(files_struct *fsp,
- SMB_STRUCT_STAT *pst,
- DOM_SID *pfile_owner_sid,
- DOM_SID *pfile_grp_sid,
+ const SMB_STRUCT_STAT *pst,
+ struct dom_sid *pfile_owner_sid,
+ struct dom_sid *pfile_grp_sid,
canon_ace **ppfile_ace,
canon_ace **ppdir_ace,
uint32 security_info_sent,
- const SEC_DESC *psd)
+ const struct security_descriptor *psd)
{
+ SMB_STRUCT_STAT st;
canon_ace *file_ace = NULL;
canon_ace *dir_ace = NULL;
* If no DACL then this is a chown only security descriptor.
*/
- if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
+ if(!(security_info_sent & SECINFO_DACL) || !psd->dacl)
return True;
/*
*/
print_canon_ace_list( "file ace - before merge", file_ace);
- merge_aces( &file_ace );
+ merge_aces( &file_ace, false);
print_canon_ace_list( "dir ace - before merge", dir_ace);
- merge_aces( &dir_ace );
+ merge_aces( &dir_ace, true);
/*
* NT ACLs are order dependent. Go through the acl lists and
*/
print_canon_ace_list( "file ace - before deny", file_ace);
- process_deny_list( &file_ace);
+ process_deny_list(fsp->conn, &file_ace);
print_canon_ace_list( "dir ace - before deny", dir_ace);
- process_deny_list( &dir_ace);
+ process_deny_list(fsp->conn, &dir_ace);
/*
* A well formed POSIX file or default ACL has at least 3 entries, a
print_canon_ace_list( "file ace - before valid", file_ace);
+ st = *pst;
+
/*
* A default 3 element mode entry for a file should be r-- --- ---.
* A default 3 element mode entry for a directory should be rwx --- ---.
*/
- pst->st_mode = create_default_mode(fsp, False);
+ st.st_ex_mode = create_default_mode(fsp, False);
- if (!ensure_canon_entry_valid(&file_ace, fsp->conn->params, fsp->is_directory, pfile_owner_sid, pfile_grp_sid, pst, True)) {
+ if (!ensure_canon_entry_valid(fsp->conn, &file_ace, fsp->conn->params,
+ fsp->is_directory, pfile_owner_sid, pfile_grp_sid, &st, True)) {
free_canon_ace_list(file_ace);
free_canon_ace_list(dir_ace);
return False;
* it's a directory.
*/
- pst->st_mode = create_default_mode(fsp, True);
+ st.st_ex_mode = create_default_mode(fsp, True);
- if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp->conn->params, fsp->is_directory, pfile_owner_sid, pfile_grp_sid, pst, True)) {
+ if (dir_ace && !ensure_canon_entry_valid(fsp->conn, &dir_ace, fsp->conn->params,
+ fsp->is_directory, pfile_owner_sid, pfile_grp_sid, &st, True)) {
free_canon_ace_list(file_ace);
free_canon_ace_list(dir_ace);
return False;
static void arrange_posix_perms(const char *filename, canon_ace **pp_list_head)
{
- canon_ace *list_head = *pp_list_head;
+ canon_ace *l_head = *pp_list_head;
canon_ace *owner_ace = NULL;
canon_ace *other_ace = NULL;
canon_ace *ace = NULL;
- for (ace = list_head; ace; ace = ace->next) {
+ for (ace = l_head; ace; ace = ace->next) {
if (ace->type == SMB_ACL_USER_OBJ)
owner_ace = ace;
else if (ace->type == SMB_ACL_OTHER) {
other_ace = ace;
}
}
-
+
if (!owner_ace || !other_ace) {
DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
filename ));
*/
if (owner_ace) {
- DLIST_PROMOTE(list_head, owner_ace);
+ DLIST_PROMOTE(l_head, owner_ace);
}
if (other_ace) {
- DLIST_DEMOTE(list_head, other_ace, canon_ace *);
+ DLIST_DEMOTE(l_head, other_ace, canon_ace *);
}
/* We have probably changed the head of the list. */
- *pp_list_head = list_head;
+ *pp_list_head = l_head;
}
-
+
/****************************************************************************
Create a linked list of canonical ACE entries.
****************************************************************************/
static canon_ace *canonicalise_acl(struct connection_struct *conn,
const char *fname, SMB_ACL_T posix_acl,
const SMB_STRUCT_STAT *psbuf,
- const DOM_SID *powner, const DOM_SID *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type)
+ const struct dom_sid *powner, const struct dom_sid *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type)
{
mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
- canon_ace *list_head = NULL;
+ canon_ace *l_head = NULL;
canon_ace *ace = NULL;
canon_ace *next_ace = NULL;
int entry_id = SMB_ACL_FIRST_ENTRY;
while ( posix_acl && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1)) {
SMB_ACL_TAG_T tagtype;
SMB_ACL_PERMSET_T permset;
- DOM_SID sid;
+ struct dom_sid sid;
posix_id unix_ug;
enum ace_owner owner_type;
case SMB_ACL_USER_OBJ:
/* Get the SID from the owner. */
sid_copy(&sid, powner);
- unix_ug.uid = psbuf->st_uid;
+ unix_ug.uid = psbuf->st_ex_uid;
owner_type = UID_ACE;
break;
case SMB_ACL_USER:
DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
continue;
}
- /*
- * A SMB_ACL_USER entry for the owner is shadowed by the
- * SMB_ACL_USER_OBJ entry and Windows also cannot represent
- * that entry, so we ignore it. We also don't create such
- * entries out of the blue when setting ACLs, so a get/set
- * cycle will drop them.
- */
- if (the_acl_type == SMB_ACL_TYPE_ACCESS && *puid == psbuf->st_uid) {
- SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
- continue;
- }
uid_to_sid( &sid, *puid);
unix_ug.uid = *puid;
owner_type = UID_ACE;
case SMB_ACL_GROUP_OBJ:
/* Get the SID from the owning group. */
sid_copy(&sid, pgroup);
- unix_ug.gid = psbuf->st_gid;
+ unix_ug.gid = psbuf->st_ex_gid;
owner_type = GID_ACE;
break;
case SMB_ACL_GROUP:
ace->trustee = sid;
ace->unix_ug = unix_ug;
ace->owner_type = owner_type;
- ace->inherited = get_inherited_flag(pal, ace, (the_acl_type == SMB_ACL_TYPE_DEFAULT));
+ ace->ace_flags = get_pai_flags(pal, ace, (the_acl_type == SMB_ACL_TYPE_DEFAULT));
- DLIST_ADD(list_head, ace);
+ DLIST_ADD(l_head, ace);
}
/*
* This next call will ensure we have at least a user/group/world set.
*/
- if (!ensure_canon_entry_valid(&list_head, conn->params,
- S_ISDIR(psbuf->st_mode), powner, pgroup,
+ if (!ensure_canon_entry_valid(conn, &l_head, conn->params,
+ S_ISDIR(psbuf->st_ex_mode), powner, pgroup,
psbuf, False))
goto fail;
DEBUG(10,("canonicalise_acl: %s ace entries before arrange :\n", the_acl_type == SMB_ACL_TYPE_ACCESS ? "Access" : "Default" ));
- for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
+ for ( ace_count = 0, ace = l_head; ace; ace = next_ace, ace_count++) {
next_ace = ace->next;
/* Masks are only applied to entries other than USER_OBJ and OTHER. */
ace->perms &= acl_mask;
if (ace->perms == 0) {
- DLIST_PROMOTE(list_head, ace);
+ DLIST_PROMOTE(l_head, ace);
}
if( DEBUGLVL( 10 ) ) {
}
}
- arrange_posix_perms(fname,&list_head );
+ arrange_posix_perms(fname,&l_head );
- print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head );
+ print_canon_ace_list( "canonicalise_acl: ace entries after arrange", l_head );
- return list_head;
+ return l_head;
fail:
- free_canon_ace_list(list_head);
+ free_canon_ace_list(l_head);
return NULL;
}
Check if the current user group list contains a given group.
****************************************************************************/
-static bool current_user_in_group(gid_t gid)
+static bool current_user_in_group(connection_struct *conn, gid_t gid)
{
int i;
+ const struct security_unix_token *utok = get_current_utok(conn);
- for (i = 0; i < current_user.ut.ngroups; i++) {
- if (current_user.ut.groups[i] == gid) {
+ for (i = 0; i < utok->ngroups; i++) {
+ if (utok->groups[i] == gid) {
return True;
}
}
****************************************************************************/
static bool acl_group_override(connection_struct *conn,
- gid_t prim_gid,
- const char *fname)
+ const struct smb_filename *smb_fname)
{
- SMB_STRUCT_STAT sbuf;
-
if ((errno != EPERM) && (errno != EACCES)) {
return false;
}
/* file primary group == user primary or supplementary group */
if (lp_acl_group_control(SNUM(conn)) &&
- current_user_in_group(prim_gid)) {
+ current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
return true;
}
/* user has writeable permission */
if (lp_dos_filemode(SNUM(conn)) &&
- can_write_to_file(conn, fname, &sbuf)) {
+ can_write_to_file(conn, smb_fname)) {
return true;
}
Attempt to apply an ACL to a file or directory.
****************************************************************************/
-static bool set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, bool default_ace, gid_t prim_gid, bool *pacl_set_support)
+static bool set_canon_ace_list(files_struct *fsp,
+ canon_ace *the_ace,
+ bool default_ace,
+ const SMB_STRUCT_STAT *psbuf,
+ bool *pacl_set_support)
{
connection_struct *conn = fsp->conn;
bool ret = False;
bool needs_mask = False;
mode_t mask_perms = 0;
+ /* Use the psbuf that was passed in. */
+ if (psbuf != &fsp->fsp_name->st) {
+ fsp->fsp_name->st = *psbuf;
+ }
+
#if defined(POSIX_ACL_NEEDS_MASK)
/* HP-UX always wants to have a mask (called "class" there). */
needs_mask = True;
default_ace ? "default" : "file", strerror(errno) ));
}
*pacl_set_support = False;
- return False;
+ goto fail;
}
if( DEBUGLVL( 10 )) {
*/
if(default_ace || fsp->is_directory || fsp->fh->fd == -1) {
- if (SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name, the_acl_type, the_acl) == -1) {
+ if (SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name->base_name,
+ the_acl_type, the_acl) == -1) {
/*
* Some systems allow all the above calls and only fail with no ACL support
* when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
*pacl_set_support = False;
}
- if (acl_group_override(conn, prim_gid, fsp->fsp_name)) {
+ if (acl_group_override(conn, fsp->fsp_name)) {
int sret;
- DEBUG(5,("set_canon_ace_list: acl group control on and current user in file %s primary group.\n",
- fsp->fsp_name ));
+ DEBUG(5,("set_canon_ace_list: acl group "
+ "control on and current user in file "
+ "%s primary group.\n",
+ fsp_str_dbg(fsp)));
become_root();
- sret = SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name, the_acl_type, the_acl);
+ sret = SMB_VFS_SYS_ACL_SET_FILE(conn,
+ fsp->fsp_name->base_name, the_acl_type,
+ the_acl);
unbecome_root();
if (sret == 0) {
ret = True;
}
if (ret == False) {
- DEBUG(2,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
- the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
- fsp->fsp_name, strerror(errno) ));
+ DEBUG(2,("set_canon_ace_list: "
+ "sys_acl_set_file type %s failed for "
+ "file %s (%s).\n",
+ the_acl_type == SMB_ACL_TYPE_DEFAULT ?
+ "directory default" : "file",
+ fsp_str_dbg(fsp), strerror(errno)));
goto fail;
}
}
*pacl_set_support = False;
}
- if (acl_group_override(conn, prim_gid, fsp->fsp_name)) {
+ if (acl_group_override(conn, fsp->fsp_name)) {
int sret;
- DEBUG(5,("set_canon_ace_list: acl group control on and current user in file %s primary group.\n",
- fsp->fsp_name ));
+ DEBUG(5,("set_canon_ace_list: acl group "
+ "control on and current user in file "
+ "%s primary group.\n",
+ fsp_str_dbg(fsp)));
become_root();
sret = SMB_VFS_SYS_ACL_SET_FD(fsp, the_acl);
}
if (ret == False) {
- DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
- fsp->fsp_name, strerror(errno) ));
+ DEBUG(2,("set_canon_ace_list: "
+ "sys_acl_set_file failed for file %s "
+ "(%s).\n",
+ fsp_str_dbg(fsp), strerror(errno)));
goto fail;
}
}
mode_t or_bits;
if (ace_count != 3) {
- DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
-posix perms.\n", fsp->fsp_name ));
+ DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE "
+ "entries for file %s to convert to posix perms.\n",
+ fsp_str_dbg(fsp)));
return False;
}
}
if (!owner_ace || !group_ace || !other_ace) {
- DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n",
- fsp->fsp_name ));
+ DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get "
+ "standard entries for file %s.\n", fsp_str_dbg(fsp)));
return False;
}
*posix_perms = (((*posix_perms) & and_bits)|or_bits);
- DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
- (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
- fsp->fsp_name ));
+ DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o "
+ "to perm=0%o for file %s.\n", (int)owner_ace->perms,
+ (int)group_ace->perms, (int)other_ace->perms,
+ (int)*posix_perms, fsp_str_dbg(fsp)));
return True;
}
Based on code from "Jim McDonough" <jmcd@us.ibm.com>.
****************************************************************************/
-static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
+static size_t merge_default_aces( struct security_ace *nt_ace_list, size_t num_aces)
{
size_t i, j;
if ((nt_ace_list[i].type == nt_ace_list[j].type) &&
(nt_ace_list[i].size == nt_ace_list[j].size) &&
(nt_ace_list[i].access_mask == nt_ace_list[j].access_mask) &&
- sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) &&
+ dom_sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) &&
(i_inh == j_inh) &&
(i_flags_ni == 0) &&
(j_flags_ni == (SEC_ACE_FLAG_OBJECT_INHERIT|
(i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
if (num_aces - i - 1 > 0)
memmove(&nt_ace_list[i], &nt_ace_list[i+1], (num_aces-i-1) *
- sizeof(SEC_ACE));
+ sizeof(struct security_ace));
DEBUG(10,("merge_default_aces: Merging zero access ACE %u onto ACE %u.\n",
(unsigned int)i, (unsigned int)j ));
(i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
if (num_aces - j - 1 > 0)
memmove(&nt_ace_list[j], &nt_ace_list[j+1], (num_aces-j-1) *
- sizeof(SEC_ACE));
+ sizeof(struct security_ace));
DEBUG(10,("merge_default_aces: Merging ACE %u onto ACE %u.\n",
(unsigned int)j, (unsigned int)i ));
return num_aces;
}
+/*
+ * Add or Replace ACE entry.
+ * In some cases we need to add a specific ACE for compatibility reasons.
+ * When doing that we must make sure we are not actually creating a duplicate
+ * entry. So we need to search whether an ACE entry already exist and eventually
+ * replacce the access mask, or add a completely new entry if none was found.
+ *
+ * This function assumes the array has enough space to add a new entry without
+ * any reallocation of memory.
+ */
+
+static void add_or_replace_ace(struct security_ace *nt_ace_list, size_t *num_aces,
+ const struct dom_sid *sid, enum security_ace_type type,
+ uint32_t mask, uint8_t flags)
+{
+ int i;
+
+ /* first search for a duplicate */
+ for (i = 0; i < *num_aces; i++) {
+ if (dom_sid_equal(&nt_ace_list[i].trustee, sid) &&
+ (nt_ace_list[i].flags == flags)) break;
+ }
+
+ if (i < *num_aces) { /* found */
+ nt_ace_list[i].type = type;
+ nt_ace_list[i].access_mask = mask;
+ DEBUG(10, ("Replacing ACE %d with SID %s and flags %02x\n",
+ i, sid_string_dbg(sid), flags));
+ return;
+ }
+
+ /* not found, append it */
+ init_sec_ace(&nt_ace_list[(*num_aces)++], sid, type, mask, flags);
+}
+
+
/****************************************************************************
Reply to query a security descriptor from an fsp. If it succeeds it allocates
the space for the return elements and returns the size needed to return the
SMB_ACL_T posix_acl,
SMB_ACL_T def_acl,
uint32_t security_info,
- SEC_DESC **ppdesc)
+ struct security_descriptor **ppdesc)
{
- DOM_SID owner_sid;
- DOM_SID group_sid;
+ struct dom_sid owner_sid;
+ struct dom_sid group_sid;
size_t sd_size = 0;
- SEC_ACL *psa = NULL;
+ struct security_acl *psa = NULL;
size_t num_acls = 0;
size_t num_def_acls = 0;
size_t num_aces = 0;
canon_ace *file_ace = NULL;
canon_ace *dir_ace = NULL;
- SEC_ACE *nt_ace_list = NULL;
+ struct security_ace *nt_ace_list = NULL;
size_t num_profile_acls = 0;
- SEC_DESC *psd = NULL;
+ struct dom_sid orig_owner_sid;
+ struct security_descriptor *psd = NULL;
+ int i;
/*
* Get the owner, group and world SIDs.
*/
+ create_file_sids(sbuf, &owner_sid, &group_sid);
+
if (lp_profile_acls(SNUM(conn))) {
/* For WXP SP1 the owner must be administrators. */
+ sid_copy(&orig_owner_sid, &owner_sid);
sid_copy(&owner_sid, &global_sid_Builtin_Administrators);
sid_copy(&group_sid, &global_sid_Builtin_Users);
- num_profile_acls = 2;
- } else {
- create_file_sids(sbuf, &owner_sid, &group_sid);
+ num_profile_acls = 3;
}
- if ((security_info & DACL_SECURITY_INFORMATION) && !(security_info & PROTECTED_DACL_SECURITY_INFORMATION)) {
+ if ((security_info & SECINFO_DACL) && !(security_info & SECINFO_PROTECTED_DACL)) {
/*
* In the optimum case Creator Owner and Creator Group would be used for
goto done;
}
- if (S_ISDIR(sbuf->st_mode) && def_acl) {
+ if (S_ISDIR(sbuf->st_ex_mode) && def_acl) {
dir_ace = canonicalise_acl(conn, name, def_acl,
sbuf,
&global_sid_Creator_Owner,
num_def_acls = count_canon_ace_list(dir_ace);
/* Allocate the ace list. */
- if ((nt_ace_list = SMB_MALLOC_ARRAY(SEC_ACE,num_acls + num_profile_acls + num_def_acls)) == NULL) {
+ if ((nt_ace_list = SMB_MALLOC_ARRAY(struct security_ace,num_acls + num_profile_acls + num_def_acls)) == NULL) {
DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
goto done;
}
- memset(nt_ace_list, '\0', (num_acls + num_def_acls) * sizeof(SEC_ACE) );
+ memset(nt_ace_list, '\0', (num_acls + num_def_acls) * sizeof(struct security_ace) );
/*
* Create the NT ACE list from the canonical ace lists.
uint32_t acc = map_canon_ace_perms(SNUM(conn),
&nt_acl_type,
ace->perms,
- S_ISDIR(sbuf->st_mode));
+ S_ISDIR(sbuf->st_ex_mode));
init_sec_ace(&nt_ace_list[num_aces++],
&ace->trustee,
nt_acl_type,
acc,
- ace->inherited ?
- SEC_ACE_FLAG_INHERITED_ACE : 0);
+ ace->ace_flags);
}
/* The User must have access to a profile share - even
* if we can't map the SID. */
if (lp_profile_acls(SNUM(conn))) {
- init_sec_ace(&nt_ace_list[num_aces++],
- &global_sid_Builtin_Users,
- SEC_ACE_TYPE_ACCESS_ALLOWED,
- FILE_GENERIC_ALL, 0);
+ add_or_replace_ace(nt_ace_list, &num_aces,
+ &global_sid_Builtin_Users,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ FILE_GENERIC_ALL, 0);
}
for (ace = dir_ace; ace != NULL; ace = ace->next) {
uint32_t acc = map_canon_ace_perms(SNUM(conn),
&nt_acl_type,
ace->perms,
- S_ISDIR(sbuf->st_mode));
+ S_ISDIR(sbuf->st_ex_mode));
init_sec_ace(&nt_ace_list[num_aces++],
&ace->trustee,
nt_acl_type,
acc,
+ ace->ace_flags |
SEC_ACE_FLAG_OBJECT_INHERIT|
SEC_ACE_FLAG_CONTAINER_INHERIT|
- SEC_ACE_FLAG_INHERIT_ONLY|
- (ace->inherited ?
- SEC_ACE_FLAG_INHERITED_ACE : 0));
+ SEC_ACE_FLAG_INHERIT_ONLY);
}
/* The User must have access to a profile share - even
* if we can't map the SID. */
if (lp_profile_acls(SNUM(conn))) {
- init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, FILE_GENERIC_ALL,
- SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
- SEC_ACE_FLAG_INHERIT_ONLY|0);
+ add_or_replace_ace(nt_ace_list, &num_aces,
+ &global_sid_Builtin_Users,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ FILE_GENERIC_ALL,
+ SEC_ACE_FLAG_OBJECT_INHERIT |
+ SEC_ACE_FLAG_CONTAINER_INHERIT |
+ SEC_ACE_FLAG_INHERIT_ONLY);
}
/*
num_aces = merge_default_aces(nt_ace_list, num_aces);
+ if (lp_profile_acls(SNUM(conn))) {
+ for (i = 0; i < num_aces; i++) {
+ if (dom_sid_equal(&nt_ace_list[i].trustee, &owner_sid)) {
+ add_or_replace_ace(nt_ace_list, &num_aces,
+ &orig_owner_sid,
+ nt_ace_list[i].type,
+ nt_ace_list[i].access_mask,
+ nt_ace_list[i].flags);
+ break;
+ }
+ }
+ }
}
if (num_aces) {
goto done;
}
}
- } /* security_info & DACL_SECURITY_INFORMATION */
+ } /* security_info & SECINFO_DACL */
psd = make_standard_sec_desc( talloc_tos(),
- (security_info & OWNER_SECURITY_INFORMATION) ? &owner_sid : NULL,
- (security_info & GROUP_SECURITY_INFORMATION) ? &group_sid : NULL,
+ (security_info & SECINFO_OWNER) ? &owner_sid : NULL,
+ (security_info & SECINFO_GROUP) ? &group_sid : NULL,
psa,
&sd_size);
* flag doesn't seem to bother Windows NT.
* Always set this if map acl inherit is turned off.
*/
- if (get_protected_flag(pal) || !lp_map_acl_inherit(SNUM(conn))) {
- psd->type |= SE_DESC_DACL_PROTECTED;
+ if (pal == NULL || !lp_map_acl_inherit(SNUM(conn))) {
+ psd->type |= SEC_DESC_DACL_PROTECTED;
+ } else {
+ psd->type |= pal->sd_type;
}
if (psd->dacl) {
}
NTSTATUS posix_fget_nt_acl(struct files_struct *fsp, uint32_t security_info,
- SEC_DESC **ppdesc)
+ struct security_descriptor **ppdesc)
{
SMB_STRUCT_STAT sbuf;
SMB_ACL_T posix_acl = NULL;
*ppdesc = NULL;
- DEBUG(10,("posix_fget_nt_acl: called for file %s\n", fsp->fsp_name ));
+ DEBUG(10,("posix_fget_nt_acl: called for file %s\n",
+ fsp_str_dbg(fsp)));
/* can it happen that fsp_name == NULL ? */
if (fsp->is_directory || fsp->fh->fd == -1) {
- return posix_get_nt_acl(fsp->conn, fsp->fsp_name,
+ return posix_get_nt_acl(fsp->conn, fsp->fsp_name->base_name,
security_info, ppdesc);
}
pal = fload_inherited_info(fsp);
- return posix_get_nt_acl_common(fsp->conn, fsp->fsp_name, &sbuf, pal,
- posix_acl, NULL, security_info, ppdesc);
+ return posix_get_nt_acl_common(fsp->conn, fsp->fsp_name->base_name,
+ &sbuf, pal, posix_acl, NULL,
+ security_info, ppdesc);
}
NTSTATUS posix_get_nt_acl(struct connection_struct *conn, const char *name,
- uint32_t security_info, SEC_DESC **ppdesc)
+ uint32_t security_info, struct security_descriptor **ppdesc)
{
- SMB_STRUCT_STAT sbuf;
SMB_ACL_T posix_acl = NULL;
SMB_ACL_T def_acl = NULL;
struct pai_val *pal;
+ struct smb_filename smb_fname;
+ int ret;
*ppdesc = NULL;
DEBUG(10,("posix_get_nt_acl: called for file %s\n", name ));
+ ZERO_STRUCT(smb_fname);
+ smb_fname.base_name = discard_const_p(char, name);
+
/* Get the stat struct for the owner info. */
- if(SMB_VFS_STAT(conn, name, &sbuf) != 0) {
+ if (lp_posix_pathnames()) {
+ ret = SMB_VFS_LSTAT(conn, &smb_fname);
+ } else {
+ ret = SMB_VFS_STAT(conn, &smb_fname);
+ }
+
+ if (ret == -1) {
return map_nt_error_from_unix(errno);
}
posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, name, SMB_ACL_TYPE_ACCESS);
/* If it's a directory get the default POSIX ACL. */
- if(S_ISDIR(sbuf.st_mode)) {
+ if(S_ISDIR(smb_fname.st.st_ex_mode)) {
def_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, name, SMB_ACL_TYPE_DEFAULT);
def_acl = free_empty_sys_acl(conn, def_acl);
}
pal = load_inherited_info(conn, name);
- return posix_get_nt_acl_common(conn, name, &sbuf, pal, posix_acl,
- def_acl, security_info, ppdesc);
+ return posix_get_nt_acl_common(conn, name, &smb_fname.st, pal,
+ posix_acl, def_acl, security_info,
+ ppdesc);
}
/****************************************************************************
Try to chown a file. We will be able to chown it under the following conditions.
1) If we have root privileges, then it will just work.
- 2) If we have SeTakeOwnershipPrivilege we can change the user to the current user.
- 3) If we have SeRestorePrivilege we can change the user to any other user.
+ 2) If we have SeRestorePrivilege we can change the user + group to any other user.
+ 3) If we have SeTakeOwnershipPrivilege we can change the user to the current user.
4) If we have write permission to the file and dos_filemodes is set
then allow chown to the currently authenticated user.
****************************************************************************/
-int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
+NTSTATUS try_chown(files_struct *fsp, uid_t uid, gid_t gid)
{
- int ret;
- files_struct *fsp;
- SMB_STRUCT_STAT st;
+ NTSTATUS status;
- if(!CAN_WRITE(conn)) {
- return -1;
+ if(!CAN_WRITE(fsp->conn)) {
+ return NT_STATUS_MEDIA_WRITE_PROTECTED;
}
/* Case (1). */
- /* try the direct way first */
- ret = SMB_VFS_CHOWN(conn, fname, uid, gid);
- if (ret == 0)
- return 0;
+ status = vfs_chown_fsp(fsp, uid, gid);
+ if (NT_STATUS_IS_OK(status)) {
+ return status;
+ }
/* Case (2) / (3) */
if (lp_enable_privileges()) {
+ bool has_take_ownership_priv = security_token_has_privilege(
+ get_current_nttok(fsp->conn),
+ SEC_PRIV_TAKE_OWNERSHIP);
+ bool has_restore_priv = security_token_has_privilege(
+ get_current_nttok(fsp->conn),
+ SEC_PRIV_RESTORE);
+
+ if (has_restore_priv) {
+ ; /* Case (2) */
+ } else if (has_take_ownership_priv) {
+ /* Case (3) */
+ if (uid == get_current_uid(fsp->conn)) {
+ gid = (gid_t)-1;
+ } else {
+ has_take_ownership_priv = false;
+ }
+ }
- bool has_take_ownership_priv = user_has_privileges(current_user.nt_user_token,
- &se_take_ownership);
- bool has_restore_priv = user_has_privileges(current_user.nt_user_token,
- &se_restore);
-
- /* Case (2) */
- if ( ( has_take_ownership_priv && ( uid == current_user.ut.uid ) ) ||
- /* Case (3) */
- ( has_restore_priv ) ) {
-
+ if (has_take_ownership_priv || has_restore_priv) {
become_root();
- /* Keep the current file gid the same - take ownership doesn't imply group change. */
- ret = SMB_VFS_CHOWN(conn, fname, uid, (gid_t)-1);
+ status = vfs_chown_fsp(fsp, uid, gid);
unbecome_root();
- return ret;
+ return status;
}
}
/* Case (4). */
- if (!lp_dos_filemode(SNUM(conn))) {
- errno = EPERM;
- return -1;
- }
-
- if (SMB_VFS_STAT(conn,fname,&st)) {
- return -1;
- }
-
- if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, fname, &st, &fsp))) {
- return -1;
+ if (!lp_dos_filemode(SNUM(fsp->conn))) {
+ return NT_STATUS_ACCESS_DENIED;
}
/* only allow chown to the current user. This is more secure,
and also copes with the case where the SID in a take ownership ACL is
- a local SID on the users workstation
+ a local SID on the users workstation
*/
- uid = current_user.ut.uid;
+ if (uid != get_current_uid(fsp->conn)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
become_root();
/* Keep the current file gid the same. */
- ret = SMB_VFS_FCHOWN(fsp, uid, (gid_t)-1);
+ status = vfs_chown_fsp(fsp, uid, (gid_t)-1);
unbecome_root();
- close_file_fchmod(NULL, fsp);
-
- return ret;
+ return status;
}
#if 0
****************************************************************************/
NTSTATUS append_parent_acl(files_struct *fsp,
- const SEC_DESC *pcsd,
- SEC_DESC **pp_new_sd)
+ const struct security_descriptor *pcsd,
+ struct security_descriptor **pp_new_sd)
{
- SEC_DESC *parent_sd = NULL;
+ struct smb_filename *smb_dname = NULL;
+ struct security_descriptor *parent_sd = NULL;
files_struct *parent_fsp = NULL;
TALLOC_CTX *mem_ctx = talloc_tos();
char *parent_name = NULL;
- SEC_ACE *new_ace = NULL;
+ struct security_ace *new_ace = NULL;
unsigned int num_aces = pcsd->dacl->num_aces;
- SMB_STRUCT_STAT sbuf;
NTSTATUS status;
int info;
unsigned int i, j;
- SEC_DESC *psd = dup_sec_desc(talloc_tos(), pcsd);
- bool is_dacl_protected = (pcsd->type & SE_DESC_DACL_PROTECTED);
-
- ZERO_STRUCT(sbuf);
+ struct security_descriptor *psd = dup_sec_desc(talloc_tos(), pcsd);
+ bool is_dacl_protected = (pcsd->type & SEC_DESC_DACL_PROTECTED);
if (psd == NULL) {
return NT_STATUS_NO_MEMORY;
}
- if (!parent_dirname_talloc(mem_ctx,
- fsp->fsp_name,
- &parent_name,
- NULL)) {
+ if (!parent_dirname(mem_ctx, fsp->fsp_name->base_name, &parent_name,
+ NULL)) {
return NT_STATUS_NO_MEMORY;
}
+ status = create_synthetic_smb_fname(mem_ctx, parent_name, NULL, NULL,
+ &smb_dname);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
status = SMB_VFS_CREATE_FILE(
fsp->conn, /* conn */
NULL, /* req */
0, /* root_dir_fid */
- parent_name, /* fname */
- 0, /* create_file_flags */
+ smb_dname, /* fname */
FILE_READ_ATTRIBUTES, /* access_mask */
FILE_SHARE_NONE, /* share_access */
FILE_OPEN, /* create_disposition*/
NULL, /* sd */
NULL, /* ea_list */
&parent_fsp, /* result */
- &info, /* pinfo */
- &sbuf); /* psbuf */
+ &info); /* pinfo */
if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(smb_dname);
return status;
}
- status = SMB_VFS_GET_NT_ACL(parent_fsp->conn, parent_fsp->fsp_name,
- DACL_SECURITY_INFORMATION, &parent_sd );
+ status = SMB_VFS_GET_NT_ACL(parent_fsp->conn, smb_dname->base_name,
+ SECINFO_DACL, &parent_sd );
close_file(NULL, parent_fsp, NORMAL_CLOSE);
+ TALLOC_FREE(smb_dname);
if (!NT_STATUS_IS_OK(status)) {
return status;
num_aces += parent_sd->dacl->num_aces;
- if((new_ace = TALLOC_ZERO_ARRAY(mem_ctx, SEC_ACE,
+ if((new_ace = TALLOC_ZERO_ARRAY(mem_ctx, struct security_ace,
num_aces)) == NULL) {
return NT_STATUS_NO_MEMORY;
}
/* Finally append any inherited ACEs. */
for (j = 0; j < parent_sd->dacl->num_aces; j++) {
- SEC_ACE *se = &parent_sd->dacl->aces[j];
+ struct security_ace *se = &parent_sd->dacl->aces[j];
if (fsp->is_directory) {
if (!(se->flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
"ignoring non container "
"inherit flags %u on ACE with sid %s "
"from parent %s\n",
- fsp->fsp_name,
+ fsp_str_dbg(fsp),
(unsigned int)se->flags,
sid_string_dbg(&se->trustee),
parent_name));
"ignoring non object "
"inherit flags %u on ACE with sid %s "
"from parent %s\n",
- fsp->fsp_name,
+ fsp_str_dbg(fsp),
(unsigned int)se->flags,
sid_string_dbg(&se->trustee),
parent_name));
* same SID. This is order N^2. Ouch :-(. JRA. */
unsigned int k;
for (k = 0; k < psd->dacl->num_aces; k++) {
- if (sid_equal(&psd->dacl->aces[k].trustee,
+ if (dom_sid_equal(&psd->dacl->aces[k].trustee,
&se->trustee)) {
break;
}
DEBUG(10,("append_parent_acl: path %s "
"ignoring ACE with protected sid %s "
"from parent %s\n",
- fsp->fsp_name,
+ fsp_str_dbg(fsp),
sid_string_dbg(&se->trustee),
parent_name));
continue;
DEBUG(10,("append_parent_acl: path %s "
"inheriting ACE with sid %s "
"from parent %s\n",
- fsp->fsp_name,
+ fsp_str_dbg(fsp),
sid_string_dbg(&se->trustee),
parent_name));
}
psd->dacl->aces = new_ace;
psd->dacl->num_aces = i;
- psd->type &= ~(SE_DESC_DACL_AUTO_INHERITED|
- SE_DESC_DACL_AUTO_INHERIT_REQ);
+ psd->type &= ~(SEC_DESC_DACL_AUTO_INHERITED|
+ SEC_DESC_DACL_AUTO_INHERIT_REQ);
*pp_new_sd = psd;
return status;
Reply to set a security descriptor on an fsp. security_info_sent is the
description of the following NT ACL.
This should be the only external function needed for the UNIX style set ACL.
+ We make a copy of psd_orig as internal functions modify the elements inside
+ it, even though it's a const pointer.
****************************************************************************/
-NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC *psd)
+NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const struct security_descriptor *psd_orig)
{
connection_struct *conn = fsp->conn;
uid_t user = (uid_t)-1;
gid_t grp = (gid_t)-1;
- SMB_STRUCT_STAT sbuf;
- DOM_SID file_owner_sid;
- DOM_SID file_grp_sid;
+ struct dom_sid file_owner_sid;
+ struct dom_sid file_grp_sid;
canon_ace *file_ace_list = NULL;
canon_ace *dir_ace_list = NULL;
bool acl_perms = False;
mode_t orig_mode = (mode_t)0;
NTSTATUS status;
- uid_t orig_uid;
- gid_t orig_gid;
- bool need_chown = False;
+ bool set_acl_as_root = false;
+ bool acl_set_support = false;
+ bool ret = false;
+ struct security_descriptor *psd = NULL;
- DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
+ DEBUG(10,("set_nt_acl: called for file %s\n",
+ fsp_str_dbg(fsp)));
if (!CAN_WRITE(conn)) {
DEBUG(10,("set acl rejected on read-only share\n"));
return NT_STATUS_MEDIA_WRITE_PROTECTED;
}
+ if (!psd_orig) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ psd = dup_sec_desc(talloc_tos(), psd_orig);
+ if (!psd) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
/*
* Get the current state of the file.
*/
- if(fsp->is_directory || fsp->fh->fd == -1) {
- if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0)
- return map_nt_error_from_unix(errno);
- } else {
- if(SMB_VFS_FSTAT(fsp, &sbuf) != 0)
- return map_nt_error_from_unix(errno);
+ status = vfs_stat_fsp(fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
- /* Save the original elements we check against. */
- orig_mode = sbuf.st_mode;
- orig_uid = sbuf.st_uid;
- orig_gid = sbuf.st_gid;
+ /* Save the original element we check against. */
+ orig_mode = fsp->fsp_name->st.st_ex_mode;
/*
* Unpack the user/group/world id's.
*/
- status = unpack_nt_owners( SNUM(conn), &user, &grp, security_info_sent, psd);
+ /* POSIX can't cope with missing owner/group. */
+ if ((security_info_sent & SECINFO_OWNER) && (psd->owner_sid == NULL)) {
+ security_info_sent &= ~SECINFO_OWNER;
+ }
+ if ((security_info_sent & SECINFO_GROUP) && (psd->group_sid == NULL)) {
+ security_info_sent &= ~SECINFO_GROUP;
+ }
+
+ status = unpack_nt_owners( conn, &user, &grp, security_info_sent, psd);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
/*
- * Do we need to chown ?
+ * Do we need to chown ? If so this must be done first as the incoming
+ * CREATOR_OWNER acl will be relative to the *new* owner, not the old.
+ * Noticed by Simo.
*/
- if (((user != (uid_t)-1) && (orig_uid != user)) || (( grp != (gid_t)-1) && (orig_gid != grp))) {
- need_chown = True;
- }
-
- if (need_chown && (user == (uid_t)-1 || user == current_user.ut.uid)) {
+ if (((user != (uid_t)-1) && (fsp->fsp_name->st.st_ex_uid != user)) ||
+ (( grp != (gid_t)-1) && (fsp->fsp_name->st.st_ex_gid != grp))) {
DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
- fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
-
- if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
- DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
- fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
- if (errno == EPERM) {
- return NT_STATUS_INVALID_OWNER;
- }
- return map_nt_error_from_unix(errno);
+ fsp_str_dbg(fsp), (unsigned int)user,
+ (unsigned int)grp));
+
+ status = try_chown(fsp, user, grp);
+ if(!NT_STATUS_IS_OK(status)) {
+ DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error "
+ "= %s.\n", fsp_str_dbg(fsp),
+ (unsigned int)user,
+ (unsigned int)grp,
+ nt_errstr(status)));
+ return status;
}
/*
* (suid/sgid bits, for instance)
*/
- if(fsp->is_directory) {
- if(SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
- return map_nt_error_from_unix(errno);
- }
- } else {
-
- int ret;
+ status = vfs_stat_fsp(fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
- if(fsp->fh->fd == -1)
- ret = SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf);
- else
- ret = SMB_VFS_FSTAT(fsp, &sbuf);
+ /* Save the original element we check against. */
+ orig_mode = fsp->fsp_name->st.st_ex_mode;
- if(ret != 0)
- return map_nt_error_from_unix(errno);
+ /* If we successfully chowned, we know we must
+ * be able to set the acl, so do it as root.
+ */
+ set_acl_as_root = true;
+ }
+
+ create_file_sids(&fsp->fsp_name->st, &file_owner_sid, &file_grp_sid);
+
+ if((security_info_sent & SECINFO_DACL) &&
+ (psd->type & SEC_DESC_DACL_PRESENT) &&
+ (psd->dacl == NULL)) {
+ struct security_ace ace[3];
+
+ /* We can't have NULL DACL in POSIX.
+ Use owner/group/Everyone -> full access. */
+
+ init_sec_ace(&ace[0],
+ &file_owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ GENERIC_ALL_ACCESS,
+ 0);
+ init_sec_ace(&ace[1],
+ &file_grp_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ GENERIC_ALL_ACCESS,
+ 0);
+ init_sec_ace(&ace[2],
+ &global_sid_World,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ GENERIC_ALL_ACCESS,
+ 0);
+ psd->dacl = make_sec_acl(talloc_tos(),
+ NT4_ACL_REVISION,
+ 3,
+ ace);
+ if (psd->dacl == NULL) {
+ return NT_STATUS_NO_MEMORY;
}
+ security_acl_map_generic(psd->dacl, &file_generic_mapping);
+ }
- /* Save the original elements we check against. */
- orig_mode = sbuf.st_mode;
- orig_uid = sbuf.st_uid;
- orig_gid = sbuf.st_gid;
+ acl_perms = unpack_canon_ace(fsp, &fsp->fsp_name->st, &file_owner_sid,
+ &file_grp_sid, &file_ace_list,
+ &dir_ace_list, security_info_sent, psd);
- /* We did chown already, drop the flag */
- need_chown = False;
+ /* Ignore W2K traverse DACL set. */
+ if (!file_ace_list && !dir_ace_list) {
+ return NT_STATUS_OK;
}
- create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
-
-#if 0
- /* Disable this - prevents ACL inheritance from the ACL editor. JRA. */
-
- /* See here: http://www.codeproject.com/KB/winsdk/accessctrl2.aspx
- * for details and also the log trace in bug #4308. JRA.
- */
-
- 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) ) {
- SEC_DESC *new_sd = NULL;
- status = append_parent_acl(fsp, psd, &new_sd);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
- psd = new_sd;
+ if (!acl_perms) {
+ DEBUG(3,("set_nt_acl: cannot set permissions\n"));
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return NT_STATUS_ACCESS_DENIED;
}
-#endif
- acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
- &file_ace_list, &dir_ace_list, security_info_sent, psd);
+ /*
+ * Only change security if we got a DACL.
+ */
- /* Ignore W2K traverse DACL set. */
- if (file_ace_list || dir_ace_list) {
+ if(!(security_info_sent & SECINFO_DACL) || (psd->dacl == NULL)) {
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return NT_STATUS_OK;
+ }
- if (!acl_perms) {
- DEBUG(3,("set_nt_acl: cannot set permissions\n"));
+ /*
+ * Try using the POSIX ACL set first. Fall back to chmod if
+ * we have no ACL support on this filesystem.
+ */
+
+ if (acl_perms && file_ace_list) {
+ if (set_acl_as_root) {
+ become_root();
+ }
+ ret = set_canon_ace_list(fsp, file_ace_list, false,
+ &fsp->fsp_name->st, &acl_set_support);
+ if (set_acl_as_root) {
+ unbecome_root();
+ }
+ if (acl_set_support && ret == false) {
+ DEBUG(3,("set_nt_acl: failed to set file acl on file "
+ "%s (%s).\n", fsp_str_dbg(fsp),
+ strerror(errno)));
free_canon_ace_list(file_ace_list);
- free_canon_ace_list(dir_ace_list);
- return NT_STATUS_ACCESS_DENIED;
+ free_canon_ace_list(dir_ace_list);
+ return map_nt_error_from_unix(errno);
}
+ }
- /*
- * Only change security if we got a DACL.
- */
-
- if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
-
- bool acl_set_support = False;
- bool ret = False;
+ if (acl_perms && acl_set_support && fsp->is_directory) {
+ if (dir_ace_list) {
+ if (set_acl_as_root) {
+ become_root();
+ }
+ ret = set_canon_ace_list(fsp, dir_ace_list, true,
+ &fsp->fsp_name->st,
+ &acl_set_support);
+ if (set_acl_as_root) {
+ unbecome_root();
+ }
+ if (ret == false) {
+ DEBUG(3,("set_nt_acl: failed to set default "
+ "acl on directory %s (%s).\n",
+ fsp_str_dbg(fsp), strerror(errno)));
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return map_nt_error_from_unix(errno);
+ }
+ } else {
+ int sret = -1;
/*
- * Try using the POSIX ACL set first. Fall back to chmod if
- * we have no ACL support on this filesystem.
+ * No default ACL - delete one if it exists.
*/
- if (acl_perms && file_ace_list) {
- ret = set_canon_ace_list(fsp, file_ace_list, False, sbuf.st_gid, &acl_set_support);
- if (acl_set_support && ret == False) {
- DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
+ if (set_acl_as_root) {
+ become_root();
+ }
+ sret = SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn,
+ fsp->fsp_name->base_name);
+ if (set_acl_as_root) {
+ unbecome_root();
+ }
+ if (sret == -1) {
+ if (acl_group_override(conn, fsp->fsp_name)) {
+ DEBUG(5,("set_nt_acl: acl group "
+ "control on and current user "
+ "in file %s primary group. "
+ "Override delete_def_acl\n",
+ fsp_str_dbg(fsp)));
+
+ become_root();
+ sret =
+ SMB_VFS_SYS_ACL_DELETE_DEF_FILE(
+ conn,
+ fsp->fsp_name->base_name);
+ unbecome_root();
+ }
+
+ if (sret == -1) {
+ DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
free_canon_ace_list(file_ace_list);
- free_canon_ace_list(dir_ace_list);
+ free_canon_ace_list(dir_ace_list);
return map_nt_error_from_unix(errno);
}
}
+ }
+ }
- if (acl_perms && acl_set_support && fsp->is_directory) {
- if (dir_ace_list) {
- if (!set_canon_ace_list(fsp, dir_ace_list, True, sbuf.st_gid, &acl_set_support)) {
- DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
- free_canon_ace_list(file_ace_list);
- free_canon_ace_list(dir_ace_list);
- return map_nt_error_from_unix(errno);
- }
- } else {
+ if (acl_set_support) {
+ if (set_acl_as_root) {
+ become_root();
+ }
+ store_inheritance_attributes(fsp,
+ file_ace_list,
+ dir_ace_list,
+ psd->type);
+ if (set_acl_as_root) {
+ unbecome_root();
+ }
+ }
- /*
- * No default ACL - delete one if it exists.
- */
+ /*
+ * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
+ */
- if (SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fsp->fsp_name) == -1) {
- int sret = -1;
-
- if (acl_group_override(conn, sbuf.st_gid, fsp->fsp_name)) {
- DEBUG(5,("set_nt_acl: acl group control on and "
- "current user in file %s primary group. Override delete_def_acl\n",
- fsp->fsp_name ));
-
- become_root();
- sret = SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fsp->fsp_name);
- unbecome_root();
- }
-
- if (sret == -1) {
- DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
- free_canon_ace_list(file_ace_list);
- free_canon_ace_list(dir_ace_list);
- return map_nt_error_from_unix(errno);
- }
- }
- }
- }
+ if(!acl_set_support && acl_perms) {
+ mode_t posix_perms;
- if (acl_set_support) {
- store_inheritance_attributes(fsp, file_ace_list, dir_ace_list,
- (psd->type & SE_DESC_DACL_PROTECTED) ? True : False);
- }
+ if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ DEBUG(3,("set_nt_acl: failed to convert file acl to "
+ "posix permissions for file %s.\n",
+ fsp_str_dbg(fsp)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
- /*
- * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
- */
+ if (orig_mode != posix_perms) {
+ int sret = -1;
- if(!acl_set_support && acl_perms) {
- mode_t posix_perms;
+ DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
+ fsp_str_dbg(fsp), (unsigned int)posix_perms));
- if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
- free_canon_ace_list(file_ace_list);
- free_canon_ace_list(dir_ace_list);
- DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
- fsp->fsp_name ));
- return NT_STATUS_ACCESS_DENIED;
+ if (set_acl_as_root) {
+ become_root();
+ }
+ sret = SMB_VFS_CHMOD(conn, fsp->fsp_name->base_name,
+ posix_perms);
+ if (set_acl_as_root) {
+ unbecome_root();
+ }
+ if(sret == -1) {
+ if (acl_group_override(conn, fsp->fsp_name)) {
+ DEBUG(5,("set_nt_acl: acl group "
+ "control on and current user "
+ "in file %s primary group. "
+ "Override chmod\n",
+ fsp_str_dbg(fsp)));
+
+ become_root();
+ sret = SMB_VFS_CHMOD(conn,
+ fsp->fsp_name->base_name,
+ posix_perms);
+ unbecome_root();
}
- if (orig_mode != posix_perms) {
-
- DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
- fsp->fsp_name, (unsigned int)posix_perms ));
-
- if(SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms) == -1) {
- int sret = -1;
- if (acl_group_override(conn, sbuf.st_gid, fsp->fsp_name)) {
- DEBUG(5,("set_nt_acl: acl group control on and "
- "current user in file %s primary group. Override chmod\n",
- fsp->fsp_name ));
-
- become_root();
- sret = SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms);
- unbecome_root();
- }
-
- if (sret == -1) {
- DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
- fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
- free_canon_ace_list(file_ace_list);
- free_canon_ace_list(dir_ace_list);
- return map_nt_error_from_unix(errno);
- }
- }
+ if (sret == -1) {
+ DEBUG(3,("set_nt_acl: chmod %s, 0%o "
+ "failed. Error = %s.\n",
+ fsp_str_dbg(fsp),
+ (unsigned int)posix_perms,
+ strerror(errno)));
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+ return map_nt_error_from_unix(errno);
}
}
}
-
- free_canon_ace_list(file_ace_list);
- free_canon_ace_list(dir_ace_list);
}
- /* Any chown pending? */
- if (need_chown) {
- DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
- fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
-
- if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
- DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
- fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
- if (errno == EPERM) {
- return NT_STATUS_INVALID_OWNER;
- }
- return map_nt_error_from_unix(errno);
- }
- }
-
+ free_canon_ace_list(file_ace_list);
+ free_canon_ace_list(dir_ace_list);
+
+ /* Ensure the stat struct in the fsp is correct. */
+ status = vfs_stat_fsp(fsp);
+
return NT_STATUS_OK;
}
on the directory.
****************************************************************************/
-bool set_unix_posix_default_acl(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf,
+bool set_unix_posix_default_acl(connection_struct *conn, const char *fname, const SMB_STRUCT_STAT *psbuf,
uint16 num_def_acls, const char *pdata)
{
SMB_ACL_T def_acl = NULL;
- if (num_def_acls && !S_ISDIR(psbuf->st_mode)) {
- DEBUG(5,("set_unix_posix_default_acl: Can't set default ACL on non-directory file %s\n", fname ));
- errno = EISDIR;
- return False;
+ if (!S_ISDIR(psbuf->st_ex_mode)) {
+ if (num_def_acls) {
+ DEBUG(5,("set_unix_posix_default_acl: Can't set default ACL on non-directory file %s\n", fname ));
+ errno = EISDIR;
+ return False;
+ } else {
+ return True;
+ }
}
if (!num_def_acls) {
Assume we are dealing with files (for now)
********************************************************************/
-SEC_DESC *get_nt_acl_no_snum( TALLOC_CTX *ctx, const char *fname)
+struct security_descriptor *get_nt_acl_no_snum( TALLOC_CTX *ctx, const char *fname)
{
- SEC_DESC *psd, *ret_sd;
+ struct security_descriptor *psd, *ret_sd;
connection_struct *conn;
files_struct finfo;
struct fd_handle fh;
+ NTSTATUS status;
conn = TALLOC_ZERO_P(ctx, connection_struct);
if (conn == NULL) {
if (!smbd_vfs_init(conn)) {
DEBUG(0,("get_nt_acl_no_snum: Unable to create a fake connection struct!\n"));
- conn_free_internal( conn );
+ conn_free(conn);
return NULL;
}
finfo.conn = conn;
finfo.fh = &fh;
finfo.fh->fd = -1;
- finfo.fsp_name = CONST_DISCARD(char *,fname);
- if (!NT_STATUS_IS_OK(SMB_VFS_FGET_NT_ACL( &finfo, DACL_SECURITY_INFORMATION, &psd))) {
+ status = create_synthetic_smb_fname(talloc_tos(), fname, NULL, NULL,
+ &finfo.fsp_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ conn_free(conn);
+ return NULL;
+ }
+
+ if (!NT_STATUS_IS_OK(SMB_VFS_FGET_NT_ACL( &finfo, SECINFO_DACL, &psd))) {
DEBUG(0,("get_nt_acl_no_snum: get_nt_acl returned zero.\n"));
- conn_free_internal( conn );
+ TALLOC_FREE(finfo.fsp_name);
+ conn_free(conn);
return NULL;
}
ret_sd = dup_sec_desc( ctx, psd );
- conn_free_internal( conn );
+ TALLOC_FREE(finfo.fsp_name);
+ conn_free(conn);
return ret_sd;
}
+
+/* Stolen shamelessly from pvfs_default_acl() in source4 :-). */
+
+NTSTATUS make_default_filesystem_acl(TALLOC_CTX *ctx,
+ const char *name,
+ SMB_STRUCT_STAT *psbuf,
+ struct security_descriptor **ppdesc)
+{
+ struct dom_sid owner_sid, group_sid;
+ size_t size = 0;
+ struct security_ace aces[4];
+ uint32_t access_mask = 0;
+ mode_t mode = psbuf->st_ex_mode;
+ struct security_acl *new_dacl = NULL;
+ int idx = 0;
+
+ DEBUG(10,("make_default_filesystem_acl: file %s mode = 0%o\n",
+ name, (int)mode ));
+
+ uid_to_sid(&owner_sid, psbuf->st_ex_uid);
+ gid_to_sid(&group_sid, psbuf->st_ex_gid);
+
+ /*
+ We provide up to 4 ACEs
+ - Owner
+ - Group
+ - Everyone
+ - NT System
+ */
+
+ if (mode & S_IRUSR) {
+ if (mode & S_IWUSR) {
+ access_mask |= SEC_RIGHTS_FILE_ALL;
+ } else {
+ access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ }
+ }
+ if (mode & S_IWUSR) {
+ access_mask |= SEC_RIGHTS_FILE_WRITE | SEC_STD_DELETE;
+ }
+
+ init_sec_ace(&aces[idx],
+ &owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ access_mask,
+ 0);
+ idx++;
+
+ access_mask = 0;
+ if (mode & S_IRGRP) {
+ access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ }
+ if (mode & S_IWGRP) {
+ /* note that delete is not granted - this matches posix behaviour */
+ access_mask |= SEC_RIGHTS_FILE_WRITE;
+ }
+ if (access_mask) {
+ init_sec_ace(&aces[idx],
+ &group_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ access_mask,
+ 0);
+ idx++;
+ }
+
+ access_mask = 0;
+ if (mode & S_IROTH) {
+ access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ }
+ if (mode & S_IWOTH) {
+ access_mask |= SEC_RIGHTS_FILE_WRITE;
+ }
+ if (access_mask) {
+ init_sec_ace(&aces[idx],
+ &global_sid_World,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ access_mask,
+ 0);
+ idx++;
+ }
+
+ init_sec_ace(&aces[idx],
+ &global_sid_System,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_RIGHTS_FILE_ALL,
+ 0);
+ idx++;
+
+ new_dacl = make_sec_acl(ctx,
+ NT4_ACL_REVISION,
+ idx,
+ aces);
+
+ if (!new_dacl) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *ppdesc = make_sec_desc(ctx,
+ SECURITY_DESCRIPTOR_REVISION_1,
+ SEC_DESC_SELF_RELATIVE|SEC_DESC_DACL_PRESENT,
+ &owner_sid,
+ &group_sid,
+ NULL,
+ new_dacl,
+ &size);
+ if (!*ppdesc) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}