Index Index for
Section 3
Index Alphabetical
listing for S
Bottom of page Bottom of
page

safe_open(3)

NAME

safe_open - Open a file for I/O with comprehensive or customized security checks

SYNOPSIS

#include <safe_open.h> int safe_open( char const *pathname, int oflags, ulong_t sflags, ... );

LIBRARY

Standard C Library (libc)

PARAMETERS

pathname Identifies the file or other file system object being opened. oflags Includes the flags (such as O_RDONLY, O_CREAT, or O_APPEND) that one would specify to an open() call. The flags actually passed by safe_open() to open() may vary from these; see DESCRIPTION for details. sflags Includes flags that can relax 20 of the restrictions that safe_open() enforces by default. You can specify one or more of the following flags, each of which tells safe_open() not to enforce a particular restriction: OPN_BLOCKING Accept the risk of blocking for off-line modems or named pipes (fifos). This flag is often appropriate when intending to write to a named pipe, since a non-blocking open of a named pipe for writing fails when there is no reader. OPN_FSTYPE_FDFS Allow opening file system objects under /dev/fd (or any other file system mounted as type fdfs). OPN_FSTYPE_FFM Allow opening file system objects that are the targets of file-on- file mounts. OPN_FSTYPE_PROCFS Allow opening file system objects under /proc (or any other file system mounted as type procfs). OPN_FSTYPE_REMOTE Allow opening files on non-local file systems. Not setting this flag excludes (at least) NFS and DCE file systems. For support of dataless client configurations, the /, /usr, /usr/var, and /var file systems are always accepted, even when this flag is not specified. Local file systems are currently defined as MFS, CFS, and any others that set the M_LOCAL bit in the m_flags field of their mount structures. (At the time this reference page was written, the full list of local file systems included AdvFS, CDFS, CFS, DVDFS, MFS, and UFS.) OPN_RELATIVE Allow relative paths in the pathname parameter. OPN_TRUST_DEFAULT_ACLS When creating a new file, do not check for a possibly-inappropriate default access control list (ACL) on the new file. If this flag is omitted, newly-created files are checked to see whether they have inherited a default ACL from the containing directory. If so, and if the owner of that directory is neither UID 0 nor the effective UID of the process, an attempt is made to delete the ACL from the file. If any other readers or writers of the file are detected after removing the ACL, the file is closed and safe_open() fails, setting errno to [EMLINK]. OPN_TRUST_DIR_OWNERS When resolving the pathname parameter, accept symbolic links that have the same UID as the directory in which they are found. If this flag is omitted, only symbolic links owned by the effective UID of the process or by UID 0 will be accepted. OPN_TRUST_NLINKS Do not check for other links to the same file. If this flag is omitted, named pipes and regular files with link counts greater than one will be rejected, causing safe_open() to fail with errno set to [EMLINK]. OPN_TRUST_GROUP Limit "writable directory" checks to making sure that directories do not have world write permission. If this flag is omitted, safe_open() makes sure that directories have neither world write nor group write permission. OPN_TRUST_PARENT_DIRS Limit "writable directory" checking to the immediate parent directory of pathname (and of any symbolic links traversed to the final symbolic link target, when relevant). If this flag is omitted, safe_open() will also traverse upward from the immediate parent directory, checking each directory until reaching (and testing) the root directory, as well as checking each directory traversed to resolve any symbolic links. OPN_TRUST_STARTING_DIRS Skip "writable directory" checks for the starting directory of the pathname parameter. See RESTRICTIONS for a limited number of cases when this flag is ignored. OPN_TRUST_STICKY_BIT Skip "writable directory" checks for directories with the "sticky bit" (S_ISVTX) set in their mode bits. OPN_TRUST_SYMLINK_OWNERS Skip the checks for symbolic link ownership. OPN_TYPE_BLK Allow block-special files to be opened. OPN_TYPE_CHR Allow character-special files to be opened. OPN_TYPE_DIR Allow directories to be opened for reading. OPN_TYPE_FIFO Allow named pipes (fifos) to be opened. OPN_TYPE_SYMLINK Allow symbolic links to be followed if all other constraints are met. Even with this flag set, the object type of the symbolic link's target must be acceptable. On systems where the open() call supports the O_NOFOLLOW flag, including O_NOFOLLOW in oflags overrides the inclusion of OPN_TYPE_SYMLINK in sflags. OPN_UNOWNED Allow files not owned by the effective UID to be opened. ... Reserved for future expansion.

