From jkuroda@CS.Berkeley.EDU  Sun Aug  6 11:58:08 2000
Return-Path: <jkuroda@CS.Berkeley.EDU>
Received: from sushi.CS.Berkeley.EDU (sushi.CS.Berkeley.EDU [128.32.46.243])
	by hub.freebsd.org (Postfix) with ESMTP id DE9AB37B804
	for <FreeBSD-gnats-submit@freebsd.org>; Sun,  6 Aug 2000 11:58:07 -0700 (PDT)
	(envelope-from jkuroda@CS.Berkeley.EDU)
Received: (from jkuroda@localhost)
	by sushi.CS.Berkeley.EDU (8.9.3/8.9.3) id LAA24180;
	Sun, 6 Aug 2000 11:58:06 -0700 (PDT)
	(envelope-from jkuroda)
Message-Id: <200008061858.LAA24180@sushi.CS.Berkeley.EDU>
Date: Sun, 6 Aug 2000 11:58:06 -0700 (PDT)
From: jkuroda@CS.Berkeley.EDU
Reply-To: jkuroda@CS.Berkeley.EDU
To: FreeBSD-gnats-submit@freebsd.org
Subject: restore(8) uses non-varying filenames for /tmp files when restoring given filesystem repeatedly
X-Send-Pr-Version: 3.2

>Number:         20445
>Category:       bin
>Synopsis:       restore(8) uses non-varying filenames for /tmp files when restoring given filesystem repeatedly
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    sheldonh
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun Aug 06 12:00:04 PDT 2000
>Closed-Date:    Fri Aug 11 03:44:32 PDT 2000
>Last-Modified:  Fri Aug 11 03:45:56 PDT 2000
>Originator:     Jon Masami Kuroda
>Release:        FreeBSD 3.3-STABLE i386
>Organization:
University of California at Berkeley, EECS
>Environment:

	"problem" discovered while cloning filesystems via restore(8) with
	the -R or -r flags to multiple disks from single dumpfile created
	via dump(8).

	# for newroot in 1 2 3 4 5; do
	cd /mnt/altroot/${newroot}/ && restore rf /scratch/root.dump &
	done

>Description:

	restore(8) uses the dumpdate of the filesystem being restored to
	create the filenames for the files in /tmp used to store the names
	of directories -- /tmp/rst* -- and of their ownership/mode/time stamps
	-- /tmp/rstmode*.  The first file, dirfile is used to store the list
	of dirs to be created before files are restored into them, the second,
	modefile, to restore the ownership,mode, and time stamps on those
	dirs after file restores are finished.  The dumpdate is the number
	of seconds since the Unix Epoch (tm) till the time the filesystem
	in question was dumped. 

	example:

