Fix packaging
[platform/upstream/libcap-ng.git] / src / cap-ng.c
index b68aa62..d178e35 100644 (file)
@@ -1,5 +1,5 @@
 /* libcap-ng.c --
- * Copyright 2009-10 Red Hat Inc., Durham, North Carolina.
+ * Copyright 2009-10, 2013, 2017, 2020-21 Red Hat Inc.
  * All Rights Reserved.
  *
  * This library is free software; you can redistribute it and/or
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; see the file COPYING.LIB. If not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1335, USA.
  *
  * Authors:
  *      Steve Grubb <sgrubb@redhat.com>
 #include <stdio_ext.h>
 #include <stdlib.h>
 #include <sys/prctl.h>
+#include <pwd.h>
 #include <grp.h>
 #include <sys/stat.h>
-#include <stdarg.h>
 #include <errno.h>
+#include <fcntl.h>
+#include <endian.h>
 #include <byteswap.h>
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>   // For pthread_atfork
+#endif
 #ifdef HAVE_SYSCALL_H
 #include <sys/syscall.h>
 #endif
 #ifdef HAVE_LINUX_SECUREBITS_H
 #include <linux/securebits.h>
 #endif
+#ifdef HAVE_LINUX_MAGIC_H
+#include <sys/vfs.h>
+#include <linux/magic.h>
+#endif
 
+# define hidden __attribute__ ((visibility ("hidden")))
+unsigned int last_cap hidden = 0;
 /*
  * Some milestones of when things became available:
  * 2.6.24 kernel       XATTR_NAME_CAPS
  * 2.6.25 kernel       PR_CAPBSET_DROP, CAPABILITY_VERSION_2
  * 2.6.26 kernel       PR_SET_SECUREBITS, SECURE_*_LOCKED, VERSION_3
+ * 3.5    kernel       PR_SET_NO_NEW_PRIVS
+ * 4.3    kernel       PR_CAP_AMBIENT
+ * 4.14   kernel       VFS_CAP_REVISION_3
  */
+#ifdef PR_CAPBSET_DROP
+static int HAVE_PR_CAPBSET_DROP = 0;
+#endif
+#ifdef PR_SET_SECUREBITS
+static int HAVE_PR_SET_SECUREBITS = 0;
+#endif
+#ifdef PR_SET_NO_NEW_PRIVS
+static int HAVE_PR_SET_NO_NEW_PRIVS = 0;
+#endif
+#ifdef PR_CAP_AMBIENT
+static int HAVE_PR_CAP_AMBIENT = 0;
+#endif
 
 /* External syscall prototypes */
 extern int capset(cap_user_header_t header, cap_user_data_t data);
 extern int capget(cap_user_header_t header, const cap_user_data_t data);
 
+// Local functions
+static void update_bounding_set(capng_act_t action, unsigned int capability,
+       unsigned int idx);
+static void update_ambient_set(capng_act_t action, unsigned int capability,
+       unsigned int idx);
+
 // Local defines
 #define MASK(x) (1U << (x))
 #ifdef PR_CAPBSET_DROP
-#define UPPER_MASK ~(unsigned)((~0U)<<(CAP_LAST_CAP-31))
+#define UPPER_MASK ~((~0U)<<(last_cap-31))
 #else
 // For v1 systems UPPER_MASK will never be used
 #define UPPER_MASK (unsigned)(~0U)
@@ -62,16 +95,22 @@ extern int capget(cap_user_header_t header, const cap_user_data_t data);
 
 // Re-define cap_valid so its uniform between V1 and V3
 #undef cap_valid
-#define cap_valid(x) ((x) <= CAP_LAST_CAP)
+#define cap_valid(x) ((x) <= last_cap)
 
 // If we don't have the xattr library, then we can't
 // compile-in file system capabilities
-#ifndef HAVE_ATTR_XATTR_H
+#if !defined(HAVE_ATTR_XATTR_H) && !defined (HAVE_SYS_XATTR_H)
 #undef VFS_CAP_U32
 #endif
 
 #ifdef VFS_CAP_U32
- #include <attr/xattr.h>
+ #ifdef HAVE_SYS_XATTR_H
+   #include <sys/xattr.h>
+ #else
+  #ifdef HAVE_ATTR_XATTR_H
+   #include <attr/xattr.h>
+  #endif
+ #endif
  #if __BYTE_ORDER == __BIG_ENDIAN
   #define FIXUP(x) bswap_32(x)
  #else
@@ -110,12 +149,21 @@ extern int capget(cap_user_header_t header, const cap_user_data_t data);
 #endif
 /* Setuid apps run by uid 0 don't get caps back */
 #ifndef SECURE_NO_SETUID_FIXUP
-#define SECURE_NO_SETUID_FIXUP          2  
+#define SECURE_NO_SETUID_FIXUP          2
 #endif
 #ifndef SECURE_NO_SETUID_FIXUP_LOCKED
 #define SECURE_NO_SETUID_FIXUP_LOCKED   3  /* make bit-2 immutable */
 #endif
 
+#ifndef VFS_CAP_U32
+#define VFS_CAP_U32 2
+#endif
+
+#if (VFS_CAP_U32 != 2)
+#error VFS_CAP_U32 does not match the library, you need a new version
+#endif
+
+
 // States: new, allocated, initted, updated, applied
 typedef enum { CAPNG_NEW, CAPNG_ERROR, CAPNG_ALLOCATED, CAPNG_INIT,
        CAPNG_UPDATED, CAPNG_APPLIED } capng_states_t;
