Blame SOURCES/0027-ssh-add-no_rules-and-all_rules-to-ssh_use_certificat.patch

0d441c
From 849d495ea948e75ecb4ea469c9f8db4a740a2377 Mon Sep 17 00:00:00 2001
0d441c
From: Sumit Bose <sbose@redhat.com>
0d441c
Date: Fri, 7 Feb 2020 20:32:45 +0100
0d441c
Subject: [PATCH 27/27] ssh: add 'no_rules' and 'all_rules' to
0d441c
 ssh_use_certificate_matching_rules
0d441c
MIME-Version: 1.0
0d441c
Content-Type: text/plain; charset=UTF-8
0d441c
Content-Transfer-Encoding: 8bit
0d441c
0d441c
To make ssh_use_certificate_matching_rules option more flexible and
0d441c
predictable the keywords 'all_rules' and 'no_rules' are added.
0d441c
'no_rules' can be used to allow all certificates.
0d441c
0d441c
If rules names are given but no matching rules can be found this is
0d441c
considered an error and no ssh keys will be derived from the
0d441c
certificates.
0d441c
0d441c
Related to https://pagure.io/SSSD/sssd/issue/4121
0d441c
0d441c
Reviewed-by: Tomáš Halman <thalman@redhat.com>
0d441c
---
0d441c
 src/man/sssd.conf.5.xml         |  16 +++--
0d441c
 src/responder/ssh/ssh_cmd.c     |  33 ++++++---
0d441c
 src/responder/ssh/ssh_private.h |   1 +
0d441c
 src/responder/ssh/ssh_reply.c   |   8 +++
0d441c
 src/tests/cmocka/test_ssh_srv.c | 122 +++++++++++++++++++++++++++++++-
0d441c
 5 files changed, 165 insertions(+), 15 deletions(-)
0d441c
0d441c
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
0d441c
index ef07c43d3..f71fbf4aa 100644
0d441c
--- a/src/man/sssd.conf.5.xml
0d441c
+++ b/src/man/sssd.conf.5.xml
0d441c
@@ -1760,12 +1760,20 @@ p11_uri = library-description=OpenSC%20smartcard%20framework;slot-id=2
0d441c
                             will be ignored.
0d441c
                         </para>
0d441c
                         <para>
0d441c
-                            If a non-existing rule name is given all rules will
0d441c
-                            be ignored and all available certificates will be
0d441c
-                            used to derive ssh keys.
0d441c
+                            There are two special key words 'all_rules' and
0d441c
+                            'no_rules' which will enable all or no rules,
0d441c
+                            respectively. The latter means that no certificates
0d441c
+                            will be filtered out and ssh keys will be generated
0d441c
+                            from all valid certificates.
0d441c
                         </para>
0d441c
                         <para>
0d441c
-                            Default: not set, all found rules are used
0d441c
+                            A non-existing rule name is considered an error.
0d441c
+                            If as a result no rule is selected all certificates
0d441c
+                            will be ignored.
0d441c
+                        </para>
0d441c
+                        <para>
0d441c
+                            Default: not set, equivalent to 'all_rules,
0d441c
+                            all found rules are used
0d441c
                         </para>
0d441c
                     </listitem>
0d441c
                 </varlistentry>
