From 088be07a9e5aae54379a7f98e9e4615cd4451501 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek@redhat.com>
Date: Wed, 29 Mar 2017 22:49:09 +0200
Subject: [PATCH 73/90] KCM: Fix off-by-one error in secrets key parsing
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
When parsing the secrets key, the code tried to protect against malformed keys
or keys that are too short, but it did an error - the UUID stringified
form is 36 bytes long, so the UUID_STR_SIZE is 37 because UUID_STR_SIZE
accounts for the null terminator.
But the code, that was trying to assert that there are two characters after
the UUID string (separator and at least a single character for the name)
didn't take the NULL terminator (which strlen() doesn't return) into
account and ended up rejecting all ccaches whose name is only a single
character.
Reviewed-by: Fabiano FidĂȘncio <fidencio@redhat.com>
(cherry picked from commit 7d73049884e3a96ca3b00b5bd4104f4edd6287ab)
---
src/responder/kcm/kcmsrv_ccache_json.c | 43 +++++++++-------
src/tests/cmocka/test_kcm_json_marshalling.c | 75 ++++++++++++++++++++++++++++
2 files changed, 101 insertions(+), 17 deletions(-)
diff --git a/src/responder/kcm/kcmsrv_ccache_json.c b/src/responder/kcm/kcmsrv_ccache_json.c
index 40b64861c209206d6f60ccd0843857edee24a844..8199bc613e4204859438e1cd820f3f4b2123dd7e 100644
--- a/src/responder/kcm/kcmsrv_ccache_json.c
+++ b/src/responder/kcm/kcmsrv_ccache_json.c
@@ -109,6 +109,28 @@ static const char *sec_key_create(TALLOC_CTX *mem_ctx,
"%s%c%s", uuid_str, SEC_KEY_SEPARATOR, name);
}
+static bool sec_key_valid(const char *sec_key)
+{
+ if (sec_key == NULL) {
+ return false;
+ }
+
+ if (strlen(sec_key) < UUID_STR_SIZE + 1) {
+ /* One char for separator (at UUID_STR_SIZE, because strlen doesn't
+ * include the '\0', but UUID_STR_SIZE does) and at least one for
+ * the name */
+ DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key);
+ return false;
+ }
+
+ if (sec_key[UUID_STR_SIZE - 1] != SEC_KEY_SEPARATOR) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n");
+ return false;
+ }
+
+ return true;
+}
+
static errno_t sec_key_parse(TALLOC_CTX *mem_ctx,
const char *sec_key,
const char **_name,
@@ -116,9 +138,7 @@ static errno_t sec_key_parse(TALLOC_CTX *mem_ctx,
{
char uuid_str[UUID_STR_SIZE];
- if (strlen(sec_key) < UUID_STR_SIZE + 2) {
- /* One char for separator and at least one for the name */
- DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key);
+ if (!sec_key_valid(sec_key)) {
return EINVAL;
}
@@ -143,14 +163,7 @@ errno_t sec_key_get_uuid(const char *sec_key,
{
char uuid_str[UUID_STR_SIZE];
- if (strlen(sec_key) < UUID_STR_SIZE + 2) {
- /* One char for separator and at least one for the name */
- DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key);
- return EINVAL;
- }
-
- if (sec_key[UUID_STR_SIZE-1] != SEC_KEY_SEPARATOR) {
- DEBUG(SSSDBG_CRIT_FAILURE, "Key doesn't contain the separator\n");
+ if (!sec_key_valid(sec_key)) {
return EINVAL;
}
@@ -162,9 +175,7 @@ errno_t sec_key_get_uuid(const char *sec_key,
const char *sec_key_get_name(const char *sec_key)
{
- if (strlen(sec_key) < UUID_STR_SIZE + 2) {
- /* One char for separator and at least one for the name */
- DEBUG(SSSDBG_CRIT_FAILURE, "Key %s is too short\n", sec_key);
+ if (!sec_key_valid(sec_key)) {
return NULL;
}
@@ -174,9 +185,7 @@ const char *sec_key_get_name(const char *sec_key)
bool sec_key_match_name(const char *sec_key,
const char *name)
{
- if (strlen(sec_key) < UUID_STR_SIZE + 2) {
- /* One char for separator and at least one for the name */
- DEBUG(SSSDBG_MINOR_FAILURE, "Key %s is too short\n", sec_key);
+ if (!sec_key_valid(sec_key) || name == NULL) {
return false;
}
diff --git a/src/tests/cmocka/test_kcm_json_marshalling.c b/src/tests/cmocka/test_kcm_json_marshalling.c
index 8eff2f501066c70a8730cd3d4dc41b92d7a03e4c..108eaf55628029a6de8c23cd6486bdccc42c0364 100644
--- a/src/tests/cmocka/test_kcm_json_marshalling.c
+++ b/src/tests/cmocka/test_kcm_json_marshalling.c
@@ -32,6 +32,12 @@
#define TEST_CREDS "TESTCREDS"
+#define TEST_UUID_STR "5f8f296b-02be-4e86-9235-500e82354186"
+#define TEST_SEC_KEY_ONEDIGIT TEST_UUID_STR"-0"
+#define TEST_SEC_KEY_MULTIDIGITS TEST_UUID_STR"-123456"
+
+#define TEST_SEC_KEY_NOSEP TEST_UUID_STR"+0"
+
const struct kcm_ccdb_ops ccdb_mem_ops;
const struct kcm_ccdb_ops ccdb_sec_ops;
@@ -188,6 +194,72 @@ static void test_kcm_ccache_marshall_unmarshall(void **state)
assert_int_equal(ret, EOK);
assert_cc_equal(cc, cc2);
+
+ /* This key is exactly one byte shorter than it should be */
+ ret = sec_kv_to_ccache(test_ctx,
+ TEST_UUID_STR"-",
+ (const char *) data,
+ &owner,
+ &cc2);
+ assert_int_equal(ret, EINVAL);
+}
+
+void test_sec_key_get_uuid(void **state)
+{
+ errno_t ret;
+ uuid_t uuid;
+ char str_uuid[UUID_STR_SIZE];
+
+ uuid_clear(uuid);
+ ret = sec_key_get_uuid(TEST_SEC_KEY_ONEDIGIT, uuid);
+ assert_int_equal(ret, EOK);
+ uuid_unparse(uuid, str_uuid);
+ assert_string_equal(TEST_UUID_STR, str_uuid);
+
+ ret = sec_key_get_uuid(TEST_SEC_KEY_NOSEP, uuid);
+ assert_int_equal(ret, EINVAL);
+
+ ret = sec_key_get_uuid(TEST_UUID_STR, uuid);
+ assert_int_equal(ret, EINVAL);
+
+ ret = sec_key_get_uuid(NULL, uuid);
+ assert_int_equal(ret, EINVAL);
+}
+
+void test_sec_key_get_name(void **state)
+{
+ const char *name;
+
+ name = sec_key_get_name(TEST_SEC_KEY_ONEDIGIT);
+ assert_non_null(name);
+ assert_string_equal(name, "0");
+
+ name = sec_key_get_name(TEST_SEC_KEY_MULTIDIGITS);
+ assert_non_null(name);
+ assert_string_equal(name, "123456");
+
+ name = sec_key_get_name(TEST_UUID_STR);
+ assert_null(name);
+
+ name = sec_key_get_name(TEST_SEC_KEY_NOSEP);
+ assert_null(name);
+
+ name = sec_key_get_name(NULL);
+ assert_null(name);
+}
+
+void test_sec_key_match_name(void **state)
+{
+ assert_true(sec_key_match_name(TEST_SEC_KEY_ONEDIGIT, "0"));
+ assert_true(sec_key_match_name(TEST_SEC_KEY_MULTIDIGITS, "123456"));
+
+ assert_false(sec_key_match_name(TEST_SEC_KEY_MULTIDIGITS, "0"));
+ assert_false(sec_key_match_name(TEST_SEC_KEY_ONEDIGIT, "123456"));
+
+ assert_false(sec_key_match_name(TEST_UUID_STR, "0"));
+ assert_false(sec_key_match_name(TEST_SEC_KEY_NOSEP, "0"));
+ assert_false(sec_key_match_name(TEST_SEC_KEY_ONEDIGIT, NULL));
+ assert_false(sec_key_match_name(NULL, "0"));
}
int main(int argc, const char *argv[])
@@ -205,6 +277,9 @@ int main(int argc, const char *argv[])
cmocka_unit_test_setup_teardown(test_kcm_ccache_marshall_unmarshall,
setup_kcm_marshalling,
teardown_kcm_marshalling),
+ cmocka_unit_test(test_sec_key_get_uuid),
+ cmocka_unit_test(test_sec_key_get_name),
+ cmocka_unit_test(test_sec_key_match_name),
};
/* Set debug level to invalid value so we can deside if -d 0 was used. */
--
2.9.3