Perform file operations in a secure directory. In most cases, a secure directory is a directory in which no one other than the user, or possibly the administrator, has the ability to create, rename, delete files or otherwise manipulate files. (Other users may read or search the directory, but generally may not modify the directory's contents in any way.) Also, other users must not be able to delete or rename files they do not own in the parent of the secure directory and all higher directories, although creating new files and deleting or renaming files they own are permissible.
...
This example implementation of a secure_dir() function will ensure that path and all directories above it are owned by either by the user or the superuser, that path does not have write access for any other users, and that directories above path may not be deleted or renamed by any other users. When checking directories, it is important to traverse from the root to the leaf to avoid a dangerous race condition where an attacker who has privileges to at least one of the directories can rename and recreate a directory after the privilege verification.
...
| 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 *fullpath) {
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 (!(path_copy = strdup(fullpath))) {
/* 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(fullpath))) {
/* Handle error */
}
if (!(path_copy = strdup(fullpath))) {
/* 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 leaf, 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_IWGRP | S_IWOTH))
&& ((i == num_of_dirs - 1) || !(buf.st_mode & S_ISVTX))) {
/* Others have permissions to the leaf directory
* or are able to delete or rename files along the way */
secure = 0;
}
free(dirs[i]);
dirs[i] = NULL;
}
free(dirs);
dirs = NULL;
return secure;
}
|
This compliant solution uses the secure_dir() function above to ensure that an attacker may not tamper with the file to be opened and subsequently removed. Note that once the path name has been canonicalized and checked using secure_dir(), all further file operations must be done performed using the canonicalized path.
...
| Wiki Markup |
|---|
\[[ISO/IEC 9899:1999|AA. C References#ISO/IEC 9899-1999]\]
\[[MITRE 07|AA. C References#MITRE 07]\] [CWE ID 552|http://cwe.mitre.org/data/definitions/552.html], "Files or Directories Accessible to External Parties," [CWE ID 379|http://cwe.mitre.org/data/definitions/379.html], and "Creation of Temporary File in Directory with Insecure Permissions"
\[[Open Group 04|AA. C References#Open Group 04]\] [{{dirname()}}|http://www.opengroup.org/onlinepubs/009695399/functions/dirname.html], [{{realpath()}}|http://www.opengroup.org/onlinepubs/009695399/functions/realpath.html]
\[[Viega 03|AA. C References#Viega 03]\] Section 2.4, "Determining Whether a Directory Is Secure" |
...