Libcore: Add support for capabilities

Add support for capget and capset, as well as related prctl constants,
to android.system.Os.

Bug: 36232535
Test: m
Test: manual
Change-Id: I28c741bef2f9e047400dae83b69fc22fd3527498
diff --git a/luni/src/main/java/android/system/Os.java b/luni/src/main/java/android/system/Os.java
index cae8083..7db7f75 100644
--- a/luni/src/main/java/android/system/Os.java
+++ b/luni/src/main/java/android/system/Os.java
@@ -63,6 +63,25 @@
   /** @hide */ public static void bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException { Libcore.os.bind(fd, address); }
 
   /**
+   * See <a href="http://man7.org/linux/man-pages/man2/capget.2.html">capget(2)</a>.
+   *
+   * @hide
+   */
+  public static StructCapUserData[] capget(StructCapUserHeader hdr) throws ErrnoException {
+      return Libcore.os.capget(hdr);
+  }
+
+  /**
+   * See <a href="http://man7.org/linux/man-pages/man2/capset.2.html">capset(2)</a>.
+   *
+   * @hide
+   */
+  public static void capset(StructCapUserHeader hdr, StructCapUserData[] data)
+          throws ErrnoException {
+      Libcore.os.capset(hdr, data);
+  }
+
+  /**
    * See <a href="http://man7.org/linux/man-pages/man2/chmod.2.html">chmod(2)</a>.
    */
   public static void chmod(String path, int mode) throws ErrnoException { Libcore.os.chmod(path, mode); }
diff --git a/luni/src/main/java/android/system/OsConstants.java b/luni/src/main/java/android/system/OsConstants.java
index 10ea52f..adad301 100644
--- a/luni/src/main/java/android/system/OsConstants.java
+++ b/luni/src/main/java/android/system/OsConstants.java
@@ -24,6 +24,20 @@
     }
 
     /**
+     * Returns the index of the element in the cap_user_data array that this capability is stored
+     * in.
+     * @hide
+     */
+    public static int CAP_TO_INDEX(int x) { return x >>> 5; }
+
+    /**
+     * Returns the mask for the given capability. This is relative to the capability's cap_user_data
+     * element, the index of which can be retrieved with CAP_TO_INDEX.
+     * @hide
+     */
+    public static int CAP_TO_MASK(int x) { return 1 << (x & 31); }
+
+    /**
      * Tests whether the given mode is a block device.
      */
     public static boolean S_ISBLK(int mode) { return (mode & S_IFMT) == S_IFBLK; }
@@ -320,6 +334,7 @@
     /** @hide */ public static final int IP_RECVTOS = placeholder();
     public static final int IP_TOS = placeholder();
     public static final int IP_TTL = placeholder();
+    /** @hide */ public static final int _LINUX_CAPABILITY_VERSION_3 = placeholder();
     public static final int MAP_FIXED = placeholder();
     /** @hide */ public static final int MAP_POPULATE = placeholder();
     public static final int MAP_PRIVATE = placeholder();
@@ -372,6 +387,8 @@
     public static final int POLLRDNORM = placeholder();
     public static final int POLLWRBAND = placeholder();
     public static final int POLLWRNORM = placeholder();
+    /** @hide */ public static final int PR_CAP_AMBIENT = placeholder();
+    /** @hide */ public static final int PR_CAP_AMBIENT_RAISE = placeholder();
     public static final int PR_GET_DUMPABLE = placeholder();
     public static final int PR_SET_DUMPABLE = placeholder();
     public static final int PR_SET_NO_NEW_PRIVS = placeholder();
