[icecast-dev] [PATCH] Configurable privileges and chroot jail

Steve Smith steve.smith at isay.com.au
Mon Apr 23 19:01:33 PDT 2001



Hi,

This patch (against the current CVS tree) is intended to add secure
configuration to icecast 'out of the box'.  It adds two configuration
directives, 'icecast_user' and 'chroot_dir'.  These are intended to be
used together to reduce the privileges icecast runs under to the
minimum necessary.  When this is enabled and run as root icecast will
enter the specified chroot jail and drop privileges to the user
specified.

The chroot_dir option will probably not work if --enable-fsstd is
specified, although I haven't actually tried it.

I would appreciate any comments and suggestions.

Cheers,
Steve

** Patch follows **

Index: conf/icecast.conf.dist.in
===================================================================
RCS file: /cvsroot/icecast/conf/icecast.conf.dist.in,v
retrieving revision 1.1
diff -b -u -r1.1 icecast.conf.dist.in
--- conf/icecast.conf.dist.in	1999/12/11 04:11:02	1.1
+++ conf/icecast.conf.dist.in	2001/04/24 01:32:55
@@ -21,6 +21,31 @@
 rp_email kirk at enterprise.space
 server_url http://www.icecast.org/
 
+######################### Server Privileges ###################################
+# Some options to increase security of unattended servers.
+#
+# If the server is run automatically on boot, then the chances are it is
+# running as root.  This is unnecessary, and a bad thing, because if there is a
+# security vunerability in the server, then an attacker can gain control of
+# your system.  If the option "icecast_user" is set, then these privileges will
+# be dropped in favour of the user specified.
+#
+# If you set the "chroot_dir" option, then early in startup the icecast server
+# will change it's root ('/') directory to the one specified.  The advantage
+# of this scheme is that should there be a vunerability in the server then the
+# damage an attacker can do is limited to this directory. Once the new root
+# is in effect, the server will only be able to access files below this
+# directory, so any files the server needs to access should be in this area.
+# The options 'staticdir', 'templatedir' and 'logdir' should be specified
+# as relative to this root (ie. '/etc' instead of '/usr/local/icecast/etc'
+# if the chroot dir is '/usr/local/icecast'.
+# 
+# Both of these options will fail if the server is not run as root.
+###############################################################################
+
+# icecast_user icecast
+# chroot_dir @prefix@
+
 ########################### Server Limits ######################################
 # These describe the maximum number of simultaneous connections allowed.
 # When reached, the server will refuse access.
Index: src/commands.c
===================================================================
RCS file: /cvsroot/icecast/src/commands.c,v
retrieving revision 1.26
diff -b -u -r1.26 commands.c
--- src/commands.c	2000/11/01 21:32:35	1.26
+++ src/commands.c	2001/04/24 01:32:56
@@ -297,6 +297,8 @@
         { "client_password", string_e,   "<unimplemented> Client password", NULL },
         { "admin_password", string_e,    "The remote admin password", NULL },
         { "oper_password", string_e,     "Operator Password", NULL },
+        { "chroot_dir", string_e,        "Dir to chroot to", NULL},
+        { "icecast_user", string_e,      "User to drop privileges to", NULL},
         { "touch_freq", integer_e,       "Touch frequency", NULL },
         { "max_clients", integer_e,      "Highest number of client connectons",  NULL },
         { "max_sources", integer_e,      "Highest number of source connections", NULL },
@@ -396,6 +398,8 @@
         configfile_settings[x++].setting = &info.client_pass;
         configfile_settings[x++].setting = &info.remote_admin_pass;
         configfile_settings[x++].setting = &info.oper_pass;
+        configfile_settings[x++].setting = &info.chroot_dir;
+        configfile_settings[x++].setting = &info.icecast_user;
         configfile_settings[x++].setting = &info.touch_freq;
         configfile_settings[x++].setting = &info.max_clients;
         configfile_settings[x++].setting = &info.max_sources;
Index: src/icetypes.h
===================================================================
RCS file: /cvsroot/icecast/src/icetypes.h,v
retrieving revision 1.14
diff -b -u -r1.14 icetypes.h
--- src/icetypes.h	2000/07/05 19:51:14	1.14
+++ src/icetypes.h	2001/04/24 01:32:57
@@ -265,6 +265,9 @@
         char *logdir;
         char *templatedir;
 
+        char* chroot_dir;
+        char* icecast_user;
+
         /* Encoder stuff */
         avl_tree *sources;	/* Source array */
         unsigned long int num_sources;	/* Encoders connected */
