The branch, master has been updated
       via  a6054c0 s4: vfs: fruit tests: Add regression test for dealing with 
NFS ACE entries.
       via  49996ca selftest: vfs.fruit: add xattr_tdb where possible
       via  013aaff selftest: run vfs.fruit_netatalk test against seperate share
       via  875ff25 s3: smbd: vfs_fruit: Replace code in fruit_fget_nt_acl() 
with remove_virtual_nfs_aces().
       via  a3c925d s3: smbd: vfs_fruit: Replace code in check_ms_nfs() with 
remove_virtual_nfs_aces().
       via  ef091e2 s3: smbd: vfs_fruit: Add remove_virtual_nfs_aces() a 
generic NFS ACE remover.
      from  da39e74 libcli/security: fix some SID values in comments

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit a6054c01c29c2507e0d5a6aa110fee4fd5c5eeb9
Author: Jeremy Allison <[email protected]>
Date:   Thu Mar 15 14:45:06 2018 -0700

    s4: vfs: fruit tests: Add regression test for dealing with NFS ACE entries.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13319
    
    Signed-off-by: Jeremy Allison <[email protected]>
    Reviewed-by: Ralph Boehme <[email protected]>
    
    Autobuild-User(master): Jeremy Allison <[email protected]>
    Autobuild-Date(master): Sat Mar 17 04:04:32 CET 2018 on sn-devel-144

commit 49996ca9324596b6cd72eb8051ca3676dab17191
Author: Ralph Boehme <[email protected]>
Date:   Fri Mar 16 21:57:31 2018 +0100

    selftest: vfs.fruit: add xattr_tdb where possible
    
    This makes the tests indepent from fs xattr support.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13319
    
    Signed-off-by: Ralph Boehme <[email protected]>
    Reviewed-by: Jeremy Allison <[email protected]>

commit 013aaffe7ff0ed4c30495761bb3208c29b3b5de2
Author: Ralph Boehme <[email protected]>
Date:   Fri Mar 16 21:55:26 2018 +0100

    selftest: run vfs.fruit_netatalk test against seperate share
    
    These tests require a fs with xattr support. This allows adding
    xattr_tdb to all other shares in the next commit.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13319
    
    Signed-off-by: Ralph Boehme <[email protected]>
    Reviewed-by: Jeremy Allison <[email protected]>

commit 875ff2575feb96d06cf2290e5b6a226b32ef9758
Author: Jeremy Allison <[email protected]>
Date:   Thu Mar 15 09:57:09 2018 -0700

    s3: smbd: vfs_fruit: Replace code in fruit_fget_nt_acl() with 
remove_virtual_nfs_aces().
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13319
    
    Signed-off-by: Jeremy Allison <[email protected]>
    Reviewed-by: Ralph Boehme <[email protected]>

commit a3c925d80433e3d4fe1b1b315edf6520cacf0a9e
Author: Jeremy Allison <[email protected]>
Date:   Thu Mar 15 09:54:41 2018 -0700

    s3: smbd: vfs_fruit: Replace code in check_ms_nfs() with 
remove_virtual_nfs_aces().
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13319
    
    Signed-off-by: Jeremy Allison <[email protected]>
    Reviewed-by: Ralph Boehme <[email protected]>

commit ef091e2cf836793e2aa533990913609ccab5119a
Author: Jeremy Allison <[email protected]>
Date:   Thu Mar 15 09:52:30 2018 -0700

    s3: smbd: vfs_fruit: Add remove_virtual_nfs_aces() a generic NFS ACE 
remover.
    
    Not yet used, will be used to tidyup existing code.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=13319
    
    Signed-off-by: Jeremy Allison <[email protected]>
    Reviewed-by: Ralph Boehme <[email protected]>

-----------------------------------------------------------------------

Summary of changes:
 selftest/target/Samba3.pm   |  20 ++++--
 source3/modules/vfs_fruit.c | 116 +++++++++++++-----------------
 source3/selftest/tests.py   |   2 +-
 source4/torture/vfs/fruit.c | 171 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 238 insertions(+), 71 deletions(-)


Changeset truncated at 500 lines:

diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index e6c95fa..6bedbde 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -1889,6 +1889,16 @@ sub provision($$$$$$$$$)
 
 [vfs_fruit]
        path = $shrdir
