[SSSD] [PATCHES] nss: check for Well-Known SIDs in SID based requests

Sumit Bose sbose at redhat.com
Mon Nov 11 14:01:09 UTC 2013


Hi,

with the patch set SSSD can resolve so called Well-Known SID, i.e. SIDs
with a special, hard-coded meaning.

Currently a man page entry for this feature is missing. I plan to add a
common section the sssd-ad and sssd-ipa man pages and a reference to
this section in the sssd.conf man page. The man reason to document it is
the introduction of reserved domain and user names. With the current
patches the reserved domain names are 'NT AUTHORITY' and 'BUILTIN'. The
reserved user names are 'NULL SID', 'Everyone', 'LOCAL', 'CONSOLE
LOGON', 'CREATOR OWNER', 'CREATOR GROUP', 'CREATOR OWNER SERVER',
'CREATOR GROUP SERVER' and 'OWNER RIGHTS'. The tries to mimic the
behaviour of Samba and AD which return a domain or authority name for
S-1-5-[0-20] and S-1-5-32-* SIDs but none for S-1-[0-3] SIDs. For the
latter it would be possible to add domain/authority names (Null, World,
Local, Creator Authority). The leaves us with no reserved user names but
more reserved domain names. Any opinions what would be the better
approach?

A second question is about translation. At least some of the names for
the Well-Knowns SIDs are translated in localized version of Windows.
Shall we do the same? If yes, is it possible to mark the strings  so
that the translators see that not a random translation is expected but
only the value Windows used in the corresponding localized version?

bye,
Sumit
-------------- next part --------------
From 01d5bfe4e38fa9fed2ea99bdb4fafb2a336d4ac4 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Thu, 7 Nov 2013 11:04:13 +0100
Subject: [PATCH 1/7] Add sss_packet_get_status()

sss_packet_get_status() will currently only used to test if packets have
the right status set.
---
 src/responder/common/responder_packet.c | 9 +++++++++
 src/responder/common/responder_packet.h | 1 +
 2 files changed, 10 insertions(+)

diff --git a/src/responder/common/responder_packet.c b/src/responder/common/responder_packet.c
index 58e7cbf..1a201c1 100644
--- a/src/responder/common/responder_packet.c
+++ b/src/responder/common/responder_packet.c
@@ -266,6 +266,15 @@ enum sss_cli_command sss_packet_get_cmd(struct sss_packet *packet)
     return (enum sss_cli_command)cmd;
 }
 
+uint32_t sss_packet_get_status(struct sss_packet *packet)
+{
+    uint32_t status;
+
+    SAFEALIGN_COPY_UINT32(&status, packet->buffer + SSS_PACKET_ERR_OFFSET,
+                          NULL);
+    return status;
+}
+
 void sss_packet_get_body(struct sss_packet *packet, uint8_t **body, size_t *blen)
 {
     *body = packet->buffer + SSS_PACKET_BODY_OFFSET;
diff --git a/src/responder/common/responder_packet.h b/src/responder/common/responder_packet.h
index 2bfdc8a..3ad0eee 100644
--- a/src/responder/common/responder_packet.h
+++ b/src/responder/common/responder_packet.h
@@ -37,6 +37,7 @@ int sss_packet_set_size(struct sss_packet *packet, size_t size);
 int sss_packet_recv(struct sss_packet *packet, int fd);
 int sss_packet_send(struct sss_packet *packet, int fd);
 enum sss_cli_command sss_packet_get_cmd(struct sss_packet *packet);
+uint32_t sss_packet_get_status(struct sss_packet *packet);
 void sss_packet_get_body(struct sss_packet *packet, uint8_t **body, size_t *blen);
 void sss_packet_set_error(struct sss_packet *packet, int error);
 
-- 
1.8.3.1

-------------- next part --------------
From bf732a38ca136bcf66a3b283149bdaa3f361b890 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Thu, 7 Nov 2013 17:38:54 +0100
Subject: [PATCH 2/7] sss_names_init: allow empty domain name

If no domain name is specified the global name pattern and regular
expression will be returned.
---
 src/tests/cmocka/test_utils.c | 122 ++++++++++++++++++++++++++++++++++++++++++
 src/util/usertools.c          |  34 ++++++------
 2 files changed, 141 insertions(+), 15 deletions(-)

diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c
index 76be129..f659b96 100644
--- a/src/tests/cmocka/test_utils.c
+++ b/src/tests/cmocka/test_utils.c
@@ -24,6 +24,10 @@
 
 #include "tests/cmocka/common_mock.h"
 
+#define TESTS_PATH "tests_utils"
+#define TEST_CONF_DB "test_utils_conf.ldb"
+#define TEST_SYSDB_FILE "cache_utils_test.ldb"
+
 #define DOM_COUNT 10
 #define DOMNAME_TMPL "name_%zu.dom"
 #define FLATNAME_TMPL "name_%zu"
@@ -430,6 +434,119 @@ void test_find_subdomain_by_sid_disabled(void **state)
     }
 }
 
+struct name_init_test_ctx {
+    struct confdb_ctx *confdb;
+};
+
+#define GLOBAL_FULL_NAME_FORMAT "%1$s@%2$s"
+#define GLOBAL_RE_EXPRESSION "(?P<name>[^@]+)@?(?P<domain>[^@]*$)"
+
+#define TEST_DOMAIN_NAME "test.dom"
+#define DOMAIN_FULL_NAME_FORMAT "%3$s\\%1$s"
+#define DOMAIN_RE_EXPRESSION "(((?P<domain>[^\\\\]+)\\\\(?P<name>.+$))|" \
+                             "((?P<name>[^@]+)@(?P<domain>.+$))|" \
+                             "(^(?P<name>[^@\\\\]+)$))"
+
+void confdb_test_setup(void **state)
+{
+    struct name_init_test_ctx *test_ctx;
+    char *conf_db = NULL;
+    char *dompath = NULL;
+    int ret;
+    const char *val[2];
+    val[1] = NULL;
+
+    assert_true(leak_check_setup());
+
+    test_ctx = talloc_zero(global_talloc_context, struct name_init_test_ctx);
+    assert_non_null(test_ctx);
+
+    conf_db = talloc_asprintf(test_ctx, "%s/%s", TESTS_PATH, TEST_CONF_DB);
+    assert_non_null(conf_db);
+
+    ret = confdb_init(test_ctx, &test_ctx->confdb, conf_db);
+    assert_int_equal(ret, EOK);
+
+    talloc_free(conf_db);
+
+    val[0] = TEST_DOMAIN_NAME;
+    ret = confdb_add_param(test_ctx->confdb, true,
+                           "config/sssd", "domains", val);
+    assert_int_equal(ret, EOK);
+
+    val[0] = GLOBAL_FULL_NAME_FORMAT;
+    ret = confdb_add_param(test_ctx->confdb, true,
+                           "config/sssd", "full_name_format", val);
+    assert_int_equal(ret, EOK);
+
+    val[0] = GLOBAL_RE_EXPRESSION;
+    ret = confdb_add_param(test_ctx->confdb, true,
+                           "config/sssd", "re_expression", val);
+    assert_int_equal(ret, EOK);
+
+    dompath = talloc_asprintf(test_ctx, "config/domain/%s", TEST_DOMAIN_NAME);
+    assert_non_null(dompath);
+
+    val[0] = "ldap";
+    ret = confdb_add_param(test_ctx->confdb, true,
+                           dompath, "id_provider", val);
+    assert_int_equal(ret, EOK);
+
+    val[0] = DOMAIN_FULL_NAME_FORMAT;
+    ret = confdb_add_param(test_ctx->confdb, true,
+                           dompath, "full_name_format", val);
+    assert_int_equal(ret, EOK);
+
+    val[0] = DOMAIN_RE_EXPRESSION;
+    ret = confdb_add_param(test_ctx->confdb, true,
+                           dompath, "re_expression", val);
+    assert_int_equal(ret, EOK);
+
+    talloc_free(dompath);
+
+    check_leaks_push(test_ctx);
+    *state = test_ctx;
+}
+
+void confdb_test_teardown(void **state)
+{
+    struct name_init_test_ctx *test_ctx;
+
+    test_ctx = talloc_get_type(*state, struct name_init_test_ctx);
+
+    assert_true(check_leaks_pop(test_ctx) == true);
+    talloc_free(test_ctx);
+    assert_true(leak_check_teardown());
+}
+
+void test_sss_names_init(void **state)
+{
+    struct name_init_test_ctx *test_ctx;
+    struct sss_names_ctx *names_ctx;
+    int ret;
+
+    test_ctx = talloc_get_type(*state, struct name_init_test_ctx);
+
+    ret = sss_names_init(test_ctx, test_ctx->confdb, NULL, &names_ctx);
+    assert_int_equal(ret, EOK);
+    assert_non_null(names_ctx);
+    assert_string_equal(names_ctx->re_pattern, GLOBAL_RE_EXPRESSION);
+    assert_string_equal(names_ctx->fq_fmt, GLOBAL_FULL_NAME_FORMAT"%3$s");
+    assert_int_equal(names_ctx->fq_flags, FQ_FMT_NAME|FQ_FMT_DOMAIN);
+
+    talloc_free(names_ctx);
+
+    ret = sss_names_init(test_ctx, test_ctx->confdb, TEST_DOMAIN_NAME,
+                         &names_ctx);
+    assert_int_equal(ret, EOK);
+    assert_non_null(names_ctx);
+    assert_string_equal(names_ctx->re_pattern, DOMAIN_RE_EXPRESSION);
+    assert_string_equal(names_ctx->fq_fmt, DOMAIN_FULL_NAME_FORMAT"%2$s");
+    assert_int_equal(names_ctx->fq_flags, FQ_FMT_NAME|FQ_FMT_FLAT_NAME);
+
+    talloc_free(names_ctx);
+}
+
 int main(int argc, const char *argv[])
 {
     poptContext pc;
@@ -457,6 +574,9 @@ int main(int argc, const char *argv[])
                                  setup_dom_list, teardown_dom_list),
         unit_test_setup_teardown(test_find_subdomain_by_name_disabled,
                                  setup_dom_list, teardown_dom_list),