@@ -123,29 +171,133 @@ typedef enum { CAPNG_NEW, CAPNG_ERROR, CAPNG_ALLOCATED, CAPNG_INIT,
 // Create an easy data struct out of the kernel definitions
 typedef union {
        struct __user_cap_data_struct v1;
-       struct __user_cap_data_struct v3[2];
+       struct __user_cap_data_struct v3[VFS_CAP_U32];
 } cap_data_t;
 
 // This struct keeps all state info
 struct cap_ng
 {
        int cap_ver;
+       int vfs_cap_ver;
        struct __user_cap_header_struct hdr;
        cap_data_t data;
        capng_states_t state;
-       __u32 bounds[2];
+       __le32 rootid;
+       __u32 bounds[VFS_CAP_U32];
+       __u32 ambient[VFS_CAP_U32];
 };
 
 // Global variables with per thread uniqueness
-static __thread struct cap_ng m =      { 1,
+static __thread struct cap_ng m =      { 1, 1,
                                        {0, 0},
                                        { {0, 0, 0} },
-                                       CAPNG_NEW,
+                                       CAPNG_NEW, CAPNG_UNSET_ROOTID,
+                                       {0, 0},
                                        {0, 0} };
 
+/*
+ * Reset the state so that init gets called to erase everything
+ */
+static void deinit(void)
+{
+       m.state = CAPNG_NEW;
+}
+
+static inline int test_cap(unsigned int cap)
+{
+       // prctl returns 0 or 1 for valid caps, -1 otherwise
+       return prctl(PR_CAPBSET_READ, cap) >= 0;
+}
+
+// The maximum cap value is determined by VFS_CAP_U32
+#define MAX_CAP_VALUE (VFS_CAP_U32 * sizeof(__le32) * 8)
+
+static void init_lib(void) __attribute__ ((constructor));
+static void init_lib(void)
+{
+       // This is so dynamic libraries don't re-init
+       static unsigned int run_once = 0;
+       if (run_once)
+               return;
+       run_once = 1;
+
+#ifdef HAVE_PTHREAD_H
+       pthread_atfork(NULL, NULL, deinit);
+#endif
+       // Detect last cap
+       if (last_cap == 0) {
+               int fd;
+
+               // Try to read last cap from procfs
+               fd = open("/proc/sys/kernel/cap_last_cap", O_RDONLY);
+               if (fd >= 0) {
+#ifdef HAVE_LINUX_MAGIC_H
+                       struct statfs st;
+                       // Bail out if procfs is invalid or fstatfs fails
+                       if (fstatfs(fd, &st) || st.f_type != PROC_SUPER_MAGIC)
+                               goto fail;
+#endif
+                       char buf[8];
+                       int num = read(fd, buf, sizeof(buf) - 1);
+                       if (num > 0) {
+                               buf[num] = 0;
+                               errno = 0;
+                               unsigned int val = strtoul(buf, NULL, 10);
+                               if (errno == 0)
+                                       last_cap = val;
+                       }
+fail:
+                       close(fd);
+               }
+               // Run a binary search over capabilities
+               if (last_cap == 0) {
+                       // starting with last_cap=MAX_CAP_VALUE means we always know
+                       // that cap1 is invalid after the first iteration
+                       last_cap = MAX_CAP_VALUE;
+                       unsigned int cap0 = 0, cap1 = MAX_CAP_VALUE;
+
+                       while (cap0 < last_cap) {
+                               if (test_cap(last_cap))
+                                       cap0 = last_cap;
+                               else
+                                       cap1 = last_cap;
+
+                               last_cap = (cap0 + cap1) / 2U;
+                       }
+               }
+       }
+       // Detect prctl options at runtime
+#ifdef PR_CAPBSET_DROP
+       errno = 0;
+       prctl(PR_CAPBSET_READ, 0, 0, 0, 0);
+       if (!errno)
+               HAVE_PR_CAPBSET_DROP = 1;
+#endif
+#ifdef PR_SET_SECUREBITS
+       errno = 0;
+       prctl(PR_GET_SECUREBITS, 0, 0, 0, 0);
+       if (!errno)
+               HAVE_PR_SET_SECUREBITS = 1;
+#endif
+#ifdef PR_SET_NO_NEW_PRIVS
+       errno = 0;
+       prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0);
+       if (!errno)
+               HAVE_PR_SET_NO_NEW_PRIVS = 1;
+#endif
+#ifdef PR_CAP_AMBIENT
+       errno = 0;
+       prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, 0, 0, 0);
+       if (!errno)
+               HAVE_PR_CAP_AMBIENT = 1;
+#endif
+}
 
 static void init(void)
 {
+       // This is so static libs get initialized
+       init_lib();
+
        if (m.state != CAPNG_NEW)
                return;
 
@@ -161,12 +313,19 @@ static void init(void)
                return;
        }
 
+#if VFS_CAP_REVISION == VFS_CAP_REVISION_1
+       m.vfs_cap_ver = 1;
+#else
+       m.vfs_cap_ver = 2; // Intentionally set to 2 for both 2 & 3
+#endif
+
        memset(&m.data, 0, sizeof(cap_data_t));
 #ifdef HAVE_SYSCALL_H
        m.hdr.pid = (unsigned)syscall(__NR_gettid);
 #else
        m.hdr.pid = (unsigned)getpid();
 #endif
+       m.rootid = CAPNG_UNSET_ROOTID;
        m.state = CAPNG_ALLOCATED;
 }
 
