Home | History | Annotate | Download | only in stdio
      1 /*	$OpenBSD: mktemp.c,v 1.35 2014/10/31 15:54:14 millert Exp $ */
      2 /*
      3  * Copyright (c) 1996-1998, 2008 Theo de Raadt
      4  * Copyright (c) 1997, 2008-2009 Todd C. Miller
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 
     19 #include <sys/types.h>
     20 #include <sys/stat.h>
     21 #include <errno.h>
     22 #include <fcntl.h>
     23 #include <limits.h>
     24 #include <stdio.h>
     25 #include <stdlib.h>
     26 #include <string.h>
     27 #include <ctype.h>
     28 #include <unistd.h>
     29 
     30 #define MKTEMP_NAME	0
     31 #define MKTEMP_FILE	1
     32 #define MKTEMP_DIR	2
     33 
     34 #define TEMPCHARS	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
     35 #define NUM_CHARS	(sizeof(TEMPCHARS) - 1)
     36 #define MIN_X		6
     37 
     38 #define MKOTEMP_FLAGS	(O_APPEND | O_CLOEXEC | O_DSYNC | O_RSYNC | O_SYNC)
     39 
     40 #ifndef nitems
     41 #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
     42 #endif
     43 
     44 static int
     45 mktemp_internal(char *path, int slen, int mode, int flags)
     46 {
     47 	char *start, *cp, *ep;
     48 	const char tempchars[] = TEMPCHARS;
     49 	unsigned int tries;
     50 	struct stat sb;
     51 	size_t len;
     52 	int fd;
     53 
     54 	len = strlen(path);
     55 	if (len < MIN_X || slen < 0 || (size_t)slen > len - MIN_X) {
     56 		errno = EINVAL;
     57 		return(-1);
     58 	}
     59 	ep = path + len - slen;
     60 
     61 	for (start = ep; start > path && start[-1] == 'X'; start--)
     62 		;
     63 	if (ep - start < MIN_X) {
     64 		errno = EINVAL;
     65 		return(-1);
     66 	}
     67 
     68 	if (flags & ~MKOTEMP_FLAGS) {
     69 		errno = EINVAL;
     70 		return(-1);
     71 	}
     72 	flags |= O_CREAT | O_EXCL | O_RDWR;
     73 
     74 	tries = INT_MAX;
     75 	do {
     76 		cp = start;
     77 		do {
     78 			unsigned short rbuf[16];
     79 			unsigned int i;
     80 
     81 			/*
     82 			 * Avoid lots of arc4random() calls by using
     83 			 * a buffer sized for up to 16 Xs at a time.
     84 			 */
     85 			arc4random_buf(rbuf, sizeof(rbuf));
     86 			for (i = 0; i < nitems(rbuf) && cp != ep; i++)
     87 				*cp++ = tempchars[rbuf[i] % NUM_CHARS];
     88 		} while (cp != ep);
     89 
     90 		switch (mode) {
     91 		case MKTEMP_NAME:
     92 			if (lstat(path, &sb) != 0)
     93 				return(errno == ENOENT ? 0 : -1);
     94 			break;
     95 		case MKTEMP_FILE:
     96 			fd = open(path, flags, S_IRUSR|S_IWUSR);
     97 			if (fd != -1 || errno != EEXIST)
     98 				return(fd);
     99 			break;
    100 		case MKTEMP_DIR:
    101 			if (mkdir(path, S_IRUSR|S_IWUSR|S_IXUSR) == 0)
    102 				return(0);
    103 			if (errno != EEXIST)
    104 				return(-1);
    105 			break;
    106 		}
    107 	} while (--tries);
    108 
    109 	errno = EEXIST;
    110 	return(-1);
    111 }
    112 
    113 char *_mktemp(char *);
    114 
    115 char *
    116 _mktemp(char *path)
    117 {
    118 	if (mktemp_internal(path, 0, MKTEMP_NAME, 0) == -1)
    119 		return(NULL);
    120 	return(path);
    121 }
    122 
    123 __warn_references(mktemp,
    124     "warning: mktemp() possibly used unsafely; consider using mkstemp()");
    125 
    126 char *
    127 mktemp(char *path)
    128 {
    129 	return(_mktemp(path));
    130 }
    131 
    132 int
    133 mkostemps(char *path, int slen, int flags)
    134 {
    135 	return(mktemp_internal(path, slen, MKTEMP_FILE, flags));
    136 }
    137 
    138 int
    139 mkstemp(char *path)
    140 {
    141 	return(mktemp_internal(path, 0, MKTEMP_FILE, 0));
    142 }
    143 
    144 int
    145 mkostemp(char *path, int flags)
    146 {
    147 	return(mktemp_internal(path, 0, MKTEMP_FILE, flags));
    148 }
    149 
    150 int
    151 mkstemps(char *path, int slen)
    152 {
    153 	return(mktemp_internal(path, slen, MKTEMP_FILE, 0));
    154 }
    155 
    156 char *
    157 mkdtemp(char *path)
    158 {
    159 	int error;
    160 
    161 	error = mktemp_internal(path, 0, MKTEMP_DIR, 0);
    162 	return(error ? NULL : path);
    163 }
    164