From 2f253e75c830341a4fe130a3c7aa042fb0ab9a59 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Fri, 13 Feb 1998 02:13:24 +0000 Subject: [PATCH] Many security improvements from OpenBSD: implement mkdtemp improve man page for mk*temp use arc4random to seed extra XXX's randomly Optionally warn of unsafe mktemp uses From various commits by theo de raadt and Todd Miller. Obtained from: OpenBSD This should go into 2.2 after a testing period. --- lib/libc/stdio/Makefile.inc | 3 +- lib/libc/stdio/mktemp.3 | 52 ++++++++++++++++----- lib/libc/stdio/mktemp.c | 93 ++++++++++++++++++++++++++++--------- 3 files changed, 113 insertions(+), 35 deletions(-) diff --git a/lib/libc/stdio/Makefile.inc b/lib/libc/stdio/Makefile.inc index 97b403a25071..031e397a920f 100644 --- a/lib/libc/stdio/Makefile.inc +++ b/lib/libc/stdio/Makefile.inc @@ -1,5 +1,5 @@ # @(#)Makefile.inc 8.3 (Berkeley) 4/17/94 -# $Id: Makefile.inc,v 1.10 1997/10/15 16:16:07 bde Exp $ +# $Id: Makefile.inc,v 1.11 1997/10/21 08:41:10 bde Exp $ # stdio sources .PATH: ${.CURDIR}/../libc/stdio @@ -30,6 +30,7 @@ MLINKS+=fseek.3 fgetpos.3 fseek.3 fsetpos.3 fseek.3 ftell.3 fseek.3 rewind.3 MLINKS+=funopen.3 fropen.3 funopen.3 fwopen.3 MLINKS+=getc.3 fgetc.3 getc.3 getchar.3 getc.3 getw.3 MLINKS+=mktemp.3 mkstemp.3 +MLINKS+=mktemp.3 mkdtemp.3 MLINKS+=printf.3 asprintf.3 printf.3 fprintf.3 \ printf.3 snprintf.3 printf.3 sprintf.3 \ printf.3 vasprintf.3 \ diff --git a/lib/libc/stdio/mktemp.3 b/lib/libc/stdio/mktemp.3 index 28a662d11beb..50d7de4cda3f 100644 --- a/lib/libc/stdio/mktemp.3 +++ b/lib/libc/stdio/mktemp.3 @@ -31,7 +31,7 @@ .\" .\" @(#)mktemp.3 8.1 (Berkeley) 6/4/93 .\" -.Dd June 4, 1993 +.Dd February 11, 1998 .Dt MKTEMP 3 .Os .Sh NAME @@ -43,6 +43,8 @@ .Fn mktemp "char *template" .Ft int .Fn mkstemp "char *template" +.Ft char * +.Fn mkdtemp "char *template" .Sh DESCRIPTION The .Fn mktemp @@ -78,11 +80,18 @@ 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 mkdtemp +function makes the same replacement to the template as in +.Xr mktemp 3 +and creates the template directory, mode 0700. .Sh RETURN VALUES The .Fn mktemp -function -returns a pointer to the template on success and +and +.Fn mkdtemp +functions return a pointer to the template on success and .Dv NULL on failure. The @@ -93,9 +102,9 @@ If either call fails an error code is placed in the global variable .Va errno . .Sh ERRORS The -.Fn mktemp -and .Fn mkstemp +and +.Fn mkdtemp functions may set .Va errno @@ -106,9 +115,9 @@ The pathname portion of the template is not an existing directory. .El .Pp The -.Fn mktemp -and .Fn mkstemp +and +.Fn mkdtemp functions may also set .Va errno @@ -124,12 +133,22 @@ may also set 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 NOTES A common problem that results in a core dump is that the programmer passes in a read-only string to -.Fn mktemp -or -.Fn mkstemp . +.Fn mktemp , +.Fn mkstemp +or +.Fn mkdtemp . This is common with programs that were developed before .St -ansiC compilers were common. @@ -146,13 +165,24 @@ so that it will store string constants in a writable segment of memory. See .Xr gcc 1 for more information. +.Sh BUGS +An attacker can guess the filenames produced by +.Fn mktemp . +Whenever it is possible +.Fn mkstemp +should be used instead. .Sh SEE ALSO .Xr chmod 2 , .Xr getpid 2 , +.Xr mkdir 2 , .Xr open 2 , .Xr stat 2 .Sh HISTORY A -.Nm mktemp +.Fn mktemp function appeared in .At v7 . +The +.Fn mkdtemp +function first appeared in +.Ox 2.2 . diff --git a/lib/libc/stdio/mktemp.c b/lib/libc/stdio/mktemp.c index 19125e677ff9..561952291fb7 100644 --- a/lib/libc/stdio/mktemp.c +++ b/lib/libc/stdio/mktemp.c @@ -36,7 +36,7 @@ static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; #endif static const char rcsid[] = - "$Id: mktemp.c,v 1.6 1997/02/22 15:02:16 peter Exp $"; + "$Id: mktemp.c,v 1.7 1997/04/07 18:01:10 guido Exp $"; #endif /* LIBC_SCCS and not lint */ #include @@ -44,10 +44,11 @@ static const char rcsid[] = #include #include #include +#include #include #include -static int _gettemp(char *, int *); +static int _gettemp(char *, int *, int); int mkstemp(path) @@ -55,49 +56,91 @@ mkstemp(path) { int fd; - return (_gettemp(path, &fd) ? fd : -1); + return (_gettemp(path, &fd, 0) ? fd : -1); } +char * +mkdtemp(path) + char *path; +{ + return(_gettemp(path, (int *)NULL, 1) ? path : (char *)NULL); +} + +char *_mktemp(char *); + +char * +_mktemp(path) + char *path; +{ + return(_gettemp(path, (int *)NULL, 0) ? path : (char *)NULL); +} + +#ifdef UNSAFE_WARN +__warn_references(mktemp, + "warning: mktemp() possibly used unsafely; consider using mkstemp()"); +#endif + char * mktemp(path) char *path; { - return(_gettemp(path, (int *)NULL) ? path : (char *)NULL); + return(_mktemp(path)); } static int -_gettemp(path, doopen) +_gettemp(path, doopen, domkdir) char *path; register int *doopen; + int domkdir; { register char *start, *trv; struct stat sbuf; - pid_t pid; + int pid, rval; + + if (doopen && domkdir) { + errno = EINVAL; + return(0); + } pid = getpid(); - for (trv = path; *trv; ++trv); /* extra X's get set to 0's */ - while (*--trv == 'X') { - *trv = (pid % 10) + '0'; + for (trv = path; *trv; ++trv) + ; + --trv; + while (*trv == 'X' && pid != 0) { + *trv-- = (pid % 10) + '0'; pid /= 10; } + while (*trv == 'X') { + char c; + + pid = (arc4random() & 0xffff) % (26+26); + if (pid < 26) + c = pid + 'A'; + else + c = (pid - 26) + 'a'; + *trv-- = c; + } /* * check the target directory; if you have six X's and it * doesn't exist this runs for a *very* long time. */ - for (start = trv + 1;; --trv) { - if (trv <= path) - break; - if (*trv == '/') { - *trv = '\0'; - if (stat(path, &sbuf)) - return(0); - if (!S_ISDIR(sbuf.st_mode)) { - errno = ENOTDIR; - return(0); + if (doopen || domkdir) { + for (start = trv + 1;; --trv) { + if (trv <= path) + break; + if (*trv == '/') { + *trv = '\0'; + rval = stat(path, &sbuf); + *trv = '/'; + if (rval != 0) + return(0); + if (!S_ISDIR(sbuf.st_mode)) { + errno = ENOTDIR; + return(0); + } + break; } - *trv = '/'; - break; } } @@ -108,8 +151,12 @@ _gettemp(path, doopen) return(1); if (errno != EEXIST) return(0); - } - else if (lstat(path, &sbuf)) + } else if (domkdir) { + if (mkdir(path, 0700) == 0) + return(1); + if (errno != EEXIST) + return(0); + } else if (lstat(path, &sbuf)) return(errno == ENOENT ? 1 : 0); /* tricky little algorithm for backward compatibility */