@@ -180,8 +339,16 @@ void capng_clear(capng_select_t set)
        if (set & CAPNG_SELECT_CAPS)
                memset(&m.data, 0, sizeof(cap_data_t));
 #ifdef PR_CAPBSET_DROP
+if (HAVE_PR_CAPBSET_DROP) {
        if (set & CAPNG_SELECT_BOUNDS)
                memset(m.bounds, 0, sizeof(m.bounds));
+}
+#endif
+#ifdef PR_CAP_AMBIENT
+if (HAVE_PR_CAP_AMBIENT) {
+       if (set & CAPNG_SELECT_AMBIENT)
+               memset(m.ambient, 0, sizeof(m.ambient));
+}
 #endif
        m.state = CAPNG_INIT;
 }
@@ -208,11 +375,22 @@ void capng_fill(capng_select_t set)
                }
        }
 #ifdef PR_CAPBSET_DROP
+if (HAVE_PR_CAPBSET_DROP) {
        if (set & CAPNG_SELECT_BOUNDS) {
                unsigned i;
                for (i=0; i<sizeof(m.bounds)/sizeof(__u32); i++)
                        m.bounds[i] = 0xFFFFFFFFU;
        }
+}
+#endif
+#ifdef PR_CAP_AMBIENT
+if (HAVE_PR_CAP_AMBIENT) {
+       if (set & CAPNG_SELECT_AMBIENT) {
+               unsigned i;
+               for (i=0; i<sizeof(m.ambient)/sizeof(__u32); i++)
+                       m.ambient[i] = 0xFFFFFFFFU;
+       }
+}
 #endif
        m.state = CAPNG_INIT;
 }
@@ -227,34 +405,128 @@ void capng_setpid(int pid)
        m.hdr.pid = pid;
 }
 
+int capng_get_rootid(void)
+{
+#ifdef VFS_CAP_REVISION_3
+       return m.rootid;
+#else
+       return CAPNG_UNSET_ROOTID;
+#endif
+}
+
+int capng_set_rootid(int rootid)
+{
+#ifdef VFS_CAP_REVISION_3
+       if (m.state == CAPNG_NEW)
+               init();
+       if (m.state == CAPNG_ERROR)
+               return -1;
+
+       if (rootid < 0)
+               return -1;
+
+       m.rootid = rootid;
+       m.vfs_cap_ver = 3;
+
+       return 0;
+#else
+       return -1;
+#endif
+}
+
 #ifdef PR_CAPBSET_DROP
 static int get_bounding_set(void)
 {
        char buf[64];
        FILE *f;
+       int rc;
 
-       snprintf(buf, sizeof(buf), "/proc/%u/status", m.hdr.pid ? m.hdr.pid :
+       snprintf(buf, sizeof(buf), "/proc/%d/status", m.hdr.pid ? m.hdr.pid :
 #ifdef HAVE_SYSCALL_H
-               (unsigned)syscall(__NR_gettid));
+               (int)syscall(__NR_gettid));
 #else
-               (unsigned)getpid();
+               (int)getpid();
 #endif
-       f = fopen(buf, "r");
-       if (f == NULL)
-               return -1;
-       __fsetlocking(f, FSETLOCKING_BYCALLER);
-       while (fgets(buf, sizeof(buf), f)) {
-               if (strncmp(buf, "CapB", 4))
-                       continue;
-               sscanf(buf, "CapBnd:  %08x%08x", &m.bounds[1], &m.bounds[0]);
+       f = fopen(buf, "re");
+       if (f) {
+               __fsetlocking(f, FSETLOCKING_BYCALLER);
+               while (fgets(buf, sizeof(buf), f)) {
+                       if (strncmp(buf, "CapB", 4))
+                               continue;
+                       sscanf(buf, "CapBnd:  %08x%08x",
+                              &m.bounds[1], &m.bounds[0]);
+                       fclose(f);
+                       return 0;
+               }
+               // Didn't find bounding set, fall through and try prctl way
                fclose(f);
-               return 0;
        }
-       fclose(f);
-       return -1;
+       // Might be in a container with no procfs - do it the hard way
+       memset(m.bounds, 0, sizeof(m.bounds));
+       unsigned int i = 0;
+       do {
+               rc = prctl(PR_CAPBSET_READ, i, 0, 0, 0);
+               if (rc < 0)
+                       return -1;
+
+               // Just add set bits
+               if (rc)
+                       update_bounding_set(CAPNG_ADD, i%32, i>>5);
+               i++;
+       } while (cap_valid(i));
+
+       return 0;
+}
+#endif
+
+#ifdef PR_CAP_AMBIENT
+static int get_ambient_set(void)
+{
+       char buf[64];
+       FILE *f;
+       int rc;
+
+       snprintf(buf, sizeof(buf), "/proc/%d/status", m.hdr.pid ? m.hdr.pid :
+#ifdef HAVE_SYSCALL_H
+               (int)syscall(__NR_gettid));
+#else
+               (int)getpid();
+#endif
+       f = fopen(buf, "re");
+       if (f) {
+               __fsetlocking(f, FSETLOCKING_BYCALLER);
+               while (fgets(buf, sizeof(buf), f)) {
+                       if (strncmp(buf, "CapA", 4))
+                               continue;
+                       sscanf(buf, "CapAmb:  %08x%08x",
+                              &m.ambient[1], &m.ambient[0]);
+                       fclose(f);
+                       return 0;
+               }
+               fclose(f);
+               // Didn't find ambient set, fall through and try prctl way
+       }
+       // Might be in a container with no procfs - do it the hard way
+       memset(m.ambient, 0, sizeof(m.ambient));
+       unsigned int i = 0;
+       do {
+               rc = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, i, 0, 0);
+               if (rc < 0)
+                       return -1;
+
+               // Just add set bits
+               if (rc)
+                       update_ambient_set(CAPNG_ADD, i%32, i>>5);
+               i++;
+       } while (cap_valid(i));
+
+       return 0;
 }
 #endif
 
