diff options
-rw-r--r-- | cifsacl.h | 57 | ||||
-rw-r--r-- | getcifsacl.c | 217 | ||||
-rw-r--r-- | getcifsacl.rst.in | 4 | ||||
-rw-r--r-- | setcifsacl.c | 695 | ||||
-rw-r--r-- | setcifsacl.rst.in | 27 |
5 files changed, 719 insertions, 281 deletions
@@ -25,13 +25,19 @@ #ifndef _CIFSACL_H #define _CIFSACL_H -#define BUFSIZE 1024 -#define ATTRNAME "system.cifs_acl" -#define ATTRNAME_ACL ATTRNAME -#define ATTRNAME_NTSD "system.cifs_ntsd" +#define BUFSIZE 1024 +#define ATTRNAME "system.cifs_acl" +#define ATTRNAME_ACL ATTRNAME +#define ATTRNAME_NTSD "system.cifs_ntsd" +#define ATTRNAME_NTSD_FULL "system.cifs_ntsd_full" #define MAX_NUM_AUTHS 6 +typedef enum { + ACE_KIND_DACL, + ACE_KIND_SACL +} ace_kinds; + /* File specific rights */ #define READ_DATA 0x00000001 /* R */ #define WRITE_DATA 0x00000002 /* W */ @@ -82,17 +88,36 @@ /* WA | WEA | A | W */ #define ALL_WRITE_BITS 0x40000116 -#define OBJECT_INHERIT_FLAG 0x01 /* OI */ -#define CONTAINER_INHERIT_FLAG 0x02 /* CI */ -#define NO_PROPAGATE_INHERIT_FLAG 0x04 /* NP */ -#define INHERIT_ONLY_FLAG 0x08 /* IO */ -#define INHERITED_ACE_FLAG 0x10 /* I */ -#define VFLAGS (OBJECT_INHERIT_FLAG|CONTAINER_INHERIT_FLAG|NO_PROPAGATE_INHERIT_FLAG|INHERIT_ONLY_FLAG|INHERITED_ACE_FLAG) - -#define ACCESS_ALLOWED 0 /* ALLOWED */ -#define ACCESS_DENIED 1 /* DENIED */ -#define ACCESS_ALLOWED_OBJECT 5 /* OBJECT_ALLOWED */ -#define ACCESS_DENIED_OBJECT 6 /* OBJECT_DENIED */ +/* R | W | A | REA | WEA | E | DC | RA | EA | D | RC | P | O */ +#define ALL_ACCESS_BITS 0x000f01ff + +/* ace flags */ +#define OBJECT_INHERIT_FLAG 0x01 /* OI */ +#define CONTAINER_INHERIT_FLAG 0x02 /* CI */ +#define NO_PROPAGATE_INHERIT_FLAG 0x04 /* NP */ +#define INHERIT_ONLY_FLAG 0x08 /* IO */ +#define INHERITED_ACE_FLAG 0x10 /* I */ +#define DACL_VFLAGS (OBJECT_INHERIT_FLAG|CONTAINER_INHERIT_FLAG|NO_PROPAGATE_INHERIT_FLAG|INHERIT_ONLY_FLAG|INHERITED_ACE_FLAG) + +#define SUCCESSFUL_ACCESS 0x40 /* SA */ +#define FAILED_ACCESS 0x80 /* FA */ +#define SACL_VFLAGS (SUCCESSFUL_ACCESS | FAILED_ACCESS) + +/* ace types */ +#define ACCESS_ALLOWED 0 /* ALLOWED */ +#define ACCESS_DENIED 1 /* DENIED */ +#define SYSTEM_AUDIT 2 /* AUDIT */ +#define ACCESS_ALLOWED_OBJECT 5 /* OBJECT_ALLOWED */ +#define ACCESS_DENIED_OBJECT 6 /* OBJECT_DENIED */ +#define SYSTEM_AUDIT_OBJECT 7 /* AUDIT_OBJECT */ +#define SYSTEM_AUDIT_CALLBACK 13 /* AUDIT_CALLBACK */ +#define SYSTEM_AUDIT_CALLBACK_OBJECT 15 /* AUDIT_CALLBACK_OBJECT */ +#define SYSTEM_MANDATORY_LABEL 17 /* MANDATORY_LABEL */ +#define SYSTEM_RESOURCE_ATTRIBUTE 18 /* RESOURCE_ATTRIBUTE */ +#define SYSTEM_SCOPED_POLICY_ID 19 /* SCOPED_POLICY_ID */ + +#define DACL_VTYPES (ACCESS_ALLOWED | ACCESS_DENIED | ACCESS_ALLOWED_OBJECT | ACCESS_DENIED_OBJECT) +#define SACL_VTYPES (SYSTEM_AUDIT | SYSTEM_AUDIT_OBJECT | SYSTEM_AUDIT_CALLBACK | SYSTEM_AUDIT_CALLBACK_OBJECT | SYSTEM_MANDATORY_LABEL | SYSTEM_RESOURCE_ATTRIBUTE | SYSTEM_SCOPED_POLICY_ID) #define COMPSID 0x1 #define COMPTYPE 0x2 @@ -100,6 +125,8 @@ #define COMPMASK 0x8 #define COMPALL (COMPSID|COMPTYPE|COMPFLAG|COMPMASK) +#define DEFAULT_ACL_REVISION 0x2 + /* * While not indicated here, the structs below represent on-the-wire data * structures. Any multi-byte values are expected to be little-endian! diff --git a/getcifsacl.c b/getcifsacl.c index 89851f6..1c01062 100644 --- a/getcifsacl.c +++ b/getcifsacl.c @@ -47,6 +47,11 @@ static bool raw = false; static void print_each_ace_mask(uint32_t mask) { + if ((mask & ALL_ACCESS_BITS) == ALL_ACCESS_BITS) { + printf("RWXDPO"); + return; + } + if ((mask & ALL_READ_BITS) && ((mask & EREAD) != EREAD && (mask & OREAD) != OREAD && (mask & BREAD) != BREAD)) { printf("0x%x", mask); @@ -74,32 +79,48 @@ print_each_ace_mask(uint32_t mask) } static void -print_ace_mask(uint32_t mask, int raw) +print_ace_mask(uint32_t mask, int raw, ace_kinds ace_kind) { if (raw) { printf("0x%x\n", mask); return; } - if (mask == FULL_CONTROL) - printf("FULL"); - else if (mask == CHANGE) - printf("CHANGE"); - else if (mask == DELETE) - printf("D"); - else if (mask == EREAD) - printf("READ"); - else if (mask & DELDHLD) - printf("0x%x", mask); - else - print_each_ace_mask(mask); - + switch (ace_kind) { + case ACE_KIND_SACL: + if (mask == FULL_CONTROL) + printf("FULL"); + else if (mask == CHANGE) + printf("CHANGE"); + else if (mask == DELETE) + printf("D"); + else if (mask == EREAD) + printf("READ"); + else + print_each_ace_mask(mask); + break; + case ACE_KIND_DACL: + default: + if (mask == FULL_CONTROL) + printf("FULL"); + else if (mask == CHANGE) + printf("CHANGE"); + else if (mask == DELETE) + printf("D"); + else if (mask == EREAD) + printf("READ"); + else if (mask & DELDHLD) + printf("0x%x", mask); + else + print_each_ace_mask(mask); + break; + } printf("\n"); return; } static void -print_ace_flags(uint8_t flags, int raw) +print_ace_flags(uint8_t flags, int raw, ace_kinds ace_kind) { bool mflags = false; @@ -108,37 +129,54 @@ print_ace_flags(uint8_t flags, int raw) return; } - if (flags & OBJECT_INHERIT_FLAG) { - mflags = true; - printf("OI"); - } - if (flags & CONTAINER_INHERIT_FLAG) { - if (mflags) - printf("|"); - else - mflags = true; - printf("CI"); - } - if (flags & NO_PROPAGATE_INHERIT_FLAG) { - if (mflags) - printf("|"); - else + switch (ace_kind) { + case ACE_KIND_SACL: + if (flags & SUCCESSFUL_ACCESS) { mflags = true; - printf("NP"); - } - if (flags & INHERIT_ONLY_FLAG) { - if (mflags) - printf("|"); - else - mflags = true; - printf("IO"); - } - if (flags & INHERITED_ACE_FLAG) { - if (mflags) - printf("|"); - else + printf("SA"); + } + if (flags & FAILED_ACCESS) { + if (mflags) + printf("|"); + else + mflags = true; + printf("FA"); + } + break; + case ACE_KIND_DACL: + if (flags & OBJECT_INHERIT_FLAG) { mflags = true; - printf("I"); + printf("OI"); + } + if (flags & CONTAINER_INHERIT_FLAG) { + if (mflags) + printf("|"); + else + mflags = true; + printf("CI"); + } + if (flags & NO_PROPAGATE_INHERIT_FLAG) { + if (mflags) + printf("|"); + else + mflags = true; + printf("NP"); + } + if (flags & INHERIT_ONLY_FLAG) { + if (mflags) + printf("|"); + else + mflags = true; + printf("IO"); + } + if (flags & INHERITED_ACE_FLAG) { + if (mflags) + printf("|"); + else + mflags = true; + printf("I"); + } + break; } if (!mflags) @@ -166,6 +204,27 @@ print_ace_type(uint8_t acetype, int raw) case ACCESS_DENIED_OBJECT: printf("OBJECT_DENIED"); break; + case SYSTEM_AUDIT: + printf("AUDIT"); + break; + case SYSTEM_AUDIT_OBJECT: + printf("AUDIT_OBJECT"); + break; + case SYSTEM_AUDIT_CALLBACK: + printf("AUDIT_CALLBACK"); + break; + case SYSTEM_AUDIT_CALLBACK_OBJECT: + printf("AUDIT_CALLBACK_OBJECT"); + break; + case SYSTEM_MANDATORY_LABEL: + printf("MANDATORY_LABEL"); + break; + case SYSTEM_RESOURCE_ATTRIBUTE: + printf("RESOURCE_ATTRIBUTE"); + break; + case SYSTEM_SCOPED_POLICY_ID: + printf("SCOPED_POLICY_ID"); + break; default: printf("UNKNOWN"); break; @@ -214,7 +273,7 @@ print_sid_raw: } static void -print_ace(struct cifs_ace *pace, char *end_of_acl, int raw) +print_ace(struct cifs_ace *pace, char *end_of_acl, int raw, ace_kinds ace_kind) { uint16_t size; @@ -237,15 +296,15 @@ print_ace(struct cifs_ace *pace, char *end_of_acl, int raw) printf(":"); print_ace_type(pace->type, raw); printf("/"); - print_ace_flags(pace->flags, raw); + print_ace_flags(pace->flags, raw, ace_kind); printf("/"); - print_ace_mask(le32toh(pace->access_req), raw); + print_ace_mask(le32toh(pace->access_req), raw, ace_kind); return; } static void -parse_dacl(struct cifs_ctrl_acl *pdacl, char *end_of_acl, int raw) + parse_acl(struct cifs_ctrl_acl *pacl, char *end_of_acl, int raw, ace_kinds ace_kind) { int i; int num_aces = 0; @@ -253,20 +312,20 @@ parse_dacl(struct cifs_ctrl_acl *pdacl, char *end_of_acl, int raw) char *acl_base; struct cifs_ace *pace; - if (!pdacl) + if (!pacl) return; - if (end_of_acl < (char *)pdacl + le16toh(pdacl->size)) + if (end_of_acl < (char *)pacl + le16toh(pacl->size)) return; - acl_base = (char *)pdacl; + acl_base = (char *)pacl; acl_size = sizeof(struct cifs_ctrl_acl); - num_aces = le32toh(pdacl->num_aces); + num_aces = le32toh(pacl->num_aces); if (num_aces > 0) { for (i = 0; i < num_aces; ++i) { pace = (struct cifs_ace *) (acl_base + acl_size); - print_ace(pace, end_of_acl, raw); + print_ace(pace, end_of_acl, raw, ace_kind); acl_base = (char *)pace; acl_size = le16toh(pace->size); } @@ -293,10 +352,10 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, ssize_t acl_len, int raw) { int rc; - uint32_t dacloffset; + uint32_t dacloffset, sacloffset; char *end_of_acl = ((char *)pntsd) + acl_len; struct cifs_sid *owner_sid_ptr, *group_sid_ptr; - struct cifs_ctrl_acl *dacl_ptr; /* no need for SACL ptr */ + struct cifs_ctrl_acl *dacl_ptr, *sacl_ptr; if (pntsd == NULL) return -EIO; @@ -307,6 +366,9 @@ parse_sec_desc(struct cifs_ntsd *pntsd, ssize_t acl_len, int raw) le32toh(pntsd->gsidoffset)); dacloffset = le32toh(pntsd->dacloffset); dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset); + sacloffset = le32toh(pntsd->sacloffset); + sacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + sacloffset); + printf("REVISION:0x%x\n", le16toh(pntsd->revision)); printf("CONTROL:0x%x\n", le16toh(pntsd->type)); @@ -318,10 +380,19 @@ parse_sec_desc(struct cifs_ntsd *pntsd, ssize_t acl_len, int raw) if (rc) return rc; - if (dacloffset) - parse_dacl(dacl_ptr, end_of_acl, raw); - else - printf("No ACL\n"); /* BB grant all or default perms? */ + if (dacloffset) { + printf("DACL:\n"); + parse_acl(dacl_ptr, end_of_acl, raw, ACE_KIND_DACL); + } else { + printf("No DACL\n"); /* BB grant all or default perms? */ + } + + if (sacloffset) { + printf("SACL:\n"); + parse_acl(sacl_ptr, end_of_acl, raw, ACE_KIND_SACL); + } else { + printf("No SACL\n"); + } return 0; } @@ -351,6 +422,9 @@ getcifsacl(const char *filename) size_t bufsize = BUFSIZE; char *attrval; int rc = 0; + /* use attribute name to fetch the whole descriptor */ + char *attrname = ATTRNAME_NTSD_FULL; + cifsacl: if (bufsize >= XATTR_SIZE_MAX) { fprintf(stderr, "buffer to allocate exceeds max size of %d\n", @@ -364,12 +438,35 @@ cifsacl: exit(1); } - attrlen = getxattr(filename, ATTRNAME, attrval, bufsize); +getxattr: + attrlen = getxattr(filename, attrname, attrval, bufsize); if (attrlen == -1) { if (errno == ERANGE) { free(attrval); bufsize += BUFSIZE; goto cifsacl; + } else if (errno == EIO && !(strcmp(attrname, ATTRNAME_NTSD_FULL))) { + /* + * attempt to fetch SACL in addition to owner and DACL via + * ATTRNAME_NTSD_FULL, fall back to owner/DACL via + * ATTRNAME_ACL if not allowed + * CIFS client maps STATUS_PRIVILEGE_NOT_HELD to EIO + */ + fprintf(stderr, "WARNING: Insufficient priviledges to fetch SACL for %s\n", + filename); + fprintf(stderr, " Fetching owner info and DACL only\n"); + attrname = ATTRNAME_ACL; + goto getxattr; + } else if (errno == EOPNOTSUPP && !(strcmp(attrname, ATTRNAME_NTSD_FULL))) { + /* + * no support for fetching SACL, fall back to owner/DACL via + * ATTRNAME_ACL + */ + fprintf(stderr, "WARNING: CIFS client does not support fetching SACL for %s\n", + filename); + fprintf(stderr, " Fetching owner info and DACL only\n"); + attrname = ATTRNAME_ACL; + goto getxattr; } else { fprintf(stderr, "Failed to getxattr %s: %s\n", filename, strerror(errno)); diff --git a/getcifsacl.rst.in b/getcifsacl.rst.in index ffde968..b7645fa 100644 --- a/getcifsacl.rst.in +++ b/getcifsacl.rst.in @@ -20,8 +20,8 @@ DESCRIPTION This tool is part of the cifs-utils suite. ``getcifsacl`` is a userspace helper program for the Linux CIFS client -file system. It is intended to display a security descriptor including -ACL for a file system object. +file system. It is intended to display a security descriptor, including +DACL and SACL for a file system object. This program uses a plugin to handle the mapping of SIDs to user and group names. *@pluginpath@* should be a symlink that points to the diff --git a/setcifsacl.c b/setcifsacl.c index 6e5a633..db9cf2c 100644 --- a/setcifsacl.c +++ b/setcifsacl.c @@ -20,6 +20,20 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + * This utility modifies various components of the security descriptor. These + * actions require different permissions and different SMB protocol-level flags. + * The user needs to make sure the share is mounted using the user credentials + * for the user who has appropriate permissions and privileges. The kernel + * CIFS client knows which flags to use based on the extended attribute name: + * - system.cifs_acl - set dacl only + * - system.cifs_ndst - set dacl and owner info + * - system.cifs_ntsd_full - set dacl, owner, and sacl + * + * For simplicity, the utility modifies one component of the descriptor: + * owner sid, group sid, DACL, or SACL. The rest of the descriptor is unchanged. + */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ @@ -46,7 +60,8 @@ enum setcifsacl_actions { ActAdd, ActSetAcl, ActSetOwner, - ActSetGroup + ActSetGroup, + ActSetSacl }; static void *plugin_handle; @@ -74,20 +89,142 @@ copy_cifs_sid(struct cifs_sid *dst, const struct cifs_sid *src) return size; } +static int +get_cifs_sid_size(const struct cifs_sid *sid) +{ + return (2 * sizeof(uint8_t) + + sizeof(uint8_t) * NUM_AUTHS + + sizeof(uint32_t) * sid->num_subauth); +} + +/* + * This function takes a pointer of the fetched (original) descriptor, and + * it returns the offset of the ACL in the new descriptor. + * + * If the original descriptor does not have an ACL, the corresponding offset + * is 0, and we need to determine where to place the ACL in the new descriptor. + * If SACL offset is zero, and there is DACL (dacloffset is not 0), then we will + * put SACL after DACL. If the DACL is not present either, we do not know if the + * ACLs should go before or after the owner and group SIDs (see below), and so + * we will use the offset right past the group SID. + * Similarly, if DACL offset is zero, we will use the offset the past the end + * of group SID. + * @todo: need to add a command-line argument to know if this is + * Azure-style descriptor or a regular-style descriptor + */ +static int get_aces_offset(const struct cifs_ntsd *pntsd, ace_kinds ace_kind) { + int dacloffset, sacloffset, acesoffset; + + switch(ace_kind) { + case ACE_KIND_SACL: + sacloffset = le32toh(pntsd->sacloffset); + if (sacloffset) { + acesoffset = sacloffset + sizeof(struct cifs_ctrl_acl); + } else { + dacloffset = le32toh(pntsd->dacloffset); + if (dacloffset) { + struct cifs_ctrl_acl *dacl_ptr = + (struct cifs_ctrl_acl *)((char *)pntsd + + dacloffset); + acesoffset = dacloffset + + le16toh(dacl_ptr->size) + + sizeof(struct cifs_ctrl_acl); + } else { + int gsidoffset = le32toh(pntsd->gsidoffset); + struct cifs_sid *group_sid_ptr = + (struct cifs_sid *)((char *)pntsd + + gsidoffset); + int gsidsize = get_cifs_sid_size(group_sid_ptr); + acesoffset = gsidoffset + gsidsize + + sizeof(struct cifs_ctrl_acl); + } + } + break; + case ACE_KIND_DACL: + default: + dacloffset = le32toh(pntsd->dacloffset); + if (dacloffset) { + acesoffset = dacloffset + sizeof(struct cifs_ctrl_acl); + } else { + int gsidoffset = le32toh(pntsd->gsidoffset); + struct cifs_sid *group_sid_ptr = + (struct cifs_sid *)((char *)pntsd + + gsidoffset); + int gsidsize = get_cifs_sid_size(group_sid_ptr); + acesoffset = gsidoffset + gsidsize + + sizeof(struct cifs_ctrl_acl); + } + break; + } + return acesoffset; +} + +int get_aces_size(const struct cifs_ntsd *pntsd, ace_kinds ace_kind) { + int acloffset, size; + struct cifs_ctrl_acl *acl_ptr; + + switch(ace_kind) { + case ACE_KIND_SACL: + acloffset = le32toh(pntsd->sacloffset); + break; + case ACE_KIND_DACL: + default: + acloffset = le32toh(pntsd->dacloffset); + } + if (acloffset) { + acl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + acloffset); + size = le16toh(acl_ptr->size); + } else { + size = 0; + } + return size; +} + +uint16_t get_acl_revision(const struct cifs_ntsd *pntsd, ace_kinds ace_kind) { + struct cifs_ctrl_acl *acl_ptr; + int acloffset; + switch(ace_kind) { + case ACE_KIND_SACL: + acloffset = le32toh(pntsd->sacloffset); + if (acloffset) { + acl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + + acloffset); + return acl_ptr->revision; + } + /* intentional fall through */ + case ACE_KIND_DACL: + default: + acloffset = le32toh(pntsd->dacloffset); + if (acloffset) { + acl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + + acloffset); + return acl_ptr->revision; + } else { + return DEFAULT_ACL_REVISION; + } + break; + } +} + +/* + * The actual changes to the ACL specified in ace_kind are performed by the + * caller of this function; this function copies/backfills the remaining + * relevant compoenents of the security descriptor that remain unchanged. + */ static ssize_t copy_sec_desc(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, - int numaces, int acessize) + int numaces, int acessize, ace_kinds ace_kind) { - int size, osidsoffset, gsidsoffset, dacloffset; + int size, osidsoffset, gsidsoffset, acloffset, dacloffset; ssize_t bufsize; struct cifs_sid *owner_sid_ptr, *group_sid_ptr; struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr; - struct cifs_ctrl_acl *dacl_ptr, *ndacl_ptr; + struct cifs_ctrl_acl *nacl_ptr, *dacl_ptr; + char *ndacl_ptr; /* copy security descriptor control portion */ osidsoffset = le32toh(pntsd->osidoffset); gsidsoffset = le32toh(pntsd->gsidoffset); - dacloffset = le32toh(pntsd->dacloffset); size = sizeof(struct cifs_ntsd); pnntsd->revision = pntsd->revision; @@ -97,26 +234,43 @@ copy_sec_desc(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, pnntsd->dacloffset = pntsd->dacloffset; bufsize = size; - dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset); - ndacl_ptr = (struct cifs_ctrl_acl *)((char *)pnntsd + dacloffset); + /* owner and group SIDs in the original defscriptor */ + owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + osidsoffset); + group_sid_ptr = (struct cifs_sid *)((char *)pntsd + gsidsoffset); + + /* get the offset of the acl control structure to initialize */ + acloffset = get_aces_offset(pntsd, ace_kind) - sizeof(struct cifs_ctrl_acl); + if (ace_kind == ACE_KIND_SACL) { + /* copy (unchanged) DACL if present, increment bufsize */ + dacloffset = le32toh(pntsd->dacloffset); + if (dacloffset) { + dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset); + ndacl_ptr = (char *)pnntsd + dacloffset; + size = sizeof(struct cifs_ctrl_acl) + le16toh(dacl_ptr->size); + memcpy(ndacl_ptr, (char *)dacl_ptr, size); + bufsize += size; + } + /* initialize SACL offset */ + pnntsd->sacloffset = acloffset; + } + nacl_ptr = (struct cifs_ctrl_acl *)((char *)pnntsd + acloffset); + nacl_ptr->revision = get_acl_revision(pntsd, ace_kind); size = acessize + sizeof(struct cifs_ctrl_acl); - ndacl_ptr->revision = dacl_ptr->revision; - ndacl_ptr->size = htole16(size); - ndacl_ptr->num_aces = htole32(numaces); + nacl_ptr->size = htole16(size); + nacl_ptr->num_aces = htole32(numaces); bufsize += size; /* copy owner sid */ - owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + osidsoffset); - group_sid_ptr = (struct cifs_sid *)((char *)pntsd + gsidsoffset); + /* * some servers like Azure return the owner and group SIDs at end rather * than at the beginning of the ACL so don't want to overwrite the last ACEs */ - if (dacloffset <= osidsoffset) { + if (acloffset <= osidsoffset) { /* owners placed at end of ACL */ - nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + dacloffset + size); - osidsoffset = dacloffset + size; + nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + acloffset + size); + osidsoffset = acloffset + size; pnntsd->osidoffset = htole32(osidsoffset); size = copy_cifs_sid(nowner_sid_ptr, owner_sid_ptr); bufsize += size; @@ -143,10 +297,9 @@ copy_sec_desc(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, } /* - * This function (and the one above) does not need to set the SACL-related - * fields, and this works fine because on the SMB protocol level, setting owner - * info, DACL, and SACL requires one to use separate flags that control which - * part of the descriptor is begin looked at on the server side + * This function does not need to set the SACL-related fields, and this works + * fine because the code path calling this function picks the 'system.cifs_ntsd' + * attribute name. This name tells Linux CIFS client that SACL is not modified. */ static ssize_t copy_sec_desc_with_sid(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, @@ -187,8 +340,13 @@ copy_sec_desc_with_sid(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, group_sid_ptr = sid; } - dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset); - daclsize = le16toh(dacl_ptr->size) + sizeof(struct cifs_ctrl_acl); + if (dacloffset) { + dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset); + daclsize = le16toh(dacl_ptr->size) + sizeof(struct cifs_ctrl_acl); + } else { + dacl_ptr = NULL; + daclsize = 0; + } /* copy owner sid */ nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + nosidoffset); @@ -205,18 +363,21 @@ copy_sec_desc_with_sid(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, nsidssize += size; /* position the dacl control info as in the fetched descriptor */ - if (dacloffset <= osidoffset) - ndacloffset = dacloffset; - else - ndacloffset = nosidoffset + nsidssize; - ndacl_ptr = (struct cifs_ctrl_acl *)((char *)pnntsd + ndacloffset); - pnntsd->dacloffset = htole32(ndacloffset); - - /* the DACL control fields do not change */ - ndacl_ptr->revision = dacl_ptr->revision; - ndacl_ptr->size = dacl_ptr->size; - ndacl_ptr->num_aces = dacl_ptr->num_aces; - + if (dacloffset) { + if (dacloffset <= osidoffset) + ndacloffset = dacloffset; + else + ndacloffset = nosidoffset + nsidssize; + ndacl_ptr = (struct cifs_ctrl_acl *)((char *)pnntsd + ndacloffset); + pnntsd->dacloffset = htole32(ndacloffset); + + /* the DACL control fields do not change */ + ndacl_ptr->revision = dacl_ptr->revision; + ndacl_ptr->size = dacl_ptr->size; + ndacl_ptr->num_aces = dacl_ptr->num_aces; + } else { + pnntsd->dacloffset = 0; + } /* * add DACL size (control portion and the array of aces) to the * buffer size @@ -278,25 +439,44 @@ compare_aces(struct cifs_ace *sace, struct cifs_ace *dace, int compflags) return 1; } +/* + * This is somewhat suboptimal, but to keep the code simple, we will still + * allocate the ACL control headers for DACL and SACL even thought there is + * no corresponding ACL (dacloffset = 0 or sacloffset = 0). + * When seetting DACL, we allocate sufficient space for the descriptor control + * structure, owner and group sids, and the DACL (ACL control structure and + * the aces). + * When setting SACL, we allocate sufficient space to copy the above components + * plus the SACL to be set (ACL controla and aces). + */ static int alloc_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, - int aces, size_t *acesoffset) + int aces, size_t *acesoffset, ace_kinds ace_kind) { - unsigned int size, acessize, bufsize, dacloffset; - - size = sizeof(struct cifs_ntsd) + - 2 * sizeof(struct cifs_sid) + - sizeof(struct cifs_ctrl_acl); - - dacloffset = le32toh(pntsd->dacloffset); + unsigned int size, acessize, bufsize; + + switch(ace_kind) { + case ACE_KIND_SACL: + size = sizeof(struct cifs_ntsd) + + 2 * sizeof(struct cifs_sid) + + sizeof(struct cifs_ctrl_acl) + + get_aces_size(pntsd, ACE_KIND_DACL) + + sizeof(struct cifs_ctrl_acl); + break; + case ACE_KIND_DACL: + default: + size = sizeof(struct cifs_ntsd) + + 2 * sizeof(struct cifs_sid) + + sizeof(struct cifs_ctrl_acl); + break; + } - *acesoffset = dacloffset + sizeof(struct cifs_ctrl_acl); + *acesoffset = get_aces_offset(pntsd, ace_kind); acessize = aces * sizeof(struct cifs_ace); bufsize = size + acessize; - *npntsd = calloc(1, bufsize); if (!*npntsd) { - printf("%s: Memory allocation failure", __func__); + fprintf(stderr, "%s: Memory allocation failure", __func__); return errno; } @@ -305,13 +485,13 @@ alloc_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, static int ace_set(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize, - struct cifs_ace **cacesptr, int numcaces) + struct cifs_ace **cacesptr, int numcaces, ace_kinds ace_kind) { int i, rc, size = 0, acessize = 0; size_t acesoffset; char *acesptr; - rc = alloc_sec_desc(pntsd, npntsd, numcaces, &acesoffset); + rc = alloc_sec_desc(pntsd, npntsd, numcaces, &acesoffset, ace_kind); if (rc) return rc; @@ -322,7 +502,7 @@ ace_set(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize, acesptr += size; } - *bufsize = copy_sec_desc(pntsd, *npntsd, numcaces, acessize); + *bufsize = copy_sec_desc(pntsd, *npntsd, numcaces, acessize, ace_kind); return 0; } @@ -330,14 +510,15 @@ ace_set(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize, static int ace_add(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize, struct cifs_ace **facesptr, int numfaces, - struct cifs_ace **cacesptr, int numcaces) + struct cifs_ace **cacesptr, int numcaces, + ace_kinds ace_kind) { int i, rc, numaces, size, acessize = 0; size_t acesoffset; char *acesptr; numaces = numfaces + numcaces; - rc = alloc_sec_desc(pntsd, npntsd, numaces, &acesoffset); + rc = alloc_sec_desc(pntsd, npntsd, numaces, &acesoffset, ace_kind); if (rc) return rc; @@ -353,7 +534,7 @@ ace_add(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize, acessize += size; } - *bufsize = copy_sec_desc(pntsd, *npntsd, numaces, acessize); + *bufsize = copy_sec_desc(pntsd, *npntsd, numaces, acessize, ace_kind); return 0; } @@ -361,18 +542,19 @@ ace_add(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize, static int ace_modify(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize, struct cifs_ace **facesptr, int numfaces, - struct cifs_ace **cacesptr, int numcaces) + struct cifs_ace **cacesptr, int numcaces, + ace_kinds ace_kind) { int i, j, rc, size, acessize = 0; size_t acesoffset; char *acesptr; if (numfaces == 0) { - printf("%s: No entries to modify", __func__); + fprintf(stderr, "%s: No entries to modify", __func__); return -1; } - rc = alloc_sec_desc(pntsd, npntsd, numfaces, &acesoffset); + rc = alloc_sec_desc(pntsd, npntsd, numfaces, &acesoffset, ace_kind); if (rc) return rc; @@ -393,7 +575,7 @@ ace_modify(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize, acessize += size; } - *bufsize = copy_sec_desc(pntsd, *npntsd, numfaces, acessize); + *bufsize = copy_sec_desc(pntsd, *npntsd, numfaces, acessize, ace_kind); return 0; } @@ -401,23 +583,24 @@ ace_modify(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize, static int ace_delete(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize, struct cifs_ace **facesptr, int numfaces, - struct cifs_ace **cacesptr, int numcaces) + struct cifs_ace **cacesptr, int numcaces, + ace_kinds ace_kind) { int i, j, numaces = 0, rc, size, acessize = 0; size_t acesoffset; char *acesptr; if (numfaces == 0) { - printf("%s: No entries to delete\n", __func__); + fprintf(stderr, "%s: No entries to delete\n", __func__); return -1; } if (numfaces < numcaces) { - printf("%s: Invalid entries to delete\n", __func__); + fprintf(stderr, "%s: Invalid entries to delete\n", __func__); return -1; } - rc = alloc_sec_desc(pntsd, npntsd, numfaces, &acesoffset); + rc = alloc_sec_desc(pntsd, npntsd, numfaces, &acesoffset, ace_kind); if (rc) return rc; @@ -429,7 +612,7 @@ ace_delete(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize, } if (j == numcaces) { size = copy_ace((struct cifs_ace *)acesptr, - facesptr[i]); + facesptr[i]); acessize += size; acesptr += size; ++numaces; @@ -437,41 +620,50 @@ ace_delete(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd, ssize_t *bufsize, } if (numaces == numfaces) { - printf("%s: Nothing to delete\n", __func__); + fprintf(stderr, "%s: Nothing to delete\n", __func__); return 1; } - *bufsize = copy_sec_desc(pntsd, *npntsd, numaces, acessize); + *bufsize = copy_sec_desc(pntsd, *npntsd, numaces, acessize, ace_kind); return 0; } static int get_numfaces(struct cifs_ntsd *pntsd, ssize_t acl_len, - struct cifs_ctrl_acl **daclptr) + struct cifs_ctrl_acl **aclptr, ace_kinds ace_kind) { int numfaces = 0; - uint32_t dacloffset; - struct cifs_ctrl_acl *ldaclptr; + uint32_t acloffset; + struct cifs_ctrl_acl *laclptr; char *end_of_acl = ((char *)pntsd) + acl_len; - dacloffset = le32toh(pntsd->dacloffset); - if (!dacloffset) + switch(ace_kind) { + case ACE_KIND_SACL: + acloffset = le32toh(pntsd->sacloffset); + break; + case ACE_KIND_DACL: + default: + acloffset = le32toh(pntsd->dacloffset); + break; + } + + if (!acloffset) return 0; - ldaclptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset); + laclptr = (struct cifs_ctrl_acl *)((char *)pntsd + acloffset); /* validate that we do not go past end of acl */ - if (end_of_acl >= (char *)ldaclptr + le16toh(ldaclptr->size)) { - numfaces = le32toh(ldaclptr->num_aces); - *daclptr = ldaclptr; + if (end_of_acl >= (char *)laclptr + le16toh(laclptr->size)) { + numfaces = le32toh(laclptr->num_aces); + *aclptr = laclptr; } return numfaces; } static struct cifs_ace ** -build_fetched_aces(char *daclptr, int numfaces) +build_fetched_aces(char *aclptr, int numfaces) { int i, acl_size; char *acl_base; @@ -479,12 +671,12 @@ build_fetched_aces(char *daclptr, int numfaces) facesptr = calloc(numfaces, sizeof(struct cifs_aces *)); if (!facesptr) { - printf("%s: Error %d allocating ACE array", + fprintf(stderr, "%s: Error %d allocating ACE array", __func__, errno); return facesptr; } - acl_base = daclptr; + acl_base = aclptr; acl_size = sizeof(struct cifs_ctrl_acl); for (i = 0; i < numfaces; ++i) { facesptr[i] = malloc(sizeof(struct cifs_ace)); @@ -498,7 +690,7 @@ build_fetched_aces(char *daclptr, int numfaces) return facesptr; build_fetched_aces_err: - printf("%s: Invalid fetched ace\n", __func__); + fprintf(stderr, "%s: Invalid fetched ace\n", __func__); for (i = 0; i < numfaces; ++i) free(facesptr[i]); free(facesptr); @@ -506,75 +698,129 @@ build_fetched_aces_err: } static int -verify_ace_type(char *typestr, uint8_t *typeval) +verify_ace_type(char *typestr, uint8_t *typeval, ace_kinds ace_kind) { int i, len; char *invaltype; + uint8_t ace_type_mask; + + switch(ace_kind) { + case ACE_KIND_SACL: + ace_type_mask = SACL_VTYPES; + break; + case ACE_KIND_DACL: + default: + ace_type_mask = DACL_VTYPES; + break; + } if (strstr(typestr, "0x")) { /* hex type value */ *typeval = strtol(typestr, &invaltype, 16); if (!strlen(invaltype)) { - if (*typeval != ACCESS_ALLOWED && - *typeval != ACCESS_DENIED && - *typeval != ACCESS_ALLOWED_OBJECT && - *typeval != ACCESS_DENIED_OBJECT) { - printf("%s: Invalid type: %s\n", - __func__, typestr); - return 1; + /* the type must be a single bit from the bit mask */ + if (*typeval != (*typeval & ace_type_mask)) { + fprintf(stderr, "%s: Invalid type: %s\n", + __func__, typestr); + return 1; } return 0; } } len = strlen(typestr); - for (i = 0; i < len; ++i) - *(typestr + i) = toupper(*(typestr + i)); - if (!strcmp(typestr, "ALLOWED")) - *typeval = 0x0; - else if (!strcmp(typestr, "DENIED")) - *typeval = 0x1; - else if (!strcmp(typestr, "ALLOWED_OBJECT")) - *typeval = 0x5; - else if (!strcmp(typestr, "DENIED_OBJECT")) - *typeval = 0x6; - else { - printf("%s: Invalid type: %s\n", __func__, typestr); - return 1; + switch(ace_kind) { + case ACE_KIND_SACL: + for (i = 0; i < len; ++i) + *(typestr + i) = toupper(*(typestr + i)); + if (!strcmp(typestr, "AUDIT")) + *typeval = SYSTEM_AUDIT; + else if (!strcmp(typestr, "AUDIT_OBJECT")) + *typeval = SYSTEM_AUDIT_OBJECT; + else if (!strcmp(typestr, "AUDIT_CALLBACK")) + *typeval = SYSTEM_AUDIT_CALLBACK; + else if (!strcmp(typestr, "AUDIT_CALLBACK_OBJECT")) + *typeval = SYSTEM_AUDIT_CALLBACK_OBJECT; + else if (!strcmp(typestr, "MANDATODY_LABEL")) + *typeval = SYSTEM_MANDATORY_LABEL; + else if (!strcmp(typestr, "RESOURCE_ATTRIBUTE")) + *typeval = SYSTEM_RESOURCE_ATTRIBUTE; + else if (!strcmp(typestr, "SCOPED_POLICY_ID")) + *typeval = SYSTEM_SCOPED_POLICY_ID; + else { + fprintf(stderr, "%s: Invalid type: %s\n", __func__, + typestr); + return 1; + } + break; + case ACE_KIND_DACL: + default: + for (i = 0; i < len; ++i) + *(typestr + i) = toupper(*(typestr + i)); + if (!strcmp(typestr, "ALLOWED")) + *typeval = ACCESS_ALLOWED; + else if (!strcmp(typestr, "DENIED")) + *typeval = ACCESS_DENIED; + else if (!strcmp(typestr, "ALLOWED_OBJECT")) + *typeval = ACCESS_ALLOWED_OBJECT; + else if (!strcmp(typestr, "DENIED_OBJECT")) + *typeval = ACCESS_DENIED_OBJECT; |