xref: /openbsd-src/usr.bin/rsync/mktemp.c (revision 95af8abf27078f6d0f9a407e501f247e19a4d49c)
1*95af8abfSderaadt /*	$OpenBSD: mktemp.c,v 1.11 2019/06/27 18:03:37 deraadt Exp $ */
2dbed5971Sflorian /*
3dbed5971Sflorian  * Copyright (c) 1996-1998, 2008 Theo de Raadt
4dbed5971Sflorian  * Copyright (c) 1997, 2008-2009 Todd C. Miller
5dbc83512Sflorian  * Copyright (c) 2019 Florian Obser <florian@openbsd.org>
6dbed5971Sflorian  *
7dbed5971Sflorian  * Permission to use, copy, modify, and distribute this software for any
8dbed5971Sflorian  * purpose with or without fee is hereby granted, provided that the above
9dbed5971Sflorian  * copyright notice and this permission notice appear in all copies.
10dbed5971Sflorian  *
11dbed5971Sflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12dbed5971Sflorian  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13dbed5971Sflorian  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14dbed5971Sflorian  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15dbed5971Sflorian  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16dbed5971Sflorian  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17dbed5971Sflorian  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18dbed5971Sflorian  */
196304135bSbenno 
20dbed5971Sflorian #include <sys/types.h>
21dbed5971Sflorian #include <sys/stat.h>
22434f41cdSflorian #include <sys/socket.h>
23434f41cdSflorian #include <sys/un.h>
2455cb9f91Sbenno 
25dbed5971Sflorian #include <errno.h>
26dbed5971Sflorian #include <fcntl.h>
27dbed5971Sflorian #include <limits.h>
28dbed5971Sflorian #include <stdio.h>
2955cb9f91Sbenno #include <stdint.h>
30dbed5971Sflorian #include <stdlib.h>
31dbed5971Sflorian #include <string.h>
32dbed5971Sflorian #include <ctype.h>
33dbed5971Sflorian #include <unistd.h>
34dbed5971Sflorian 
3555cb9f91Sbenno #include "extern.h"
36dbed5971Sflorian 
3755cb9f91Sbenno /*
3855cb9f91Sbenno  * The type of temporary files we can create.
3955cb9f91Sbenno  */
4055cb9f91Sbenno enum	tmpmode {
4155cb9f91Sbenno 	MKTEMP_NAME,
4255cb9f91Sbenno 	MKTEMP_FILE,
4355cb9f91Sbenno 	MKTEMP_DIR,
4455cb9f91Sbenno 	MKTEMP_LINK,
4555cb9f91Sbenno 	MKTEMP_FIFO,
4655cb9f91Sbenno 	MKTEMP_NOD,
4755cb9f91Sbenno 	MKTEMP_SOCK
4855cb9f91Sbenno };
4955cb9f91Sbenno 
5055cb9f91Sbenno /*
5155cb9f91Sbenno  * Characters we'll use for replacement in the template string.
5255cb9f91Sbenno  */
53dbed5971Sflorian #define TEMPCHARS	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
54dbed5971Sflorian #define NUM_CHARS	(sizeof(TEMPCHARS) - 1)
5555cb9f91Sbenno 
5655cb9f91Sbenno /*
5755cb9f91Sbenno  * The number of template replacement values (foo.XXXXXX = 6) that we
5855cb9f91Sbenno  * require as a minimum for the filename.
5955cb9f91Sbenno  */
60dbed5971Sflorian #define MIN_X		6
61dbed5971Sflorian 
6255cb9f91Sbenno /*
6355cb9f91Sbenno  * The only flags we'll accept for creation of the temporary file.
6455cb9f91Sbenno  */
65dbed5971Sflorian #define MKOTEMP_FLAGS	(O_APPEND | O_CLOEXEC | O_DSYNC | O_RSYNC | O_SYNC)
66dbed5971Sflorian 
67dbed5971Sflorian #ifndef nitems
68dbed5971Sflorian #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
69dbed5971Sflorian #endif
70dbed5971Sflorian 
7155cb9f91Sbenno /*
7255cb9f91Sbenno  * Adapted from libc/stdio/mktemp.c.
7355cb9f91Sbenno  */
74dbed5971Sflorian static int
mktemp_internalat(int pfd,char * path,int slen,enum tmpmode mode,int flags,const char * link,mode_t dev_type,dev_t dev)7555cb9f91Sbenno mktemp_internalat(int pfd, char *path, int slen, enum tmpmode mode,
7655cb9f91Sbenno 	int flags, const char *link, mode_t dev_type, dev_t dev)
77dbed5971Sflorian {
78dbed5971Sflorian 	char		*start, *cp, *ep;
79dbed5971Sflorian 	const char	 tempchars[] = TEMPCHARS;
80dbed5971Sflorian 	unsigned int	 tries;
81dbed5971Sflorian 	struct stat	 sb;
82434f41cdSflorian 	struct sockaddr_un sun;
83dbed5971Sflorian 	size_t		 len;
84434f41cdSflorian 	int		 fd, saved_errno;
85dbed5971Sflorian 
86dbed5971Sflorian 	len = strlen(path);
87dbed5971Sflorian 	if (len < MIN_X || slen < 0 || (size_t)slen > len - MIN_X) {
88dbed5971Sflorian 		errno = EINVAL;
89dbed5971Sflorian 		return(-1);
90dbed5971Sflorian 	}
91dbed5971Sflorian 	ep = path + len - slen;
92dbed5971Sflorian 
93dbed5971Sflorian 	for (start = ep; start > path && start[-1] == 'X'; start--)
9455cb9f91Sbenno 		/* continue */ ;
9555cb9f91Sbenno 
96dbed5971Sflorian 	if (ep - start < MIN_X) {
97dbed5971Sflorian 		errno = EINVAL;
98dbed5971Sflorian 		return(-1);
99dbed5971Sflorian 	}
100dbed5971Sflorian 
101dbed5971Sflorian 	if (flags & ~MKOTEMP_FLAGS) {
102dbed5971Sflorian 		errno = EINVAL;
103dbed5971Sflorian 		return(-1);
104dbed5971Sflorian 	}
105dbed5971Sflorian 	flags |= O_CREAT | O_EXCL | O_RDWR;
106dbed5971Sflorian 
107dbed5971Sflorian 	tries = INT_MAX;
108dbed5971Sflorian 	do {
109dbed5971Sflorian 		cp = start;
110dbed5971Sflorian 		do {
111dbed5971Sflorian 			unsigned short rbuf[16];
112dbed5971Sflorian 			unsigned int i;
113dbed5971Sflorian 
114dbed5971Sflorian 			/*
115dbed5971Sflorian 			 * Avoid lots of arc4random() calls by using
116dbed5971Sflorian 			 * a buffer sized for up to 16 Xs at a time.
117dbed5971Sflorian 			 */
118dbed5971Sflorian 			arc4random_buf(rbuf, sizeof(rbuf));
119dbed5971Sflorian 			for (i = 0; i < nitems(rbuf) && cp != ep; i++)
120dbed5971Sflorian 				*cp++ = tempchars[rbuf[i] % NUM_CHARS];
121dbed5971Sflorian 		} while (cp != ep);
122dbed5971Sflorian 
123dbed5971Sflorian 		switch (mode) {
124dbed5971Sflorian 		case MKTEMP_NAME:
125dbed5971Sflorian 			if (fstatat(pfd, path, &sb, AT_SYMLINK_NOFOLLOW) != 0)
126dbed5971Sflorian 				return(errno == ENOENT ? 0 : -1);
127dbed5971Sflorian 			break;
128dbed5971Sflorian 		case MKTEMP_FILE:
129dbed5971Sflorian 			fd = openat(pfd, path, flags, S_IRUSR|S_IWUSR);
130dbed5971Sflorian 			if (fd != -1 || errno != EEXIST)
131dbed5971Sflorian 				return(fd);
132dbed5971Sflorian 			break;
133dbed5971Sflorian 		case MKTEMP_DIR:
134dbed5971Sflorian 			if (mkdirat(pfd, path, S_IRUSR|S_IWUSR|S_IXUSR) == 0)
135dbed5971Sflorian 				return(0);
136dbed5971Sflorian 			if (errno != EEXIST)
137dbed5971Sflorian 				return(-1);
138dbed5971Sflorian 			break;
139dbed5971Sflorian 		case MKTEMP_LINK:
140dbed5971Sflorian 			if (symlinkat(link, pfd, path) == 0)
141dbed5971Sflorian 				return(0);
142dbed5971Sflorian 			else if (errno != EEXIST)
143dbed5971Sflorian 				return(-1);
144dbed5971Sflorian 			break;
145434f41cdSflorian 		case MKTEMP_FIFO:
146434f41cdSflorian 			if (mkfifoat(pfd, path, S_IRUSR|S_IWUSR) == 0)
147434f41cdSflorian 				return(0);
148434f41cdSflorian 			else if (errno != EEXIST)
149434f41cdSflorian 				return(-1);
150434f41cdSflorian 			break;
151434f41cdSflorian 		case MKTEMP_NOD:
152434f41cdSflorian 			if (!(dev_type == S_IFCHR || dev_type == S_IFBLK)) {
153434f41cdSflorian 				errno = EINVAL;
154434f41cdSflorian 				return(-1);
155434f41cdSflorian 			}
156434f41cdSflorian 			if (mknodat(pfd, path, S_IRUSR|S_IWUSR|dev_type, dev)
157434f41cdSflorian 			    == 0)
158434f41cdSflorian 				return(0);
159434f41cdSflorian 			else if (errno != EEXIST)
160434f41cdSflorian 				return(-1);
161434f41cdSflorian 			break;
162434f41cdSflorian 		case MKTEMP_SOCK:
163434f41cdSflorian 			memset(&sun, 0, sizeof(sun));
164434f41cdSflorian 			sun.sun_family = AF_UNIX;
165434f41cdSflorian 			if ((len = strlcpy(sun.sun_path, link,
166434f41cdSflorian 			    sizeof(sun.sun_path))) >= sizeof(sun.sun_path)) {
167434f41cdSflorian 				errno = EINVAL;
168434f41cdSflorian 				return(-1);
169434f41cdSflorian 			}
170434f41cdSflorian 			if (sun.sun_path[len] != '/') {
171434f41cdSflorian 				if (strlcat(sun.sun_path, "/",
172434f41cdSflorian 				    sizeof(sun.sun_path)) >=
173434f41cdSflorian 				    sizeof(sun.sun_path)) {
174434f41cdSflorian 					errno = EINVAL;
175434f41cdSflorian 					return(-1);
176434f41cdSflorian 				}
177434f41cdSflorian 			}
178434f41cdSflorian 			if (strlcat(sun.sun_path, path, sizeof(sun.sun_path)) >=
179434f41cdSflorian 			    sizeof(sun.sun_path)) {
180434f41cdSflorian 				errno = EINVAL;
181434f41cdSflorian 				return(-1);
182434f41cdSflorian 			}
183434f41cdSflorian 			if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC |
184434f41cdSflorian 			    SOCK_NONBLOCK, 0)) == -1)
185434f41cdSflorian 				return -1;
186434f41cdSflorian 			if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) ==
187434f41cdSflorian 			    0) {
188434f41cdSflorian 				close(fd);
189434f41cdSflorian 				return(0);
190434f41cdSflorian 			} else if (errno != EEXIST) {
191434f41cdSflorian 					saved_errno = errno;
192434f41cdSflorian 					close(fd);
193434f41cdSflorian 					errno = saved_errno;
194434f41cdSflorian 					return -1;
195434f41cdSflorian 			}
196434f41cdSflorian 			close(fd);
197434f41cdSflorian 			break;
198dbed5971Sflorian 		}
199dbed5971Sflorian 	} while (--tries);
200dbed5971Sflorian 
201dbed5971Sflorian 	errno = EEXIST;
202dbed5971Sflorian 	return(-1);
203dbed5971Sflorian }
204dbed5971Sflorian 
205dbed5971Sflorian /*
206dbed5971Sflorian  * A combination of mkstemp(3) and openat(2).
207dbed5971Sflorian  * On success returns a file descriptor and trailing Xs are overwritten in
208dbed5971Sflorian  * path to create a unique file name.
20955cb9f91Sbenno  * Returns -1 on failure and sets errno.
210dbed5971Sflorian  */
211dbed5971Sflorian int
mkstempat(int fd,char * path)212dbed5971Sflorian mkstempat(int fd, char *path)
213dbed5971Sflorian {
21455cb9f91Sbenno 	return mktemp_internalat(fd, path, 0, MKTEMP_FILE, 0, NULL, 0, 0);
215dbed5971Sflorian }
216dbed5971Sflorian 
217dbed5971Sflorian /*
218dbed5971Sflorian  * A combination of mkstemp(3) and symlinkat(2).
219dbed5971Sflorian  * On success returns path with trailing Xs overwritten to create a unique
220dbed5971Sflorian  * file name.
22155cb9f91Sbenno  * Returns NULL on failure and sets errno.
222dbed5971Sflorian  */
223dbed5971Sflorian char *
mkstemplinkat(char * link,int fd,char * path)224dbed5971Sflorian mkstemplinkat(char *link, int fd, char *path)
225dbed5971Sflorian {
22655cb9f91Sbenno 
227434f41cdSflorian 	if (mktemp_internalat(fd, path, 0, MKTEMP_LINK, 0, link, 0, 0) == -1)
22855cb9f91Sbenno 		return NULL;
22955cb9f91Sbenno 	return path;
230434f41cdSflorian }
231434f41cdSflorian 
232434f41cdSflorian /*
233434f41cdSflorian  * A combination of mkstemp(3) and mkfifoat(2).
234434f41cdSflorian  * On success returns path with trailing Xs overwritten to create a unique
235434f41cdSflorian  * file name.
23655cb9f91Sbenno  * Returns NULL on failure and sets errno.
237434f41cdSflorian  */
238434f41cdSflorian char *
mkstempfifoat(int fd,char * path)239434f41cdSflorian mkstempfifoat(int fd, char *path)
240434f41cdSflorian {
24155cb9f91Sbenno 
242434f41cdSflorian 	if (mktemp_internalat(fd, path, 0, MKTEMP_FIFO, 0, NULL, 0, 0) == -1)
24355cb9f91Sbenno 		return NULL;
24455cb9f91Sbenno 	return path;
245434f41cdSflorian }
246434f41cdSflorian 
247434f41cdSflorian /*
248434f41cdSflorian  * A combination of mkstemp(3) and mknodat(2).
249434f41cdSflorian  * On success returns path with trailing Xs overwritten to create a unique
250434f41cdSflorian  * file name.
25155cb9f91Sbenno  * Returns NULL on failure and sets errno.
252434f41cdSflorian  */
253434f41cdSflorian char *
mkstempnodat(int fd,char * path,mode_t mode,dev_t dev)254434f41cdSflorian mkstempnodat(int fd, char *path, mode_t mode, dev_t dev)
255434f41cdSflorian {
25655cb9f91Sbenno 
25755cb9f91Sbenno 	if (mktemp_internalat(fd, path, 0,
25855cb9f91Sbenno 	    MKTEMP_NOD, 0, NULL, mode, dev) == -1)
25955cb9f91Sbenno 		return NULL;
26055cb9f91Sbenno 	return path;
261434f41cdSflorian }
262434f41cdSflorian 
263434f41cdSflorian /*
264434f41cdSflorian  * A combination of mkstemp(3) and bind(2) on a unix domain socket.
265434f41cdSflorian  * On success returns path with trailing Xs overwritten to create a unique
266434f41cdSflorian  * file name.
26755cb9f91Sbenno  * Returns NULL on failure and sets errno.
268434f41cdSflorian  */
269434f41cdSflorian char *
mkstempsock(const char * root,char * path)270434f41cdSflorian mkstempsock(const char *root, char *path)
271434f41cdSflorian {
27255cb9f91Sbenno 
273434f41cdSflorian 	if (mktemp_internalat(0, path, 0, MKTEMP_SOCK, 0, root, 0, 0) == -1)
27455cb9f91Sbenno 		return NULL;
27555cb9f91Sbenno 	return path;
276dbed5971Sflorian }
277dbed5971Sflorian 
278dbed5971Sflorian /*
279dbed5971Sflorian  * Turn path into a suitable template for mkstemp*at functions and
280dbed5971Sflorian  * place it into the newly allocated string returned in ret.
281dbed5971Sflorian  * The caller must free ret.
282dbed5971Sflorian  * Returns -1 on failure or number of characters output to ret
283dbed5971Sflorian  * (excluding the final '\0').
284dbed5971Sflorian  */
285dbed5971Sflorian int
mktemplate(char ** ret,const char * path,int recursive)286ba617adaSbenno mktemplate(char **ret, const char *path, int recursive)
287dbed5971Sflorian {
288dbed5971Sflorian 	int		 n, dirlen;
289dbed5971Sflorian 	const char	*cp;
290dbed5971Sflorian 
291dbed5971Sflorian 	if (recursive && (cp = strrchr(path, '/')) != NULL) {
292dbed5971Sflorian 		dirlen = cp - path;
29355cb9f91Sbenno 		n = asprintf(ret, "%.*s/.%s.XXXXXXXXXX",
29455cb9f91Sbenno 			dirlen, path, path + dirlen + 1);
295*95af8abfSderaadt 		if (n == -1) {
296b2a7eac7Sbenno 			ERR("asprintf");
297dbed5971Sflorian 			*ret = NULL;
298dbed5971Sflorian 		}
299*95af8abfSderaadt 	} else if ((n = asprintf(ret, ".%s.XXXXXXXXXX", path)) == -1) {
300b2a7eac7Sbenno 		ERR("asprintf");
30155cb9f91Sbenno 		*ret = NULL;
30255cb9f91Sbenno 	}
30355cb9f91Sbenno 
30455cb9f91Sbenno 	return n;
305dbed5971Sflorian }
306