+/*
+ * Returns 0 on success and -1 on failure
+ */
 int capng_get_caps_process(void)
 {
        int rc;
@@ -268,9 +540,18 @@ int capng_get_caps_process(void)
        if (rc == 0) {
                m.state = CAPNG_INIT;
 #ifdef PR_CAPBSET_DROP
+if (HAVE_PR_CAPBSET_DROP) {
                rc = get_bounding_set();
                if (rc < 0)
                        m.state = CAPNG_ERROR;
+}
+#endif
+#ifdef PR_CAP_AMBIENT
+if (HAVE_PR_CAP_AMBIENT) {
+               rc = get_ambient_set();
+               if (rc < 0)
+                       m.state = CAPNG_ERROR;
+}
 #endif
        }
 
@@ -278,10 +559,14 @@ int capng_get_caps_process(void)
 }
 
 #ifdef VFS_CAP_U32
+#ifdef VFS_CAP_REVISION_3
+static int load_data(const struct vfs_ns_cap_data *filedata, int size)
+#else
 static int load_data(const struct vfs_cap_data *filedata, int size)
+#endif
 {
        unsigned int magic;
-       
+
        if (m.cap_ver == 1)
                return -1;      // Should never get here but just in case
 
@@ -289,15 +574,22 @@ static int load_data(const struct vfs_cap_data *filedata, int size)
        switch (magic & VFS_CAP_REVISION_MASK)
        {
                case VFS_CAP_REVISION_1:
-                       m.cap_ver = 1;
+                       m.vfs_cap_ver = 1;
                        if (size != XATTR_CAPS_SZ_1)
                                return -1;
                        break;
                case VFS_CAP_REVISION_2:
-                       m.cap_ver = 2;
+                       m.vfs_cap_ver = 2;
                        if (size != XATTR_CAPS_SZ_2)
                                return -1;
                        break;
+#ifdef VFS_CAP_REVISION_3
+               case VFS_CAP_REVISION_3:
+                       m.vfs_cap_ver = 3;
+                       if (size != XATTR_CAPS_SZ_3)
+                               return -1;
+                       break;
+#endif
                default:
                        return -1;
        }
@@ -316,6 +608,12 @@ static int load_data(const struct vfs_cap_data *filedata, int size)
                m.data.v3[0].effective = 0;
                m.data.v3[1].effective = 0;
        }
+#ifdef VFS_CAP_REVISION_3
+       if (size == XATTR_CAPS_SZ_3) {
+           struct vfs_ns_cap_data *d = (struct vfs_ns_cap_data *)filedata;
+           m.rootid = FIXUP(d->rootid);
+       }
+#endif
        return 0;
 }
 #endif
@@ -326,8 +624,11 @@ int capng_get_caps_fd(int fd)
        return -1;
 #else
        int rc;
+#ifdef VFS_CAP_REVISION_3
+       struct vfs_ns_cap_data filedata;
+#else
        struct vfs_cap_data filedata;
-
+#endif
        if (m.state == CAPNG_NEW)
                init();
        if (m.state == CAPNG_ERROR)
@@ -340,6 +641,8 @@ int capng_get_caps_fd(int fd)
        rc = load_data(&filedata, rc);
        if (rc == 0)
                m.state = CAPNG_INIT;
+       else
+               m.state = CAPNG_ERROR; // If load data failed, malformed data
 
        return rc;
 #endif
@@ -384,10 +687,25 @@ static void update_bounding_set(capng_act_t action, unsigned int capability,
        unsigned int idx)
 {
 #ifdef PR_CAPBSET_DROP
+if (HAVE_PR_CAPBSET_DROP) {
        if (action == CAPNG_ADD)
                m.bounds[idx] |= MASK(capability);
        else
                m.bounds[idx] &= ~(MASK(capability));
+}
+#endif
+}
+
+static void update_ambient_set(capng_act_t action, unsigned int capability,
+       unsigned int idx)
+{
+#ifdef PR_CAP_AMBIENT
+if (HAVE_PR_CAP_AMBIENT) {
+       if (action == CAPNG_ADD)
+               m.ambient[idx] |= MASK(capability);
+       else
+               m.ambient[idx] &= ~(MASK(capability));
+}
 #endif
 }
 
