Blob Blame History Raw
From 9b6c5e3233cdbd5fce7b6beed0cf057fe3d5da61 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Fri, 24 Oct 2014 11:30:33 +0200
Subject: [PATCH 59/64] nss: return user_attributes in origbyname request

To allow IPA clients to offer special attributes of AD users form
trusted domain the extdom plugin on the IPA server must send them to the
clients. The extdom plugin already uses sss_nss_getorigbyname() to get
attributes like the SID and the user principal name. This patch adds the
attributes given by the NSS/IFP user_attributes option to the list of
attributes returned by sss_nss_getorigbyname().

Fixes https://fedorahosted.org/sssd/ticket/2464

Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
---
 src/responder/nss/nsssrv_cmd.c  | 120 +++++++++++++++++++++++++++---------
 src/tests/cmocka/test_nss_srv.c | 133 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 223 insertions(+), 30 deletions(-)

diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
index 4ac5eb91eab80291e60afad2bf9c65edfbc21e7d..b100aae08fc04ccf1a295745767c5445cf2e01be 100644
--- a/src/responder/nss/nsssrv_cmd.c
+++ b/src/responder/nss/nsssrv_cmd.c
@@ -4148,18 +4148,19 @@ static errno_t nss_cmd_getsidby_search(struct nss_dom_ctx *dctx)
     struct nss_ctx *nctx;
     int ret;
     int err;
-    const char *attrs[] = {SYSDB_NAME, SYSDB_OBJECTCLASS, SYSDB_SID_STR,
-                           ORIGINALAD_PREFIX SYSDB_NAME,
-                           ORIGINALAD_PREFIX SYSDB_UIDNUM,
-                           ORIGINALAD_PREFIX SYSDB_GIDNUM,
-                           ORIGINALAD_PREFIX SYSDB_GECOS,
-                           ORIGINALAD_PREFIX SYSDB_HOMEDIR,
-                           ORIGINALAD_PREFIX SYSDB_SHELL,
-                           SYSDB_UPN,
-                           SYSDB_DEFAULT_OVERRIDE_NAME,
-                           SYSDB_AD_ACCOUNT_EXPIRES,
-                           SYSDB_AD_USER_ACCOUNT_CONTROL,
-                           SYSDB_DEFAULT_ATTRS, NULL};
+    const char *default_attrs[] = {SYSDB_NAME, SYSDB_OBJECTCLASS, SYSDB_SID_STR,
+                                   ORIGINALAD_PREFIX SYSDB_NAME,
+                                   ORIGINALAD_PREFIX SYSDB_UIDNUM,
+                                   ORIGINALAD_PREFIX SYSDB_GIDNUM,
+                                   ORIGINALAD_PREFIX SYSDB_GECOS,
+                                   ORIGINALAD_PREFIX SYSDB_HOMEDIR,
+                                   ORIGINALAD_PREFIX SYSDB_SHELL,
+                                   SYSDB_UPN,
+                                   SYSDB_DEFAULT_OVERRIDE_NAME,
+                                   SYSDB_AD_ACCOUNT_EXPIRES,
+                                   SYSDB_AD_USER_ACCOUNT_CONTROL,
+                                   SYSDB_DEFAULT_ATTRS, NULL};
+    const char **attrs;
     bool user_found = false;
     bool group_found = false;
     struct ldb_message *msg = NULL;
@@ -4281,6 +4282,18 @@ static errno_t nss_cmd_getsidby_search(struct nss_dom_ctx *dctx)
             goto done;
         }
 