+       vfs objects = catia fruit streams_xattr acl_xattr xattr_tdb
+       fruit:resource = file
+       fruit:metadata = netatalk
+       fruit:locking = netatalk
+       fruit:encoding = native
+       fruit:veto_appledouble = no
+
+[vfs_fruit_xattr]
+       path = $shrdir
+        # This is used by vfs.fruit tests that require real fs xattr
        vfs objects = catia fruit streams_xattr acl_xattr
        fruit:resource = file
        fruit:metadata = netatalk
@@ -1898,29 +1908,29 @@ sub provision($$$$$$$$$)
 
 [vfs_fruit_metadata_stream]
        path = $shrdir
-       vfs objects = fruit streams_xattr acl_xattr
+       vfs objects = fruit streams_xattr acl_xattr xattr_tdb
        fruit:resource = file
        fruit:metadata = stream
        fruit:veto_appledouble = no
 
 [vfs_fruit_stream_depot]
        path = $shrdir
-       vfs objects = fruit streams_depot acl_xattr
+       vfs objects = fruit streams_depot acl_xattr xattr_tdb
        fruit:resource = stream
        fruit:metadata = stream
        fruit:veto_appledouble = no
 
 [vfs_wo_fruit]
        path = $shrdir
-       vfs objects = streams_xattr acl_xattr
+       vfs objects = streams_xattr acl_xattr xattr_tdb
 
 [vfs_wo_fruit_stream_depot]
        path = $shrdir
-       vfs objects = streams_depot acl_xattr
+       vfs objects = streams_depot acl_xattr xattr_tdb
 
 [vfs_fruit_timemachine]
        path = $shrdir
-       vfs objects = fruit streams_xattr acl_xattr
+       vfs objects = fruit streams_xattr acl_xattr xattr_tdb
        fruit:resource = file
        fruit:metadata = stream
        fruit:time machine = yes
diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 29372e9..19b78ed 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -2954,6 +2954,49 @@ static NTSTATUS readdir_attr_macmeta(struct 
vfs_handle_struct *handle,
        return status;
 }
 
+static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
+{
+       NTSTATUS status;
+       uint32_t i;
+
+       if (psd->dacl == NULL) {
+               return NT_STATUS_OK;
+       }
+
+       for (i = 0; i < psd->dacl->num_aces; i++) {
+               /* MS NFS style mode/uid/gid */
+               if (!dom_sid_compare_domain(
+                               &global_sid_Unix_NFS,
+                               &psd->dacl->aces[i].trustee) == 0) {
+                       /* Normal ACE entry. */
+                       continue;
+               }
+
+               /*
+                * security_descriptor_dacl_del()
+                * *must* return NT_STATUS_OK as we know
+                * we have something to remove.
+                */
+
+               status = security_descriptor_dacl_del(psd,
+                               &psd->dacl->aces[i].trustee);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
+                               nt_errstr(status));
+                       return status;
+               }
+
+               /*
+                * security_descriptor_dacl_del() may delete more
+                * then one entry subsequent to this one if the
+                * SID matches, but we only need to ensure that
+                * we stay looking at the same element in the array.
+                */
+               i--;
+       }
+       return NT_STATUS_OK;
+}
+
 /* Search MS NFS style ACE with UNIX mode */
 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
                             files_struct *fsp,