@@ -409,7 +727,7 @@ int capng_update(capng_act_t action, capng_type_t type, unsigned int capability)
                if (CAPNG_INHERITABLE & type)
                        v1_update(action, capability, &m.data.v1.inheritable);
        } else {
-               int idx;
+               unsigned int idx;
 
                if (capability > 31) {
                        idx = capability>>5;
@@ -425,6 +743,8 @@ int capng_update(capng_act_t action, capng_type_t type, unsigned int capability)
                        update_inheritable(action, capability, idx);
                if (CAPNG_BOUNDING_SET & type)
                        update_bounding_set(action, capability, idx);
+               if (CAPNG_AMBIENT & type)
+                       update_ambient_set(action, capability, idx);
        }
 
        m.state = CAPNG_UPDATED;
@@ -464,51 +784,116 @@ int capng_updatev(capng_act_t action, capng_type_t type,
 
 int capng_apply(capng_select_t set)
 {
-       int rc = -1;
+       int rc = 0;
 
        // Before updating, we expect that the data is initialized to something
        if (m.state < CAPNG_INIT)
                return -1;
 
-       if (set & CAPNG_SELECT_BOUNDS) { 
+       if (set & CAPNG_SELECT_BOUNDS) {
 #ifdef PR_CAPBSET_DROP
-               void *s = capng_save_state();
-               capng_get_caps_process();
+if (HAVE_PR_CAPBSET_DROP) {
+               struct cap_ng state;
+               memcpy(&state, &m, sizeof(state)); /* save state */
+               if (capng_get_caps_process())
+                       return -9;
                if (capng_have_capability(CAPNG_EFFECTIVE, CAP_SETPCAP)) {
-                       int i;
-                       capng_restore_state(&s);
-                       rc = 0;
-                       for (i=0; i <= CAP_LAST_CAP && rc == 0; i++)
+                       unsigned int i;
+                       memcpy(&m, &state, sizeof(m)); /* restore state */
+                       for (i=0; i <= last_cap; i++) {
                                if (capng_have_capability(CAPNG_BOUNDING_SET,
-                                                                i) == 0)
-                                       rc = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
-                       if (rc == 0)
-                               m.state = CAPNG_APPLIED;
-               } else
-                       capng_restore_state(&s);
-#else
-               rc = 0;
+                                                                i) == 0) {
+                                   if (prctl(PR_CAPBSET_DROP, i, 0, 0, 0) <0) {
+                                       rc = -2;
+                                       goto try_caps;
+                                   }
+                               }
+                       }
+                       m.state = CAPNG_APPLIED;
+                       if (get_bounding_set() < 0) {
+                               rc = -3;
+                               goto try_caps;
+                       }
+               } else {
+                       memcpy(&m, &state, sizeof(m)); /* restore state */
+                       rc = -4;
+                       goto try_caps;
+               }
+}
 #endif
        }
+
+       // Try caps is here so that if someone had SELECT_BOTH and we blew up
+       // doing the bounding set, we at least try to set any capabilities
+       // before returning in case the caller also doesn't bother checking
+       // the return code.
+try_caps:
        if (set & CAPNG_SELECT_CAPS) {
-               rc = capset((cap_user_header_t)&m.hdr,
-                               (cap_user_data_t)&m.data);
-               if (rc == 0)
+               if (capset((cap_user_header_t)&m.hdr,
+                               (cap_user_data_t)&m.data) == 0)
                        m.state = CAPNG_APPLIED;
+               else
+                       rc = -5;
+       }
+
+       // Most programs do not and should not mess with ambient capabilities.
+       // Instead of returning here if rc is set, we'll let it try to
+       // do something with ambient capabilities in hopes that it's lowering
+       // capabilities. Again, this is for people that don't check their
+       // return codes.
+       //
+       // Do ambient last so that inheritable and permitted are set by the
+       // time we get here.
+       if (set & CAPNG_SELECT_AMBIENT) {
+#ifdef PR_CAP_AMBIENT
+if (HAVE_PR_CAP_AMBIENT) {
+               if (capng_have_capabilities(CAPNG_SELECT_AMBIENT) ==
+                                                               CAPNG_NONE) {
+                       if (prctl(PR_CAP_AMBIENT,
+                                  PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0) < 0) {
+                               rc = -6;
+                               goto out;
+                       }
+               } else {
+                       unsigned int i;
+
+                       // Clear them all
+                       if (prctl(PR_CAP_AMBIENT,
+                                  PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0) < 0) {
+                               rc = -7;
+                               goto out;
+                       }
+                       for (i=0; i <= last_cap; i++) {
+                               if (capng_have_capability(CAPNG_AMBIENT, i))
+                                       if (prctl(PR_CAP_AMBIENT,
+                                           PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0){
+                                               rc = -8;
+                                               goto out;
+                                       }
+                       }
+               }
+               m.state = CAPNG_APPLIED;
+}
+#endif
        }
+out:
        return rc;
 }
 
 #ifdef VFS_CAP_U32
+#ifdef VFS_CAP_REVISION_3
+static int save_data(struct vfs_ns_cap_data *filedata, int *size)
+#else
 static int save_data(struct vfs_cap_data *filedata, int *size)
+#endif
 {
        // Now stuff the data structures
-       if (m.cap_ver == 1) {
+       if (m.vfs_cap_ver == 1) {
                filedata->data[0].permitted = FIXUP(m.data.v1.permitted);
                filedata->data[0].inheritable = FIXUP(m.data.v1.inheritable);
                filedata->magic_etc = FIXUP(VFS_CAP_REVISION_1);
                *size = XATTR_CAPS_SZ_1;
-       } else {
+       } else if (m.vfs_cap_ver == 2 || m.vfs_cap_ver == 3) {
                int eff;
 
                if (m.data.v3[0].effective || m.data.v3[1].effective)
@@ -522,6 +907,15 @@ static int save_data(struct vfs_cap_data *filedata, int *size)
                filedata->magic_etc = FIXUP(VFS_CAP_REVISION_2 | eff);
                *size = XATTR_CAPS_SZ_2;
        }
+#ifdef VFS_CAP_REVISION_3
+       if (m.vfs_cap_ver == 3) {
+               // Kernel doesn't support namespaces with non-0 rootid
+               if (m.rootid!= 0)
+                       return -1;
+               filedata->rootid = FIXUP(m.rootid);
+               *size = XATTR_CAPS_SZ_3;
+       }
+#endif
 
        return 0;
 }
@@ -532,8 +926,12 @@ int capng_apply_caps_fd(int fd)
 #ifndef VFS_CAP_U32
        return -1;
 #else
-       int rc, size;
+       int rc, size = 0;
+#ifdef VFS_CAP_REVISION_3
+       struct vfs_ns_cap_data filedata;
+#else
        struct vfs_cap_data filedata;
+#endif
        struct stat buf;
 
        // Before updating, we expect that the data is initialized to something
@@ -549,7 +947,11 @@ int capng_apply_caps_fd(int fd)
        if (capng_have_capabilities(CAPNG_SELECT_CAPS) == CAPNG_NONE)
                rc = fremovexattr(fd, XATTR_NAME_CAPS);
        else {
-               save_data(&filedata, &size);
+               if (save_data(&filedata, &size)) {
+                       m.state = CAPNG_ERROR;
+                       errno = EINVAL;
+                       return -2;
+               }
                rc = fsetxattr(fd, XATTR_NAME_CAPS, &filedata, size, 0);
        }
 
@@ -560,11 +962,11 @@ int capng_apply_caps_fd(int fd)
 #endif
 }
 
-// Change uids keeping/removing only certain capabilities 
+// Change uids keeping/removing only certain capabilities
 // flag to drop supp groups
 int capng_change_id(int uid, int gid, capng_flags_t flag)
 {
-       int rc, need_setgid, need_setuid;
+       int rc, ret, need_setgid, need_setuid;
 
        // Before updating, we expect that the data is initialized to something
        if (m.state < CAPNG_INIT)
@@ -572,11 +974,13 @@ int capng_change_id(int uid, int gid, capng_flags_t flag)
 
        // Check the current capabilities
 #ifdef PR_CAPBSET_DROP
+if (HAVE_PR_CAPBSET_DROP) {
        // If newer kernel, we need setpcap to change the bounding set
-       if (capng_have_capability(CAPNG_EFFECTIVE, CAP_SETPCAP) == 0 && 
+       if (capng_have_capability(CAPNG_EFFECTIVE, CAP_SETPCAP) == 0 &&
                                        flag & CAPNG_CLEAR_BOUNDING)
                capng_update(CAPNG_ADD,
                                CAPNG_EFFECTIVE|CAPNG_PERMITTED, CAP_SETPCAP);
+}
 #endif
        if (gid == -1 || capng_have_capability(CAPNG_EFFECTIVE, CAP_SETGID))
                need_setgid = 0;
@@ -599,35 +1003,67 @@ int capng_change_id(int uid, int gid, capng_flags_t flag)
 
        // Change to the temp capabilities
        rc = capng_apply(CAPNG_SELECT_CAPS);
-       if (rc < 0)
-               return -3;
+       if (rc < 0) {
+               ret = -3;
+               goto err_out;
+       }
+
+       // If we are clearing ambient, only clear since its applied at the end
+       if (flag & CAPNG_CLEAR_AMBIENT)
+               capng_clear(CAPNG_SELECT_AMBIENT);
 
        // Clear bounding set if needed while we have CAP_SETPCAP
        if (flag & CAPNG_CLEAR_BOUNDING) {
-               capng_clear(CAPNG_BOUNDING_SET);
+               capng_clear(CAPNG_SELECT_BOUNDS);
                rc = capng_apply(CAPNG_SELECT_BOUNDS);
-               if (rc)
-                       return -8;
+               if (rc) {
+                       ret = -8;
+                       goto err_out;
+               }
        }
 
        // Change gid
        if (gid != -1) {
                rc = setresgid(gid, gid, gid);
-               if (rc)
-                       return -4;
+               if (rc) {
+                       ret = -4;
+                       goto err_out;
+               }
+       }
+
+       // See if we need to init supplemental groups
+       if ((flag & CAPNG_INIT_SUPP_GRP) && uid != -1) {
+               struct passwd *pw = getpwuid(uid);
+               if (pw == NULL) {
+                       ret = -10;
+                       goto err_out;
+               }
+               if (gid != -1) {
+                       if (initgroups(pw->pw_name, gid)) {
+                               ret = -5;
+                               goto err_out;
+                       }
+               } else if (initgroups(pw->pw_name, pw->pw_gid)) {
+                       ret = -5;
+                       goto err_out;
+               }
        }
 
        // See if we need to unload supplemental groups
        if ((flag & CAPNG_DROP_SUPP_GRP) && gid != -1) {
-               if (setgroups(0, NULL))
-                       return -5;
+               if (setgroups(0, NULL)) {
+                       ret = -5;
+                       goto err_out;
+               }
        }
 
        // Change uid
        if (uid != -1) {
                rc = setresuid(uid, uid, uid);
-               if (rc)
-                       return -6;
+               if (rc) {
+                       ret = -6;
+                       goto err_out;
+               }
        }
 
        // Tell it we are done keeping capabilities
@@ -646,25 +1082,38 @@ int capng_change_id(int uid, int gid, capng_flags_t flag)
        // Now drop setpcap & apply
        capng_update(CAPNG_DROP, CAPNG_EFFECTIVE|CAPNG_PERMITTED,
                                CAP_SETPCAP);
-       rc = capng_apply(CAPNG_SELECT_CAPS);
+       rc = capng_apply(CAPNG_SELECT_CAPS|CAPNG_SELECT_AMBIENT);
        if (rc < 0)
                return -9;
 
        // Done
        m.state = CAPNG_UPDATED;
        return 0;
+
+err_out:
+       prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0);
+       return ret;
 }
 
 int capng_lock(void)
 {
+       // If either fail, return -1 since something is not right
 #ifdef PR_SET_SECUREBITS
+if (HAVE_PR_SET_SECUREBITS) {
        int rc = prctl(PR_SET_SECUREBITS,
                        1 << SECURE_NOROOT |
                        1 << SECURE_NOROOT_LOCKED |
                        1 << SECURE_NO_SETUID_FIXUP |
                        1 << SECURE_NO_SETUID_FIXUP_LOCKED, 0, 0, 0);
+#ifdef PR_SET_NO_NEW_PRIVS
+if (HAVE_PR_SET_NO_NEW_PRIVS) {
+       if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
+               return -1;
+}
+#endif
        if (rc)
                return -1;
+}
 #endif
 
        return 0;
@@ -676,8 +1125,10 @@ capng_results_t capng_have_capabilities(capng_select_t set)
        int empty = 0, full = 0;
 
        // First, try to init with current set
-       if (m.state < CAPNG_INIT)
-               capng_get_caps_process();
+       if (m.state < CAPNG_INIT) {
+               if (capng_get_caps_process())
+                       return CAPNG_FAIL;
+       }
 
        // If we still don't have anything, error out
        if (m.state < CAPNG_INIT)
@@ -702,16 +1153,17 @@ capng_results_t capng_have_capabilities(capng_select_t set)
                                full = 1;
                        else
                                return CAPNG_PARTIAL;
-                       if ((m.data.v3[1].effective & UPPER_MASK) == 0)
+                       if ((m.data.v3[1].effective & UPPER_MASK) == 0 && !full)
                                empty = 1;
                        else if ((m.data.v3[1].effective & UPPER_MASK) ==
-                                                       UPPER_MASK)
+                                               UPPER_MASK && !empty)
                                full = 1;
                        else
                                return CAPNG_PARTIAL;
                }
        }
 #ifdef PR_CAPBSET_DROP
