From: millert Date: Fri, 19 Jan 2024 16:30:28 +0000 (+0000) Subject: Move mktemp.c to stdlib where it belongs. X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=3cb1b7a60aa0d1c5e2de86620eca8180d9a4c93d;p=openbsd Move mktemp.c to stdlib where it belongs. OK deraadt@ --- diff --git a/lib/libc/stdio/Makefile.inc b/lib/libc/stdio/Makefile.inc index 00ae1b2c2b6..356e3fc956f 100644 --- a/lib/libc/stdio/Makefile.inc +++ b/lib/libc/stdio/Makefile.inc @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile.inc,v 1.29 2016/03/30 06:38:41 jmc Exp $ +# $OpenBSD: Makefile.inc,v 1.30 2024/01/19 16:30:28 millert Exp $ # stdio sources .PATH: ${LIBCSRCDIR}/stdio @@ -9,7 +9,7 @@ SRCS+= asprintf.c clrerr.c fclose.c fdopen.c feof.c ferror.c fflush.c fgetc.c \ fgetln.c fgetpos.c fgets.c fileno.c findfp.c flags.c fmemopen.c \ fopen.c fprintf.c fpurge.c fputc.c fputs.c fread.c freopen.c fscanf.c \ fseek.c fsetpos.c ftell.c funopen.c fvwrite.c fwalk.c fwrite.c \ - getc.c getchar.c getw.c makebuf.c mktemp.c open_memstream.c \ + getc.c getchar.c getw.c makebuf.c open_memstream.c \ open_wmemstream.c perror.c printf.c putc.c putchar.c puts.c putw.c \ refill.c remove.c rewind.c rget.c scanf.c setbuf.c setbuffer.c \ setvbuf.c snprintf.c sprintf.c sscanf.c stdio.c tempnam.c tmpfile.c \ @@ -22,7 +22,7 @@ SRCS+= asprintf.c clrerr.c fclose.c fdopen.c feof.c ferror.c fflush.c fgetc.c \ getdelim.c getline.c dprintf.c vdprintf.c MAN+= fclose.3 ferror.3 fflush.3 fgetln.3 fgets.3 fgetwln.3 fmemopen.3 \ - fopen.3 fputs.3 fread.3 fseek.3 funopen.3 getc.3 mktemp.3 \ + fopen.3 fputs.3 fread.3 fseek.3 funopen.3 getc.3 \ open_memstream.3 perror.3 printf.3 putc.3 remove.3 scanf.3 setbuf.3 \ setvbuf.3 stdio.3 tmpnam.3 ungetc.3 fgetws.3 fputws.3 fwide.3 getwc.3 \ putwc.3 ungetwc.3 wprintf.3 wscanf.3 getdelim.3 diff --git a/lib/libc/stdio/mktemp.3 b/lib/libc/stdio/mktemp.3 deleted file mode 100644 index 050a5c8cb44..00000000000 --- a/lib/libc/stdio/mktemp.3 +++ /dev/null @@ -1,415 +0,0 @@ -.\" $OpenBSD: mktemp.3,v 1.56 2022/08/04 06:20:24 jsg Exp $ -.\" -.\" Copyright (c) 1989, 1991, 1993 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.Dd $Mdocdate: August 4 2022 $ -.Dt MKTEMP 3 -.Os -.Sh NAME -.Nm mktemp , -.Nm mkstemp , -.Nm mkostemp , -.Nm mkstemps , -.Nm mkostemps , -.Nm mkdtemp -.Nd make temporary file name (unique) -.Sh SYNOPSIS -.In stdlib.h -.Ft char * -.Fn mktemp "char *template" -.Ft int -.Fn mkstemp "char *template" -.Ft int -.Fn mkstemps "char *template" "int suffixlen" -.Ft char * -.Fn mkdtemp "char *template" -.In stdlib.h -.In fcntl.h -.Ft int -.Fn mkostemp "char *template" "int flags" -.Ft int -.Fn mkostemps "char *template" "int suffixlen" "int flags" -.Sh DESCRIPTION -The -.Fn mktemp -family of functions take the given file name template and overwrite -a portion of it to create a new file name. -This file name is unique and suitable for use by the application. -The template may be any file name with at least six trailing -.Em X Ns s , -for example -.Pa /tmp/temp.XXXXXXXX . -The trailing -.Em X Ns s -are replaced with a unique digit and letter combination. -The number of unique file names that can be returned -depends on the number of -.Em X Ns s -provided; -.Fn mktemp -will try at least 2 ** 31 combinations before giving up. -At least six -.Em X Ns s -must be used, though 10 is much better. -.Pp -The -.Fn mktemp -function generates a temporary file name based on a template as -described above. -Because -.Fn mktemp -does not actually create the temporary file, there is a window of -opportunity during which another process can open the file instead. -Because of this race condition, -.Fn mktemp -should not be used where -.Fn mkstemp -can be used instead. -.Fn mktemp -was marked as a legacy interface in -.St -p1003.1-2001 . -.Pp -The -.Fn mkstemp -function makes the same replacement to the template and creates the template -file, mode 0600, returning a file descriptor opened for reading and writing. -This avoids the race between testing for a file's existence and opening it -for use. -.Pp -The -.Fn mkostemp -function acts the same as -.Fn mkstemp , -except that the -.Fa flags -argument may contain zero or more of the following flags for the underlying -.Xr open 2 -system call: -.Pp -.Bl -tag -width "O_CLOEXECXX" -offset indent -compact -.It Dv O_APPEND -Append on each write. -.It Dv O_CLOEXEC -Set the close-on-exec flag on the new file descriptor. -.It Dv O_SYNC -Perform synchronous I/O operations. -.El -.Pp -The -.Fn mkstemps -and -.Fn mkostemps -functions act the same as -.Fn mkstemp -and -.Fn mkostemp , -except they permit a suffix to exist in the template. -The template should be of the form -.Pa /tmp/tmpXXXXXXXXXXsuffix . -.Fn mkstemps -and -.Fn mkostemps -are told the length of the suffix string, i.e., -.Li strlen("suffix") . -.Pp -The -.Fn mkdtemp -function makes the same replacement to the template as in -.Fn mktemp -and creates the template directory, mode 0700. -.Sh RETURN VALUES -The -.Fn mktemp -and -.Fn mkdtemp -functions return a pointer to the template on success and -.Dv NULL -on failure. -The -.Fn mkstemp , -.Fn mkostemp , -.Fn mkstemps , -and -.Fn mkostemps -functions return \-1 if no suitable file could be created. -If any call fails, an error code is placed in the global variable -.Va errno . -.Sh EXAMPLES -Quite often a programmer will want to replace a use of -.Fn mktemp -with -.Fn mkstemp , -usually to avoid the problems described above. -Doing this correctly requires a good understanding of the code in question. -.Pp -For instance, code of this form: -.Bd -literal -offset indent -char sfn[19]; -FILE *sfp; - -strlcpy(sfn, "/tmp/ed.XXXXXXXXXX", sizeof(sfn)); -if (mktemp(sfn) == NULL || (sfp = fopen(sfn, "w+")) == NULL) { - warn("%s", sfn); - return (NULL); -} -return (sfp); -.Ed -.Pp -should be rewritten like this: -.Bd -literal -offset indent -char sfn[19]; -FILE *sfp; -int fd; - -strlcpy(sfn, "/tmp/ed.XXXXXXXXXX", sizeof(sfn)); -if ((fd = mkstemp(sfn)) == -1 || - (sfp = fdopen(fd, "w+")) == NULL) { - if (fd != -1) { - unlink(sfn); - close(fd); - } - warn("%s", sfn); - return (NULL); -} -return (sfp); -.Ed -.Pp -Often one will find code which uses -.Fn mktemp -very early on, perhaps to globally initialize the template nicely, but the -code which calls -.Xr open 2 -or -.Xr fopen 3 -on that file name will occur much later. -(In almost all cases, the use of -.Xr fopen 3 -will mean that the flags -.Dv O_CREAT -| -.Dv O_EXCL -are not given to -.Xr open 2 , -and thus a symbolic link race becomes possible, hence making -necessary the use of -.Xr fdopen 3 -as seen above.) -Furthermore, one must be careful about code which opens, closes, and then -re-opens the file in question. -Finally, one must ensure that upon error the temporary file is -removed correctly. -.Pp -There are also cases where modifying the code to use -.Fn mktemp , -in concert with -.Xr open 2 -using the flags -.Dv O_CREAT -| -.Dv O_EXCL , -is better, as long as the code retries a new template if -.Xr open 2 -fails with an -.Va errno -of -.Er EEXIST . -.Sh ERRORS -The -.Fn mktemp , -.Fn mkstemp , -.Fn mkostemp , -and -.Fn mkdtemp -functions may set -.Va errno -to one of the following values: -.Bl -tag -width Er -.It Bq Er EINVAL -The -.Ar template -argument has fewer than six trailing -.Em X Ns s . -.It Bq Er EEXIST -All file names tried are already in use. -Consider appending more -.Em X Ns s to the -.Ar template . -.El -.Pp -The -.Fn mkstemps -and -.Fn mkostemps -functions may set -.Va errno -to -.Bl -tag -width Er -.It Bq Er EINVAL -The -.Ar template -argument length is less than -.Ar suffixlen -or it has fewer than six -.Em X Ns s -before the suffix. -.It Bq Er EEXIST -All file names tried are already in use. -Consider appending more -.Em X Ns s to the -.Ar template . -.El -.Pp -In addition, the -.Fn mkostemp -and -.Fn mkostemps -functions may also set -.Va errno -to -.Bl -tag -width Er -.It Bq Er EINVAL -.Fa flags -is invalid. -.El -.Pp -The -.Fn mktemp -function may also set -.Va errno -to any value specified by the -.Xr lstat 2 -function. -.Pp -The -.Fn mkstemp , -.Fn mkostemp , -.Fn mkstemps , -and -.Fn mkostemps -functions may also set -.Va errno -to any value specified by the -.Xr open 2 -function. -.Pp -The -.Fn mkdtemp -function may also set -.Va errno -to any value specified by the -.Xr mkdir 2 -function. -.Sh SEE ALSO -.Xr chmod 2 , -.Xr lstat 2 , -.Xr mkdir 2 , -.Xr open 2 , -.Xr tempnam 3 , -.Xr tmpfile 3 , -.Xr tmpnam 3 -.Sh STANDARDS -The -.Fn mkdtemp -and -.Fn mkstemp -functions conform to the -.St -p1003.1-2008 -specification. -The ability to specify more than six -.Em X Ns s -is an extension to that standard. -The -.Fn mkostemp -function is expected to conform to a future revision of that standard. -.Pp -The -.Fn mktemp -function conforms to -.St -p1003.1-2001 ; -as of -.St -p1003.1-2008 -it is no longer a part of the standard. -.Pp -The -.Fn mkstemps -and -.Fn mkostemps -functions are non-standard and should not be used if portability is required. -.Sh HISTORY -A -.Fn mktemp -function appeared in -.At v7 . -The -.Fn mkdtemp -function appeared in -.Ox 2.2 . -The -.Fn mkstemp -function appeared in -.Bx 4.3 . -The -.Fn mkstemps -function appeared in -.Ox 2.3 . -The -.Fn mkostemp -and -.Fn mkostemps -functions appeared in -.Ox 5.7 . -.Sh BUGS -For -.Fn mktemp -there is an obvious race between file name selection and file -creation and deletion: the program is typically written to call -.Xr tmpnam 3 , -.Xr tempnam 3 , -or -.Fn mktemp . -Subsequently, the program calls -.Xr open 2 -or -.Xr fopen 3 -and erroneously opens a file (or symbolic link, FIFO or other -device) that the attacker has created in the expected file location. -Hence -.Fn mkstemp -is recommended, since it atomically creates the file. -An attacker can guess the file names produced by -.Fn mktemp . -Whenever it is possible, -.Fn mkstemp -or -.Fn mkdtemp -should be used instead. -.Pp -For this reason, -.Xr ld 1 -will output a warning message whenever it links code that uses -.Fn mktemp . diff --git a/lib/libc/stdio/mktemp.c b/lib/libc/stdio/mktemp.c deleted file mode 100644 index ef9a1836107..00000000000 --- a/lib/libc/stdio/mktemp.c +++ /dev/null @@ -1,163 +0,0 @@ -/* $OpenBSD: mktemp.c,v 1.39 2017/11/28 06:55:49 tb Exp $ */ -/* - * Copyright (c) 1996-1998, 2008 Theo de Raadt - * Copyright (c) 1997, 2008-2009 Todd C. Miller - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MKTEMP_NAME 0 -#define MKTEMP_FILE 1 -#define MKTEMP_DIR 2 - -#define TEMPCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" -#define NUM_CHARS (sizeof(TEMPCHARS) - 1) -#define MIN_X 6 - -#define MKOTEMP_FLAGS (O_APPEND | O_CLOEXEC | O_DSYNC | O_RSYNC | O_SYNC) - -#ifndef nitems -#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) -#endif - -static int -mktemp_internal(char *path, int slen, int mode, int flags) -{ - char *start, *cp, *ep; - const char tempchars[] = TEMPCHARS; - unsigned int tries; - struct stat sb; - size_t len; - int fd; - - len = strlen(path); - if (len < MIN_X || slen < 0 || (size_t)slen > len - MIN_X) { - errno = EINVAL; - return(-1); - } - ep = path + len - slen; - - for (start = ep; start > path && start[-1] == 'X'; start--) - ; - if (ep - start < MIN_X) { - errno = EINVAL; - return(-1); - } - - if (flags & ~MKOTEMP_FLAGS) { - errno = EINVAL; - return(-1); - } - flags |= O_CREAT | O_EXCL | O_RDWR; - - tries = INT_MAX; - do { - cp = start; - do { - unsigned short rbuf[16]; - unsigned int i; - - /* - * Avoid lots of arc4random() calls by using - * a buffer sized for up to 16 Xs at a time. - */ - arc4random_buf(rbuf, sizeof(rbuf)); - for (i = 0; i < nitems(rbuf) && cp != ep; i++) - *cp++ = tempchars[rbuf[i] % NUM_CHARS]; - } while (cp != ep); - - switch (mode) { - case MKTEMP_NAME: - if (lstat(path, &sb) != 0) - return(errno == ENOENT ? 0 : -1); - break; - case MKTEMP_FILE: - fd = open(path, flags, S_IRUSR|S_IWUSR); - if (fd != -1 || errno != EEXIST) - return(fd); - break; - case MKTEMP_DIR: - if (mkdir(path, S_IRUSR|S_IWUSR|S_IXUSR) == 0) - return(0); - if (errno != EEXIST) - return(-1); - break; - } - } while (--tries); - - errno = EEXIST; - return(-1); -} - -char * -_mktemp(char *path) -{ - if (mktemp_internal(path, 0, MKTEMP_NAME, 0) == -1) - return(NULL); - return(path); -} - -__warn_references(mktemp, - "mktemp() possibly used unsafely; consider using mkstemp()"); - -char * -mktemp(char *path) -{ - return(_mktemp(path)); -} - -int -mkostemps(char *path, int slen, int flags) -{ - return(mktemp_internal(path, slen, MKTEMP_FILE, flags)); -} - -int -mkstemp(char *path) -{ - return(mktemp_internal(path, 0, MKTEMP_FILE, 0)); -} -DEF_WEAK(mkstemp); - -int -mkostemp(char *path, int flags) -{ - return(mktemp_internal(path, 0, MKTEMP_FILE, flags)); -} -DEF_WEAK(mkostemp); - -int -mkstemps(char *path, int slen) -{ - return(mktemp_internal(path, slen, MKTEMP_FILE, 0)); -} - -char * -mkdtemp(char *path) -{ - int error; - - error = mktemp_internal(path, 0, MKTEMP_DIR, 0); - return(error ? NULL : path); -} diff --git a/lib/libc/stdlib/Makefile.inc b/lib/libc/stdlib/Makefile.inc index 55b80185222..fa4836f42bb 100644 --- a/lib/libc/stdlib/Makefile.inc +++ b/lib/libc/stdlib/Makefile.inc @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile.inc,v 1.64 2017/12/16 20:06:55 guenther Exp $ +# $OpenBSD: Makefile.inc,v 1.65 2024/01/19 16:30:28 millert Exp $ # stdlib sources .PATH: ${LIBCSRCDIR}/arch/${MACHINE_CPU}/stdlib ${LIBCSRCDIR}/stdlib @@ -6,7 +6,7 @@ SRCS+= a64l.c abort.c atexit.c atoi.c atof.c atol.c atoll.c bsearch.c \ exit.c ecvt.c gcvt.c getenv.c getopt_long.c \ getsubopt.c hcreate.c heapsort.c imaxabs.c imaxdiv.c insque.c \ - l64a.c llabs.c lldiv.c lsearch.c malloc.c reallocarray.c \ + l64a.c llabs.c lldiv.c lsearch.c malloc.c mktemp.c reallocarray.c \ merge.c posix_pty.c qsort.c radixsort.c rand.c random.c \ realpath.c remque.c setenv.c strtoimax.c \ 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 MAN+= a64l.3 abort.3 abs.3 alloca.3 atexit.3 atof.3 atoi.3 atol.3 atoll.3 \ bsearch.3 div.3 ecvt.3 exit.3 getenv.3 getopt.3 getopt_long.3 \ getsubopt.3 hcreate.3 imaxabs.3 imaxdiv.3 insque.3 labs.3 ldiv.3 \ - lldiv.3 lsearch.3 malloc.3 posix_memalign.3 posix_openpt.3 ptsname.3 \ - qsort.3 radixsort.3 rand48.3 rand.3 random.3 realpath.3 \ + lldiv.3 lsearch.3 malloc.3 mktemp.3 posix_memalign.3 posix_openpt.3 \ + ptsname.3 qsort.3 radixsort.3 rand48.3 rand.3 random.3 realpath.3 \ strtod.3 strtonum.3 strtol.3 strtoul.3 system.3 tsearch.3 diff --git a/lib/libc/stdlib/mktemp.3 b/lib/libc/stdlib/mktemp.3 new file mode 100644 index 00000000000..d4bd7bdc91b --- /dev/null +++ b/lib/libc/stdlib/mktemp.3 @@ -0,0 +1,415 @@ +.\" $OpenBSD: mktemp.3,v 1.1 2024/01/19 16:30:28 millert Exp $ +.\" +.\" Copyright (c) 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd $Mdocdate: January 19 2024 $ +.Dt MKTEMP 3 +.Os +.Sh NAME +.Nm mktemp , +.Nm mkstemp , +.Nm mkostemp , +.Nm mkstemps , +.Nm mkostemps , +.Nm mkdtemp +.Nd make temporary file name (unique) +.Sh SYNOPSIS +.In stdlib.h +.Ft char * +.Fn mktemp "char *template" +.Ft int +.Fn mkstemp "char *template" +.Ft int +.Fn mkstemps "char *template" "int suffixlen" +.Ft char * +.Fn mkdtemp "char *template" +.In stdlib.h +.In fcntl.h +.Ft int +.Fn mkostemp "char *template" "int flags" +.Ft int +.Fn mkostemps "char *template" "int suffixlen" "int flags" +.Sh DESCRIPTION +The +.Fn mktemp +family of functions take the given file name template and overwrite +a portion of it to create a new file name. +This file name is unique and suitable for use by the application. +The template may be any file name with at least six trailing +.Em X Ns s , +for example +.Pa /tmp/temp.XXXXXXXX . +The trailing +.Em X Ns s +are replaced with a unique digit and letter combination. +The number of unique file names that can be returned +depends on the number of +.Em X Ns s +provided; +.Fn mktemp +will try at least 2 ** 31 combinations before giving up. +At least six +.Em X Ns s +must be used, though 10 is much better. +.Pp +The +.Fn mktemp +function generates a temporary file name based on a template as +described above. +Because +.Fn mktemp +does not actually create the temporary file, there is a window of +opportunity during which another process can open the file instead. +Because of this race condition, +.Fn mktemp +should not be used where +.Fn mkstemp +can be used instead. +.Fn mktemp +was marked as a legacy interface in +.St -p1003.1-2001 . +.Pp +The +.Fn mkstemp +function makes the same replacement to the template and creates the template +file, mode 0600, returning a file descriptor opened for reading and writing. +This avoids the race between testing for a file's existence and opening it +for use. +.Pp +The +.Fn mkostemp +function acts the same as +.Fn mkstemp , +except that the +.Fa flags +argument may contain zero or more of the following flags for the underlying +.Xr open 2 +system call: +.Pp +.Bl -tag -width "O_CLOEXECXX" -offset indent -compact +.It Dv O_APPEND +Append on each write. +.It Dv O_CLOEXEC +Set the close-on-exec flag on the new file descriptor. +.It Dv O_SYNC +Perform synchronous I/O operations. +.El +.Pp +The +.Fn mkstemps +and +.Fn mkostemps +functions act the same as +.Fn mkstemp +and +.Fn mkostemp , +except they permit a suffix to exist in the template. +The template should be of the form +.Pa /tmp/tmpXXXXXXXXXXsuffix . +.Fn mkstemps +and +.Fn mkostemps +are told the length of the suffix string, i.e., +.Li strlen("suffix") . +.Pp +The +.Fn mkdtemp +function makes the same replacement to the template as in +.Fn mktemp +and creates the template directory, mode 0700. +.Sh RETURN VALUES +The +.Fn mktemp +and +.Fn mkdtemp +functions return a pointer to the template on success and +.Dv NULL +on failure. +The +.Fn mkstemp , +.Fn mkostemp , +.Fn mkstemps , +and +.Fn mkostemps +functions return \-1 if no suitable file could be created. +If any call fails, an error code is placed in the global variable +.Va errno . +.Sh EXAMPLES +Quite often a programmer will want to replace a use of +.Fn mktemp +with +.Fn mkstemp , +usually to avoid the problems described above. +Doing this correctly requires a good understanding of the code in question. +.Pp +For instance, code of this form: +.Bd -literal -offset indent +char sfn[19]; +FILE *sfp; + +strlcpy(sfn, "/tmp/ed.XXXXXXXXXX", sizeof(sfn)); +if (mktemp(sfn) == NULL || (sfp = fopen(sfn, "w+")) == NULL) { + warn("%s", sfn); + return (NULL); +} +return (sfp); +.Ed +.Pp +should be rewritten like this: +.Bd -literal -offset indent +char sfn[19]; +FILE *sfp; +int fd; + +strlcpy(sfn, "/tmp/ed.XXXXXXXXXX", sizeof(sfn)); +if ((fd = mkstemp(sfn)) == -1 || + (sfp = fdopen(fd, "w+")) == NULL) { + if (fd != -1) { + unlink(sfn); + close(fd); + } + warn("%s", sfn); + return (NULL); +} +return (sfp); +.Ed +.Pp +Often one will find code which uses +.Fn mktemp +very early on, perhaps to globally initialize the template nicely, but the +code which calls +.Xr open 2 +or +.Xr fopen 3 +on that file name will occur much later. +(In almost all cases, the use of +.Xr fopen 3 +will mean that the flags +.Dv O_CREAT +| +.Dv O_EXCL +are not given to +.Xr open 2 , +and thus a symbolic link race becomes possible, hence making +necessary the use of +.Xr fdopen 3 +as seen above.) +Furthermore, one must be careful about code which opens, closes, and then +re-opens the file in question. +Finally, one must ensure that upon error the temporary file is +removed correctly. +.Pp +There are also cases where modifying the code to use +.Fn mktemp , +in concert with +.Xr open 2 +using the flags +.Dv O_CREAT +| +.Dv O_EXCL , +is better, as long as the code retries a new template if +.Xr open 2 +fails with an +.Va errno +of +.Er EEXIST . +.Sh ERRORS +The +.Fn mktemp , +.Fn mkstemp , +.Fn mkostemp , +and +.Fn mkdtemp +functions may set +.Va errno +to one of the following values: +.Bl -tag -width Er +.It Bq Er EINVAL +The +.Ar template +argument has fewer than six trailing +.Em X Ns s . +.It Bq Er EEXIST +All file names tried are already in use. +Consider appending more +.Em X Ns s to the +.Ar template . +.El +.Pp +The +.Fn mkstemps +and +.Fn mkostemps +functions may set +.Va errno +to +.Bl -tag -width Er +.It Bq Er EINVAL +The +.Ar template +argument length is less than +.Ar suffixlen +or it has fewer than six +.Em X Ns s +before the suffix. +.It Bq Er EEXIST +All file names tried are already in use. +Consider appending more +.Em X Ns s to the +.Ar template . +.El +.Pp +In addition, the +.Fn mkostemp +and +.Fn mkostemps +functions may also set +.Va errno +to +.Bl -tag -width Er +.It Bq Er EINVAL +.Fa flags +is invalid. +.El +.Pp +The +.Fn mktemp +function may also set +.Va errno +to any value specified by the +.Xr lstat 2 +function. +.Pp +The +.Fn mkstemp , +.Fn mkostemp , +.Fn mkstemps , +and +.Fn mkostemps +functions may also set +.Va errno +to any value specified by the +.Xr open 2 +function. +.Pp +The +.Fn mkdtemp +function may also set +.Va errno +to any value specified by the +.Xr mkdir 2 +function. +.Sh SEE ALSO +.Xr chmod 2 , +.Xr lstat 2 , +.Xr mkdir 2 , +.Xr open 2 , +.Xr tempnam 3 , +.Xr tmpfile 3 , +.Xr tmpnam 3 +.Sh STANDARDS +The +.Fn mkdtemp +and +.Fn mkstemp +functions conform to the +.St -p1003.1-2008 +specification. +The ability to specify more than six +.Em X Ns s +is an extension to that standard. +The +.Fn mkostemp +function is expected to conform to a future revision of that standard. +.Pp +The +.Fn mktemp +function conforms to +.St -p1003.1-2001 ; +as of +.St -p1003.1-2008 +it is no longer a part of the standard. +.Pp +The +.Fn mkstemps +and +.Fn mkostemps +functions are non-standard and should not be used if portability is required. +.Sh HISTORY +A +.Fn mktemp +function appeared in +.At v7 . +The +.Fn mkdtemp +function appeared in +.Ox 2.2 . +The +.Fn mkstemp +function appeared in +.Bx 4.3 . +The +.Fn mkstemps +function appeared in +.Ox 2.3 . +The +.Fn mkostemp +and +.Fn mkostemps +functions appeared in +.Ox 5.7 . +.Sh BUGS +For +.Fn mktemp +there is an obvious race between file name selection and file +creation and deletion: the program is typically written to call +.Xr tmpnam 3 , +.Xr tempnam 3 , +or +.Fn mktemp . +Subsequently, the program calls +.Xr open 2 +or +.Xr fopen 3 +and erroneously opens a file (or symbolic link, FIFO or other +device) that the attacker has created in the expected file location. +Hence +.Fn mkstemp +is recommended, since it atomically creates the file. +An attacker can guess the file names produced by +.Fn mktemp . +Whenever it is possible, +.Fn mkstemp +or +.Fn mkdtemp +should be used instead. +.Pp +For this reason, +.Xr ld 1 +will output a warning message whenever it links code that uses +.Fn mktemp . diff --git a/lib/libc/stdlib/mktemp.c b/lib/libc/stdlib/mktemp.c new file mode 100644 index 00000000000..3b8bba78463 --- /dev/null +++ b/lib/libc/stdlib/mktemp.c @@ -0,0 +1,163 @@ +/* $OpenBSD: mktemp.c,v 1.1 2024/01/19 16:30:28 millert Exp $ */ +/* + * Copyright (c) 1996-1998, 2008 Theo de Raadt + * Copyright (c) 1997, 2008-2009 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MKTEMP_NAME 0 +#define MKTEMP_FILE 1 +#define MKTEMP_DIR 2 + +#define TEMPCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" +#define NUM_CHARS (sizeof(TEMPCHARS) - 1) +#define MIN_X 6 + +#define MKOTEMP_FLAGS (O_APPEND | O_CLOEXEC | O_DSYNC | O_RSYNC | O_SYNC) + +#ifndef nitems +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +#endif + +static int +mktemp_internal(char *path, int slen, int mode, int flags) +{ + char *start, *cp, *ep; + const char tempchars[] = TEMPCHARS; + unsigned int tries; + struct stat sb; + size_t len; + int fd; + + len = strlen(path); + if (len < MIN_X || slen < 0 || (size_t)slen > len - MIN_X) { + errno = EINVAL; + return(-1); + } + ep = path + len - slen; + + for (start = ep; start > path && start[-1] == 'X'; start--) + ; + if (ep - start < MIN_X) { + errno = EINVAL; + return(-1); + } + + if (flags & ~MKOTEMP_FLAGS) { + errno = EINVAL; + return(-1); + } + flags |= O_CREAT | O_EXCL | O_RDWR; + + tries = INT_MAX; + do { + cp = start; + do { + unsigned short rbuf[16]; + unsigned int i; + + /* + * Avoid lots of arc4random() calls by using + * a buffer sized for up to 16 Xs at a time. + */ + arc4random_buf(rbuf, sizeof(rbuf)); + for (i = 0; i < nitems(rbuf) && cp != ep; i++) + *cp++ = tempchars[rbuf[i] % NUM_CHARS]; + } while (cp != ep); + + switch (mode) { + case MKTEMP_NAME: + if (lstat(path, &sb) != 0) + return(errno == ENOENT ? 0 : -1); + break; + case MKTEMP_FILE: + fd = open(path, flags, S_IRUSR|S_IWUSR); + if (fd != -1 || errno != EEXIST) + return(fd); + break; + case MKTEMP_DIR: + if (mkdir(path, S_IRUSR|S_IWUSR|S_IXUSR) == 0) + return(0); + if (errno != EEXIST) + return(-1); + break; + } + } while (--tries); + + errno = EEXIST; + return(-1); +} + +char * +_mktemp(char *path) +{ + if (mktemp_internal(path, 0, MKTEMP_NAME, 0) == -1) + return(NULL); + return(path); +} + +__warn_references(mktemp, + "mktemp() possibly used unsafely; consider using mkstemp()"); + +char * +mktemp(char *path) +{ + return(_mktemp(path)); +} + +int +mkostemps(char *path, int slen, int flags) +{ + return(mktemp_internal(path, slen, MKTEMP_FILE, flags)); +} + +int +mkstemp(char *path) +{ + return(mktemp_internal(path, 0, MKTEMP_FILE, 0)); +} +DEF_WEAK(mkstemp); + +int +mkostemp(char *path, int flags) +{ + return(mktemp_internal(path, 0, MKTEMP_FILE, flags)); +} +DEF_WEAK(mkostemp); + +int +mkstemps(char *path, int slen) +{ + return(mktemp_internal(path, slen, MKTEMP_FILE, 0)); +} + +char * +mkdtemp(char *path) +{ + int error; + + error = mktemp_internal(path, 0, MKTEMP_DIR, 0); + return(error ? NULL : path); +}