Commit d9799c19 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

Bring the root wrapper back

parent 793c4fa6
...@@ -427,9 +427,12 @@ misc/revision.c: ...@@ -427,9 +427,12 @@ misc/revision.c:
# Building vlc # Building vlc
############################################################################### ###############################################################################
EXTRA_PROGRAMS = vlc EXTRA_PROGRAMS = vlc vlc-wrapper
if BUILD_VLC if BUILD_VLC
bin_PROGRAMS = vlc bin_PROGRAMS = vlc
if !HAVE_WIN32
bin_PROGRAMS += vlc-wrapper
endif
endif endif
EXTRA_vlc_SOURCES = vlc.c winvlc.c EXTRA_vlc_SOURCES = vlc.c winvlc.c
...@@ -438,6 +441,7 @@ vlc_SOURCES = vlc.c ...@@ -438,6 +441,7 @@ vlc_SOURCES = vlc.c
else else
vlc_SOURCES = winvlc.c vlc_SOURCES = winvlc.c
endif endif
vlc_wrapper_SOURCES = network/rootwrap.c
vlc_DEPENDENCIES = $(DATA_win32_rc) libvlc.la vlc_DEPENDENCIES = $(DATA_win32_rc) libvlc.la
......
/***************************************************************************** /*****************************************************************************
* rootwrap.c * rootwrap.c
***************************************************************************** *****************************************************************************
* Copyright © 2005 Rémi Denis-Courmont * Copyright © 2005-2008 Rémi Denis-Courmont
* $Id$
*
* Author: Rémi Denis-Courmont <rem # videolan.org>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -25,12 +22,6 @@ ...@@ -25,12 +22,6 @@
# include <config.h> # include <config.h>
#endif #endif
#if defined (HAVE_GETEUID) && !defined (SYS_BEOS)
# define ENABLE_ROOTWRAP 1
#endif
#ifdef ENABLE_ROOTWRAP
#include <stdlib.h> /* exit() */ #include <stdlib.h> /* exit() */
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
...@@ -38,19 +29,13 @@ ...@@ -38,19 +29,13 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/stat.h>
#include <sys/socket.h> #include <sys/socket.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <sys/uio.h> #include <sys/uio.h>
#include <sys/resource.h> /* getrlimit() */ #include <sys/resource.h> /* getrlimit() */
#include <sys/wait.h> #include <sched.h>
#include <sys/un.h>
#include <pwd.h> /* getpwnam(), getpwuid() */
#include <grp.h> /* setgroups() */
#include <errno.h> #include <errno.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <pthread.h>
#if defined (AF_INET6) && !defined (IPV6_V6ONLY) #if defined (AF_INET6) && !defined (IPV6_V6ONLY)
# warning Uho, your IPv6 support is broken and has been disabled. Fix your C library. # warning Uho, your IPv6 support is broken and has been disabled. Fix your C library.
...@@ -61,66 +46,20 @@ ...@@ -61,66 +46,20 @@
# define AF_LOCAL AF_UNIX # define AF_LOCAL AF_UNIX
#endif #endif
/*#ifndef HAVE_CLEARENV static inline int is_allowed_port (uint16_t port)
extern char **environ;
static int clearenv (void)
{
environ = NULL;
return 0;
}
#endif*/
/**
* Tries to find a real non-root user to use
*/
static struct passwd *guess_user (void)
{
const char *name;
struct passwd *pw;
uid_t uid;
/* Try real UID */
uid = getuid ();
if (uid)
if ((pw = getpwuid (uid)) != NULL)
return pw;
/* Try sudo */
name = getenv ("SUDO_USER");
if (name != NULL)
if ((pw = getpwnam (name)) != NULL)
return pw;
/* Try VLC_USER */
name = getenv ("VLC_USER");
if (name != NULL)
if ((pw = getpwnam (name)) != NULL)
return pw;
/* Try vlc */
if ((pw = getpwnam ("vlc")) != NULL)
return pw;
return getpwuid (0);
}
static int is_allowed_port (uint16_t port)
{ {
port = ntohs (port); port = ntohs (port);
return (port == 80) || (port == 443) || (port == 554); return (port == 80) || (port == 443) || (port == 554);
} }
static int send_err (int fd, int err) static inline int send_err (int fd, int err)
{ {
return send (fd, &err, sizeof (err), 0) == sizeof (err) ? 0 : -1; return send (fd, &err, sizeof (err), 0) == sizeof (err) ? 0 : -1;
} }
/** /**
* Ugly POSIX(?) code to pass a file descriptor to another process * Send a file descriptor to another process
*/ */
static int send_fd (int p, int fd) static int send_fd (int p, int fd)
{ {
...@@ -158,10 +97,6 @@ static void rootprocess (int fd) ...@@ -158,10 +97,6 @@ static void rootprocess (int fd)
{ {
struct sockaddr_storage ss; struct sockaddr_storage ss;
/* TODO:
* - use libcap if available,
* - call chroot
*/
while (recv (fd, &ss, sizeof (ss), 0) == sizeof (ss)) while (recv (fd, &ss, sizeof (ss), 0) == sizeof (ss))
{ {
unsigned len; unsigned len;
...@@ -218,236 +153,76 @@ static void rootprocess (int fd) ...@@ -218,236 +153,76 @@ static void rootprocess (int fd)
} }
} }
static int rootwrap_sock = -1; /* TODO?
static pid_t rootwrap_pid = -1; * - use libcap if available,
* - call chroot
static void close_rootwrap (void) */
{
close (rootwrap_sock);
waitpid (rootwrap_pid, NULL, 0);
}
void rootwrap (void) int main (int argc, char *argv[])
{ {
struct rlimit lim; /* Support for dynamically opening RTSP, HTTP and HTTP/SSL ports */
int fd, pair[2]; int pair[2];
struct passwd *pw;
uid_t u;
u = geteuid ();
/* Are we running with root privileges? */
if (u != 0)
{
setuid (u);
return;
}
/* Make sure 0, 1 and 2 are opened, and only these. */
if (getrlimit (RLIMIT_NOFILE, &lim))
exit (1);
for (fd = 3; ((unsigned)fd) < lim.rlim_cur; fd++)
close (fd);
fd = dup (2);
if (fd <= 2)
exit (1);
close (fd);
fputs ("starting VLC root wrapper...", stderr);
pw = guess_user ();
if (pw == NULL)
return; /* Should we rather print an error and exit ? */
u = pw->pw_uid,
fprintf (stderr, " using UID %u (%s)\n", (unsigned)u, pw->pw_name);
if (u == 0)
{
fputs ("***************************************\n"
"* Running VLC as root is discouraged. *\n"
"***************************************\n"
"\n"
" It is potentially dangerous, "
"and might not even work properly.\n", stderr);
return;
}
/* GID */
initgroups (pw->pw_name, pw->pw_gid);
setgid (pw->pw_gid);
if (socketpair (AF_LOCAL, SOCK_STREAM, 0, pair)) if (socketpair (AF_LOCAL, SOCK_STREAM, 0, pair))
{ return 1;
perror ("socketpair"); if (pair[0] < 3)
goto nofork; goto error; /* we want 0, 1 and 2 open */
}
switch (rootwrap_pid = fork ()) pid_t pid = fork ();
switch (pid)
{ {
case -1: case -1:
perror ("fork"); goto error;
close (pair[0]);
close (pair[1]);
break;
case 0: case 0:
close (0); {
close (1); int null = open ("/dev/null", O_RDWR);
close (2); if (null != -1)
{
dup2 (null, 0);
dup2 (null, 1);
dup2 (null, 2);
close (null);
}
close (pair[0]); close (pair[0]);
setsid ();
rootprocess (pair[1]); rootprocess (pair[1]);
exit (0); exit (0);
default:
close (pair[1]);
rootwrap_sock = pair[0];
break;
}
nofork:
/* UID */
setuid (u);
atexit (close_rootwrap);
}
/**
* Ugly POSIX(?) code to receive a file descriptor from another process
*/
static int recv_fd (int p)
{
struct msghdr hdr;
struct iovec iov;
struct cmsghdr *cmsg;
int val, fd;
char buf[CMSG_SPACE (sizeof (fd))];
hdr.msg_name = NULL;
hdr.msg_namelen = 0;
hdr.msg_iov = &iov;
hdr.msg_iovlen = 1;
hdr.msg_control = buf;
hdr.msg_controllen = sizeof (buf);
iov.iov_base = &val;
iov.iov_len = sizeof (val);
if (recvmsg (p, &hdr, 0) != sizeof (val))
return -1;
for (cmsg = CMSG_FIRSTHDR (&hdr); cmsg != NULL;
cmsg = CMSG_NXTHDR (&hdr, cmsg))
{
if ((cmsg->cmsg_level == SOL_SOCKET)
&& (cmsg->cmsg_type = SCM_RIGHTS)
&& (cmsg->cmsg_len >= CMSG_LEN (sizeof (fd))))
{
memcpy (&fd, CMSG_DATA (cmsg), sizeof (fd));
return fd;
} }
} }
return -1; close (pair[1]);
} pair[1] = -1;
/**
* Tries to obtain a bound TCP socket from the root process
*/
int rootwrap_bind (int family, int socktype, int protocol,
const struct sockaddr *addr, size_t alen)
{
/* can't use libvlc */
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
struct sockaddr_storage ss;
int fd;
if (rootwrap_sock == -1) char buf[21];
{ snprintf (buf, sizeof (buf), "%d", pair[0]);
errno = EACCES; setenv ("VLC_ROOTWRAP_SOCK", buf, 1);
return -1;
}
switch (family)
{
case AF_INET:
if (alen < sizeof (struct sockaddr_in))
{
errno = EINVAL;
return -1;
}
break;
#ifdef AF_INET6 /* Support for real-time priorities */
case AF_INET6: #ifdef RLIMIT_RTPRIO
if (alen < sizeof (struct sockaddr_in6)) struct rlimit rlim;
{ rlim.rlim_max = rlim.rlim_cur = sched_get_priority_min (SCHED_RR) + 24;
errno = EINVAL; setrlimit (RLIMIT_RTPRIO, &rlim);
return -1;
}
break;
#endif #endif
default: setuid (getuid ());
errno = EAFNOSUPPORT;
return -1;
}
if (family != addr->sa_family)
{
errno = EAFNOSUPPORT;
return -1;
}
/* Only TCP is implemented at the moment */
if ((socktype != SOCK_STREAM)
|| (protocol && (protocol != IPPROTO_TCP)))
{
errno = EACCES;
return -1;
}
memset (&ss, 0, sizeof (ss));
memcpy (&ss, addr, alen > sizeof (ss) ? sizeof (ss) : alen);
pthread_mutex_lock (&mutex);
if (send (rootwrap_sock, &ss, sizeof (ss), 0) != sizeof (ss))
return -1;
fd = recv_fd (rootwrap_sock);
pthread_mutex_unlock (&mutex);
if (fd != -1) if (!setuid (0)) /* sanity check: we cannot get root back */
{ exit (1);
int val;
val = fcntl (fd, F_GETFL, 0);
fcntl (fd, F_SETFL, ((val != -1) ? val : 0) | O_NONBLOCK);
}
return fd;
}
#else
# include <stddef.h>
struct sockaddr;
void rootwrap (void)
{
}
int rootwrap_bind (int family, int socktype, int protocol, /* Yeah, the user can force to execute just about anything from here.
const struct sockaddr *addr, size_t alen) * But we've dropped privileges, so it does not matter. */
{ if (strlen (argv[0]) < sizeof ("-wrapper"))
(void)family; goto error;
(void)socktype; argv[0][strlen (argv[0]) - strlen ("-wrapper")] = '\0';
(void)protocol;
(void)addr; (void)argc;
(void)alen; if (execvp (argv[0], argv))
return -1; perror (argv[0]);
error:
close (pair[0]);
if (pair[1] != -1)
close (pair[1]);
return 1;
} }
#endif /* ENABLE_ROOTWRAP */
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