Add a function to switch the process UID/GID to a specified user in
order to drop the root privileges, but keep the capabilities needed to
adjust the clock, enable HW timestamping, bind to privileged ports and
raw sockets, using the libcap library.

Add a function to create a directory for a UDS address with a specified
owner where the programs will be able to bind and unlink their sockets
without root privileges.

Signed-off-by: Miroslav Lichvar <mlich...@redhat.com>
---
 incdefs.sh |  11 +++++-
 makefile   |   4 ++
 util.c     | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 util.h     |  19 +++++++++
 4 files changed, 144 insertions(+), 1 deletion(-)

diff --git a/incdefs.sh b/incdefs.sh
index 19e620e..9889059 100755
--- a/incdefs.sh
+++ b/incdefs.sh
@@ -19,7 +19,8 @@
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
 #
-# Look for functional prototypes in the C library.
+# Look for functional prototypes in the C library and development files
+# needed for optional features.
 #
 user_flags()
 {
@@ -50,6 +51,14 @@ user_flags()
                        fi
                done
        done
+
+       # Look for libcap header.
+       for d in $dirs; do
+               if test -a $d/sys/capability.h; then
+                       printf " -DHAVE_LIBCAP"
+                       break
+               fi
+       done
 }
 
 #
diff --git a/makefile b/makefile
index 33e7ca0..a42bd21 100644
--- a/makefile
+++ b/makefile
@@ -43,6 +43,10 @@ incdefs := $(shell $(srcdir)/incdefs.sh)
 version := $(shell $(srcdir)/version.sh $(srcdir))
 VPATH  = $(srcdir)
 
+ifneq (,$(findstring -DHAVE_LIBCAP,$(incdefs)))
+       LDLIBS += -lcap
+endif
+
 prefix = /usr/local
 sbindir        = $(prefix)/sbin
 mandir = $(prefix)/man
diff --git a/util.c b/util.c
index 113467d..6a488d5 100644
--- a/util.c
+++ b/util.c
@@ -18,13 +18,25 @@
  */
 #include <arpa/inet.h>
 #include <errno.h>
+#include <libgen.h>
 #include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef HAVE_LIBCAP
+#include <grp.h>
+#include <pwd.h>
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#include <unistd.h>
+#endif
 
 #include "address.h"
+#include "interface.h"
 #include "phc.h"
 #include "print.h"
 #include "sk.h"
@@ -753,3 +765,102 @@ int rate_limited(int interval, time_t *last)
 
        return 0;
 }
+
+void create_uds_directory(const char *address, const char *user)
+{
+       char path[MAX_IFNAME_SIZE + 1], *dir;
+#ifdef HAVE_LIBCAP
+       struct passwd *pw;
+#endif
+
+       if (snprintf(path, sizeof(path), "%s", address) >= sizeof(path)) {
+               pr_err("path too long for UDS");
+               return;
+       }
+
+       dir = dirname(path);
+
+       /* Don't do anything if it already exists or cannot be created */
+       if (mkdir(dir, 0770)) {
+               if (errno != EEXIST)
+                       pr_err("failed to create %s: %m", dir);
+               return;
+       }
+
+#ifdef HAVE_LIBCAP
+       if (user[0] == '\0')
+               return;
+
+       pw = getpwnam(user);
+       if (!pw) {
+               pr_err("failed to get user/group ID of %s", user);
+               rmdir(dir);
+               return;
+       }
+
+       if (chown(dir, pw->pw_uid, pw->pw_gid)) {
+               pr_err("failed to change owner of %s: %m", dir);
+               rmdir(dir);
+               return;
+       }
+#endif
+}
+
+int drop_root_privileges(const char *user)
+{
+#ifdef HAVE_LIBCAP
+       struct passwd *pw;
+       cap_t cap;
+#endif
+
+       if (user[0] == '\0')
+               return 0;
+
+#ifdef HAVE_LIBCAP
+       pw = getpwnam(user);
+       if (!pw) {
+               pr_err("failed to get user/group ID of %s", user);
+               return -1;
+       }
+
+       if (prctl(PR_SET_KEEPCAPS, 1)) {
+               pr_err("failed to set KEEPCAPS flag");
+               return -1;
+       }
+
+       if (setgroups(0, NULL)) {
+               pr_err("failed to drop supplementary groups");
+               return -1;
+       }
+
+       if (setgid(pw->pw_gid)) {
+               pr_err("failed to set group ID");
+               return -1;
+       }
+
+       if (setuid(pw->pw_uid)) {
+               pr_err("failed to set user ID");
+               return -1;
+       }
+
+       cap = cap_from_text("cap_sys_time,cap_net_admin,"
+                           "cap_net_bind_service,cap_net_raw=ep");
+       if (!cap) {
+               pr_err("failed to initialize capabilities");
+               return -1;
+       }
+
+       if (cap_set_proc(cap)) {
+               pr_err("failed to set process capabilities");
+               cap_free(cap);
+               return -1;
+       }
+
+       cap_free(cap);
+
+       return 0;
+#else
+       pr_err("cannot drop root privileges without libcap");
+       return -1;
+#endif
+}
diff --git a/util.h b/util.h
index 739c8fd..454ab07 100644
--- a/util.h
+++ b/util.h
@@ -455,4 +455,23 @@ void parray_extend(void ***a, ...);
  */
 int rate_limited(int interval, time_t *last);
 
+/**
+ * Create directory for a UDS address owned by the specified user.
+ * Don't do anything if it already exists, even if it has a different owner.
+ *
+ * @param address   UDS address.
+ * @param user      Name of the user.
+ */
+void create_uds_directory(const char *address, const char *user);
+
+/**
+ * Change the user/group ID in order to drop the root privileges. Keep only
+ * capabilities needed to set/adjust the clock, bind to a privileged port,
+ * enable HW timestamping, and open a raw socket.
+ *
+ * @param user      Name of the user.
+ * @return          0 on success, -1 on error.
+ */
+int drop_root_privileges(const char *user);
+
 #endif
-- 
2.26.3



_______________________________________________
Linuxptp-devel mailing list
Linuxptp-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linuxptp-devel

Reply via email to