CVE-2020-14976: Improper Privilege Management (CWE-269)
value: GNS3 ubridge through 0.9.18 on macOS, as used in GNS3 server before 2.1.17, allows a local attacker to read arbitrary files because it handles configuration-file errors by printing the configuration file while executing in a setuid root context.
CWE description: The product does not properly assign, modify, track, or check privileges for an actor, creating an unintended sphere of control for that actor.
CVSS v3 base score: 5.5
Published: 2020-06-23T20:15Z
Repository: https://github.com/GNS3/ubridge
Language: C
Files changed: src/iniparser/iniparser.c, src/iniparser/iniparser.h, src/parse.c
Commit date: 2019-05-08 16:48:24 +0200
Commit message: Hide errored line content during parsing configuration INI file on default

Diff: diff --git a/src/iniparser/iniparser.c b/src/iniparser/iniparser.c
index 1eb1004..5dd6751 100644
--- a/src/iniparser/iniparser.c
+++ b/src/iniparser/iniparser.c
@@ -624,11 +624,11 @@ static line_status iniparser_line(
   instead.
 
   The returned dictionary must be freed using iniparser_freedict().
  */
 /*--------------------------------------------------------------------------*/
-dictionary * iniparser_load(const char * ininame)
+dictionary * iniparser_load(const char * ininame, load_options options)
 {
     FILE * in ;
 
     char line    [ASCIILINESZ+1] ;
     char section [ASCIILINESZ+1] ;
@@ -705,14 +705,22 @@ dictionary * iniparser_load(const char * ininame)
             sprintf(tmp, "%s:%s", section, key);
             errs = dictionary_set(dict, tmp, val) ;
             break ;
 
             case LINE_ERROR:
-            fprintf(stderr, "iniparser: syntax error in %s (%d):\n",
-                    ininame,
-                    lineno);
-            fprintf(stderr, "-> %s\n", line);
+
+            if(options & HIDE_ERRORED_LINE_CONTENT) {
+              fprintf(stderr, "iniparser: syntax error in %s (%d)\n",
+                      ininame,
+                      lineno);
+            }
+            else {
+              fprintf(stderr, "iniparser: syntax error in %s (%d):\n",
+                      ininame,
+                      lineno);
+              fprintf(stderr, "-> %s\n", line);
+            }
             errs++ ;
             break;
 
             default:
             break ;
diff --git a/src/iniparser/iniparser.h b/src/iniparser/iniparser.h
index 0a9da72..0fb89af 100644
--- a/src/iniparser/iniparser.h
+++ b/src/iniparser/iniparser.h
@@ -29,10 +29,14 @@
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+typedef enum {
+  HIDE_ERRORED_LINE_CONTENT = (1<<0)
+} load_options;
+
 /*-------------------------------------------------------------------------*/
 /**
   @brief    Get number of sections in a dictionary
   @param    d   Dictionary to examine
   @return   int Number of sections found in dictionary
@@ -284,21 +288,22 @@ int iniparser_find_entry(const dictionary * ini, const char * entry) ;
 
 /*-------------------------------------------------------------------------*/
 /**
   @brief    Parse an ini file and return an allocated dictionary object
   @param    ininame Name of the ini file to read.
+  @param    options Name of the ini file to read.
   @return   Pointer to newly allocated dictionary
 
   This is the parser for ini files. This function is called, providing
   the name of the file to be read. It returns a dictionary object that
   should not be accessed directly, but through accessor functions
   instead.
 
   The returned dictionary must be freed using iniparser_freedict().
  */
 /*--------------------------------------------------------------------------*/
-dictionary * iniparser_load(const char * ininame);
+dictionary * iniparser_load(const char * ininame, load_options options);
 
 /*-------------------------------------------------------------------------*/
 /**
   @brief    Free all memory associated to an ini dictionary
   @param    d Dictionary to free
diff --git a/src/parse.c b/src/parse.c
index 82e881b..0722b9c 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -184,11 +184,11 @@ int parse_config(char *filename, bridge_t **bridges)
     dictionary *ubridge_config = NULL;
     const char *value;
     const char *bridge_name;
     int i, nsec;
 
-    if ((ubridge_config = iniparser_load(filename)) == NULL) {
+    if ((ubridge_config = iniparser_load(filename, HIDE_ERRORED_LINE_CONTENT)) == NULL) {
        return FALSE;
     }
 
     nsec = iniparser_getnsec(ubridge_config);
     for (i = 0; i < nsec; i++) {

Vulnerable code: dictionary * iniparser_load(const char * ininame)
            fprintf(stderr, "iniparser: syntax error in %s (%d):\n",
                    ininame,
                    lineno);
            fprintf(stderr, "-> %s\n", line);
dictionary * iniparser_load(const char * ininame);
    if ((ubridge_config = iniparser_load(filename)) == NULL) {
Fixed code: dictionary * iniparser_load(const char * ininame, load_options options)

            if(options & HIDE_ERRORED_LINE_CONTENT) {
              fprintf(stderr, "iniparser: syntax error in %s (%d)\n",
                      ininame,
                      lineno);
            }
            else {
              fprintf(stderr, "iniparser: syntax error in %s (%d):\n",
                      ininame,
                      lineno);
              fprintf(stderr, "-> %s\n", line);
            }
typedef enum {
  HIDE_ERRORED_LINE_CONTENT = (1<<0)
} load_options;

  @param    options Name of the ini file to read.
dictionary * iniparser_load(const char * ininame, load_options options);
    if ((ubridge_config = iniparser_load(filename, HIDE_ERRORED_LINE_CONTENT)) == NULL) {