xref: /freebsd/crypto/openssh/openbsd-compat/mktemp.c (revision 87569f75a91f298c52a71823c04d41cf53c88889)
1 /* OPENBSD ORIGINAL: lib/libc/stdio/mktemp.c */
2 
3 /* THIS FILE HAS BEEN MODIFIED FROM THE ORIGINAL OPENBSD SOURCE */
4 /* Changes: Removed mktemp */
5 
6 /*
7  * Copyright (c) 1987, 1993
8  *	The Regents of the University of California.  All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include "includes.h"
36 
37 #if !defined(HAVE_MKDTEMP) || defined(HAVE_STRICT_MKSTEMP)
38 
39 #if defined(LIBC_SCCS) && !defined(lint)
40 static char rcsid[] = "$OpenBSD: mktemp.c,v 1.17 2003/06/02 20:18:37 millert Exp $";
41 #endif /* LIBC_SCCS and not lint */
42 
43 static int _gettemp(char *, int *, int, int);
44 
45 int
46 mkstemps(path, slen)
47 	char *path;
48 	int slen;
49 {
50 	int fd;
51 
52 	return (_gettemp(path, &fd, 0, slen) ? fd : -1);
53 }
54 
55 int
56 mkstemp(path)
57 	char *path;
58 {
59 	int fd;
60 
61 	return (_gettemp(path, &fd, 0, 0) ? fd : -1);
62 }
63 
64 char *
65 mkdtemp(path)
66 	char *path;
67 {
68 	return(_gettemp(path, (int *)NULL, 1, 0) ? path : (char *)NULL);
69 }
70 
71 static int
72 _gettemp(path, doopen, domkdir, slen)
73 	char *path;
74 	register int *doopen;
75 	int domkdir;
76 	int slen;
77 {
78 	register char *start, *trv, *suffp;
79 	struct stat sbuf;
80 	int rval;
81 	pid_t pid;
82 
83 	if (doopen && domkdir) {
84 		errno = EINVAL;
85 		return(0);
86 	}
87 
88 	for (trv = path; *trv; ++trv)
89 		;
90 	trv -= slen;
91 	suffp = trv;
92 	--trv;
93 	if (trv < path) {
94 		errno = EINVAL;
95 		return (0);
96 	}
97 	pid = getpid();
98 	while (trv >= path && *trv == 'X' && pid != 0) {
99 		*trv-- = (pid % 10) + '0';
100 		pid /= 10;
101 	}
102 	while (trv >= path && *trv == 'X') {
103 		char c;
104 
105 		pid = (arc4random() & 0xffff) % (26+26);
106 		if (pid < 26)
107 			c = pid + 'A';
108 		else
109 			c = (pid - 26) + 'a';
110 		*trv-- = c;
111 	}
112 	start = trv + 1;
113 
114 	/*
115 	 * check the target directory; if you have six X's and it
116 	 * doesn't exist this runs for a *very* long time.
117 	 */
118 	if (doopen || domkdir) {
119 		for (;; --trv) {
120 			if (trv <= path)
121 				break;
122 			if (*trv == '/') {
123 				*trv = '\0';
124 				rval = stat(path, &sbuf);
125 				*trv = '/';
126 				if (rval != 0)
127 					return(0);
128 				if (!S_ISDIR(sbuf.st_mode)) {
129 					errno = ENOTDIR;
130 					return(0);
131 				}
132 				break;
133 			}
134 		}
135 	}
136 
137 	for (;;) {
138 		if (doopen) {
139 			if ((*doopen =
140 			    open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
141 				return(1);
142 			if (errno != EEXIST)
143 				return(0);
144 		} else if (domkdir) {
145 			if (mkdir(path, 0700) == 0)
146 				return(1);
147 			if (errno != EEXIST)
148 				return(0);
149 		} else if (lstat(path, &sbuf))
150 			return(errno == ENOENT ? 1 : 0);
151 
152 		/* tricky little algorithm for backward compatibility */
153 		for (trv = start;;) {
154 			if (!*trv)
155 				return (0);
156 			if (*trv == 'Z') {
157 				if (trv == suffp)
158 					return (0);
159 				*trv++ = 'a';
160 			} else {
161 				if (isdigit(*trv))
162 					*trv = 'a';
163 				else if (*trv == 'z')	/* inc from z to A */
164 					*trv = 'A';
165 				else {
166 					if (trv == suffp)
167 						return (0);
168 					++*trv;
169 				}
170 				break;
171 			}
172 		}
173 	}
174 	/*NOTREACHED*/
175 }
176 
177 #endif /* !defined(HAVE_MKDTEMP) || defined(HAVE_STRICT_MKSTEMP) */
178