summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormillert <>2024-01-19 16:30:28 +0000
committermillert <>2024-01-19 16:30:28 +0000
commit1d17a25a597033d38c420a0a3add7e5e82dd4c02 (patch)
treebd07cfcf290c1cc6a0ac3df3a16498fa87c30876
parentb3ad0ad004d3d681310fe0d75cda8331381c5869 (diff)
downloadopenbsd-1d17a25a597033d38c420a0a3add7e5e82dd4c02.tar.gz
openbsd-1d17a25a597033d38c420a0a3add7e5e82dd4c02.tar.bz2
openbsd-1d17a25a597033d38c420a0a3add7e5e82dd4c02.zip
Move mktemp.c to stdlib where it belongs.
OK deraadt@
-rw-r--r--src/lib/libc/stdlib/Makefile.inc8
-rw-r--r--src/lib/libc/stdlib/mktemp.3415
-rw-r--r--src/lib/libc/stdlib/mktemp.c163
3 files changed, 582 insertions, 4 deletions
diff --git a/src/lib/libc/stdlib/Makefile.inc b/src/lib/libc/stdlib/Makefile.inc
index 55b8018522..fa4836f42b 100644
--- a/src/lib/libc/stdlib/Makefile.inc
+++ b/src/lib/libc/stdlib/Makefile.inc
@@ -1,4 +1,4 @@
1# $OpenBSD: Makefile.inc,v 1.64 2017/12/16 20:06:55 guenther Exp $ 1# $OpenBSD: Makefile.inc,v 1.65 2024/01/19 16:30:28 millert Exp $
2 2
3# stdlib sources 3# stdlib sources
4.PATH: ${LIBCSRCDIR}/arch/${MACHINE_CPU}/stdlib ${LIBCSRCDIR}/stdlib 4.PATH: ${LIBCSRCDIR}/arch/${MACHINE_CPU}/stdlib ${LIBCSRCDIR}/stdlib
@@ -6,7 +6,7 @@
6SRCS+= a64l.c abort.c atexit.c atoi.c atof.c atol.c atoll.c bsearch.c \ 6SRCS+= a64l.c abort.c atexit.c atoi.c atof.c atol.c atoll.c bsearch.c \
7 exit.c ecvt.c gcvt.c getenv.c getopt_long.c \ 7 exit.c ecvt.c gcvt.c getenv.c getopt_long.c \
8 getsubopt.c hcreate.c heapsort.c imaxabs.c imaxdiv.c insque.c \ 8 getsubopt.c hcreate.c heapsort.c imaxabs.c imaxdiv.c insque.c \
9 l64a.c llabs.c lldiv.c lsearch.c malloc.c reallocarray.c \ 9 l64a.c llabs.c lldiv.c lsearch.c malloc.c mktemp.c reallocarray.c \
10 merge.c posix_pty.c qsort.c radixsort.c rand.c random.c \ 10 merge.c posix_pty.c qsort.c radixsort.c rand.c random.c \
11 realpath.c remque.c setenv.c strtoimax.c \ 11 realpath.c remque.c setenv.c strtoimax.c \
12 strtol.c strtoll.c strtonum.c strtoul.c strtoull.c strtoumax.c \ 12 strtol.c strtoll.c strtonum.c strtoul.c strtoull.c strtoumax.c \
@@ -28,6 +28,6 @@ SRCS+= abs.c div.c labs.c ldiv.c
28MAN+= a64l.3 abort.3 abs.3 alloca.3 atexit.3 atof.3 atoi.3 atol.3 atoll.3 \ 28MAN+= a64l.3 abort.3 abs.3 alloca.3 atexit.3 atof.3 atoi.3 atol.3 atoll.3 \
29 bsearch.3 div.3 ecvt.3 exit.3 getenv.3 getopt.3 getopt_long.3 \ 29 bsearch.3 div.3 ecvt.3 exit.3 getenv.3 getopt.3 getopt_long.3 \
30 getsubopt.3 hcreate.3 imaxabs.3 imaxdiv.3 insque.3 labs.3 ldiv.3 \ 30 getsubopt.3 hcreate.3 imaxabs.3 imaxdiv.3 insque.3 labs.3 ldiv.3 \
31 lldiv.3 lsearch.3 malloc.3 posix_memalign.3 posix_openpt.3 ptsname.3 \ 31 lldiv.3 lsearch.3 malloc.3 mktemp.3 posix_memalign.3 posix_openpt.3 \
32 qsort.3 radixsort.3 rand48.3 rand.3 random.3 realpath.3 \ 32 ptsname.3 qsort.3 radixsort.3 rand48.3 rand.3 random.3 realpath.3 \
33 strtod.3 strtonum.3 strtol.3 strtoul.3 system.3 tsearch.3 33 strtod.3 strtonum.3 strtol.3 strtoul.3 system.3 tsearch.3
diff --git a/src/lib/libc/stdlib/mktemp.3 b/src/lib/libc/stdlib/mktemp.3
new file mode 100644
index 0000000000..d4bd7bdc91
--- /dev/null
+++ b/src/lib/libc/stdlib/mktemp.3
@@ -0,0 +1,415 @@
1.\" $OpenBSD: mktemp.3,v 1.1 2024/01/19 16:30:28 millert Exp $
2.\"
3.\" Copyright (c) 1989, 1991, 1993
4.\" The Regents of the University of California. All rights reserved.
5.\"
6.\" Redistribution and use in source and binary forms, with or without
7.\" modification, are permitted provided that the following conditions
8.\" are met:
9.\" 1. Redistributions of source code must retain the above copyright
10.\" notice, this list of conditions and the following disclaimer.
11.\" 2. Redistributions in binary form must reproduce the above copyright
12.\" notice, this list of conditions and the following disclaimer in the
13.\" documentation and/or other materials provided with the distribution.
14.\" 3. Neither the name of the University nor the names of its contributors
15.\" may be used to endorse or promote products derived from this software
16.\" without specific prior written permission.
17.\"
18.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28.\" SUCH DAMAGE.
29.\"
30.Dd $Mdocdate: January 19 2024 $
31.Dt MKTEMP 3
32.Os
33.Sh NAME
34.Nm mktemp ,
35.Nm mkstemp ,
36.Nm mkostemp ,
37.Nm mkstemps ,
38.Nm mkostemps ,
39.Nm mkdtemp
40.Nd make temporary file name (unique)
41.Sh SYNOPSIS
42.In stdlib.h
43.Ft char *
44.Fn mktemp "char *template"
45.Ft int
46.Fn mkstemp "char *template"
47.Ft int
48.Fn mkstemps "char *template" "int suffixlen"
49.Ft char *
50.Fn mkdtemp "char *template"
51.In stdlib.h
52.In fcntl.h
53.Ft int
54.Fn mkostemp "char *template" "int flags"
55.Ft int
56.Fn mkostemps "char *template" "int suffixlen" "int flags"
57.Sh DESCRIPTION
58The
59.Fn mktemp
60family of functions take the given file name template and overwrite
61a portion of it to create a new file name.
62This file name is unique and suitable for use by the application.
63The template may be any file name with at least six trailing
64.Em X Ns s ,
65for example
66.Pa /tmp/temp.XXXXXXXX .
67The trailing
68.Em X Ns s
69are replaced with a unique digit and letter combination.
70The number of unique file names that can be returned
71depends on the number of
72.Em X Ns s
73provided;
74.Fn mktemp
75will try at least 2 ** 31 combinations before giving up.
76At least six
77.Em X Ns s
78must be used, though 10 is much better.
79.Pp
80The
81.Fn mktemp
82function generates a temporary file name based on a template as
83described above.
84Because
85.Fn mktemp
86does not actually create the temporary file, there is a window of
87opportunity during which another process can open the file instead.
88Because of this race condition,
89.Fn mktemp
90should not be used where
91.Fn mkstemp
92can be used instead.
93.Fn mktemp
94was marked as a legacy interface in
95.St -p1003.1-2001 .
96.Pp
97The
98.Fn mkstemp
99function makes the same replacement to the template and creates the template
100file, mode 0600, returning a file descriptor opened for reading and writing.
101This avoids the race between testing for a file's existence and opening it
102for use.
103.Pp
104The
105.Fn mkostemp
106function acts the same as
107.Fn mkstemp ,
108except that the
109.Fa flags
110argument may contain zero or more of the following flags for the underlying
111.Xr open 2
112system call:
113.Pp
114.Bl -tag -width "O_CLOEXECXX" -offset indent -compact
115.It Dv O_APPEND
116Append on each write.
117.It Dv O_CLOEXEC
118Set the close-on-exec flag on the new file descriptor.
119.It Dv O_SYNC
120Perform synchronous I/O operations.
121.El
122.Pp
123The
124.Fn mkstemps
125and
126.Fn mkostemps
127functions act the same as
128.Fn mkstemp
129and
130.Fn mkostemp ,
131except they permit a suffix to exist in the template.
132The template should be of the form
133.Pa /tmp/tmpXXXXXXXXXXsuffix .
134.Fn mkstemps
135and
136.Fn mkostemps
137are told the length of the suffix string, i.e.,
138.Li strlen("suffix") .
139.Pp
140The
141.Fn mkdtemp
142function makes the same replacement to the template as in
143.Fn mktemp
144and creates the template directory, mode 0700.
145.Sh RETURN VALUES
146The
147.Fn mktemp
148and
149.Fn mkdtemp
150functions return a pointer to the template on success and
151.Dv NULL
152on failure.
153The
154.Fn mkstemp ,
155.Fn mkostemp ,
156.Fn mkstemps ,
157and
158.Fn mkostemps
159functions return \-1 if no suitable file could be created.
160If any call fails, an error code is placed in the global variable
161.Va errno .
162.Sh EXAMPLES
163Quite often a programmer will want to replace a use of
164.Fn mktemp
165with
166.Fn mkstemp ,
167usually to avoid the problems described above.
168Doing this correctly requires a good understanding of the code in question.
169.Pp
170For instance, code of this form:
171.Bd -literal -offset indent
172char sfn[19];
173FILE *sfp;
174
175strlcpy(sfn, "/tmp/ed.XXXXXXXXXX", sizeof(sfn));
176if (mktemp(sfn) == NULL || (sfp = fopen(sfn, "w+")) == NULL) {
177 warn("%s", sfn);
178 return (NULL);
179}
180return (sfp);
181.Ed
182.Pp
183should be rewritten like this:
184.Bd -literal -offset indent
185char sfn[19];
186FILE *sfp;
187int fd;
188
189strlcpy(sfn, "/tmp/ed.XXXXXXXXXX", sizeof(sfn));
190if ((fd = mkstemp(sfn)) == -1 ||
191 (sfp = fdopen(fd, "w+")) == NULL) {
192 if (fd != -1) {
193 unlink(sfn);
194 close(fd);
195 }
196 warn("%s", sfn);
197 return (NULL);
198}
199return (sfp);
200.Ed
201.Pp
202Often one will find code which uses
203.Fn mktemp
204very early on, perhaps to globally initialize the template nicely, but the
205code which calls
206.Xr open 2
207or
208.Xr fopen 3
209on that file name will occur much later.
210(In almost all cases, the use of
211.Xr fopen 3
212will mean that the flags
213.Dv O_CREAT
214|
215.Dv O_EXCL
216are not given to
217.Xr open 2 ,
218and thus a symbolic link race becomes possible, hence making
219necessary the use of
220.Xr fdopen 3
221as seen above.)
222Furthermore, one must be careful about code which opens, closes, and then
223re-opens the file in question.
224Finally, one must ensure that upon error the temporary file is
225removed correctly.
226.Pp
227There are also cases where modifying the code to use
228.Fn mktemp ,
229in concert with
230.Xr open 2
231using the flags
232.Dv O_CREAT
233|
234.Dv O_EXCL ,
235is better, as long as the code retries a new template if
236.Xr open 2
237fails with an
238.Va errno
239of
240.Er EEXIST .
241.Sh ERRORS
242The
243.Fn mktemp ,
244.Fn mkstemp ,
245.Fn mkostemp ,
246and
247.Fn mkdtemp
248functions may set
249.Va errno
250to one of the following values:
251.Bl -tag -width Er
252.It Bq Er EINVAL
253The
254.Ar template
255argument has fewer than six trailing
256.Em X Ns s .
257.It Bq Er EEXIST
258All file names tried are already in use.
259Consider appending more
260.Em X Ns s to the
261.Ar template .
262.El
263.Pp
264The
265.Fn mkstemps
266and
267.Fn mkostemps
268functions may set
269.Va errno
270to
271.Bl -tag -width Er
272.It Bq Er EINVAL
273The
274.Ar template
275argument length is less than
276.Ar suffixlen
277or it has fewer than six
278.Em X Ns s
279before the suffix.
280.It Bq Er EEXIST
281All file names tried are already in use.
282Consider appending more
283.Em X Ns s to the
284.Ar template .
285.El
286.Pp
287In addition, the
288.Fn mkostemp
289and
290.Fn mkostemps
291functions may also set
292.Va errno
293to
294.Bl -tag -width Er
295.It Bq Er EINVAL
296.Fa flags
297is invalid.
298.El
299.Pp
300The
301.Fn mktemp
302function may also set
303.Va errno
304to any value specified by the
305.Xr lstat 2
306function.
307.Pp
308The
309.Fn mkstemp ,
310.Fn mkostemp ,
311.Fn mkstemps ,
312and
313.Fn mkostemps
314functions may also set
315.Va errno
316to any value specified by the
317.Xr open 2
318function.
319.Pp
320The
321.Fn mkdtemp
322function may also set
323.Va errno
324to any value specified by the
325.Xr mkdir 2
326function.
327.Sh SEE ALSO
328.Xr chmod 2 ,
329.Xr lstat 2 ,
330.Xr mkdir 2 ,
331.Xr open 2 ,
332.Xr tempnam 3 ,
333.Xr tmpfile 3 ,
334.Xr tmpnam 3
335.Sh STANDARDS
336The
337.Fn mkdtemp
338and
339.Fn mkstemp
340functions conform to the
341.St -p1003.1-2008
342specification.
343The ability to specify more than six
344.Em X Ns s
345is an extension to that standard.
346The
347.Fn mkostemp
348function is expected to conform to a future revision of that standard.
349.Pp
350The
351.Fn mktemp
352function conforms to
353.St -p1003.1-2001 ;
354as of
355.St -p1003.1-2008
356it is no longer a part of the standard.
357.Pp
358The
359.Fn mkstemps
360and
361.Fn mkostemps
362functions are non-standard and should not be used if portability is required.
363.Sh HISTORY
364A
365.Fn mktemp
366function appeared in
367.At v7 .
368The
369.Fn mkdtemp
370function appeared in
371.Ox 2.2 .
372The
373.Fn mkstemp
374function appeared in
375.Bx 4.3 .
376The
377.Fn mkstemps
378function appeared in
379.Ox 2.3 .
380The
381.Fn mkostemp
382and
383.Fn mkostemps
384functions appeared in
385.Ox 5.7 .
386.Sh BUGS
387For
388.Fn mktemp
389there is an obvious race between file name selection and file
390creation and deletion: the program is typically written to call
391.Xr tmpnam 3 ,
392.Xr tempnam 3 ,
393or
394.Fn mktemp .
395Subsequently, the program calls
396.Xr open 2
397or
398.Xr fopen 3
399and erroneously opens a file (or symbolic link, FIFO or other
400device) that the attacker has created in the expected file location.
401Hence
402.Fn mkstemp
403is recommended, since it atomically creates the file.
404An attacker can guess the file names produced by
405.Fn mktemp .
406Whenever it is possible,
407.Fn mkstemp
408or
409.Fn mkdtemp
410should be used instead.
411.Pp
412For this reason,
413.Xr ld 1
414will output a warning message whenever it links code that uses
415.Fn mktemp .
diff --git a/src/lib/libc/stdlib/mktemp.c b/src/lib/libc/stdlib/mktemp.c
new file mode 100644
index 0000000000..3b8bba7846
--- /dev/null
+++ b/src/lib/libc/stdlib/mktemp.c
@@ -0,0 +1,163 @@
1/* $OpenBSD: mktemp.c,v 1.1 2024/01/19 16:30:28 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#define MKOTEMP_FLAGS (O_APPEND | O_CLOEXEC | O_DSYNC | O_RSYNC | O_SYNC)
39
40#ifndef nitems
41#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
42#endif
43
44static int
45mktemp_internal(char *path, int slen, int mode, int flags)
46{
47 char *start, *cp, *ep;
48 const char tempchars[] = TEMPCHARS;
49 unsigned int tries;
50 struct stat sb;
51 size_t len;
52 int fd;
53
54 len = strlen(path);
55 if (len < MIN_X || slen < 0 || (size_t)slen > len - MIN_X) {
56 errno = EINVAL;
57 return(-1);
58 }
59 ep = path + len - slen;
60
61 for (start = ep; start > path && start[-1] == 'X'; start--)
62 ;
63 if (ep - start < MIN_X) {
64 errno = EINVAL;
65 return(-1);
66 }
67
68 if (flags & ~MKOTEMP_FLAGS) {
69 errno = EINVAL;
70 return(-1);
71 }
72 flags |= O_CREAT | O_EXCL | O_RDWR;
73
74 tries = INT_MAX;
75 do {
76 cp = start;
77 do {
78 unsigned short rbuf[16];
79 unsigned int i;
80
81 /*
82 * Avoid lots of arc4random() calls by using
83 * a buffer sized for up to 16 Xs at a time.
84 */
85 arc4random_buf(rbuf, sizeof(rbuf));
86 for (i = 0; i < nitems(rbuf) && cp != ep; i++)
87 *cp++ = tempchars[rbuf[i] % NUM_CHARS];
88 } while (cp != ep);
89
90 switch (mode) {
91 case MKTEMP_NAME:
92 if (lstat(path, &sb) != 0)
93 return(errno == ENOENT ? 0 : -1);
94 break;
95 case MKTEMP_FILE:
96 fd = open(path, flags, S_IRUSR|S_IWUSR);
97 if (fd != -1 || errno != EEXIST)
98 return(fd);
99 break;
100 case MKTEMP_DIR:
101 if (mkdir(path, S_IRUSR|S_IWUSR|S_IXUSR) == 0)
102 return(0);
103 if (errno != EEXIST)
104 return(-1);
105 break;
106 }
107 } while (--tries);
108
109 errno = EEXIST;
110 return(-1);
111}
112
113char *
114_mktemp(char *path)
115{
116 if (mktemp_internal(path, 0, MKTEMP_NAME, 0) == -1)
117 return(NULL);
118 return(path);
119}
120
121__warn_references(mktemp,
122 "mktemp() possibly used unsafely; consider using mkstemp()");
123
124char *
125mktemp(char *path)
126{
127 return(_mktemp(path));
128}
129
130int
131mkostemps(char *path, int slen, int flags)
132{
133 return(mktemp_internal(path, slen, MKTEMP_FILE, flags));
134}
135
136int
137mkstemp(char *path)
138{
139 return(mktemp_internal(path, 0, MKTEMP_FILE, 0));
140}
141DEF_WEAK(mkstemp);
142
143int
144mkostemp(char *path, int flags)
145{
146 return(mktemp_internal(path, 0, MKTEMP_FILE, flags));
147}
148DEF_WEAK(mkostemp);
149
150int
151mkstemps(char *path, int slen)
152{
153 return(mktemp_internal(path, slen, MKTEMP_FILE, 0));
154}
155
156char *
157mkdtemp(char *path)
158{
159 int error;
160
161 error = mktemp_internal(path, 0, MKTEMP_DIR, 0);
162 return(error ? NULL : path);
163}