Security Tips

Here is a short summary of security recommendations for the YaST developers. This is very likely not a full list, feel free to add more tips.

Obtaining the Root Privileges

YaST needs to be already started under the root account, there is no switch from unprivileged user to privileged one at runtime.

Some modules can be run under unprivileged user but they do not support switching to privileged user later. That means they usually work only in the read-only mode and do not allow to change anything.

The result is that there should not be any security problem in this area.

Sensitive Data

Registration codes, access tokens, private keys, passwords (in URLs as well!) or other sensitive data should never be logged in to the y2log. Although reading the y2log file requires root access it can be attached to bugzilla where everybody could read it.

  • Be careful when logging with .inspect, the result might contain the internal object data
  • For logging URLs use the URL.HidePassword() method

Temporary Files

Never use fixed or predictable names when writing to the /tmp directory (or in general any world-writable directory).

If you write to a predictable file the attacker could prepare a symlink with that name in advance pointing to some other file. Later when the user would run the code the other file would be overwritten.

Non-root Writable Directories

This is similar to the temporary files above but it is about the /home or similar directories.

To avoid a possible TOCTOU timing issue (CVE) you need to atomically check and create the file.

  • Use O_CREAT, O_EXCL, O_WRONLY flags when creating the file. In this case write fails if the file already exists. See man 2 open for more details.
  • Create an unique file on the same file system and use link to set the the final target name. See man 2 link and man 2 open for more details.

Examples

Ruby

# raises Errno::EEXIST if the file already exists
File.open(filename, File::WRONLY | File::CREAT | File::EXCL) do |file|
  file.write contents
end

C/C++

int fd = open(file, O_CREAT | O_EXCL | O_WRONLY);
if ((fd < 0) && (errno == EEXIST))
{
    // the file already exists
}

Downloading

Always prefer HTTPS over HTTP. It ensures you really connect to the intended server and the communication is encrypted so nobody can read it or modify it.

Do not disable the peer verification, if you still need to do it (e.g. because of a self-signed certificate) always ask the user for confirmation and display the certificate details so the user can at least verify the certificate fingerprint.

SSL Certificates

Downloading and importing a SSL certificate from an insecure source (e.g. HTTP) does not improve the security, in reality it makes it even worse.

If you need to import a certificate into the system the user should verify the certificate details (including the fingerprint) and confirm the import.

Package Download and Installation

Downloading and installing packages requires extra security checks. That includes repository meta-data verification and RPM signature verification. Fortunately libzypp already handles that for us.

  • Always use libzypp functionality (through yast2-pkg-bindings), never download or install the packages manually.

Running External Tools

The shell injection vulnerability is usually not a problem as YaST is already running as root and the input comes from the root user. So privilege escalation should not be possible.

But usually the problem happens when using a file with spaces in the file name or or the user input contains some special characters like <&%{}>. Then the module probably does not work as expected or can fail.

Also when executing external tools always use the absolute path to avoid file not found error when $PATH variable is not set properly or to avoid running possibly malicious replacements when the path contains the . directory (which is not recommended).

Another issue might be with the order of the $PATH components, e.g. /usr/local/bin:/usr/bin prefers the /usr/local/bin which might be also problematic.

  • Use the Yast::Execute module or the cheetah gem directly for running the external commands
  • Use the Shellwords.escape Ruby method, e.g. /bin/rpm -q #{Shellwords.escape(user_input)}
  • Use the absolute path, e.g. /bin/rpm
    Note: The very old YaST approach was NOT using the absolute path, this has been changed. But it might be still used somewhere, feel free to fix that.

Passing Sensitive Data to External Tools

Sometimes you need to pass sensitive data like password to an external tool.

  • Pass the data through a pipe connected to STDIN of the child process if it is possible to provide the data via the standard input.
  • Use pipe() and fork() in C++ (example), Open3.popen or IO.popen in Ruby
  • Use /proc/<PID>/fd/0 file

The other options are less secure or even insecure:

  • Save the data into a file, let the tool read the file
  • The file content is kept on the disk even after unlinking the file
  • Overwriting the file content might not help on some file systems (see man shred)
  • A FS snapshot might be created before removing the file
  • Pass the data on the command line - this is very insecure as it can be displayed in the ps output so it could be read by anybody on the local machine.
    (Exception: This could be possibly used during the initial installation where only the installer is running and no other user is logged in. But make sure this is not used in installed system.)

Debugging

The debugging features might also potentially affect security or leak some sensitive data.

  • If you need to use a separate log file (not the standard y2log) make sure the file has root-only access.
  • Use a directory accessible only to root, otherwise avoid file collisions, see the temporary files section.

When using a debugger (or any other special debugging features) make sure that they are disabled by default.

  • Enable the debugging features only on user request, use a command line option or an environment variable.
  • If the tool allows remote control then enable the remote access also only on request. And if possible by default allow only the local access via the loopback device (localhost/127.0.0.1).

See more details in the debugging document about the integrated Ruby debugger support in YaST.

Random Numbers

Linux has two devices generating random data, /dev/urandom (non-blocking) and cryptographically secure /dev/random (can block if there is not enough entropy).

Similarly in Ruby there is a simple rand() call and SecureRandom class providing cryptographically secure methods for generating random values.

  • Use the simple method when generating non-security related random values, e.g. a random host name, random time out, etc...
  • For security related values (access keys, tokens) use the cryptographically secure sources.