0d441c
diff --git a/src/responder/ssh/ssh_cmd.c b/src/responder/ssh/ssh_cmd.c
0d441c
index 09f9b73b6..d1e7c667b 100644
0d441c
--- a/src/responder/ssh/ssh_cmd.c
0d441c
+++ b/src/responder/ssh/ssh_cmd.c
0d441c
@@ -157,10 +157,26 @@ static errno_t ssh_cmd_refresh_certmap_ctx(struct ssh_ctx *ssh_ctx,
0d441c
     size_t c;
0d441c
     int ret;
0d441c
     bool rule_added;
0d441c
+    bool all_rules = false;
0d441c
+    bool no_rules = false;
0d441c
+
0d441c
+    ssh_ctx->cert_rules_error = false;
0d441c
+
0d441c
+    if (ssh_ctx->cert_rules == NULL || ssh_ctx->cert_rules[0] == NULL) {
0d441c
+        all_rules = true;
0d441c
+    } else if (ssh_ctx->cert_rules[0] != NULL
0d441c
+                    && ssh_ctx->cert_rules[1] == NULL) {
0d441c
+        if (strcmp(ssh_ctx->cert_rules[0], "all_rules") == 0) {
0d441c
+            all_rules = true;
0d441c
+        } else if (strcmp(ssh_ctx->cert_rules[0], "no_rules") == 0) {
0d441c
+            no_rules = true;
0d441c
+        }
0d441c
+    }
0d441c
 
0d441c
     if (!ssh_ctx->use_cert_keys
0d441c
             || ssh_ctx->certmap_last_read
0d441c
-                    >= ssh_ctx->rctx->get_domains_last_call.tv_sec) {
0d441c
+                    >= ssh_ctx->rctx->get_domains_last_call.tv_sec
0d441c
+            || no_rules) {
0d441c
         DEBUG(SSSDBG_TRACE_ALL, "No certmap update needed.\n");
0d441c
         return EOK;
0d441c
     }
0d441c
@@ -180,9 +196,8 @@ static errno_t ssh_cmd_refresh_certmap_ctx(struct ssh_ctx *ssh_ctx,
0d441c
 
0d441c
         for (c = 0; certmap_list[c] != NULL; c++) {
0d441c
 
0d441c
-            if (ssh_ctx->cert_rules != NULL
0d441c
-                        && !string_in_list(certmap_list[c]->name,
0d441c
-                                           ssh_ctx->cert_rules, true)) {
0d441c
+            if (!all_rules && !string_in_list(certmap_list[c]->name,
0d441c
+                                              ssh_ctx->cert_rules, true)) {
0d441c
                 DEBUG(SSSDBG_TRACE_ALL, "Skipping matching rule [%s], it is "
0d441c
                       "not listed in the ssh_use_certificate_matching_rules "
0d441c
                       "option.\n", certmap_list[c]->name);
0d441c
@@ -212,11 +227,12 @@ static errno_t ssh_cmd_refresh_certmap_ctx(struct ssh_ctx *ssh_ctx,
0d441c
     }
0d441c
 
0d441c
     if (!rule_added) {
0d441c
-        DEBUG(SSSDBG_TRACE_ALL,
0d441c
-              "No matching rule added, all certificates will be used.\n");
0d441c
+        DEBUG(SSSDBG_CONF_SETTINGS,
0d441c
+              "No matching rule added, please check "
0d441c
+              "ssh_use_certificate_matching_rules option values for typos .\n");
0d441c
 
0d441c
-        sss_certmap_free_ctx(sss_certmap_ctx);
0d441c
-        sss_certmap_ctx = NULL;
0d441c
+        ret = EINVAL;
0d441c
+        goto done;
0d441c
     }
0d441c
 
0d441c
     ret = EOK;
0d441c
@@ -228,6 +244,7 @@ done:
0d441c
         ssh_ctx->certmap_last_read = ssh_ctx->rctx->get_domains_last_call.tv_sec;
0d441c
     } else {
0d441c
         sss_certmap_free_ctx(sss_certmap_ctx);
0d441c
+        ssh_ctx->cert_rules_error = true;
0d441c
     }
0d441c
 
0d441c
     return ret;
0d441c
diff --git a/src/responder/ssh/ssh_private.h b/src/responder/ssh/ssh_private.h
0d441c
index 76a1aead3..028ccd616 100644
0d441c
--- a/src/responder/ssh/ssh_private.h
0d441c
+++ b/src/responder/ssh/ssh_private.h
0d441c
@@ -40,6 +40,7 @@ struct ssh_ctx {
0d441c
     time_t certmap_last_read;
0d441c
     struct sss_certmap_ctx *sss_certmap_ctx;
0d441c
     char **cert_rules;
0d441c
+    bool cert_rules_error;
0d441c
 };
0d441c
 
0d441c
 struct sss_cmd_table *get_ssh_cmds(void);
0d441c
diff --git a/src/responder/ssh/ssh_reply.c b/src/responder/ssh/ssh_reply.c
0d441c
index 1200a3a36..97914266d 100644
0d441c
--- a/src/responder/ssh/ssh_reply.c
0d441c
+++ b/src/responder/ssh/ssh_reply.c
0d441c
@@ -196,6 +196,14 @@ struct tevent_req *ssh_get_output_keys_send(TALLOC_CTX *mem_ctx,
0d441c
         goto done;
0d441c
     }
0d441c
 
0d441c
+    if (state->ssh_ctx->cert_rules_error) {
0d441c
+        DEBUG(SSSDBG_CONF_SETTINGS,
0d441c
+              "Skipping keys from certificates because there was an error "
0d441c
+              "while processing matching rules.\n");
0d441c
+        ret = EOK;
0d441c
+        goto done;
0d441c
+    }
0d441c
+
0d441c
     ret = confdb_get_string(cli_ctx->rctx->cdb, state,
0d441c
                             CONFDB_MONITOR_CONF_ENTRY,
0d441c
                             CONFDB_MONITOR_CERT_VERIFICATION, NULL,
0d441c
diff --git a/src/tests/cmocka/test_ssh_srv.c b/src/tests/cmocka/test_ssh_srv.c
0d441c
index 45915f681..fc43663a7 100644
0d441c
--- a/src/tests/cmocka/test_ssh_srv.c
0d441c
+++ b/src/tests/cmocka/test_ssh_srv.c
0d441c
@@ -712,6 +712,120 @@ void test_ssh_user_pubkey_cert_with_rule(void **state)
0d441c
     assert_int_equal(ret, EOK);
0d441c
 }
0d441c
 
0d441c
+void test_ssh_user_pubkey_cert_with_all_rules(void **state)
0d441c
+{
0d441c
+    int ret;
0d441c
+    struct sysdb_attrs *attrs;
0d441c
+    /* Both rules are enabled, both certificates should be handled. */
0d441c
+    const char *rule_list[] = { "all_rules", NULL };
0d441c
+    struct certmap_info *certmap_list[] = { &rule_1, &rule_2, NULL};
0d441c
+
0d441c
+    attrs = sysdb_new_attrs(ssh_test_ctx);
0d441c
+    assert_non_null(attrs);
0d441c
+    ret = sysdb_attrs_add_string(attrs, SYSDB_SSH_PUBKEY, TEST_SSH_PUBKEY);
0d441c
+    assert_int_equal(ret, EOK);
0d441c
+    ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT,
0d441c
+                                      SSSD_TEST_CERT_0001);
0d441c
+    assert_int_equal(ret, EOK);
0d441c
+    ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT,
0d441c
+                                      SSSD_TEST_CERT_0002);
0d441c
+    assert_int_equal(ret, EOK);
0d441c
+
0d441c
+    ret = sysdb_set_user_attr(ssh_test_ctx->tctx->dom,
0d441c
+                              ssh_test_ctx->ssh_user_fqdn,
0d441c
+                              attrs,
0d441c
+                              LDB_FLAG_MOD_ADD);
0d441c
+    talloc_free(attrs);
0d441c
+    assert_int_equal(ret, EOK);
0d441c
+
0d441c
+    mock_input_user(ssh_test_ctx, ssh_test_ctx->ssh_user_fqdn);
0d441c
+    will_return(__wrap_sss_packet_get_cmd, SSS_SSH_GET_USER_PUBKEYS);
0d441c
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
0d441c
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
0d441c
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
0d441c
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
0d441c
+
0d441c
+    /* Enable certificate support */
0d441c
+    ssh_test_ctx->ssh_ctx->use_cert_keys = true;
0d441c
+    ssh_test_ctx->ssh_ctx->rctx->domains->certmaps = certmap_list;
0d441c
+    ssh_test_ctx->ssh_ctx->certmap_last_read = 0;
0d441c
+    ssh_test_ctx->ssh_ctx->rctx->get_domains_last_call.tv_sec = 1;
0d441c
+    ssh_test_ctx->ssh_ctx->cert_rules = discard_const(rule_list);
0d441c
+#ifdef HAVE_NSS
0d441c
+    ssh_test_ctx->ssh_ctx->ca_db = discard_const("sql:" ABS_BUILD_DIR
0d441c
+                                                "/src/tests/test_CA/p11_nssdb");
0d441c
+#else
0d441c
+    ssh_test_ctx->ssh_ctx->ca_db = discard_const(ABS_BUILD_DIR
0d441c
+                                                "/src/tests/test_CA/SSSD_test_CA.pem");
0d441c
+#endif
0d441c
+
0d441c
+    set_cmd_cb(test_ssh_user_pubkey_cert_check);
0d441c
+    ret = sss_cmd_execute(ssh_test_ctx->cctx, SSS_SSH_GET_USER_PUBKEYS,
0d441c
+                          ssh_test_ctx->ssh_cmds);
0d441c
+    assert_int_equal(ret, EOK);
0d441c
+
0d441c
+    /* Wait until the test finishes with EOK */
0d441c
+    ret = test_ev_loop(ssh_test_ctx->tctx);
0d441c
+    assert_int_equal(ret, EOK);
0d441c
+}
0d441c
+
0d441c
+void test_ssh_user_pubkey_cert_with_no_rules(void **state)
0d441c
+{
0d441c
+    int ret;
0d441c
+    struct sysdb_attrs *attrs;
0d441c
+    /* No rules should be used, both certificates should be handled. */
0d441c
+    const char *rule_list[] = { "no_rules", NULL };
0d441c
+    struct certmap_info *certmap_list[] = { &rule_1, &rule_2, NULL};
0d441c
+
0d441c
+    attrs = sysdb_new_attrs(ssh_test_ctx);
0d441c
+    assert_non_null(attrs);
0d441c
+    ret = sysdb_attrs_add_string(attrs, SYSDB_SSH_PUBKEY, TEST_SSH_PUBKEY);
0d441c
+    assert_int_equal(ret, EOK);
0d441c
+    ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT,
0d441c
+                                      SSSD_TEST_CERT_0001);
0d441c
+    assert_int_equal(ret, EOK);
0d441c
+    ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT,
0d441c
+                                      SSSD_TEST_CERT_0002);
0d441c
+    assert_int_equal(ret, EOK);
0d441c
+
0d441c
+    ret = sysdb_set_user_attr(ssh_test_ctx->tctx->dom,
0d441c
+                              ssh_test_ctx->ssh_user_fqdn,
0d441c
+                              attrs,
0d441c
+                              LDB_FLAG_MOD_ADD);
0d441c
+    talloc_free(attrs);
0d441c
+    assert_int_equal(ret, EOK);
0d441c
+
0d441c
+    mock_input_user(ssh_test_ctx, ssh_test_ctx->ssh_user_fqdn);
0d441c
+    will_return(__wrap_sss_packet_get_cmd, SSS_SSH_GET_USER_PUBKEYS);
0d441c
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
0d441c
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
0d441c
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
0d441c
+    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
0d441c
+
0d441c
+    /* Enable certificate support */
0d441c
+    ssh_test_ctx->ssh_ctx->use_cert_keys = true;
0d441c
+    ssh_test_ctx->ssh_ctx->rctx->domains->certmaps = certmap_list;
0d441c
+    ssh_test_ctx->ssh_ctx->certmap_last_read = 0;
0d441c
+    ssh_test_ctx->ssh_ctx->rctx->get_domains_last_call.tv_sec = 1;
0d441c
+    ssh_test_ctx->ssh_ctx->cert_rules = discard_const(rule_list);
0d441c
+#ifdef HAVE_NSS
0d441c
+    ssh_test_ctx->ssh_ctx->ca_db = discard_const("sql:" ABS_BUILD_DIR
0d441c
+                                                "/src/tests/test_CA/p11_nssdb");
0d441c
+#else
0d441c
+    ssh_test_ctx->ssh_ctx->ca_db = discard_const(ABS_BUILD_DIR
0d441c
+                                                "/src/tests/test_CA/SSSD_test_CA.pem");
0d441c
+#endif
0d441c
+
0d441c
+    set_cmd_cb(test_ssh_user_pubkey_cert_check);
0d441c
+    ret = sss_cmd_execute(ssh_test_ctx->cctx, SSS_SSH_GET_USER_PUBKEYS,
0d441c
+                          ssh_test_ctx->ssh_cmds);
0d441c
+    assert_int_equal(ret, EOK);
0d441c
+
0d441c
+    /* Wait until the test finishes with EOK */
0d441c
+    ret = test_ev_loop(ssh_test_ctx->tctx);
0d441c
+    assert_int_equal(ret, EOK);
0d441c
+}
0d441c
+
0d441c
 void test_ssh_user_pubkey_cert_with_unknow_rule_name(void **state)
