Versions Compared

Key

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

...

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 **dirs = NULL;
  int num_of_dirs = 1;
  int secure = 1;
  int i;
  struct stat buf;
  uid_t my_uid = geteuid();
  size_t linksize;
  char* link;

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

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

  /* Now fill the dirs array */
  for (i = num_of_dirs - 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 (!(link = (char *)malloc(linksize))) {
        /* Handle error */
      }
      if (readlink( dirs[i], link, linksize) == -1) {
        /* Handle error */
      }
      if (!secure_dir(link)) {
        secure = 0;
        break;
      }
      break;
    }
    if (!S_ISDIR( buf.st_mode)) { // not a directory
      secure = 0;
      break;
    }
    if ((buf.st_uid != my_uid) && (buf.st_uid != 0)) {
      /* Directory is owned by someone besides user or root */
      secure = 0;
      break;
    }
    if (i == num_of_dirs - 1) { /* leaf dir */
      if (buf.st_mode & (S_IWGRP | S_IWOTH)) { /* dir is writable by others */
        secure = 0;
        break;
      }
    } else { /* parent dirs */
      if ((buf.st_mode & (S_IWGRP | S_IWOTH)) ||
          (buf.st_mode & S_ISVTX)) { /* dir has sticky betbit off */
        secure = 0;
        break;
      }
    }
            
    free(dirs[i]);
    dirs[i] = NULL;
  }

  free(dirs);
  dirs = NULL;

  return secure;
}

...