xref: /freebsd/crypto/openssh/openbsd-compat/mktemp.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
1 /* THIS FILE HAS BEEN MODIFIED FROM THE ORIGINAL OPENBSD SOURCE */
2 /* Changes: Removed mktemp */
3 
4 /*	$OpenBSD: mktemp.c,v 1.30 2010/03/21 23:09:30 schwarze Exp $ */
5 /*
6  * Copyright (c) 1996-1998, 2008 Theo de Raadt
7  * Copyright (c) 1997, 2008-2009 Todd C. Miller
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 
22 /* OPENBSD ORIGINAL: lib/libc/stdio/mktemp.c */
23 
24 #include "includes.h"
25 
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <limits.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <unistd.h>
36 
37 #ifdef mkstemp
38 #undef mkstemp
39 #endif
40 int mkstemp(char *);
41 
42 /*
43  * From glibc man page: 'In glibc versions 2.06 and earlier, the file is
44  * created with permissions 0666, that is, read and write for all users.'
45  * Provide a wrapper to make sure the mask is reasonable (POSIX requires
46  * mode 0600, so mask off any other bits).
47  */
48 int
49 _ssh_mkstemp(char *template)
50 {
51 	mode_t mask;
52 	int ret;
53 
54 	mask = umask(0177);
55 	ret = mkstemp(template);
56 	(void)umask(mask);
57 	return ret;
58 }
59 
60 #if !defined(HAVE_MKDTEMP)
61 
62 #define MKTEMP_NAME	0
63 #define MKTEMP_FILE	1
64 #define MKTEMP_DIR	2
65 
66 #define TEMPCHARS	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
67 #define NUM_CHARS	(sizeof(TEMPCHARS) - 1)
68 
69 static int
70 mktemp_internal(char *path, int slen, int mode)
71 {
72 	char *start, *cp, *ep;
73 	const char *tempchars = TEMPCHARS;
74 	unsigned int r, tries;
75 	struct stat sb;
76 	size_t len;
77 	int fd;
78 
79 	len = strlen(path);
80 	if (len == 0 || slen < 0 || (size_t)slen >= len) {
81 		errno = EINVAL;
82 		return(-1);
83 	}
84 	ep = path + len - slen;
85 
86 	tries = 1;
87 	for (start = ep; start > path && start[-1] == 'X'; start--) {
88 		if (tries < INT_MAX / NUM_CHARS)
89 			tries *= NUM_CHARS;
90 	}
91 	tries *= 2;
92 
93 	do {
94 		for (cp = start; cp != ep; cp++) {
95 			r = arc4random_uniform(NUM_CHARS);
96 			*cp = tempchars[r];
97 		}
98 
99 		switch (mode) {
100 		case MKTEMP_NAME:
101 			if (lstat(path, &sb) != 0)
102 				return(errno == ENOENT ? 0 : -1);
103 			break;
104 		case MKTEMP_FILE:
105 			fd = open(path, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);
106 			if (fd != -1 || errno != EEXIST)
107 				return(fd);
108 			break;
109 		case MKTEMP_DIR:
110 			if (mkdir(path, S_IRUSR|S_IWUSR|S_IXUSR) == 0)
111 				return(0);
112 			if (errno != EEXIST)
113 				return(-1);
114 			break;
115 		}
116 	} while (--tries);
117 
118 	errno = EEXIST;
119 	return(-1);
120 }
121 
122 #if 0
123 char *_mktemp(char *);
124 
125 char *
126 _mktemp(char *path)
127 {
128 	if (mktemp_internal(path, 0, MKTEMP_NAME) == -1)
129 		return(NULL);
130 	return(path);
131 }
132 
133 __warn_references(mktemp,
134     "warning: mktemp() possibly used unsafely; consider using mkstemp()");
135 
136 char *
137 mktemp(char *path)
138 {
139 	return(_mktemp(path));
140 }
141 #endif
142 
143 int
144 mkstemp(char *path)
145 {
146 	return(mktemp_internal(path, 0, MKTEMP_FILE));
147 }
148 
149 int
150 mkstemps(char *path, int slen)
151 {
152 	return(mktemp_internal(path, slen, MKTEMP_FILE));
153 }
154 
155 char *
156 mkdtemp(char *path)
157 {
158 	int error;
159 
160 	error = mktemp_internal(path, 0, MKTEMP_DIR);
161 	return(error ? NULL : path);
162 }
163 
164 #endif /* !defined(HAVE_MKDTEMP) */
165