Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Updated secure_dir() compliant solution.

...

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

enum { MAX_SYMLINKS = 5 };

/* Returns nonzero if directory is secure, zero otherwise */
int secure_dir(const char *fullpath) {
  static unsigned int num_symlinks = 0;
  char *path_copy = NULL;
  char **dirs = NULL;
  int num_of_dirs = 1;
  int secure = 1;
  int i, r;
  struct stat buf;
  uid_t my_uid = geteuid();
  size_t linksize;
  char* link;
  
  if (!(path_copy = strdup(fullpath)))fullpath || fullpath[0] != '/') {
    /* Handle error */
  }
  
  /* Figure out how far it is to the root */
  for (; ((strcmp(path_copy, "/") != 0) &&
          (strcmp(path_copy, "//") != 0));
       path_copy = dirname(path_copy)) {
    num_of_dirs++;
  } // now num_of_dirs indicates # of dirs we must check
  free(path_copy);
  path_copy = NULL;

  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))) {
    if (num_symlinks > MAX_SYMLINKS) {  // Could be a symlink loop
    /* Handle error */
  }
 
  if (!(path_copy = strdup(fullpath))) {
    /* Handle error */
  }
 
  // Figure out how far it is to the root
  for (; ((strcmp(path_copy, "/") != 0) &&
          (strcmp(path_copy, "//") != 0));
       path_copy = dirname(path_copy)) {
    num_of_dirs++;
  } // now num_of_dirs indicates # of dirs we must check
  free(path_copy);
  path_copy = NULL;
 
  if (!(dirs = (char **)malloc(num_of_dirs * sizeof(char *)))) {
    /* Handle error */
  }

  /* Now fill the dirs array */
  forif (i = !(dirs[num_of_dirs - 2; i >= 0; i--) {
1] = strdup(fullpath))) {
    /* path_copyHandle = dirname(path_copy);
  error */
  }
 
  if (!(dirs[i]path_copy = strdup(path_copyfullpath))) {
      /* handleHandle error */
    }
  }
  free(path_copy);
  path_copy = NULL;

  /* Traverse from the root to the fullpath,
   * checking permissions along the way */
  // Now fill the dirs array
  for (i = 0; i < num_of_dirs; i++) {
 - 2; i >= 0; i--) {
    path_copy = dirname(path_copy);
    if (!(dirs[i] = strdup(path_copy))) {
      /* Handle error */
    }
  }
  free(path_copy);
  path_copy = NULL;
 
  // Traverse from the root to the fullpath,
  // checking permissions along the way
  for (i = 0; i < num_of_dirs; i++) {
    if (lstat(dirs[i], &buf) != 0) {
      /* Handle error */
    }
    
    if (S_ISLNK(buf.st_mode)) { // symlink, test linked-to file
      linksize = buf.st_size + 1;
      if (lstat(dirs[i], &buf) != 0!(link = (char *)malloc(linksize))) {
        /* Handle error */
      }
    if (S_ISLNK(buf.st_mode)) { //
 symlink, test linked-to file
  r = readlink(dirs[i], link, linksize = buf.st_size+1);
      if (!(linkr = (char *)malloc(linksize))= -1) {
        /* Handle error */
      }
      if (readlink( dirs[i], link, linksize) == -1 else if (r >= linksize) {
        /* Handle truncation error */
      }
      link[linksize-1r] = '\0';

      if (!secure_dir(link)) {num_symlinks++;
      r = secure = 0_dir(link);
      }num_symlinks--;
      free(link);
      link = NULL;if (!r) {
      break;
  secure = }0;

    if (!S_ISDIR( buf.st_mode)) { // not a directory
 free(link);
         securelink = 0NULL;
      break;
    }break;
    if ((buf.st_uid != my_uid) && (buf.st_uid != 0)) { }
      
      /* Directory is owned by someone besides user or root */
  free(link);
      link = NULL;
    secure = 0;
      breakcontinue;
    }
    
    if (i == num_of_dirs - 1!S_ISDIR(buf.st_mode)) { //* leafnot dira */directory
      secure if (buf.st_mode & (S_IWGRP | S_IWOTH)) { /* dir is writable by others */= 0;
      break;
    }
    
    if ((buf.st_uid != my_uid)  secure && (buf.st_uid != 0;)) {
      // Directory is break;
owned by someone besides user or }root
    } else {secure /* parent dirs */= 0;
      if ((buf.st_mode & (S_IWGRP | S_IWOTH)) ||break;
    }
    
     if (buf.st_mode & (S_ISVTXIWGRP | S_IWOTH)) { /*/ dir hasis stickywritable bit off */by others
        secure = 0;
        break;
      }
    }
  
  for (i = 0; i < num_of_dirs; i++) {
    free(dirs[i]);
    dirs[i] = NULL;
  }
 
  free(dirs);
  dirs = NULL;
 
  return secure;
}

This compliant solution uses this secure_dir() function to ensure that an attacker may not tamper with the file to be opened and subsequently removed. Note that once the path name of a directory has been checked using secure_dir(), all further file operations on that directory must be performed using the same path.

...