diff mbox series

Add support for setting owner and group in ntsd

Message ID 20200106163119.9083-2-boris.v.protopopov@gmail.com
State New
Headers show
Series Add support for setting owner and group in ntsd | expand

Commit Message

Boris Protopopov Jan. 6, 2020, 4:31 p.m. UTC
Extend setcifsacl utility to allow setting owner and group SIDs
in the security descriptor in addition to setting ACLs. This is
a user-friendly intefrace for setting owner and group SIDs that
takes advantage of the recent extensions in the CIFS kernel
client, and it complements setting raw values via setfattr.

Signed-off-by: Boris Protopopov <boris.v.protopopov@gmail.com>
---
 cifsacl.h         |   4 +-
 setcifsacl.c      | 253 ++++++++++++++++++++++++++++++++++++++++++++----------
 setcifsacl.rst.in |  27 ++++--
 3 files changed, 235 insertions(+), 49 deletions(-)

Comments

Pavel Shilovsky Jan. 8, 2020, 8:42 p.m. UTC | #1
пн, 6 янв. 2020 г. в 08:31, Boris Protopopov <boris.v.protopopov@gmail.com>:
>
> Extend setcifsacl utility to allow setting owner and group SIDs
> in the security descriptor in addition to setting ACLs. This is
> a user-friendly intefrace for setting owner and group SIDs that
> takes advantage of the recent extensions in the CIFS kernel
> client, and it complements setting raw values via setfattr.
>
> Signed-off-by: Boris Protopopov <boris.v.protopopov@gmail.com>

Thanks for the patch. Could you clarify which recent changes in the
CIFS kernel client are referenced in the patch description?

