[Message CC'd to bugtraq per Theo's request] theo>Check this out. >> Date: Wed, 2 Feb 2000 12:50:14 +0200 >> Sender: Bugtraq List <BUGTRAQat_private> >> From: Neil Blakey-Milner <nbmat_private> >> To: BUGTRAQat_private >> Organization: Rhodes University Computer Users' Society >> Subject: Re: Tempfile vulnerabilities >> X-To: Grant Taylor <gtaylor+bugtraq_hcdbb013100at_private> >> In-Reply-To: <200002010455.XAA20677at_private> >> >> On Mon 2000-01-31 (23:55), Grant Taylor wrote: >> > >> > sub get_tmpfile { >> > my $file; >> > do { >> > open RAN, "/dev/random" || die; >> > read(RAN,$foo,16); >> > close RAN; >> > $file = '/tmp/autobuse' . unpack('H16',$foo); >> > } while (-e $file || -l $file); >> > >> > return $file; >> > } >> > >> > This method is Linux-specific, but that's all I need. The fixed >> > autobuse is available at http://www.picante.com/~gtaylor/autobuse/ >> > >> > Note that Autobuse has, as far as I know, zero users (including me). >> > If I am wrong about this, please let me know! >> >> I was about to suggest using mkstemp() from File::MkTemp, available >> on CPAN, until I noticed that this mkstemp doesn't seem to use >> O_CREAT and O_EXCL from Fcntl nor does it chmod 600 or similar. >> >> It uses: >> >> $template = mktemp(@_); >> >> $openup = File::Spec->catfile($_[1], $template); >> >> $fh = new FileHandle ">$openup"; #and say ahhh. >> >> croak("Could not open file: $openup") >> unless(defined $fh); >> >> return($fh); >> >> Which seems to be just as bad as using mktemp, and then opening a >> file, with the usual race conditions. Considering the general >> feeling about the "mkstemp" concept, this implementation isn't >> quite there. >> >> Neil >> -- >> Neil Blakey-Milner >> nbmat_private >Is there a correct mkstemp(3) in perl? >THERE REALLY SHOULD BE. Can you give me details? I have always advocated one of two approaches. These are published. I can't help it if people do stupid things. :-( 1) use POSIX; do { $name = tmpnam(); } until sysopen(FH, $name, O_RDWR|O_CREAT|O_EXCL, 0666); 2) use IO::File; $handle = IO::File->new_tmpfile(); # this is POSIX's tmpfile(3) The second case is what you're told to use if if you ask for POSIX::tmpfile, which has always struck me as a bit as awkward at best. I think one should just do it instead of blowing up as POSIX::tmpfile current does. This may be an issue of FDs versus FPs; Perl I/O operators/functions expect to deal with (FILE *)FPs, not (int)FDs. So you'd have to fdopen the FD you got back anyway to do much with it. Anyway, here's the IO::File description: new_tmpfile Creates an IO::File opened for read/write on a newly created temporary file. On systems where this is possible, the temporary file is anonymous (ie. it is unlinked after creation, but held open). If the temporary file cannot be created or opened, the IO::File object is destroyed. Otherwise, it is returned to the caller. In the XS code[see footnote #0], we have the following: MODULE = IO PACKAGE = IO::File PREFIX = f SV * new_tmpfile(packname = "IO::File") char * packname PREINIT: OutputStream fp; GV *gv; CODE: #ifdef PerlIO fp = PerlIO_tmpfile(); #else fp = tmpfile(); #endif gv = (GV*)SvREFCNT_inc(newGVgen(packname)); hv_delete(GvSTASH(gv), GvNAME(gv), GvNAMELEN(gv), G_DISCARD); if (do_open(gv, "+>&", 3, FALSE, 0, 0, fp)) { ST(0) = sv_2mortal(newRV((SV*)gv)); sv_bless(ST(0), gv_stashpv(packname, TRUE)); SvREFCNT_dec(gv); /* undo increment in newRV() */ } else { ST(0) = &PL_sv_undef; SvREFCNT_dec(gv); } Which is just calling the standard POSIX tmpfile() function. Well, sometimes. If you use the Perl I/O abstraction, then you get either the real tmpfile(), or under sfio, some sftmp(0) thingie that I know nothing about. The only hole I see is in my ignorance of the possible sfio-related sftmp(0) call. Enlightenment in this area is welcome. I also don't know what sub-Unix systems do about the unlink issue, since their primitive or alien filesystems are notoriously too paltry to support that approach, since they can't bring themselves to talk about a file as something distinct from a filename. In that case, I don't know what happens to the file created by IO::File::new_tmpfile. Perhaps it has a destructor to unlink it eventually, but this isn't in any code I could find. There's been some discussion of having Perl automatically call tmpfile() in the event that its filename were undefined. For example: # get an anon filehandle (ie. stdio/sfio stream object) open(TMPFH, undef) || die "can't get tmpfilehandle: $!"; # or using monadic open on an undefined variable undef $tmpfh; # initial state of all variables, actually open($tmpfh) || die "can't get tmpfilehandle: $!"; In fact, Perl hero Nick Ing-Simmons once offered up the code to make open(F, undef) automatically call tmpfile(). Perhaps in retrospect lamentably, this proposal was shot down due to its ugly interface. It may well be that we want to resurrect that notion, perhaps amending it to produce a form more acceptable to the interface lawyers. I do not dispute that there's a great deal of code out there that for a temporary file, blindly does an open(TMPOUT, "> /tmp/foo.$$") in order to get its temporary file. I do feel that Perl has facilities to create safe temporary files, but that, for various reasons, people are largely unaware of them. So they end up doing the /tmp/foo.$$ thing, and thus open themselves up to a large host of maladies. That "strategy", to use the term generously, is vulnerable to pid prediction, which, while reasonably stymied by OpenBSD's randomized pid assignment, is of no help to people on systems that don't do that. Worse, it is subject to someone else pre-creating that file in a world-writable directory. Consider what hitting a named pipe instead of a regular file could do to your program. Now, accessing open(2) instead of fopen(3) and then using O_EXCL|O_CREAT would help, but does not suffice for all possible cases. Someone could have created a symbolic link from that file to elsewhere--or to nowhere. If the file did exist, then without the O_EXCL|O_CREAT check it would blindly overwrite it, as we so often saw in the execve() symlink/suid-script bug targetting important files like /etc/passwd with the link. But because a symlink can point to nowhere, the O_EXCL|O_CREAT test does not suffice: you might still end up making a "new" file, even one that you own, that's somewhere else than you think it is. Who knows what nastiness the nefarious crackers could possibly make of that? It isn't clear how best to address the issues raised in the original bug report. We *have* the technology now to take care of most if not all the problems, but inadequate user education remains the bottleneck. Quite bluntly, people make poor decisions, and I don't see how to stop this from happening: "The poor we shall have with us always." :-( [Back to Theo, from private mail] theo>In the last 6 months, I think that about 25 /tmp races in perl theo>programs have hit it. That number is going to go way up. theo> theo>A proactive approach to education could squish that now, instead theo>of reacting 3 years from now when everyone has done it wrong. Perl has two things you can do, neither of which is "automatic" in the sense that it's part of the normal Perl open() function. 1) You *can* use POSIX::tmpnam(), and this seems to behave reasonably well on most systems. However, you still have to know to do the right thing (the sysopen loop previous demonstrated), and you have no control over *where* the file is placed, which means it can very well land in a directory that's mode 0777 not 01777 (or be run on a system that doesn't honor sticky directories as owner-delete-only), just to mention one potential problem. 2) On the other hand, IO::File::new_tmpfile (which IM!HO should really be merely POSIX::tmpfile per expectations) dodges at least one bullet by making the proper open call for you. However, it gives you no access to the filename created; in fact, there often isn't one, due to unlink magic. This is less error prone, but still imperfect. We could adopt some of the "automatic tmpfile" strategies involving an undefined filename, but that's not the whole of the issue. To answer your original question, no, Perl does *not* currently support the mk*temp*(3) family of functions (meaning mktemp(3), mkstemp(3), mkstemps(3), and mkdtemp(3)). Apparently, there's something on CPAN called File::Temp, but it looks like it's not doing the right thing and just calling the real functions[FN#1]. These would allow you to supply a full template and thus place your temporary files in the directory that you want them in. We could add those trivially enough. But this still can do nothing to make people use them. In fact, nothing can. :-( I think that the easiest but perhaps not the most effective "solution" I can offer you is to add a notice in the Perl documentation on the open and sysopen functions, and also in the security section, that mentions the use of POSIX::tmpnam() and IO::File::new_tmpfile(). This might have more effect than adding the mk*temp*(3) family. But we could do that, too. There *is* one more thing we might be able to do, one that's a bit more proactive. We might augment Perl's open function so that it would emit a warning when run with warnings[FN#2] and/or that it would raise an exception when run in taint mode[FN#3]. This would mean checking for using the perilously simplistic open(FH, ">filename") style of open (which, being fopen(path, "w"), is really O_TRUNC|O_CREAT not O_EXCL|O_CREAT) on a file whose name looks like a tempfile. Now, just how could you ever tell that? Well, one could watch for "$$" at the end of the filename. Maybe this would be only in conjunction with a "/tmp/" component, but not necessarily rooted at slash, so that /usr/tmp and /var/tmp would show up, too. Perl could, upon finding this situation (however we define it), emit a warning or raise an exception to the effect that this operation is insecure because it's subject to a race condition, and suggest using one of POSIX::tmpnam() and IO::File::new_tmpfile() instead. I will forward this message to p5p, Perl's release and development team for further discussion. Interested and creative parties should feel free to *constructively* kibitz there. :-) --tom Footnotes: [0] XS is C-like glue code that gets massaged by Perl's xsubpp (the external subroutine preprocessor) into legit C, somewhat akin to Sun RPC xdr .x files. [1] The "real" mk*temp*(3) functions are described at http://www.openbsd.org/cgi-bin/man.cgi?query=mktemp&apropos=0&sektion=3&m [2] Warnings are enabled via the -w/-W command line switches, or else under the "use warnings" pragma. [3] "taintperl" security checks are enabled either optionally through the -T command line switch, or else automatically when running with differing real vs. effective user and/or group IDs. -- Tom Christiansen Perl Documentation Pumpking tchristat_private
This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 15:33:33 PDT