CVE-2011-3177 (published 2017-09-08T18:29Z)

The YaST2 network created files with world readable permissions which could have allowed local users to read sensitive material out of network configuration files, like passwords for wireless networks.

Severity: LOW
CVSS v2 base score: 2.1 (AV:L/AC:L/Au:N/C:P/I:N/A:N)
CVSS v3 base score: 7.8, HIGH (CVSS:3.0/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-200
cwe_name: Exposure of Sensitive Information to an Unauthorized Actor
description: The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
extended_description: 
	   There are many different kinds of mistakes that introduce information exposures. The severity of the error can range widely, depending on the context in which the product operates, the type of sensitive information that is revealed, and the benefits it may provide to an attacker.  Some kinds of sensitive information include:
       
         private, personal information, such as personal messages, financial data, health records, geographic location, or contact details
         system status and environment, such as the operating system and installed packages
		 business secrets and intellectual property
		 network status and configuration
		 the product's own code or internal state
         metadata, e.g. logging of connections or message headers
		 indirect information, such as a discrepancy between two internal operations that can be observed by an outsider
       
	    Information might be sensitive to different parties, each of which may have their own expectations for whether the information should be protected.  These parties include:
               
                  the product's own users
                  people or organizations whose information is created or used by the product, even if they are not direct product users
		  the product's administrators, including the admins of the system(s) and/or networks on which the product operates
                  the developer
               
	    Information exposures can occur in different ways:
               
                  the code explicitly inserts sensitive information into resources or messages that are intentionally made accessible to unauthorized actors, but should not contain the information - i.e., the information should have been "scrubbed" or "sanitized"
                  a different weakness or mistake indirectly inserts the sensitive information into resources, such as a web script error revealing the full system path of the program.
                  the code manages resources that intentionally contain sensitive information, but the resources are unintentionally made accessible to unauthorized actors. In this case, the information exposure is resultant - i.e., a different weakness enabled the access to the information in the first place.
               
	    It is common practice to describe any loss of confidentiality as an "information exposure," but this can lead to overuse of CWE-200 in CWE mapping. From the CWE perspective, loss of confidentiality is a technical impact that can arise from dozens of different weaknesses, such as insecure file permissions or out-of-bounds read.  CWE-200 and its lower-level descendants are intended to cover the mistakes that occur in behaviors that explicitly manage, store, transfer, or cleanse sensitive information.
         
         

Fix information:
repo_url: https://github.com/yast/yast-core
commit_details: repo_url: https://github.com/yast/yast-core
author: Martin Vidner
author_date: 2012-01-05 17:03:40+00:00
msg: - ini-agent: added Write(.section_private.SECTION, BOOLEAN) (bnc#713661, CVE-2011-3177)
- system agent: added  Write(.target.string, [filename, mode],  content)

svn merge -r66758:66764 http://svn.opensuse.org/svn/yast/branches/SuSE-Code-11-SP2-Branch/core

svn path=/trunk/core/; revision=67117
repository: repo_url: https://github.com/yast/yast-core
repo_name: yast/yast-core
description: YaST module core
repo_language: C++
owner: yast
file_changes: filename: VERSION
change_type: ModificationType.MODIFY
diff: @@ -1 +1 @@
-2.22.0
+2.22.1

programming_language: SQL
filename: ag_ini.html
change_type: ModificationType.MODIFY
diff: @@ -280,7 +280,7 @@ longer lines. Example:</p>
 <p>Then begin regexp is: ([^=]+)="([^"]*) and end regexp is
 ([^"]"). These are compared at the end so they are the last
 possibility. But once we get into this "divided line" by accident,
-it becomes greedy, so be carefull to forgotten ". If "multiline" is
+it becomes greedy, so be carefull to forgotten &quot;. If "multiline" is
 not present, this mechanism does not take in effect of course.</p>
 
 <p>See also the option <a
@@ -473,7 +473,7 @@ equivalent IniAgent is:</p>
         "comments": [ "^[ \t]*#.*$", "#.*", "^[ \t]*$", ],
         "params" : [
             $[
-                "match" : [ "([a-zA-Z0-9_]+)[ \t]*=[ \t]*\"([^\"]*)\"", "%s=\"%s\"" ],
+                "match" : [ "([a-zA-Z0-9_]+)[ \t]*=[ \t]*\"([^\&quot;]*)\"", "%s=\"%s\"" ],
                 "multiline" : [ "([a-zA-Z0-9_]+)[ \t]*=[ \t]*\"([^\"]*)", "([^\"]*)\"", ],
             ],
             $[
@@ -533,6 +533,13 @@ was read by.</td>
 section was read by.</td>
 </tr>
 
+<tr>
+<td>
+<tt>.ini.section_private.<i>sectionname</i></tt></td>
+<td>a boolean write-only property for sections corresponding to files.
+If true, the file will not be readable by group and others.</td>
+</tr>
+
 <tr class="notimpl">
 <td>
 <tt>.ini.section_file.<i>sectionname</i>.<i>sectionname</i></tt></td>
@@ -694,7 +701,7 @@ examples:<br />
       ],
       "params" : [
         $[
-        "match" : [ "^[ \t]*([^=]*[^ \t=])[ \t]*=[ \t]*(.*[^ \t]|)[ \t]*$" , "%s =       ],
+        "match" : [ "^[ \t]*([^=]*[^ \t=])[ \t]*=[ \t]*(.*[^ \t]|)[ \t]*$" , "%s = %s"   ],
     ],
     ]
   )

programming_language: HTML
filename: IniAgent.cc
change_type: ModificationType.MODIFY
diff: @@ -98,7 +98,7 @@ YCPBoolean IniAgent::Write(const YCPPath &path, const YCPValue& value, const YCP
     else
     {
 	if (( parser.repeatNames () && value->isList ()) ||
-	    (!parser.repeatNames () &&  (value->isString () || value->isInteger())) ||
+	    (!parser.repeatNames () &&  (value->isString () || value->isBoolean() || value->isInteger())) ||
 	    path->component_str(0) == "all"
 	    )
 	    {

programming_language: C++
filename: IniFile.cc
change_type: ModificationType.MODIFY
diff: @@ -75,6 +75,19 @@ YCPInteger as_integer (const YCPValue& v, const char * context)
     return YCPNull ();
 }
 
+/**
+ * Return the YCPBoolean or YCPNull if it is not one. Log an error.
+ */
+static
+YCPBoolean as_boolean (const YCPValue& v, const char * context)
+{
+    if (v->isBoolean ())
+	return v->asBoolean ();
+    ycp2error ("Expected a boolean for %s, got %s %s",
+	       context, v->valuetype_str(), v->toString().c_str());
+    return YCPNull ();
+}
+
 void IniSection::initValue (const string&key,const string&val,const string&comment,int rb)
 {
     string k = ip->changeCase (key);
@@ -486,6 +499,9 @@ int IniSection::Write (const YCPPath&p, const YCPValue&v, bool rewrite)
       return setSectionProp (p, v, 0, 1);
     if (s == "st" || s == "section_type" || s == "sectiontype")
       return setSectionProp (p, v, rewrite? 1:2, 1);
+    if (s == "section_private")
+      return setSectionProp (p, v, 3, 1);
+
     return -1;
 }
 
@@ -591,12 +607,18 @@ int IniSection::setSectionProp (const YCPPath&p,const YCPValue&in, int what, int
 			return -1;
 		    s.setRewriteBy (i->value());
 		}
-		else {
+		else if (what == 2) {
 		    YCPInteger i = as_integer (prop, "section_type");
 		    if (i.isNull())
 			return -1;
 		    s.setReadBy (i->value());
 		}
+		else if (what == 3) {
+		    YCPBoolean b = as_boolean (prop, "section_private");
+		    if (b.isNull())
+			return -1;
+		    s.setPrivate (b->value());
+		}
 
 		if (xi != xe)
 		{

programming_language: C++
filename: IniFile.h
change_type: ModificationType.MODIFY
diff: @@ -241,6 +241,13 @@ private:
      */
     string end_comment;
 
+    /**
+     * It is effective only when the section corresponds to a file.
+     * The file will not be readable by group and others.
+     * bnc#713661
+     */
+    bool is_private;
+
     /** index to IniParser::rewrites for filename - section name mapping
      * It appears that read_by was used for both purposes,
      * causing bug (#19066).
@@ -435,7 +442,7 @@ public:
     IniSection (const IniParser *p)
 	: IniBase (-1),
 	  ip (p),
-	  end_comment (), rewrite_by(-1),
+	  end_comment (), is_private(false), rewrite_by(-1),
 	  container (), ivalues (), isections ()
 	    {}
 
@@ -446,7 +453,7 @@ public:
     IniSection (const IniSection &s) :
 	IniBase (s),
 	  ip (s.ip),
-	  end_comment (s.end_comment), rewrite_by (s.rewrite_by),
+          end_comment (s.end_comment), is_private(s.is_private), rewrite_by (s.rewrite_by),
 	  container (s.container)
 	{ reindex (); }
 
@@ -458,7 +465,9 @@ public:
 	    } 
 	    IniBase::operator = (s);
 	    ip = s.ip;
-	    end_comment = s.end_comment; rewrite_by = s.rewrite_by;
+	    end_comment = s.end_comment;
+            is_private = s.is_private;
+            rewrite_by = s.rewrite_by;
 	    container = s.container;
 
 	    reindex ();
@@ -474,7 +483,7 @@ public:
     IniSection (const IniParser *p, string n)
 	: IniBase (n),
 	  ip (p),
-	  end_comment (), rewrite_by(0),
+	  end_comment (), is_private(false), rewrite_by(0),
 	  container(), ivalues (), isections ()
 	    {}
     /**
@@ -511,6 +520,9 @@ public:
      */
     int getSubSectionRewriteBy (const char*name);
 
+    void setPrivate(bool p) { is_private = p; }
+    bool isPrivate() const { return is_private; }
+
     /** 
      * If there is no comment at the beginning and no values and no
      * sections, it is better to set is as comment at the beginning.

programming_language: C++
filename: IniParser.cc
change_type: ModificationType.MODIFY
diff: @@ -965,19 +965,7 @@ int IniParser::write()
 			    continue;
 			}
 			s.initReadBy ();
-			// ensure that the directories exist
-			Pathname pn (filename);
-			PathInfo::assert_dir (pn.dirname ());
-			ofstream of(filename.c_str());
-			if (!of.good())
-			{
-			    bugs++;
-			    y2error ("Can not open file %s for write", filename.c_str());
-			    continue;
-			}
-			write_helper (s, of, 0);
-			s.clean();
-			of.close ();
+                        bugs += write_file(filename, s);
 		    }
 		else
 		    {
@@ -997,24 +985,37 @@ int IniParser::write()
     }
     else
     {
-	// ensure that the directories exist
-	Pathname pn (file);
-	PathInfo::assert_dir (pn.dirname ());
-	ofstream of(file.c_str());
-	if (!of.good())
-	{
-	    y2error ("Can not open file %s for write", file.c_str());
-	    return -1;
-	}
-
-	write_helper (inifile, of, 0);
-
-	of.close();
+        bugs += write_file(file, inifile);
 	timestamp = getTimeStamp ();
     }
-    inifile.clean ();
     return bugs ? -1 : 0;
 }
+
+// return 0 on success, like write
+int IniParser::write_file(const string & filename, IniSection & section)
+{
+    // ensure that the directories exist
+    Pathname pn(filename);
+    PathInfo::assert_dir (pn.dirname ());
+
+    mode_t file_umask = section.isPrivate()? 0077: 0022;
+    mode_t orig_umask = umask(file_umask);
+    // rewriting an existing file wouldnt change its mode
+    unlink(filename.c_str());
+
+    ofstream of(filename.c_str());
+    if (!of.good()) {
+        y2error ("Can not open file %s for write", filename.c_str());
+        return -1;
+    }
+
+    write_helper (section, of, 0);
+
+    of.close();
+    umask(orig_umask);
+    return 0;
+}
+
 int IniParser::write_helper(IniSection&ini, ofstream&of, int depth)
 {
     char * out_buffer;

programming_language: C++
filename: IniParser.h
change_type: ModificationType.MODIFY
diff: @@ -369,6 +369,10 @@ private:
      * Parse one ini file and build a structure of IniSection.
      */
     int parse_helper(IniSection&ini);
+    /**
+     * Write one ini file.
+     */
+    int write_file(const string & filename, IniSection & section);
     /**
      * Write one ini file.
      */

programming_language: C++
filename: private.err
change_type: ModificationType.ADD
diff: @@ -0,0 +1,11 @@
+[agent-ini] IniParser.cc(parse):XXX Rewriting multi/private.in.1.test to 1
+[agent-ini] IniParser.cc(parse):XXX Rewriting multi/private.in.2.test to 2
+[agent-ini] IniFile.cc(setMyValue):XXX Adding value .v."1"."Totalise"."Password" = "Secret password"
+[agent-ini] IniFile.cc(setMyValue):XXX Adding value .v."2"."arcor"."Password" = "Public password"
+[agent-ini] IniParser.cc(parse):XXX Rewriting multi/private.in.1.test to 1
+[agent-ini] IniParser.cc(parse):XXX Rewriting multi/private.in.2.test to 2
+[agent-ini] IniParser.cc(getFileName):XXX Rewriting 1 to multi/private.in.1.test
+[agent-ini] IniParser.cc(getFileName):XXX Rewriting 2 to multi/private.in.2.test
+[YCP] multi/private.ycp:XXX secret file mode: $["exit":0, "stderr":"", "stdout":"600\n"]
+[YCP] multi/private.ycp:XXX public file mode: $["exit":0, "stderr":"", "stdout":"644\n"]
+[agent-ini] IniParser.cc(write):XXX File multi/private.in.*.test did not change. Not saving.

programming_language: Batchfile
filename: private.in.1
change_type: ModificationType.ADD
diff: @@ -0,0 +1,10 @@
+[Dialer Totalise]
+Compuserve = 0
+Provider = Totalise
+Phone = 08453001470
+[End]
+
+[Dialer force9]
+Provider = Force9
+Phone = 08451424000
+[End]

programming_language: Markdown
filename: private.in.2
change_type: ModificationType.ADD
diff: @@ -0,0 +1,11 @@
+[Dialer arcor]
+Compuserve = 0
+Provider = Arcor
+[End]
+
+[Dialer mobilcom]
+Compuserve = 0
+Provider = Mobilcom
+Phone = 0101901929
+[End]
+

programming_language: Markdown
filename: private.out
change_type: ModificationType.ADD
diff: @@ -0,0 +1,25 @@
+(nil)
+multi/private.in.1.test  -------------------------------
+[Dialer Totalise]
+  Compuserve = 0
+  Provider = Totalise
+  Phone = 08453001470
+  Password = Secret password
+[End]
+
+[Dialer force9]
+  Provider = Force9
+  Phone = 08451424000
+[End]
+multi/private.in.2.test  -------------------------------
+[Dialer arcor]
+  Compuserve = 0
+  Provider = Arcor
+  Password = Public password
+[End]
+
+[Dialer mobilcom]
+  Compuserve = 0
+  Provider = Mobilcom
+  Phone = 0101901929
+[End]

programming_language: Markdown
filename: private.scr
change_type: ModificationType.ADD
diff: @@ -0,0 +1,24 @@
+.
+
+`ag_ini(
+  `IniAgent( [ "multi/private.in.*.test" ],
+    $[
+      "rewrite" : [
+	[ "multi/private\.in\.(.)\.test$", "multi/private.in.%s.test"],
+      ],
+      "subindent" : "  ",
+      "comments": [ "^[ \t]*#.*", "^[ \t]*$" ],
+      "sections" : [
+        $[
+        "begin" : [ "[ \t]*\\[Dialer[ \t]+(.*[^ \t])[ \t]*\\][ \t]*", "[Dialer %s]" ],
+	"end" : [ "^[ \t]*\\[End\\][ \t]*$", "[End]", ],
+        ],
+      ],
+      "params" : [
+        $[
+        "match" : [ "^[ \t]*([^=]*[^ \t=])[ \t]*=[ \t]*(.*[^ \t]|)[ \t]*$" , "%s = %s"],
+      ],
+    ],
+    ]
+  )
+)

programming_language: Shell
filename: private.ycp
change_type: ModificationType.ADD
diff: @@ -0,0 +1,26 @@
+//
+// Multiple files test
+//
+// providers.rpm
+//
+
+
+{
+    SCR::RegisterAgent (.target, `ag_system ());
+    any out = nil;
+
+    SCR::Write (.v."1"."Totalise"."Password", "Secret password");
+    SCR::Write (.section_private."1", true);
+
+    SCR::Write (.v."2"."arcor"."Password", "Public password");
+
+    SCR::Write (., nil); //flush
+
+    out = SCR::Execute (.target.bash_output, "stat -c %a multi/private.in.1.test");
+    y2milestone("secret file mode: %1", out);
+
+    out = SCR::Execute (.target.bash_output, "stat -c %a multi/private.in.2.test");
+    y2milestone("public file mode: %1", out);
+
+    return nil;
+}

programming_language: Rust
filename: SystemAgent.cc
change_type: ModificationType.MODIFY
diff: @@ -709,25 +709,50 @@ SystemAgent::Write (const YCPPath& path, const YCPValue& value,
     {
 	/**
 	 * @builtin Write (.target.string, string filename, string value) -> boolean
+	 * @builtin Write (.target.string, [string filename, integer filemode] , string value) -> boolean
 	 * Writes the string <tt>value</tt> into a file. If the file already
 	 * exists, the existing file is overwritten. The return value is
 	 * true, if the file has been written successfully.
+         *
+         * @example Write(.target.string, "/etc/papersize", "a4") -> true
+         * @example Write(.target.string, ["/etc/rsyncd.secrets", 0600], "user:passwd") -> true
 	 */
 
-	if (value.isNull() || !value->isString())
+	if (value.isNull() || !(value->isString() || value->isList()))
 	{
 	    ycp2error ("Bad filename arg for Write (.string ...)");
 	    return YCPBoolean (false);
 	}
 
+	string filename;
+	mode_t filemode = 0644;
+
+	if (value->isString())
+	{
+	    filename = value->asString()->value();
+	}
+	else
+	{			// value is list
+	    YCPList flist = value->asList();
+	    if ((flist->size() != 2)
+		|| (!flist->value(0)->isString())
+		|| (!flist->value(1)->isInteger()))
+	    {
+		ycp2error ("Bad [filename, mode] list in call to Write (%s, [ string filename, integer mode ], ...)",
+		    cmd.c_str ());
+		return YCPBoolean (false);
+	    }
+	    filename = flist->value(0)->asString()->value();
+	    filemode = (int)(flist->value(1)->asInteger()->value());
+	}
+
 	if (arg.isNull() || !arg->isString())
 	{
 	    ycp2error ("Bad string value for Write (.string ...)");
 	    return YCPBoolean (false);
 	}
 
-	string filename = value->asString()->value();
-	int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
+	int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, filemode);
 	if (fd >= 0)
 	{
 	    string cont = arg->asString()->value();
@@ -1039,8 +1064,8 @@ SystemAgent::Execute (const YCPPath& path, const YCPValue& value,
 	 * Creates a symbolic link named newpath which contains the
 	 * string oldpath.
 	 *
-	 * Symbolic links are interpreted at run-time as if the  con
-	 * tents of the link had been substituted into the path being
+	 * Symbolic links are interpreted at run-time as if the contents
+	 * of the link had been substituted into the path being
 	 * followed to find a file or directory.
 	 *
 	 * The return value is true or false, depending of the success.

programming_language: C++
filename: string.out
change_type: ModificationType.MODIFY
diff: @@ -3,3 +3,4 @@
 (true)
 (nil)
 ("never mind")
+("secret file mode: 600\n")

programming_language: Batchfile
filename: string.ycp
change_type: ModificationType.MODIFY
diff: @@ -24,3 +24,13 @@
     // this must not produce a error in the log
     return SCR::Read (.string, ["not-here.txt", "never mind"]);
 }
+
+{
+    map out = nil;
+    string filename = "tmp.secret.string";
+
+    SCR::Write (.string, [filename, 0600], "This is secret\n");
+
+    out = (map) SCR::Execute (.bash_output, "stat -c %a " + filename);
+    return "secret file mode: " + out["stdout"]:"";
+}

programming_language: C++
filename: yast2-core.changes
change_type: ModificationType.MODIFY
diff: @@ -1,3 +1,11 @@
+-------------------------------------------------------------------
+Mon Nov  7 18:19:27 CET 2011 - mvidner@suse.cz
+
+- ini-agent: added Write(.section_private.SECTION, BOOLEAN)
+  (bnc#713661, CVE-2011-3177)
+- system agent: added  Write(.target.string, [filename, mode], content)
+- 2.22.1
+
 -------------------------------------------------------------------
 Fri Dec  2 11:49:56 UTC 2011 - lslezak@suse.cz
 

programming_language: Lua