+
+        unit_test_setup_teardown(test_sss_names_init,
+                                 confdb_test_setup, confdb_test_teardown),
     };
 
     /* Set debug level to invalid value so we can deside if -d 0 was used. */
@@ -479,6 +599,8 @@ int main(int argc, const char *argv[])
     /* Even though normally the tests should clean up after themselves
      * they might not after a failed run. Remove the old db to be sure */
     tests_set_cwd();
+    test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_SYSDB_FILE);
+    test_dom_suite_setup(TESTS_PATH);
 
     return run_tests(tests);
 }
diff --git a/src/util/usertools.c b/src/util/usertools.c
index 88f6480..e8e4dce 100644
--- a/src/util/usertools.c
+++ b/src/util/usertools.c
@@ -241,9 +241,9 @@ int sss_names_init(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb,
                    const char *domain, struct sss_names_ctx **out)
 {
     TALLOC_CTX *tmpctx = NULL;
-    char *conf_path;
-    char *re_pattern;
-    char *fq_fmt;
+    char *conf_path = NULL;
+    char *re_pattern = NULL;;
+    char *fq_fmt = NULL;
     int ret;
 
     tmpctx = talloc_new(NULL);
@@ -252,15 +252,17 @@ int sss_names_init(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb,
         goto done;
     }
 
-    conf_path = talloc_asprintf(tmpctx, CONFDB_DOMAIN_PATH_TMPL, domain);
-    if (conf_path == NULL) {
-        ret = ENOMEM;
-        goto done;
-    }
+    if (domain != NULL) {
+        conf_path = talloc_asprintf(tmpctx, CONFDB_DOMAIN_PATH_TMPL, domain);
+        if (conf_path == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
 
-    ret = confdb_get_string(cdb, tmpctx, conf_path,
-                            CONFDB_NAME_REGEX, NULL, &re_pattern);
-    if (ret != EOK) goto done;
+        ret = confdb_get_string(cdb, tmpctx, conf_path,
+                                CONFDB_NAME_REGEX, NULL, &re_pattern);
+        if (ret != EOK) goto done;
+    }
 
     /* If not found in the domain, look in globals */
     if (re_pattern == NULL) {
@@ -269,7 +271,7 @@ int sss_names_init(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb,
         if (ret != EOK) goto done;
     }
 
-    if (re_pattern == NULL) {
+    if (re_pattern == NULL && conf_path != NULL) {
         ret = get_id_provider_default_re(tmpctx, cdb, conf_path, &re_pattern);
         if (ret != EOK) {
             DEBUG(SSSDBG_OP_FAILURE, ("Failed to get provider default regular " \
@@ -295,9 +297,11 @@ int sss_names_init(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb,
 #endif
     }
 
-    ret = confdb_get_string(cdb, tmpctx, conf_path,
-                            CONFDB_FULL_NAME_FORMAT, NULL, &fq_fmt);
-    if (ret != EOK) goto done;
+    if (conf_path != NULL) {
+        ret = confdb_get_string(cdb, tmpctx, conf_path,
+                                CONFDB_FULL_NAME_FORMAT, NULL, &fq_fmt);
+        if (ret != EOK) goto done;
+    }
 
     /* If not found in the domain, look in globals */
     if (fq_fmt == NULL) {
-- 
1.8.3.1

-------------- next part --------------
From 5d6b44ed007f96ad8e99925e89e4ed2476bdb658 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Thu, 7 Nov 2013 18:39:49 +0100
Subject: [PATCH 3/7] nss: save global name configuration to the nss context

---
 src/responder/nss/nsssrv.c | 6 ++++++
 src/responder/nss/nsssrv.h | 1 +
 2 files changed, 7 insertions(+)

diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c
index 0baa808..4da774c 100644
--- a/src/responder/nss/nsssrv.c
+++ b/src/responder/nss/nsssrv.c
@@ -553,6 +553,12 @@ int nss_process_init(TALLOC_CTX *mem_ctx,
         goto fail;
     }
 
+    ret = sss_names_init(nctx, nctx->rctx->cdb, NULL, &nctx->global_names);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("sss_names_init failed.\n"));
+        goto fail;
+    }
+
     DEBUG(SSSDBG_TRACE_FUNC, ("NSS Initialization complete\n"));
 
     return EOK;
diff --git a/src/responder/nss/nsssrv.h b/src/responder/nss/nsssrv.h
index 2153d01..a365898 100644
--- a/src/responder/nss/nsssrv.h
+++ b/src/responder/nss/nsssrv.h
@@ -73,6 +73,7 @@ struct nss_ctx {
     struct sss_mc_ctx *grp_mc_ctx;
 
     struct sss_idmap_ctx *idmap_ctx;
+    struct sss_names_ctx *global_names;
 };
 
 struct nss_packet;
-- 
1.8.3.1

-------------- next part --------------
From a44b639bfc3bd866e3bb95c0429103935f7807de Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Fri, 8 Nov 2013 15:18:40 +0100
Subject: [PATCH 4/7] Add sss_tc_fqname2()

sss_tc_fqname2() is similar to sss_tc_fqname() but expects domain and
flat domain name as string arguments instead of a domain struct.
---
 src/util/usertools.c | 13 +++++++++++++
 src/util/util.h      |  9 +++++++++
 2 files changed, 22 insertions(+)

diff --git a/src/util/usertools.c b/src/util/usertools.c
index e8e4dce..cd57d0c 100644
--- a/src/util/usertools.c
+++ b/src/util/usertools.c
@@ -612,6 +612,19 @@ sss_tc_fqname(TALLOC_CTX *mem_ctx, struct sss_names_ctx *nctx,
                            safe_flat_name(nctx, domain));
 }
 
+char *
+sss_tc_fqname2(TALLOC_CTX *mem_ctx, struct sss_names_ctx *nctx,
+               const char *domain_name, const char *flat_dom_name,
+               const char *name)
+{
+    if (nctx == NULL) return NULL;
+
+    return talloc_asprintf(mem_ctx, nctx->fq_fmt,
+                           safe_fq_str(nctx, FQ_FMT_NAME, name),
+                           safe_fq_str(nctx, FQ_FMT_DOMAIN, domain_name),
+                           safe_fq_str(nctx, FQ_FMT_FLAT_NAME, flat_dom_name));
+}
+
 int
 sss_fqname(char *str, size_t size, struct sss_names_ctx *nctx,
            struct sss_domain_info *domain, const char *name)
diff --git a/src/util/util.h b/src/util/util.h
index e41b576..2f2eadb 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -374,6 +374,15 @@ char *
 sss_tc_fqname(TALLOC_CTX *mem_ctx, struct sss_names_ctx *nctx,
               struct sss_domain_info *domain, const char *name);
 
+/* Return fully-qualified name according to the fq_fmt. The name is allocated using
+ * talloc on top of mem_ctx. In contrast to sss_tc_fqname() sss_tc_fqname2()
+ * expects the domain and flat domain name as separate arguments.
+ */
+char *
+sss_tc_fqname2(TALLOC_CTX *mem_ctx, struct sss_names_ctx *nctx,
+               const char *dom_name, const char *flat_dom_name,
+               const char *name);
+
 /* Return fully-qualified name formatted according to the fq_fmt. The buffer in "str" is
  * "size" bytes long. Returns the number of bytes written on success or a negative
  * value of failure.
-- 
1.8.3.1

-------------- next part --------------
From e130c2f6a7c6eebc3a18ca37792f3f01af15513a Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Wed, 6 Nov 2013 09:58:35 +0100
Subject: [PATCH 5/7] Add utility to handle Well-Known SIDs

---
 Makefile.am                   |   3 +-
 src/tests/cmocka/test_utils.c | 111 ++++++++++++++++
 src/util/util.h               |   7 +
 src/util/well_known_sids.c    | 301 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 421 insertions(+), 1 deletion(-)
 create mode 100644 src/util/well_known_sids.c

diff --git a/Makefile.am b/Makefile.am
index a27bb51..244160d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -591,7 +591,8 @@ libsss_util_la_SOURCES = \
     src/util/util_errors.c \
     src/util/sss_ini.c \
     src/util/io.c \
-    src/util/util_sss_idmap.c
+    src/util/util_sss_idmap.c \
+    src/util/well_known_sids.c
 libsss_util_la_LIBADD = \
     $(SSSD_LIBS) \
     $(UNICODE_LIBS)
diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c
index f659b96..4566c49 100644
--- a/src/tests/cmocka/test_utils.c
+++ b/src/tests/cmocka/test_utils.c
@@ -547,6 +547,114 @@ void test_sss_names_init(void **state)
     talloc_free(names_ctx);
 }
 
+void test_well_known_sid_to_name(void **state)
+{
+    int ret;
+    const char *name;
+    const char *dom;
+
+    ret = well_known_sid_to_name(NULL, NULL, NULL);
+    assert_int_equal(ret, EINVAL);
+
+    ret = well_known_sid_to_name("abc", &dom, &name);
+    assert_int_equal(ret, EINVAL);
+
+    ret = well_known_sid_to_name("S-1", &dom, &name);
+    assert_int_equal(ret, EINVAL);
+
+    ret = well_known_sid_to_name("S-1-", &dom, &name);
+    assert_int_equal(ret, EINVAL);
+
+    ret = well_known_sid_to_name("S-1-0", &dom, &name);
+    assert_int_equal(ret, EINVAL);
+
+    ret = well_known_sid_to_name("S-1-0-", &dom, &name);
+    assert_int_equal(ret, EINVAL);
+
+    ret = well_known_sid_to_name("S-1-0-0", &dom, &name);
+    assert_int_equal(ret, EOK);
+    assert_null(dom);
+    assert_string_equal(name, "NULL SID");
+
+    ret = well_known_sid_to_name("S-1-0-0-", &dom, &name);
+    assert_int_equal(ret, EINVAL);
+
+    ret = well_known_sid_to_name("S-1-5", &dom, &name);
+    assert_int_equal(ret, EINVAL);
+
+    ret = well_known_sid_to_name("S-1-5-", &dom, &name);
+    assert_int_equal(ret, EINVAL);
+
+    ret = well_known_sid_to_name("S-1-5-6", &dom, &name);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(dom, "NT AUTHORITY");
+    assert_string_equal(name, "SERVICE");
+
+    ret = well_known_sid_to_name("S-1-5-6-", &dom, &name);
+    assert_int_equal(ret, EINVAL);
+
+    ret = well_known_sid_to_name("S-1-5-21", &dom, &name);
+    assert_int_equal(ret, EINVAL);
+
+    ret = well_known_sid_to_name("S-1-5-21-", &dom, &name);
+    assert_int_equal(ret, ENOENT);
+
+    ret = well_known_sid_to_name("S-1-5-21-abc", &dom, &name);
+    assert_int_equal(ret, ENOENT);
+
+    ret = well_known_sid_to_name("S-1-5-32", &dom, &name);
+    assert_int_equal(ret, EINVAL);
+
+    ret = well_known_sid_to_name("S-1-5-32-", &dom, &name);
+    assert_int_equal(ret, EINVAL);
+
+    ret = well_known_sid_to_name("S-1-5-32-551", &dom, &name);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(dom, "BUILTIN");
+    assert_string_equal(name, "Backup Operators");
+
+    ret = well_known_sid_to_name("S-1-5-32-551-", &dom, &name);
+    assert_int_equal(ret, EINVAL);
+
+}
+
+void test_name_to_well_known_sid(void **state)
+{
+    int ret;
+    const char *sid;
+
+    ret = name_to_well_known_sid(NULL, NULL, NULL);
+    assert_int_equal(ret, EINVAL);
+
+    ret = name_to_well_known_sid("abc", "def", &sid);
+    assert_int_equal(ret, ENOENT);
+
+    ret = name_to_well_known_sid("", "def", &sid);
+    assert_int_equal(ret, EINVAL);
+
+    ret = name_to_well_known_sid("BUILTIN", "def", &sid);
+    assert_int_equal(ret, EINVAL);
+
+    ret = name_to_well_known_sid("NT AUTHORITY", "def", &sid);
+    assert_int_equal(ret, EINVAL);
+
+    ret = name_to_well_known_sid("", "LOCAL", &sid);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(sid, "S-1-2-0");
+
+    ret = name_to_well_known_sid(NULL, "LOCAL", &sid);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(sid, "S-1-2-0");
+
+    ret = name_to_well_known_sid("BUILTIN", "Cryptographic Operators", &sid);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(sid, "S-1-5-32-569");
+
+    ret = name_to_well_known_sid("NT AUTHORITY", "DIALUP", &sid);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(sid, "S-1-5-1");
+}
+
 int main(int argc, const char *argv[])
 {
     poptContext pc;
@@ -577,6 +685,9 @@ int main(int argc, const char *argv[])
 
         unit_test_setup_teardown(test_sss_names_init,
                                  confdb_test_setup, confdb_test_teardown),
+
+        unit_test(test_well_known_sid_to_name),
+        unit_test(test_name_to_well_known_sid),
     };
 
     /* Set debug level to invalid value so we can deside if -d 0 was used. */
diff --git a/src/util/util.h b/src/util/util.h
index 2f2eadb..1451fe3 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -618,4 +618,11 @@ errno_t sss_br_lock_file(int fd, size_t start, size_t len,
 #define BUILD_WITH_PAC_RESPONDER false
 #endif
 
+/* from well_known_sids.c */
+errno_t well_known_sid_to_name(const char *sid, const char **dom,
+                               const char **name);
+
+errno_t name_to_well_known_sid(const char *dom, const char *name,
+                               const char **sid);
+
 #endif /* __SSSD_UTIL_H__ */
diff --git a/src/util/well_known_sids.c b/src/util/well_known_sids.c
new file mode 100644
index 0000000..12027d4
--- /dev/null
+++ b/src/util/well_known_sids.c
@@ -0,0 +1,301 @@
+/*
+    Authors:
+        Sumit Bose <sbose at redhat.com>
+
+    Copyright (C) 2013 Red Hat
+
+    Translate well-known SIDs to domains and names
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "util/strtonum.h"
+
+/* Well-Known SIDs are documented in section 2.4.2.4 "Well-Known SID
+ * Structures" of the "[MS-DTYP]: Windows Data Types" document. */
+
+#define DOM_SID_PREFIX "S-1-5-21-"
+#define DOM_SID_PREFIX_LEN (sizeof(DOM_SID_PREFIX) - 1)
+
+#define BUILTIN_SID_PREFIX "S-1-5-32-"
+#define BUILTIN_SID_PREFIX_LEN (sizeof(BUILTIN_SID_PREFIX) - 1)
+#define BUILTIN_DOM_NAME "BUILTIN"
+
+#define NT_SID_PREFIX "S-1-5-"
+#define NT_SID_PREFIX_LEN (sizeof(NT_SID_PREFIX) - 1)
+#define NT_DOM_NAME "NT AUTHORITY"
+
+#define SPECIAL_SID_PREFIX "S-1-"
+#define SPECIAL_SID_PREFIX_LEN (sizeof(SPECIAL_SID_PREFIX) - 1)
+
+#define NT_MAP_ENTRY(rid, name) {rid, NT_SID_PREFIX #rid, name}
+#define BUILTIN_MAP_ENTRY(rid, name) {rid, BUILTIN_SID_PREFIX #rid, name}
+#define SPECIAL_MAP_ENTRY(id_auth, rid, name) \
+        {(48 + id_auth), (48 + rid), SPECIAL_SID_PREFIX #id_auth "-" #rid, name}
+
+struct rid_sid_name {
+    uint32_t rid;
+    const char *sid;
+    const char *name;
+};
+
+struct special_map {
+    const char id_auth;
+    char rid;
+    const char *sid;
+    const char *name;
+};
+
+struct rid_sid_name builtin_map[] = {
+    BUILTIN_MAP_ENTRY(544, "Administrators"),
+    BUILTIN_MAP_ENTRY(545, "Users"),
+    BUILTIN_MAP_ENTRY(546, "Guests"),
+    BUILTIN_MAP_ENTRY(547, "Power Users"),
+    BUILTIN_MAP_ENTRY(548, "Account Operators"),
+    BUILTIN_MAP_ENTRY(549, "Server Operators"),
+    BUILTIN_MAP_ENTRY(550, "Print Operators"),
+    BUILTIN_MAP_ENTRY(551, "Backup Operators"),
+    BUILTIN_MAP_ENTRY(552, "Replicator"),
+    BUILTIN_MAP_ENTRY(554, "Pre-Windows 2000 Compatible Access"),
+    BUILTIN_MAP_ENTRY(555, "Remote Desktop Users"),
+    BUILTIN_MAP_ENTRY(556, "Network Configuration Operators"),
+    BUILTIN_MAP_ENTRY(557, "Incoming Forest Trust Builders"),
+    BUILTIN_MAP_ENTRY(558, "Performance Monitor Users"),
+    BUILTIN_MAP_ENTRY(559, "Performance Log Users"),
+    BUILTIN_MAP_ENTRY(560, "Windows Authorization Access Group"),
+    BUILTIN_MAP_ENTRY(561, "Terminal Server License Servers"),
+    BUILTIN_MAP_ENTRY(562, "Distributed COM Users"),
+    BUILTIN_MAP_ENTRY(568, "IIS_IUSRS"),
+    BUILTIN_MAP_ENTRY(569, "Cryptographic Operators"),
+    BUILTIN_MAP_ENTRY(573, "Event Log Readers"),
+    BUILTIN_MAP_ENTRY(574, "Certificate Service DCOM Access"),
+    BUILTIN_MAP_ENTRY(575, "RDS Remote Access Servers"),
+    BUILTIN_MAP_ENTRY(576, "RDS Endpoint Servers"),
+    BUILTIN_MAP_ENTRY(577, "RDS Management Servers"),
+    BUILTIN_MAP_ENTRY(578, "Hyper-V Admins"),
+    BUILTIN_MAP_ENTRY(579, "Access Control Assistance OPS"),
+    BUILTIN_MAP_ENTRY(580, "Remote Management Users"),
+
+    {UINT32_MAX, NULL, NULL}
+};
+
+struct rid_sid_name nt_map[] = {
+    NT_MAP_ENTRY(1, "DIALUP"),
+    NT_MAP_ENTRY(2, "NETWORK"),
+    NT_MAP_ENTRY(3, "BATCH"),
+    NT_MAP_ENTRY(4, "INTERACTIVE"),
+    NT_MAP_ENTRY(6, "SERVICE"),
+    NT_MAP_ENTRY(7, "ANONYMOUS LOGON"),
+    NT_MAP_ENTRY(8, "PROXY"),
+    NT_MAP_ENTRY(9, "ENTERPRISE DOMAIN CONTROLLERS"),
+    NT_MAP_ENTRY(10, "SELF"),
+    NT_MAP_ENTRY(11, "Authenticated Users"),
+    NT_MAP_ENTRY(12, "RESTRICTED"),
+    NT_MAP_ENTRY(13, "TERMINAL SERVER USER"),
+    NT_MAP_ENTRY(14, "REMOTE INTERACTIVE LOGON"),
+    NT_MAP_ENTRY(15, "This Organization"),
+    NT_MAP_ENTRY(17, "IUSR"),
+    NT_MAP_ENTRY(18, "SYSTEM"),
+    NT_MAP_ENTRY(19, "LOCAL SERVICE"),
+    NT_MAP_ENTRY(20, "NETWORK SERVICE"),
+
+    {UINT32_MAX, NULL, NULL}
+};
+
+/* The code to handle the SIDs of the Null, World, Local and Creator
+ * Authorities (id_auth=0,1,2,3 respectively) is optimized to handle only
+ * single digit id_auth and rid. */
+
+struct special_map sp_map[] = {
+    SPECIAL_MAP_ENTRY(0, 0, "NULL SID"),
+    SPECIAL_MAP_ENTRY(1, 0, "Everyone"),
+    SPECIAL_MAP_ENTRY(2, 0, "LOCAL"),
+    SPECIAL_MAP_ENTRY(2, 1, "CONSOLE LOGON"),
+    SPECIAL_MAP_ENTRY(3, 0, "CREATOR OWNER"),
+    SPECIAL_MAP_ENTRY(3, 1, "CREATOR GROUP"),
+    SPECIAL_MAP_ENTRY(3, 2, "CREATOR OWNER SERVER"),
+    SPECIAL_MAP_ENTRY(3, 3, "CREATOR GROUP SERVER"),
+    SPECIAL_MAP_ENTRY(3, 4, "OWNER RIGHTS"),
+
+    {'\0', '\0', NULL, NULL}
+};
+
+static errno_t handle_special_sids(const char *sid, const char **dom,
+                                   const char **name)
+{
+    size_t c;
+
+    if (!isdigit(sid[SPECIAL_SID_PREFIX_LEN])
+            || sid[SPECIAL_SID_PREFIX_LEN + 1] != '-'
+            || !isdigit(sid[SPECIAL_SID_PREFIX_LEN + 2])
+            || sid[SPECIAL_SID_PREFIX_LEN + 3] != '\0' ) {
+        return EINVAL;
+    }
+
+    for (c = 0; sp_map[c].name != NULL; c++) {
+        if (sid[SPECIAL_SID_PREFIX_LEN] == sp_map[c].id_auth
+                && sid[SPECIAL_SID_PREFIX_LEN + 2] == sp_map[c].rid) {
+            *name = sp_map[c].name;
+            *dom = NULL;
+            return EOK;
+        }
+    }
+
+    return EINVAL;
+}
+
+static errno_t handle_special_names(const char *name, const char **sid)
+{
+    size_t c;
+
+    for (c = 0; sp_map[c].name != NULL; c++) {
+        if (strcmp(name, sp_map[c].name) == 0) {
+            *sid = sp_map[c].sid;
+            return EOK;
+        }
+    }
+
+    return EINVAL;
+}
+
+static errno_t handle_rid_to_name_map(const char *sid, size_t prefix_len,
+                                      struct rid_sid_name *map,
+                                      const char* dom_name, const char **dom,
+                                      const char **name)
+{
+    uint32_t rid;
+    char *endptr;
+    size_t c;
+
+    errno = 0;
+    rid = (uint32_t) strtouint32(sid + prefix_len, &endptr, 10);
+    if (errno != 0 || *endptr != '\0') {
+        return EINVAL;
+    }
+
+    for (c = 0; map[c].name != NULL; c++) {
+        if (rid == map[c].rid) {
+            *name = map[c].name;
+            *dom = dom_name;
+            return EOK;
+        }
+    }
+
+    return EINVAL;
+}
+
+static errno_t handle_name_to_sid_map(const char *name,
+                                      struct rid_sid_name *map,
+                                      const char **sid)
+{
+    size_t c;
+
+    for (c = 0; map[c].name != NULL; c++) {
+        if (strcmp(name, map[c].name) == 0) {
+            *sid = map[c].sid;
+            return EOK;
+        }
+    }
+
+    return EINVAL;
+}
+
+static errno_t handle_nt_sids(const char *sid, const char **dom,
+                              const char **name)
+{
+    return handle_rid_to_name_map(sid, NT_SID_PREFIX_LEN, nt_map, NT_DOM_NAME,
+                                  dom, name);
+}
+
+static errno_t handle_nt_names(const char *name, const char **sid)
+{
+    return handle_name_to_sid_map(name, nt_map, sid);
+}
+
+static errno_t handle_builtin_sids(const char *sid, const char **dom,
+                                   const char **name)
+{
+    return handle_rid_to_name_map(sid, BUILTIN_SID_PREFIX_LEN, builtin_map,
+                                  BUILTIN_DOM_NAME, dom, name);
+}
+
+static errno_t handle_builtin_names(const char *name, const char **sid)
+{
+    return handle_name_to_sid_map(name, builtin_map, sid);
+}
+
+errno_t well_known_sid_to_name(const char *sid, const char **dom,
+                               const char **name)
+{
+    int ret;
+
+    if (sid == NULL || dom == NULL || name == NULL) {
+        return EINVAL;
+    }
+
+    if (strncmp(sid, DOM_SID_PREFIX, DOM_SID_PREFIX_LEN) == 0) {
+        ret = ENOENT;
+    } else if (strncmp(sid, BUILTIN_SID_PREFIX, BUILTIN_SID_PREFIX_LEN) == 0) {
+        ret = handle_builtin_sids(sid, dom, name);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("handle_builtin_sids failed.\n"));
+        }
+    } else if (strncmp(sid, NT_SID_PREFIX, NT_SID_PREFIX_LEN) == 0) {
+        ret = handle_nt_sids(sid, dom, name);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("handle_nt_sids failed.\n"));
+        }
+    } else if (strncmp(sid, SPECIAL_SID_PREFIX, SPECIAL_SID_PREFIX_LEN) == 0) {
+        ret = handle_special_sids(sid, dom, name);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("handle_special_sids failed.\n"));
+        }
+    } else {
+        ret = EINVAL;
+    }
+
+    return ret;
+}
+
+errno_t name_to_well_known_sid(const char *dom, const char *name,
+                               const char **sid)
+{
+    int ret;
+
+    if (sid == NULL || name == NULL) {
+        return EINVAL;
+    }
+
+    if (dom == NULL || *dom == '\0') {
+        ret = handle_special_names(name, sid);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("handle_special_name failed.\n"));
+        }
+    } else if (strcmp(dom, NT_DOM_NAME) == 0) {
+        ret = handle_nt_names(name, sid);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("handle_nt_name failed.\n"));
+        }
+    } else if (strcmp(dom, BUILTIN_DOM_NAME) == 0) {
+        ret = handle_builtin_names(name, sid);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("handle_builtin_name failed.\n"));
+        }
+    } else {
+        ret = ENOENT;
+    }
+
+    return ret;
+}
-- 
1.8.3.1

