mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2024-12-04 14:46:30 -05:00
Ian Kumlien's security (chroot() and setuid() patch), with some modifications.
svn path=/trunk/icecast/; revision=3302
This commit is contained in:
parent
669cfbd02f
commit
4d884d832f
@ -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.
|
|||||||
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
|
||||||
|
46
src/config.c
46
src/config.c
@ -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);
|
|||||||
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)
|
|||||||
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)
|
|||||||
_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)
|
|||||||
_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)
|
|||||||
} 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
|
|||||||
|
|
||||||
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);
|
||||||
|
105
src/main.c
105
src/main.c
@ -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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
/* 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)
|
|||||||
/* 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();
|
||||||
|
Loading…
Reference in New Issue
Block a user