1/*	$OpenBSD: mktemp.c,v 1.33 2014/05/06 22:55:27 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#ifndef nitems
39#define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
40#endif
41
42static int
43mktemp_internal(char *path, int slen, int mode)
44{
45	char *start, *cp, *ep;
46	const char *tempchars = TEMPCHARS;
47	unsigned int tries;
48	struct stat sb;
49	size_t len;
50	int fd;
51
52	len = strlen(path);
53	if (len < MIN_X || slen < 0 || (size_t)slen > len - MIN_X) {
54		errno = EINVAL;
55		return(-1);
56	}
57	ep = path + len - slen;
58
59	for (start = ep; start > path && start[-1] == 'X'; start--)
60		;
61	if (ep - start < MIN_X) {
62		errno = EINVAL;
63		return(-1);
64	}
65
66	tries = INT_MAX;
67	do {
68		cp = start;
69		do {
70			unsigned short rbuf[16];
71			unsigned int i;
72
73			/*
74			 * Avoid lots of arc4random() calls by using
75			 * a buffer sized for up to 16 Xs at a time.
76			 */
77			arc4random_buf(rbuf, sizeof(rbuf));
78			for (i = 0; i < nitems(rbuf) && cp != ep; i++)
79				*cp++ = tempchars[rbuf[i] % NUM_CHARS];
80		} while (cp != ep);
81
82		switch (mode) {
83		case MKTEMP_NAME:
84			if (lstat(path, &sb) != 0)
85				return(errno == ENOENT ? 0 : -1);
86			break;
87		case MKTEMP_FILE:
88			fd = open(path, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);
89			if (fd != -1 || errno != EEXIST)
90				return(fd);
91			break;
92		case MKTEMP_DIR:
93			if (mkdir(path, S_IRUSR|S_IWUSR|S_IXUSR) == 0)
94				return(0);
95			if (errno != EEXIST)
96				return(-1);
97			break;
98		}
99	} while (--tries);
100
101	errno = EEXIST;
102	return(-1);
103}
104
105char *_mktemp(char *);
106
107char *
108_mktemp(char *path)
109{
110	if (mktemp_internal(path, 0, MKTEMP_NAME) == -1)
111		return(NULL);
112	return(path);
113}
114
115__warn_references(mktemp,
116    "warning: mktemp() possibly used unsafely; consider using mkstemp()");
117
118char *
119mktemp(char *path)
120{
121	return(_mktemp(path));
122}
123
124int
125mkstemp(char *path)
126{
127	return(mktemp_internal(path, 0, MKTEMP_FILE));
128}
129
130int
131mkstemps(char *path, int slen)
132{
133	return(mktemp_internal(path, slen, MKTEMP_FILE));
134}
135
136char *
137mkdtemp(char *path)
138{
139	int error;
140
141	error = mktemp_internal(path, 0, MKTEMP_DIR);
142	return(error ? NULL : path);
143}
144