-------------- next part --------------
From f2ccfc49004835eb61cada0721695a6dee574131 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Thu, 7 Nov 2013 11:08:29 +0100
Subject: [PATCH 6/7] nss-srv-tests: check packet status

Besides checking the content of output packets it might also be useful
to check the status. This is e.g. important if no results are expected
and the status should be set to ENOENT and not to any other error code.
---
 src/tests/cmocka/test_nss_srv.c | 64 ++++++++++++++++++++++++++++++++---------
 1 file changed, 50 insertions(+), 14 deletions(-)

diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
index e2e81a6..a6c2068 100644
--- a/src/tests/cmocka/test_nss_srv.c
+++ b/src/tests/cmocka/test_nss_srv.c
@@ -96,7 +96,7 @@ void __wrap_sss_packet_get_body(struct sss_packet *packet,
 }
 
 /* Mock returning result to client. Terminate the unit test instead. */
-typedef int (*cmd_cb_fn_t)(uint8_t *, size_t );
+typedef int (*cmd_cb_fn_t)(uint32_t, uint8_t *, size_t );
 
 static void set_cmd_cb(cmd_cb_fn_t fn)
 {
@@ -114,7 +114,8 @@ void __wrap_sss_cmd_done(struct cli_ctx *cctx, void *freectx)
 
     __real_sss_packet_get_body(packet, &body, &blen);
 
-    nss_test_ctx->tctx->error = check_cb(body, blen);
+    nss_test_ctx->tctx->error = check_cb(sss_packet_get_status(packet),
+                                         body, blen);
     nss_test_ctx->tctx->done = true;
 }
 
