When working with files, it is important to ensure all operations take place Perform file operations in a secure directory. In most cases this means that , a secure directory is a directory no one other than the user or superuser (or Administrator) , or possibly the administrator, has the ability to read, write, execute, create, move, or delete files. Since C99 makes no mention of the concept of file systems or file permissions, any solution would necessarily be platform specific and likely not very portabledelete files or otherwise manipulate files.
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-A. Be careful using functions that use file names for identification). In some cases, file operations can be performed securely (and should be). In other cases, the only way to ensure secure file operations is to perform the operation within a secure directory.
Ensuring that file systems are configured in a safe manner is typically a system administration function. However, programs can often check that a file system is securely configured before performing file operations that may potentially lead to security vulnerabilities if the system is misconfigured. There is a slight possibility that file systems will be reconfigured in an insecure manner while a process is running and after the check has been made. As a result, it is always advisable to implement your code in a secure manner (that is, consistent with the other rules and recommendations in this section) even when not running in a secure directory.
For examples on how to create a secure directory inside another secure directory see FIO15-A. Do not create temporary files in shared directories.
Non-Compliant Code Example
In this non-compliant code example, the file identified by file_name is opened, processed, closed, and removed. However, it is 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().
| Code Block | ||
|---|---|---|
| ||
char *file_name;
FILE *f_ptr;
/* initialize file_name */
f_ptr = fopen(file_name, "w");
if (f_ptr == NULL) {
/* Handle Error */
}
/*... Process file ...*/
if (fclose(f_ptr) != 0) {
/* Handle Error */
}
if (remove(file_name) != 0) {
/* Handle Error */
}
|
There is not much that can be 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.
Non-Compliant Code Example
This code example attempts to save a secret in a file specified by path. While path is assumed to come from a trusted source (see FIO02-A. Canonicalize path names originating from untrusted sources), this does not imply that an attacker will not be able to read the secret after it is written out to file.
...
| 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 secure = 1;
int i;
struct stat buf;
uid_t my_uid = geteuid();
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)) {
/* 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;
}
|
This compliant solution uses the secure_dir() function above to verify that my_secret is written to a secure file.
| Code Block | ||
|---|---|---|
| ||
void write_secret(const char* path, secret_t my_secret) {
if (!secure_dir(path)) {
/* Handle Error */
}
/* save my_secret to the file specified by path */
}
|
This compliant solution uses the secure_dir() function above to ensure that the file that is opened and written to is also the file that is eventually removed.
| Code Block | ||
|---|---|---|
| ||
char *file_name; FILE *f_ptr; /* initialize file_name */ if (!secure_dir(path)) { /* Handle Error */ } f_ptr = fopen(file_name, "w"); if (f_ptr == NULL) { /* Handle Error */ } /*... Process file ...*/ if (fclose(f_ptr) != 0) { /* Handle Error */ } if (remove(file_name) != 0) { /* Handle Error */ } |
There is not much that can be 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.
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.
...