diff --git a/luni/src/main/java/android/system/StructCapUserData.java b/luni/src/main/java/android/system/StructCapUserData.java
new file mode 100644
index 0000000..af63caf
--- /dev/null
+++ b/luni/src/main/java/android/system/StructCapUserData.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.system;
+
+import libcore.util.Objects;
+
+/**
+ * Corresponds to Linux' __user_cap_data_struct for capget and capset.
+ *
+ * @hide
+ */
+public final class StructCapUserData {
+  /** Effective capability mask. */
+  public final int effective; /* __u32 */
+
+  /** Permitted capability mask. */
+  public final int permitted; /* __u32 */
+
+  /** Inheritable capability mask. */
+  public final int inheritable; /* __u32 */
+
+  /**
+   * Constructs an instance with the given field values.
+   */
+  public StructCapUserData(int effective, int permitted, int inheritable) {
+    this.effective = effective;
+    this.permitted = permitted;
+    this.inheritable = inheritable;
+  }
+
+  @Override public String toString() {
+    return Objects.toString(this);
+  }
+}
diff --git a/luni/src/main/java/android/system/StructCapUserHeader.java b/luni/src/main/java/android/system/StructCapUserHeader.java
new file mode 100644
index 0000000..abbb395
--- /dev/null
+++ b/luni/src/main/java/android/system/StructCapUserHeader.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.system;
+
+import libcore.util.Objects;
+
+/**
+ * Corresponds to Linux' __user_cap_header_struct for capget and capset.
+ *
+ * @hide
+ */
+public final class StructCapUserHeader {
+  /**
+   * Version of the header. Note this is not final as capget() may mutate the field when an
+   * invalid version is provided. See
+   * <a href="http://man7.org/linux/man-pages/man2/capget.2.html">capget(2)</a>.
+   */
+  public int version; /* __u32 */
+
+  /** Pid of the header. The pid a call applies to. */
+  public final int pid;
+
+  /**
+   * Constructs an instance with the given field values.
+   */
+  public StructCapUserHeader(int version, int pid) {
+    this.version = version;
+    this.pid = pid;
+  }
+
+  @Override public String toString() {
+    return Objects.toString(this);
+  }
+}
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
index ac805e8..55d4d82 100644
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -19,6 +19,8 @@
 import android.system.ErrnoException;
 import android.system.GaiException;
 import android.system.StructAddrinfo;
+import android.system.StructCapUserData;
+import android.system.StructCapUserHeader;
 import android.system.StructFlock;
 import android.system.StructGroupReq;
 import android.system.StructGroupSourceReq;
@@ -56,6 +58,14 @@
     public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException { return os.android_getaddrinfo(node, hints, netId); }
     public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { os.bind(fd, address, port); }
     public void bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException { os.bind(fd, address); }
+    @Override
+    public StructCapUserData[] capget(StructCapUserHeader hdr) throws ErrnoException {
+        return os.capget(hdr);
+    }
+    @Override
+    public void capset(StructCapUserHeader hdr, StructCapUserData[] data) throws ErrnoException {
+        os.capset(hdr, data);
+    }
     public void chmod(String path, int mode) throws ErrnoException { os.chmod(path, mode); }
     public void chown(String path, int uid, int gid) throws ErrnoException { os.chown(path, uid, gid); }
     public void close(FileDescriptor fd) throws ErrnoException { os.close(fd); }
diff --git a/luni/src/main/java/libcore/io/Linux.java b/luni/src/main/java/libcore/io/Linux.java
index cbd7b25..09adb09 100644
--- a/luni/src/main/java/libcore/io/Linux.java
+++ b/luni/src/main/java/libcore/io/Linux.java
@@ -19,6 +19,8 @@
 import android.system.ErrnoException;
 import android.system.GaiException;
 import android.system.StructAddrinfo;
+import android.system.StructCapUserData;
+import android.system.StructCapUserHeader;
 import android.system.StructFlock;
 import android.system.StructGroupReq;
 import android.system.StructGroupSourceReq;
@@ -50,6 +52,11 @@
     public native InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException;
     public native void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
     public native void bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException;
+    @Override
+    public native StructCapUserData[] capget(StructCapUserHeader hdr) throws ErrnoException;
+    @Override
+    public native void capset(StructCapUserHeader hdr, StructCapUserData[] data)
+            throws ErrnoException;
     public native void chmod(String path, int mode) throws ErrnoException;
     public native void chown(String path, int uid, int gid) throws ErrnoException;
     public native void close(FileDescriptor fd) throws ErrnoException;
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
index 41b034b..20a84bd 100644
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -19,6 +19,8 @@
 import android.system.ErrnoException;
 import android.system.GaiException;
 import android.system.StructAddrinfo;
+import android.system.StructCapUserData;
+import android.system.StructCapUserHeader;
 import android.system.StructFlock;
 import android.system.StructGroupReq;
 import android.system.StructGroupSourceReq;
@@ -47,6 +49,8 @@
     public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException;
     public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
     public void bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException;
+    public StructCapUserData[] capget(StructCapUserHeader hdr) throws ErrnoException;
+    public void capset(StructCapUserHeader hdr, StructCapUserData[] data) throws ErrnoException;
     public void chmod(String path, int mode) throws ErrnoException;
     public void chown(String path, int uid, int gid) throws ErrnoException;
     public void close(FileDescriptor fd) throws ErrnoException;