@@ -248,11 +249,13 @@ static int parse_group_packet(uint8_t *body, size_t blen, struct group *gr, uint
  * not be called and test_nss_getpwnam_check will make sure the user is
  * the same as the test entered before starting
  */
-static int test_nss_getpwnam_check(uint8_t *body, size_t blen)
+static int test_nss_getpwnam_check(uint32_t status, uint8_t *body, size_t blen)
 {
     struct passwd pwd;
     errno_t ret;
 
+    assert_int_equal(status, EOK);
+
     ret = parse_user_packet(body, blen, &pwd);
     assert_int_equal(ret, EOK);
 
@@ -345,11 +348,14 @@ static int test_nss_getpwnam_search_acct_cb(void *pvt)
     return EOK;
 }
 
-static int test_nss_getpwnam_search_check(uint8_t *body, size_t blen)
+static int test_nss_getpwnam_search_check(uint32_t status,
+                                          uint8_t *body, size_t blen)
 {
     struct passwd pwd;
     errno_t ret;
 
+    assert_int_equal(status, EOK);
+
     ret = parse_user_packet(body, blen, &pwd);
     assert_int_equal(ret, EOK);
 
@@ -413,11 +419,14 @@ static int test_nss_getpwnam_update_acct_cb(void *pvt)
     return EOK;
 }
 
-static int test_nss_getpwnam_update_check(uint8_t *body, size_t blen)
+static int test_nss_getpwnam_update_check(uint32_t status,
+                                          uint8_t *body, size_t blen)
 {
     struct passwd pwd;
     errno_t ret;
 
+    assert_int_equal(status, EOK);
+
     ret = parse_user_packet(body, blen, &pwd);
     assert_int_equal(ret, EOK);
 
@@ -476,11 +485,14 @@ void test_nss_getpwnam_update(void **state)
 /* Check that a FQDN is returned if the domain is FQDN-only and a
  * FQDN is requested
  */
-static int test_nss_getpwnam_check_fqdn(uint8_t *body, size_t blen)
+static int test_nss_getpwnam_check_fqdn(uint32_t status,
+                                        uint8_t *body, size_t blen)
 {
     struct passwd pwd;
     errno_t ret;
 
+    assert_int_equal(status, EOK);
+
     nss_test_ctx->cctx->rctx->domains[0].fqnames = false;
 
     ret = parse_user_packet(body, blen, &pwd);
@@ -525,11 +537,14 @@ void test_nss_getpwnam_fqdn(void **state)
  * Check that FQDN processing is able to handle arbitrarily sized
  * delimeter
  */
-static int test_nss_getpwnam_check_resize_fqdn(uint8_t *body, size_t blen)
+static int test_nss_getpwnam_check_resize_fqdn(uint32_t status,
+                                               uint8_t *body, size_t blen)
 {
     struct passwd pwd;
     errno_t ret;
 
+    assert_int_equal(status, EOK);
+
     nss_test_ctx->cctx->rctx->domains[0].fqnames = false;
 
     ret = parse_user_packet(body, blen, &pwd);
@@ -622,7 +637,8 @@ static int test_nss_getgrnam_check(struct group *expected, struct group *gr, con
     return EOK;
 }
 
-static int test_nss_getgrnam_no_members_check(uint8_t *body, size_t blen)
+static int test_nss_getgrnam_no_members_check(uint32_t status,
+                                              uint8_t *body, size_t blen)
 {
     int ret;
     uint32_t nmem;
@@ -634,6 +650,8 @@ static int test_nss_getgrnam_no_members_check(uint8_t *body, size_t blen)
         .gr_mem = NULL,
     };
 
+    assert_int_equal(status, EOK);
+
     ret = parse_group_packet(body, blen, &gr, &nmem);
     assert_int_equal(ret, EOK);
     assert_int_equal(nmem, 0);
@@ -673,7 +691,8 @@ void test_nss_getgrnam_no_members(void **state)
     assert_int_equal(ret, EOK);
 }
 
-static int test_nss_getgrnam_members_check(uint8_t *body, size_t blen)
+static int test_nss_getgrnam_members_check(uint32_t status,
+                                           uint8_t *body, size_t blen)
 {
     int ret;
     uint32_t nmem;
@@ -686,6 +705,8 @@ static int test_nss_getgrnam_members_check(uint8_t *body, size_t blen)
         .gr_mem = discard_const(exp_members)
     };
 
+    assert_int_equal(status, EOK);
+
     ret = parse_group_packet(body, blen, &gr, &nmem);
     assert_int_equal(ret, EOK);
     assert_int_equal(nmem, 2);
@@ -751,7 +772,8 @@ void test_nss_getgrnam_members(void **state)
     assert_int_equal(ret, EOK);
 }
 
-static int test_nss_getgrnam_members_check_fqdn(uint8_t *body, size_t blen)
+static int test_nss_getgrnam_members_check_fqdn(uint32_t status,
+                                                uint8_t *body, size_t blen)
 {
     int ret;
     uint32_t nmem;
@@ -765,6 +787,8 @@ static int test_nss_getgrnam_members_check_fqdn(uint8_t *body, size_t blen)
         .gr_mem = discard_const(exp_members)
     };
 
+    assert_int_equal(status, EOK);
+
     ret = parse_group_packet(body, blen, &gr, &nmem);
     assert_int_equal(ret, EOK);
     assert_int_equal(nmem, 2);
@@ -802,7 +826,8 @@ void test_nss_getgrnam_members_fqdn(void **state)
     assert_int_equal(ret, EOK);
 }
 
-static int test_nss_getgrnam_members_check_subdom(uint8_t *body, size_t blen)
+static int test_nss_getgrnam_members_check_subdom(uint32_t status,
+                                                  uint8_t *body, size_t blen)
 {
     int ret;
     uint32_t nmem;
@@ -816,6 +841,8 @@ static int test_nss_getgrnam_members_check_subdom(uint8_t *body, size_t blen)
         .gr_mem = discard_const(exp_members)
     };
 
+    assert_int_equal(status, EOK);
+
     ret = parse_group_packet(body, blen, &gr, &nmem);
     assert_int_equal(ret, EOK);
     assert_int_equal(nmem, 2);
@@ -892,7 +919,8 @@ void test_nss_getgrnam_members_subdom(void **state)
     assert_int_equal(ret, EOK);
 }
 
