Writing Secure Privileged Programs

 

Writing Secure Privileged Programs
Chapter 38.1 โ€” What Are Privileged Programs & Why They Matter
๐Ÿ“˜ Chapter 38
๐Ÿ” Linux Security
๐Ÿ›ก๏ธ Secure Programming

What is a Privileged Program?

In Linux and UNIX systems, most programs run with the same permissions as the user who launched them. A privileged program is special โ€” it can access system resources, files, and devices that are normally restricted to ordinary users. Think of these programs as having a “master key” to the system. With this power comes great responsibility: a bug in a privileged program can open the entire system to attack.

This chapter teaches you how to write such programs safely โ€” minimizing the chance of being exploited, and minimizing damage if an exploit does happen.

๐Ÿ”‘ Two Ways a Program Gains Privileges

A program can run with elevated privileges in exactly two ways:

๐Ÿ‘ค
Method 1: Run as Root
Program is started by root (UID 0). Daemons and network servers like sshd, cron use this method.
๐Ÿท๏ธ
Method 2: Set-UID / Set-GID Bit
A permission bit on the file itself. When any user runs it, the process gets the file owner’s identity. e.g., passwd, sudo.

How Set-User-ID Works โ€” Step by Step

Normally when you run a program, your process gets your user ID (say, UID=1000). But if a file has the set-user-ID (SUID) bit set, and the file is owned by root, then when you run it, your process’s effective user ID becomes 0 (root) โ€” even though your real user ID is still 1000.

Scenario Real UID Effective UID Saved Set-UID
Normal program run by user 1000 1000 1000 1000
Set-UID-root program run by user 1000 1000 0 (root) 0 (root)
Set-UID program (owner=200) run by user 1000 1000 200 200

The Three User IDs You Must Know

Every Linux process carries three user IDs. Understanding them is fundamental to secure programming:

Real User ID (RUID)
Who you actually are. Set at login. Doesn’t change when you run an SUID program.
Effective User ID (EUID)
What privilege checks use. This changes when you run an SUID program. Kernel uses this for permission decisions.
Saved Set-UID (SUID)
A saved copy of the EUID before you drop privilege. Allows the program to temporarily drop and then re-raise privilege.

โš ๏ธ The Golden Rule: Avoid Privilege When You Can

The very best piece of advice from security experts is: don’t write set-UID programs unless you absolutely have to. Every line of code in a privileged program is a potential attack vector.

If the task can be done by a daemon running as a specific non-root user, or by adjusting file permissions, prefer that approach. Privilege is a last resort, not a convenience.

Isolate Privilege into a Separate Program

When some privilege is unavoidable, a powerful pattern is to isolate the privileged operation into a tiny helper program that does one thing and exits. The main program (unprivileged) forks and execs this helper only when needed. This limits the attack surface enormously.

Example: The pt_chown program used internally by the GNU C library is exactly such a helper โ€” it performs one privileged operation (changing ownership of a pseudo-terminal) and nothing else.

Use a Non-Root Identity When Possible

Sometimes full root privilege isn’t needed. If you just need to allow users to write to a protected file, consider this safer design pattern:

โŒ Dangerous Way
Set-UID-root program. One bug = total system compromise.
โœ… Safer Way
Create a dedicated group. Set-GID program gives only group access, not root. Damage is contained.

๐Ÿ’ป Code Example 1: Check Your Process’s User IDs

Before you can manage privilege, you need to see the current UID state of your process. This program prints all three user IDs:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main(void)
{
    uid_t ruid, euid, suid;

    /* getresuid() โ€” Linux-specific, gets all three user IDs at once */
    if (getresuid(&ruid, &euid, &suid) == -1) {
        perror("getresuid");
        return 1;
    }

    printf("Real    UID (RUID): %d\n", (int)ruid);
    printf("Effective UID (EUID): %d\n", (int)euid);
    printf("Saved set-UID (SUID): %d\n", (int)suid);

    if (euid == 0)
        printf("Status: Running with ROOT privileges\n");
    else if (euid != ruid)
        printf("Status: Running with ELEVATED privileges (UID %d)\n", (int)euid);
    else
        printf("Status: Running with normal user privileges\n");

    return 0;
}

