Date: Tue, 19 Mar 2024 08:10:54 -0400 (EDT) Message-ID: <958129033.15.1710850254574@wilbert.sei.cmu.edu> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_14_1507555133.1710850254571" ------=_Part_14_1507555133.1710850254571 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html
File operations should be performed 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, re= name, delete, or otherwise manipulate files. (Other users may read or searc= h the directory but generally may not modify the directory's contents in an= y way.) Also, other users must not be able to delete or rename files in the= parent of the secure directory and all higher directories, although creati= ng new files or deleting or renaming files they own is permissible.
Performing file operations in a secure directory eliminates the possibil= ity that an attacker might tamper with the files or file system to exploit= a> 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 usi= ng functions that use file names for identification.) In some cases, fi= le operations can be performed securely anywhere. In other cases, the only = way to ensure secure file operations is to perform the operation within a s= ecure 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 m= ay lead to security vulnerabilities if the system is misconfigured. There i= s a slight possibility that file systems will be reconfigured in an insecur= e 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 sectio= n) even when running in a secure directory.
In this noncompliant code example, the file identified by file_nam=
e
is opened, processed, closed, and removed:
char *file= _name; FILE *fp; /* Initialize file_name */ fp =3D fopen(file_name, "w"); if (fp =3D=3D NULL) { /* Handle error */ } /* ... Process file ... */ if (fclose(fp) !=3D 0) { /* Handle error */ } if (remove(file_name) !=3D 0) { /* Handle error */ }
An attacker can replace the file object identified by file_name with a link to an arbitrary file before the call to
fopen(). It is also possible that the file object identified by
file_name<=
/code> in the call to
remove()
is not the same file object ide=
ntified by file_name
in the call to fopen()
. If t=
he file is not in a secure directory, for example, /tmp/app/tmpdir/pa=
sswd
, then an attacker can manipulate the location of the file as fo=
llows:
% cd /t= mp/app/=20 % rm -rf tmpdir % ln -s /etc tmpdir
Not much can be done programmatically 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.
This sample implementation of the function secure_dir()=
code> 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 ra=
ce 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 verif=
ication.
fullpath
need not be canonicalized (see FIO02-C. Canonicalize path names originating from tainted sources=
). If the path contains a symbolic link, this routine recursively invokes i=
tself on the linked-to directory and ensures it is also secure. A symbolica=
lly linked directory may be secure if both its source and linked-to directo=
ry are secure.
Note that this function is effective only on file systems that are fully= compatible with UNIX permissions, and it may not behave normally for file = systems with other permission mechanisms, such as AFS (Andrew File System).=
#include &= lt;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 =3D 5 }; /* Returns nonzero if directory is secure, zero otherwise */ int secure_dir(const char *fullpath) { static unsigned int num_symlinks =3D 0; char *path_copy =3D NULL; char **dirs =3D NULL; int num_of_dirs =3D 1; int secure =3D 1; int i, r; struct stat buf; uid_t my_uid =3D geteuid(); size_t linksize; char* link; =20 if (!fullpath || fullpath[0] !=3D '/') { /* Handle error */ } =20 if (num_symlinks > MAX_SYMLINKS) { /* Could be a symlink loop */ /* Handle error */ } =20 if (!(path_copy =3D strdup(fullpath))) { /* Handle error */ } =20 /* Figure out how far it is to the root */ char* path_parent =3D path_copy; for (; ((strcmp(path_parent, "/") !=3D 0) && (strcmp(path_parent, "//") !=3D 0) && (strcmp(path_parent, ".") !=3D 0)); path_parent =3D dirname(path_parent)) { num_of_dirs++; } /* Now num_of_dirs indicates # of dirs we must check */ free(path_copy); path_copy =3D NULL; =20 if (!(dirs =3D (char **)malloc(num_of_dirs * sizeof(char *)))) { /* Handle error */ } =20 if (!(dirs[num_of_dirs - 1] =3D strdup(fullpath))) { /* Handle error */ } =20 if (!(path_copy =3D strdup(fullpath))) { /* Handle error */ } =20 /* Now fill the dirs array */ path_parent =3D path_copy; for (i =3D num_of_dirs - 2; i >=3D 0; i--) { path_parent =3D dirname(path_parent); if (!(dirs[i] =3D strdup(path_parent))) { /* Handle error */ } } free(path_copy); path_copy =3D NULL; =20 /* * Traverse from the root to the fullpath, * checking permissions along the way. */ for (i =3D 0; i < num_of_dirs; i++) { if (lstat(dirs[i], &buf) !=3D 0) { /* Handle error */ } =20 if (S_ISLNK(buf.st_mode)) { /* Symlink, test linked-to file */ linksize =3D buf.st_size + 1; if (!(link =3D (char *)malloc(linksize))) { /* Handle error */ } =20 r =3D readlink(dirs[i], link, linksize); if (r =3D=3D -1) { /* Handle error */ } else if (r >=3D linksize) { /* Handle truncation error */ } link[r] =3D '\0'; =20 num_symlinks++; r =3D secure_dir(link); num_symlinks--; =20 if (!r) { secure =3D 0; =20 free(link); link =3D NULL; break; } =20 free(link); link =3D NULL; =20 continue; } =20 if (!S_ISDIR(buf.st_mode)) { /* Not a directory */ secure =3D 0; break; } =20 if ((buf.st_uid !=3D my_uid) && (buf.st_uid !=3D 0)) { /* Directory is owned by someone besides user or root */ secure =3D 0; break; } =20 if (buf.st_mode & (S_IWGRP | S_IWOTH)) { /* dir is writable by othe= rs */ secure =3D 0; break; } } =20 for (i =3D 0; i < num_of_dirs; i++) { free(dirs[i]); dirs[i] =3D NULL; } =20 free(dirs); dirs =3D NULL; =20 return secure; }
This compliant solution uses the secure_dir()
function to e=
nsure that an attacker may not tamper with the file to be opened and subseq=
uently removed. Note that once the path name of a directory has been checke=
d using secure_dir()
, all further file operations on that dire=
ctory must be performed using the same path.
char *dir_= name; const char *file_name =3D "passwd"; /* File name within the secure director= y */ FILE *fp; /* Initialize dir_name */ if (!secure_dir(dir_name)) { /* Handle error */ } if (chdir(dir_name) =3D=3D -1) { /* Handle error */ } fp =3D fopen(file_name, "w"); if (fp =3D=3D NULL) { /* Handle error */ } /* ... Process file ... */ if (fclose(fp) !=3D 0) { /* Handle error */ } if (remove(file_name) !=3D 0) { /* Handle error */ }
Failing to perform file I/O operations in a secure directory that cannot= otherwise be securely performed can result in a broad range of file system= vulnerabilities.
Recommendation |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
FIO15-C |
Medium |
Probable |
High |
P4 |
L3 |
Search for vulnerabilities resulting from the violation of this = rule on the CERT website.
SEI CERT C++ Coding Standard | VOID FIO15-CPP. Ensure that file operations are performed i= n a secure directory |
MITRE CWE | CWE-379, Creation o=
f temporary file in directory with insecure permissions CWE-552, Files or directories accessible to external parties= td> |
[IEEE Std 1003.1:2013] | XSH, System Interfaces, dirname XSH, System Interfaces, realpath |
[Viega 2003] | Section 2.4, "Determining Whether a Directory Is= Secure" |