Commit 4d884d83 authored by Michael Smith's avatar Michael Smith

Ian Kumlien's security (chroot() and setuid() patch), with some modifications.

svn path=/trunk/icecast/; revision=3302
parent 669cfbd0
...@@ -34,4 +34,12 @@ ...@@ -34,4 +34,12 @@
<accesslog>access.log</accesslog> <accesslog>access.log</accesslog>
<errorlog>error.log</errorlog> <errorlog>error.log</errorlog>
</logging> </logging>
<security>
<chroot>0</chroot>
<!--<changeowner>
<user>nobody</user>
<group>nogroup</group>
</changeowner> -->
</security>
</icecast> </icecast>
...@@ -72,6 +72,8 @@ dnl Checks for header files. ...@@ -72,6 +72,8 @@ dnl Checks for header files.
AC_HEADER_STDC AC_HEADER_STDC
AC_CHECK_HEADER(stdint.h, AC_DEFINE(HAVE_STDINT_H, 1),,) AC_CHECK_HEADER(stdint.h, AC_DEFINE(HAVE_STDINT_H, 1),,)
AC_CHECK_HEADER(pwd.h, AC_DEFINE(CHUID, 1),,)
AC_CHECK_HEADER(unistd.h, AC_DEFINE(CHROOT, 1),,)
dnl Checks for typedefs, structures, and compiler characteristics. dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST AC_C_CONST
......
...@@ -19,6 +19,10 @@ ...@@ -19,6 +19,10 @@
#define CONFIG_DEFAULT_PORT 8888 #define CONFIG_DEFAULT_PORT 8888
#define CONFIG_DEFAULT_ACCESS_LOG "access.log" #define CONFIG_DEFAULT_ACCESS_LOG "access.log"
#define CONFIG_DEFAULT_ERROR_LOG "error.log" #define CONFIG_DEFAULT_ERROR_LOG "error.log"
#define CONFIG_DEFAULT_CHROOT 0
#define CONFIG_DEFAULT_CHUID 0
#define CONFIG_DEFAULT_USER NULL
#define CONFIG_DEFAULT_GROUP NULL
#ifndef _WIN32 #ifndef _WIN32
#define CONFIG_DEFAULT_BASE_DIR "/usr/local/icecast" #define CONFIG_DEFAULT_BASE_DIR "/usr/local/icecast"
...@@ -37,6 +41,7 @@ static void _parse_limits(xmlDocPtr doc, xmlNodePtr node); ...@@ -37,6 +41,7 @@ static void _parse_limits(xmlDocPtr doc, xmlNodePtr node);
static void _parse_directory(xmlDocPtr doc, xmlNodePtr node); static void _parse_directory(xmlDocPtr doc, xmlNodePtr node);
static void _parse_paths(xmlDocPtr doc, xmlNodePtr node); static void _parse_paths(xmlDocPtr doc, xmlNodePtr node);
static void _parse_logging(xmlDocPtr doc, xmlNodePtr node); static void _parse_logging(xmlDocPtr doc, xmlNodePtr node);
static void _parse_security(xmlDocPtr doc, xmlNodePtr node);
static void _add_server(xmlDocPtr doc, xmlNodePtr node); static void _add_server(xmlDocPtr doc, xmlNodePtr node);
void config_initialize(void) void config_initialize(void)
...@@ -60,6 +65,8 @@ void config_shutdown(void) ...@@ -60,6 +65,8 @@ void config_shutdown(void)
if (_configuration.access_log) free(_configuration.access_log); if (_configuration.access_log) free(_configuration.access_log);
if (_configuration.error_log) free(_configuration.error_log); if (_configuration.error_log) free(_configuration.error_log);
if (_configuration.bind_address) free(_configuration.bind_address); if (_configuration.bind_address) free(_configuration.bind_address);
if (_configuration.user) free(_configuration.user);
if (_configuration.group) free(_configuration.group);
dirnode = _configuration.dir_list; dirnode = _configuration.dir_list;
while(dirnode) { while(dirnode) {
nextdirnode = dirnode->next; nextdirnode = dirnode->next;
...@@ -138,6 +145,10 @@ static void _set_defaults(void) ...@@ -138,6 +145,10 @@ static void _set_defaults(void)
_configuration.log_dir = (char *)strdup(CONFIG_DEFAULT_LOG_DIR); _configuration.log_dir = (char *)strdup(CONFIG_DEFAULT_LOG_DIR);
_configuration.access_log = (char *)strdup(CONFIG_DEFAULT_ACCESS_LOG); _configuration.access_log = (char *)strdup(CONFIG_DEFAULT_ACCESS_LOG);
_configuration.error_log = (char *)strdup(CONFIG_DEFAULT_ERROR_LOG); _configuration.error_log = (char *)strdup(CONFIG_DEFAULT_ERROR_LOG);
_configuration.chroot = CONFIG_DEFAULT_CHROOT;
_configuration.chuid = CONFIG_DEFAULT_CHUID;
_configuration.user = CONFIG_DEFAULT_USER;
_configuration.group = CONFIG_DEFAULT_GROUP;
} }
static void _parse_root(xmlDocPtr doc, xmlNodePtr node) static void _parse_root(xmlDocPtr doc, xmlNodePtr node)
...@@ -175,6 +186,8 @@ static void _parse_root(xmlDocPtr doc, xmlNodePtr node) ...@@ -175,6 +186,8 @@ static void _parse_root(xmlDocPtr doc, xmlNodePtr node)
_parse_paths(doc, node->xmlChildrenNode); _parse_paths(doc, node->xmlChildrenNode);
} else if (strcmp(node->name, "logging") == 0) { } else if (strcmp(node->name, "logging") == 0) {
_parse_logging(doc, node->xmlChildrenNode); _parse_logging(doc, node->xmlChildrenNode);
} else if (strcmp(node->name, "security") == 0) {
_parse_security(doc, node->xmlChildrenNode);
} }
} while ((node = node->next)); } while ((node = node->next));
} }
...@@ -265,6 +278,39 @@ static void _parse_logging(xmlDocPtr doc, xmlNodePtr node) ...@@ -265,6 +278,39 @@ static void _parse_logging(xmlDocPtr doc, xmlNodePtr node)
} while ((node = node->next)); } while ((node = node->next));
} }
static void _parse_security(xmlDocPtr doc, xmlNodePtr node)
{
char *tmp;
xmlNodePtr oldnode;
do {
if (node == NULL) break;
if (xmlIsBlankNode(node)) continue;
if (strcmp(node->name, "chroot") == 0) {
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
_configuration.chroot = atoi(tmp);
if (tmp) free(tmp);
} else if (strcmp(node->name, "changeowner") == 0) {
_configuration.chuid = 1;
oldnode = node;
node = node->xmlChildrenNode;
do {
if(node == NULL) break;
if(xmlIsBlankNode(node)) continue;
if(strcmp(node->name, "user") == 0) {
if(_configuration.user) free(_configuration.user);
_configuration.user = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
} else if(strcmp(node->name, "group") == 0) {
if(_configuration.group) free(_configuration.group);
_configuration.group = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
}
} while((node = node->next));
node = oldnode;
}
} while ((node = node->next));
}
static void _add_server(xmlDocPtr doc, xmlNodePtr node) static void _add_server(xmlDocPtr doc, xmlNodePtr node)
{ {
ice_config_dir_t *dirnode, *server; ice_config_dir_t *dirnode, *server;
......
...@@ -38,6 +38,11 @@ typedef struct ice_config_tag ...@@ -38,6 +38,11 @@ typedef struct ice_config_tag
char *access_log; char *access_log;
char *error_log; char *error_log;
int chroot;
int chuid;
char *user;
char *group;
} ice_config_t; } ice_config_t;
void config_initialize(void); void config_initialize(void);
......
...@@ -8,6 +8,12 @@ ...@@ -8,6 +8,12 @@
#include "resolver.h" #include "resolver.h"
#include "httpp.h" #include "httpp.h"
#ifdef CHUID
#include <sys/types.h>
#include <grp.h>
#include <pwd.h>
#include <errno.h>
#endif
#include "config.h" #include "config.h"
#include "sighandler.h" #include "sighandler.h"
...@@ -43,7 +49,6 @@ static void _initialize_subsystems(void) ...@@ -43,7 +49,6 @@ static void _initialize_subsystems(void)
config_initialize(); config_initialize();
connection_initialize(); connection_initialize();
global_initialize(); global_initialize();
stats_initialize();
refbuf_initialize(); refbuf_initialize();
} }
...@@ -135,8 +140,8 @@ static int _start_listening(void) ...@@ -135,8 +140,8 @@ static int _start_listening(void)
return 1; return 1;
} }
/* this is the heart of the beast */ /* bind the socket and start listening */
static void _server_proc(void) static void _server_proc_init(void)
{ {
if (!_setup_socket()) { if (!_setup_socket()) {
ERROR1("Could not create listener socket on port %d", config_get_config()->port); ERROR1("Could not create listener socket on port %d", config_get_config()->port);
...@@ -147,12 +152,76 @@ static void _server_proc(void) ...@@ -147,12 +152,76 @@ static void _server_proc(void)
ERROR0("Failed trying to listen on server socket"); ERROR0("Failed trying to listen on server socket");
return; return;
} }
}
/* this is the heart of the beast */
static void _server_proc(void)
{
connection_accept_loop(); connection_accept_loop();
sock_close(global.serversock); sock_close(global.serversock);
} }
#ifdef CHROOT
/* chroot the process. Watch out - we need to do this before starting other
* threads */
static void _chroot_setup(void)
{
ice_config_t *conf = config_get_config();
if (conf->chroot)
{
if(getuid()) /* root check */
{
fprintf(stderr, "WARNING: Cannot change server root unless running as root.\n");
return;
}
if(chroot(conf->base_dir))
{
fprintf(stderr,"WARNING: Couldn't change server root: %s\n", strerror(errno));
return;
}
else
fprintf(stdout, "Changed root successfully to \"%s\".\n", conf->base_dir);
}
}
#endif
#ifdef CHUID
/* change uid and gid */
static void _chuid_setup(void)
{
ice_config_t *conf = config_get_config();
struct passwd *user;
struct group *group;
if(conf->chuid)
{
if(getuid()) /* root check */
{
fprintf(stderr, "WARNING: Can't change user id unless you are root.\n");
return;
}
user = getpwnam(conf->user);
group = getgrnam(conf->group);
if(!setgid(group->gr_gid))
fprintf(stdout, "Changed groupid to %i.\n", group->gr_gid);
else
fprintf(stdout, "Error changing groupid: %s.\n", strerror(errno));
if(!setuid(user->pw_uid))
fprintf(stdout, "Changed userid to %i.\n", user->pw_uid);
else
fprintf(stdout, "Error changing userid: %s.\n", strerror(errno));
}
}
#endif
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int res, ret; int res, ret;
...@@ -161,9 +230,6 @@ int main(int argc, char **argv) ...@@ -161,9 +230,6 @@ int main(int argc, char **argv)
/* startup all the modules */ /* startup all the modules */
_initialize_subsystems(); _initialize_subsystems();
/* setup the default signal handlers */
sighandler_initialize();
/* parse the '-c icecast.xml' option /* parse the '-c icecast.xml' option
** only, so that we can read a configfile ** only, so that we can read a configfile
*/ */
...@@ -197,6 +263,33 @@ int main(int argc, char **argv) ...@@ -197,6 +263,33 @@ int main(int argc, char **argv)
/* override config file options with commandline options */ /* override config file options with commandline options */
config_parse_cmdline(argc, argv); config_parse_cmdline(argc, argv);
#ifdef CHROOT
_chroot_setup(); /* Perform chroot, if requested */
#endif
_server_proc_init(); /* Bind socket, before we change userid */
#ifdef CHUID
_chuid_setup(); /* change user id */
#endif
stats_initialize(); /* We have to do this later on because of threading */
#ifdef CHUID
/* We'll only have getuid() if we also have setuid(), it's reasonable to
* assume */
if(!getuid()) /* Running as root! Don't allow this */
{
fprintf(stderr, "WARNING: You should not run icecast2 as root\n");
fprintf(stderr, "Use the changeowner directive in the config file\n");
_shutdown_subsystems();
return 1;
}
#endif
/* setup default signal handlers */
sighandler_initialize();
if (!_start_logging()) { if (!_start_logging()) {
fprintf(stderr, "FATAL: Could not start logging\n"); fprintf(stderr, "FATAL: Could not start logging\n");
_shutdown_subsystems(); _shutdown_subsystems();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment