TLPI - Chapter 38Writing Secure PrivilegedProgramsShu-Yu Fu (firstname.lastname@example.org)
In This ChapterWe discuss a set of recommended practices forsecure programming, and describes variouspitfalls that should be avoided when writingprivileged programs.
Privileged Programs● have access to features and resources thatare not available to ordinary users.● was started under a privileged user ID.● has its set-user-ID or set-group-IDpermission bit set.○ When a set-user-id (set-group-ID) program isexeced, it changes the effective user (group) ID ofthe process to be the same as the owner (group) ofthe program file.
Is a Set-User-ID or Set-Group-IDProgram Required?● Avoid writing them whenever possible.● Is there an alternative way?● Isolate the functionality that needs privilegeinto a separate program.● It isnt always necessary for a set-user-IDprogram to give a process root credentials.● A dedicated group account (group ID) for theprogram.
Operate with Least Privilege● The saved set-user-ID facility was designedfor this purpose.uid_t orig_euid;orig_euid = geteuid();/* Drop privileges */if (seteuid(getuid()) == -1) errExit("seteuid");/* Do unprivileged work*//* Hold privileges only while they are required. *//* Reacquire privileges */if (seteuid(orig_euid) == -1) errExit("seteuid");/* Do privileged work*//* We must gain privilege prior to dropping it permanently *//* Drop privileges permanently when they will never agin be required. *//* Use setuid() for set-user-root and setreuid() (or setresuid()) for set-user-IDprogram */if (setuid(getuid() == -1) errExit("setuid");
Operate with Least Privilege (cont.)● [Tsafrir et al., 2008] recommends thatapplications should use system specificnonstandard system calls for changingprocess credentials.● Not only check that a credential-changingsystem call has succeeded, but also to verifythat the change occurred as expected.● We should drop/raise the privileged effectiveuser ID last/first when dropping/raisingprivileged IDs.
Be Careful When Executing aProgram● Drop privileges permanently before execinganother program.● Avoid executing a shell (or other interpreter)with privileges.● Close all unnecessary file descriptors beforean exec().
Avoid Exposing SensitiveInformation● When a program reads sensitive information,it should perform whatever processing isrequired, and then immediately erase theinformation from memory.○ The virtual memory page may be swapped out, andcould then be read from the swap area by aprivileged program.○ If the process receives a signal that causes it toproduce a core dump file, then that file may be readto obtain the information.
Confine the Process● Consider using capabilitiesThe Linux capabilities scheme divides thetraditional all-or-nothing UNIX privilegescheme into distinct units called capabilities.● Consider using a chroot jail to limit the set ofdirectories and files that a program mayaccess. However, a chroot jail in insufficientto confine a set-user-ID-root program.
Beware of Signals and RaceConditions● We need to consider the race conditions thatcan occur if a signal is delivered at any pointin the execution of the program.○ Time-of-check, time-of-use race condition.
Pitfalls When Performing FileOperations and File I/O● The process umask should be set to a value thatensures that the process never creates publicly writablefiles.● Use of seteuid() or setreuid() may required in orderensure that a newly created file doesnt belong to thewrong user.● Checks on file attributes should be performed on openfile descriptors. (Avoid time-of-use, time-of-checkproblem)● If a program must ensure that it is the creator of a file,then the O_EXCL flag should be used when callingopen().● A privileged program should avoid creating or relying onfiles in publicly writable directories.
Dont Trust Inputs or theEnvironment● Dont trust the environment list.○ Especially PATH and IFS.● Handle untrusted user inputs defensively.○ User-created files, command-line arguments,interactive inputs, CGI inputs, email messages,environment variables, IPC (FIFOs, shared memory,and so on) accessible by untrusted users, andnetwork packets.● Avoid unreliable assumptions about theprocesss run-time environment.
Beware of Buffer Overruns● Never use gets(), and employ functions suchas scanf(), sprintf(), strcpy(), and strcat() withcaution.○ stack crashing (a.k.a. stack smashing)■ address-space-randomization■ NX (no execute)● Alternatives, snprintf(), strncpy(), and strncat().○ The caller must check if truncation occurred.○ Using strncpy() can carry a performance impact.○ If the maximum size value given to strncpy() is notlong enough to permit the inclusion of theterminating null character, the target string is notnull-terminated.
Beware of Denial-of-Service Attacks● Denial-of-service attacks attempt to make aservice unavailable to legitimate clients.○ A server should be programmed to rigorously check its inputand avoid buffer overruns.○ The Server should perform load throttling.○ A server should employ timeouts for communication with aclient. SYN flood○ In the event of an overload, the server should log suitablemessages. However, logging should be throttled.○ The server should be programmed so that it doesnt crash inthe face of an unexpected load.○ Data structures should be designed to avoid algorithmic-complexity attacks.○ Spoofing attack
Check Return Statuses and FailSafely● A program should always check to seewhether system calls and library functionssucceed, and whether they return expectedvalues.● If a privileged program encounters anunexpected situation, then the appropriatebehavior is usually either to terminate or, inthe case of a server, to drop the clientrequest.
SummaryIn this chapter, we presented a set ofguidelines for writing privileged programs. Theaim of these guidelines is twofold: to minimizethe chances of a privileged program beingsubverted, and to minimize the damage thatcan be done in the event that a privilegedprogram is subverted.
Reference● Oracle LinuxSecurity Guide for Release 6,Chapter 4. Security Considerations forDevelopers● Secure Programming for Linux and UnixHOWTO