Index: src/main.c
===================================================================
RCS file: /cvsroot/icecast/src/main.c,v
retrieving revision 1.18
diff -b -u -r1.18 main.c
--- src/main.c	2000/07/27 07:04:15	1.18
+++ src/main.c	2001/04/24 01:32:57
@@ -46,6 +46,9 @@
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/types.h>
 
 #ifndef _WIN32
 #include <sys/socket.h> 
@@ -164,6 +167,9 @@
         /* Initialize some platform dependant network stuff */
         initialize_network ();
         
+        /* Drop to unprivileged user if defined. */
+        drop_privileges();
+
         /* Initialize the interpreter (if configured) */
         interpreter_init ();
 
@@ -231,6 +237,80 @@
 #endif
 }
 
+void drop_privileges()
+{
+#ifdef _WIN32
+        return;
+#else
+        struct passwd *pwd;
+        uid_t   uid, cuid;
+        gid_t   gid;
+
+        if (info.icecast_user != 0) {
+                pwd = getpwnam(info.icecast_user);
+                if (!pwd) {
+                        write_log (LOG_DEFAULT, "ERROR: icecast_user %s doesn't exist",
+                                   info.icecast_user);
+                        clean_shutdown (&info);
+                }
+
+                /* These will get clobbered during the chroot, so save them */
+                uid = pwd->pw_uid;
+                gid = pwd->pw_gid;
+
+                cuid = getuid();
+                if (cuid != 0 && uid != cuid) {
+                        write_log (LOG_DEFAULT, "ERROR: Don't have privileges to "
+                                   "change to icecast_user %s", info.icecast_user);
+                        clean_shutdown (&info);
+                }
+
+                if (setgid(gid) < 0 ||
+                    initgroups(info.icecast_user, gid) < 0)
+                {
+                        write_log (LOG_DEFAULT, "ERROR: Failed to change to "
+                                   "icecast_user %s: %s", info.icecast_user, strerror(errno));
+                        clean_shutdown (&info);
+                }
+                xa_debug(1, "Initialised groups for %s", info.icecast_user);
+        }
+
+
+        if (info.chroot_dir != 0) {
+                /* Only root can do this */
+                if (cuid != 0) {
+                        write_log (LOG_DEFAULT, "ERROR: Don't have privileges to "
+                                   "chroot to %s", info.chroot_dir);
+                        clean_shutdown (&info);
+                }
+
+                /* Set root dir and climb in */
+                if (chroot(info.chroot_dir) < 0) {
+                        write_log (LOG_DEFAULT, "ERROR: Failed to chroot to %s: %s",
+                                   info.chroot_dir,strerror(errno));
+                        clean_shutdown (&info);
+                }
+                if (chdir("/") < 0) {
+                        write_log (LOG_DEFAULT, "ERROR: Failed to enter jail: %s",
+                                   strerror(errno));
+                        clean_shutdown (&info);
+                }
+
+                xa_debug(1, "Entered jail %s", info.chroot_dir);
+        }
+
+        if (info.icecast_user != 0) {
+                if (setuid(uid) < 0) {
+                        write_log (LOG_DEFAULT, "ERROR: Failed to change to "
+                                   "icecast_user %s: %s", info.icecast_user, strerror(errno));
+                        clean_shutdown (&info);
+                }
+                xa_debug(1, "Dropped privileges to user %s", info.icecast_user);
+        }
+#endif
+}
+
+
 /* Print header, select the console mode, and start the main server loop. */
 void
 startup_mode()
@@ -544,7 +624,7 @@
         directory_server_t *ds;
         int i;
         avl_traverser trav = {0};
-	static main_shutting_down = 0;
+        static int main_shutting_down = 0;
         
         thread_library_lock ();
                 if (!main_shutting_down)
Index: src/main.h
===================================================================
RCS file: /cvsroot/icecast/src/main.h,v
retrieving revision 1.4
diff -b -u -r1.4 main.h
--- src/main.h	2000/05/25 16:00:15	1.4
+++ src/main.h	2001/04/24 01:32:57
@@ -27,6 +27,7 @@
 void setup_defaults();
 void setup_signal_traps();
 void allocate_resources();
+void drop_privileges();
 void startup_mode();
 void clean_shutdown(server_info_t *info);
 void usage();

--- >8 ----
List archives:  http://www.xiph.org/archives/
icecast project homepage: http://www.icecast.org/
To unsubscribe from this list, send a message to 'icecast-dev-request at xiph.org'
containing only the word 'unsubscribe' in the body.  No subject is needed.
Unsubscribe messages sent to the list will be ignored/filtered.



More information about the Icecast-dev mailing list