-static int test_nss_getgrnam_check_mix_dom(uint8_t *body, size_t blen)
+static int test_nss_getgrnam_check_mix_dom(uint32_t status,
+                                           uint8_t *body, size_t blen)
 {
     int ret;
     uint32_t nmem;
@@ -907,6 +935,8 @@ static int test_nss_getgrnam_check_mix_dom(uint8_t *body, size_t blen)
         .gr_mem = discard_const(exp_members)
     };
 
+    assert_int_equal(status, EOK);
+
     ret = parse_group_packet(body, blen, &gr, &nmem);
     assert_int_equal(ret, EOK);
     assert_int_equal(nmem, 3);
@@ -952,7 +982,8 @@ void test_nss_getgrnam_mix_dom(void **state)
     assert_int_equal(ret, EOK);
 }
 
-static int test_nss_getgrnam_check_mix_dom_fqdn(uint8_t *body, size_t blen)
+static int test_nss_getgrnam_check_mix_dom_fqdn(uint32_t status,
+                                                uint8_t *body, size_t blen)
 {
     int ret;
     uint32_t nmem;
@@ -967,6 +998,8 @@ static int test_nss_getgrnam_check_mix_dom_fqdn(uint8_t *body, size_t blen)
         .gr_mem = discard_const(exp_members)
     };
 