DESCRIPTION

The safe_open() function attempts to open the file system object identified by its pathname parameter and makes validity checks, as controlled by the oflags and sflags parameters. The validity checking that this function performs helps to protect against some common security vulnerabilities, such as "time of check, time of use" (TOCTOU) race conditions, that can be created when programs manipulate arbitrary file system objects. If successful, safe_open() returns a file descriptor. On failure, the function returns -1 and sets errno to indicate the error. For many applications that want to protect against security vulnerabilities when opening files, the safe_open() function will be a drop-in replacement for an open() call, after selecting a suitable set of values for the sflags parameter. However, the oflags parameter is not necessarily passed to the open() call or passed exactly as specified in order to protect against security vulnerabilities. The safe_open() function differs from the open() function with respect to oflags values in the following ways: · The safe_open() function never implements O_CREAT without an implicit O_EXCL. When called with only O_CREAT in its oflags parameter, safe_open() always passes 0600 to open() as a mode parameter. Therefore, applications that need to use an oflags parameter that specifies O_CREAT without O_EXCL will need to add some retry logic in addition to the safe_open() call. (See EXAMPLES.) · If the pathname parameter is a symbolic link, safe_open() will never create a file, even when O_CREAT is specified. · The safe_open() function never passes O_TRUNC to open(). When called with O_TRUNC in its oflags parameter, safe_open() simulates the O_TRUNC operation by using a call to ftruncate(). · The safe_open() function always adds both O_NONBLOCK and O_NDELAY to any open() calls (even if O_CREAT is specified in oflags) unless OPN_BLOCKING is included in sflags. This function makes the following checks before opening the specified pathname parameter whether or not a new or existing file system object is being opened. There is no guaranteed order in which these checks are made. If a file system or the target object fails any of the checks, the function will return failure (unless otherwise noted). Because of the potential for race conditions, some of the checks may be made more than once. · Validate the type of the target file system, depending on the OPN_FSTYPE_* flags that are provided for sflags. If no OPN_FSTYPE_* flags are set, only local file systems (as defined in the description of the OPN_FSTYPE_REMOTE flag) are permitted and they cannot be one of the "pseudo" local file systems (FDFS, FFM, or PROCFS). · Validate the type of the file system object, depending on the OPN_TYPE_* flags that are provided for sflags. If no OPN_TYPE_* flags are set, only a regular file may be opened. · Confirm that the target file does not exist if O_CREAT and O_EXCL are included in oflags · Validate that no component of the resolved pathname parameter is in an untrustworthy (world- or group-writable) directory, depending on the OPN_TRUST_* flags that are provided for sflags. Regardless of whether OPN_TRUST_* flags are set, safe_open() always subjects any symbolic link component found in pathname to the writable-directory check. Other directory components are checked, depending on whether OPN_TRUST_PARENT_DIRS, OPN_TRUST_STARTING_DIRS, or both are set. Symbolic links are also subject to proper ownership checking, as controlled by the OPN_TRUST_DIR_OWNERS and OPN_TRUST_SYMLINK_OWNERS flags. If no OPN_TRUST_* flags are present: -- All directories in the absolute pathname of the file system object being opened will be checked -- None of those directories may be group-writable or world-writable -- All symbolic links found during pathname traversal must be owned by UID 0 or the effective UID of the calling process · If the pathname parameter identifies a symbolic link and OPN_TYPE_SYMLINK is specified, the link's target must also pass the object type validation, depending on any other OPN_TYPE_* flags that are included. To open the file system object, safe_open() may use the resolved pathname that was obtained during the per-component, access-checking traversal of pathname. (This is always done on file creation.) If OPN_BLOCKING was not included in sflags, the open is always done as a non-blocking open and fcntl() is called to clear the non-blocking bits. If an existing file was opened, safe_open() makes additional checks as follows: · Verify that pathname was successfully opened and that the opened file system object is what was expected based on the earlier checks on the type or types of file system objects that are allowed to be opened. · Check to make sure that the call to fcntl() succeeded if OPN_BLOCKING was not specified in sflags, and O_NONBLOCK and O_NDELAY were not both specified in oflags. · Verify that the call to ftruncate() succeeded if O_TRUNC was specified in oflags. (This step is skipped for named pipes and objects opened through file systems of type FDFS.) · Confirm that the object being opened does not have a link count greater than one if OPN_TRUST_NLINKS was not specified in sflags, and the file system object being opened is a named pipe or a regular file. For a newly created file, assuming OPN_TRUST_DEFAULT_ACLS was not specified in sflags, safe_open() makes sure that at least one of the following additional conditions applies: · The file was created in a directory owned by UID 0 or the effective UID of the calling process · The file did not inherit a default ACL from its directory For more information, see the "ACL Inheritance" section in acl(4). · Deleting the default ACL on the new file and resetting its mode to 0600 must succeed and, after doing so, there must be no other processes accessing the file. On failure of any check after a file descriptor is obtained, safe_open() closes the file descriptor before returning failure status.

