On Unix, the operating system stores a value known as the umask for each process it uses when creating new files on behalf of the process. The umask is used to disable permission bits that may be specified by the system call used to create files.
Remember that umasks apply only on file or directory
creation. Calls to chmod(
)
and
fchmod( )
are not modified by umask
settings.
When a process creates a new file, it specifies the access
permissions to assign the new file as a parameter to the system call
that creates the file. The operating system modifies the access
permissions by computing the intersection of the inverse of the umask
and the permissions requested by the process. The access permission
bits that remain after the intersection is computed are what the
operating system actually uses for the new file. In other words, in
the following example code, if the variable
requested_permissions
contained the permissions
passed to the operating system to create a new file, the variable
actual_permissions
would be the actual permissions
that the operating system would use to create the file.
requested_permissions = 0666; actual_permissions = requested_permissions & ~umask( );
A process inherits the value of its umask from its parent process when the process is created. Normally, the shell sets a default umask of either 022 (disable group- and world-writable bits) or 02 (disable world-writable bits) when a user logs in, but users have free reign to change the umask as they want. Many users are not even aware of the existence of umasks, never mind how to set them appropriately. Therefore, the umask value as set by the user should never be trusted to be appropriate.
When using the open( )
system call to create a new
file, you can force more restrictive permissions to be used than what
the user's umask might allow, but the only way to
create a file with less restrictive permissions is either to modify
the umask before creating the file or to use fchmod(
)
to change the permissions after the file is created.
In most cases, you'll be attempting to loosen
restrictions, but consider what happens when fopen(
)
is used
to create a new file. The fopen( )
function
provides no way to specify the permissions to use for the new file,
and it always uses 0666, which grants read and write access to the
owning user, the owning group, and everyone else. Again, the only way
to modify this behavior is either to set the umask before calling
fopen( )
or to use fchmod( )
after the file is created.
Using fchmod( )
to change the permissions of a file after it
is created is not a good idea because it introduces a race condition.
Between the time the file is created and the time the permissions are
modified, an attacker could possibly gain unauthorized access to the
file. The proper solution is therefore to modify the umask before
creating the file.
Properly using umasks in your program can be a bit complicated, but here are some general guidelines:
If you are creating files that contain sensitive data, always create them readable and writable by only the file owner, and deny access to group members and all other users.
Be aware that files that do not contain sensitive data may be readable by other users on the system. If the user wants to stop this behavior, the umask can be set appropriately before starting your program.
Avoid setting execute permissions on files, especially group and world execute. If your program generates files that are meant to be executable, set the execute bit only for the file owner.
Create directories that may contain files used to store sensitive information such that only the owner of the directory has read, write, and execute permissions for the directory. This allows only the owner of the directory to enter the directory or view or change its contents, but no other users can view or otherwise access the directory. (See the discussion of secure directories in Recipe 2.4 for more information on the importance of this requirement.)
Create directories that are not intended to store sensitive files such that the owner has read, write, and execute permissions, while group members and everyone else has only read and execute permissions. If the user wants to stop this behavior, the umask can be set appropriately before starting your program.
Do not rely on setting the umask to a "secure" value once at the beginning of the program and then calling all file or directory creation functions with overly permissive file modes. Explicitly set the mode of the file at the point of creation. There are two reasons to do this. First, it makes the code clear; your intent concerning permissions is obvious. Second, if an attacker managed to somehow reset the umask between your adjustment of the umask and any of your file creation calls, you could potentially create sensitive files with wide-open permissions.
Modifying the umask programmatically is a simple matter of calling
the function umask( )
with the new mask. The
return value will be the old umask value. The standard header file
sys/stat.h prototypes the umask(
)
function, and it also contains definitions for a sizable
set of macros that map to the various permission bits. Table 2-2 lists the macros, their values in octal, and
the permission bit or bits to which each one corresponds.
Table 2-2. Macros for permission bits and their octal values
Macro |
Octal value |
Permission bit(s) |
---|---|---|
|
0700 |
Owner read, write, execute |
|
0400 |
Owner read |
|
0200 |
Owner write |
|
0100 |
Owner execute |
|
0070 |
Group read, write, execute |
|
0040 |
Group read |
|
0020 |
Group write |
|
0010 |
Group execute |
|
0007 |
Other/world read, write, execute |
|
0004 |
Other/world read |
|
0002 |
Other/world write |
|
0001 |
Other/world execute |
umasks are a useful tool for users, allowing them to limit the amount of access others get to their files. Your program should make every attempt to honor the users' wishes in this regard, but if extra security is required for files that your application generates, you should always explicitly set this permission yourself.