> ---
>  cifsacl.h         |   4 +-
>  setcifsacl.c      | 253 ++++++++++++++++++++++++++++++++++++++++++++----------
>  setcifsacl.rst.in |  27 ++++--
>  3 files changed, 235 insertions(+), 49 deletions(-)
>
> diff --git a/cifsacl.h b/cifsacl.h
> index ca72dd4..bd0c695 100644
> --- a/cifsacl.h
> +++ b/cifsacl.h
> @@ -26,7 +26,9 @@
>  #define _CIFSACL_H
>
>  #define BUFSIZE 1024
> -#define ATTRNAME "system.cifs_acl"
> +#define ATTRNAME       "system.cifs_acl"
> +#define ATTRNAME_ACL   ATTRNAME
> +#define ATTRNAME_NTSD  "system.cifs_ntsd"
>
>  #define MAX_NUM_AUTHS 6
>
> diff --git a/setcifsacl.c b/setcifsacl.c
> index 9a301e2..6e5a633 100644
> --- a/setcifsacl.c
> +++ b/setcifsacl.c
> @@ -44,7 +44,9 @@ enum setcifsacl_actions {
>         ActDelete,
>         ActModify,
>         ActAdd,
> -       ActSet
> +       ActSetAcl,
> +       ActSetOwner,
> +       ActSetGroup
>  };
>
>  static void *plugin_handle;
> @@ -140,6 +142,90 @@ copy_sec_desc(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
>         return bufsize;
>  }
>
> +/*
> + * 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
> + */
> +static ssize_t
> +copy_sec_desc_with_sid(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
> +               struct cifs_sid *sid, int maction)
> +{
> +       int size, daclsize;
> +       int osidoffset, gsidoffset, dacloffset;
> +       int nosidoffset, ngsidoffset, ndacloffset, nsidssize;
> +       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;
> +
> +       /* copy security descriptor control portion */
> +       osidoffset = le32toh(pntsd->osidoffset);
> +       gsidoffset = le32toh(pntsd->gsidoffset);
> +       dacloffset = le32toh(pntsd->dacloffset);
> +       /*
> +        * the size of the owner or group sid might be different from the old
> +        * one, so the group sid offest might change, and if the owner is
> +        * positioned before the DACL, the dacl offset might change as well;
> +        * note however, that the owner sid offset does not change
> +        */
> +       nosidoffset = osidoffset;
> +       size = sizeof(struct cifs_ntsd);
> +       pnntsd->revision = pntsd->revision;
> +       pnntsd->type = pntsd->type;
> +       pnntsd->osidoffset = pntsd->osidoffset;
> +       bufsize = size;
> +
> +       /* set the pointers for source sids */
> +       if (maction == ActSetOwner) {
> +               owner_sid_ptr = sid;
> +               group_sid_ptr = (struct cifs_sid *)((char *)pntsd + gsidoffset);
> +       }
> +       if (maction == ActSetGroup) {
> +               owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + osidoffset);
> +               group_sid_ptr = sid;
> +       }
> +
> +       dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
> +       daclsize = le16toh(dacl_ptr->size) + sizeof(struct cifs_ctrl_acl);
> +
> +       /* copy owner sid */
> +       nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + nosidoffset);
> +       size = copy_cifs_sid(nowner_sid_ptr, owner_sid_ptr);
> +       bufsize += size;
> +       nsidssize = size;
> +
> +       /* copy group sid */
> +       ngsidoffset = nosidoffset + size;
> +       ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + ngsidoffset);
> +       pnntsd->gsidoffset = htole32(ngsidoffset);
> +       size = copy_cifs_sid(ngroup_sid_ptr, group_sid_ptr);
> +       bufsize += size;
> +       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;
> +
> +       /*
> +        * add DACL size (control portion and the array of aces) to the
> +        * buffer size
> +        */
> +       bufsize += daclsize;
> +
> +       return bufsize;
> +}
> +
>  static int
>  copy_ace(struct cifs_ace *dace, struct cifs_ace *sace)
>  {
> @@ -788,7 +874,7 @@ setacl_action(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd,
>                 rc = ace_add(pntsd, npntsd, bufsize, facesptr,
>                                 numfaces, cacesptr, numcaces);
>                 break;
> -       case ActSet:
> +       case ActSetAcl:
>                 rc = ace_set(pntsd, npntsd, bufsize, cacesptr, numcaces);
>                 break;
>         default:
> @@ -803,9 +889,10 @@ static void
>  setcifsacl_usage(const char *prog)
>  {
>         fprintf(stderr,
> -       "%s: Alter CIFS/NTFS ACL in a security descriptor of a file object\n",
> +       "%s: Alter CIFS/NTFS ACL or owner/group in a security descriptor of a file object\n",
> +               prog);
> +       fprintf(stderr, "Usage: %s option [<list_of_ACEs>|<SID>] <file_name>\n",
>                 prog);
> -       fprintf(stderr, "Usage: %s option <list_of_ACEs> <file_name>\n", prog);
>         fprintf(stderr, "Valid options:\n");
>         fprintf(stderr, "\t-v   Version of the program\n");
>         fprintf(stderr, "\n\t-a Add ACE(s), separated by a comma, to an ACL\n");
> @@ -825,21 +912,32 @@ setcifsacl_usage(const char *prog)
>         "\n\t-S Replace existing ACL with ACE(s), separated by a comma\n");
>         fprintf(stderr,
>         "\tsetcifsacl -S \"ACL:Administrator:ALLOWED/0x0/D\" <file_name>\n");
> +       fprintf(stderr,
> +       "\n\t-o Set owner using specified SID (name or raw format)\n");
> +       fprintf(stderr,
> +       "\tsetcifsacl -o \"Administrator\" <file_name>\n");
> +       fprintf(stderr,
> +       "\n\t-g Set group using specified SID (name or raw format)\n");
> +       fprintf(stderr,
> +       "\tsetcifsacl -g \"Administrators\" <file_name>\n");
>         fprintf(stderr, "\nRefer to setcifsacl(1) manpage for details\n");
>  }
>
>  int
>  main(const int argc, char *const argv[])
>  {
> -       int i, rc, c, numcaces, numfaces;
> +       int i, rc, c, numcaces = 0, numfaces = 0;
>         enum setcifsacl_actions maction = ActUnknown;
>         ssize_t attrlen, bufsize = BUFSIZE;
> -       char *ace_list, *filename, *attrval, **arrptr = NULL;
> +       char *ace_list = NULL, *filename = NULL, *attrval = NULL,
> +               **arrptr = NULL, *sid_str = NULL;
>         struct cifs_ctrl_acl *daclptr = NULL;
>         struct cifs_ace **cacesptr = NULL, **facesptr = NULL;
>         struct cifs_ntsd *ntsdptr = NULL;
> +       struct cifs_sid sid;
> +       char *attrname = ATTRNAME_ACL;
>
> -       c = getopt(argc, argv, "hvD:M:a:S:");
> +       c = getopt(argc, argv, "hvD:M:a:S:o:g:");
>         switch (c) {
>         case 'D':
>                 maction = ActDelete;
> @@ -854,9 +952,19 @@ main(const int argc, char *const argv[])
>                 ace_list = optarg;
>                 break;
>         case 'S':
> -               maction = ActSet;
> +               maction = ActSetAcl;
>                 ace_list = optarg;
>                 break;
> +       case 'o':
> +               maction = ActSetOwner;
> +               sid_str = optarg;
> +               attrname = ATTRNAME_NTSD;
> +               break;
> +       case 'g':
> +               maction = ActSetGroup;
> +               sid_str = optarg;
> +               attrname = ATTRNAME_NTSD;
> +               break;
>         case 'h':
>                 setcifsacl_usage(basename(argv[0]));
>                 return 0;
> @@ -875,11 +983,16 @@ main(const int argc, char *const argv[])
>         }
>         filename = argv[3];
>
> -       if (!ace_list) {
> +       if (!ace_list && maction != ActSetOwner && maction != ActSetGroup) {
>                 printf("%s: No valid ACEs specified\n", __func__);
>                 return -1;
>         }
>
> +       if (!sid_str && (maction == ActSetOwner || maction == ActSetGroup)) {
> +               printf("%s: No valid SIDs specified\n", __func__);
> +               return -1;
> +       }
> +
>         if (init_plugin(&plugin_handle)) {
>                 fprintf(stderr, "WARNING: unable to initialize idmapping "
>                                 "plugin. Only \"raw\" SID strings will be "
> @@ -889,16 +1002,24 @@ main(const int argc, char *const argv[])
>                 plugin_loaded = true;
>         }
>
> -       numcaces = get_numcaces(ace_list);
> -
> -       arrptr = parse_cmdline_aces(ace_list, numcaces);
> -       if (!arrptr)
> -               goto setcifsacl_numcaces_ret;
> +       if (maction == ActSetOwner || maction == ActSetGroup) {
> +               /* parse the sid */
> +               if (setcifsacl_str_to_sid(sid_str, &sid)) {
> +                       printf("%s: failed to parce \'%s\' as SID\n", __func__,
> +                               sid_str);
> +                       goto setcifsacl_numcaces_ret;
> +               }
> +       } else {
> +               numcaces = get_numcaces(ace_list);
>
> -       cacesptr = build_cmdline_aces(arrptr, numcaces);
> -       if (!cacesptr)
> -               goto setcifsacl_cmdlineparse_ret;
> +               arrptr = parse_cmdline_aces(ace_list, numcaces);
> +               if (!arrptr)
> +                       goto setcifsacl_numcaces_ret;
>
> +               cacesptr = build_cmdline_aces(arrptr, numcaces);
> +               if (!cacesptr)
> +                       goto setcifsacl_cmdlineparse_ret;
> +       }
>  cifsacl:
>         if (bufsize >= XATTR_SIZE_MAX) {
>                 printf("%s: Buffer size %zd exceeds max size of %d\n",
> @@ -912,7 +1033,7 @@ cifsacl:
>                 goto setcifsacl_cmdlineverify_ret;
>         }
>
> -       attrlen = getxattr(filename, ATTRNAME, attrval, bufsize);
> +       attrlen = getxattr(filename, attrname, attrval, bufsize);
>         if (attrlen == -1) {
>                 if (errno == ERANGE) {
>                         free(attrval);
> @@ -924,26 +1045,64 @@ cifsacl:
>                 }
>         }
>
> -       numfaces = get_numfaces((struct cifs_ntsd *)attrval, attrlen, &daclptr);
> -       if (!numfaces && maction != ActAdd) { /* if we are not adding aces */
> -               printf("%s: Empty DACL\n", __func__);
> -               goto setcifsacl_facenum_ret;
> -       }
> +       if (maction == ActSetOwner || maction == ActSetGroup) {
> +               struct cifs_ntsd *pfntsd = (struct cifs_ntsd *)attrval;
> +               int dacloffset = le32toh(pfntsd->dacloffset);
> +               struct cifs_ctrl_acl *daclinfo =
> +                               (struct cifs_ctrl_acl *)(attrval + dacloffset);
> +               int numaces = le16toh(daclinfo->num_aces);
> +               int acessize = le32toh(daclinfo->size);
> +               size_t faceoffset, naceoffset;
> +               char *faceptr, *naceptr;
>
> -       facesptr = build_fetched_aces((char *)daclptr, numfaces);
> -       if (!facesptr)
> -               goto setcifsacl_facenum_ret;
> +               /*
> +                * this allocates large enough buffer for max sid size and the
> +                * dacl info from the fetched security descriptor
> +                */
> +               rc = alloc_sec_desc(pfntsd, &ntsdptr, numaces, &faceoffset);
> +               if (rc)
> +                       goto setcifsacl_numcaces_ret;
>
> -       bufsize = 0;
> -       rc = setacl_action((struct cifs_ntsd *)attrval, &ntsdptr, &bufsize,
> -               facesptr, numfaces, cacesptr, numcaces, maction);
> -       if (rc)
> -               goto setcifsacl_action_ret;
> +               /*
> +                * copy the control structures from the fetched descriptor, the
> +                * sid specified by the user, and adjust the offsets/move dacl
> +                * control structure if needed
> +                */
> +               bufsize = copy_sec_desc_with_sid(pfntsd, ntsdptr, &sid,
> +                               maction);
> +
> +               /* copy aces verbatim as they have not changed */
> +               faceptr = attrval + faceoffset;
> +               naceoffset = le32toh(ntsdptr->dacloffset) +
> +                               sizeof(struct cifs_ctrl_acl);
> +               naceptr = (char *)ntsdptr + naceoffset;
> +               memcpy(naceptr, faceptr, acessize);
> +       } else {
> +               bufsize = 0;
> +
> +               numfaces = get_numfaces((struct cifs_ntsd *)attrval, attrlen,
> +                               &daclptr);
> +               if (!numfaces && maction != ActAdd) {
> +                       /* if we are not adding aces */
> +                       printf("%s: Empty DACL\n", __func__);
> +                       goto setcifsacl_facenum_ret;
> +               }
> +
> +               facesptr = build_fetched_aces((char *)daclptr, numfaces);
> +               if (!facesptr)
> +                       goto setcifsacl_facenum_ret;
>
> -       attrlen = setxattr(filename, ATTRNAME, ntsdptr, bufsize, 0);
> +               rc = setacl_action((struct cifs_ntsd *)attrval, &ntsdptr,
> +                               &bufsize, facesptr, numfaces, cacesptr,
> +                               numcaces, maction);
> +               if (rc)
> +                       goto setcifsacl_action_ret;
> +       }
> +
> +       attrlen = setxattr(filename, attrname, ntsdptr, bufsize, 0);
>         if (attrlen == -1) {
>                 printf("%s: setxattr error: %s\n", __func__, strerror(errno));
> -               goto setcifsacl_facenum_ret;
> +               goto setcifsacl_action_ret;
>         }
>
>         if (plugin_loaded)
> @@ -951,25 +1110,33 @@ cifsacl:
>         return 0;
>
>  setcifsacl_action_ret:
> -       free(ntsdptr);
> +       if (ntsdptr)
> +               free(ntsdptr);
>
>  setcifsacl_facenum_ret:
> -       for (i = 0; i < numfaces; ++i)
> -               free(facesptr[i]);
> -       free(facesptr);
> +       if (facesptr) {
> +               for (i = 0; i < numfaces; ++i)
> +                       free(facesptr[i]);
> +               free(facesptr);
> +       }
>
>  setcifsacl_getx_ret:
> -       free(attrval);
> +       if (attrval)
> +               free(attrval);
>
>  setcifsacl_cmdlineverify_ret:
> -       for (i = 0; i < numcaces; ++i)
> -               free(cacesptr[i]);
> -       free(cacesptr);
> +       if (cacesptr) {
> +               for (i = 0; i < numcaces; ++i)
> +                       free(cacesptr[i]);
> +               free(cacesptr);
> +       }
>
>  setcifsacl_cmdlineparse_ret:
> -       free(arrptr);
> +       if (arrptr)
> +               free(arrptr);
>
>  setcifsacl_numcaces_ret:
> -       exit_plugin(plugin_handle);
> +       if (plugin_loaded)
> +               exit_plugin(plugin_handle);
>         return -1;
>  }
> diff --git a/setcifsacl.rst.in b/setcifsacl.rst.in
> index de9c758..985af7c 100644
> --- a/setcifsacl.rst.in
> +++ b/setcifsacl.rst.in
> @@ -2,16 +2,16 @@
>  setcifsacl
>  ==========
>
> -------------------------------------------------------------------------------------------------
> -Userspace helper to alter an ACL in a security descriptor for Common Internet File System (CIFS)
> -------------------------------------------------------------------------------------------------
> +-------------------------------------------------------------------------------------------------------------------
> +Userspace helper to alter an ACL or owner/group SID in a security descriptor for Common Internet File System (CIFS)
> +-------------------------------------------------------------------------------------------------------------------
>  :Manual section: 1
>
>  ********
>  SYNOPSIS
>  ********
>
> -  setcifsacl [-v|-a|-D|-M|-S] "{one or more ACEs}" {file system object}
> +  setcifsacl [-v|-a|-D|-M|-S|-o|-g] "{one or more ACEs or a SID}" {file system object}
>
>  ***********
>  DESCRIPTION
> @@ -20,7 +20,7 @@ DESCRIPTION
>  This tool is part of the cifs-utils suite.
>
>  ``setcifsacl`` is a userspace helper program for the Linux CIFS client
> -file system. It is intended to alter an ACL of a security descriptor
> +file system. It is intended to alter an ACL or set owner/group SID of a security descriptor
>  for a file system object. Whether a security descriptor to be set is
>  applied or not is determined by the CIFS/SMB server.
>
> @@ -55,6 +55,13 @@ OPTIONS
>    Set an ACL of security descriptor with the list of ACEs Existing ACL
>    is replaced entirely with the specified ACEs.
>
> +-o
> +  Set owner SID to one specified as a command line argument.
> +
> +-g
> +  Set group SID to one specified as a command line argument.
> +
> +  The owner/group SID can be specified as a name or a raw SID value.
>    Every ACE entry starts with "ACL:" One or more ACEs are specified
>    within double quotes.  Multiple ACEs are separated by a comma.
>
> @@ -93,6 +100,16 @@ Set an ACL
>
>    setcifsacl -S "ACL:CIFSTESTDOM\Administrator:0x0/0x0/FULL,ACL:CIFSTESTDOM\user2:0x0/0x0/FULL" <file_name>
>
> +Set owner SID
> +=============
> +
> +  setcifsacl -o "S-1-5-21-3338130290-3403600371-1423429424-2102" <file_name>
> +
> +Set group SID
> +=============
> +
> +  setcifsacl -g "Administrators@BUILTIN" <file_name>
> +
>  *****
>  NOTES
>  *****
> --
> 2.14.5
>


--
Best regards,
Pavel Shilovsky
Boris Protopopov Jan. 9, 2020, 4:07 p.m. UTC | #2
Yes, there is a patch that I have recently posted to linux-cifs and
linux-kernel list (subject line "Add support for setting owner info,
dos attributes, and create time") that enable setting owner/group in
ntsd, file native attributes, and file create time.

Best regards, Boris Protopopov.


On Wed, Jan 8, 2020 at 3:42 PM Pavel Shilovsky <piastryyy@gmail.com> wrote:
>
> пн, 6 янв. 2020 г. в 08:31, Boris Protopopov <boris.v.protopopov@gmail.com>:
> >
> > Extend setcifsacl utility to allow setting owner and group SIDs
> > in the security descriptor in addition to setting ACLs. This is
> > a user-friendly intefrace for setting owner and group SIDs that
> > takes advantage of the recent extensions in the CIFS kernel
> > client, and it complements setting raw values via setfattr.
> >
> > Signed-off-by: Boris Protopopov <boris.v.protopopov@gmail.com>
>
> Thanks for the patch. Could you clarify which recent changes in the
> CIFS kernel client are referenced in the patch description?
>
> > ---
> >  cifsacl.h         |   4 +-
> >  setcifsacl.c      | 253 ++++++++++++++++++++++++++++++++++++++++++++----------
> >  setcifsacl.rst.in |  27 ++++--
> >  3 files changed, 235 insertions(+), 49 deletions(-)
> >
> > diff --git a/cifsacl.h b/cifsacl.h
> > index ca72dd4..bd0c695 100644
> > --- a/cifsacl.h
> > +++ b/cifsacl.h
> > @@ -26,7 +26,9 @@
> >  #define _CIFSACL_H
> >
> >  #define BUFSIZE 1024
> > -#define ATTRNAME "system.cifs_acl"
> > +#define ATTRNAME       "system.cifs_acl"
> > +#define ATTRNAME_ACL   ATTRNAME
> > +#define ATTRNAME_NTSD  "system.cifs_ntsd"
> >
> >  #define MAX_NUM_AUTHS 6
> >
> > diff --git a/setcifsacl.c b/setcifsacl.c
> > index 9a301e2..6e5a633 100644
> > --- a/setcifsacl.c
> > +++ b/setcifsacl.c
> > @@ -44,7 +44,9 @@ enum setcifsacl_actions {
> >         ActDelete,
> >         ActModify,
> >         ActAdd,
> > -       ActSet
> > +       ActSetAcl,
> > +       ActSetOwner,
> > +       ActSetGroup
> >  };
> >
> >  static void *plugin_handle;
> > @@ -140,6 +142,90 @@ copy_sec_desc(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
> >         return bufsize;
> >  }
> >
> > +/*
> > + * 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
> > + */
> > +static ssize_t
> > +copy_sec_desc_with_sid(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
> > +               struct cifs_sid *sid, int maction)
> > +{
> > +       int size, daclsize;
> > +       int osidoffset, gsidoffset, dacloffset;
> > +       int nosidoffset, ngsidoffset, ndacloffset, nsidssize;
> > +       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;
> > +
> > +       /* copy security descriptor control portion */
> > +       osidoffset = le32toh(pntsd->osidoffset);
> > +       gsidoffset = le32toh(pntsd->gsidoffset);
> > +       dacloffset = le32toh(pntsd->dacloffset);
> > +       /*
> > +        * the size of the owner or group sid might be different from the old
> > +        * one, so the group sid offest might change, and if the owner is
> > +        * positioned before the DACL, the dacl offset might change as well;
> > +        * note however, that the owner sid offset does not change
> > +        */
> > +       nosidoffset = osidoffset;
> > +       size = sizeof(struct cifs_ntsd);
> > +       pnntsd->revision = pntsd->revision;
> > +       pnntsd->type = pntsd->type;
> > +       pnntsd->osidoffset = pntsd->osidoffset;
> > +       bufsize = size;
> > +
> > +       /* set the pointers for source sids */
> > +       if (maction == ActSetOwner) {
> > +               owner_sid_ptr = sid;
> > +               group_sid_ptr = (struct cifs_sid *)((char *)pntsd + gsidoffset);
> > +       }
> > +       if (maction == ActSetGroup) {
> > +               owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + osidoffset);
> > +               group_sid_ptr = sid;
> > +       }
> > +
> > +       dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
> > +       daclsize = le16toh(dacl_ptr->size) + sizeof(struct cifs_ctrl_acl);
> > +
> > +       /* copy owner sid */
> > +       nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + nosidoffset);
> > +       size = copy_cifs_sid(nowner_sid_ptr, owner_sid_ptr);
> > +       bufsize += size;
> > +       nsidssize = size;
> > +
> > +       /* copy group sid */
> > +       ngsidoffset = nosidoffset + size;
> > +       ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + ngsidoffset);
> > +       pnntsd->gsidoffset = htole32(ngsidoffset);
> > +       size = copy_cifs_sid(ngroup_sid_ptr, group_sid_ptr);
> > +       bufsize += size;
> > +       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;
> > +
> > +       /*
> > +        * add DACL size (control portion and the array of aces) to the
> > +        * buffer size
> > +        */
> > +       bufsize += daclsize;
> > +
> > +       return bufsize;
> > +}
> > +
> >  static int
> >  copy_ace(struct cifs_ace *dace, struct cifs_ace *sace)
> >  {
> > @@ -788,7 +874,7 @@ setacl_action(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd,
> >                 rc = ace_add(pntsd, npntsd, bufsize, facesptr,
> >                                 numfaces, cacesptr, numcaces);
> >                 break;
> > -       case ActSet:
> > +       case ActSetAcl:
> >                 rc = ace_set(pntsd, npntsd, bufsize, cacesptr, numcaces);
> >                 break;
> >         default:
> > @@ -803,9 +889,10 @@ static void
> >  setcifsacl_usage(const char *prog)
> >  {
> >         fprintf(stderr,
> > -       "%s: Alter CIFS/NTFS ACL in a security descriptor of a file object\n",
> > +       "%s: Alter CIFS/NTFS ACL or owner/group in a security descriptor of a file object\n",
> > +               prog);
> > +       fprintf(stderr, "Usage: %s option [<list_of_ACEs>|<SID>] <file_name>\n",
> >                 prog);
> > -       fprintf(stderr, "Usage: %s option <list_of_ACEs> <file_name>\n", prog);
> >         fprintf(stderr, "Valid options:\n");
> >         fprintf(stderr, "\t-v   Version of the program\n");
> >         fprintf(stderr, "\n\t-a Add ACE(s), separated by a comma, to an ACL\n");
> > @@ -825,21 +912,32 @@ setcifsacl_usage(const char *prog)
> >         "\n\t-S Replace existing ACL with ACE(s), separated by a comma\n");
> >         fprintf(stderr,
> >         "\tsetcifsacl -S \"ACL:Administrator:ALLOWED/0x0/D\" <file_name>\n");
> > +       fprintf(stderr,
> > +       "\n\t-o Set owner using specified SID (name or raw format)\n");
> > +       fprintf(stderr,
> > +       "\tsetcifsacl -o \"Administrator\" <file_name>\n");
> > +       fprintf(stderr,
> > +       "\n\t-g Set group using specified SID (name or raw format)\n");
> > +       fprintf(stderr,
> > +       "\tsetcifsacl -g \"Administrators\" <file_name>\n");
> >         fprintf(stderr, "\nRefer to setcifsacl(1) manpage for details\n");
> >  }
> >
> >  int
> >  main(const int argc, char *const argv[])
> >  {
> > -       int i, rc, c, numcaces, numfaces;
> > +       int i, rc, c, numcaces = 0, numfaces = 0;
> >         enum setcifsacl_actions maction = ActUnknown;
> >         ssize_t attrlen, bufsize = BUFSIZE;
> > -       char *ace_list, *filename, *attrval, **arrptr = NULL;
> > +       char *ace_list = NULL, *filename = NULL, *attrval = NULL,
> > +               **arrptr = NULL, *sid_str = NULL;
> >         struct cifs_ctrl_acl *daclptr = NULL;
> >         struct cifs_ace **cacesptr = NULL, **facesptr = NULL;
> >         struct cifs_ntsd *ntsdptr = NULL;
> > +       struct cifs_sid sid;
> > +       char *attrname = ATTRNAME_ACL;
> >
> > -       c = getopt(argc, argv, "hvD:M:a:S:");
> > +       c = getopt(argc, argv, "hvD:M:a:S:o:g:");
> >         switch (c) {
> >         case 'D':
> >                 maction = ActDelete;
> > @@ -854,9 +952,19 @@ main(const int argc, char *const argv[])
> >                 ace_list = optarg;
> >                 break;
> >         case 'S':
> > -               maction = ActSet;
> > +               maction = ActSetAcl;
> >                 ace_list = optarg;
> >                 break;
> > +       case 'o':
> > +               maction = ActSetOwner;
> > +               sid_str = optarg;
> > +               attrname = ATTRNAME_NTSD;
> > +               break;
> > +       case 'g':
> > +               maction = ActSetGroup;
> > +               sid_str = optarg;
> > +               attrname = ATTRNAME_NTSD;
> > +               break;
> >         case 'h':
> >                 setcifsacl_usage(basename(argv[0]));
> >                 return 0;
> > @@ -875,11 +983,16 @@ main(const int argc, char *const argv[])
> >         }
> >         filename = argv[3];
> >
> > -       if (!ace_list) {
> > +       if (!ace_list && maction != ActSetOwner && maction != ActSetGroup) {
> >                 printf("%s: No valid ACEs specified\n", __func__);
> >                 return -1;
> >         }
> >
> > +       if (!sid_str && (maction == ActSetOwner || maction == ActSetGroup)) {
> > +               printf("%s: No valid SIDs specified\n", __func__);
> > +               return -1;
> > +       }
> > +
> >         if (init_plugin(&plugin_handle)) {
> >                 fprintf(stderr, "WARNING: unable to initialize idmapping "
> >                                 "plugin. Only \"raw\" SID strings will be "
> > @@ -889,16 +1002,24 @@ main(const int argc, char *const argv[])
> >                 plugin_loaded = true;
> >         }
> >
> > -       numcaces = get_numcaces(ace_list);
> > -
> > -       arrptr = parse_cmdline_aces(ace_list, numcaces);
> > -       if (!arrptr)
> > -               goto setcifsacl_numcaces_ret;
> > +       if (maction == ActSetOwner || maction == ActSetGroup) {
> > +               /* parse the sid */
> > +               if (setcifsacl_str_to_sid(sid_str, &sid)) {
> > +                       printf("%s: failed to parce \'%s\' as SID\n", __func__,
> > +                               sid_str);
> > +                       goto setcifsacl_numcaces_ret;
> > +               }
> > +       } else {
> > +               numcaces = get_numcaces(ace_list);
> >
> > -       cacesptr = build_cmdline_aces(arrptr, numcaces);
> > -       if (!cacesptr)
> > -               goto setcifsacl_cmdlineparse_ret;
> > +               arrptr = parse_cmdline_aces(ace_list, numcaces);
> > +               if (!arrptr)
> > +                       goto setcifsacl_numcaces_ret;
> >
> > +               cacesptr = build_cmdline_aces(arrptr, numcaces);
> > +               if (!cacesptr)
> > +                       goto setcifsacl_cmdlineparse_ret;
> > +       }
> >  cifsacl:
> >         if (bufsize >= XATTR_SIZE_MAX) {
> >                 printf("%s: Buffer size %zd exceeds max size of %d\n",
> > @@ -912,7 +1033,7 @@ cifsacl:
> >                 goto setcifsacl_cmdlineverify_ret;
> >         }
> >
> > -       attrlen = getxattr(filename, ATTRNAME, attrval, bufsize);
> > +       attrlen = getxattr(filename, attrname, attrval, bufsize);
> >         if (attrlen == -1) {
> >                 if (errno == ERANGE) {
> >                         free(attrval);
> > @@ -924,26 +1045,64 @@ cifsacl:
> >                 }
> >         }
> >
> > -       numfaces = get_numfaces((struct cifs_ntsd *)attrval, attrlen, &daclptr);
> > -       if (!numfaces && maction != ActAdd) { /* if we are not adding aces */
> > -               printf("%s: Empty DACL\n", __func__);
> > -               goto setcifsacl_facenum_ret;
> > -       }
> > +       if (maction == ActSetOwner || maction == ActSetGroup) {
> > +               struct cifs_ntsd *pfntsd = (struct cifs_ntsd *)attrval;
> > +               int dacloffset = le32toh(pfntsd->dacloffset);
> > +               struct cifs_ctrl_acl *daclinfo =
> > +                               (struct cifs_ctrl_acl *)(attrval + dacloffset);
> > +               int numaces = le16toh(daclinfo->num_aces);
> > +               int acessize = le32toh(daclinfo->size);
> > +               size_t faceoffset, naceoffset;
> > +               char *faceptr, *naceptr;
> >
> > -       facesptr = build_fetched_aces((char *)daclptr, numfaces);
> > -       if (!facesptr)
> > -               goto setcifsacl_facenum_ret;
> > +               /*
> > +                * this allocates large enough buffer for max sid size and the
> > +                * dacl info from the fetched security descriptor
> > +                */
> > +               rc = alloc_sec_desc(pfntsd, &ntsdptr, numaces, &faceoffset);
> > +               if (rc)
> > +                       goto setcifsacl_numcaces_ret;
> >
> > -       bufsize = 0;
> > -       rc = setacl_action((struct cifs_ntsd *)attrval, &ntsdptr, &bufsize,
> > -               facesptr, numfaces, cacesptr, numcaces, maction);
> > -       if (rc)
> > -               goto setcifsacl_action_ret;
> > +               /*
> > +                * copy the control structures from the fetched descriptor, the
> > +                * sid specified by the user, and adjust the offsets/move dacl
> > +                * control structure if needed
> > +                */
> > +               bufsize = copy_sec_desc_with_sid(pfntsd, ntsdptr, &sid,
> > +                               maction);
> > +
> > +               /* copy aces verbatim as they have not changed */
> > +               faceptr = attrval + faceoffset;
> > +               naceoffset = le32toh(ntsdptr->dacloffset) +
> > +                               sizeof(struct cifs_ctrl_acl);
> > +               naceptr = (char *)ntsdptr + naceoffset;
> > +               memcpy(naceptr, faceptr, acessize);
> > +       } else {
> > +               bufsize = 0;
> > +
> > +               numfaces = get_numfaces((struct cifs_ntsd *)attrval, attrlen,
> > +                               &daclptr);
> > +               if (!numfaces && maction != ActAdd) {
> > +                       /* if we are not adding aces */
> > +                       printf("%s: Empty DACL\n", __func__);
> > +                       goto setcifsacl_facenum_ret;
> > +               }
> > +
> > +               facesptr = build_fetched_aces((char *)daclptr, numfaces);
> > +               if (!facesptr)
> > +                       goto setcifsacl_facenum_ret;
> >
> > -       attrlen = setxattr(filename, ATTRNAME, ntsdptr, bufsize, 0);
> > +               rc = setacl_action((struct cifs_ntsd *)attrval, &ntsdptr,
> > +                               &bufsize, facesptr, numfaces, cacesptr,
> > +                               numcaces, maction);
> > +               if (rc)
> > +                       goto setcifsacl_action_ret;
> > +       }
> > +
> > +       attrlen = setxattr(filename, attrname, ntsdptr, bufsize, 0);
> >         if (attrlen == -1) {
> >                 printf("%s: setxattr error: %s\n", __func__, strerror(errno));
> > -               goto setcifsacl_facenum_ret;
> > +               goto setcifsacl_action_ret;
> >         }
> >
> >         if (plugin_loaded)
> > @@ -951,25 +1110,33 @@ cifsacl:
> >         return 0;
> >
> >  setcifsacl_action_ret:
> > -       free(ntsdptr);
> > +       if (ntsdptr)
> > +               free(ntsdptr);
> >
> >  setcifsacl_facenum_ret:
> > -       for (i = 0; i < numfaces; ++i)
> > -               free(facesptr[i]);
> > -       free(facesptr);
> > +       if (facesptr) {
> > +               for (i = 0; i < numfaces; ++i)
> > +                       free(facesptr[i]);
> > +               free(facesptr);
> > +       }
> >
> >  setcifsacl_getx_ret:
> > -       free(attrval);
> > +       if (attrval)
> > +               free(attrval);
> >
> >  setcifsacl_cmdlineverify_ret:
> > -       for (i = 0; i < numcaces; ++i)
> > -               free(cacesptr[i]);
> > -       free(cacesptr);
> > +       if (cacesptr) {
> > +               for (i = 0; i < numcaces; ++i)
> > +                       free(cacesptr[i]);
> > +               free(cacesptr);
> > +       }
> >
> >  setcifsacl_cmdlineparse_ret:
> > -       free(arrptr);
> > +       if (arrptr)
> > +               free(arrptr);
> >
> >  setcifsacl_numcaces_ret:
> > -       exit_plugin(plugin_handle);
> > +       if (plugin_loaded)
> > +               exit_plugin(plugin_handle);
> >         return -1;
> >  }
> > diff --git a/setcifsacl.rst.in b/setcifsacl.rst.in
> > index de9c758..985af7c 100644
> > --- a/setcifsacl.rst.in
> > +++ b/setcifsacl.rst.in
> > @@ -2,16 +2,16 @@
> >  setcifsacl
> >  ==========
> >
> > -------------------------------------------------------------------------------------------------
> > -Userspace helper to alter an ACL in a security descriptor for Common Internet File System (CIFS)
> > -------------------------------------------------------------------------------------------------
> > +-------------------------------------------------------------------------------------------------------------------
> > +Userspace helper to alter an ACL or owner/group SID in a security descriptor for Common Internet File System (CIFS)
> > +-------------------------------------------------------------------------------------------------------------------
> >  :Manual section: 1
> >
> >  ********
> >  SYNOPSIS
> >  ********
> >
> > -  setcifsacl [-v|-a|-D|-M|-S] "{one or more ACEs}" {file system object}
> > +  setcifsacl [-v|-a|-D|-M|-S|-o|-g] "{one or more ACEs or a SID}" {file system object}
> >
> >  ***********
> >  DESCRIPTION
> > @@ -20,7 +20,7 @@ DESCRIPTION
> >  This tool is part of the cifs-utils suite.
> >
> >  ``setcifsacl`` is a userspace helper program for the Linux CIFS client
> > -file system. It is intended to alter an ACL of a security descriptor
> > +file system. It is intended to alter an ACL or set owner/group SID of a security descriptor
> >  for a file system object. Whether a security descriptor to be set is
> >  applied or not is determined by the CIFS/SMB server.
> >
> > @@ -55,6 +55,13 @@ OPTIONS
> >    Set an ACL of security descriptor with the list of ACEs Existing ACL
> >    is replaced entirely with the specified ACEs.
> >
> > +-o
> > +  Set owner SID to one specified as a command line argument.
> > +
> > +-g
> > +  Set group SID to one specified as a command line argument.
> > +
> > +  The owner/group SID can be specified as a name or a raw SID value.
> >    Every ACE entry starts with "ACL:" One or more ACEs are specified
> >    within double quotes.  Multiple ACEs are separated by a comma.
> >
> > @@ -93,6 +100,16 @@ Set an ACL
> >
> >    setcifsacl -S "ACL:CIFSTESTDOM\Administrator:0x0/0x0/FULL,ACL:CIFSTESTDOM\user2:0x0/0x0/FULL" <file_name>
> >
> > +Set owner SID
> > +=============
> > +
> > +  setcifsacl -o "S-1-5-21-3338130290-3403600371-1423429424-2102" <file_name>
> > +
> > +Set group SID
> > +=============
> > +
> > +  setcifsacl -g "Administrators@BUILTIN" <file_name>
> > +
> >  *****
> >  NOTES
> >  *****
> > --
> > 2.14.5
> >
>
>
> --
> Best regards,
> Pavel Shilovsky
Pavel Shilovsky Jan. 9, 2020, 6:29 p.m. UTC | #3
чт, 9 янв. 2020 г. в 08:07, Boris Protopopov <boris.v.protopopov@gmail.com>:
>
> Yes, there is a patch that I have recently posted to linux-cifs and
> linux-kernel list (subject line "Add support for setting owner info,
> dos attributes, and create time") that enable setting owner/group in
> ntsd, file native attributes, and file create time.

Thanks for clarifying!

We need to make sure that the appropriate error is returned for old
version of the kernel that don't have the patch.

--
Best regards,
Pavel Shilovsky
Pavel Shilovsky Jan. 22, 2020, 12:26 a.m. UTC | #4
чт, 9 янв. 2020 г. в 10:29, Pavel Shilovsky <piastryyy@gmail.com>:
>
> чт, 9 янв. 2020 г. в 08:07, Boris Protopopov <boris.v.protopopov@gmail.com>:
> >
> > Yes, there is a patch that I have recently posted to linux-cifs and
> > linux-kernel list (subject line "Add support for setting owner info,
> > dos attributes, and create time") that enable setting owner/group in
> > ntsd, file native attributes, and file create time.
>
> Thanks for clarifying!
>
> We need to make sure that the appropriate error is returned for old
> version of the kernel that don't have the patch.
>
> --
> Best regards,
> Pavel Shilovsky


Merged into "next". Thanks!

--
Best regards,
Pavel Shilovsky
diff mbox series

Patch

diff --git a/cifsacl.h b/cifsacl.h
index ca72dd4..bd0c695 100644
--- a/cifsacl.h
+++ b/cifsacl.h
@@ -26,7 +26,9 @@ 
 #define _CIFSACL_H
 
 #define BUFSIZE 1024
-#define ATTRNAME "system.cifs_acl"
+#define ATTRNAME	"system.cifs_acl"
+#define ATTRNAME_ACL	ATTRNAME
+#define ATTRNAME_NTSD	"system.cifs_ntsd"
 
 #define MAX_NUM_AUTHS 6
 
diff --git a/setcifsacl.c b/setcifsacl.c
index 9a301e2..6e5a633 100644
--- a/setcifsacl.c
+++ b/setcifsacl.c
@@ -44,7 +44,9 @@  enum setcifsacl_actions {
 	ActDelete,
 	ActModify,
 	ActAdd,
-	ActSet
+	ActSetAcl,
+	ActSetOwner,
+	ActSetGroup
 };
 
 static void *plugin_handle;
@@ -140,6 +142,90 @@  copy_sec_desc(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
 	return bufsize;
 }
 
+/*
+ * 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
+ */
+static ssize_t
+copy_sec_desc_with_sid(const struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
+		struct cifs_sid *sid, int maction)
+{
+	int size, daclsize;
+	int osidoffset, gsidoffset, dacloffset;
+	int nosidoffset, ngsidoffset, ndacloffset, nsidssize;
+	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;
+
+	/* copy security descriptor control portion */
+	osidoffset = le32toh(pntsd->osidoffset);
+	gsidoffset = le32toh(pntsd->gsidoffset);
+	dacloffset = le32toh(pntsd->dacloffset);
+	/*
+	 * the size of the owner or group sid might be different from the old
+	 * one, so the group sid offest might change, and if the owner is
+	 * positioned before the DACL, the dacl offset might change as well;
+	 * note however, that the owner sid offset does not change
+	 */
+	nosidoffset = osidoffset;
+	size = sizeof(struct cifs_ntsd);
+	pnntsd->revision = pntsd->revision;
+	pnntsd->type = pntsd->type;
+	pnntsd->osidoffset = pntsd->osidoffset;
+	bufsize = size;
+
+	/* set the pointers for source sids */
+	if (maction == ActSetOwner) {
+		owner_sid_ptr = sid;
+		group_sid_ptr = (struct cifs_sid *)((char *)pntsd + gsidoffset);
+	}
+	if (maction == ActSetGroup) {
+		owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + osidoffset);
+		group_sid_ptr = sid;
+	}
+
+	dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
+	daclsize = le16toh(dacl_ptr->size) + sizeof(struct cifs_ctrl_acl);
+
+	/* copy owner sid */
+	nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + nosidoffset);
+	size = copy_cifs_sid(nowner_sid_ptr, owner_sid_ptr);
+	bufsize += size;
+	nsidssize = size;
+
+	/* copy group sid */
+	ngsidoffset = nosidoffset + size;
+	ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + ngsidoffset);
+	pnntsd->gsidoffset = htole32(ngsidoffset);
+	size = copy_cifs_sid(ngroup_sid_ptr, group_sid_ptr);
+	bufsize += size;
+	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;
+
+	/*
+	 * add DACL size (control portion and the array of aces) to the
+	 * buffer size
+	 */
+	bufsize += daclsize;
+
+	return bufsize;
+}
+
 static int
 copy_ace(struct cifs_ace *dace, struct cifs_ace *sace)
 {
@@ -788,7 +874,7 @@  setacl_action(struct cifs_ntsd *pntsd, struct cifs_ntsd **npntsd,
 		rc = ace_add(pntsd, npntsd, bufsize, facesptr,
 				numfaces, cacesptr, numcaces);
 		break;
-	case ActSet:
+	case ActSetAcl:
 		rc = ace_set(pntsd, npntsd, bufsize, cacesptr, numcaces);
 		break;
 	default:
@@ -803,9 +889,10 @@  static void
 setcifsacl_usage(const char *prog)
 {
 	fprintf(stderr,
-	"%s: Alter CIFS/NTFS ACL in a security descriptor of a file object\n",
+	"%s: Alter CIFS/NTFS ACL or owner/group in a security descriptor of a file object\n",
+		prog);
+	fprintf(stderr, "Usage: %s option [<list_of_ACEs>|<SID>] <file_name>\n",
 		prog);
-	fprintf(stderr, "Usage: %s option <list_of_ACEs> <file_name>\n", prog);
 	fprintf(stderr, "Valid options:\n");
 	fprintf(stderr, "\t-v	Version of the program\n");
 	fprintf(stderr, "\n\t-a	Add ACE(s), separated by a comma, to an ACL\n");
@@ -825,21 +912,32 @@  setcifsacl_usage(const char *prog)
 	"\n\t-S	Replace existing ACL with ACE(s), separated by a comma\n");
 	fprintf(stderr,
 	"\tsetcifsacl -S \"ACL:Administrator:ALLOWED/0x0/D\" <file_name>\n");
+	fprintf(stderr,
+	"\n\t-o	Set owner using specified SID (name or raw format)\n");
+	fprintf(stderr,
+	"\tsetcifsacl -o \"Administrator\" <file_name>\n");
+	fprintf(stderr,
+	"\n\t-g	Set group using specified SID (name or raw format)\n");
+	fprintf(stderr,
+	"\tsetcifsacl -g \"Administrators\" <file_name>\n");
 	fprintf(stderr, "\nRefer to setcifsacl(1) manpage for details\n");
 }
 
 int
 main(const int argc, char *const argv[])
 {
-	int i, rc, c, numcaces, numfaces;
+	int i, rc, c, numcaces = 0, numfaces = 0;
 	enum setcifsacl_actions maction = ActUnknown;
 	ssize_t attrlen, bufsize = BUFSIZE;
-	char *ace_list, *filename, *attrval, **arrptr = NULL;
+	char *ace_list = NULL, *filename = NULL, *attrval = NULL,
+		**arrptr = NULL, *sid_str = NULL;
 	struct cifs_ctrl_acl *daclptr = NULL;
 	struct cifs_ace **cacesptr = NULL, **facesptr = NULL;
 	struct cifs_ntsd *ntsdptr = NULL;
+	struct cifs_sid sid;
+	char *attrname = ATTRNAME_ACL;
 
-	c = getopt(argc, argv, "hvD:M:a:S:");
+	c = getopt(argc, argv, "hvD:M:a:S:o:g:");
 	switch (c) {
 	case 'D':
 		maction = ActDelete;
@@ -854,9 +952,19 @@  main(const int argc, char *const argv[])
 		ace_list = optarg;
 		break;
 	case 'S':
-		maction = ActSet;
+		maction = ActSetAcl;
 		ace_list = optarg;
 		break;
+	case 'o':
+		maction = ActSetOwner;
+		sid_str = optarg;
+		attrname = ATTRNAME_NTSD;
+		break;
+	case 'g':
+		maction = ActSetGroup;
+		sid_str = optarg;
+		attrname = ATTRNAME_NTSD;
+		break;
 	case 'h':
 		setcifsacl_usage(basename(argv[0]));
 		return 0;
@@ -875,11 +983,16 @@  main(const int argc, char *const argv[])
 	}
 	filename = argv[3];
 
-	if (!ace_list) {
+	if (!ace_list && maction != ActSetOwner && maction != ActSetGroup) {
 		printf("%s: No valid ACEs specified\n", __func__);
 		return -1;
 	}
 
+	if (!sid_str && (maction == ActSetOwner || maction == ActSetGroup)) {
+		printf("%s: No valid SIDs specified\n", __func__);
+		return -1;
+	}
+
 	if (init_plugin(&plugin_handle)) {
 		fprintf(stderr, "WARNING: unable to initialize idmapping "
 				"plugin. Only \"raw\" SID strings will be "
@@ -889,16 +1002,24 @@  main(const int argc, char *const argv[])
 		plugin_loaded = true;
 	}
 
-	numcaces = get_numcaces(ace_list);
-
-	arrptr = parse_cmdline_aces(ace_list, numcaces);
-	if (!arrptr)
-		goto setcifsacl_numcaces_ret;
+	if (maction == ActSetOwner || maction == ActSetGroup) {
+		/* parse the sid */
+		if (setcifsacl_str_to_sid(sid_str, &sid)) {
+			printf("%s: failed to parce \'%s\' as SID\n", __func__,
+				sid_str);
+			goto setcifsacl_numcaces_ret;
+		}
+	} else {
+		numcaces = get_numcaces(ace_list);
 
-	cacesptr = build_cmdline_aces(arrptr, numcaces);
-	if (!cacesptr)
-		goto setcifsacl_cmdlineparse_ret;
+		arrptr = parse_cmdline_aces(ace_list, numcaces);
+		if (!arrptr)
+			goto setcifsacl_numcaces_ret;
 
+		cacesptr = build_cmdline_aces(arrptr, numcaces);
+		if (!cacesptr)
+			goto setcifsacl_cmdlineparse_ret;
+	}
 cifsacl:
 	if (bufsize >= XATTR_SIZE_MAX) {
 		printf("%s: Buffer size %zd exceeds max size of %d\n",
@@ -912,7 +1033,7 @@  cifsacl:
 		goto setcifsacl_cmdlineverify_ret;
 	}
 
-	attrlen = getxattr(filename, ATTRNAME, attrval, bufsize);
+	attrlen = getxattr(filename, attrname, attrval, bufsize);
 	if (attrlen == -1) {
 		if (errno == ERANGE) {
 			free(attrval);
@@ -924,26 +1045,64 @@  cifsacl:
 		}
 	}
 
-	numfaces = get_numfaces((struct cifs_ntsd *)attrval, attrlen, &daclptr);
-	if (!numfaces && maction != ActAdd) { /* if we are not adding aces */
-		printf("%s: Empty DACL\n", __func__);
-		goto setcifsacl_facenum_ret;
-	}
+	if (maction == ActSetOwner || maction == ActSetGroup) {
+		struct cifs_ntsd *pfntsd = (struct cifs_ntsd *)attrval;
+		int dacloffset = le32toh(pfntsd->dacloffset);
+		struct cifs_ctrl_acl *daclinfo =
+				(struct cifs_ctrl_acl *)(attrval + dacloffset);
+		int numaces = le16toh(daclinfo->num_aces);
+		int acessize = le32toh(daclinfo->size);
+		size_t faceoffset, naceoffset;
+		char *faceptr, *naceptr;
 
-	facesptr = build_fetched_aces((char *)daclptr, numfaces);
-	if (!facesptr)
-		goto setcifsacl_facenum_ret;
+		/*
+		 * this allocates large enough buffer for max sid size and the
+		 * dacl info from the fetched security descriptor
+		 */
+		rc = alloc_sec_desc(pfntsd, &ntsdptr, numaces, &faceoffset);
+		if (rc)
+			goto setcifsacl_numcaces_ret;
 
-	bufsize = 0;
-	rc = setacl_action((struct cifs_ntsd *)attrval, &ntsdptr, &bufsize,
-		facesptr, numfaces, cacesptr, numcaces, maction);
-	if (rc)
-		goto setcifsacl_action_ret;
+		/*
+		 * copy the control structures from the fetched descriptor, the
+		 * sid specified by the user, and adjust the offsets/move dacl
+		 * control structure if needed
+		 */
+		bufsize = copy_sec_desc_with_sid(pfntsd, ntsdptr, &sid,
+				maction);
+
+		/* copy aces verbatim as they have not changed */
+		faceptr = attrval + faceoffset;
+		naceoffset = le32toh(ntsdptr->dacloffset) +
+				sizeof(struct cifs_ctrl_acl);
+		naceptr = (char *)ntsdptr + naceoffset;
+		memcpy(naceptr, faceptr, acessize);
+	} else {
+		bufsize = 0;
+
+		numfaces = get_numfaces((struct cifs_ntsd *)attrval, attrlen,
+				&daclptr);
+		if (!numfaces && maction != ActAdd) {
+			/* if we are not adding aces */
+			printf("%s: Empty DACL\n", __func__);
+			goto setcifsacl_facenum_ret;
+		}
+
+		facesptr = build_fetched_aces((char *)daclptr, numfaces);
+		if (!facesptr)
+			goto setcifsacl_facenum_ret;
 
-	attrlen = setxattr(filename, ATTRNAME, ntsdptr, bufsize, 0);
+		rc = setacl_action((struct cifs_ntsd *)attrval, &ntsdptr,
+				&bufsize, facesptr, numfaces, cacesptr,
+				numcaces, maction);
+		if (rc)
+			goto setcifsacl_action_ret;
+	}
+
+	attrlen = setxattr(filename, attrname, ntsdptr, bufsize, 0);
 	if (attrlen == -1) {
 		printf("%s: setxattr error: %s\n", __func__, strerror(errno));
-		goto setcifsacl_facenum_ret;
+		goto setcifsacl_action_ret;
 	}
 
 	if (plugin_loaded)
@@ -951,25 +1110,33 @@  cifsacl:
 	return 0;
 
 setcifsacl_action_ret:
-	free(ntsdptr);
+	if (ntsdptr)
+		free(ntsdptr);
 
 setcifsacl_facenum_ret:
-	for (i = 0; i < numfaces; ++i)
-		free(facesptr[i]);
-	free(facesptr);
+	if (facesptr) {
+		for (i = 0; i < numfaces; ++i)
+			free(facesptr[i]);
+		free(facesptr);
+	}
 
 setcifsacl_getx_ret:
-	free(attrval);
+	if (attrval)
+		free(attrval);
 
 setcifsacl_cmdlineverify_ret:
-	for (i = 0; i < numcaces; ++i)
-		free(cacesptr[i]);
-	free(cacesptr);
+	if (cacesptr) {
+		for (i = 0; i < numcaces; ++i)
+			free(cacesptr[i]);
+		free(cacesptr);
+	}
 
 setcifsacl_cmdlineparse_ret:
-	free(arrptr);
+	if (arrptr)
+		free(arrptr);
 
 setcifsacl_numcaces_ret:
-	exit_plugin(plugin_handle);
+	if (plugin_loaded)
+		exit_plugin(plugin_handle);
 	return -1;
 }
diff --git a/setcifsacl.rst.in b/setcifsacl.rst.in
index de9c758..985af7c 100644
--- a/setcifsacl.rst.in
+++ b/setcifsacl.rst.in
@@ -2,16 +2,16 @@ 
 setcifsacl
 ==========
 
-------------------------------------------------------------------------------------------------
-Userspace helper to alter an ACL in a security descriptor for Common Internet File System (CIFS)
-------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------
+Userspace helper to alter an ACL or owner/group SID in a security descriptor for Common Internet File System (CIFS)
+-------------------------------------------------------------------------------------------------------------------
 :Manual section: 1
 
 ********
 SYNOPSIS
 ********
 
-  setcifsacl [-v|-a|-D|-M|-S] "{one or more ACEs}" {file system object}
+  setcifsacl [-v|-a|-D|-M|-S|-o|-g] "{one or more ACEs or a SID}" {file system object}
 
 ***********
 DESCRIPTION
@@ -20,7 +20,7 @@  DESCRIPTION
 This tool is part of the cifs-utils suite.
 
 ``setcifsacl`` is a userspace helper program for the Linux CIFS client
-file system. It is intended to alter an ACL of a security descriptor
+file system. It is intended to alter an ACL or set owner/group SID of a security descriptor
 for a file system object. Whether a security descriptor to be set is
 applied or not is determined by the CIFS/SMB server.
 
@@ -55,6 +55,13 @@  OPTIONS
   Set an ACL of security descriptor with the list of ACEs Existing ACL
   is replaced entirely with the specified ACEs.
 
+-o
+  Set owner SID to one specified as a command line argument.
+
+-g
+  Set group SID to one specified as a command line argument.
+
+  The owner/group SID can be specified as a name or a raw SID value.
   Every ACE entry starts with "ACL:" One or more ACEs are specified
   within double quotes.  Multiple ACEs are separated by a comma.
 
@@ -93,6 +100,16 @@  Set an ACL
 
   setcifsacl -S "ACL:CIFSTESTDOM\Administrator:0x0/0x0/FULL,ACL:CIFSTESTDOM\user2:0x0/0x0/FULL" <file_name>
 
+Set owner SID
+=============
+
+  setcifsacl -o "S-1-5-21-3338130290-3403600371-1423429424-2102" <file_name>
+
+Set group SID
+=============
+
+  setcifsacl -g "Administrators@BUILTIN" <file_name>
+
 *****
 NOTES
 *****