+if (HAVE_PR_CAPBSET_DROP) {
        if (set & CAPNG_SELECT_BOUNDS) {
                if (m.bounds[0] == 0)
                        empty = 1;
@@ -726,13 +1178,70 @@ capng_results_t capng_have_capabilities(capng_select_t set)
                else
                        return CAPNG_PARTIAL;
        }
-#endif 
+} else
+       empty = 1;
+#endif
+#ifdef PR_CAP_AMBIENT
+if (HAVE_PR_CAP_AMBIENT) {
+       if (set & CAPNG_SELECT_AMBIENT) {
+               if (m.ambient[0] == 0)
+                       empty = 1;
+               else if (m.ambient[0] == 0xFFFFFFFFU)
+                       full = 1;
+               else
+                       return CAPNG_PARTIAL;
+               if ((m.ambient[1] & UPPER_MASK) == 0)
+                       empty = 1;
+               else if ((m.ambient[1] & UPPER_MASK) == UPPER_MASK)
+                       full = 1;
+               else
+                       return CAPNG_PARTIAL;
+       }
+} else
+       empty = 1;
+#endif
+       if (empty == 1 && full == 0)
+               return CAPNG_NONE;
+       else if (empty == 0 && full == 1)
+               return CAPNG_FULL;
+
+       return CAPNG_PARTIAL;
+}
+
+// -1 - error, 0 - no caps, 1 partial caps, 2 full caps
+capng_results_t capng_have_permitted_capabilities(void)
+{
+       int empty = 0, full = 0;
+
+       // First, try to init with current set
+       if (m.state < CAPNG_INIT) {
+               if (capng_get_caps_process())
+                       return CAPNG_FAIL;
+       }
+
+       // If we still don't have anything, error out
+       if (m.state < CAPNG_INIT)
+               return CAPNG_FAIL;
+
+       if (m.data.v3[0].permitted == 0)
+               empty = 1;
+       else if (m.data.v3[0].permitted == 0xFFFFFFFFU)
+               full = 1;
+       else
+               return CAPNG_PARTIAL;
+
+       if ((m.data.v3[1].permitted & UPPER_MASK) == 0 && !full)
+               empty = 1;
+       else if ((m.data.v3[1].permitted & UPPER_MASK) == UPPER_MASK && !empty)
+               full = 1;
+       else
+               return CAPNG_PARTIAL;
 
        if (empty == 1 && full == 0)
                return CAPNG_NONE;
        else if (empty == 0 && full == 1)
                return CAPNG_FULL;
-       
+
        return CAPNG_PARTIAL;
 }
 
