]> arthur.barton.de Git - ngircd-alex.git/commitdiff
startup: fork only once, never run with uid 0.
authorFlorian Westphal <fw@strlen.de>
Fri, 17 Jun 2005 18:22:45 +0000 (18:22 +0000)
committerFlorian Westphal <fw@strlen.de>
Fri, 17 Jun 2005 18:22:45 +0000 (18:22 +0000)
src/ngircd/ngircd.c

index 33dfdbc55600465ab9a9314e506cc05cc8250c3f..41c823bcdbc0edcbec6035a08cb289dad81411a4 100644 (file)
@@ -12,7 +12,7 @@
 
 #include "portab.h"
 
-static char UNUSED id[] = "$Id: ngircd.c,v 1.96 2005/06/01 21:52:18 alex Exp $";
+static char UNUSED id[] = "$Id: ngircd.c,v 1.97 2005/06/17 18:22:45 fw Exp $";
 
 /**
  * @file
@@ -69,6 +69,7 @@ LOCAL void Fill_Version PARAMS(( void ));
 
 LOCAL void Setup_FDStreams PARAMS(( void ));
 
+LOCAL bool NGIRCd_Init PARAMS(( bool ));
 
 /**
  * The main() function of ngIRCd.
@@ -81,10 +82,7 @@ LOCAL void Setup_FDStreams PARAMS(( void ));
 GLOBAL int
 main( int argc, const char *argv[] )
 {
-       struct passwd *pwd;
-       struct group *grp;
        bool ok, configtest = false;
-       long pid;
        int i;
        size_t n;
 
@@ -263,74 +261,9 @@ main( int argc, const char *argv[] )
                Log_Init( );
                Conf_Init( );
 
-               if( Conf_Chroot[0] )
-               {
-                       /* Chroot */
-                       if( chdir( Conf_Chroot ) != 0 ) Log( LOG_ERR, "Can't chdir() in ChrootDir (%s): %s", Conf_Chroot, strerror( errno ));
-
-                       if( chroot( Conf_Chroot ) != 0 ) Log( LOG_ERR, "Can't change root directory to \"%s\": %s", Conf_Chroot, strerror( errno ));
-                       else Log( LOG_INFO, "Changed root and working directory to \"%s\".", Conf_Chroot );
-               }
-
-               if( Conf_GID != 0 )
-               {
-                       /* Set new group ID */
-                       if( setgid( Conf_GID ) != 0 ) Log( LOG_ERR, "Can't change group ID to %u: %s", Conf_GID, strerror( errno ));
-               }
-               if( Conf_UID != 0 )
-               {
-                       /* Set new user ID */
-                       if( setuid( Conf_UID ) != 0 ) Log( LOG_ERR, "Can't change user ID to %u: %s", Conf_UID, strerror( errno ));
-               }
-
-               /* Normally a child process is forked which isn't any longer
-                * connected to ther controlling terminal. Use "--nodaemon"
-                * to disable this "daemon mode" (useful for debugging). */
-               if( ! NGIRCd_NoDaemon )
-               {
-                       /* fork child process */
-                       pid = (long)fork( );
-                       if( pid > 0 )
-                       {
-                               /* "Old" process: exit. */
-                               exit( 0 );
-                       }
-                       if( pid < 0 )
-                       {
-                               /* Error!? */
-                               fprintf( stderr, "%s: Can't fork: %s!\nFatal error, exiting now ...\n", PACKAGE_NAME, strerror( errno ));
-                               exit( 1 );
-                       }
-
-                       /* New child process */
-                       (void)setsid( );
-                       chdir( "/" );
-
-                       /* Detach stdin, stdout and stderr */
-                       Setup_FDStreams( );
-               }
-
-               /* Create PID file */
-               pid = (long) getpid( );
-               Pidfile_Create( pid );
-
-               /* Show user, group, and PID of the running daemon */
-               pwd = getpwuid( getuid( )); grp = getgrgid( getgid( ));
-               Log( LOG_INFO, "Running as user %s(%ld), group %s(%ld), with PID %ld.", pwd ? pwd->pw_name : "unknown", (long)getuid( ), grp ? grp->gr_name : "unknown", (long)getgid( ), pid);
-
-               /* Change working directory to home directory of the user
-                * we are running as (when not running chroot()'ed!) */
-               if( Conf_UID != 0 && ! Conf_Chroot[0] )
-               {
-                       struct passwd *pwd;
-
-                       pwd = getpwuid( Conf_UID );
-                       if( pwd != NULL )
-                       {
-                               if( chdir( pwd->pw_dir ) == 0 ) Log( LOG_DEBUG, "Changed working directory to \"%s\" ...", pwd->pw_dir );
-                               else Log( LOG_ERR, "Can't change working directory to \"%s\": %s", pwd->pw_dir, strerror( errno ));
-                       }
-                       else Log( LOG_ERR, "Can't get user informaton for UID %d!?", Conf_UID );
+               if (!NGIRCd_Init( NGIRCd_NoDaemon )) {
+                       Log(LOG_WARNING, "Fatal: Initialization failed");
+                       exit(1);
                }
 
                /* Initialize modules, part II: these functions are eventually
@@ -357,17 +290,17 @@ main( int argc, const char *argv[] )
                 * beim PASS-Befehl verwendete Syntax sowie die erweiterten Flags
                 * sind in doc/Protocol.txt beschrieben. */
 #ifdef IRCPLUS
-               sprintf( NGIRCd_ProtoID, "%s%s %s|%s:%s", PROTOVER, PROTOIRCPLUS, PACKAGE_NAME, PACKAGE_VERSION, IRCPLUSFLAGS );
+               snprintf( NGIRCd_ProtoID, sizeof NGIRCd_ProtoID, "%s%s %s|%s:%s", PROTOVER, PROTOIRCPLUS, PACKAGE_NAME, PACKAGE_VERSION, IRCPLUSFLAGS );
 #ifdef ZLIB
                strcat( NGIRCd_ProtoID, "Z" );
 #endif
                if( Conf_OperCanMode ) strcat( NGIRCd_ProtoID, "o" );
 #else
-               sprintf( NGIRCd_ProtoID, "%s%s %s|%s", PROTOVER, PROTOIRC, PACKAGE_NAME, PACKAGE_VERSION );
+               snprintf( NGIRCd_ProtoID, sizeof NGIRCd_ProtoID, "%s%s %s|%s", PROTOVER, PROTOIRC, PACKAGE_NAME, PACKAGE_VERSION );
 #endif
-               strcat( NGIRCd_ProtoID, " P" );
+               strlcat( NGIRCd_ProtoID, " P", sizeof NGIRCd_ProtoID );
 #ifdef ZLIB
-               strcat( NGIRCd_ProtoID, "Z" );
+               strlcat( NGIRCd_ProtoID, "Z", sizeof NGIRCd_ProtoID );
 #endif
                Log( LOG_DEBUG, "Protocol and server ID is \"%s\".", NGIRCd_ProtoID );
 
@@ -395,9 +328,8 @@ main( int argc, const char *argv[] )
                Channel_Exit( );
                Lists_Exit( );
                Log_Exit( );
-
-               Pidfile_Delete( );
        }
