From: davidw@mel.dit.csiro.au (David Woodgate)
Subject: Re: File security
Organization: CSIRO DIT (Melb.)
Date: Wed, 8 Apr 92 17:26:59 GMT


Paul Lindner <lindner@boombox.micro.umn.edu> writes:

>Well, I just looked at the source.  (I didn't write this code, John
>Sellins did..)  Looks like you don't need the -c option to change the
>userid.  Thus the following is perfectly legal:
>
>gopherd -u ftp /usr/ftp 70
>
>This uses chroot *and* changes the user to ftp. 

Well, not quite. As it stands the setuid() is done before the
chroot() call, so when the attempt to chroot() happens it aborts.
Furthermore, when gopherd is called from inetd, the setuid
doesn't get called at all, so everything is read with root
permissions.

However, the fix seems to be quite easy. If you move the setuid()
call in gopherd.c to immediately after the chroot ( see diff at
end ) in do_command things seem to work OK ; things that are
readable only by root are not readable when the -u option is
used - either with gopherd as a daemon or as an inetd client.

I can't think of any reason why the setuid call can't be left
that late. Presumably no data-file accesses have been
performed up to that point since the environment is still being
established ( with chroot ).

But then I didn't write the code....


					David Woodgate
					davidw@mel.dit.csiro.au






*** /tmp/gopherd.c      Wed Mar 18 07:18:53 1992
--- gopherd.c   Thu Apr  9 03:12:19 1992
***************
*** 288,309 ****
       
       bzero((char *) &serv_addr, sizeof(serv_addr));
       serv_addr.sin_family             = AF_INET;
       serv_addr.sin_addr.s_addr        = htonl(INADDR_ANY);
       serv_addr.sin_port               = htons(GopherPort);
       
       if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) <0)
          err_dump("server: can't bind local address");
       
       /* have to setuid() here, in case we're using a privileged port */
!      if ( setuid(uid) != 0 )
!         err_sys("Cannot setuid(%d): ",uid);
  
       listen(sockfd, 5);
       
       for ( ; ; ) {
          /*
           * Wait for a connection from a client process.
           * This is an example of a concurrent server.
           */
          
          clilen = sizeof(cli_addr);
--- 288,309 ----
       
       bzero((char *) &serv_addr, sizeof(serv_addr));
       serv_addr.sin_family             = AF_INET;
       serv_addr.sin_addr.s_addr        = htonl(INADDR_ANY);
       serv_addr.sin_port               = htons(GopherPort);
       
       if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) <0)
          err_dump("server: can't bind local address");
       
       /* have to setuid() here, in case we're using a privileged port */
! /*     if ( setuid(uid) != 0 )
!         err_sys("Cannot setuid(%d): ",uid); */
  
       listen(sockfd, 5);
       
       for ( ; ; ) {
          /*
           * Wait for a connection from a client process.
           * This is an example of a concurrent server.
           */
          
          clilen = sizeof(cli_addr);
***************
*** 551,570 ****
--- 551,576 ----
  
       /** Change our root directory **/
       
       if ( dochroot ) {
          if (chroot(Data_Dir)) {
               LOGGopher(sockfd, "Data_Dir dissappeared!");
               exit(-1);
          }
          uchdir("/");  /* needed after chroot */
       }
+ 
+      /** At last do the setuid() **/
+ 
+      if ( setuid(uid) != 0 )
+           err_sys("Cannot setuid(%d): ",uid);
+ 
          
       length = readline(sockfd, inputline, MAXLINE); /** Get the line **/
  
       if (length <= 0)
          err_dump("getcommand: readline error");
       
       ZapCRLF(inputline);
  
       /*** With the funky new capability system we can just check the
            first letter, end decide what the object refers to. ***/
Newsgroups: alt.gopher