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

ImpactDetails
ConfidentialityScope: Confidentiality

Read Files or Directories - Incorrect umask settings can create files that are readable by unauthorized users, exposing sensitive information.
IntegrityScope: Integrity

Modify Files or Directories - Files may be created with write permissions for unintended users, allowing unauthorized modification.
Access ControlScope: 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

  1. MITRE Corporation. "CWE-560: Use of umask() with chmod-style Argument." https://cwe.mitre.org/data/definitions/560.html
  2. IEEE Std 1003.1 (POSIX). "umask - set and get the file mode creation mask."
  3. Linux man pages. "umask(2) - set file mode creation mask."