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
mktemp_internal(char * path,int slen,int mode,int flags)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 *
_mktemp(char * path)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 *
mktemp(char * path)127 mktemp(char *path)
128 {
129 	return(_mktemp(path));
130 }
131 
132 int
mkostemps(char * path,int slen,int flags)133 mkostemps(char *path, int slen, int flags)
134 {
135 	return(mktemp_internal(path, slen, MKTEMP_FILE, flags));
136 }
137 
138 int
mkstemp(char * path)139 mkstemp(char *path)
140 {
141 	return(mktemp_internal(path, 0, MKTEMP_FILE, 0));
142 }
143 
144 int
mkostemp(char * path,int flags)145 mkostemp(char *path, int flags)
146 {
147 	return(mktemp_internal(path, 0, MKTEMP_FILE, flags));
148 }
149 
150 int
mkstemps(char * path,int slen)151 mkstemps(char *path, int slen)
152 {
153 	return(mktemp_internal(path, slen, MKTEMP_FILE, 0));
154 }
155 
156 char *
mkdtemp(char * path)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