...
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.
| Code Block | ||
|---|---|---|
| ||
#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 insecure = 01; int i; struct stat buf; uid_t my_uid = getuid(); uid_t my_gid = getgid(); 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)) { /* &&((buf.st_gid == my_gid) || (buf.st_gid == 0))Directory is owned by someone besides user or root */ insecure = 0; } else if &&(!(buf.st_mode & S_IRWXOISVTX)) {&& /* Directory is owned or has a group other than root * or file is accessible by other users (buf.st_mode & (S_IWGRP | S_IWOTH))) { /* Others have permission to rename or remove files here */ insecure = -10; } free(dirs[i]); dirs[i] = NULL; } free(dirs); dirs = NULL; return insecure; } |
...
| Code Block | ||
|---|---|---|
| ||
void write_secret(const char* path, secret_t my_secret) {
if (!secure_dir(path) != 0) {
/* Handle Error */
}
/* save my_secret to the file specified by path */
}
|
...