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 \