Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Edited by sciSpider v2.4 (sch jbop) (X_X)@==(Q_Q)@

...

Ensuring that file systems are configured in a safe manner is typically a system administration function. However, programs can often check that a file system is securely configured before performing file operations that may potentially lead to security vulnerabilities if the system is misconfigured. There is a slight possibility that file systems will be reconfigured in an insecure manner while a process is running and after the check has been made. As a result, it is always advisable to implement your code in a secure manner (that is, consistent with the other rules and recommendations in this section) even when running in a secure directory.

...

Noncompliant Code Example

In this non-compliant noncompliant code example, the file identified by file_name is opened, processed, closed, and removed.

...

There is not much that can be programmatically done to ensure the file removed is the same file that was opened, processed, and closed except to make sure that the file is opened in a secure directory with privileges that would prevent the file from being manipulated by an untrusted user.

Compliant Solution (POSIX)

This example implementation of a secure_dir() function will ensure that path and all directories above it are owned either by the user or the superuser, that path does not have write access for any other users, and that directories above path may not be deleted or renamed by any other users. When checking directories, it is important to traverse from the root to the leaf in order to avoid a dangerous race condition where an attacker who has privileges to at least one of the directories can rename and recreate a directory after the privilege verification.

...

Code Block
bgColor#ccccff
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <libgen.h>
#include <sys/stat.h>
#include <string.h>

/* Returns nonzero if directory is secure, zero otherwise */
int secure_dir(const char * fullpath) {
  char *path_copy = NULL;
  char *dirname_res = NULL;
  char ** dirs = NULL;
  int num_of_dirs = 0;
  int secure = 1;
  int i;
  struct stat buf;
  uid_t my_uid = geteuid();


  if (!(path_copy = strdup(fullpath))) {
    /* Handle error */
  }

  dirname_res = path_copy;
  /* Figure out how far it is to the root */
  while (1) {
    dirname_res = dirname(dirname_res);

    num_of_dirs++;

    if ((strcmp(dirname_res, "/") == 0) ||
	(strcmp(dirname_res, "//") == 0)) {
      break;
    }
  }
  free(path_copy);
  path_copy = NULL;

  /* Now allocate and fill the dirs array */
  if (!(dirs = (char **)malloc(num_of_dirs*sizeof(*dirs)))) {
    /* Handle error */
  }
  if (!(dirs[num_of_dirs - 1] = strdup(fullpath))) {
    /* Handle error */
  }

  if (!(path_copy = strdup(fullpath))) {
    /* Handle error */
  }

  dirname_res = path_copy;
  for (i = 1; i < num_of_dirs; i++) {
    dirname_res = dirname(dirname_res);

    dirs[num_of_dirs - i - 1] = strdup(dirname_res);

  }
  free(path_copy);
  path_copy = NULL;

  /* Traverse from the root to the leaf, checking
   * permissions along the way */
  for (i = 0; i < num_of_dirs; i++) {
    if (stat(dirs[i], &buf) != 0) {
       /* Handle error */
    }
    if ((buf.st_uid != my_uid) && (buf.st_uid != 0)) {
      /* Directory is owned by someone besides user or root */
      secure = 0;
    } else if ((buf.st_mode & (S_IWGRP | S_IWOTH))
      && ((i == num_of_dirs - 1) || !(buf.st_mode & S_ISVTX))) {
        /* Others have permissions to the leaf directory
         * or are able to delete or rename files along the way */
        secure = 0;
     }
            
    free(dirs[i]);
    dirs[i] = NULL;
  }

  free(dirs);
  dirs = NULL;

  return secure;
}

...

Code Block
bgColor#ccccff
char *dir_name;
char *canonical_dir_name;
const char const *file_name = "passwd"; /* file name within the secure directory */
FILE *fp;

/* initialize dir_name */

canonical_dir_name = realpath(dir_name, NULL);
if (canonical_dir_name == NULL) {
  /* Handle error */
}

if (!secure_dir(canonical_dir_name)) {
  /* Handle error */
}

if (chdir(canonical_dir_name) == -1) {
  /* Handle error */
}

fp = fopen(file_name, "w");
if (fp == NULL) {
  /* Handle error */
}

/*... Process file ...*/

if (fclose(fp) != 0) {
  /* Handle error */
}

if (remove(file_name) != 0) {
  /* Handle error */
}

Risk Assessment

Failing to ensure proper permissions in a directory may lead to sensitive data getting saved to (or critical configuration or other input files being read from) public directories to which an attacker has access.

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

FIO15-A C

medium

probable

high

P4

L3

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

References

Wiki Markup
\[[ISO/IEC 9899:1999|AA. C References#ISO/IEC 9899-1999]\]
\[[Open Group 04|AA. C References#Open Group 04]\] [{{dirname()}}|http://www.opengroup.org/onlinepubs/009695399/functions/dirname.html], [{{realpath()}}|http://www.opengroup.org/onlinepubs/009695399/functions/realpath.html]
\[[Viega 03|AA. C References#Viega 03]\] Section 2.4, "Determining Whether a Directory Is Secure"

...

      09. Input Output (FIO)       FIO16-AC. Limit access to files by creating a jail