Blame SOURCES/0058-mmap_cache-make-checks-independent-of-input-size.patch

ced1f5
From 531c0ad3e13ddc1f5c31a620aa1f8b91aa8a4053 Mon Sep 17 00:00:00 2001
ced1f5
From: Sumit Bose <sbose@redhat.com>
ced1f5
Date: Fri, 17 Nov 2017 10:51:44 +0100
ced1f5
Subject: [PATCH 58/59] mmap_cache: make checks independent of input size
ced1f5
MIME-Version: 1.0
ced1f5
Content-Type: text/plain; charset=UTF-8
ced1f5
Content-Transfer-Encoding: 8bit
ced1f5
ced1f5
Currently the consistency checks for the mmap_cache payload data on the
ced1f5
client and the responder side include the length of the input string of
ced1f5
the current request. Since there might be hash collisions which other
ced1f5
much longer or much shorter names those checks might fail although there
ced1f5
is no data corruption.
ced1f5
ced1f5
This patch removes the checks using the length of the input and adds a
ced1f5
check if the name found in the payload is zero-terminated inside of the
ced1f5
payload data.
ced1f5
ced1f5
Resolves https://pagure.io/SSSD/sssd/issue/3571
ced1f5
ced1f5
Reviewed-by: Michal Židek <mzidek@redhat.com>
ced1f5
Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
ced1f5
(cherry picked from commit 4382047490dd4f80b407cc1e618da048f13e5f8f)
ced1f5
---
ced1f5
 src/responder/nss/nsssrv_mmap_cache.c | 34 ++++++++++++++++++++++++----------
ced1f5
 src/sss_client/nss_mc_group.c         | 14 ++++++++------
ced1f5
 src/sss_client/nss_mc_initgr.c        | 14 +++++++++-----
ced1f5
 src/sss_client/nss_mc_passwd.c        | 14 ++++++++------
ced1f5
 4 files changed, 49 insertions(+), 27 deletions(-)
ced1f5
ced1f5
diff --git a/src/responder/nss/nsssrv_mmap_cache.c b/src/responder/nss/nsssrv_mmap_cache.c
ced1f5
index a87ad646f9b741db3eb18680678697032fc420ba..ad5adbce15e50c065d4d16e626be97fd23d06643 100644
ced1f5
--- a/src/responder/nss/nsssrv_mmap_cache.c
ced1f5
+++ b/src/responder/nss/nsssrv_mmap_cache.c
ced1f5
@@ -547,18 +547,32 @@ static struct sss_mc_rec *sss_mc_find_record(struct sss_mc_ctx *mcc,
ced1f5
             return NULL;
ced1f5
         }
ced1f5
 
ced1f5
+        if (key->len > strs_len) {
ced1f5
+            /* The string cannot be in current record */
ced1f5
+            slot = sss_mc_next_slot_with_hash(rec, hash);
ced1f5
+            continue;
ced1f5
+        }
ced1f5
+
ced1f5
         safealign_memcpy(&name_ptr, rec->data, sizeof(rel_ptr_t), NULL);
ced1f5
-        if (key->len > strs_len
ced1f5
-            || (name_ptr + key->len) > (strs_offset + strs_len)
ced1f5
-            || (uint8_t *)rec->data + strs_offset + strs_len > max_addr) {
ced1f5
-            DEBUG(SSSDBG_FATAL_FAILURE,
ced1f5
-                  "Corrupted fastcache. name_ptr value is %u.\n", name_ptr);
ced1f5
-            sss_mc_save_corrupted(mcc);
ced1f5
-            sss_mmap_cache_reset(mcc);
ced1f5
-            return NULL;
ced1f5
-        }
ced1f5
-
ced1f5
         t_key = (char *)rec->data + name_ptr;
ced1f5
+        /* name_ptr must point to some data in the strs/gids area of the data
ced1f5
+         * payload. Since it is a pointer relative to rec->data it must larger
ced1f5
+         * equal strs_offset and must be smaller then strs_offset + strs_len.
ced1f5
+         * Additionally the area must not end outside of the data table and
ced1f5
+         * t_key must be a zero-terminates string. */
ced1f5
+        if (name_ptr < strs_offset
ced1f5
+                || name_ptr >= strs_offset + strs_len
ced1f5
+                || (uint8_t *)rec->data > max_addr
ced1f5
+                || strs_offset > max_addr - (uint8_t *)rec->data
ced1f5
+                || strs_len > max_addr - (uint8_t *)rec->data - strs_offset) {
ced1f5
+            DEBUG(SSSDBG_FATAL_FAILURE,
ced1f5
+                  "Corrupted fastcache entry at slot %u. "
ced1f5
+                  "name_ptr value is %u.\n", slot, name_ptr);
ced1f5
+            sss_mc_save_corrupted(mcc);
ced1f5
+            sss_mmap_cache_reset(mcc);
ced1f5
+            return NULL;
ced1f5
+        }
ced1f5
+
ced1f5
         if (strcmp(key->str, t_key) == 0) {
ced1f5
             break;
ced1f5
         }
ced1f5
diff --git a/src/sss_client/nss_mc_group.c b/src/sss_client/nss_mc_group.c
ced1f5
index ce88d42fdaf4f19e78fc43e187bc28651cdc3c4e..4b1601a171a3af700b6f0d2bfedb3a6198e6df6d 100644
ced1f5
--- a/src/sss_client/nss_mc_group.c
ced1f5
+++ b/src/sss_client/nss_mc_group.c
ced1f5
@@ -148,20 +148,22 @@ errno_t sss_nss_mc_getgrnam(const char *name, size_t name_len,
ced1f5
         }
