Use of umask() with chmod-style Argument
Description
Use of umask() with chmod-style Argument is a vulnerability where a program incorrectly calls the umask() function with an argument specified as if it were an argument to chmod(). This stems from a fundamental misunderstanding of how umask() operates. While chmod() sets permissions that are granted, umask() sets permissions that are denied (masked off). When developers confuse these semantics and use chmod-style arguments with umask(), files are created with incorrect permissions—typically more permissive than intended—potentially exposing sensitive data or allowing unauthorized modifications.
Risk
Incorrect umask() usage creates significant file permission vulnerabilities. When developers intend to restrict permissions but use chmod-style arguments, the resulting files have the opposite permissions from what was intended. For example, calling umask(0644) thinking it will create files with rw-r--r-- permissions actually creates files with -------w- (0022) permissions, as umask inverts the argument. This can result in world-writable files, executable permissions where none should exist, or files readable by all users when they should be private. In security-sensitive contexts, this exposes credentials, configurations, and user data.
Solution
Understand that umask() operates inversely to chmod(): permissions in the umask are turned off from the mode argument to open() and other file creation functions. Use umask() with the correct argument representing permissions to deny, not grant. For restrictive file creation, use umask(0077) to deny all group and other permissions. Document umask calls clearly to prevent future confusion. Consider using explicit chmod() or fchmod() after file creation for precise permission control. Implement automated static analysis to detect suspicious umask() patterns.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Read Files or Directories - Incorrect umask settings can create files that are readable by unauthorized users, exposing sensitive information. |
| Integrity | Scope: Integrity Modify Files or Directories - Files may be created with write permissions for unintended users, allowing unauthorized modification. |
| Access Control | Scope: Access Control Bypass Protection Mechanism - Intended file access restrictions are ineffective due to inverted permission logic. |
Example Code
Vulnerable Code
// Vulnerable: Using chmod-style argument with umask()
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
void vulnerable_create_config_file(const char* filename, const char* content) {
// Vulnerable: Developer thinks this sets rw-r--r-- (0644) permissions
// But umask works inversely - this actually masks OFF 0644
// Resulting permissions: ~0644 & 0777 = 0133 (--x-wx-wx)
umask(0644);
// File created with very wrong permissions!
int fd = open(filename, O_CREAT | O_WRONLY, 0777);
// Actual permissions: 0777 & ~0644 = 0133
write(fd, content, strlen(content));
close(fd);
}
void vulnerable_create_private_file(const char* filename) {
// Vulnerable: Developer thinks this restricts to owner-only (0600)
// Actually creates files with permissions 0177 (-rwxrwxrwx & ~0600)
umask(0600);
int fd = open(filename, O_CREAT | O_WRONLY, 0666);
// Intended: rw------- (0600)
// Actual: 0666 & ~0600 = 0066 (----rw-rw-)
// File is writable by group and others!
close(fd);
}
void vulnerable_create_secret_file(const char* filename, const char* secret) {
// Vulnerable: Thinking "I want 0700 permissions"
umask(0700);
int fd = open(filename, O_CREAT | O_WRONLY, 0777);
// Intended: rwx------ (0700)
// Actual: 0777 & ~0700 = 0077 (----rwxrwx)
// File is fully accessible to group and others!
write(fd, secret, strlen(secret));
close(fd);
}
// Vulnerable: Common misunderstanding pattern
#include <stdio.h>
#include <sys/stat.h>
int main() {
// Vulnerable: Developer wants restrictive permissions
// Mistakenly uses the desired permissions as umask value
mode_t old_umask = umask(0644); // WRONG!
// Creates file - permissions are inverted
FILE* fp = fopen("/tmp/config.txt", "w");
if (fp) {
fprintf(fp, "DB_PASSWORD=secret123\n");
fclose(fp);
// File has wrong permissions, potentially world-writable
}
// Vulnerable: Another common mistake
umask(0755); // Thinking this gives rwxr-xr-x
// Actually masks: owner write+read, group execute, other execute
// Result for 0777 open: ----w--w- (0022)
int fd = open("/tmp/script.sh", O_CREAT | O_WRONLY, 0777);
// Script is writable by others!
close(fd);
return 0;
}
// Vulnerable: In daemon/service initialization
void vulnerable_daemon_init() {
// Vulnerable: Common error in daemon setup
// Developer wants daemon files to have 0640 permissions
umask(0640); // WRONG - this masks OFF 0640
// Log file creation
int log_fd = open("/var/log/myapp.log", O_CREAT | O_WRONLY | O_APPEND, 0666);
// Expected: rw-r----- (0640)
// Actual: 0666 & ~0640 = 0026 (-----w-rw-)
// Log file is world-writable!
// PID file creation
int pid_fd = open("/var/run/myapp.pid", O_CREAT | O_WRONLY, 0644);
// Expected: rw-r--r-- (0644)
// Actual: 0644 & ~0640 = 0004 (-------r--)
// PID file has almost no permissions!
}
Fixed Code
// Fixed: Correct umask usage
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
void secure_create_config_file(const char* filename, const char* content) {
// Fixed: umask specifies permissions to DENY
// To get rw-r--r-- (0644), deny: ----w--w- (0022)
// Alternative: deny write for group and other = 0022
mode_t old_umask = umask(0022);
int fd = open(filename, O_CREAT | O_WRONLY, 0644);
// Actual permissions: 0644 & ~0022 = 0644 (rw-r--r--)
// Correct!
if (fd >= 0) {
write(fd, content, strlen(content));
close(fd);
}
umask(old_umask); // Restore previous umask
}
void secure_create_private_file(const char* filename) {
// Fixed: To get rw------- (0600), deny everything for group/other
// Deny: ----rwxrwx (0077)
mode_t old_umask = umask(0077);
int fd = open(filename, O_CREAT | O_WRONLY, 0600);
// Actual permissions: 0600 & ~0077 = 0600 (rw-------)
// Correct!
if (fd >= 0) {
close(fd);
}
umask(old_umask);
}
void secure_create_secret_file(const char* filename, const char* secret) {
// Fixed: For owner-only access (0700), deny all group/other
mode_t old_umask = umask(0077);
int fd = open(filename, O_CREAT | O_WRONLY, 0700);
// Actual: 0700 & ~0077 = 0700 (rwx------)
// Correct!
if (fd >= 0) {
write(fd, secret, strlen(secret));
close(fd);
}
umask(old_umask);
}
// Fixed: Using explicit chmod() for precise control
#include <sys/stat.h>
void secure_create_with_chmod(const char* filename, const char* content,
mode_t desired_mode) {
// Fixed: Create with restrictive permissions first
mode_t old_umask = umask(0077); // Most restrictive
int fd = open(filename, O_CREAT | O_WRONLY | O_EXCL, desired_mode);
if (fd < 0) {
umask(old_umask);
return;
}
write(fd, content, strlen(content));
close(fd);
// Fixed: Use explicit chmod() for exact permissions
if (chmod(filename, desired_mode) != 0) {
perror("chmod failed");
unlink(filename); // Remove file if can't set permissions
}
umask(old_umask);
}
// Fixed: Safe daemon initialization
void secure_daemon_init() {
// Fixed: Correct umask for daemon - deny group/other write
// Common daemon umask: 0027 (deny group write, deny all other)
umask(0027);
// Log file: rw-r----- (0640)
int log_fd = open("/var/log/myapp.log",
O_CREAT | O_WRONLY | O_APPEND, 0640);
// With umask 0027: 0640 & ~0027 = 0640
// Correct!
if (log_fd >= 0) close(log_fd);
// For stricter files, use explicit chmod
int secret_fd = open("/var/run/myapp.secret", O_CREAT | O_WRONLY, 0600);
if (secret_fd >= 0) {
fchmod(secret_fd, 0600); // Ensure correct permissions
close(secret_fd);
}
}
// Fixed: Utility functions for safe file creation
#include <sys/stat.h>
#include <errno.h>
/**
* Create file with exact permissions, regardless of umask.
* Returns file descriptor or -1 on error.
*/
int create_file_with_mode(const char* path, mode_t mode, int flags) {
// Save and set restrictive umask
mode_t old_umask = umask(0077);
// Create file
int fd = open(path, O_CREAT | O_EXCL | flags, mode);
int saved_errno = errno;
// Restore umask before any error handling
umask(old_umask);
if (fd < 0) {
errno = saved_errno;
return -1;
}
// Set exact permissions with fchmod
if (fchmod(fd, mode) != 0) {
saved_errno = errno;
close(fd);
unlink(path);
errno = saved_errno;
return -1;
}
return fd;
}
/**
* Create private file readable only by owner.
*/
int create_private_file(const char* path) {
return create_file_with_mode(path, 0600, O_WRONLY);
}
/**
* Create file readable by owner and group.
*/
int create_group_readable_file(const char* path) {
return create_file_with_mode(path, 0640, O_WRONLY);
}
// Usage example
int main() {
// Create private configuration file
int fd = create_private_file("/etc/myapp/secrets.conf");
if (fd >= 0) {
const char* secret = "API_KEY=supersecret\n";
write(fd, secret, strlen(secret));
close(fd);
}
return 0;
}
// Fixed: Reference table for umask values
/*
* UMASK REFERENCE:
*
* umask value | Files created with 0666 | Directories with 0777
* -----------+--------------------------+----------------------
* 0000 | rw-rw-rw- (0666) | rwxrwxrwx (0777)
* 0022 | rw-r--r-- (0644) | rwxr-xr-x (0755) [typical user]
* 0027 | rw-r----- (0640) | rwxr-x--- (0750) [typical daemon]
* 0077 | rw------- (0600) | rwx------ (0700) [private]
* 0002 | rw-rw-r-- (0664) | rwxrwxr-x (0775) [group writable]
*
* Remember: umask DENIES the specified permissions
* Final mode = requested_mode & ~umask
*/
// Macro for clarity
#define UMASK_PRIVATE 0077 // Deny all group/other
#define UMASK_USER_DEFAULT 0022 // Deny group/other write
#define UMASK_DAEMON 0027 // Deny group write, all other
#define UMASK_WORLD_READ 0000 // Allow all (use with caution!)
void example_usage() {
// For private files (credentials, keys)
umask(UMASK_PRIVATE);
// For normal user files
umask(UMASK_USER_DEFAULT);
// For daemon files
umask(UMASK_DAEMON);
}
CVE Examples
No specific CVEs are listed in the MITRE database for this CWE. However, incorrect umask usage has contributed to numerous local privilege escalation vulnerabilities in Unix/Linux applications.
References
- MITRE Corporation. "CWE-560: Use of umask() with chmod-style Argument." https://cwe.mitre.org/data/definitions/560.html
- IEEE Std 1003.1 (POSIX). "umask - set and get the file mode creation mask."
- Linux man pages. "umask(2) - set file mode creation mask."