+    assert_int_equal(status, EOK);
+
     ret = parse_group_packet(body, blen, &gr, &nmem);
     assert_int_equal(ret, EOK);
     assert_int_equal(nmem, 3);
@@ -1001,7 +1034,8 @@ void test_nss_getgrnam_mix_dom_fqdn(void **state)
     assert_int_equal(ret, EOK);
 }
 
-static int test_nss_getgrnam_check_mix_subdom(uint8_t *body, size_t blen)
+static int test_nss_getgrnam_check_mix_subdom(uint32_t status,
+                                              uint8_t *body, size_t blen)
 {
     int ret;
     uint32_t nmem;
@@ -1016,6 +1050,8 @@ static int test_nss_getgrnam_check_mix_subdom(uint8_t *body, size_t blen)
         .gr_mem = discard_const(exp_members)
     };
 
+    assert_int_equal(status, EOK);
+
     ret = parse_group_packet(body, blen, &gr, &nmem);
     assert_int_equal(ret, EOK);
     assert_int_equal(nmem, 3);
-- 
1.8.3.1

-------------- next part --------------
From ee6b0f3f1123c26179786c946b1530decd9c91ea Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Thu, 7 Nov 2013 11:09:35 +0100
Subject: [PATCH 7/7] nss: check for Well-Known SIDs in SID based requests

---
 src/responder/nss/nsssrv_cmd.c  | 134 ++++++++++++++++++++++++++++
 src/tests/cmocka/test_nss_srv.c | 192 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 326 insertions(+)

diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index b80b3ef..80b44e3 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -975,6 +975,57 @@ done:
     }
 }
 