0d441c
 {
0d441c
     int ret;
0d441c
@@ -743,8 +857,6 @@ void test_ssh_user_pubkey_cert_with_unknow_rule_name(void **state)
0d441c
     will_return(__wrap_sss_packet_get_cmd, SSS_SSH_GET_USER_PUBKEYS);
0d441c
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
0d441c
     will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
0d441c
-    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
0d441c
-    will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL);
0d441c
 
0d441c
     /* Enable certificate support */
0d441c
     ssh_test_ctx->ssh_ctx->use_cert_keys = true;
0d441c
@@ -760,7 +872,7 @@ void test_ssh_user_pubkey_cert_with_unknow_rule_name(void **state)
0d441c
                                                 "/src/tests/test_CA/SSSD_test_CA.pem");
0d441c
 #endif
0d441c
 
0d441c
-    set_cmd_cb(test_ssh_user_pubkey_cert_check);
0d441c
+    set_cmd_cb(test_ssh_user_one_pubkey_check);
0d441c
     ret = sss_cmd_execute(ssh_test_ctx->cctx, SSS_SSH_GET_USER_PUBKEYS,
0d441c
                           ssh_test_ctx->ssh_cmds);
0d441c
     assert_int_equal(ret, EOK);
0d441c
@@ -852,6 +964,10 @@ int main(int argc, const char *argv[])
0d441c
                                         ssh_test_setup, ssh_test_teardown),
0d441c
         cmocka_unit_test_setup_teardown(test_ssh_user_pubkey_cert_with_rule,
0d441c
                                         ssh_test_setup, ssh_test_teardown),
0d441c
+        cmocka_unit_test_setup_teardown(test_ssh_user_pubkey_cert_with_all_rules,
0d441c
+                                        ssh_test_setup, ssh_test_teardown),
0d441c
+        cmocka_unit_test_setup_teardown(test_ssh_user_pubkey_cert_with_no_rules,
0d441c
+                                        ssh_test_setup, ssh_test_teardown),
0d441c
         cmocka_unit_test_setup_teardown(test_ssh_user_pubkey_cert_with_unknow_rule_name,
0d441c
                                         ssh_test_setup, ssh_test_teardown),
0d441c
         cmocka_unit_test_setup_teardown(test_ssh_user_pubkey_cert_with_rule_1,
0d441c
-- 
0d441c
2.20.1
0d441c