|
|
ecf709 |
From a531a785f57be7ae228ca04a7af606debd66eeb1 Mon Sep 17 00:00:00 2001
|
|
|
ecf709 |
From: Sumit Bose <sbose@redhat.com>
|
|
|
ecf709 |
Date: Wed, 3 May 2017 16:30:12 +0200
|
|
|
ecf709 |
Subject: [PATCH 158/160] PAM: send user name hint response when needed
|
|
|
ecf709 |
MIME-Version: 1.0
|
|
|
ecf709 |
Content-Type: text/plain; charset=UTF-8
|
|
|
ecf709 |
Content-Transfer-Encoding: 8bit
|
|
|
ecf709 |
|
|
|
ecf709 |
If the PAM client didn't send a user name and promtusername is enable
|
|
|
ecf709 |
the PAM responder will tell pam_sss to ask for an optional user name as
|
|
|
ecf709 |
well.
|
|
|
ecf709 |
|
|
|
ecf709 |
Resolves:
|
|
|
ecf709 |
https://pagure.io/SSSD/sssd/issue/3395
|
|
|
ecf709 |
|
|
|
ecf709 |
Reviewed-by: Fabiano FidĂȘncio <fidencio@redhat.com>
|
|
|
ecf709 |
(cherry picked from commit 32474fa2f0a6dc09386bab405fc3461cb3dd12ac)
|
|
|
ecf709 |
---
|
|
|
ecf709 |
src/responder/pam/pamsrv_cmd.c | 72 ++++++++++------
|
|
|
ecf709 |
src/tests/cmocka/test_pam_srv.c | 180 +++++++++++++++++++++++++++++-----------
|
|
|
ecf709 |
2 files changed, 177 insertions(+), 75 deletions(-)
|
|
|
ecf709 |
|
|
|
ecf709 |
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
|
|
|
ecf709 |
index 080cfafa709d63542fbf57d26fab11f0a367dea7..49a05657e03feef564d6196029da4cacc2ab8eaf 100644
|
|
|
ecf709 |
--- a/src/responder/pam/pamsrv_cmd.c
|
|
|
ecf709 |
+++ b/src/responder/pam/pamsrv_cmd.c
|
|
|
ecf709 |
@@ -1414,7 +1414,7 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
|
|
|
ecf709 |
struct cache_req_result **results;
|
|
|
ecf709 |
struct pam_auth_req *preq = tevent_req_callback_data(req,
|
|
|
ecf709 |
struct pam_auth_req);
|
|
|
ecf709 |
- const char *cert_user;
|
|
|
ecf709 |
+ const char *cert_user = NULL;
|
|
|
ecf709 |
|
|
|
ecf709 |
ret = cache_req_recv(preq, req, &results);
|
|
|
ecf709 |
talloc_zfree(req);
|
|
|
ecf709 |
@@ -1439,35 +1439,55 @@ static void pam_forwarder_lookup_by_cert_done(struct tevent_req *req)
|
|
|
ecf709 |
goto done;
|
|
|
ecf709 |
}
|
|
|
ecf709 |
|
|
|
ecf709 |
- if (preq->cert_user_objs->count != 1) {
|
|
|
ecf709 |
- DEBUG(SSSDBG_CRIT_FAILURE,
|
|
|
ecf709 |
- "More than one user mapped to certificate.\n");
|
|
|
ecf709 |
- /* TODO: send pam response to ask for a user name */
|
|
|
ecf709 |
- ret = ERR_NO_CREDS;
|
|
|
ecf709 |
- goto done;
|
|
|
ecf709 |
- }
|
|
|
ecf709 |
- cert_user = ldb_msg_find_attr_as_string(
|
|
|
ecf709 |
+ if (preq->cert_user_objs->count == 1) {
|
|
|
ecf709 |
+ cert_user = ldb_msg_find_attr_as_string(
|
|
|
ecf709 |
preq->cert_user_objs->msgs[0],
|
|
|
ecf709 |
SYSDB_NAME, NULL);
|
|
|
ecf709 |
+ if (cert_user == NULL) {
|
|
|
ecf709 |
+ DEBUG(SSSDBG_CRIT_FAILURE,
|
|
|
ecf709 |
+ "Certificate user object has not name.\n");
|
|
|
ecf709 |
+ ret = ENOENT;
|
|
|
ecf709 |
+ goto done;
|
|
|
ecf709 |
+ }
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ DEBUG(SSSDBG_FUNC_DATA,
|
|
|
ecf709 |
+ "Found certificate user [%s].\n", cert_user);
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ ret = sss_parse_name_for_domains(preq->pd,
|
|
|
ecf709 |
+ preq->cctx->rctx->domains,
|
|
|
ecf709 |
+ preq->cctx->rctx->default_domain,
|
|
|
ecf709 |
+ cert_user,
|
|
|
ecf709 |
+ &preq->pd->domain,
|
|
|
ecf709 |
+ &preq->pd->user);
|
|
|
ecf709 |
+ if (ret != EOK) {
|
|
|
ecf709 |
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
|
ecf709 |
+ "sss_parse_name_for_domains failed.\n");
|
|
|
ecf709 |
+ goto done;
|
|
|
ecf709 |
+ }
|
|
|
ecf709 |
+ }
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ if (preq->cctx->rctx->domains->user_name_hint) {
|
|
|
ecf709 |
+ ret = add_pam_cert_response(preq->pd, cert_user,
|
|
|
ecf709 |
+ preq->token_name,
|
|
|
ecf709 |
+ preq->module_name,
|
|
|
ecf709 |
+ preq->key_id,
|
|
|
ecf709 |
+ SSS_PAM_CERT_INFO_WITH_HINT);
|
|
|
ecf709 |
+ if (ret != EOK) {
|
|
|
ecf709 |
+ DEBUG(SSSDBG_OP_FAILURE, "add_pam_cert_response failed.\n");
|
|
|
ecf709 |
+ preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL;
|
|
|
ecf709 |
+ }
|
|
|
ecf709 |
+ ret = EOK;
|
|
|
ecf709 |
+ preq->pd->pam_status = PAM_SUCCESS;
|
|
|
ecf709 |
+ pam_reply(preq);
|
|
|
ecf709 |
+ goto done;
|
|
|
ecf709 |
+ }
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ /* Without user name hints the certificate must map to single user
|
|
|
ecf709 |
+ * if no login name was given */
|
|
|
ecf709 |
if (cert_user == NULL) {
|
|
|
ecf709 |
DEBUG(SSSDBG_CRIT_FAILURE,
|
|
|
ecf709 |
- "Certificate user object has not name.\n");
|
|
|
ecf709 |
- ret = ENOENT;
|
|
|
ecf709 |
- goto done;
|
|
|
ecf709 |
- }
|
|
|
ecf709 |
-
|
|
|
ecf709 |
- DEBUG(SSSDBG_FUNC_DATA, "Found certificate user [%s].\n",
|
|
|
ecf709 |
- cert_user);
|
|
|
ecf709 |
-
|
|
|
ecf709 |
- ret = sss_parse_name_for_domains(preq->pd,
|
|
|
ecf709 |
- preq->cctx->rctx->domains,
|
|
|
ecf709 |
- preq->cctx->rctx->default_domain,
|
|
|
ecf709 |
- cert_user,
|
|
|
ecf709 |
- &preq->pd->domain,
|
|
|
ecf709 |
- &preq->pd->user);
|
|
|
ecf709 |
- if (ret != EOK) {
|
|
|
ecf709 |
- DEBUG(SSSDBG_OP_FAILURE,
|
|
|
ecf709 |
- "sss_parse_name_for_domains failed.\n");
|
|
|
ecf709 |
+ "More than one user mapped to certificate.\n");
|
|
|
ecf709 |
+ ret = ERR_NO_CREDS;
|
|
|
ecf709 |
goto done;
|
|
|
ecf709 |
}
|
|
|
ecf709 |
|
|
|
ecf709 |
diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
|
|
|
ecf709 |
index 35afbdd81d004236885ee80914771ccb4b8acff4..0f92f05417025e41a702127099d1d01e269412dc 100644
|
|
|
ecf709 |
--- a/src/tests/cmocka/test_pam_srv.c
|
|
|
ecf709 |
+++ b/src/tests/cmocka/test_pam_srv.c
|
|
|
ecf709 |
@@ -747,57 +747,83 @@ static int test_pam_cert_check_gdm_smartcard(uint32_t status, uint8_t *body,
|
|
|
ecf709 |
return EOK;
|
|
|
ecf709 |
}
|
|
|
ecf709 |
|
|
|
ecf709 |
+static int test_pam_cert_check_ex(uint32_t status, uint8_t *body, size_t blen,
|
|
|
ecf709 |
+ enum response_type type, const char *name)
|
|
|
ecf709 |
+{
|
|
|
ecf709 |
+ size_t rp = 0;
|
|
|
ecf709 |
+ uint32_t val;
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ assert_int_equal(status, 0);
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
|
|
|
ecf709 |
+ assert_int_equal(val, pam_test_ctx->exp_pam_status);
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
|
|
|
ecf709 |
+ if (name == NULL || *name == '\0') {
|
|
|
ecf709 |
+ assert_int_equal(val, 1);
|
|
|
ecf709 |
+ } else {
|
|
|
ecf709 |
+ assert_int_equal(val, 2);
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
|
|
|
ecf709 |
+ assert_int_equal(val, SSS_PAM_DOMAIN_NAME);
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
|
|
|
ecf709 |
+ assert_int_equal(val, 9);
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ assert_int_equal(*(body + rp + val - 1), 0);
|
|
|
ecf709 |
+ assert_string_equal(body + rp, TEST_DOM_NAME);
|
|
|
ecf709 |
+ rp += val;
|
|
|
ecf709 |
+ }
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
|
|
|
ecf709 |
+ assert_int_equal(val, type);
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
|
|
|
ecf709 |
+ assert_int_equal(val, (strlen(name) + 1
|
|
|
ecf709 |
+ + sizeof(TEST_TOKEN_NAME)
|
|
|
ecf709 |
+ + sizeof(TEST_MODULE_NAME)
|
|
|
ecf709 |
+ + sizeof(TEST_KEY_ID)));
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ assert_int_equal(*(body + rp + strlen(name)), 0);
|
|
|
ecf709 |
+ assert_string_equal(body + rp, name);
|
|
|
ecf709 |
+ rp += strlen(name) + 1;
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ assert_int_equal(*(body + rp + sizeof(TEST_TOKEN_NAME) - 1), 0);
|
|
|
ecf709 |
+ assert_string_equal(body + rp, TEST_TOKEN_NAME);
|
|
|
ecf709 |
+ rp += sizeof(TEST_TOKEN_NAME);
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ assert_int_equal(*(body + rp + sizeof(TEST_MODULE_NAME) - 1), 0);
|
|
|
ecf709 |
+ assert_string_equal(body + rp, TEST_MODULE_NAME);
|
|
|
ecf709 |
+ rp += sizeof(TEST_MODULE_NAME);
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ assert_int_equal(*(body + rp + sizeof(TEST_KEY_ID) - 1), 0);
|
|
|
ecf709 |
+ assert_string_equal(body + rp, TEST_KEY_ID);
|
|
|
ecf709 |
+ rp += sizeof(TEST_KEY_ID);
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ assert_int_equal(rp, blen);
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ return EOK;
|
|
|
ecf709 |
+}
|
|
|
ecf709 |
+
|
|
|
ecf709 |
static int test_pam_cert_check(uint32_t status, uint8_t *body, size_t blen)
|
|
|
ecf709 |
{
|
|
|
ecf709 |
- size_t rp = 0;
|
|
|
ecf709 |
- uint32_t val;
|
|
|
ecf709 |
-
|
|
|
ecf709 |
- assert_int_equal(status, 0);
|
|
|
ecf709 |
-
|
|
|
ecf709 |
- SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
|
|
|
ecf709 |
- assert_int_equal(val, pam_test_ctx->exp_pam_status);
|
|
|
ecf709 |
-
|
|
|
ecf709 |
- SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
|
|
|
ecf709 |
- assert_int_equal(val, 2);
|
|
|
ecf709 |
-
|
|
|
ecf709 |
- SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
|
|
|
ecf709 |
- assert_int_equal(val, SSS_PAM_DOMAIN_NAME);
|
|
|
ecf709 |
-
|
|
|
ecf709 |
- SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
|
|
|
ecf709 |
- assert_int_equal(val, 9);
|
|
|
ecf709 |
-
|
|
|
ecf709 |
- assert_int_equal(*(body + rp + val - 1), 0);
|
|
|
ecf709 |
- assert_string_equal(body + rp, TEST_DOM_NAME);
|
|
|
ecf709 |
- rp += val;
|
|
|
ecf709 |
-
|
|
|
ecf709 |
- SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
|
|
|
ecf709 |
- assert_int_equal(val, SSS_PAM_CERT_INFO);
|
|
|
ecf709 |
-
|
|
|
ecf709 |
- SAFEALIGN_COPY_UINT32(&val, body + rp, &rp);
|
|
|
ecf709 |
- assert_int_equal(val, (sizeof("pamuser@"TEST_DOM_NAME)
|
|
|
ecf709 |
- + sizeof(TEST_TOKEN_NAME)
|
|
|
ecf709 |
- + sizeof(TEST_MODULE_NAME)
|
|
|
ecf709 |
- + sizeof(TEST_KEY_ID)));
|
|
|
ecf709 |
-
|
|
|
ecf709 |
- assert_int_equal(*(body + rp + sizeof("pamuser@"TEST_DOM_NAME) - 1), 0);
|
|
|
ecf709 |
- assert_string_equal(body + rp, "pamuser@"TEST_DOM_NAME);
|
|
|
ecf709 |
- rp += sizeof("pamuser@"TEST_DOM_NAME);
|
|
|
ecf709 |
-
|
|
|
ecf709 |
- assert_int_equal(*(body + rp + sizeof(TEST_TOKEN_NAME) - 1), 0);
|
|
|
ecf709 |
- assert_string_equal(body + rp, TEST_TOKEN_NAME);
|
|
|
ecf709 |
- rp += sizeof(TEST_TOKEN_NAME);
|
|
|
ecf709 |
-
|
|
|
ecf709 |
- assert_int_equal(*(body + rp + sizeof(TEST_MODULE_NAME) - 1), 0);
|
|
|
ecf709 |
- assert_string_equal(body + rp, TEST_MODULE_NAME);
|
|
|
ecf709 |
- rp += sizeof(TEST_MODULE_NAME);
|
|
|
ecf709 |
-
|
|
|
ecf709 |
- assert_int_equal(*(body + rp + sizeof(TEST_KEY_ID) - 1), 0);
|
|
|
ecf709 |
- assert_string_equal(body + rp, TEST_KEY_ID);
|
|
|
ecf709 |
- rp += sizeof(TEST_KEY_ID);
|
|
|
ecf709 |
-
|
|
|
ecf709 |
- assert_int_equal(rp, blen);
|
|
|
ecf709 |
-
|
|
|
ecf709 |
- return EOK;
|
|
|
ecf709 |
+ return test_pam_cert_check_ex(status, body, blen,
|
|
|
ecf709 |
+ SSS_PAM_CERT_INFO, "pamuser@"TEST_DOM_NAME);
|
|
|
ecf709 |
+}
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+static int test_pam_cert_check_with_hint(uint32_t status, uint8_t *body,
|
|
|
ecf709 |
+ size_t blen)
|
|
|
ecf709 |
+{
|
|
|
ecf709 |
+ return test_pam_cert_check_ex(status, body, blen,
|
|
|
ecf709 |
+ SSS_PAM_CERT_INFO_WITH_HINT,
|
|
|
ecf709 |
+ "pamuser@"TEST_DOM_NAME);
|
|
|
ecf709 |
+}
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+static int test_pam_cert_check_with_hint_no_user(uint32_t status, uint8_t *body,
|
|
|
ecf709 |
+ size_t blen)
|
|
|
ecf709 |
+{
|
|
|
ecf709 |
+ return test_pam_cert_check_ex(status, body, blen,
|
|
|
ecf709 |
+ SSS_PAM_CERT_INFO_WITH_HINT, "");
|
|
|
ecf709 |
}
|
|
|
ecf709 |
|
|
|
ecf709 |
static int test_pam_offline_chauthtok_check(uint32_t status,
|
|
|
ecf709 |
@@ -1895,6 +1921,33 @@ void test_pam_preauth_cert_no_logon_name(void **state)
|
|
|
ecf709 |
assert_int_equal(ret, EOK);
|
|
|
ecf709 |
}
|
|
|
ecf709 |
|
|
|
ecf709 |
+void test_pam_preauth_cert_no_logon_name_with_hint(void **state)
|
|
|
ecf709 |
+{
|
|
|
ecf709 |
+ int ret;
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
|
|
|
ecf709 |
+ pam_test_ctx->rctx->domains->user_name_hint = true;
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ /* If no logon name is given the user is looked by certificate first.
|
|
|
ecf709 |
+ * Since user name hint is enabled we do not have to search the user
|
|
|
ecf709 |
+ * during pre-auth and there is no need for an extra mocked response as in
|
|
|
ecf709 |
+ * test_pam_preauth_cert_no_logon_name. */
|
|
|
ecf709 |
+ mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL,
|
|
|
ecf709 |
+ test_lookup_by_cert_cb, TEST_TOKEN_CERT, false);
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
|
|
|
ecf709 |
+ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ set_cmd_cb(test_pam_cert_check_with_hint);
|
|
|
ecf709 |
+ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH,
|
|
|
ecf709 |
+ pam_test_ctx->pam_cmds);
|
|
|
ecf709 |
+ assert_int_equal(ret, EOK);
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ /* Wait until the test finishes with EOK */
|
|
|
ecf709 |
+ ret = test_ev_loop(pam_test_ctx->tctx);
|
|
|
ecf709 |
+ assert_int_equal(ret, EOK);
|
|
|
ecf709 |
+}
|
|
|
ecf709 |
+
|
|
|
ecf709 |
void test_pam_preauth_cert_no_logon_name_double_cert(void **state)
|
|
|
ecf709 |
{
|
|
|
ecf709 |
int ret;
|
|
|
ecf709 |
@@ -1917,6 +1970,29 @@ void test_pam_preauth_cert_no_logon_name_double_cert(void **state)
|
|
|
ecf709 |
assert_int_equal(ret, EOK);
|
|
|
ecf709 |
}
|
|
|
ecf709 |
|
|
|
ecf709 |
+void test_pam_preauth_cert_no_logon_name_double_cert_with_hint(void **state)
|
|
|
ecf709 |
+{
|
|
|
ecf709 |
+ int ret;
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ set_cert_auth_param(pam_test_ctx->pctx, NSS_DB);
|
|
|
ecf709 |
+ pam_test_ctx->rctx->domains->user_name_hint = true;
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ mock_input_pam_cert(pam_test_ctx, NULL, NULL, NULL,
|
|
|
ecf709 |
+ test_lookup_by_cert_double_cb, TEST_TOKEN_CERT, false);
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH);
|
|
|
ecf709 |
+ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ set_cmd_cb(test_pam_cert_check_with_hint_no_user);
|
|
|
ecf709 |
+ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH,
|
|
|
ecf709 |
+ pam_test_ctx->pam_cmds);
|
|
|
ecf709 |
+ assert_int_equal(ret, EOK);
|
|
|
ecf709 |
+
|
|
|
ecf709 |
+ /* Wait until the test finishes with EOK */
|
|
|
ecf709 |
+ ret = test_ev_loop(pam_test_ctx->tctx);
|
|
|
ecf709 |
+ assert_int_equal(ret, EOK);
|
|
|
ecf709 |
+}
|
|
|
ecf709 |
+
|
|
|
ecf709 |
void test_pam_preauth_no_cert_no_logon_name(void **state)
|
|
|
ecf709 |
{
|
|
|
ecf709 |
int ret;
|
|
|
ecf709 |
@@ -2426,8 +2502,14 @@ int main(int argc, const char *argv[])
|
|
|
ecf709 |
cmocka_unit_test_setup_teardown(test_pam_preauth_cert_no_logon_name,
|
|
|
ecf709 |
pam_test_setup, pam_test_teardown),
|
|
|
ecf709 |
cmocka_unit_test_setup_teardown(
|
|
|
ecf709 |
+ test_pam_preauth_cert_no_logon_name_with_hint,
|
|
|
ecf709 |
+ pam_test_setup, pam_test_teardown),
|
|
|
ecf709 |
+ cmocka_unit_test_setup_teardown(
|
|
|
ecf709 |
test_pam_preauth_cert_no_logon_name_double_cert,
|
|
|
ecf709 |
pam_test_setup, pam_test_teardown),
|
|
|
ecf709 |
+ cmocka_unit_test_setup_teardown(
|
|
|
ecf709 |
+ test_pam_preauth_cert_no_logon_name_double_cert_with_hint,
|
|
|
ecf709 |
+ pam_test_setup, pam_test_teardown),
|
|
|
ecf709 |
cmocka_unit_test_setup_teardown(test_pam_preauth_no_cert_no_logon_name,
|
|
|
ecf709 |
pam_test_setup, pam_test_teardown),
|
|
|
ecf709 |
cmocka_unit_test_setup_teardown(
|
|
|
ecf709 |
--
|
|
|
ecf709 |
2.9.4
|
|
|
ecf709 |
|