+static int nss_check_name_of_well_known_sid(struct nss_cmd_ctx *cmdctx,
+                                            const char *full_name)
+{
+    char *wk_name = NULL;
+    char *wk_dom_name = NULL;
+    const char *wk_sid;
+    int ret;
+    struct sized_string sid;
+    uint8_t *body;
+    size_t blen;
+    struct cli_ctx *cctx;
+    struct nss_ctx *nss_ctx;
+
+    nss_ctx = talloc_get_type(cmdctx->cctx->rctx->pvt_ctx, struct nss_ctx);
+    ret = sss_parse_name(cmdctx, nss_ctx->global_names, full_name,
+                         &wk_dom_name, &wk_name);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sss_parse_name failed.\n"));
+        return ret;
+    }
+
+    ret = name_to_well_known_sid(wk_dom_name, wk_name, &wk_sid);
+    talloc_free(wk_dom_name);
+    talloc_free(wk_name);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_TRACE_ALL, ("Name [%s] is not the name of a " \
+                                 "Well-Known SID.\n", full_name));
+        return ret;
+    }
+
+    to_sized_string(&sid, wk_sid);
+
+    cctx = cmdctx->cctx;
+    ret = sss_packet_new(cctx->creq, sid.len + 3 * sizeof(uint32_t),
+                         sss_packet_get_cmd(cctx->creq->in),
+                         &cctx->creq->out);
+    if (ret != EOK) {
+        return ENOMEM;
+    }
+
+    sss_packet_get_body(cctx->creq->out, &body, &blen);
+    ((uint32_t *)body)[0] = 1; /* num results */
+    ((uint32_t *)body)[1] = 0; /* reserved */
+    ((uint32_t *)body)[2] = (uint32_t) SSS_ID_TYPE_GID;
+    memcpy(&body[3*sizeof(uint32_t)], sid.str, sid.len);
+
+    sss_packet_set_error(cctx->creq->out, EOK);
+    sss_cmd_done(cctx, cmdctx);
+    return EOK;
+}
+
 static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx);
 static void nss_cmd_getbynam_done(struct tevent_req *req);
 static int nss_cmd_getpwnam(struct cli_ctx *cctx)
@@ -1039,6 +1090,20 @@ static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx)
     DEBUG(SSSDBG_TRACE_FUNC, ("Running command [%d] with input [%s].\n",
                                dctx->cmdctx->cmd, rawname));
 
+    if (dctx->cmdctx->cmd == SSS_NSS_GETSIDBYNAME) {
+        ret = nss_check_name_of_well_known_sid(cmdctx, rawname);
+        if (ret != ENOENT) {
+            if (ret == EOK) {
+                DEBUG(SSSDBG_TRACE_ALL, ("Name [%s] is the name of a " \
+                                         "Well-Known SID.\n", rawname));
+            } else {
+                DEBUG(SSSDBG_OP_FAILURE,
+                      ("nss_check_name_of_well_known_sid failed.\n"));
+            }
+            goto done;
+        }
+    }
+
     /* We need to attach to subdomain request, if the first one is not
      * finished yet. We may not be able to lookup object in AD otherwise. */
     if (cctx->rctx->get_domains_last_call.tv_sec == 0) {
@@ -4292,6 +4357,64 @@ static errno_t nss_cmd_getbysid_send_reply(struct nss_dom_ctx *dctx)
     return EOK;
 }
 
+static int nss_check_well_known_sid(struct nss_cmd_ctx *cmdctx)
+{
+    const char *wk_name;
+    const char *wk_dom_name;
+    int ret;
+    char *fq_name = NULL;
+    struct sized_string name;
+    uint8_t *body;
+    size_t blen;
+    struct cli_ctx *cctx;
+    struct nss_ctx *nss_ctx;
+
+    ret = well_known_sid_to_name(cmdctx->secid, &wk_dom_name, &wk_name);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_TRACE_ALL, ("SID [%s] is not a Well-Known SID.\n",
+                                 cmdctx->secid));
+        return ret;
+    }
+
+    if (cmdctx->cmd != SSS_NSS_GETNAMEBYSID) {
+        DEBUG(SSSDBG_TRACE_ALL,
+              ("Well-Known SIDs can only be translated to names.\n"));
+        return EINVAL;
+    }
+
+    if (wk_dom_name != NULL) {
+        nss_ctx = talloc_get_type(cmdctx->cctx->rctx->pvt_ctx, struct nss_ctx);
+        fq_name = sss_tc_fqname2(cmdctx, nss_ctx->global_names,
+                                 wk_dom_name, wk_dom_name, wk_name);
+        if (fq_name == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, ("sss_tc_fqname2 failed.\n"));
+            return ENOMEM;
+        }
+        to_sized_string(&name, fq_name);
+    } else {
+        to_sized_string(&name, wk_name);
+    }
+
+    cctx = cmdctx->cctx;
+    ret = sss_packet_new(cctx->creq, name.len + 3 * sizeof(uint32_t),
+                         sss_packet_get_cmd(cctx->creq->in),
+                         &cctx->creq->out);
+    if (ret != EOK) {
+        talloc_free(fq_name);
+        return ENOMEM;
+    }
+
+    sss_packet_get_body(cctx->creq->out, &body, &blen);
+    ((uint32_t *)body)[0] = 1; /* num results */
+    ((uint32_t *)body)[1] = 0; /* reserved */
+    ((uint32_t *)body)[2] = (uint32_t) SSS_ID_TYPE_GID;
+    memcpy(&body[3*sizeof(uint32_t)], name.str, name.len);
+
+    sss_packet_set_error(cctx->creq->out, EOK);
+    sss_cmd_done(cctx, cmdctx);
+    return EOK;
+}
+
 static int nss_cmd_getbysid(enum sss_cli_command cmd, struct cli_ctx *cctx)
 {
 
@@ -4360,6 +4483,17 @@ static int nss_cmd_getbysid(enum sss_cli_command cmd, struct cli_ctx *cctx)
         goto done;
     }
 
+    ret = nss_check_well_known_sid(cmdctx);
+    if (ret != ENOENT) {
+        if (ret == EOK) {
+            DEBUG(SSSDBG_TRACE_ALL, ("SID [%s] is a Well-Known SID.\n",
+                                         cmdctx->secid));
+        } else {
+            DEBUG(SSSDBG_OP_FAILURE, ("nss_check_well_known_sid failed.\n"));
+        }
+        goto done;
+    }
+
     ret = responder_get_domain_by_id(cctx->rctx, cmdctx->secid, &dctx->domain);
     if (ret == EAGAIN || ret == ENOENT) {
         req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, NULL);
diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
index a6c2068..4fd5f70 100644
--- a/src/tests/cmocka/test_nss_srv.c
+++ b/src/tests/cmocka/test_nss_srv.c
@@ -30,6 +30,8 @@
 #include "responder/common/negcache.h"
 #include "responder/nss/nsssrv.h"
 #include "responder/nss/nsssrv_private.h"
+#include "sss_client/idmap/sss_nss_idmap.h"
+#include "util/util_sss_idmap.h"
 
 #define TESTS_PATH "tests_nss"
 #define TEST_CONF_DB "test_nss_conf.ldb"
