From c449946b9d06571b447fce3fc0dcad89e8df05b5 Mon Sep 17 00:00:00 2001
From: Bart Van Assche <bvanassche@acm.org>
Date: Wed, 15 May 2019 14:09:25 +0200
Subject: [PATCH 1/5] CHANGES: libsnmp: Scan MIB directories in alphabetical
 order

This guarantees that e.g. mibs/RFC1213-MIB.txt is read before mibs/SNMPv2-MIB.txt.
The order in which these MIBs is read matters because both define sysLocation but
with different attributes.

CVE: CVE-2020-15861
Upstream-Status: Backport [https://github.com/net-snmp/net-snmp/commit/2b3e300ade4add03b889e61d610b0db77d300fc3]

Signed-off-by: Ovidiu Panait <ovidiu.panait@windriver.com>
---
 snmplib/parse.c | 113 +++++++++++++++++++++++++++++++++++-------------
 1 file changed, 82 insertions(+), 31 deletions(-)

diff --git a/snmplib/parse.c b/snmplib/parse.c
index 7678b35..51d119b 100644
--- a/snmplib/parse.c
+++ b/snmplib/parse.c
@@ -4894,6 +4894,79 @@ add_mibfile(const char* tmpstr, const char* d_name, FILE *ip )
     }
 }
 
+static int elemcmp(const void *a, const void *b)
+{
+    const char *const *s1 = a, *const *s2 = b;
+
+    return strcmp(*s1, *s2);
+}
+
+/*
+ * Scan a directory and return all filenames found as an array of pointers to
+ * directory entries (@result).
+ */
+static int scan_directory(char ***result, const char *dirname)
+{
+    DIR            *dir, *dir2;
+    struct dirent  *file;
+    char          **filenames = NULL;
+    int             fname_len, i, filename_count = 0, array_size = 0;
+    char           *tmpstr;
+
+    *result = NULL;
+
+    dir = opendir(dirname);
+    if (!dir)
+        return -1;
+
+    while ((file = readdir(dir))) {
+        /*
+         * Only parse file names that don't begin with a '.'
+         * Also skip files ending in '~', or starting/ending
+         * with '#' which are typically editor backup files.
+         */
+        fname_len = strlen(file->d_name);
+        if (fname_len > 0 && file->d_name[0] != '.'
+            && file->d_name[0] != '#'
+            && file->d_name[fname_len-1] != '#'
+            && file->d_name[fname_len-1] != '~') {
+            if (asprintf(&tmpstr, "%s/%s", dirname, file->d_name) < 0)
+                continue;
+            dir2 = opendir(tmpstr);
+            if (dir2) {
+                /* file is a directory, don't read it */
+                closedir(dir2);
+            } else {
+                if (filename_count >= array_size) {
+                    char **new_filenames;
+
+                    array_size = (array_size + 16) * 2;
+                    new_filenames = realloc(filenames,
+                                        array_size * sizeof(filenames[0]));
+                    if (!new_filenames) {
+                        free(tmpstr);
+                        for (i = 0; i < filename_count; i++)
+                            free(filenames[i]);
+                        free(filenames);
+                        closedir(dir);
+                        return -1;
+                    }
+                    filenames = new_filenames;
+                }
+                filenames[filename_count++] = tmpstr;
+                tmpstr = NULL;
+            }
+            free(tmpstr);
+        }
+    }
+    closedir(dir);
+
+    qsort(filenames, filename_count, sizeof(filenames[0]), elemcmp);
+    *result = filenames;
+
+    return filename_count;
+}
+
 /* For Win32 platforms, the directory does not maintain a last modification
  * date that we can compare with the modification date of the .index file.
  * Therefore there is no way to know whether any .index file is valid.
@@ -4904,12 +4977,11 @@ int
 add_mibdir(const char *dirname)
 {
     FILE           *ip;
-    DIR            *dir, *dir2;
     const char     *oldFile = File;
-    struct dirent  *file;
+    char          **filenames;
     char            tmpstr[300];
     int             count = 0;
-    int             fname_len = 0;
+    int             filename_count, i;
 #if !(defined(WIN32) || defined(cygwin))
     char           *token;
     char space;
@@ -4957,36 +5029,15 @@ add_mibdir(const char *dirname)
         DEBUGMSGTL(("parse-mibs", "No index\n"));
 #endif
 
-    if ((dir = opendir(dirname))) {
-        ip = netsnmp_mibindex_new( dirname );
-        while ((file = readdir(dir))) {
-            /*
-             * Only parse file names that don't begin with a '.' 
-             * Also skip files ending in '~', or starting/ending
-             * with '#' which are typically editor backup files.
-             */
-            if (file->d_name != NULL) {
-              fname_len = strlen( file->d_name );
-              if (fname_len > 0 && file->d_name[0] != '.' 
-                                && file->d_name[0] != '#'
-                                && file->d_name[fname_len-1] != '#'
-                                && file->d_name[fname_len-1] != '~') {
-                snprintf(tmpstr, sizeof(tmpstr), "%s/%s", dirname, file->d_name);
-                tmpstr[ sizeof(tmpstr)-1 ] = 0;
-                if ((dir2 = opendir(tmpstr))) {
-                    /*
-                     * file is a directory, don't read it 
-                     */
-                    closedir(dir2);
-                } else {
-                    if ( !add_mibfile( tmpstr, file->d_name, ip ))
-                        count++;
-                }
-              }
-            }
+    filename_count = scan_directory(&filenames, dirname);
+
+    if (filename_count >= 0) {
+        ip = netsnmp_mibindex_new(dirname);
+        for (i = 0; i < filename_count; i++) {
+            if (add_mibfile(filenames[i], strrchr(filenames[i], '/'), ip) == 0)
+                count++;
         }
         File = oldFile;
-        closedir(dir);
         if (ip)
             fclose(ip);
         return (count);
-- 
2.17.1

