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 2cac7749764c8bb35900f834554adeef2230a6f5..506a7b5576d503c9bd4fd7995d6d9bc647d44649 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 ca9c528e0d86aa61a85d00ee8343ee73a1b2c701..dd3494aa17aae6951b52e6f7c92315008c522768 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 e1d84796b7c46b1c8fd78ad27afa76a28988f6c9..248e29b303151a117b6832c689b6f557b9cc310e 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 53cb12b04393167a6f8231e2eefa448f9b94b8e6..88f283a0fd5657e504edc14ac7f31e04b5e6fe83 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 4866ad386fc0476e4cb45f5e0127e4088f620a2d..b7385547da4bbe88046b1845b9fe9e69da4bcd3b 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 b2679b65b92db6c19a8f3a812d46e54d79c01c95..ae73dfc49a2ae330a5f992c368236014fc78ff15 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 dcee16082f32a237d0b12bb639a707f556aed337..48940bd674792c987ef9235b5ff17ca9dd25a629 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 c9a0a1ffa0f4767deb038ca08f198b1994493130..d7092a1aac9e9eec332e855a3cb6273d9b27a1d6 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