capabilities support
authorAndy Green <andy@warmcat.com>
Tue, 6 Jun 2017 22:10:02 +0000 (06:10 +0800)
committerAndy Green <andy@warmcat.com>
Tue, 6 Jun 2017 22:49:20 +0000 (06:49 +0800)
CMakeLists.txt
README.build.md
lib/context.c
lib/libwebsockets.c
lib/libwebsockets.h
lib/lws-plat-unix.c
lib/private-libwebsockets.h
lws_config.h.in

index 2cac774..506a7b5 100644 (file)
@@ -495,6 +495,9 @@ CHECK_INCLUDE_FILE(sys/stat.h LWS_HAVE_SYS_STAT_H)
 CHECK_INCLUDE_FILE(sys/types.h LWS_HAVE_SYS_TYPES_H)
 CHECK_INCLUDE_FILE(unistd.h LWS_HAVE_UNISTD_H)
 CHECK_INCLUDE_FILE(vfork.h LWS_HAVE_VFORK_H)
+CHECK_INCLUDE_FILE(sys/capability.h LWS_HAVE_SYS_CAPABILITY_H)
+
+CHECK_LIBRARY_EXISTS(cap cap_set_flag "" LWS_HAVE_LIBCAP) 
 
 if (LWS_WITH_LIBUV)
 CHECK_INCLUDE_FILE(uv-version.h LWS_HAVE_UV_VERSION_H)
@@ -1016,6 +1019,12 @@ if (UNIX)
        list(APPEND LIB_LIST m)
 endif()
 
+if (LWS_HAVE_LIBCAP)
+       list(APPEND LIB_LIST cap )
+endif()
+
+
+
 # Setup the linking for all libs.
 foreach (lib ${LWS_LIBRARIES})
        target_link_libraries(${lib} ${LIB_LIST})
@@ -1767,6 +1776,8 @@ message(" LWS_WITH_ZIP_FOPS = ${LWS_WITH_ZIP_FOPS}")
 message(" LWS_AVOID_SIGPIPE_IGN = ${LWS_AVOID_SIGPIPE_IGN}")
 message(" LWS_WITH_STATS = ${LWS_WITH_STATS}")
 message(" LWS_WITH_SOCKS5 = ${LWS_WITH_SOCKS5}")
+message(" LWS_HAVE_SYS_CAPABILITY_H = ${LWS_HAVE_SYS_CAPABILITY_H}")
+message(" LWS_HAVE_LIBCAP = ${LWS_HAVE_LIBCAP}")
 
 message("---------------------------------------------------------------------")
 
