Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

The shell would interpret this string as two separate commands,:

Code Block
any_cmd 'happy';
useradd 'attacker'

...

This compliant solution is significantly different from the preceding noncompliant code example. First, input is incorporated into the args array and passed as an argument to execve(), eliminating any concerns about buffer overflow or string truncation while forming the command string. Second, this compliant solution forks a new process before executing "/usr/bin/any_cmd" in the child process. Although this method is more complicated than calling system(), the added security is worth the additional effort.

The exit status of 127 is the value set by the shell when a command is not found, and POSIX recommends that applications should do the same. XCU, Section 2.8.2, of Standard for Information Technology—Portable Operating System Interface (POSIX®), Base Specifications, Issue 7 [IEEE Std 1003.1:2013], says:

If a command is not found, the exit status shall be 127. If the command name is found, but it is not an executable utility, the exit status shall be 126. Applications that invoke utilities without using the shell should use these exit status values to report similar errors.

...

An alternative to invoking the system() call to execute an external program to perform a required operation is to implement the functionality directly in the program using existing library calls. This compliant solution calls the POSIX unlink() function to remove a file without invoking the system() function [IEEE Std 1003.1:2013]:

Code Block
bgColor#ccccff
languagec
#include <pwd.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
void func(void) {
  const char *file_format = "%s/.config";
  size_t len;
  char *pathname;
  struct passwd *pwd;

  /* Get /etc/passwd entry for current user */
  pwd = getpwuid(getuid());
  if (pwd == NULL) {
    /* Handle error */
  }

  /* Build full path name home dir from pw entry */

  len = strlen(pwd->pw_dir) + strlen(file_format) + 1;
  pathname = (char *)malloc(len);
  if (NULL == pathname) {
    /* Handle error */
  }
  int r = snprintf(pathname, len, file_format, pwd->pw_dir);
  if (r < 0 || r >= len) {
    /* Handle error */
  }
  if (unlink(pathname) != 0) {
    /* Handle error */
  }

  free(pathname);
}

The unlink() function is not susceptible to a symlink attack where the final component of pathname (the file name) is a symbolic link because unlink() will remove the symbolic link and not affect any file or directory named by the contents of the symbolic link. (see See FIO01-C. Be careful using functions that use file names for identification.) .  While this reduces the susceptibility of the unlink() function to symlink attacks, it does not eliminate it.  The unlink() function is still susceptible if one of the directory names included in the pathname is a symbolic link.  This could cause the unlink() function to delete a similarly named file in a different directory.

...