NOTES

HP reserves the right to revise the safe_open() implementation to address additional security vulnerabilities in the future. It is possible that these future additional checks might cause application code that is designed to use the current implementation and that has a previously- undetected vulnerability to fail under circumstances that exercise that vulnerability. This is appropriate for privileged programs that must maintain the highest level of safe operation. For the benefit of applications with looser security requirements, the validity checking on the sflags mask only rejects bits in the range reserved for future expansion of the argument list. The start of that reserved range of bit numbers is identified by the OPN_first_reserved symbol. Those bits that are currently unassigned, but which may be assigned in the future to disable new checks, are accepted by the current implementation. The start of the unassigned range of bit numbers is identified by the OPN_num_flags symbol. The full mask of acceptable unassigned bits can thus be obtained by the following expression: ((1UL<<OPN_first_reserved) - (1UL<<OPN_num_flags))

RESTRICTIONS

Even if OPN_TRUST_STARTING_DIRS is included in sflags, it is possible that the starting directory of the pathname parameter will be subject to "writable directory" testing. This happens if the pathname resolution process encounters a sequence like subdir/../file. This behavior is considered a restriction rather than a bug due to the possibility of races with other processes in the handling of pathnames and directory modes. For similar reasons, the target of a symbolic link is always subject to the "writable directory" checks, even when it is the same as the starting directory.

RETURN VALUES

Non-negative integer Success. The returned value is the file descriptor of the file system object that was opened. -1 Failure. In this case, errno is set to indicate the condition that caused the failure.

ERRORS

Except for a few cases, error conditions documented in this reference page are specific to the safe_open() function. However, the safe_open() function can also set errno in response to failure conditions returned by any of the following calls: acl_get_fd(), acl_set_fd(), fcntl(), fstat(), ftruncate(), fuser(), lstat(), open(), readlink(), realpath(), stat(), and statfs(). Refer to SEE ALSO find the reference pages for these functions. [EBADF] The oflags parameter had O_TRUNC set, but the access portion of that parameter was O_RDONLY. (This error is propagated from an ftruncate() call.) [EFTYPE ] The type of the file system object was rejected based on the OPN_TYPE_* values omitted from sflags, or the type of file system type in which the object was to be opened was rejected, based on the OPN_FSTYPE_* values omitted from sflags. [EINVAL] The pathname parameter was NULL or did not start with a / character and the sflags parameter did not include OPN_RELATIVE, or the sflags parameter included bits that are reserved for future argument expansion. [EMLINK] One of the following conditions was encountered: · OPN_TRUST_NLINKS flag was not included in sflags, an existing file system object was found that was either a named pipe or a regular file, and its link count was greater than one. · OPN_TRUST_DEFAULT_ACLS was not included in sflags, a newly-created file was found to have inherited a default access control list from a directory owned by an inappropriate UID, and other processes were found to be accessing the new file after resetting its ACL. [ENOENT ] One of the following conditions was encountered: · The pathname parameter was an empty string. · The pathname parameter had at least one trailing / character. If the intent was to specify a directory, it should be done by appending a dot (.) character. · O_CREAT was specified without O_EXCL in oflags and a file system object associated with the pathname parameter existed at the start of the call but no longer existed when an attempt was made to open the existing file. The safe_open() routine will have retried this sequence of operations at least once before returning this error. [ENOTSUP ] One of the following conditions was encountered: · Some directory permissions found were overly permissive (given the constraints not relaxed by the OPN_TRUST_* flags). · An attempt was made to create a new file by traversing a symbolic link. [EPERM ] One of the following conditions was encountered: · OPN_UNOWNED was not included in sflags, and the file found was not owned by the effective UID of the process. · OPN_TRUST_SYMLINK_OWNERS was not included in sflags, and a symbolic link was found that failed the symbolic link ownership checks during resolution of pathname. [EXDEV ] Either some check on the pathname parameter or on symbolic links that needed to be resolved for that parameter returned inconsistent results, or the file originally tested (by means of the pathname parameter) did not match the file object returned by an internal call to open().

