Commit Diff


commit - 94dd7fa7d6239dabaab243e334399e351c4fdc1f
commit + bc2dac3c74996fc1164e7b5d6dbf5ae79f6dbb59
blob - 33dfdbc55600465ab9a9314e506cc05cc8250c3f
blob + 41c823bcdbc0edcbec6035a08cb289dad81411a4
--- src/ngircd/ngircd.c
+++ src/ngircd/ngircd.c
@@ -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;
 
@@ -262,77 +260,12 @@ main( int argc, const char *argv[] )
 		/* Initialize modules, part I */
 		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( );
+		if (!NGIRCd_Init( NGIRCd_NoDaemon )) {
+			Log(LOG_WARNING, "Fatal: Initialization failed");
+			exit(1);
 		}
 
-		/* 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 );
-		}
-
 		/* Initialize modules, part II: these functions are eventually
 		 * called with already dropped privileges ... */
 		Resolve_Init( );
@@ -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- */