@@ -754,10 +1263,21 @@ static int check_inheritable(unsigned int capability, unsigned int idx)
 static int bounds_bit_check(unsigned int capability, unsigned int idx)
 {
 #ifdef PR_CAPBSET_DROP
+if (HAVE_PR_CAPBSET_DROP) {
        return MASK(capability) & m.bounds[idx] ? 1 : 0;
-#else
+}
+#endif
        return 0;
+}
+
+static int ambient_bit_check(unsigned int capability, unsigned int idx)
+{
+#ifdef PR_CAP_AMBIENT
+if (HAVE_PR_CAP_AMBIENT) {
+       return MASK(capability) & m.ambient[idx] ? 1 : 0;
+}
 #endif
+       return 0;
 }
 
 static int v1_check(unsigned int capability, __u32 data)
@@ -768,12 +1288,14 @@ static int v1_check(unsigned int capability, __u32 data)
 int capng_have_capability(capng_type_t which, unsigned int capability)
 {
        // First, try to init with current set
-       if (m.state < CAPNG_INIT)
-               capng_get_caps_process();
+       if (m.state < CAPNG_INIT) {
+               if (capng_get_caps_process())
+                       return 0;
+       }
 
        // If we still don't have anything, error out
        if (m.state < CAPNG_INIT)
-               return CAPNG_FAIL;
+               return 0;
        if (m.cap_ver == 1 && capability > 31)
                return 0;
        if (!cap_valid(capability))
@@ -803,6 +1325,8 @@ int capng_have_capability(capng_type_t which, unsigned int capability)
                        return check_inheritable(capability, idx);
                else if (which == CAPNG_BOUNDING_SET)
                        return bounds_bit_check(capability, idx);
+               else if (which == CAPNG_AMBIENT)
+                       return ambient_bit_check(capability, idx);
        }
        return 0;
 }
