Go to main content

Developer's Guide to Oracle® Solaris 11.4 Security

Exit Print View

Updated: November 2020
 
 

Security Considerations When Using C Functions

The necessary security considerations when using C library functions are outlined in the following table. Each function is classified into one of the following categories:

UNRESTRICTED

Default for all the functions.

USE WITH CAUTION

Requires special care to use securely.

AVOID

Avoid using these functions.

UNSAFE

Do not use these functions.

Table 14  Security Considerations When Using C Functions
Function
Format
Category
Comments
Alternative
access()
int access(const char *path, in mode)
AVOID
The information this function provides is outdated by the time you receive it. Using the access() function followed by the open() function causes a race condition that cannot be solved.
Open the file with the permissions of the intended user.
bcopy()
void bcopy(const void *s1, void *s2, size_t n) void *memcpy(void *s1, const void *s2, size_t n)
USE WITH CAUTION
Should not be used for copying strings, even though the length is known. Instead, use the strlcpy() function.
NA
catopen()
nl_catd catopen(const char *name, int oflag)
USE WITH CAUTION
Libraries and programs should not call the catopen() function on user-supplied pathnames. User-supplied message catalogues can be leveraged to break privileged code easily.
NA
cftime()
int cftime(char *s, char *format, const time_t *clock)
int ascftime(char *s, const char *format, const struct tm *timeptr)
UNSAFE
These functions do not check for bounds on the output buffer and might import the user data through the CFTIME environment variable.
strftime(buf, sizeof (buf), fmt, &tm)
chdir()
int chdir(const char *path)
USE WITH CAUTION
Prone to pathname race conditions. Do not use in multithreaded programs.
To avoid the race condition, use the fchdir() function after the directory has been opened and the properties have been checked using the fstat() function). Oracle Solaris 11 has added the POSIX 2008 *at() versions of the system calls that operate on files such as openat(), linkat(), mkdirat(), mkfifoat(), readlinkat(), and symlinkat(). These calls take the file descriptor of a directory as the first argument to use as the working directory for relative paths. These methods avoid the race condition when one thread calls chdir() while another is calling open(), unlink() and the like.
chmod()
int chmod(const char *path, mode_t mode)
int fchmodat(int fd, const char *path, mode_t mode, int flag)
int chown(const char *path, uid_t owner, gid_t group)
int lchown(const char *path, uid_t owner, gid_t group)
AVOID
These functions operate on pathnames and are prone to race conditions. Normally, programs need not call chown() or chmod(), but honor the current UID (switch back to it before opening files) and umask. Note that chmod() always follows symbolic links.
If the attributes of a file must be changed, open the file safely and use the fchown() or the fchmod() functions on the resulting file descriptor.
chroot()
int chroot(const char *path)
USE WITH CAUTION
After the chroot() function is called, the environment in which it is called offers little protection. Programs can easily escape. Do not run privileged programs in such a environment and that you change the directory to a point below the new root after the chroot() function.
Run in a non-global zone.
copylist()
char *copylist(const char *filenm, off_t *szptr)
DBM *dbm_open(const char *file, int open_flags, mode_t file_mode)
int dbminit(char *file)
USE WITH CAUTION
Used to open files and should only be used to open pathnames known to be safe.
NA
dlopen()
void *dlopen(const char *pathname, int mode)
USE WITH CAUTION
Parameters passed to the dlopen() function should only be unqualified pathnames which are then found using the runtime linker's path, or full pathnames not in any way derived from user input (including from argv[0] ). There is no way to safely open a user-supplied shared object. The object's _init() function is run before dlopen() returns.
NA
drand48()
double drand48(void)
double erand48(unsigned short xi[3])
long lrand48(void)long mrand48(void)
long jrand48(unsigned short xi[3])
long nrand48(unsigned short xi[3])
void srand48(long seedval)
int rand(void)
int rand_r(unsigned int *seed)
void srand(unsigned int seed)
long random(void)
AVOID
To generate random numbers for security or cryptography, use the getrandom() function.
NA
dup()
int dup(int fildes)
int dup2(int fildes, int fildes2)
USE WITH CAUTION
Both the dup() and the dup2() functions return file descriptors with the FD_CLOEXEC cleared and therefore they might leak when a program calls exec(). Older code made fcntl() calls shortly after these functions returned to set that flag. But in multithreaded code (including programs that only run one thread themselves but may be linked with libraries that run additional threads), that leaves a window open for a race with another thread. The F_DUPFD_CLOEXEC and F_DUP2FD_CLOEXE calls to fcntl (available in Oracle Solaris 11 and later releases) combine the duplication and flag setting into an atomic operation so there is no race.
fcntl(fildes, F_DUPFD_CLOEXEC, 0)
fcntl(fildes, F_DUP2FD_CLOEXEC, fildes2)
execl()
int execl(const char *path, const char *arg0, ..., const char *argn, NULL)
int execv(const char *path, char *const argv[])
int execve(const char *path, char *const argv[], char *const envp[])
USE WITH CAUTION
Make sure that the environment is sanitized and non-essential file descriptors are closed before running a new program.
NA
execvp()
int execvp(const char *file, const char *argv[])
int execlp(const char *file, const char *arg0, ..., const char *argn, NULL)
AVOID
Too dangerous to use in libraries or privileged commands and daemons because they find the executable by searching the directories in the PATH environment variable, which is under the complete control of the user. They should be avoided for most other programs.
Use the execl(), execv(), or execve() functions.
fattach()
int fattach(int filedes, const char *path)
USE WITH CAUTION
Check the file descriptor after the open() function (using fstat()), and not the pathname before the open() function.
NA
fchmod()
int fchmod(int filedes, mode_t mode)
int fchown(int filedes, uid_t owner, gid_t group)
UNRESTRICTED
Preferred alternative to chmod() and chown() functions.
NA
fdopen()
FILE *fdopen(int filedes, const char *mode)
UNRESTRICTED
Alternative for fopen()
NA
fopen()
FILE *fopen(const char *path, const char *mode)
FILE *freopen(const char *path, const char *mode, FILE *stream)
USE WITH CAUTION
It is not possible to safely create files by using fopen(). However, once a pathname is verified to exist, that is, after calling the mkstemp() function, it can be used to open those pathnames. In other cases, a safe invocation of open() followed by fdopen() should be used.
Use open() followed by fdopen(), For example:
FILE *fp; int fd;
fd = open(path,
O_CREAT|O_EXCL|O_WRONLY,
0600); 
if (fd < 0){
......  }
fp = fdopen(fd, "w");
fstat()
int fstat(int filedes, struct stat *buf)
UNRESTRICTED
Useful to check whether the file that is opened is the file you expected to open.
NA
ftw()
int ftw(const char *path, int (*fn)(), int depth)
int nftw(const char *path, int (*fn)(), int depth, int flags)
USE WITH CAUTION
Follows symbolic links and crosses mount points.
Use nftw with the appropriate flags set (a combination of FTW_PHYS and FTW_MOUNT).
getenv()
char *getenv(const char *name)
USE WITH CAUTION
The environment is completely user-specified. If possible, avoid the use of getenv() in libraries. Strings returned by getenv() can be up to NCARGS bytes long (currently 1MB for 32-bit environments). Pathnames derived from environment variables should not be trusted. They should not be used as input for any of the *open() functions (including catopen() and dlopen()).
NA
getlogin()
char *getlogin(void)
AVOID
The value returned by getlogin() is not reliable. It is only a hint for the user name.
NA
getpass()
char *getpass(const char *prompt)
AVOID
Only the first 8 bytes of input are used. Avoid using it in new code.
Use the getpassphrase() function.
gets()
char *gets(char *s)
UNSAFE
This function does not check for bounds when storing the input. This function cannot be used securely.
Use fgets(buf, sizeof (buf), stdin) OR getline(buf, bufsize, stdin).
The getline(buf, bufsize, stdin) function is new in Oracle Solaris 11.
kvm_open()
kvm_t *kvm_open(char *namelist, char *corefile, char *swapfile, int flag, char *errstr)
int nlist(const char *filename, struct nlist *nl)
AVOID
Write a proper kstat or other interface if you need information from the kernel. If you accept a user-specified namelist argument, make sure you revoke privileges before using it. Otherwise, a specifically constructed namelist can be used to read random parts of the kernel, revealing possibly sensitive data.
NA
lstat()
int lstat(const char *path, struct stat *buf)
int stat(const char *path, struct stat *buf)
int fstatat(int fildes, const char *path, struct stat *buf, int flag)
USE WITH CAUTION
Do not use these functions to check for the existence or absence of a file. The lstat(), stat(), or fstatat() functions followed by open() have an inherent race condition.
If the purpose is to create the file that does not exist, use
open(file, O_CREAT|O_EXCL, mode)
If the purpose is to read the file, open it for reading. If the purpose is to make sure the file attributes are correct before reading from it, use
fd = open(file, O_RDONLY); fstat(fd, &statbuf);
If the pathname can't be trusted, add O_NONBLOCK to the open flags. This prevents the application from freezing upon opening a device.
mkdir()
int mkdir(const char *path, mode_t mode)
int mkdirat(int fd, const char *path, mode_t mode)
int mknod(const char *path, mode_t mode, dev_t dev)
int mknodat(int fd, const char *path, mode_t mode, dev_t dev)
USE WITH CAUTION
Be careful about the path used. These functions will not follow symbolic links for the last component and hence they are relatively safe.
NA
mkstemp()
int mkstemp(char *template)
UNRESTRICTED
Safe temporary file creation function.
NA
mktemp()
char *mktemp(char *template)
AVOID
Generates a temporary filename but the use of the generated pathname is not guaranteed safe because there is a race condition between the checks in mktemp() and the subsequent call to open() by the application.
Use mkstemp() to create a file and mkdtemp() to create a directory.
open()
int open(const char *path, int oflag, /* mode_t mode */...)
int creat(const char *path, mode_t mode)
USE WITH CAUTION
When opening for reading from a privileged program, make sure that you open the file as a user by dropping privileges or setting the effective UID to the real UID. Under no circumstances should programs implement their own access control based on file ownership and modes. Similarly, when creating files, do not open and then use chown() on the file.
When opening for writing, the program can be tricked into opening the wrong file by following symbolic or hard links. To avoid this problem, either use the O_NOFOLLOW and O_NOLINKS flags, or use O_CREAT|O_EXCL to ensure that a new file is created instead of opening an existing file.
When opening a file, consider whether the file descriptor should be kept open across an exec() call. In Oracle Solaris 11, you can specify O_CLOEXEC in the open flags to atomically mark the file descriptor to be closed by exec system calls. In older releases, you must use the fcntl() function with the FD_CLOEXEC flag, which allows a race condition in multithreaded programs, if another thread forks and runs between the open() and fcntl() calls.
NA
popen()
FILE *popen(const char *command, const char *mode)
int p2open(const char *cmd, FILE *fp[2])
int system(const char *string)
AVOID
These three library calls always involve the shell which involves PATH, IFS, other environment variables and interpretation of special characters. Refer CERT C Coding Recommendation ENV04-C for more details.
Use posix_spawn() to run other programs, with waitpid() or pipe() as necessary.
printf()
int printf(const char *format, ...)
int vprintf(const char *format, va_list ap)
int fprintf(FILE *stream, const char *format, ...)
int vfprintf(FILE *stream, const char *format, va_list ap)
int snprintf(char *s, size_t n, const char *format, ...)
int vsnprintf(char *s, size_t n, const char *format, va_list ap)
int wprintf(const wchar_t *format, ...)
int vwprintf(const wchar_t format, va_list arg)
int fwprintf(FILE *stream, const wchar_t *format, ...)
int vfwprintf(FILE *stream, const wchar_t *format, va_list arg)
int swprintf(wchar_t *s, size_t n, const wchar_t *format, ...)
int vswprintf(wchar_t *s, size_t n, const wchar_t *format, va_list arg)
int asprintf(char **ret, const char *format, ...)
USE WITH CAUTION
At risk from user-specified format strings. If the format string comes from a message catalog, verify your NLSPATH manipulations and catopen() or catget() uses. The C library tries to be safe by ignoring NLSPATH settings for setuid and setgid applications.
The snprintf() and vsnprintf() functions return the number of characters that would have been written to the buffer if it were large enough. You cannot use this value in constructs like, p += snprintf(p, lenp, "...") because p might point beyond p+lenp afterwards.
scanf()
int scanf(const char *format, ...)
int vscanf(const char *format, va_list arg)
int fscanf(FILE *stream, const char *format, ...)
int vfscanf(FILE *stream, const char *format, va_list arg)
int sscanf(const char *s, const char *format, ...)
int vsscanf(const char *s, const char *format, va_list arg)
USE WITH CAUTION
When scanning strings, make sure the format specified includes maximum buffer lengths. Use scanf("%10s", p) to limit scanf() to read 10 characters at most. Note that the corresponding buffer must be at least eleven bytes to allow space for the terminating NULL character.
NA
sprintf()
int sprintf(char *s, const char *fmt, ...)
int vsprintf(char *s, const char *fmt, va_list ap)
AVOID
Typically cause buffer overflow. If you must use these functions, make sure that the fmt argument cannot be user-controlled and that you can trust the parameters not to overflow the destination buffer.
Use snprintf(), vsnprintf() or asprintf(). The asprintf() function is new in Oracle Solaris 11.
strcat()
char *strcat(char *s1, const char *s2)
char *strcpy(char *s1, const char *s2)
AVOID
It is not possible to limit these functions to a maximum buffer size. However, you can calculate the amount of space required before calling strcat or strcpy. Use of these functions always forces reviewers to follow the logic, and prevent automated scanning of source code for vulnerabilities.
strlcat(dst, src, dstsize)
strlcpy(dst, src, dstsize)
strccpy()
char *strccpy(char *output, const char *input)
char *strcadd(char *output, const char *input)
char *streadd(char *output, const char *input)
char *strecpy(char *output, const char *input, const char *exceptions)
char *strtrns(const char *string, const char *old, const char *new, char *result)
USE WITH CAUTION
Similar problems as with strcpy(). See the strcpy and strccpy man pages for proper use.
NA
strlcpy()
size_t strlcpy(char *dst, const char *src, size_t dstsize)
size_t strlcat(char *dst, const char *src, size_t dstsize)
UNRESTRICTED
Preferred alternative to the strcpy() and the strcat() functions. Available in Solaris 8 and later. Should be used with constant and not computed size arguments to facilitate code review.
NA
strncat()
char *strncat(char *s1, const char *s2, size_t n)
char *strncpy(char *s1, const char *s2, size_t n)
USE WITH CAUTION
The strncpy() function is not guaranteed to null-terminate the destination buffer. The strncat() function is hard to use as it requires the proper size of the destination buffer to be calculated.
The fact that the strncpy() function does not null- terminate on insufficient space, together with the side effect that it will add NULL bytes if there is space left, makes it a useful function for updating structures that reside on disk. For example, the wtmpx files, are often generated with write (fd, w, sizeof (*w));
strlcpy(dst, src, dstsize)
strlcat(dst, src, dstsize)
syslog()
void syslog(int priority, const char *message, ...)
void vsyslog(int priority, const char *message, va_list ap)
USE WITH CAUTION
At risk from user-specified format strings. Verify your NLSPATH manipulations and catopen() or catget() uses.
NA
tempnam()
char *tempnam(const char *dir, const char *pfx)
char *tmpnam(char *s)
char *tmpnam_r(char *s)
AVOID
These functions are not suitable for generating unpredictable filenames. There is a race condition between the generation of the filename and its use, for example, open().
mkstem()
tmpfile()
FILE *tmpfile(void)
USE WITH CAUTION
Uses mkstemp(), so it is safe to use. However, because this function changes the umask, it is not multithread safe.
NA
truncate()
int truncate(const char *path, off_t length)
AVOID
This function is prone to pathname race conditions.
Use ftruncate() after a safe open().
umask()
mode_t umask(mode_t cmask)
USE WITH CAUTION
Should not be used in libraries or applications; the user's umask should be used. Also it is not multithread safe.
NA
utmpname()
int utmpname(const char *file) int utmpxname(const char *file)
AVOID
Use the default utmp and utmpx files.
NA
wordexp()
int wordexp(const char *restrict words, wordexp_t *restrict pwordexp, int flags)
USE WITH CAUTION
wordexp() passes the input string to a shell for expansion. Input provided by untrusted sources may attempt to use shell injection attacks to run additional commands.
If only wildcard expansion is required, use the glob() function.