Hi, vdr has a feature to use dvb time information to set the system clock. That usually means that vdr has to run as root. The following patch enables vdr 1.2.6 to run as unprivileged user while still beeing able to set the system clock. This is achieved by keeping capabilties during setuid() and dropping all of them except cap_sys_time afterwards. You need libcap if you want to enable the use of capabilities (echo WITH_CAPABILITIES = 1 >> Make.config). The patch replaces my last patch which had a bug at setgid() (was else if instead of if). cu Ludwig Index: vdr-1.2.6/Makefile =================================================================== --- vdr-1.2.6.orig/Makefile 2004-01-18 01:15:22.000000000 +0100 +++ vdr-1.2.6/Makefile 2004-01-18 01:15:22.000000000 +0100 @@ -55,7 +55,7 @@ ifdef DEBUG_OSD DEFINES += -DDEBUG_OSD -NCURSESLIB = -lncurses +LIBS += -lncurses endif ifdef VFAT @@ -63,6 +63,11 @@ DEFINES += -DVFAT endif +ifdef WITH_CAPABILITIES +DEFINES += -DWITH_CAPABILITIES +LIBS += -lcap +endif + all: vdr font: genfontfile fontfix.c fontosd.c fontsmallfix.c fontsmallosd.c @echo "font files created." @@ -84,7 +89,7 @@ # The main program: vdr: $(OBJS) $(DTVLIB) - $(CXX) $(CXXFLAGS) -rdynamic $(OBJS) $(NCURSESLIB) -ljpeg -lpthread -ldl $(LIBDIRS) $(DTVLIB) -o vdr + $(CXX) $(CXXFLAGS) -rdynamic $(OBJS) $(LIBS) -ljpeg -lpthread -ldl $(LIBDIRS) $(DTVLIB) -o vdr # The font files: Index: vdr-1.2.6/vdr.c =================================================================== --- vdr-1.2.6.orig/vdr.c 2004-01-18 01:15:22.000000000 +0100 +++ vdr-1.2.6/vdr.c 2004-01-19 20:03:03.000000000 +0100 @@ -31,6 +31,12 @@ #include <stdlib.h> #include <termios.h> #include <unistd.h> +#include <pwd.h> +#include <grp.h> +#ifdef WITH_CAPABILITIES +#include <sys/capability.h> +#include <sys/prctl.h> +#endif #include "audio.h" #include "channels.h" #include "config.h" @@ -77,6 +83,104 @@ exit(1); } +// switch user and group uid +// taken from startproc by Werner Fink +static int su(const char* username, const char* groupname) +{ + gid_t ngid = 0; + struct group* grp = NULL; + struct passwd *user = NULL; + + if(!username) return 0; + + user = getpwnam(username); + endpwent(); + if(!user) + { + fprintf(stderr,"invalid user %s: %s\n",username,strerror(errno)); + return 1; + } + if(groupname) + { + grp = getgrnam(groupname); + endgrent(); + if(!grp) + { + fprintf(stderr,"invalid group %s: %s\n",groupname,strerror(errno)); + return 1; + } + } + + ngid = user->pw_gid; + if (grp) + ngid = grp->gr_gid; + + if (setgid(ngid) < 0) + { + fprintf(stderr,"cannot set group id %u: %s\n", (unsigned int)ngid, strerror(errno)); + return 1; + } + if (!getuid()) + { + if (initgroups(user->pw_name, ngid) < 0) + { + fprintf(stderr,"cannot set supplemental group ids for user %s: %s\n", + user->pw_name, strerror(errno)); + return 1; + } + } + if (setuid(user->pw_uid) < 0) + { + fprintf(stderr,"cannot set user id %u: %s\n", + (unsigned int)user->pw_uid, strerror(errno)); + return 1; + } + return 0; +} + +#ifdef WITH_CAPABILITIES +// drop all capabilities except cap_sys_time +static int set_cap_sys_time(void) +{ + cap_t caps; + + caps = cap_from_text("= cap_sys_time=ep"); + if(!caps) + { + perror("cap_from_text"); + return -1; + } + + if( cap_set_proc(caps) == -1 ) + { + perror("cap_set_proc"); + cap_free(caps); + return -1; + } + + cap_free(caps); + + return 0; +} + +// keep capabilities during setuid() +static inline int set_keepcaps(void) +{ + return prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); +} + +static inline int set_nokeepcaps(void) +{ + return prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0); +} +#else +static inline int return0() { return 0; } +#define printcap() return0() +#define set_cap_sys_time() return0() +#define set_keepcaps() return0() +#define set_nokeepcaps() return0() +#endif + int main(int argc, char *argv[]) { // Save terminal settings: @@ -106,6 +210,8 @@ const char *Terminal = NULL; const char *Shutdown = NULL; cPluginManager PluginManager(DEFAULTPLUGINDIR); + const char* username = NULL; + const char* groupname = NULL; static struct option long_options[] = { { "audio", required_argument, NULL, 'a' }, @@ -125,11 +231,13 @@ { "version", no_argument, NULL, 'V' }, { "video", required_argument, NULL, 'v' }, { "watchdog", required_argument, NULL, 'w' }, + { "user", required_argument, NULL, 'u' }, + { "group", required_argument, NULL, 'g' }, { NULL } }; int c; - while ((c = getopt_long(argc, argv, "a:c:dD:E:hl:L:mp:P:r:s:t:v:Vw:", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "a:c:dD:E:hl:L:mp:P:r:s:t:v:Vw:u:g:", long_options, NULL)) != -1) { switch (c) { case 'a': AudioCommand = optarg; break; @@ -219,6 +327,10 @@ fprintf(stderr, "vdr: invalid watchdog timeout: %s\n", optarg); return 2; break; + case 'u': username = optarg; + break; + case 'g': groupname = optarg; + break; default: return 2; } } @@ -260,6 +372,8 @@ " -V, --version print version information and exit\n" " -w SEC, --watchdog=SEC activate the watchdog timer with a timeout of SEC\n" " seconds (default: %d); '0' disables the watchdog\n" + " -u USER, --user=USER run as user USER instead of root\n" + " -g GROUP, --group=GROUP use group GROUP instead of primary group of user\n" "\n", cSIProcessor::GetEpgDataFileName() ? cSIProcessor::GetEpgDataFileName() : "'-'", DEFAULTPLUGINDIR, @@ -290,6 +404,17 @@ return 0; } + if(username && set_keepcaps() != 0) + return 2; + + if (su(username, groupname) != 0) + return 2; + + if(username && set_nokeepcaps() != 0) + return 2; + + set_cap_sys_time(); + // Log file: if (SysLogLevel > 0) -- (o_ Ludwig.Nussel@gmx.de //\ PGP Key ID: FF8135CE V_/_ ICQ: 52166811
Attachment:
pgp00017.pgp
Description: PGP signature