EXAMPLES

1. The following replacement example shows how file opening might be updated for an application that intends to display a notice file to a user. This example assumes that the application is not in complete control of the pathname to be displayed, but that it is running with the user's own UID, and that the application displays a regular file (however it was found). Original code: int fd = open(pathname, O_RDONLY); Replacement code: const ulong_t soflags = (OPN_UNOWNED | OPN_TRUST_STICKY_BIT | OPN_TRUST_NLINKS | OPN_TYPE_SYMLINK | OPN_RELATIVE | OPN_FSTYPE_REMOTE); int fd = safe_open(pathname, O_RDONLY, soflags); 2. The following replacement example shows how adding data to a user- specified pathname can be done more safely. This example assumes that the application has no control over the user-provided pathname, but that it is running with the user's credentials. It further assumes that the application is running as a daemon, so the practice of accepting relative pathnames is inadvisable. Finally, it assumes that the expected location for the user's file is in either /tmp or /var/tmp, so the use of the OPN_TRUST_STICKY_BIT is appropriate. Original code: int fd = open(pathname, O_CREAT|O_APPEND|O_WRONLY, 0644); Replacement code: The replacement code is complicated slightly by the need to retry file creation attempts and to reset the file's mode if it was newly created. const ulong_t soflags = (OPN_TRUST_STICKY_BIT); int loop_limit = 3, fd; do { fd = safe_open(pathname, O_CREAT|O_APPEND|O_WRONLY,soflags); } while (-1 == fd && ENOENT == errno && --loop_limit); if (fd >= 0) (void) fchmod(fd, 0644); (The retry for [ENOENT] errors is only needed for robustness because safe_open() does its own retry in this case.) 3. The following replacement example shows how an operation similar to the >> redirection of the shells can be made safer against inadvertent file substitution, while still allowing the user to write to a hardcopy terminal, a magnetic tape, a named pipe, a file in /tmp, or a file in the user's (possibly NFS-mounted) home directory. Original code: int fd = open(pathname, O_CREAT|O_EXCL|O_WRONLY, 0666); if (-1 == fd && EEXIST == errno) fd = open(pathname, O_APPEND|O_WRONLY); Replacement code: The replacement code uses the process umask setting to (possibly) open up the permissions on a newly-created file beyond the 0600 mode always applied by the safe_open() routine. This code also deliberately excludes block-special devices because they are not suitable for text output. const ulong_t soflags_append = (OPN_TRUST_STICKY_BIT | OPN_UNOWNED | OPN_TRUST_NLINKS | OPN_RELATIVE | OPN_FSTYPE_REMOTE | OPN_TYPE_CHR | OPN_TYPE_FIFO | OPN_TYPE_SYMLINK | OPN_BLOCKING); const ulong_t soflags_create = (OPN_TRUST_STICKY_BIT | OPN_RELATIVE | OPN_FSTYPE_REMOTE | OPN_BLOCKING | OPN_TRUST_DEFAULT_ACLS); int fd = safe_open(pathname, O_CREAT|O_EXCL|O_WRONLY, soflags_create); if (fd >= 0) { mode_t cmask = umask(077); /* Get prevailing umask. */ (void) umask(cmask); /* Restore it. */ (void) fchmod(fd, 0666 & ~cmask); /* Apply it. */ } else fd = safe_open(pathname, O_APPEND|O_WRONLY, soflags_append); (It should be noted that this method of obtaining the process umask value, while simple, is not thread safe. A multi-threaded application should use the TBL_UAREA function of the table() system call, and examine the u_cmask structure member to obtain the umask value.)

SEE ALSO

Functions: fcntl(2), ftruncate(2), fuser(2), open(2), readlink(2), stat(2), statfs(2), table(2), umask(2), acl_get_fd(3), acl_set_fd(3), fdopen(3), realpath(3) Files: acl(4), fd(4), ffm(4), proc(4)

Index Index for
Section 3
Index Alphabetical
listing for S
Top of page Top of
page