From 7281b8dd4d917f8b7f7c36460b78b686427ce973 Mon Sep 17 00:00:00 2001 From: Alexander Barton Date: Fri, 7 May 2004 11:19:20 +0000 Subject: [PATCH] New "chroot" feature (from Benjamin Pineau), introducing new configuration variables "ChrootDir" and "MotdPhrase". --- AUTHORS | 3 +- ChangeLog | 7 ++++- INSTALL | 7 ++++- NEWS | 7 ++++- README | 5 +-- doc/sample-ngircd.conf | 13 +++++++- man/ngircd.conf.5 | 18 ++++++++++- src/ngircd/conf.c | 20 +++++++++++- src/ngircd/conf.h | 8 ++++- src/ngircd/defines.h | 4 ++- src/ngircd/irc-info.c | 11 +++++-- src/ngircd/log.c | 11 +++++-- src/ngircd/ngircd.c | 71 ++++++++++++++++++++++++------------------ 13 files changed, 139 insertions(+), 46 deletions(-) diff --git a/AUTHORS b/AUTHORS index 929679a4..6cca6cb4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -19,6 +19,7 @@ Contributors ~~~~~~~~~~~~ Goetz Hoffart, (goetz) Ilja Osthoff, (ilja) +Benjamin Pineau, Sean Reifschneider, @@ -30,4 +31,4 @@ Andrew Tridgell & Martin Pool: strl{cpy|cat}()-functions -- -$Id: AUTHORS,v 1.8 2004/01/26 02:23:54 alex Exp $ +$Id: AUTHORS,v 1.9 2004/05/07 11:19:20 alex Exp $ diff --git a/ChangeLog b/ChangeLog index 793832bb..3226ece1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,11 @@ ngIRCd CVSHEAD + - Two new configuration options: "ChrootDir" and "MotdPhrase", thanks to + Benjamin Pineau . Now you can force the daemon to change + its root and working directory to something "safe". MotdPhrase is used + to define an "MOTD string" instead of a whole file, useful if the + "real" MOTD file would be outside the "jail". - INVITE- and BAN-lists become synchronized between IRC+ servers when establishing new connections, if the peer supports this as well. - Reorganized autogen.sh and configure scripts. @@ -516,4 +521,4 @@ ngIRCd 0.0.1, 31.12.2001 -- -$Id: ChangeLog,v 1.233 2004/04/25 15:51:15 alex Exp $ +$Id: ChangeLog,v 1.234 2004/05/07 11:19:20 alex Exp $ diff --git a/INSTALL b/INSTALL index e19ea044..6df155fd 100644 --- a/INSTALL +++ b/INSTALL @@ -75,6 +75,11 @@ In addition, you can pass some command line options to "configure" to enable and/or disable some features of ngIRCd. All these options are shown using "./configure --help", too. +Compiling a static binary will avoid you the hassle of feeding a chroot dir +(if you want use the chroot feature). Just do something like: + CFLAGS=-static ./configure [--your-options ...] +Then you can use a void directory as ChrootDir (like OpenSSH's /var/empty). + 3): "make" @@ -158,4 +163,4 @@ number. In both cases the server exits after the output. -- -$Id: INSTALL,v 1.18 2004/02/22 22:12:44 alex Exp $ +$Id: INSTALL,v 1.19 2004/05/07 11:19:20 alex Exp $ diff --git a/NEWS b/NEWS index b4571954..fdf928d4 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,11 @@ ngIRCd CVSHEAD + - Two new configuration options: "ChrootDir" and "MotdPhrase", thanks to + Benjamin Pineau . Now you can force the daemon to change + its root and working directory to something "safe". MotdPhrase is used + to define an "MOTD string" instead of a whole file, useful if the + "real" MOTD file would be outside the "jail". - INVITE- and BAN-lists become synchronized between IRC+ servers when establishing new connections, if the peer supports this as well. - The type of service (TOS) of all sockets is set to "interactive" now. @@ -184,4 +189,4 @@ ngIRCd 0.0.1, 31.12.2001 -- -$Id: NEWS,v 1.64 2004/04/25 15:51:15 alex Exp $ +$Id: NEWS,v 1.65 2004/05/07 11:19:20 alex Exp $ diff --git a/README b/README index dd171bf9..ee4ce5cf 100644 --- a/README +++ b/README @@ -45,7 +45,8 @@ III. Features (or: why use ngIRCd?) - ngIRCd will be developed on in the future. - supported platforms (tested versions): AIX (3.2.5), A/UX (3.0.1), FreeBSD (4.5), HP-UX (10.20), IRIX (6.5), Linux (2.x), Mac OS X (10.x), NetBSD - (1.5.2/i386, 1.5.3/m68k), Solaris (2.5.1, 2.6), and Windows with Cygwin. + (1.5.2/i386, 1.5.3/m68k), Solaris (2.5.1, 2.6), Windows with Cygwin, and + OpenBSD (3.4/i386). IV. Documentation @@ -83,4 +84,4 @@ mail to: or -- -$Id: README,v 1.18 2004/01/26 02:23:54 alex Exp $ +$Id: README,v 1.19 2004/05/07 11:19:20 alex Exp $ diff --git a/doc/sample-ngircd.conf b/doc/sample-ngircd.conf index f136d27c..e8178a5a 100644 --- a/doc/sample-ngircd.conf +++ b/doc/sample-ngircd.conf @@ -1,4 +1,4 @@ -# $Id: sample-ngircd.conf,v 1.25 2003/12/19 14:32:59 alex Exp $ +# $Id: sample-ngircd.conf,v 1.26 2004/05/07 11:19:20 alex Exp $ # # This is a sample configuration file for the ngIRCd, which must be adepted @@ -45,6 +45,10 @@ # be shown to all users connecting to the server: ;MotdFile = /usr/local/etc/ngircd.motd + # A simple Phrase (<256 chars) if you don't want to use a motd file. + # If it is set no MotdFile will be read at all. + ;MotdPhrase = "Hello world!" + # User ID under which the server should run; you can use the name # of the user or the numerical ID. ATTENTION: For this to work the # server must have been started with root privileges! In addition, @@ -57,6 +61,13 @@ # server must have been started with root privileges! ;ServerGID = 65534 + # A directory to chroot in when everything is initialized. It + # doesn't need to be populated if ngIRCd is compiled as a static + # binary. By default ngIRCd won't use the chroot() feature. + # ATTENTION: For this to work the server must have been started + # with root privileges! + ;ChrootDir = /var/empty + # After seconds of inactivity the server will send a # PING to the peer to test whether it is alive or not. ;PingTimeout = 120 diff --git a/man/ngircd.conf.5 b/man/ngircd.conf.5 index d1cf47e2..97d77c6d 100644 --- a/man/ngircd.conf.5 +++ b/man/ngircd.conf.5 @@ -1,5 +1,5 @@ .\" -.\" $Id: ngircd.conf.5,v 1.12 2003/11/05 21:41:02 alex Exp $ +.\" $Id: ngircd.conf.5,v 1.13 2004/05/07 11:19:20 alex Exp $ .\" .TH ngircd.conf 5 "Mai 2003" ngircd "ngIRCd Manual" .SH NAME @@ -75,6 +75,10 @@ the server listens on all configured ip addresses and interfaces. Text file with the "message of the day" (MOTD). This message will be shown to all users connecting to the server. .TP +\fBMotdPhrase\fR +A simple Phrase (<256 chars) if you don't want to use a motd file. +If it is set no MotdFile will be read at all. +.TP \fBServerUID\fR User ID under which the server should run; you can use the name of the user or the numerical ID. @@ -98,6 +102,18 @@ For this to work the server must have been started with root privileges! .RE .TP +\fBChrootDir\fR +A directory to chroot in when everything is initialized. It doesn't need +to be populated if ngIRCd is compiled as a static binary. By default ngIRCd +won't use the chroot() feature. +.PP +.RS +.B Attention: +.br +For this to work the server must have +been started with root privileges! +.RE +.TP \fBPingTimeout\fR After seconds of inactivity the server will send a PING to the peer to test whether it is alive or not. Default: 120. diff --git a/src/ngircd/conf.c b/src/ngircd/conf.c index fde37b00..1ea1fe74 100644 --- a/src/ngircd/conf.c +++ b/src/ngircd/conf.c @@ -14,7 +14,7 @@ #include "portab.h" -static char UNUSED id[] = "$Id: conf.c,v 1.63 2004/01/17 03:17:00 alex Exp $"; +static char UNUSED id[] = "$Id: conf.c,v 1.64 2004/05/07 11:19:21 alex Exp $"; #include "imp.h" #include @@ -114,6 +114,8 @@ Conf_Test( VOID ) printf( " AdminInfo2 = %s\n", Conf_ServerAdmin2 ); printf( " AdminEMail = %s\n", Conf_ServerAdminMail ); printf( " MotdFile = %s\n", Conf_MotdFile ); + printf( " MotdPhrase = %s\n", Conf_MotdPhrase ); + printf( " ChrootDir= %s\n", Conf_Chroot ); printf( " Ports = " ); for( i = 0; i < Conf_ListenPorts_Count; i++ ) { @@ -343,6 +345,10 @@ Set_Defaults( BOOLEAN InitServers ) strlcpy( Conf_MotdFile, SYSCONFDIR, sizeof( Conf_MotdFile )); strlcat( Conf_MotdFile, MOTD_FILE, sizeof( Conf_MotdFile )); + strlcpy( Conf_MotdPhrase, MOTD_PHRASE, sizeof( Conf_MotdPhrase )); + + strlcpy( Conf_Chroot, CHROOT_DIR, sizeof( Conf_Chroot )); + Conf_ListenPorts_Count = 0; strcpy( Conf_ListenAddress, "" ); @@ -613,6 +619,18 @@ Handle_GLOBAL( INT Line, CHAR *Var, CHAR *Arg ) if( strlcpy( Conf_MotdFile, Arg, sizeof( Conf_MotdFile )) >= sizeof( Conf_MotdFile )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"MotdFile\" too long!", NGIRCd_ConfFile, Line ); return; } + if( strcasecmp( Var, "MotdPhrase" ) == 0 ) + { + /* "Message of the day" phrase (instead of file) */ + if( strlcpy( Conf_MotdPhrase, Arg, sizeof( Conf_MotdPhrase )) >= sizeof( Conf_MotdPhrase )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"MotdPhrase\" too long!", NGIRCd_ConfFile, Line ); + return; + } + if( strcasecmp( Var, "ChrootDir" ) == 0 ) + { + /* directory for chroot() */ + if( strlcpy( Conf_Chroot, Arg, sizeof( Conf_Chroot )) >= sizeof( Conf_Chroot )) Config_Error( LOG_WARNING, "%s, line %d: Value of \"ChrootDir\" too long!", NGIRCd_ConfFile, Line ); + return; + } if( strcasecmp( Var, "ServerUID" ) == 0 ) { /* UID the daemon should switch to */ diff --git a/src/ngircd/conf.h b/src/ngircd/conf.h index b900610b..4fbf2d68 100644 --- a/src/ngircd/conf.h +++ b/src/ngircd/conf.h @@ -8,7 +8,7 @@ * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. * - * $Id: conf.h,v 1.28 2003/11/05 21:41:02 alex Exp $ + * $Id: conf.h,v 1.29 2004/05/07 11:19:21 alex Exp $ * * Configuration management (header) */ @@ -72,6 +72,9 @@ GLOBAL CHAR Conf_ServerAdminMail[CLIENT_INFO_LEN]; /* File with MOTD text */ GLOBAL CHAR Conf_MotdFile[FNAME_LEN]; +/* Phrase with MOTD text */ +GLOBAL CHAR Conf_MotdPhrase[LINE_LEN]; + /* Ports the server should listen on */ GLOBAL UINT Conf_ListenPorts[MAX_LISTEN_PORTS]; GLOBAL INT Conf_ListenPorts_Count; @@ -83,6 +86,9 @@ GLOBAL CHAR Conf_ListenAddress[16]; GLOBAL UINT Conf_UID; GLOBAL UINT Conf_GID; +/* A directory to chroot() in */ +GLOBAL CHAR Conf_Chroot[FNAME_LEN]; + /* Timeouts for PING and PONG */ GLOBAL INT Conf_PingTimeout; GLOBAL INT Conf_PongTimeout; diff --git a/src/ngircd/defines.h b/src/ngircd/defines.h index e351b236..34951aee 100644 --- a/src/ngircd/defines.h +++ b/src/ngircd/defines.h @@ -8,7 +8,7 @@ * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. * - * $Id: defines.h,v 1.45 2004/04/25 15:46:50 alex Exp $ + * $Id: defines.h,v 1.46 2004/05/07 11:19:21 alex Exp $ * * Global defines of ngIRCd. */ @@ -83,6 +83,8 @@ #define CONFIG_FILE "/ngircd.conf" #define MOTD_FILE "/ngircd.motd" +#define MOTD_PHRASE "" +#define CHROOT_DIR "" #define ERROR_DIR "/tmp" diff --git a/src/ngircd/irc-info.c b/src/ngircd/irc-info.c index 59d04dd1..26306949 100644 --- a/src/ngircd/irc-info.c +++ b/src/ngircd/irc-info.c @@ -14,7 +14,7 @@ #include "portab.h" -static char UNUSED id[] = "$Id: irc-info.c,v 1.21 2004/01/17 03:17:49 alex Exp $"; +static char UNUSED id[] = "$Id: irc-info.c,v 1.22 2004/05/07 11:19:21 alex Exp $"; #include "imp.h" #include @@ -773,6 +773,13 @@ IRC_Show_MOTD( CLIENT *Client ) assert( Client != NULL ); + if( Conf_MotdPhrase[0] ) + { + if( ! IRC_WriteStrClient( Client, RPL_MOTDSTART_MSG, Client_ID( Client ), Client_ID( Client_ThisServer( )))) return DISCONNECTED; + if( ! IRC_WriteStrClient( Client, RPL_MOTD_MSG, Client_ID( Client ), Conf_MotdPhrase )) return DISCONNECTED; + return IRC_WriteStrClient( Client, RPL_ENDOFMOTD_MSG, Client_ID( Client )); + } + fd = fopen( Conf_MotdFile, "r" ); if( ! fd ) { @@ -780,7 +787,7 @@ IRC_Show_MOTD( CLIENT *Client ) return IRC_WriteStrClient( Client, ERR_NOMOTD_MSG, Client_ID( Client ) ); } - IRC_WriteStrClient( Client, RPL_MOTDSTART_MSG, Client_ID( Client ), Client_ID( Client_ThisServer( ))); + if( ! IRC_WriteStrClient( Client, RPL_MOTDSTART_MSG, Client_ID( Client ), Client_ID( Client_ThisServer( )))) return DISCONNECTED; while( TRUE ) { if( ! fgets( line, 126, fd )) break; diff --git a/src/ngircd/log.c b/src/ngircd/log.c index 1c8d2c7b..cb149939 100644 --- a/src/ngircd/log.c +++ b/src/ngircd/log.c @@ -14,7 +14,7 @@ #include "portab.h" -static char UNUSED id[] = "$Id: log.c,v 1.44 2003/12/26 15:55:07 alex Exp $"; +static char UNUSED id[] = "$Id: log.c,v 1.45 2004/05/07 11:19:21 alex Exp $"; #include "imp.h" #include @@ -85,6 +85,8 @@ Log_Init( VOID ) } #endif if( Init_Txt[0] ) Log( LOG_INFO, "Activating: %s.", Init_Txt ); + + Error_File[0] = '\0'; } /* Log_Init */ @@ -122,8 +124,11 @@ Log_Exit( VOID ) if( NGIRCd_SignalRestart ) Log( LOG_NOTICE, "%s done (restarting).", PACKAGE_NAME ); else Log( LOG_NOTICE, "%s done.", PACKAGE_NAME ); - /* Error-File (stderr) loeschen */ - if( unlink( Error_File ) != 0 ) Log( LOG_ERR, "Can't delete \"%s\": %s", Error_File, strerror( errno )); + if( Error_File[0] ) + { + /* Error-File (stderr) loeschen */ + if( unlink( Error_File ) != 0 ) Log( LOG_ERR, "Can't delete \"%s\": %s", Error_File, strerror( errno )); + } #ifdef SYSLOG /* syslog abmelden */ diff --git a/src/ngircd/ngircd.c b/src/ngircd/ngircd.c index 8fbdba3f..8ced6e3f 100644 --- a/src/ngircd/ngircd.c +++ b/src/ngircd/ngircd.c @@ -14,7 +14,7 @@ #include "portab.h" -static char UNUSED id[] = "$Id: ngircd.c,v 1.83 2004/01/19 21:54:59 alex Exp $"; +static char UNUSED id[] = "$Id: ngircd.c,v 1.84 2004/05/07 11:19:21 alex Exp $"; #include "imp.h" #include @@ -228,6 +228,38 @@ main( int argc, const char *argv[] ) while( ! NGIRCd_SignalQuit ) { + /* Initialize global variables */ + NGIRCd_Start = time( NULL ); + (VOID)strftime( NGIRCd_StartStr, 64, "%a %b %d %Y at %H:%M:%S (%Z)", localtime( &NGIRCd_Start )); + + NGIRCd_SignalRehash = FALSE; + NGIRCd_SignalRestart = FALSE; + NGIRCd_SignalQuit = FALSE; + + /* 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 )); + } + /* In der Regel wird ein Sub-Prozess ge-fork()'t, der * nicht mehr mit dem Terminal verbunden ist. Mit der * Option "--nodaemon" kann dies (z.B. zum Debuggen) @@ -252,18 +284,10 @@ main( int argc, const char *argv[] ) (VOID)setsid( ); chdir( "/" ); } - - /* Globale Variablen initialisieren */ - NGIRCd_Start = time( NULL ); - (VOID)strftime( NGIRCd_StartStr, 64, "%a %b %d %Y at %H:%M:%S (%Z)", localtime( &NGIRCd_Start )); - NGIRCd_SignalRehash = FALSE; - NGIRCd_SignalRestart = FALSE; - NGIRCd_SignalQuit = FALSE; - /* Module initialisieren */ - Log_Init( ); + /* Initialize modules, part II: these functions are eventually + * called with already dropped privileges ... */ Resolve_Init( ); - Conf_Init( ); Lists_Init( ); Channel_Init( ); Client_Init( ); @@ -272,28 +296,15 @@ main( int argc, const char *argv[] ) #endif Conn_Init( ); - /* Wenn als root ausgefuehrt und eine andere UID - * konfiguriert ist, jetzt zu dieser wechseln */ - if( getuid( ) == 0 ) - { - if( Conf_GID != 0 ) - { - /* Neue Group-ID setzen */ - if( setgid( Conf_GID ) != 0 ) Log( LOG_ERR, "Can't change Group-ID to %u: %s", Conf_GID, strerror( errno )); - } - if( Conf_UID != 0 ) - { - /* Neue User-ID setzen */ - if( setuid( Conf_UID ) != 0 ) Log( LOG_ERR, "Can't change User-ID to %u: %s", Conf_UID, strerror( errno )); - } - } - - /* User, Gruppe und Prozess-ID des Daemon ausgeben */ + /* 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( ), (LONG)getpid( )); - /* stderr in "Error-File" umlenken */ - Log_InitErrorfile( ); + /* Redirect stderr handle to "error file" for debugging. + * But don't try to write in the chroot jail, since it's more + * secure to have a chroot dir not writable by the daemon. + */ + if( ! Conf_Chroot[0] ) Log_InitErrorfile( ); /* Signal-Handler initialisieren */ Initialize_Signal_Handler( ); -- 2.39.2