Versions Compared

Key

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

...

Code Block
bgColor#FFCCCC
char *file_name;
FILE *fp;

/* initialize file_name */

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

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

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

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

An attacker can replace the file object identified by file_name with a link to an arbitrary file before the call to fopen(). It is also possible that the file object identified by file_name in the call to remove() is not the same file object identified by file_name in the call to fopen(). If the file is not in a secure directory, for example, /tmp/app/tmpdir/passwd, then an attacker can manipulate the location of the file as follows:

...

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* 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 = geteuid();

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

  if (!(path_copy = strdup(realpath_res))) {
    /* Handle Errorerror */
  }

  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 Errorerror */
  }
  if (!(dirs[num_of_dirs - 1] = strdup(realpath_res))) {
    /* Handle Errorerror */
  }

  if(!(path_copy = strdup(realpath_res))) {
    /* Handle Errorerror */
  }

  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 Errorerror */
    }
    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_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;
}

...

Code Block
bgColor#ccccff
char *file_name;
FILE *fp;

/* initialize file_name */

if (!secure_dir(file_name)) {
  /* Handle Errorerror */
}

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

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

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

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

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.

...