@@ -2963,9 +3006,6 @@ static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
 {
        uint32_t i;
        struct fruit_config_data *config = NULL;
-       struct dom_sid sid;
-       NTSTATUS status = NT_STATUS_OK;
-       bool remove_ok = false;
 
        *pdo_chmod = false;
 
@@ -2999,40 +3039,7 @@ static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
         * fruit_fget_nt_acl().
         */
 
-       /* MS NFS style mode */
-       sid_compose(&sid, &global_sid_Unix_NFS_Mode,
-                   fsp->fsp_name->st.st_ex_mode);
-       status = security_descriptor_dacl_del(psd, &sid);
-       remove_ok = (NT_STATUS_IS_OK(status) ||
-                    NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND));
-       if (!remove_ok) {
-               DBG_WARNING("failed to remove MS NFS_mode style ACE\n");
-               return status;
-       }
-
-       /* MS NFS style uid */
-       sid_compose(&sid, &global_sid_Unix_NFS_Users,
-                   fsp->fsp_name->st.st_ex_uid);
-       status = security_descriptor_dacl_del(psd, &sid);
-       remove_ok = (NT_STATUS_IS_OK(status) ||
-                    NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND));
-       if (!remove_ok) {
-               DBG_WARNING("failed to remove MS NFS_users style ACE\n");
-               return status;
-       }
-
-       /* MS NFS style gid */
-       sid_compose(&sid, &global_sid_Unix_NFS_Groups,
-                   fsp->fsp_name->st.st_ex_gid);
-       status = security_descriptor_dacl_del(psd, &sid);
-       remove_ok = (NT_STATUS_IS_OK(status) ||
-                    NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND));
-       if (!remove_ok) {
-               DBG_WARNING("failed to remove MS NFS_groups style ACE\n");
-               return status;
-       }
-
-       return NT_STATUS_OK;
+       return remove_virtual_nfs_aces(psd);
 }
 
 /****************************************************************************
@@ -5728,7 +5735,6 @@ static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct 
*handle,
        struct security_ace ace;
        struct dom_sid sid;
        struct fruit_config_data *config;
-       bool remove_ok = false;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct fruit_config_data,
@@ -5750,18 +5756,16 @@ static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct 
*handle,
                return NT_STATUS_OK;
        }
 
+       /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
+       status = remove_virtual_nfs_aces(*ppdesc);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_WARNING("failed to remove MS NFS style ACEs\n");
+               return status;
+       }
+
        /* MS NFS style mode */
        sid_compose(&sid, &global_sid_Unix_NFS_Mode, 
fsp->fsp_name->st.st_ex_mode);
        init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
-
-       /* First remove any existing ACE's with this SID. */
-       status = security_descriptor_dacl_del(*ppdesc, &sid);
-       remove_ok = (NT_STATUS_IS_OK(status) ||
-                    NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND));
-       if (!remove_ok) {
-               DBG_WARNING("failed to remove MS NFS_mode style ACE\n");
-               return status;
-       }
        status = security_descriptor_dacl_add(*ppdesc, &ace);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(1,("failed to add MS NFS style ACE\n"));
@@ -5771,15 +5775,6 @@ static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct 
*handle,
        /* MS NFS style uid */
        sid_compose(&sid, &global_sid_Unix_NFS_Users, 
fsp->fsp_name->st.st_ex_uid);
        init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
-
-       /* First remove any existing ACE's with this SID. */
-       status = security_descriptor_dacl_del(*ppdesc, &sid);
-       remove_ok = (NT_STATUS_IS_OK(status) ||
-                    NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND));
-       if (!remove_ok) {
-               DBG_WARNING("failed to remove MS NFS_users style ACE\n");
-               return status;
-       }
        status = security_descriptor_dacl_add(*ppdesc, &ace);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(1,("failed to add MS NFS style ACE\n"));
@@ -5789,15 +5784,6 @@ static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct 
*handle,
        /* MS NFS style gid */
        sid_compose(&sid, &global_sid_Unix_NFS_Groups, 
fsp->fsp_name->st.st_ex_gid);
        init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