How to compile and test:

# Compile
gcc -o show_ids show_ids.c

# Run as normal user โ€” all three IDs will be identical
./show_ids

# Make it set-UID-root to see the difference
sudo chown root show_ids
sudo chmod u+s show_ids
./show_ids
# Now EUID=0, RUID=your-uid, SUID=0

๐Ÿ’ป Code Example 2: Detecting Set-UID Execution

A program can detect whether it is running with elevated privilege by comparing the real and effective UIDs. This is a common check at program startup:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>

/*
 * safe_check_privilege():
 * Returns 1 if running with privilege (EUID != RUID or EUID == 0)
 * Returns 0 otherwise
 */
int safe_check_privilege(void)
{
    return (geteuid() != getuid()) || (geteuid() == 0);
}

int main(void)
{
    printf("=== Privilege Diagnostic ===\n");
    printf("Real UID     : %d\n", (int)getuid());
    printf("Effective UID: %d\n", (int)geteuid());
    printf("Real GID     : %d\n", (int)getgid());
    printf("Effective GID: %d\n", (int)getegid());

    if (safe_check_privilege()) {
        printf("\n[!] WARNING: Program running with elevated privileges.\n");
        printf("    Ensure privilege is dropped before untrusted operations.\n");
    } else {
        printf("\n[OK] Program running with normal user privileges.\n");
    }

    return 0;
}

This kind of check at startup is good practice โ€” it helps the program know its privilege state and act accordingly.

๐Ÿ“Œ Key Terms for This Section

Privileged Program Set-User-ID (SUID) Set-Group-ID (SGID) Real UID Effective UID Saved Set-UID getresuid() geteuid() getuid() Attack Surface Privilege Isolation Root (UID 0)

๐ŸŽฏ Interview Questions โ€” Privileged Programs Basics
Q1. What is a privileged program in Linux? Give two examples.A privileged program has access to system resources unavailable to ordinary users. It either runs as root (e.g., sshd, cron) or has the set-UID/GID permission bit set (e.g., passwd, sudo).

Q2. Explain the difference between Real UID, Effective UID, and Saved Set-UID.Real UID: Who the process owner actually is (set at login, doesn’t change on exec).
Effective UID: What the kernel checks for permission decisions. Changes when running an SUID program.
Saved Set-UID: A copy of the EUID saved on exec. Allows temporarily dropping and re-raising privilege.

Q3. Why should set-UID programs be avoided whenever possible?Every bug in a privileged program is a potential security hole. If compromised, the attacker gains the program’s elevated privileges. Non-privileged alternatives reduce the attack surface to zero.

Q4. What is the “isolate privilege” design pattern?Move the privileged operation into a minimal helper program that does only one thing. The main program (unprivileged) forks and execs this helper only when needed. This limits how much code runs with elevated privilege.

Q5. Which system call retrieves all three user IDs simultaneously on Linux?getresuid(uid_t *ruid, uid_t *euid, uid_t *suid) โ€” retrieves Real UID, Effective UID, and Saved Set-UID in one call. It’s Linux-specific but preferred because it gives a complete, unambiguous picture of privilege state.

Q6. A program needs to let users update a protected file. Why is a set-GID approach safer than set-UID-root?A set-GID program only grants the specific group’s access, not root access. If compromised, the attacker only gains control of that group’s resources, not the entire system. The damage is contained.

Next: Operate with Least Privilege โ†’
Learn how to temporarily and permanently drop privileges using seteuid(), setuid(), and setresuid()

Continue to Part 2 EmbeddedPathashala Home

Leave a Reply

Your email address will not be published. Required fields are marked *