Prepare setresuid()/setresgid() seccomp filter in AppZygote.

The application zygote can run untrusted user code; since it also
has the capability to change the uid/gid of the process, we need
to ensure that any changes to the uid and/or gid stay within the
range that we have allocated for this application zygote.

For application zygotes, we install the app_zygote seccomp
filter instead of the regular app filter; the only difference
between this filter and the app one is that it allows
setuid/setgid calls.

To further limit this, pass down the allocated UID range to the
Zygote itself, which in turn installs an additional seccomp
filter that restricts setuid/setgid calls to this range.

The actual calls into seccomp are commented out until the seccomp
changes are merged; to avoid catastrophe, this will leave the
regular app filter for the app_zygote, which is more restrictive
and doesn't allow setuid at all.

Bug: 111434506
Test: atest CtsSeccompHostTestCases passes
Change-Id: I112419629f5ee4774ccbf77e2b1cfa5ddcf77e73
This commit is contained in:
Martijn Coenen
2019-01-03 16:23:01 +01:00
parent e9ffc74145
commit 86f08a5190
7 changed files with 119 additions and 13 deletions

View File

@@ -34,8 +34,15 @@ import com.android.internal.annotations.GuardedBy;
public class AppZygote {
private static final String LOG_TAG = "AppZygote";
// UID of the Zygote itself
private final int mZygoteUid;
// First UID/GID of the range the AppZygote can setuid()/setgid() to
private final int mZygoteUidGidMin;
// Last UID/GID of the range the AppZygote can setuid()/setgid() to
private final int mZygoteUidGidMax;
private final Object mLock = new Object();
/**
@@ -47,9 +54,11 @@ public class AppZygote {
private final ApplicationInfo mAppInfo;
public AppZygote(ApplicationInfo appInfo, int zygoteUid) {
public AppZygote(ApplicationInfo appInfo, int zygoteUid, int uidGidMin, int uidGidMax) {
mAppInfo = appInfo;
mZygoteUid = zygoteUid;
mZygoteUidGidMin = uidGidMin;
mZygoteUidGidMax = uidGidMax;
}
/**
@@ -104,7 +113,9 @@ public class AppZygote {
"app_zygote", // seInfo
abi, // abi
abi, // acceptedAbiList
null); // instructionSet
null, // instructionSet
mZygoteUidGidMin,
mZygoteUidGidMax);
ZygoteProcess.waitForConnectionToZygote(mZygote.getPrimarySocketAddress());
// preload application code in the zygote

View File

@@ -809,6 +809,8 @@ public class ZygoteProcess {
* may be different from <code>abi</code> in case the children
* spawned from this Zygote only communicate using ABI-safe methods.
* @param instructionSet null-ok the instruction set to use.
* @param uidRangeStart The first UID in the range the child zygote may setuid()/setgid() to
* @param uidRangeEnd The last UID in the range the child zygote may setuid()/setgid() to
*/
public ChildZygoteProcess startChildZygote(final String processClass,
final String niceName,
@@ -817,13 +819,17 @@ public class ZygoteProcess {
String seInfo,
String abi,
String acceptedAbiList,
String instructionSet) {
String instructionSet,
int uidRangeStart,
int uidRangeEnd) {
// Create an unguessable address in the global abstract namespace.
final LocalSocketAddress serverAddress = new LocalSocketAddress(
processClass + "/" + UUID.randomUUID().toString());
final String[] extraArgs = {Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + serverAddress.getName(),
Zygote.CHILD_ZYGOTE_ABI_LIST_ARG + acceptedAbiList};
Zygote.CHILD_ZYGOTE_ABI_LIST_ARG + acceptedAbiList,
Zygote.CHILD_ZYGOTE_UID_RANGE_START + uidRangeStart,
Zygote.CHILD_ZYGOTE_UID_RANGE_END + uidRangeEnd};
Process.ProcessStartResult result;
try {

View File

@@ -160,7 +160,9 @@ public class WebViewZygote {
"webview_zygote", // seInfo
sPackage.applicationInfo.primaryCpuAbi, // abi
TextUtils.join(",", Build.SUPPORTED_ABIS),
null); // instructionSet
null, // instructionSet
Process.FIRST_ISOLATED_UID,
Process.LAST_ISOLATED_UID);
// All the work below is usually done by LoadedApk, but the zygote can't talk to
// PackageManager or construct a LoadedApk since it's single-threaded pre-fork, so

View File

@@ -15,6 +15,7 @@
*/
package com.android.internal.os;
import android.os.Process;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -49,6 +50,22 @@ public class ChildZygoteInit {
return null;
}
static int parseIntFromArg(String[] argv, String desiredArg) {
int value = -1;
for (String arg : argv) {
if (arg.startsWith(desiredArg)) {
String valueStr = arg.substring(arg.indexOf('=') + 1);
try {
value = Integer.parseInt(valueStr);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid int argument: "
+ valueStr, e);
}
}
}
return value;
}
/**
* Starts a ZygoteServer and listens for requests
*
@@ -72,6 +89,27 @@ public class ChildZygoteInit {
throw new RuntimeException("Failed to set PR_SET_NO_NEW_PRIVS", ex);
}
int uidGidMin = parseIntFromArg(args, Zygote.CHILD_ZYGOTE_UID_RANGE_START);
int uidGidMax = parseIntFromArg(args, Zygote.CHILD_ZYGOTE_UID_RANGE_END);
if (uidGidMin == -1 || uidGidMax == -1) {
throw new RuntimeException("Couldn't parse UID range start/end");
}
if (uidGidMin > uidGidMax) {
throw new RuntimeException("Passed in UID range is invalid, min > max.");
}
// Verify the UIDs are in the isolated UID range, as that's the only thing that we should
// be forking right now
if (!Process.isIsolated(uidGidMin) || !Process.isIsolated(uidGidMax)) {
throw new RuntimeException("Passed in UID range does not map to isolated processes.");
}
/**
* Install a seccomp filter that ensure this Zygote can only setuid()/setgid()
* to the passed in range.
*/
Zygote.nativeInstallSeccompUidGidFilter(uidGidMin, uidGidMax);
final Runnable caller;
try {
server.registerServerSocketAtAbstractName(socketName);

View File

@@ -104,6 +104,20 @@ public final class Zygote {
*/
public static final String CHILD_ZYGOTE_ABI_LIST_ARG = "--abi-list=";
/**
* An extraArg passed when a zygote process is forking a child-zygote, specifying the
* start of the UID range the children of the Zygote may setuid()/setgid() to. This
* will be enforced with a seccomp filter.
*/
public static final String CHILD_ZYGOTE_UID_RANGE_START = "--uid-range-start=";
/**
* An extraArg passed when a zygote process is forking a child-zygote, specifying the
* end of the UID range the children of the Zygote may setuid()/setgid() to. This
* will be enforced with a seccomp filter.
*/
public static final String CHILD_ZYGOTE_UID_RANGE_END = "--uid-range-end=";
private Zygote() {}
/** Called for some security initialization before any fork. */
@@ -221,6 +235,13 @@ public final class Zygote {
*/
native protected static void nativeAllowFileAcrossFork(String path);
/**
* Installs a seccomp filter that limits setresuid()/setresgid() to the passed-in range
* @param uidGidMin The smallest allowed uid/gid
* @param uidGidMax The largest allowed uid/gid
*/
native protected static void nativeInstallSeccompUidGidFilter(int uidGidMin, int uidGidMax);
/**
* Zygote unmount storage space on initializing.
* This method is called once.

View File

@@ -303,7 +303,7 @@ static void PreApplicationInit() {
mallopt(M_DECAY_TIME, 1);
}
static void SetUpSeccompFilter(uid_t uid) {
static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) {
if (!g_is_security_enforced) {
ALOGI("seccomp disabled by setenforce 0");
return;
@@ -311,7 +311,14 @@ static void SetUpSeccompFilter(uid_t uid) {
// Apply system or app filter based on uid.
if (uid >= AID_APP_START) {
set_app_seccomp_filter();
if (is_child_zygote) {
// set_app_zygote_seccomp_filter();
// TODO(b/111434506) install the filter; for now, install the app filter
// which is more restrictive.
set_app_seccomp_filter();
} else {
set_app_seccomp_filter();
}
} else {
set_system_seccomp_filter();
}
@@ -996,7 +1003,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
// alternative is to call prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that
// breaks SELinux domain transition (see b/71859146). As the result,
// privileged syscalls used below still need to be accessible in app process.
SetUpSeccompFilter(uid);
SetUpSeccompFilter(uid, is_child_zygote);
if (setresuid(uid, uid, uid) == -1) {
fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
@@ -1295,6 +1302,23 @@ static void com_android_internal_os_Zygote_nativeUnmountStorageOnInit(JNIEnv* en
UnmountTree("/storage");
}
static void com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter(
JNIEnv* env, jclass, jint uidGidMin, jint uidGidMax) {
if (!g_is_security_enforced) {
ALOGI("seccomp disabled by setenforce 0");
return;
}
// TODO(b/111434506) install the filter
/*
bool installed = install_setuidgid_seccomp_filter(uidGidMin, uidGidMax);
if (!installed) {
RuntimeAbort(env, __LINE__, "Could not install setuid/setgid seccomp filter.");
}
*/
}
static const JNINativeMethod gMethods[] = {
{ "nativeSecurityInit", "()V",
(void *) com_android_internal_os_Zygote_nativeSecurityInit },
@@ -1308,7 +1332,9 @@ static const JNINativeMethod gMethods[] = {
{ "nativeUnmountStorageOnInit", "()V",
(void *) com_android_internal_os_Zygote_nativeUnmountStorageOnInit },
{ "nativePreApplicationInit", "()V",
(void *) com_android_internal_os_Zygote_nativePreApplicationInit }
(void *) com_android_internal_os_Zygote_nativePreApplicationInit },
{ "nativeInstallSeccompUidGidFilter", "(II)V",
(void *) com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter }
};
int register_com_android_internal_os_Zygote(JNIEnv* env) {

View File

@@ -1678,12 +1678,14 @@ public final class ProcessList {
AppZygote appZygote = mAppZygotes.get(app.info.processName, app.info.uid);
final ArrayList<ProcessRecord> zygoteProcessList;
if (appZygote == null) {
final int userId = UserHandle.getUserId(app.info.uid);
final IsolatedUidRange uidRange =
mAppIsolatedUidRangeAllocator.getIsolatedUidRangeLocked(app.info);
// Allocate an isolated UID out of this range for the Zygote itself
final int zygoteIsolatedUid = uidRange.allocateIsolatedUidLocked(userId);
appZygote = new AppZygote(app.info, zygoteIsolatedUid);
final int userId = UserHandle.getUserId(app.info.uid);
// Create the app-zygote and provide it with the UID-range it's allowed
// to setresuid/setresgid to.
final int firstUid = UserHandle.getUid(userId, uidRange.mFirstUid);
final int lastUid = UserHandle.getUid(userId, uidRange.mLastUid);
appZygote = new AppZygote(app.info, app.info.uid, firstUid, lastUid);
mAppZygotes.put(app.info.processName, app.info.uid, appZygote);
zygoteProcessList = new ArrayList<ProcessRecord>();
mAppZygoteProcesses.put(appZygote, zygoteProcessList);