You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 9 Next »

When working with files, it is important to ensure all operations take place in a secure directory. In most cases this means that no one other than the user or superuser (or Administrator) has the ability to read, write, execute, create, move, or delete files. Since C99 makes no mention of the concept of file systems or file permissions, any solution would necessarily be platform specific and likely not very portable.

For examples on how to create a secure directory inside another secure directory see FIO15-A. Do not create temporary files in shared directories.

Non-Compliant Code Example

This code example attempts to save a secret in a file specified by path. While path is assumed to come from a trusted source (see FIO02-A. Canonicalize path names originating from untrusted sources), this does not imply that an attacker will not be able to read the secret after it is written out to file.

void write_secret(const char* path, secret_t my_secret) {
  /* save my_secret to the file specified by path */
}

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, and are not accessible by any other users. When checking directories, it is important to traverse from the root to the top in order to avoid a dangerous race condition where an attacker who has privileges to at least one of the directories can delete and recreate a directory after the privilege verification.

This function uses the realpath() function to canonicalize the input path...see FIO02-A. Canonicalize path names originating from untrusted sources for more information on realpath(). It then checks every directory in the canonical path, ensuring that every directory is owned by the current user or by root. Furthermore, every directory must disallow write access to everyone but the owner, either by turning off group write access and world write access, or by turning on the sticky bit.

#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* path) {
  char *realpath_res = realpath(path, NULL);
  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 = getuid();

  if (realpath_res == NULL) {
    /* Handle Error */
  }

  if (!(path_copy = strdup(realpath_res))) {
    /* 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(realpath_res))) {
    /* Handle Error */
  }

  if(!(path_copy = strdup(realpath_res))) {
    /* 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 top, 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 */
      insecure = 0;
    } else if (!(buf.st_mode & S_ISVTX) &&
	       (buf.st_mode & (S_IWGRP | S_IWOTH))) {
      /* Others have permission to rename or remove files here */
      secure = 0;
    }
    free(dirs[i]);
    dirs[i] = NULL;
  }

  free(dirs);
  dirs = NULL;

  return secure;
}

This solution uses the secure_dir() function above to verify that my_secret is written to a secure file.

void write_secret(const char* path, secret_t my_secret) {
  if (!secure_dir(path)) {
    /* Handle Error */
  }
  /* save my_secret to the file specified by path */
}

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

FIO17-A

medium

probable

high

P4

L3

Related Vulnerabilities

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

References

[[ISO/IEC 9899:1999]]
[[Open Group 04]] dirname(), realpath()
[[Viega 03]] Section 2.4, "Determining Whether a Directory Is Secure"


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

  • No labels