+       Pidfile_Delete( );
 
        return 0;
 } /* main */
@@ -415,42 +347,60 @@ Fill_Version( void )
        NGIRCd_VersionAddition[0] = '\0';
 
 #ifdef SYSLOG
-       strcpy( NGIRCd_VersionAddition, "SYSLOG" );
+       strlcpy( NGIRCd_VersionAddition, "SYSLOG", sizeof NGIRCd_VersionAddition );
 #endif
 #ifdef ZLIB
-       if( NGIRCd_VersionAddition[0] ) strcat( NGIRCd_VersionAddition, "+" );
-       strcat( NGIRCd_VersionAddition, "ZLIB" );
+       if( NGIRCd_VersionAddition[0] )
+               strlcat( NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition );
+
+       strlcat( NGIRCd_VersionAddition, "ZLIB", sizeof NGIRCd_VersionAddition );
 #endif
 #ifdef TCPWRAP
-       if( NGIRCd_VersionAddition[0] ) strcat( NGIRCd_VersionAddition, "+" );
-       strcat( NGIRCd_VersionAddition, "TCPWRAP" );
+       if( NGIRCd_VersionAddition[0] )
+                       strlcat( NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition );
+
+       strlcat( NGIRCd_VersionAddition, "TCPWRAP", sizeof NGIRCd_VersionAddition );
 #endif
 #ifdef RENDEZVOUS