diff --git a/luni/src/main/native/android_system_OsConstants.cpp b/luni/src/main/native/android_system_OsConstants.cpp
index 3cb3ee9..3ae4af6 100644
--- a/luni/src/main/native/android_system_OsConstants.cpp
+++ b/luni/src/main/native/android_system_OsConstants.cpp
@@ -342,6 +342,9 @@
     initConstant(env, c, "IP_RECVTOS", IP_RECVTOS);
     initConstant(env, c, "IP_TOS", IP_TOS);
     initConstant(env, c, "IP_TTL", IP_TTL);
+#if defined(_LINUX_CAPABILITY_VERSION_3)
+    initConstant(env, c, "_LINUX_CAPABILITY_VERSION_3", _LINUX_CAPABILITY_VERSION_3);
+#endif
     initConstant(env, c, "MAP_FIXED", MAP_FIXED);
     initConstant(env, c, "MAP_POPULATE", MAP_POPULATE);
     initConstant(env, c, "MAP_PRIVATE", MAP_PRIVATE);
@@ -406,6 +409,12 @@
     initConstant(env, c, "POLLRDNORM", POLLRDNORM);
     initConstant(env, c, "POLLWRBAND", POLLWRBAND);
     initConstant(env, c, "POLLWRNORM", POLLWRNORM);
+#if defined(PR_CAP_AMBIENT)
+    initConstant(env, c, "PR_CAP_AMBIENT", PR_CAP_AMBIENT);
+#endif
+#if defined(PR_CAP_AMBIENT_RAISE)
+    initConstant(env, c, "PR_CAP_AMBIENT_RAISE", PR_CAP_AMBIENT_RAISE);
+#endif
 #if defined(PR_GET_DUMPABLE)
     initConstant(env, c, "PR_GET_DUMPABLE", PR_GET_DUMPABLE);
 #endif
diff --git a/luni/src/main/native/libcore_io_Linux.cpp b/luni/src/main/native/libcore_io_Linux.cpp
index e9f35c2..1e2f3a5 100644
--- a/luni/src/main/native/libcore_io_Linux.cpp
+++ b/luni/src/main/native/libcore_io_Linux.cpp
@@ -29,6 +29,7 @@
 #include <pwd.h>
 #include <signal.h>
 #include <stdlib.h>
+#include <sys/capability.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <sys/prctl.h>
@@ -732,6 +733,154 @@
     struct passwd* mResult;
 };
 