index ca9c528..dd3494a 100644 (file)
@@ -122,12 +122,25 @@ and libnsl, and only builds in 64bit mode.
        $ make
  ```
 
+@section lcap Linux Capabilities
+
+On Linux, lws now lets you retain selected root capabilities when dropping
+privileges.  If libcap-dev or similar package is installed providing
+sys/capabilities.h, and libcap or similar package is installed providing
+libcap.so, CMake will enable the capability features.
+
+The context creation info struct .caps[] and .count_caps members can then
+be set by user code to enable selected root capabilities to survive the
+transition to running under an unprivileged user.
+
 @section cmq Quirk of cmake
 
 When changing cmake options, for some reason the only way to get it to see the
 changes sometimes is delete the contents of your build directory and do the
 cmake from scratch.
 
+deleting build/CMakeCache.txt may be enough.
+
 
 @section cmw Building on Windows (Visual Studio)
 
index e1d8479..248e29b 100644 (file)
@@ -892,6 +892,11 @@ lws_create_context(struct lws_context_creation_info *info)
        context->uid = info->uid;
        context->gid = info->gid;
 
+#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
+       memcpy(context->caps, info->caps, sizeof(context->caps));
+       context->count_caps = info->count_caps;
+#endif
+
        /*
         * drop any root privs for this process
         * to listen on port < 1023 we would have needed root, but now we are
index 53cb12b..88f283a 100755 (executable)
@@ -2220,6 +2220,11 @@ lws_finalize_startup(struct lws_context *context)
        info.uid = context->uid;
        info.gid = context->gid;
 
+#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
+       memcpy(info.caps, context->caps, sizeof(info.caps));
+       info.count_caps = context->count_caps;
+#endif
+
        if (lws_check_opt(context->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
                lws_plat_drop_app_privileges(&info);
 
index 4866ad3..b738554 100644 (file)
@@ -102,6 +102,9 @@ struct sockaddr_in;
 
 #else /* NOT WIN32 */
 #include <unistd.h>
+#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
+#include <sys/capability.h>
+#endif
 
 #if defined(__NetBSD__) || defined(__FreeBSD__)
 #include <netinet/in.h>
@@ -1976,6 +1979,18 @@ struct lws_context_creation_info {
         * If proxy auth is required, use format "username:password\@server:port" */
        unsigned int socks_proxy_port;
        /**< VHOST: If socks_proxy_address was non-NULL, uses this port */
+#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
+       cap_value_t caps[4];
+       /**< CONTEXT: array holding Linux capabilities you want to
+        * continue to be available to the server after it transitions
+        * to a noprivileged user.  Usually none are needed but for, eg,
+        * .bind_iface, CAP_NET_RAW is required.  This gives you a way
+        * to still have the capability but drop root.
+        */
+       char count_caps;
+       /**< CONTEXT: count of Linux capabilities in .caps[].  0 means
+        * no capabilities will be inherited from root (the default) */
+#endif
 
        /* Add new things just above here ---^
         * This is part of the ABI, don't needlessly break compatibility
index b2679b6..ae73dfc 100644 (file)
@@ -279,9 +279,29 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd)
        return 0;
 }
 
+#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
+static void
+_lws_plat_apply_caps(int mode, cap_value_t *cv, int count)
+{
+       cap_t caps = cap_get_proc();
+
+       if (!count)
+               return;
+
+       cap_set_flag(caps, mode, count, cv, CAP_SET);
+       cap_set_proc(caps);
+       prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+       cap_free(caps);
+}
+#endif
+
 LWS_VISIBLE void
 lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
 {
+#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
+       int n;
+#endif
+
        if (info->gid != -1)
                if (setgid(info->gid))
                        lwsl_warn("setgid: %s\n", strerror(LWS_ERRNO));
@@ -290,11 +310,25 @@ lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
                struct passwd *p = getpwuid(info->uid);
 
                if (p) {
+
+#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
+                       _lws_plat_apply_caps(CAP_PERMITTED, info->caps, info->count_caps);
+#endif
+
                        initgroups(p->pw_name, info->gid);
                        if (setuid(info->uid))
                                lwsl_warn("setuid: %s\n", strerror(LWS_ERRNO));
                        else
                                lwsl_notice("Set privs to user '%s'\n", p->pw_name);
+
+#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
+                       _lws_plat_apply_caps(CAP_EFFECTIVE, info->caps, info->count_caps);
+
+                       if (info->count_caps)
+                               for (n = 0; n < info->count_caps; n++)
+                                       lwsl_notice("   RETAINING CAPABILITY %d\n", (int)info->caps[n]);
+#endif
+
                } else
                        lwsl_warn("getpwuid: unable to find uid %d", info->uid);
        }
index dcee160..48940bd 100644 (file)
@@ -935,6 +935,11 @@ struct lws_context {
        const struct lws_protocol_vhost_options *reject_service_keywords;
        lws_reload_func deprecation_cb;
 
+#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
+       cap_value_t caps[4];
+       char count_caps;
+#endif
+
 #if defined(LWS_USE_LIBEV)
        lws_ev_signal_cb_t * lws_ev_sigint_cb;
 #endif
index c9a0a1f..d7092a1 100644 (file)
 #cmakedefine LWS_WITH_STATS
 #cmakedefine LWS_WITH_SOCKS5
 
+#cmakedefine LWS_HAVE_SYS_CAPABILITY_H
+#cmakedefine LWS_HAVE_LIBCAP
+
 /* OpenSSL various APIs */
 
 #cmakedefine LWS_HAVE_TLS_CLIENT_METHOD