Mailing List archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[vdr] [PATCH] run vdr as user but allow to set the system clock



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


Home | Main Index | Thread Index