+        attrs = default_attrs;
+        if (cmdctx->cmd == SSS_NSS_GETORIGBYNAME
+                && nctx->extra_attributes != NULL) {
+            ret = add_strings_lists(cmdctx, default_attrs,
+                                    nctx->extra_attributes, false,
+                                    discard_const(&attrs));
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE, "add_strings_lists failed.\n");
+                goto done;
+            }
+        }
+
         if (cmdctx->cmd == SSS_NSS_GETSIDBYID) {
             ret = sysdb_search_user_by_uid(cmdctx, dom, cmdctx->id, attrs,
                                            &msg);
@@ -4593,16 +4606,21 @@ static errno_t fill_sid(struct sss_packet *packet,
 }
 
 static errno_t fill_orig(struct sss_packet *packet,
+                         struct resp_ctx *rctx,
                          enum sss_id_type id_type,
                          struct ldb_message *msg)
 {
     int ret;
+    TALLOC_CTX *tmp_ctx;
     const char *tmp_str;
     uint8_t *body;
     size_t blen;
     size_t pctr = 0;
     size_t c;
     size_t sum;
+    size_t found;
+    size_t extra_attrs_count = 0;
+    const char **extra_attrs_list = NULL;
     const char *orig_attr_list[] = {SYSDB_SID_STR,
                                     ORIGINALAD_PREFIX SYSDB_NAME,
                                     ORIGINALAD_PREFIX SYSDB_UIDNUM,
@@ -4615,42 +4633,83 @@ static errno_t fill_orig(struct sss_packet *packet,
                                     SYSDB_AD_ACCOUNT_EXPIRES,
                                     SYSDB_AD_USER_ACCOUNT_CONTROL,
                                     NULL};
-    struct sized_string keys[sizeof(orig_attr_list)];
-    struct sized_string vals[sizeof(orig_attr_list)];
+    struct sized_string *keys;
+    struct sized_string *vals;
+    struct nss_ctx *nctx;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+        return ENOMEM;
+    }
+
+    nctx = talloc_get_type(rctx->pvt_ctx, struct nss_ctx);
+    if (nctx->extra_attributes != NULL) {
+        extra_attrs_list = nctx->extra_attributes;
+            for(extra_attrs_count = 0;
+                extra_attrs_list[extra_attrs_count] != NULL;
+                extra_attrs_count++);
+    }
+
+    keys = talloc_array(tmp_ctx, struct sized_string,
+                        sizeof(orig_attr_list) + extra_attrs_count);
+    vals = talloc_array(tmp_ctx, struct sized_string,
+                        sizeof(orig_attr_list) + extra_attrs_count);
+    if (keys == NULL || vals == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
+        ret = ENOMEM;
+        goto done;
+    }
 
     sum = 0;
+    found = 0;
     for (c = 0; orig_attr_list[c] != NULL; c++) {
         tmp_str = ldb_msg_find_attr_as_string(msg, orig_attr_list[c], NULL);
         if (tmp_str != NULL) {
-            to_sized_string(&keys[c], orig_attr_list[c]);
-            sum += keys[c].len;
-            to_sized_string(&vals[c], tmp_str);
-            sum += vals[c].len;
-        } else {
-            vals[c].len = 0;
+            to_sized_string(&keys[found], orig_attr_list[c]);
+            sum += keys[found].len;
+            to_sized_string(&vals[found], tmp_str);
+            sum += vals[found].len;
+
+            found++;
+        }
+    }
+
+    for (c = 0; c < extra_attrs_count; c++) {
+        tmp_str = ldb_msg_find_attr_as_string(msg, extra_attrs_list[c], NULL);
+        if (tmp_str != NULL) {
+            to_sized_string(&keys[found], extra_attrs_list[c]);
+            sum += keys[found].len;
+            to_sized_string(&vals[found], tmp_str);
+            sum += vals[found].len;
+
+            found++;
         }
     }
 
     ret = sss_packet_grow(packet, sum +  3 * sizeof(uint32_t));
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n");
-        return ret;
+        goto done;
     }
 
     sss_packet_get_body(packet, &body, &blen);
     SAFEALIGN_SETMEM_UINT32(body, 1, &pctr); /* Num results */
     SAFEALIGN_SETMEM_UINT32(body + pctr, 0, &pctr); /* reserved */
     SAFEALIGN_COPY_UINT32(body + pctr, &id_type, &pctr);
-    for (c = 0; orig_attr_list[c] != NULL; c++) {
-        if (vals[c].len != 0) {
-            memcpy(&body[pctr], keys[c].str, keys[c].len);
-            pctr+= keys[c].len;
-            memcpy(&body[pctr], vals[c].str, vals[c].len);
-            pctr+= vals[c].len;
-        }
+    for (c = 0; c < found; c++) {
+        memcpy(&body[pctr], keys[c].str, keys[c].len);
+        pctr+= keys[c].len;
+        memcpy(&body[pctr], vals[c].str, vals[c].len);
+        pctr+= vals[c].len;
     }
 
-    return EOK;
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+
+    return ret;
 }
 
 static errno_t fill_name(struct sss_packet *packet,
@@ -4818,7 +4877,8 @@ static errno_t nss_cmd_getbysid_send_reply(struct nss_dom_ctx *dctx)
         ret = fill_sid(cctx->creq->out, id_type, dctx->res->msgs[0]);
         break;
     case SSS_NSS_GETORIGBYNAME:
-        ret = fill_orig(cctx->creq->out, id_type, dctx->res->msgs[0]);
+        ret = fill_orig(cctx->creq->out, cctx->rctx, id_type,
+                        dctx->res->msgs[0]);
         break;
     default:
         DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported request type.\n");
diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
index 65298cdf04e37e85820d3308c773f1c95a21ce31..c318d94be867bbb9991de288cdae45d2ddc29b24 100644
--- a/src/tests/cmocka/test_nss_srv.c
+++ b/src/tests/cmocka/test_nss_srv.c
@@ -52,6 +52,8 @@ struct nss_test_ctx {
     bool ncache_hit;
 };
 
+const char *global_extra_attrs[] = {"phone", "mobile", NULL};
+
 struct nss_test_ctx *nss_test_ctx;
 
 /* Mock NSS structure */
@@ -1000,6 +1002,7 @@ void test_nss_setup(struct sss_test_conf_param params[],
     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);
+    nss_test_ctx->rctx->cdb = nss_test_ctx->tctx->confdb;
     nss_test_ctx->nctx->rctx = nss_test_ctx->rctx;
 
     /* Create client context */
@@ -1827,6 +1830,122 @@ void test_nss_getorigbyname(void **state)
     assert_int_equal(ret, EOK);
 }
 
+static int test_nss_getorigbyname_extra_check(uint32_t status, uint8_t *body,
+                                              size_t blen)
+{
+    const char *s;
+    enum sss_id_type id_type;
+    size_t rp = 2 * sizeof(uint32_t);
+
+    assert_int_equal(status, EOK);
+
+    SAFEALIGN_COPY_UINT32(&id_type, body+rp, &rp);
+    assert_int_equal(id_type, SSS_ID_TYPE_UID);
+
+    /* Sequence of null terminated strings */
+    s = (char *) body+rp;
+    assert_string_equal(s, SYSDB_SID_STR);
+    rp += strlen(s) + 1;
+    assert_true(rp < blen);
+
+    s = (char *) body+rp;
+    assert_string_equal(s, "S-1-2-3-4");
+    rp += strlen(s) + 1;
+    assert_true(rp < blen);
+
+    s = (char *) body+rp;
+    assert_string_equal(s, ORIGINALAD_PREFIX SYSDB_NAME);
+    rp += strlen(s) + 1;
+    assert_true(rp < blen);
+
+    s = (char *) body+rp;
+    assert_string_equal(s, "orig_name");
+    rp += strlen(s) + 1;
+    assert_true(rp < blen);
+
+    s = (char *) body+rp;
+    assert_string_equal(s, ORIGINALAD_PREFIX SYSDB_UIDNUM);
+    rp += strlen(s) + 1;
+    assert_true(rp < blen);
+
+    s = (char *) body+rp;
+    assert_string_equal(s, "1234");
+    rp += strlen(s) + 1;
+    assert_true(rp < blen);
+
+    s = (char *) body+rp;
+    assert_string_equal(s, "phone");
+    rp += strlen(s) + 1;
+    assert_true(rp < blen);
+
+    s = (char *) body+rp;
+    assert_string_equal(s, "+12-34 56 78");
+    rp += strlen(s) + 1;
+    assert_true(rp < blen);
+
+    s = (char *) body+rp;
+    assert_string_equal(s, "mobile");
+    rp += strlen(s) + 1;
+    assert_true(rp < blen);
+
+    s = (char *) body+rp;
+    assert_string_equal(s, "+98-76 54 32");
+    rp += strlen(s) + 1;
+    assert_int_equal(rp, blen);
+
+    return EOK;
+}
+
+void test_nss_getorigbyname_extra_attrs(void **state)
+{
+    errno_t ret;
+    struct sysdb_attrs *attrs;
+
+    attrs = sysdb_new_attrs(nss_test_ctx);
+    assert_non_null(attrs);
+
+    ret = sysdb_attrs_add_string(attrs, SYSDB_SID_STR, "S-1-2-3-4");
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_attrs_add_string(attrs, ORIGINALAD_PREFIX SYSDB_NAME,
+                                 "orig_name");
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_attrs_add_uint32(attrs, ORIGINALAD_PREFIX SYSDB_UIDNUM, 1234);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_attrs_add_string(attrs, "phone", "+12-34 56 78");
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_attrs_add_string(attrs, "mobile", "+98-76 54 32");
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_attrs_add_string(attrs, "not_extra", "abc");
+    assert_int_equal(ret, EOK);
+
+    /* Prime the cache with a valid user */
+    ret = sysdb_add_user(nss_test_ctx->tctx->dom,
+                         "testuserorigextra", 2345, 6789,
+                         "test user orig extra",
+                         "/home/testuserorigextra", "/bin/sh", NULL,
+                         attrs, 300, 0);
+    assert_int_equal(ret, EOK);
+
+    mock_input_user_or_group("testuserorigextra");
+    will_return(__wrap_sss_packet_get_cmd, SSS_NSS_GETORIGBYNAME);
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
+
+    /* Query for that user, call a callback when command finishes */
+    set_cmd_cb(test_nss_getorigbyname_extra_check);
+    ret = sss_cmd_execute(nss_test_ctx->cctx, SSS_NSS_GETORIGBYNAME,
+                          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[] = {
@@ -1848,6 +1967,18 @@ void nss_fqdn_test_setup(void **state)
     test_nss_setup(params, state);
 }
 
+void nss_test_setup_extra_attr(void **state)
+{
+    struct sss_test_conf_param params[] = {
+        { "enumerate", "false" },
+        { NULL, NULL },             /* Sentinel */
+    };
+
+    test_nss_setup(params, state);
+
+    nss_test_ctx->nctx->extra_attributes = global_extra_attrs;
+}
+
 void nss_subdom_test_setup(void **state)
 {
     const char *const testdom[4] = { TEST_SUBDOM_NAME, "TEST.SUB", "test", "S-3" };
@@ -1963,6 +2094,8 @@ int main(int argc, const char *argv[])
                                  nss_test_setup, nss_test_teardown),
         unit_test_setup_teardown(test_nss_getorigbyname,
                                  nss_test_setup, nss_test_teardown),
+        unit_test_setup_teardown(test_nss_getorigbyname_extra_attrs,
+                                 nss_test_setup_extra_attr, nss_test_teardown),
     };
 
     /* Set debug level to invalid value so we can deside if -d 0 was used. */
-- 
1.9.3