+static void AssertException(JNIEnv* env) {
+    if (env->ExceptionCheck() == JNI_FALSE) {
+        env->FatalError("Expected exception");
+    }
+}
+
+// Note for capabilities functions:
+// We assume the calls are rare enough that it does not make sense to cache class objects. The
+// advantage is lower maintenance burden.
+
+static bool ReadStructCapUserHeader(
+        JNIEnv* env, jobject java_header, __user_cap_header_struct* c_header) {
+    if (java_header == nullptr) {
+        jniThrowNullPointerException(env, "header is null");
+        return false;
+    }
+
+    ScopedLocalRef<jclass> header_class(env, env->FindClass("android/system/StructCapUserHeader"));
+    if (header_class.get() == nullptr) {
+        return false;
+    }
+
+    {
+        static jfieldID version_fid = env->GetFieldID(header_class.get(), "version", "I");
+        if (version_fid == nullptr) {
+            return false;
+        }
+        c_header->version = env->GetIntField(java_header, version_fid);
+    }
+
+    {
+        static jfieldID pid_fid = env->GetFieldID(header_class.get(), "pid", "I");
+        if (pid_fid == nullptr) {
+            return false;
+        }
+        c_header->pid = env->GetIntField(java_header, pid_fid);
+    }
+
+    return true;
+}
+
+static void SetStructCapUserHeaderVersion(
+        JNIEnv* env, jobject java_header, __user_cap_header_struct* c_header) {
+    ScopedLocalRef<jclass> header_class(env, env->FindClass("android/system/StructCapUserHeader"));
+    if (header_class.get() == nullptr) {
+        env->ExceptionClear();
+        return;
+    }
+
+    static jfieldID version_fid = env->GetFieldID(header_class.get(), "version", "I");
+    if (version_fid == nullptr) {
+        env->ExceptionClear();
+        return;
+    }
+    env->SetIntField(java_header, version_fid, c_header->version);
+}
+
+static jobject CreateStructCapUserData(
+        JNIEnv* env, jclass data_class, __user_cap_data_struct* c_data) {
+    if (c_data == nullptr) {
+        // Should not happen.
+        jniThrowNullPointerException(env, "data is null");
+        return nullptr;
+    }
+
+    static jmethodID data_cons = env->GetMethodID(data_class, "<init>", "(III)V");
+    if (data_cons == nullptr) {
+        return nullptr;
+    }
+
+    jint e = static_cast<jint>(c_data->effective);
+    jint p = static_cast<jint>(c_data->permitted);
+    jint i = static_cast<jint>(c_data->inheritable);
+    return env->NewObject(data_class, data_cons, e, p, i);
+}
+
+static bool ReadStructCapUserData(JNIEnv* env, jobject java_data, __user_cap_data_struct* c_data) {
+    if (java_data == nullptr) {
+        jniThrowNullPointerException(env, "data is null");
+        return false;
+    }
+
+    ScopedLocalRef<jclass> data_class(env, env->FindClass("android/system/StructCapUserData"));
+    if (data_class.get() == nullptr) {
+        return false;
+    }
+
+    {
+        static jfieldID effective_fid = env->GetFieldID(data_class.get(), "effective", "I");
+        if (effective_fid == nullptr) {
+            return false;
+        }
+        c_data->effective = env->GetIntField(java_data, effective_fid);
+    }
+
+    {
+        static jfieldID permitted_fid = env->GetFieldID(data_class.get(), "permitted", "I");
+        if (permitted_fid == nullptr) {
+            return false;
+        }
+        c_data->permitted = env->GetIntField(java_data, permitted_fid);
+    }
+
+
+    {
+        static jfieldID inheritable_fid = env->GetFieldID(data_class.get(), "inheritable", "I");
+        if (inheritable_fid == nullptr) {
+            return false;
+        }
+        c_data->inheritable = env->GetIntField(java_data, inheritable_fid);
+    }
+
+    return true;
+}
+
+static constexpr size_t kMaxCapUserDataLength = 2U;
+#ifdef _LINUX_CAPABILITY_VERSION_1
+static_assert(kMaxCapUserDataLength >= _LINUX_CAPABILITY_U32S_1, "Length too small.");
+#endif
+#ifdef _LINUX_CAPABILITY_VERSION_2
+static_assert(kMaxCapUserDataLength >= _LINUX_CAPABILITY_U32S_2, "Length too small.");
+#endif
+#ifdef _LINUX_CAPABILITY_VERSION_3
+static_assert(kMaxCapUserDataLength >= _LINUX_CAPABILITY_U32S_3, "Length too small.");
+#endif
+#ifdef _LINUX_CAPABILITY_VERSION_4
+static_assert(false, "Unsupported capability version, please update.");
+#endif
+
+static size_t GetCapUserDataLength(uint32_t version) {
+#ifdef _LINUX_CAPABILITY_VERSION_1
+    if (version == _LINUX_CAPABILITY_VERSION_1) {
+        return _LINUX_CAPABILITY_U32S_1;
+    }
+#endif
+#ifdef _LINUX_CAPABILITY_VERSION_2
+    if (version == _LINUX_CAPABILITY_VERSION_2) {
+        return _LINUX_CAPABILITY_U32S_2;
+    }
+#endif
+#ifdef _LINUX_CAPABILITY_VERSION_3
+    if (version == _LINUX_CAPABILITY_VERSION_3) {
+        return _LINUX_CAPABILITY_U32S_3;
+    }
+#endif
+    return 0;
+}
+
 static jobject Linux_accept(JNIEnv* env, jobject, jobject javaFd, jobject javaSocketAddress) {
     sockaddr_storage ss;
     socklen_t sl = sizeof(ss);
@@ -776,6 +925,83 @@
     (void) NET_FAILURE_RETRY(env, int, bind, javaFd, sa, sa_len);
 }
 