-
-       /* First remove any existing ACE's with this SID. */
-       status = security_descriptor_dacl_del(*ppdesc, &sid);
-       remove_ok = (NT_STATUS_IS_OK(status) ||
-                    NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND));
-       if (!remove_ok) {
-               DBG_WARNING("failed to remove MS NFS_groups style ACE\n");
-               return status;
-       }
        status = security_descriptor_dacl_add(*ppdesc, &ace);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(1,("failed to add MS NFS style ACE\n"));
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index 402f44f..c93abf5 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -503,7 +503,7 @@ for t in tests:
         plansmbtorture4testsuite(t, "nt4_dc", 
'//$SERVER_IP/vfs_fruit_metadata_stream -U$USERNAME%$PASSWORD 
--option=torture:localdir=$SELFTEST_PREFIX/nt4_dc/share 
--option=torture:share2=vfs_wo_fruit', 'metadata_stream')
         plansmbtorture4testsuite(t, "nt4_dc", 
'//$SERVER_IP/vfs_fruit_stream_depot -U$USERNAME%$PASSWORD 
--option=torture:localdir=$SELFTEST_PREFIX/nt4_dc/share 
--option=torture:share2=vfs_wo_fruit_stream_depot', 'streams_depot')
     elif t == "vfs.fruit_netatalk":
-        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit 
-U$USERNAME%$PASSWORD --option=torture:localdir=$SELFTEST_PREFIX/nt4_dc/share')
+        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/vfs_fruit_xattr 
-U$USERNAME%$PASSWORD --option=torture:localdir=$SELFTEST_PREFIX/nt4_dc/share')
     elif t == "vfs.fruit_timemachine":
         plansmbtorture4testsuite(t, "nt4_dc", 
'//$SERVER_IP/vfs_fruit_timemachine -U$USERNAME%$PASSWORD 
--option=torture:localdir=$SELFTEST_PREFIX/nt4_dc/share')
     elif t == "vfs.fruit_file_id":
diff --git a/source4/torture/vfs/fruit.c b/source4/torture/vfs/fruit.c
index d071cf6..65109cc 100644
--- a/source4/torture/vfs/fruit.c
+++ b/source4/torture/vfs/fruit.c
@@ -36,6 +36,10 @@
 #include "torture/smb2/proto.h"
 #include "torture/vfs/proto.h"
 #include "librpc/gen_ndr/ndr_ioctl.h"
+#include "libcli/security/dom_sid.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "libcli/security/secace.h"
+#include "libcli/security/security_descriptor.h"
 
 #define BASEDIR "vfs_fruit_dir"
 #define FNAME_CC_SRC "testfsctl.dat"
@@ -4426,6 +4430,172 @@ done:
 }
 
 /*
+ * Ensure this security descriptor has exactly one mode, uid
+ * and gid.
+ */
+
+static NTSTATUS check_nfs_sd(const struct security_descriptor *psd)
+{
+       uint32_t i;
+       bool got_one_mode = false;
+       bool got_one_uid = false;
+       bool got_one_gid = false;
+
+       if (psd->dacl == NULL) {
+               return NT_STATUS_INVALID_SECURITY_DESCR;
+       }
+
+       for (i = 0; i < psd->dacl->num_aces; i++) {
+               if (dom_sid_compare_domain(&global_sid_Unix_NFS_Mode,
+                                          &psd->dacl->aces[i].trustee) == 0) {
+                       if (got_one_mode == true) {
+                               /* Can't have more than one. */
+                               return NT_STATUS_INVALID_SECURITY_DESCR;
+                       }
+                       got_one_mode = true;
+               }
+       }
+       for (i = 0; i < psd->dacl->num_aces; i++) {
+               if (dom_sid_compare_domain(&global_sid_Unix_NFS_Users,
+                                          &psd->dacl->aces[i].trustee) == 0) {
+                       if (got_one_uid == true) {
+                               /* Can't have more than one. */
+                               return NT_STATUS_INVALID_SECURITY_DESCR;
+                       }
+                       got_one_uid = true;
+               }
+       }
+       for (i = 0; i < psd->dacl->num_aces; i++) {
+               if (dom_sid_compare_domain(&global_sid_Unix_NFS_Groups,
+                                          &psd->dacl->aces[i].trustee) == 0) {
+                       if (got_one_gid == true) {
+                               /* Can't have more than one. */
+                               return NT_STATUS_INVALID_SECURITY_DESCR;
+                       }
+                       got_one_gid = true;
+               }
+       }
+       /* Must have at least one of each. */
+       if (got_one_mode == false ||
+                       got_one_uid == false ||
+                       got_one_gid == false) {
+               return NT_STATUS_INVALID_SECURITY_DESCR;
+       }
+       return NT_STATUS_OK;
+}
+
+static bool test_nfs_aces(struct torture_context *tctx,
+                         struct smb2_tree *tree)
+{
+       TALLOC_CTX *mem_ctx = talloc_new(tctx);
+       struct security_ace ace;
+       struct dom_sid sid;
+       const char *fname = BASEDIR "\\nfs_aces.txt";
+       struct smb2_handle h = {{0}};
+       union smb_fileinfo finfo2;
+       union smb_setfileinfo set;
+       struct security_descriptor *psd = NULL;
+       NTSTATUS status;
+       bool ret = true;
+
+       ret = enable_aapl(tctx, tree);
+       torture_assert(tctx, ret == true, "enable_aapl failed");
+
+       /* clean slate ...*/
+       smb2_util_unlink(tree, fname);
+       smb2_deltree(tree, fname);
+       smb2_deltree(tree, BASEDIR);
+
+       status = torture_smb2_testdir(tree, BASEDIR, &h);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       smb2_util_close(tree, h);
+
+       /* Create a test file. */
+       status = torture_smb2_testfile_access(tree,
+                               fname,
+                               &h,
+                               SEC_STD_READ_CONTROL |
+                               SEC_STD_WRITE_DAC |
+                               SEC_RIGHTS_FILE_ALL);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Get the ACL. */
+       finfo2.query_secdesc.in.secinfo_flags =
+               SECINFO_OWNER |
+               SECINFO_GROUP |
+               SECINFO_DACL;
+       finfo2.generic.level = RAW_FILEINFO_SEC_DESC;
+       finfo2.generic.in.file.handle = h;
+       status = smb2_getinfo_file(tree, tctx, &finfo2);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       psd = finfo2.query_secdesc.out.sd;
+
+       /* Ensure we have only single mode/uid/gid NFS entries. */
+       status = check_nfs_sd(psd);
+       if (!NT_STATUS_IS_OK(status)) {
+               NDR_PRINT_DEBUG(
+                       security_descriptor,
+                       discard_const_p(struct security_descriptor, psd));
+       }
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Add a couple of extra NFS uids and gids. */
+       sid_compose(&sid, &global_sid_Unix_NFS_Users, 27);
+       init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
+       status = security_descriptor_dacl_add(psd, &ace);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       status = security_descriptor_dacl_add(psd, &ace);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       sid_compose(&sid, &global_sid_Unix_NFS_Groups, 300);
+       init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
+       status = security_descriptor_dacl_add(psd, &ace);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       status = security_descriptor_dacl_add(psd, &ace);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Now set on the file handle. */
+       set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+       set.set_secdesc.in.file.handle = h;
+       set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+       set.set_secdesc.in.sd = psd;
+       status = smb2_setinfo_file(tree, &set);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Get the ACL again. */
+       finfo2.query_secdesc.in.secinfo_flags =
+               SECINFO_OWNER |
+               SECINFO_GROUP |
+               SECINFO_DACL;
+       finfo2.generic.level = RAW_FILEINFO_SEC_DESC;
+       finfo2.generic.in.file.handle = h;
+       status = smb2_getinfo_file(tree, tctx, &finfo2);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       psd = finfo2.query_secdesc.out.sd;
+
+       /* Ensure we have only single mode/uid/gid NFS entries. */
+       status = check_nfs_sd(psd);
+       if (!NT_STATUS_IS_OK(status)) {
+               NDR_PRINT_DEBUG(
+                       security_descriptor,
+                       discard_const_p(struct security_descriptor, psd));
+       }
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+done:
+       if (!smb2_util_handle_empty(h)) {
+               smb2_util_close(tree, h);
+       }
+       smb2_util_unlink(tree, fname);
+       smb2_deltree(tree, fname);
+       smb2_deltree(tree, BASEDIR);
+       talloc_free(mem_ctx);
+       return ret;
+}
+
+/*
  * Note: This test depends on "vfs objects = catia fruit streams_xattr".  For
  * some tests torture must be run on the host it tests and takes an additional
  * argument with the local path to the share:
@@ -4465,6 +4635,7 @@ struct torture_suite *torture_vfs_fruit(TALLOC_CTX *ctx)
        torture_suite_add_1smb2_test(suite, "creating rsrc with read-only 
access", test_rfork_create_ro);
        torture_suite_add_1smb2_test(suite, "copy-chunk streams", 
test_copy_chunk_streams);
        torture_suite_add_1smb2_test(suite, "OS X AppleDouble file conversion", 
test_adouble_conversion);
+       torture_suite_add_1smb2_test(suite, "NFS ACE entries", test_nfs_aces);
 
        return suite;
 }


-- 
Samba Shared Repository

Reply via email to