dirs.c:
  1 /*
     *      Extract directory contents, building up a directory structure
     *      on disk for extraction by name.
     *      If genmode is requested, save mode, owner, and times for all
  5  *      directories on the tape.
     */
    void
    extractdirs(genmode)
            int genmode;
 10 {
            register int i;
            register struct dinode *ip;
            struct inotab *itp;
            struct direct nulldir;
 15     int fd;

        vprintf(stdout, "Extract directories from tape\n");

        (void) sprintf(dirfile, "%srstdir%d", _PATH_TMP, dumpdate);
 20     if (command != 'r' && command != 'R') {
                (void *) strcat(dirfile, "-XXXXXX");
                fd = mkstemp(dirfile);
        } else  
                fd = open(dirfile, O_RDWR|O_CREAT|O_EXCL, 0666);
 25     if (fd == -1 || (df = fdopen(fd, "w")) == NULL) {
                if (fd != -1)
                        close(fd);
                warn("%s - cannot create directory temporary\nfopen", dirfile);
                done(1);
 30     }
        if (genmode != 0) {
                (void) sprintf(modefile, "%srstmode%d", _PATH_TMP, dumpdate);
                if (command != 'r' && command != 'R') {
                        (void *) strcat(modefile, "-XXXXXX");
 35                     fd = mkstemp(modefile);
                } else  
                        fd = open(modefile, O_RDWR|O_CREAT|O_EXCL, 0666);
                if (fd == -1 || (mf = fdopen(fd, "w")) == NULL) {
                        if (fd != -1)
 40                             close(fd);
                        warn("%s - cannot create modefile\nfopen", modefile);
                        done(1);
                }
 44     }

    
	The dumpdate made of the filenames prior to their creation in lines
	19 and 32.  Note that after that, a call to mkstemp(3) is made but
	*only* if neither the "R" or "r" flags are provided to restore(8).  
	Else, the file is created via open(2) with mode 0666, and umask 077
	obtained from main.c:93. 

	This introduces one very annoying problem.

	The name of restore(8)'s temporary files when restore(8) is given
	the "R" or "r" flags are very predictable -- in fact, they never 
	change.  While the use of the O_CREAT and O_EXCL flags to open(2)
	should prevent a non-privileged user from exploiting an otherwise
	possible race condition (right?), the problem I found is that this
	prevents one from using ``restore rf'' or ``restore Rf'' to clone a
	filesystem to several blank filesystems in parallel from a single
	dumpfile created earlier by dump(8) as each restore will attempt
	to create the same filenames in /tmp and only the first one will
	succeed.  (given the example shown above)

	The rest will fail out like so:

	restore: /tmp/rstdir965378984 - cannot create directory temporary
    	fopen: File exists
	restore: /tmp/rstmode965378984 - cannot create modefile
	fopen: File exists

	and so prevent one from restoring from one dumpfile to multiple
	filesystems (such as in FS cloning) simultaneously.

	It seems that mkstemp(3) was added in revision 1.6.2.1, but mkstemp(3)
	was used only in certain places, presumably only those places that
	would introduce nasty race conditions. 

>How-To-Repeat:

	* given a number of newfs'd fs's mounted in /mnt/altroot
	* and a dump of an example root fs to be cloned in /scratch/...

        # for newroot in 1 2 3 4 5; do
        cd /mnt/altroot/${newroot}/ && restore rf /scratch/root.dump &
        done            
	
	watch the 2nd restore and onward complain about not being to
	open temporary files correctly.

>Fix:
	
	Use mkstemp(3) instead of open(2) by itself in extractdirs(),
	when creating "dirfile" and "modefile".  Do not recreate the
	value of "modefile" from "dumpdate" in setdirmodes(), and instead
	rely on mkstemp to modify the value of "modefile", back in			extractdirs(), which is in an array of "static char"'s.  This
	can easily be accomplished by removing the special casing for
	the "R" and "r" flags and letting these cases reap the benefits
	of mkstemp(3) as in other case such as when the "x" flag is used.
    
	I scoured the code and the CVS logs and couldn't find a reason
	for why mkstemp shouldn't be used here.  I think it makes sense
	to on its own merit and also because it makes the code slightly
	simpler for not having to special case "R" and "r".  But, I 
	could be mistaken in this and my patch below could be terribly
	naive, so take it all with a grain of salt and the 10 coke's I 
	had prior to writing this up.

	Thanks to Mike Smith for helping me remember that I had been
	meaning to submit a PR for this for sometime now.

	A first pass at patch on ${SRC}/sbin/restore/dirs.c
	This patch tested on 3.4-STABLE and 4.1-RELEASE.

*** dirs.c	Sun Aug  6 11:46:31 2000
--- dirs.c.moremkstemp	Sun Aug  6 11:46:22 2000
***************
*** 148,158 ****
  
  	vprintf(stdout, "Extract directories from tape\n");
  	(void) sprintf(dirfile, "%srstdir%d", _PATH_TMP, dumpdate);
! 	if (command != 'r' && command != 'R') {
  		(void *) strcat(dirfile, "-XXXXXX");
  		fd = mkstemp(dirfile);
! 	} else
! 		fd = open(dirfile, O_RDWR|O_CREAT|O_EXCL, 0666);
  	if (fd == -1 || (df = fdopen(fd, "w")) == NULL) {
  		if (fd != -1)
  			close(fd);
--- 148,158 ----
  
  	vprintf(stdout, "Extract directories from tape\n");
  	(void) sprintf(dirfile, "%srstdir%d", _PATH_TMP, dumpdate);
! 	/* if (command != 'r' && command != 'R') { */
  		(void *) strcat(dirfile, "-XXXXXX");
  		fd = mkstemp(dirfile);
! 	/*} else
! 		fd = open(dirfile, O_RDWR|O_CREAT|O_EXCL, 0666); */
  	if (fd == -1 || (df = fdopen(fd, "w")) == NULL) {
  		if (fd != -1)
  			close(fd);
***************
*** 161,171 ****
  	}
  	if (genmode != 0) {
  		(void) sprintf(modefile, "%srstmode%d", _PATH_TMP, dumpdate);
! 		if (command != 'r' && command != 'R') {
  			(void *) strcat(modefile, "-XXXXXX");
  			fd = mkstemp(modefile);
! 		} else
! 			fd = open(modefile, O_RDWR|O_CREAT|O_EXCL, 0666);
  		if (fd == -1 || (mf = fdopen(fd, "w")) == NULL) {
  			if (fd != -1)
  				close(fd);
--- 161,171 ----
  	}
  	if (genmode != 0) {
  		(void) sprintf(modefile, "%srstmode%d", _PATH_TMP, dumpdate);
! 		/* if (command != 'r' && command != 'R') { */
  			(void *) strcat(modefile, "-XXXXXX");
  			fd = mkstemp(modefile);
! 		/* } else 
! 			fd = open(modefile, O_RDWR|O_CREAT|O_EXCL, 0666); */
  		if (fd == -1 || (mf = fdopen(fd, "w")) == NULL) {
  			if (fd != -1)
  				close(fd);
***************
*** 594,601 ****
  	char *cp;
  
  	vprintf(stdout, "Set directory mode, owner, and times.\n");
! 	if (command == 'r' || command == 'R')
! 		(void) sprintf(modefile, "%srstmode%d", _PATH_TMP, dumpdate);
  	if (modefile[0] == '#') {
  		panic("modefile not defined\n");
  		fprintf(stderr, "directory mode, owner, and times not set\n");
--- 594,601 ----
  	char *cp;
  
  	vprintf(stdout, "Set directory mode, owner, and times.\n");
! 	/* if (command == 'r' || command == 'R')
! 		(void) sprintf(modefile, "%srstmode%d", _PATH_TMP, dumpdate); */
  	if (modefile[0] == '#') {
  		panic("modefile not defined\n");
  		fprintf(stderr, "directory mode, owner, and times not set\n");

>Release-Note:
>Audit-Trail:
State-Changed-From-To: open->feedback 
State-Changed-By: sheldonh 
State-Changed-When: Mon Aug 7 05:33:42 PDT 2000 
State-Changed-Why:  
Does this problem exist in OpenBSD as well?  I know that we  
stole some security fixes from them a while back, and wonder 
if perhaps they haven't sorted this out since then. 


http://www.freebsd.org/cgi/query-pr.cgi?pr=20445 

From: Sheldon Hearn <sheldonh@uunet.co.za>
To: Jon Masami Kuroda <jkuroda@EECS.Berkeley.EDU>
Cc: freebsd-gnats-submit@FreeBSD.org
Subject: Re: bin/20445: restore(8) -r and -R don't use mktemp(3)
Date: Tue, 08 Aug 2000 12:16:39 +0200

 On Mon, 07 Aug 2000 09:37:22 MST, Jon Masami Kuroda wrote:
 
 > This "bug" should not have any security impact if I understand open(2)
 > and the "O_EXCL" flag correctly.  It is, so far, just a productivity
 > annoyance.
 
 While that's certainly the way it looks to me, I'm very uncomfortable
 with proceding without finding out _why_ OpenBSD's rev 1.3 was required.
 Essentially, your patch proposes that we revert NetBSD's rev 1.3.  The
 CVS log for that delta implies that the patch you propose will somehow
 break the -r and -R options.
 
 I'm not sure whether <lukem@supp.cpr.itg.telecom.com.au> is still a
 deliverable address.  Perhaps you could chat to Theo and ask him why the
 revision was introduced in the first place?
 
 Ciao,
 Sheldon.
 

From: Jon Masami Kuroda <jkuroda@eecs.berkeley.edu>
To: Sheldon Hearn <sheldonh@uunet.co.za>
Cc: Jon Masami Kuroda <jkuroda@eecs.berkeley.edu>,
	freebsd-gnats-submit@freebsd.org
Subject: Re: bin/20445: restore(8) -r and -R don't use mktemp(3) 
Date: Tue, 08 Aug 2000 08:53:56 -0700

 In looking at the OpeBSD 1.1->1.2 diff and the 1.2->1.3 diffs for dirs.c,
 it looks like that mkstemp(3) was applied in 1.1->1.2 in extractdirs(), but
 that setdirmodes() still recreated the value of modefile on its own instead
 of using the value of modefile created by mkstemp(3) back in extractdirs().
 
 OpenBSD, src/sbin/dirs.c, r1.2
 setdirmodes(flags)
         int flags;
 {
         FILE *mf;
         struct modeinfo node;
         struct entry *ep;
         char *cp;
 
         vprintf(stdout, "Set directory mode, owner, and times.\n");
         (void) sprintf(modefile, "%s/rstmode%d", _PATH_TMP, dumpdate);
         mf = fopen(modefile, "r");
         if (mf == NULL) {
                 fprintf(stderr, "fopen: %s\n", strerror(errno));
                 fprintf(stderr, "cannot open mode file %s\n", modefile);
                 fprintf(stderr, "directory mode, owner, and times not set\n");
                 return;
         }
 ...
 
 Since extractdirs() in 1.2 used mkstemp(3) to create unique filenames, 
 setdirmodes() would fail on opening the file since it expected a filename
 of the form "/tmp/rstmodeDECIMAL", instead of "/tmp/rstmodeDECIMAL-MKSTEMP"
 which extractdirs created via mkstemp(3).
 
 the 1.2->1.3 patch fixed this by having restore use "/tmp/rstmodeDECIMAL"
 for the "R" and "r" cases in both extractdirs() and setdirmodes() so that
 they were using the same filename.  It is not clear to me why setdirmodes()
 was not modified to just rely on the mkstemp(3) value of modefile used
 back in extractdirs() instead of the special casing of "R" and "r" or why
 the value of dirfile is not created via mkstemp(3) in all cases for that 
 matter.  I guess a better question should be "why did setdirmodes() ever
 try to recreate the value of modefile on its own?"  None of the other
 functions that rely on dirfile, for instance, ever try to recreate that
 value from scratch, and instead rely on extractdirs() to do its thing
 to the value dirfile.
 
 In any case, it seems like a good idea to talk to the author of the original
 patch for this since we all seem to have a bunch of questions about this now
 
 Cheers,
 Jon
 
 Sheldon Hearn <sheldonh@uunet.co.za> writes:
 : On Mon, 07 Aug 2000 09:37:22 MST, Jon Masami Kuroda wrote:
 : 
 : > This "bug" should not have any security impact if I understand open(2)
 : > and the "O_EXCL" flag correctly.  It is, so far, just a productivity
 : > annoyance.
 : 
 : While that's certainly the way it looks to me, I'm very uncomfortable
 : with proceding without finding out _why_ OpenBSD's rev 1.3 was required.
 : Essentially, your patch proposes that we revert NetBSD's rev 1.3.  The
 : CVS log for that delta implies that the patch you propose will somehow
 : break the -r and -R options.
 : 
 : I'm not sure whether <lukem@supp.cpr.itg.telecom.com.au> is still a
 : deliverable address.  Perhaps you could chat to Theo and ask him why the
 : revision was introduced in the first place?
 

From: Jon Masami Kuroda <jkuroda@eecs.berkeley.edu>
To: Sheldon Hearn <sheldonh@uunet.co.za>
Cc: Jon Masami Kuroda <jkuroda@eecs.berkeley.edu>,
	freebsd-gnats-submit@freebsd.org
Subject: Re: bin/20445: restore(8) -r and -R don't use mktemp(3) 
Date: Tue, 08 Aug 2000 11:01:10 -0700

 Aha, I found out why and I didnt even have to talk to Luke yet! =)
 
 From the NetBSD restore(8) manpage:
 
      The temporary files /tmp/rstdir* and /tmp/rstmode* are generated with a
      unique name based on the date of the dump and the process ID (see
      mktemp(3)),  except for when -r or -R is used.  Because -R allows you to
      restart a -r operation that may have been interrupted, the temporary
      files should be the same across different processes.  In all other cases,
      the files are unique because it is possible to have two different dumps
      started at the same time, and separate operations shouldn't conflict with
      each other.
 
 I found this while going through the NetBSD cvsweb stuff where I found
 revision 1.20 of dirs.c and traced my way to the man page.
 
     http://cvsweb.netbsd.org/cgi-bin/cvsweb.cgi/basesrc/sbin/restore/dirs.c
 
 Now this makes sense.  It's still annoying, but it makes sense.  Perhaps
 restore(8) needs a flag to say "screw this, use mkstemp!" if the operator
 so desires.  Or a way to save the names of the files for later use.
 
 --Jon
 
 Sheldon Hearn wrote on Tue, 08 Aug 2000 12:16:39 +0200:
 : 
 : On Mon, 07 Aug 2000 09:37:22 MST, Jon Masami Kuroda wrote:
 : 
 : > This "bug" should not have any security impact if I understand open(2)
 : > and the "O_EXCL" flag correctly.  It is, so far, just a productivity
 : > annoyance.
 : 
 : While that's certainly the way it looks to me, I'm very uncomfortable
 : with proceding without finding out _why_ OpenBSD's rev 1.3 was required.
 : Essentially, your patch proposes that we revert NetBSD's rev 1.3.  The
 : CVS log for that delta implies that the patch you propose will somehow
 : break the -r and -R options.
 : 
 : I'm not sure whether <lukem@supp.cpr.itg.telecom.com.au> is still a
 : deliverable address.  Perhaps you could chat to Theo and ask him why the
 : revision was introduced in the first place?
 

From: Jon Masami Kuroda <jkuroda@eecs.berkeley.edu>
To: Jon Masami Kuroda <jkuroda@eecs.berkeley.edu>
Cc: Sheldon Hearn <sheldonh@uunet.co.za>,
	freebsd-gnats-submit@freebsd.org
Subject: Re: bin/20445: restore(8) -r and -R don't use mktemp(3) 
Date: Tue, 08 Aug 2000 11:15:34 -0700

 Someone in the office asked why I didnt just use "restore xf dumpfile"
 and it seems the question and answer is relevant enough to put in here
 as explanation for others:
 
 from FreeBSD restore(8)
 
      If a backup was made using more than one tape volume, restore will notify
      the user when it is time to mount the next volume.  If the -x or -i flag
      has been specified, restore will also ask which volume the user wishes to
      mount.  The fastest way to extract a few files is to start with the last
      volume, and work towards the first volume.
 
 For the purpose for which I used restore, this is annoying since I dont
 really want to have to type "1" or anything interactively to restore
 when I have a dozen or more fs's being cloned.  Now granted I could use
 expect as an interactive wrapper, but I should be able to do this all
 with restore on it's own.  Or maybe I'm just ultra-lazy.
 
 --Jon
 

From: Sheldon Hearn <sheldonh@uunet.co.za>
To: Jon Masami Kuroda <jkuroda@eecs.berkeley.edu>
Cc: freebsd-gnats-submit@freebsd.org
Subject: Re: bin/20445: restore(8) -r and -R don't use mktemp(3) 
Date: Thu, 10 Aug 2000 09:32:37 +0200

 On Tue, 08 Aug 2000 11:01:10 MST, Jon Masami Kuroda wrote:
 
 > Now this makes sense.  It's still annoying, but it makes sense.  Perhaps
 > restore(8) needs a flag to say "screw this, use mkstemp!" if the operator
 > so desires.  Or a way to save the names of the files for later use.
 
 I can't think of an elegant solution.  I think we're approaching the
 limits of the software, beyond which we'd stub our toes trying to make
 it all things to all men.
 
 I'd like to close the PR, but if you have any great ideas that you think
 we won't stub our toes on, I'll leave it open.
 
 What say you?
 
 Ciao,
 Sheldon.
 

From: Jon Masami Kuroda <jkuroda@eecs.berkeley.edu>
To: Sheldon Hearn <sheldonh@uunet.co.za>
Cc: Jon Masami Kuroda <jkuroda@eecs.berkeley.edu>,
	freebsd-gnats-submit@freebsd.org
Subject: Re: bin/20445: restore(8) -r and -R don't use mktemp(3) 
Date: Thu, 10 Aug 2000 11:30:14 -0700

 I think the only thing that could be done is to add that bit from the NetBSD
 restore(8) manpage to the FreeBSD one so future young bright file system
 cloners wont repeat my steps.
 
 BUGS
      restore can get confused when doing incremental restores from dumps that
      were made on active file systems.
 
      A level zero dump must be done after a full restore.  Because restore
      runs in user code, it has no control over inode allocation; thus a full
      dump must be done to get a new set of directories reflecting the new in-
      ode numbering, even though the content of the files is unchanged.
 
 >    The temporary files /tmp/rstdir* and /tmp/rstmode* are generated with a
 >    unique name based on the date of the dump and the process ID (see
 >    mktemp(3)), except for when -r or -R is used.  Because -R allows you to
 >    restart a -r operation that may have been interrupted, the temporary
 >    files should be the same across different processes.  In all other cases,
 >    the files are unique because it is possible to have two different dumps
 >    started at the same time, and separate operations shouldn't conflict with
 >    each other.
 
 I would add one more note to that manpage change:
 
      This will prevent cloning of filesystems from a single dumpfile to
      multiple filesystems at one time via "restore -r" or "restore -R".
 
 
 Given time, I could see implementing a -c[lone] flag or the NetBSD TMPDIR
 environ(7) behaviour, but I think that is well beyond the scope of this PR.
 
 ENVIRONMENT
      If the following environment variable exists it will be utilized by
      restore:
      TMPDIR  The directory given in TMPDIR will be used instead of /tmp to
              store temporary files.  Refer to environ(7) for more information.
 
 Thanks for your time on this one, lets close it.
 
 Cheers,
 Jon
 
State-Changed-From-To: feedback->closed 
State-Changed-By: sheldonh 
State-Changed-When: Fri Aug 11 03:44:32 PDT 2000 
State-Changed-Why:  
AProposed change committed to HEAD as rev 1.22 and merged 
onto the RELENG_4 and RELENG_3 branches. 


Responsible-Changed-From-To: freebsd-bugs->sheldonh 
Responsible-Changed-By: sheldonh 
Responsible-Changed-When: Fri Aug 11 03:44:32 PDT 2000 
Responsible-Changed-Why:  
He did it. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=20445 
>Unformatted:
