...
Performing file operations in a secure directory eliminates the possibility that an attacker might tamper with the files or file system to exploit a file system vulnerability in a program. These vulnerabilities often exist because there is a loose binding between the file name and the actual file. (See FIO01-C. Be careful using functions that use file names for identification.) In some cases, file operations can be performed securely anywhere. In other cases, the only way to ensure secure file operations is to perform the operation within a secure directory.
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
char *file_name;
FILE *fp;
/* Initialize file_name */
fp = fopen(file_name, "w");
if (fp == NULL) {
/* Handle error */
}
/* ... processProcess file ... */
if (fclose(fp) != 0) {
/* Handle error */
}
if (remove(file_name) != 0) {
/* Handle error */
}
|
...
| Code Block |
|---|
% cd /tmp/app/ % rm -rf tmpdir % ln -s /etc tmpdir |
There is not Not much that can be done programmatically done to ensure the file removed is the same file that was opened, processed, and closed, except to make sure that the file is opened in a secure directory with privileges that would prevent the file from being manipulated by an untrusted user.
Compliant Solution (POSIX)
This sample implementation of the function secure_dir() ensures that the directory fullpath and all directories above it are owned by either the user or the superuser and that other users do not have write access to the directories. When checking directories, it is important to traverse from the root to the leaf to avoid a dangerous race condition in which an attacker who has privileges to at least one of the directories can rename and re-create a directory after the privilege verification.
fullpath need not be canonicalized (See see FIO02-C. Canonicalize path names originating from untrusted sources). If the path contains a symbolic link, this routine recursively invokes itself on the linked-to directory and ensures it is also secure. A symbolically linked directory may be secure if both its source and linked-to directory are secure.
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <libgen.h>
#include <unistd.h>
#include <sys/stat.h>
#include <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 (!fullpath || fullpath[0] != '/') {
/* Handle error */
}
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++;
} //* nowNow 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 */
}
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 */
for (i = 0; i < num_of_dirs; i++) {
if (lstat(dirs[i], &buf) != 0) {
/* Handle error */
}
if (S_ISLNK(buf.st_mode)) { //* symlinkSymlink, test linked-to file */
linksize = buf.st_size + 1;
if (!(link = (char *)malloc(linksize))) {
/* Handle error */
}
r = readlink(dirs[i], link, linksize);
if (r == -1) {
/* Handle error */
} else if (r >= linksize) {
/* Handle truncation error */
}
link[r] = '\0';
num_symlinks++;
r = secure_dir(link);
num_symlinks--;
if (!r) {
secure = 0;
free(link);
link = NULL;
break;
}
free(link);
link = NULL;
continue;
}
if (!S_ISDIR(buf.st_mode)) { //* notNot 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 (buf.st_mode & (S_IWGRP | S_IWOTH)) { //* dir is writable 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;
}
|
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
char *dir_name;
const char *file_name = "passwd"; /* File name within the secure directory */
FILE *fp;
/* Initialize dir_name */
if (!secure_dir(dir_name)) {
/* Handle error */
}
if (chdir(dir_name) == -1) {
/* Handle error */
}
fp = fopen(file_name, "w");
if (fp == NULL) {
/* Handle error */
}
/* ... processProcess file ... */
if (fclose(fp) != 0) {
/* Handle error */
}
if (remove(file_name) != 0) {
/* Handle error */
}
|
...
Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
FIO15-C | mediumMedium | probableProbable | highHigh | P4 | L3 |
Related Vulnerabilities
...