+static jobjectArray Linux_capget(JNIEnv* env, jobject, jobject header) {
+    // Convert Java header struct to kernel datastructure.
+    __user_cap_header_struct cap_header;
+    if (!ReadStructCapUserHeader(env, header, &cap_header)) {
+        AssertException(env);
+        return nullptr;
+    }
+
+    // Call capget.
+    __user_cap_data_struct cap_data[kMaxCapUserDataLength];
+    if (capget(&cap_header, &cap_data[0]) == -1) {
+        // Check for EINVAL. In that case, mutate the header.
+        if (errno == EINVAL) {
+            int saved_errno = errno;
+            SetStructCapUserHeaderVersion(env, header, &cap_header);
+            errno = saved_errno;
+        }
+        throwErrnoException(env, "capget");
+        return nullptr;
+    }
+
+    // Create the result array.
+    ScopedLocalRef<jclass> data_class(env, env->FindClass("android/system/StructCapUserData"));
+    if (data_class.get() == nullptr) {
+        return nullptr;
+    }
+    size_t result_size = GetCapUserDataLength(cap_header.version);
+    ScopedLocalRef<jobjectArray> result(
+            env, env->NewObjectArray(result_size, data_class.get(), nullptr));
+    if (result.get() == nullptr) {
+        return nullptr;
+    }
+    // Translate the values we got.
+    for (size_t i = 0; i < result_size; ++i) {
+        ScopedLocalRef<jobject> value(
+                env, CreateStructCapUserData(env, data_class.get(), &cap_data[i]));
+        if (value.get() == nullptr) {
+            AssertException(env);
+            return nullptr;
+        }
+        env->SetObjectArrayElement(result.get(), i, value.get());
+    }
+    return result.release();
+}
+
+static void Linux_capset(
+        JNIEnv* env, jobject, jobject header, jobjectArray data) {
+    // Convert Java header struct to kernel datastructure.
+    __user_cap_header_struct cap_header;
+    if (!ReadStructCapUserHeader(env, header, &cap_header)) {
+        AssertException(env);
+        return;
+    }
+    size_t result_size = GetCapUserDataLength(cap_header.version);
+    // Ensure that the array has the expected length.
+    if (env->GetArrayLength(data) != static_cast<jint>(result_size)) {
+        jniThrowExceptionFmt(env,
+                             "java/lang/IllegalArgumentException",
+                             "Unsupported input length %d (expected %zu)",
+                             env->GetArrayLength(data),
+                             result_size);
+        return;
+    }
+
+    __user_cap_data_struct cap_data[kMaxCapUserDataLength];
+    // Translate the values we got.
+    for (size_t i = 0; i < result_size; ++i) {
+        ScopedLocalRef<jobject> value(env, env->GetObjectArrayElement(data, i));
+        if (!ReadStructCapUserData(env, value.get(), &cap_data[i])) {
+            AssertException(env);
+            return;
+        }
+    }
+
+    throwIfMinusOne(env, "capset", capset(&cap_header, &cap_data[0]));
+}
+
 static void Linux_chmod(JNIEnv* env, jobject, jstring javaPath, jint mode) {
     ScopedUtfChars path(env, javaPath);
     if (path.c_str() == NULL) {
@@ -2152,6 +2378,10 @@
     NATIVE_METHOD(Linux, android_getaddrinfo, "(Ljava/lang/String;Landroid/system/StructAddrinfo;I)[Ljava/net/InetAddress;"),
     NATIVE_METHOD(Linux, bind, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"),
     NATIVE_METHOD_OVERLOAD(Linux, bind, "(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V", SocketAddress),
+    NATIVE_METHOD(Linux, capget,
+                  "(Landroid/system/StructCapUserHeader;)[Landroid/system/StructCapUserData;"),
+    NATIVE_METHOD(Linux, capset,
+                  "(Landroid/system/StructCapUserHeader;[Landroid/system/StructCapUserData;)V"),
     NATIVE_METHOD(Linux, chmod, "(Ljava/lang/String;I)V"),
     NATIVE_METHOD(Linux, chown, "(Ljava/lang/String;II)V"),
     NATIVE_METHOD(Linux, close, "(Ljava/io/FileDescriptor;)V"),
diff --git a/non_openjdk_java_files.mk b/non_openjdk_java_files.mk
index e851faf..67dc00c 100644
--- a/non_openjdk_java_files.mk
+++ b/non_openjdk_java_files.mk
@@ -6,6 +6,8 @@
   luni/src/main/java/android/system/OsConstants.java \
   luni/src/main/java/android/system/PacketSocketAddress.java \
   luni/src/main/java/android/system/StructAddrinfo.java \
+  luni/src/main/java/android/system/StructCapUserData.java \
+  luni/src/main/java/android/system/StructCapUserHeader.java \
   luni/src/main/java/android/system/StructFlock.java \
   luni/src/main/java/android/system/StructGroupReq.java \
   luni/src/main/java/android/system/StructGroupSourceReq.java \