ced1f5
 
ced1f5
         data = (struct sss_mc_grp_data *)rec->data;
ced1f5
+        rec_name = (char *)data + data->name;
ced1f5
         /* Integrity check
ced1f5
-         * - name_len cannot be longer than all strings
ced1f5
          * - data->name cannot point outside strings
ced1f5
          * - all strings must be within copy of record
ced1f5
-         * - size of record must be lower that data table size */
ced1f5
-        if (name_len > data->strs_len
ced1f5
-            || (data->name + name_len) > (strs_offset + data->strs_len)
ced1f5
+         * - record must not end outside data table
ced1f5
+         * - rec_name is a zero-terminated string */
ced1f5
+        if (data->name < strs_offset
ced1f5
+            || data->name >= strs_offset + data->strs_len
ced1f5
             || data->strs_len > rec->len
ced1f5
-            || rec->len > data_size) {
ced1f5
+            || (uint8_t *) rec + rec->len > gr_mc_ctx.data_table + data_size
ced1f5
+            || memchr(rec_name, '\0',
ced1f5
+                      (strs_offset + data->strs_len) - data->name) == NULL) {
ced1f5
             ret = ENOENT;
ced1f5
             goto done;
ced1f5
         }
ced1f5
 
ced1f5
-        rec_name = (char *)data + data->name;
ced1f5
         if (strcmp(name, rec_name) == 0) {
ced1f5
             break;
ced1f5
         }
ced1f5
diff --git a/src/sss_client/nss_mc_initgr.c b/src/sss_client/nss_mc_initgr.c
ced1f5
index a77088d849ad3601cb3edb55fc5ea4ae4c52fe38..d8c01f52ea2d23515d0b462541657dc9416b0915 100644
ced1f5
--- a/src/sss_client/nss_mc_initgr.c
ced1f5
+++ b/src/sss_client/nss_mc_initgr.c
ced1f5
@@ -131,15 +131,19 @@ errno_t sss_nss_mc_initgroups_dyn(const char *name, size_t name_len,
ced1f5
         data = (struct sss_mc_initgr_data *)rec->data;
ced1f5
         rec_name = (char *)data + data->name;
ced1f5
         /* Integrity check
ced1f5
-         * - name_len cannot be longer than all strings or data
ced1f5
+         * - data->name cannot point outside all strings or data
ced1f5
          * - all data must be within copy of record
ced1f5
          * - size of record must be lower that data table size
ced1f5
-         * - data->strs cannot point outside strings */
ced1f5
-        if (name_len > data->strs_len
ced1f5
+         * - data->strs cannot point outside strings
ced1f5
+         * - rec_name is a zero-terminated string */
ced1f5
+        if (data->name < data_offset
ced1f5
+            || data->name >= data_offset + data->data_len
ced1f5
             || data->strs_len > data->data_len
ced1f5
             || data->data_len > rec->len
ced1f5
-            || rec->len > data_size
ced1f5
-            || (data->strs + name_len) > (data_offset + data->data_len)) {
ced1f5
+            || (uint8_t *) rec + rec->len
ced1f5
+                                       > initgr_mc_ctx.data_table + data_size
ced1f5
+            || memchr(rec_name, '\0',
ced1f5
+                      (data_offset + data->data_len) - data->name) == NULL) {
ced1f5
             ret = ENOENT;
ced1f5
             goto done;
ced1f5
         }
ced1f5
diff --git a/src/sss_client/nss_mc_passwd.c b/src/sss_client/nss_mc_passwd.c
ced1f5
index 0da7ad0aeece7d38ca34bb3fde64adc898eaf0ae..868427f03a7ec0c8bd7401c8547a6f6bead7af28 100644
ced1f5
--- a/src/sss_client/nss_mc_passwd.c
ced1f5
+++ b/src/sss_client/nss_mc_passwd.c
ced1f5
@@ -141,20 +141,22 @@ errno_t sss_nss_mc_getpwnam(const char *name, size_t name_len,
ced1f5
         }
ced1f5
 
ced1f5
         data = (struct sss_mc_pwd_data *)rec->data;
ced1f5
+        rec_name = (char *)data + data->name;
ced1f5
         /* Integrity check
ced1f5
-         * - name_len cannot be longer than all strings
ced1f5
          * - data->name cannot point outside strings
ced1f5
          * - all strings must be within copy of record
ced1f5
-         * - size of record must be lower that data table size */
ced1f5
-        if (name_len > data->strs_len
ced1f5
-            || (data->name + name_len) > (strs_offset + data->strs_len)
ced1f5
+         * - record must not end outside data table
ced1f5
+         * - rec_name is a zero-terminated string */
ced1f5
+        if (data->name < strs_offset
ced1f5
+            || data->name >= strs_offset + data->strs_len
ced1f5
             || data->strs_len > rec->len
ced1f5
-            || rec->len > data_size) {
ced1f5
+            || (uint8_t *) rec + rec->len > pw_mc_ctx.data_table + data_size
ced1f5
+            || memchr(rec_name, '\0',
ced1f5
+                      (strs_offset + data->strs_len) - data->name) == NULL ) {
ced1f5
             ret = ENOENT;
ced1f5
             goto done;
ced1f5
         }
ced1f5
 
ced1f5
-        rec_name = (char *)data + data->name;
ced1f5
         if (strcmp(name, rec_name) == 0) {
ced1f5
             break;
ced1f5
         }
ced1f5
-- 
ced1f5
2.14.3
ced1f5