New "chroot" feature (from Benjamin Pineau), introducing new configuration
authorAlexander Barton <alex@barton.de>
Fri, 7 May 2004 11:19:20 +0000 (11:19 +0000)
committerAlexander Barton <alex@barton.de>
Fri, 7 May 2004 11:19:20 +0000 (11:19 +0000)
variables "ChrootDir" and "MotdPhrase".

13 files changed:
AUTHORS
ChangeLog
INSTALL
NEWS
README
doc/sample-ngircd.conf
man/ngircd.conf.5
src/ngircd/conf.c
src/ngircd/conf.h
src/ngircd/defines.h
src/ngircd/irc-info.c
src/ngircd/log.c
src/ngircd/ngircd.c

diff --git a/AUTHORS b/AUTHORS
index 929679a4295c005b2b1e111d87d49fe10f1ff17e..6cca6cb4689e0e00071ce4859a0d522a2446d682 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -19,6 +19,7 @@ Contributors
 ~~~~~~~~~~~~
 Goetz Hoffart, <goetz@hoffart.de> (goetz)
 Ilja Osthoff, <i.osthoff@gmx.net> (ilja)
+Benjamin Pineau, <ben@zouh.org>
 Sean Reifschneider, <jafo-rpms@tummy.com>
 
 
@@ -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 $
index 793832bbd8c99f41bb56eff623dd09378ed01192..3226ece1115d805fb67be038f8eec91a1abb2666 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
 
 ngIRCd CVSHEAD
 
+  - Two new configuration options: "ChrootDir" and "MotdPhrase", thanks to
+    Benjamin Pineau <ben@zouh.org>. 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 e19ea044efe467c018d495b2c5e67636715c90d6..6df155fdd784e6bc6754d6bbeaf37848a81bcdbf 100644 (file)
--- 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 b45719549da8913bae1a7405b368df0c1990d910..fdf928d4ec4f237d92febf84fee0c771f82c2540 100644 (file)
--- a/NEWS
+++ b/NEWS
 
 ngIRCd CVSHEAD
 
+  - Two new configuration options: "ChrootDir" and "MotdPhrase", thanks to
+    Benjamin Pineau <ben@zouh.org>. 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 dd171bf9680cec6b8f3120bf5d0b6db19fdb45a6..ee4ce5cf38838481f13bcd95b1044d5eb22239e7 100644 (file)
--- 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: <alex@barton.de> or <alex@arthur.ath.cx>
 
 
 -- 
-$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 $
index f136d27ce4973770dcd6b2c473deb0446412ab16..e8178a5a7483f6c010604d66b1c40b119f4c1992 100644 (file)
@@ -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
        # 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,
        # 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 <PingTimeout> seconds of inactivity the server will send a
        # PING to the peer to test whether it is alive or not.
        ;PingTimeout = 120
index d1cf47e2d60a24a006b0f9d36ee3ea90783a1876..97d77c6d1b35e8142a08bbcfce34d10a291c95ca 100644 (file)
@@ -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 <PingTimeout> seconds of inactivity the server will send a PING to
 the peer to test whether it is alive or not. Default: 120.
index fde37b00948cc1d048273496239c0515f7b5c112..1ea1fe741ef3397c35a6d151c1bac575c4c1eec7 100644 (file)
@@ -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 <assert.h>
@@ -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 */
index b900610b71fa838627e413a3c3bda680cf4ec668..4fbf2d68597c4d8a69f990d5244570ff69d11104 100644 (file)
@@ -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;
index e351b236234f64ea48a82f9af043800db5395ef3..34951aeef8757d41a6837b996cd4ec45ef314714 100644 (file)
@@ -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"
 
index 59d04dd1bc6634fe32c872783273fcfb92b2e556..26306949ed865ecc014d0299770c73e5ee4ab790 100644 (file)
@@ -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 <assert.h>
@@ -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;
index 1c8d2c7b3e1b52ab37b6b14cb88568d63d434212..cb149939b789db792419481e2c74545ba3897f51 100644 (file)
@@ -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 <assert.h>
@@ -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 */
index 8fbdba3f78ab017aa1bdb32848ab1fb2cadc3b1b..8ced6e3f8903c8698e2aa505c8c558a15798b539 100644 (file)
@@ -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 <assert.h>
@@ -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( );