-       if( NGIRCd_VersionAddition[0] ) strcat( NGIRCd_VersionAddition, "+" );
-       strcat( NGIRCd_VersionAddition, "RENDEZVOUS" );
+       if( NGIRCd_VersionAddition[0] )
+               strlcat( NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition );
+
+       strlcat( NGIRCd_VersionAddition, "RENDEZVOUS", sizeof NGIRCd_VersionAddition );
 #endif
 #ifdef IDENTAUTH
-       if( NGIRCd_VersionAddition[0] ) strcat( NGIRCd_VersionAddition, "+" );
-       strcat( NGIRCd_VersionAddition, "IDENT" );
+       if( NGIRCd_VersionAddition[0] )
+               strlcat( NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition );
+
+       strlcat( NGIRCd_VersionAddition, "IDENT", sizeof NGIRCd_VersionAddition );
 #endif
 #ifdef DEBUG
-       if( NGIRCd_VersionAddition[0] ) strcat( NGIRCd_VersionAddition, "+" );
-       strcat( NGIRCd_VersionAddition, "DEBUG" );
+       if( NGIRCd_VersionAddition[0] )
+               strlcat( NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition );
+
+       strlcat( NGIRCd_VersionAddition, "DEBUG", sizeof NGIRCd_VersionAddition );
 #endif
 #ifdef SNIFFER
-       if( NGIRCd_VersionAddition[0] ) strcat( NGIRCd_VersionAddition, "+" );
-       strcat( NGIRCd_VersionAddition, "SNIFFER" );
+       if( NGIRCd_VersionAddition[0] )
+               strlcat( NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition );
+
+       strlcat( NGIRCd_VersionAddition, "SNIFFER", sizeof NGIRCd_VersionAddition );
 #endif
 #ifdef STRICT_RFC
-       if( NGIRCd_VersionAddition[0] ) strcat( NGIRCd_VersionAddition, "+" );
-       strcat( NGIRCd_VersionAddition, "RFC" );
+       if( NGIRCd_VersionAddition[0] )
+               strlcat( NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition );
+
+       strlcat( NGIRCd_VersionAddition, "RFC", sizeof NGIRCd_VersionAddition );
 #endif
 #ifdef IRCPLUS
-       if( NGIRCd_VersionAddition[0] ) strcat( NGIRCd_VersionAddition, "+" );
-       strcat( NGIRCd_VersionAddition, "IRCPLUS" );
+       if( NGIRCd_VersionAddition[0] )
+               strlcat( NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition );
+
+       strlcat( NGIRCd_VersionAddition, "IRCPLUS", sizeof NGIRCd_VersionAddition );
 #endif
 
-       if( NGIRCd_VersionAddition[0] ) strlcat( NGIRCd_VersionAddition, "-", sizeof( NGIRCd_VersionAddition ));
+       if( NGIRCd_VersionAddition[0] )
+               strlcat( NGIRCd_VersionAddition, "-", sizeof( NGIRCd_VersionAddition ));
+
        strlcat( NGIRCd_VersionAddition, TARGET_CPU, sizeof( NGIRCd_VersionAddition ));
        strlcat( NGIRCd_VersionAddition, "/", sizeof( NGIRCd_VersionAddition ));
        strlcat( NGIRCd_VersionAddition, TARGET_VENDOR, sizeof( NGIRCd_VersionAddition ));