@@ -833,17 +1357,25 @@ char *capng_print_caps_numeric(capng_print_t where, capng_select_t set)
                                        m.data.v3[0].permitted,
                                        m.data.v3[1].inheritable & UPPER_MASK,
                                        m.data.v3[0].inheritable);
-                       
                        }
                }
 #ifdef PR_CAPBSET_DROP
+if (HAVE_PR_CAPBSET_DROP) {
                if (set & CAPNG_SELECT_BOUNDS)
                        printf("Bounding Set: %08X, %08X\n",
                                m.bounds[1] & UPPER_MASK, m.bounds[0]);
+}
+#endif
+#ifdef PR_CAP_AMBIENT
+if (HAVE_PR_CAP_AMBIENT) {
+               if (set & CAPNG_SELECT_AMBIENT)
+                       printf("Ambient :     %08X, %08X\n",
+                               m.ambient[1] & UPPER_MASK, m.ambient[0]);
+}
 #endif
        } else if (where == CAPNG_PRINT_BUFFER) {
                if (set & CAPNG_SELECT_CAPS) {
-                       // Make it big enough for bounding set, too
+                       // Make it big enough for bounding & ambient set, too
                        ptr = malloc(160);
                        if (m.cap_ver == 1) {
                                snprintf(ptr, 160,
@@ -868,9 +1400,11 @@ char *capng_print_caps_numeric(capng_print_t where, capng_select_t set)
                }
                if (set & CAPNG_SELECT_BOUNDS) {
 #ifdef PR_CAPBSET_DROP
+if (HAVE_PR_CAPBSET_DROP) {
                        char *s;
-                       if (ptr == NULL ){
-                               ptr = malloc(40);
+                       // If ptr is NULL, we only room for bounding and ambient
+                       if (ptr == NULL ) {
+                               ptr = malloc(80);
                                if (ptr == NULL)
                                        return ptr;
                                *ptr = 0;
@@ -879,6 +1413,26 @@ char *capng_print_caps_numeric(capng_print_t where, capng_select_t set)
                                s = ptr + strlen(ptr);
                        snprintf(s, 40, "Bounding Set: %08X, %08X\n",
                                        m.bounds[1] & UPPER_MASK, m.bounds[0]);
+}
+#endif
+               }
+               if (set & CAPNG_SELECT_AMBIENT) {
+#ifdef PR_CAP_AMBIENT
+if (HAVE_PR_CAP_AMBIENT) {
+                       char *s;
+                       // If ptr is NULL, we only room for ambient
+                       if (ptr == NULL ) {
+                               ptr = malloc(40);
+                               if (ptr == NULL)
+                                       return ptr;
+                               *ptr = 0;
+                               s = ptr;
+                       } else
+                               s = ptr + strlen(ptr);
+                       snprintf(s, 40, "Ambient Set: %08X, %08X\n",
+                                       m.ambient[1] & UPPER_MASK,
+                                       m.ambient[0]);
+}
 #endif
                }
        }
@@ -888,16 +1442,17 @@ char *capng_print_caps_numeric(capng_print_t where, capng_select_t set)
 
 char *capng_print_caps_text(capng_print_t where, capng_type_t which)
 {
-       unsigned int i; 
-       int once = 0, cnt = 0;
+       unsigned int i, once = 0, cnt = 0;
        char *ptr = NULL;
 
        if (m.state < CAPNG_INIT)
                return ptr;
 
-       for (i=0; i<=CAP_LAST_CAP; i++) {
+       for (i=0; i<=last_cap; i++) {
                if (capng_have_capability(which, i)) {
                        const char *n = capng_capability_to_name(i);
+                       if (n == NULL)
+                               n = "unknown";
                        if (where == CAPNG_PRINT_STDOUT) {
                                if (once == 0) {
                                        printf("%s", n);
@@ -907,7 +1462,7 @@ char *capng_print_caps_text(capng_print_t where, capng_type_t which)
                        } else if (where == CAPNG_PRINT_BUFFER) {
                                int len;
                                if (once == 0) {
-                                       ptr = malloc(CAP_LAST_CAP*18);
+                                       ptr = malloc(last_cap*20);
                                        if (ptr == NULL)
                                                return ptr;
                                        len = sprintf(ptr+cnt, "%s", n);