xref: /openbsd/lib/libc/stdlib/__mktemp4.c (revision 3bef86f7)
1 /*	$OpenBSD: __mktemp4.c,v 1.1 2024/01/19 19:45:02 millert Exp $ */
2 /*
3  * Copyright (c) 1996-1998, 2008 Theo de Raadt
4  * Copyright (c) 1997, 2008-2009, 2024 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 <errno.h>
20 #include <limits.h>
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #define TEMPCHARS	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
25 #define NUM_CHARS	(sizeof(TEMPCHARS) - 1)
26 #define MIN_X		6
27 
28 #ifndef nitems
29 #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
30 #endif
31 
32 /*
33  * Internal driver for the mktemp(3) family of functions.
34  * The supplied callback does the actual work of testing or
35  * creating the file/directory.
36  */
37 int
38 __mktemp4(char *path, int slen, int flags, int (*cb)(const char *, int))
39 {
40 	char *start, *cp, *ep;
41 	const char tempchars[] = TEMPCHARS;
42 	unsigned int tries;
43 	size_t len;
44 	int ret;
45 
46 	len = strlen(path);
47 	if (len < MIN_X || slen < 0 || (size_t)slen > len - MIN_X) {
48 		errno = EINVAL;
49 		return -1;
50 	}
51 	ep = path + len - slen;
52 
53 	for (start = ep; start > path && start[-1] == 'X'; start--)
54 		;
55 	if (ep - start < MIN_X) {
56 		errno = EINVAL;
57 		return -1;
58 	}
59 
60 	tries = INT_MAX;
61 	do {
62 		cp = start;
63 		do {
64 			unsigned short rbuf[16];
65 			unsigned int i;
66 
67 			/*
68 			 * Avoid lots of arc4random() calls by using
69 			 * a buffer sized for up to 16 Xs at a time.
70 			 */
71 			arc4random_buf(rbuf, sizeof(rbuf));
72 			for (i = 0; i < nitems(rbuf) && cp != ep; i++)
73 				*cp++ = tempchars[rbuf[i] % NUM_CHARS];
74 		} while (cp != ep);
75 
76 		ret = cb(path, flags);
77 		if (ret != -1 || errno != EEXIST)
78 			return ret;
79 	} while (--tries);
80 
81 	errno = EEXIST;
82 	return -1;
83 }
84