@@ -480,7 +430,7 @@ NGIRCd_Rehash( void )
        Conn_ExitListeners( );
 
        /* Remember old server name */
-       strcpy( old_name, Conf_ServerName );
+       strlcpy( old_name, Conf_ServerName, sizeof old_name );
 
        /* Re-read configuration ... */
        Conf_Rehash( );
@@ -651,7 +601,9 @@ Pidfile_Delete( void )
 LOCAL void
 Pidfile_Create( long pid )
 {
-       FILE *pidf;
+       int pidfd;
+       char pidbuf[64];
+       int len;
 
        /* Pidfile configured? */
        if( ! Conf_PidFile[0] ) return;
@@ -660,18 +612,22 @@ Pidfile_Create( long pid )
        Log( LOG_DEBUG, "Creating PID file (%s) ...", Conf_PidFile );
 #endif
 
-       pidf = fopen( Conf_PidFile, "w" );
-
-       if( ! pidf )
-       {
+       pidfd = open( Conf_PidFile, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+       if ( pidfd < 0 ) {
                Log( LOG_ERR, "Error writing PID file (%s): %s", Conf_PidFile, strerror( errno ));
                return;
        }
 
-       if( fprintf( pidf, "%ld\n", pid ) < 0 )
+       len = snprintf( pidbuf, sizeof pidbuf, "%ld\n", pid );
+       if (len < 0|| len < (int)sizeof pid) {
+               Log( LOG_ERR, "Error converting pid");
+               return;
+       }
+       
+       if( write( pidfd, pidbuf, len) != len)
                Log( LOG_ERR, "Can't write PID file (%s): %s", Conf_PidFile, strerror( errno ));
 
-       if( fclose(pidf) != 0 )
+       if( close(pidfd) != 0 )
                Log( LOG_ERR, "Error closing PID file (%s): %s", Conf_PidFile, strerror( errno ));
 } /* Pidfile_Create */
 
@@ -688,10 +644,13 @@ Setup_FDStreams( void )
         * we are most probably chrooted already and the server has been
         * restarted. So we simply don't try to redirect stdXXX ... */
        fd = open( "/dev/null", O_RDWR );
-       if ( fd < 0 ) return;
+       if ( fd < 0 ) {
+               Log(LOG_WARNING, "Could not open /dev/null: %s", strerror(errno));      
+               return;
+       } 
 
-       /* Close "old" stdin/out/err descriptors */
-       close( 0 ); close( 1 ); close( 2 );
+       fflush(stdout);
+       fflush(stderr);
 
        /* Create new stdin(0), stdout(1) and stderr(2) descriptors */
        dup2( fd, 0 ); dup2( fd, 1 ); dup2( fd, 2 );
@@ -701,4 +660,146 @@ Setup_FDStreams( void )
 } /* Setup_FDStreams */
 
 
+LOCAL bool
+NGIRCd_getNobodyID(unsigned int *uid, unsigned int *gid )
+{
+       struct passwd *pwd;
+
+       pwd = getpwnam("nobody");
+       if (!pwd) return false;
+
+       if ( !pwd->pw_uid || !pwd->pw_gid)
+               return false;
+
+       *uid = pwd->pw_uid;     
+       *gid = pwd->pw_gid;
+       endpwent();
+
+       return true;    
+}
+
+
+LOCAL bool
+NGIRCd_Init( bool NGIRCd_NoDaemon ) 
+{
+       static bool initialized;
+       bool chrooted = false;
+       struct passwd *pwd;
+       struct group *grp;
+       int real_errno;
+       long pid;
+
+       if (initialized)
+               return true;
+
+       if( Conf_Chroot[0] ) {
+               if( chdir( Conf_Chroot ) != 0 ) {
+                       Log( LOG_ERR, "Can't chdir() in ChrootDir (%s): %s", Conf_Chroot, strerror( errno ));
+                       return false;
+               }
+
+               if( chroot( Conf_Chroot ) != 0 ) {
+                       if (errno != EPERM) {
+                               Log( LOG_ERR, "Can't change root directory to \"%s\": %s",
+                                                               Conf_Chroot, strerror( errno ));
+
+                               return false;
+                       }
+               } else {
+                       chrooted = true;
+                       Log( LOG_INFO, "Changed root and working directory to \"%s\".", Conf_Chroot );
+               }
+       }
+
+       if ( Conf_UID == 0 ) {
+               Log( LOG_INFO, "Conf_UID must not be 0, switching to user nobody", Conf_UID );
+
+               if (!NGIRCd_getNobodyID(&Conf_UID, &Conf_GID )) {
+                       Log( LOG_WARNING, "Could not get uid/gid of user nobody: %s",
+                                       errno ? strerror(errno) : "not found" );
+                       return false;
+               }
+       }
+
+       if( setgid( Conf_GID ) != 0 ) {
+               real_errno = errno;
+               Log( LOG_ERR, "Can't change group ID to %u: %s", Conf_GID, strerror( errno ));
+               if (real_errno != EPERM) 
+                       return false;
+       }
+
+       if( setuid( Conf_UID ) != 0 ) {
+               real_errno = errno;
+               Log( LOG_ERR, "Can't change user ID to %u: %s", Conf_UID, strerror( errno ));
+               if (real_errno != EPERM) 
+                       return false;
+       }
+
+       initialized = true;
+
+       /* Normally a child process is forked which isn't any longer
+        * connected to ther controlling terminal. Use "--nodaemon"
+        * to disable this "daemon mode" (useful for debugging). */
+       if ( ! NGIRCd_NoDaemon ) {
+               initialized = true;
+               pid = (long)fork( );
+               if( pid > 0 ) {
+                       /* "Old" process: exit. */
+                       exit( 0 );
+               }
+               if( pid < 0 ) {
+                       /* Error!? */
+                       fprintf( stderr, "%s: Can't fork: %s!\nFatal error, exiting now ...\n",
+                                                               PACKAGE_NAME, strerror( errno ));
+                       exit( 1 );
+               }
+
+               /* New child process */
+               (void)setsid( );
+               chdir( "/" );
+
+               /* Detach stdin, stdout and stderr */
+               Setup_FDStreams( );
+       }
+       pid = getpid();
+
+       Pidfile_Create( pid );
+
+       /* check uid we are running as, can be different from values configured (e.g. if we were already
+       started with a uid > 0 */
+       Conf_UID = getuid();
+       Conf_GID = getgid();
+
+       assert( Conf_GID > 0);
+       assert( Conf_UID > 0);
+
+       pwd = getpwuid( Conf_UID );
+       grp = getgrgid( Conf_GID );
+       Log( LOG_INFO, "Running as user %s(%ld), group %s(%ld), with PID %ld.",
+                               pwd ? pwd->pw_name : "unknown", Conf_UID,
+                               grp ? grp->gr_name : "unknown", Conf_GID, pid);
+
+       if ( chrooted ) {
+               Log( LOG_INFO, "Running chrooted, chrootdir \"%s\".",  Conf_Chroot );
+               return true;
+       } else {
+               Log( LOG_INFO, "Not running chrooted." );
+       }
+
+       /* Change working directory to home directory of the user
+        * we are running as (when not running chroot()'ed!) */
+       
+       if ( pwd ) {
+               if( chdir( pwd->pw_dir ) == 0 ) 
+                       Log( LOG_DEBUG, "Changed working directory to \"%s\" ...", pwd->pw_dir );
+               else 
+                       Log( LOG_ERR, "Can't change working directory to \"%s\": %s",
+                                                       pwd->pw_dir, strerror( errno ));
+       } else {
+               Log( LOG_ERR, "Can't get user informaton for UID %d!?", Conf_UID );
+       }
+
+return true;
+}
+
 /* -eof- */