CVE-2011-3177 (C++, published 2017-09-08T18:29Z)
Description: value: 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 v3 base score: 7.8)
Weakness: CWE-200 \u2014 Exposure of Sensitive Information to an Unauthorized Actor
The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
Repository: https://github.com/yast/yast-core
Files changed:
VERSION
agent-ini/doc/ag_ini.html
agent-ini/src/IniAgent.cc
agent-ini/src/IniFile.cc
agent-ini/src/IniFile.h
agent-ini/src/IniParser.cc
agent-ini/src/IniParser.h
agent-ini/testsuite/multi/private.err
agent-ini/testsuite/multi/private.in.1
agent-ini/testsuite/multi/private.in.2
agent-ini/testsuite/multi/private.out
agent-ini/testsuite/multi/private.scr
agent-ini/testsuite/multi/private.ycp
agent-system/src/SystemAgent.cc
agent-system/testsuite/tests/string.out
agent-system/testsuite/tests/string.ycp
package/yast2-core.changes
Commit message: - 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


Vulnerable code:
2.22.0
it becomes greedy, so be carefull to forgotten ". If "multiline" is
                "match" : [ "([a-zA-Z0-9_]+)[ \t]*=[ \t]*\"([^\"]*)\"", "%s=\"%s\"" ],
        "match" : [ "^[ \t]*([^=]*[^ \t=])[ \t]*=[ \t]*(.*[^ \t]|)[ \t]*$" , "%s =       ],
	    (!parser.repeatNames () &&  (value->isString () || value->isInteger())) ||
		else {
	  end_comment (), rewrite_by(-1),
	  end_comment (s.end_comment), rewrite_by (s.rewrite_by),
	    end_comment = s.end_comment; rewrite_by = s.rewrite_by;
	  end_comment (), rewrite_by(0),
			// 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 ();
	// 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();
    inifile.clean ();
	if (value.isNull() || !value->isString())
	string filename = value->asString()->value();
	int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
	 * Symbolic links are interpreted at run-time as if the  con­
	 * tents of the link had been substituted into the path being

Fixed code:
2.22.1
it becomes greedy, so be carefull to forgotten &quot;. If "multiline" is
                "match" : [ "([a-zA-Z0-9_]+)[ \t]*=[ \t]*\"([^\&quot;]*)\"", "%s=\"%s\"" ],
<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>

        "match" : [ "^[ \t]*([^=]*[^ \t=])[ \t]*=[ \t]*(.*[^ \t]|)[ \t]*$" , "%s = %s"   ],
	    (!parser.repeatNames () &&  (value->isString () || value->isBoolean() || value->isInteger())) ||
/**
 * 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 ();
}

    if (s == "section_private")
      return setSectionProp (p, v, 3, 1);

		else if (what == 2) {
		else if (what == 3) {
		    YCPBoolean b = as_boolean (prop, "section_private");
		    if (b.isNull())
			return -1;
		    s.setPrivate (b->value());
		}
    /**
     * 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;

	  end_comment (), is_private(false), rewrite_by(-1),
          end_comment (s.end_comment), is_private(s.is_private), rewrite_by (s.rewrite_by),
	    end_comment = s.end_comment;
            is_private = s.is_private;
            rewrite_by = s.rewrite_by;
	  end_comment (), is_private(false), rewrite_by(0),
    void setPrivate(bool p) { is_private = p; }
    bool isPrivate() const { return is_private; }

                        bugs += write_file(filename, s);
        bugs += write_file(file, inifile);

// 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;
}

    /**
     * Write one ini file.
     */
    int write_file(const string & filename, IniSection & section);
[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.
[Dialer Totalise]
Compuserve = 0
Provider = Totalise
Phone = 08453001470
[End]

[Dialer force9]
Provider = Force9
Phone = 08451424000
[End]
[Dialer arcor]
Compuserve = 0
Provider = Arcor
[End]

[Dialer mobilcom]
Compuserve = 0
Provider = Mobilcom
Phone = 0101901929
[End]

(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]
.

`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"],
      ],
    ],
    ]
  )
)
//
// 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;
}
	 * @builtin Write (.target.string, [string filename, integer filemode] , string value) -> boolean
         *
         * @example Write(.target.string, "/etc/papersize", "a4") -> true
         * @example Write(.target.string, ["/etc/rsyncd.secrets", 0600], "user:passwd") -> true
	if (value.isNull() || !(value->isString() || value->isList()))
	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());
	}

	int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, filemode);
	 * Symbolic links are interpreted at run-time as if the contents
	 * of the link had been substituted into the path being
("secret file mode: 600\n")

{
    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"]:"";
}
-------------------------------------------------------------------
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