@@ -58,6 +60,7 @@ mock_nctx(TALLOC_CTX *mem_ctx)
 {
     struct nss_ctx *nctx;
     errno_t ret;
+    enum idmap_error_code err;
 
     nctx = talloc_zero(mem_ctx, struct nss_ctx);
     if (!nctx) {
@@ -72,6 +75,14 @@ mock_nctx(TALLOC_CTX *mem_ctx)
     nctx->neg_timeout = 10;
     nctx->pwfield = discard_const("*");
 
+    err = sss_idmap_init(sss_idmap_talloc, nctx, sss_idmap_talloc_free,
+                         &nctx->idmap_ctx);
+    if (err != IDMAP_SUCCESS) {
+        DEBUG(SSSDBG_FATAL_FAILURE, ("sss_idmap_init failed.\n"));
+        talloc_free(nctx);
+        return NULL;
+    }
+
     return nctx;
 }
 
@@ -614,6 +625,11 @@ void test_nss_setup(struct sss_test_conf_param params[],
     nss_test_ctx->nctx = mock_nctx(nss_test_ctx);
     assert_non_null(nss_test_ctx->nctx);
 
+    ret = sss_names_init(nss_test_ctx->nctx, nss_test_ctx->tctx->confdb,
+                         NULL, &nss_test_ctx->nctx->global_names);
+    assert_int_equal(ret, EOK);
+    assert_non_null(nss_test_ctx->nctx->global_names);
+
     nss_test_ctx->rctx = mock_rctx(nss_test_ctx, nss_test_ctx->tctx->ev,
                                    nss_test_ctx->tctx->dom, nss_test_ctx->nctx);
     assert_non_null(nss_test_ctx->rctx);
@@ -1097,6 +1113,168 @@ void test_nss_getgrnam_mix_subdom(void **state)
     assert_int_equal(ret, EOK);
 }
 
+static int test_nss_well_known_sid_check(uint32_t status,
+                                         uint8_t *body, size_t blen)
+{
+    const char *name;
+    enum sss_id_type type;
+    size_t rp = 2 * sizeof(uint32_t);
+    char *expected_result = sss_mock_ptr_type(char *);
+
+    if (expected_result == NULL) {
+        assert_int_equal(status, EINVAL);
+        assert_int_equal(blen, 0);
+    } else {
+        assert_int_equal(status, EOK);
+
+        SAFEALIGN_COPY_UINT32(&type, body+rp, &rp);
+
+        name = (char *) body+rp;
+
+        assert_int_equal(type, SSS_ID_TYPE_GID);
+        assert_string_equal(name, expected_result);
+    }
+
+    return EOK;
+}
+
+void test_nss_well_known_getnamebysid(void **state)
+{
+    errno_t ret;
+
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER);
+    will_return(__wrap_sss_packet_get_body, "S-1-5-32-550");
+    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETNAMEBYSID);
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+    will_return(test_nss_well_known_sid_check, "Print Operators at BUILTIN");
+
+    set_cmd_cb(test_nss_well_known_sid_check);
+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETNAMEBYSID,
+                          nss_test_ctx->nss_cmds);
+    assert_int_equal(ret, EOK);
+
+    /* Wait until the test finishes with EOK */
+    ret = test_ev_loop(nss_test_ctx->tctx);
+    assert_int_equal(ret, EOK);
+}
+
+void test_nss_well_known_getnamebysid_special(void **state)
+{
+    errno_t ret;
+
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER);
+    will_return(__wrap_sss_packet_get_body, "S-1-2-0");
+    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETNAMEBYSID);
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+    will_return(test_nss_well_known_sid_check, "LOCAL");
+
+    set_cmd_cb(test_nss_well_known_sid_check);
+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETNAMEBYSID,
+                          nss_test_ctx->nss_cmds);
+    assert_int_equal(ret, EOK);
+
+    /* Wait until the test finishes with EOK */
+    ret = test_ev_loop(nss_test_ctx->tctx);
+    assert_int_equal(ret, EOK);
+}
+
+void test_nss_well_known_getnamebysid_non_existing(void **state)
+{
+    errno_t ret;
+
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER);
+    will_return(__wrap_sss_packet_get_body, "S-1-5-32-123");
+    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETNAMEBYSID);
+    will_return(test_nss_well_known_sid_check, NULL);
+
+    set_cmd_cb(test_nss_well_known_sid_check);
+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETNAMEBYSID,
+                          nss_test_ctx->nss_cmds);
+    assert_int_equal(ret, EOK);
+
+    /* Wait until the test finishes with EOK */
+    ret = test_ev_loop(nss_test_ctx->tctx);
+    assert_int_equal(ret, EOK);
+}
+
+void test_nss_well_known_getidbysid_failure(void **state)
+{
+    errno_t ret;
+
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER);
+    will_return(__wrap_sss_packet_get_body, "S-1-5-32-550");
+    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETIDBYSID);
+    will_return(test_nss_well_known_sid_check, NULL);
+
+    set_cmd_cb(test_nss_well_known_sid_check);
+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETIDBYSID,
+                          nss_test_ctx->nss_cmds);
+    assert_int_equal(ret, EOK);
+
+    /* Wait until the test finishes with EOK */
+    ret = test_ev_loop(nss_test_ctx->tctx);
+    assert_int_equal(ret, EOK);
+}
+
+void test_nss_well_known_getsidbyname(void **state)
+{
+    errno_t ret;
+
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER);
+    will_return(__wrap_sss_packet_get_body, "Cryptographic Operators at BUILTIN");
+    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETSIDBYNAME);
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+    will_return(test_nss_well_known_sid_check, "S-1-5-32-569");
+
+    set_cmd_cb(test_nss_well_known_sid_check);
+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETSIDBYNAME,
+                          nss_test_ctx->nss_cmds);
+    assert_int_equal(ret, EOK);
+
+    /* Wait until the test finishes with EOK */
+    ret = test_ev_loop(nss_test_ctx->tctx);
+    assert_int_equal(ret, EOK);
+}
+
+void test_nss_well_known_getsidbyname_nonexisting(void **state)
+{
+    errno_t ret;
+
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER);
+    will_return(__wrap_sss_packet_get_body, "Abc at BUILTIN");
+    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETSIDBYNAME);
+    will_return(test_nss_well_known_sid_check, NULL);
+
+    set_cmd_cb(test_nss_well_known_sid_check);
+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETSIDBYNAME,
+                          nss_test_ctx->nss_cmds);
+    assert_int_equal(ret, EOK);
+
+    /* Wait until the test finishes with EOK */
+    ret = test_ev_loop(nss_test_ctx->tctx);
+    assert_int_equal(ret, EOK);
+}
+
+void test_nss_well_known_getsidbyname_special(void **state)
+{
+    errno_t ret;
+
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER);
+    will_return(__wrap_sss_packet_get_body, "CREATOR OWNER");
+    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETSIDBYNAME);
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+    will_return(test_nss_well_known_sid_check, "S-1-3-0");
+
+    set_cmd_cb(test_nss_well_known_sid_check);
+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETSIDBYNAME,
+                          nss_test_ctx->nss_cmds);
+    assert_int_equal(ret, EOK);
+
+    /* Wait until the test finishes with EOK */
+    ret = test_ev_loop(nss_test_ctx->tctx);
+    assert_int_equal(ret, EOK);
+}
+
 void nss_test_setup(void **state)
 {
     struct sss_test_conf_param params[] = {
@@ -1199,6 +1377,20 @@ int main(int argc, const char *argv[])
                                  nss_subdom_test_setup, nss_test_teardown),
         unit_test_setup_teardown(test_nss_getgrnam_mix_subdom,
                                  nss_subdom_test_setup, nss_test_teardown),
+        unit_test_setup_teardown(test_nss_well_known_getnamebysid,
+                                 nss_test_setup, nss_test_teardown),
+        unit_test_setup_teardown(test_nss_well_known_getnamebysid_special,
+                                 nss_test_setup, nss_test_teardown),
+        unit_test_setup_teardown(test_nss_well_known_getnamebysid_non_existing,
+                                 nss_test_setup, nss_test_teardown),
+        unit_test_setup_teardown(test_nss_well_known_getidbysid_failure,
+                                 nss_test_setup, nss_test_teardown),
+        unit_test_setup_teardown(test_nss_well_known_getsidbyname,
+                                 nss_test_setup, nss_test_teardown),
+        unit_test_setup_teardown(test_nss_well_known_getsidbyname_nonexisting,
+                                 nss_test_setup, nss_test_teardown),
+        unit_test_setup_teardown(test_nss_well_known_getsidbyname_special,
+                                 nss_test_setup, nss_test_teardown),
     };
 
     /* Set debug level to invalid value so we can deside if -d 0 was used. */
-- 
1.8.3.1



More information about the sssd-devel mailing list