1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.os;
18
19import android.net.Credentials;
20import android.net.LocalSocket;
21import android.os.Process;
22import android.os.SELinux;
23import android.os.SystemProperties;
24import android.util.Log;
25
26import dalvik.system.PathClassLoader;
27import dalvik.system.Zygote;
28
29import java.io.BufferedReader;
30import java.io.DataInputStream;
31import java.io.DataOutputStream;
32import java.io.FileDescriptor;
33import java.io.FileInputStream;
34import java.io.FileOutputStream;
35import java.io.IOException;
36import java.io.InputStreamReader;
37import java.io.PrintStream;
38import java.util.ArrayList;
39
40import libcore.io.ErrnoException;
41import libcore.io.IoUtils;
42import libcore.io.Libcore;
43
44/**
45 * A connection that can make spawn requests.
46 */
47class ZygoteConnection {
48    private static final String TAG = "Zygote";
49
50    /** a prototype instance for a future List.toArray() */
51    private static final int[][] intArray2d = new int[0][0];
52
53    /**
54     * {@link android.net.LocalSocket#setSoTimeout} value for connections.
55     * Effectively, the amount of time a requestor has between the start of
56     * the request and the completed request. The select-loop mode Zygote
57     * doesn't have the logic to return to the select loop in the middle of
58     * a request, so we need to time out here to avoid being denial-of-serviced.
59     */
60    private static final int CONNECTION_TIMEOUT_MILLIS = 1000;
61
62    /** max number of arguments that a connection can specify */
63    private static final int MAX_ZYGOTE_ARGC=1024;
64
65    /**
66     * The command socket.
67     *
68     * mSocket is retained in the child process in "peer wait" mode, so
69     * that it closes when the child process terminates. In other cases,
70     * it is closed in the peer.
71     */
72    private final LocalSocket mSocket;
73    private final DataOutputStream mSocketOutStream;
74    private final BufferedReader mSocketReader;
75    private final Credentials peer;
76    private final String peerSecurityContext;
77
78    /**
79     * A long-lived reference to the original command socket used to launch
80     * this peer. If "peer wait" mode is specified, the process that requested
81     * the new VM instance intends to track the lifetime of the spawned instance
82     * via the command socket. In this case, the command socket is closed
83     * in the Zygote and placed here in the spawned instance so that it will
84     * not be collected and finalized. This field remains null at all times
85     * in the original Zygote process, and in all spawned processes where
86     * "peer-wait" mode was not requested.
87     */
88    private static LocalSocket sPeerWaitSocket = null;
89
90    /**
91     * Constructs instance from connected socket.
92     *
93     * @param socket non-null; connected socket
94     * @throws IOException
95     */
96    ZygoteConnection(LocalSocket socket) throws IOException {
97        mSocket = socket;
98
99        mSocketOutStream
100                = new DataOutputStream(socket.getOutputStream());
101
102        mSocketReader = new BufferedReader(
103                new InputStreamReader(socket.getInputStream()), 256);
104
105        mSocket.setSoTimeout(CONNECTION_TIMEOUT_MILLIS);
106
107        try {
108            peer = mSocket.getPeerCredentials();
109        } catch (IOException ex) {
110            Log.e(TAG, "Cannot read peer credentials", ex);
111            throw ex;
112        }
113
114        peerSecurityContext = SELinux.getPeerContext(mSocket.getFileDescriptor());
115    }
116
117    /**
118     * Returns the file descriptor of the associated socket.
119     *
120     * @return null-ok; file descriptor
121     */
122    FileDescriptor getFileDesciptor() {
123        return mSocket.getFileDescriptor();
124    }
125
126    /**
127     * Reads start commands from an open command socket.
128     * Start commands are presently a pair of newline-delimited lines
129     * indicating a) class to invoke main() on b) nice name to set argv[0] to.
130     * Continues to read commands and forkAndSpecialize children until
131     * the socket is closed. This method is used in ZYGOTE_FORK_MODE
132     *
133     * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main()
134     * method in child process
135     */
136    void run() throws ZygoteInit.MethodAndArgsCaller {
137
138        int loopCount = ZygoteInit.GC_LOOP_COUNT;
139
140        while (true) {
141            /*
142             * Call gc() before we block in readArgumentList().
143             * It's work that has to be done anyway, and it's better
144             * to avoid making every child do it.  It will also
145             * madvise() any free memory as a side-effect.
146             *
147             * Don't call it every time, because walking the entire
148             * heap is a lot of overhead to free a few hundred bytes.
149             */
150            if (loopCount <= 0) {
151                ZygoteInit.gc();
152                loopCount = ZygoteInit.GC_LOOP_COUNT;
153            } else {
154                loopCount--;
155            }
156
157            if (runOnce()) {
158                break;
159            }
160        }
161    }
162
163    /**
164     * Reads one start command from the command socket. If successful,
165     * a child is forked and a {@link ZygoteInit.MethodAndArgsCaller}
166     * exception is thrown in that child while in the parent process,
167     * the method returns normally. On failure, the child is not
168     * spawned and messages are printed to the log and stderr. Returns
169     * a boolean status value indicating whether an end-of-file on the command
170     * socket has been encountered.
171     *
172     * @return false if command socket should continue to be read from, or
173     * true if an end-of-file has been encountered.
174     * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main()
175     * method in child process
176     */
177    boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
178
179        String args[];
180        Arguments parsedArgs = null;
181        FileDescriptor[] descriptors;
182
183        try {
184            args = readArgumentList();
185            descriptors = mSocket.getAncillaryFileDescriptors();
186        } catch (IOException ex) {
187            Log.w(TAG, "IOException on command socket " + ex.getMessage());
188            closeSocket();
189            return true;
190        }
191
192        if (args == null) {
193            // EOF reached.
194            closeSocket();
195            return true;
196        }
197
198        /** the stderr of the most recent request, if avail */
199        PrintStream newStderr = null;
200
201        if (descriptors != null && descriptors.length >= 3) {
202            newStderr = new PrintStream(
203                    new FileOutputStream(descriptors[2]));
204        }
205
206        int pid = -1;
207        FileDescriptor childPipeFd = null;
208        FileDescriptor serverPipeFd = null;
209
210        try {
211            parsedArgs = new Arguments(args);
212
213            applyUidSecurityPolicy(parsedArgs, peer, peerSecurityContext);
214            applyRlimitSecurityPolicy(parsedArgs, peer, peerSecurityContext);
215            applyCapabilitiesSecurityPolicy(parsedArgs, peer, peerSecurityContext);
216            applyInvokeWithSecurityPolicy(parsedArgs, peer, peerSecurityContext);
217            applyseInfoSecurityPolicy(parsedArgs, peer, peerSecurityContext);
218
219            applyDebuggerSystemProperty(parsedArgs);
220            applyInvokeWithSystemProperty(parsedArgs);
221
222            int[][] rlimits = null;
223
224            if (parsedArgs.rlimits != null) {
225                rlimits = parsedArgs.rlimits.toArray(intArray2d);
226            }
227
228            if (parsedArgs.runtimeInit && parsedArgs.invokeWith != null) {
229                FileDescriptor[] pipeFds = Libcore.os.pipe();
230                childPipeFd = pipeFds[1];
231                serverPipeFd = pipeFds[0];
232                ZygoteInit.setCloseOnExec(serverPipeFd, true);
233            }
234
235            pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
236                    parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
237                    parsedArgs.niceName);
238        } catch (IOException ex) {
239            logAndPrintError(newStderr, "Exception creating pipe", ex);
240        } catch (ErrnoException ex) {
241            logAndPrintError(newStderr, "Exception creating pipe", ex);
242        } catch (IllegalArgumentException ex) {
243            logAndPrintError(newStderr, "Invalid zygote arguments", ex);
244        } catch (ZygoteSecurityException ex) {
245            logAndPrintError(newStderr,
246                    "Zygote security policy prevents request: ", ex);
247        }
248
249        try {
250            if (pid == 0) {
251                // in child
252                IoUtils.closeQuietly(serverPipeFd);
253                serverPipeFd = null;
254                handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
255
256                // should never get here, the child is expected to either
257                // throw ZygoteInit.MethodAndArgsCaller or exec().
258                return true;
259            } else {
260                // in parent...pid of < 0 means failure
261                IoUtils.closeQuietly(childPipeFd);
262                childPipeFd = null;
263                return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
264            }
265        } finally {
266            IoUtils.closeQuietly(childPipeFd);
267            IoUtils.closeQuietly(serverPipeFd);
268        }
269    }
270
271    /**
272     * Closes socket associated with this connection.
273     */
274    void closeSocket() {
275        try {
276            mSocket.close();
277        } catch (IOException ex) {
278            Log.e(TAG, "Exception while closing command "
279                    + "socket in parent", ex);
280        }
281    }
282
283    /**
284     * Handles argument parsing for args related to the zygote spawner.
285     *
286     * Current recognized args:
287     * <ul>
288     *   <li> --setuid=<i>uid of child process, defaults to 0</i>
289     *   <li> --setgid=<i>gid of child process, defaults to 0</i>
290     *   <li> --setgroups=<i>comma-separated list of supplimentary gid's</i>
291     *   <li> --capabilities=<i>a pair of comma-separated integer strings
292     * indicating Linux capabilities(2) set for child. The first string
293     * represents the <code>permitted</code> set, and the second the
294     * <code>effective</code> set. Precede each with 0 or
295     * 0x for octal or hexidecimal value. If unspecified, both default to 0.
296     * This parameter is only applied if the uid of the new process will
297     * be non-0. </i>
298     *   <li> --rlimit=r,c,m<i>tuple of values for setrlimit() call.
299     *    <code>r</code> is the resource, <code>c</code> and <code>m</code>
300     *    are the settings for current and max value.</i>
301     *   <li> --peer-wait indicates that the command socket should
302     * be inherited by (and set to close-on-exec in) the spawned process
303     * and used to track the lifetime of that process. The spawning process
304     * then exits. Without this flag, it is retained by the spawning process
305     * (and closed in the child) in expectation of a new spawn request.
306     *   <li> --classpath=<i>colon-separated classpath</i> indicates
307     * that the specified class (which must b first non-flag argument) should
308     * be loaded from jar files in the specified classpath. Incompatible with
309     * --runtime-init
310     *   <li> --runtime-init indicates that the remaining arg list should
311     * be handed off to com.android.internal.os.RuntimeInit, rather than
312     * processed directly
313     * Android runtime startup (eg, Binder initialization) is also eschewed.
314     *   <li> --nice-name=<i>nice name to appear in ps</i>
315     *   <li> If <code>--runtime-init</code> is present:
316     *      [--] &lt;args for RuntimeInit &gt;
317     *   <li> If <code>--runtime-init</code> is absent:
318     *      [--] &lt;classname&gt; [args...]
319     * </ul>
320     */
321    static class Arguments {
322        /** from --setuid */
323        int uid = 0;
324        boolean uidSpecified;
325
326        /** from --setgid */
327        int gid = 0;
328        boolean gidSpecified;
329
330        /** from --setgroups */
331        int[] gids;
332
333        /** from --peer-wait */
334        boolean peerWait;
335
336        /**
337         * From --enable-debugger, --enable-checkjni, --enable-assert,
338         * --enable-safemode, and --enable-jni-logging.
339         */
340        int debugFlags;
341
342        /** From --mount-external */
343        int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
344
345        /** from --target-sdk-version. */
346        int targetSdkVersion;
347        boolean targetSdkVersionSpecified;
348
349        /** from --classpath */
350        String classpath;
351
352        /** from --runtime-init */
353        boolean runtimeInit;
354
355        /** from --nice-name */
356        String niceName;
357
358        /** from --capabilities */
359        boolean capabilitiesSpecified;
360        long permittedCapabilities;
361        long effectiveCapabilities;
362
363        /** from --seinfo */
364        boolean seInfoSpecified;
365        String seInfo;
366
367        /** from all --rlimit=r,c,m */
368        ArrayList<int[]> rlimits;
369
370        /** from --invoke-with */
371        String invokeWith;
372
373        /**
374         * Any args after and including the first non-option arg
375         * (or after a '--')
376         */
377        String remainingArgs[];
378
379        /**
380         * Constructs instance and parses args
381         * @param args zygote command-line args
382         * @throws IllegalArgumentException
383         */
384        Arguments(String args[]) throws IllegalArgumentException {
385            parseArgs(args);
386        }
387
388        /**
389         * Parses the commandline arguments intended for the Zygote spawner
390         * (such as "--setuid=" and "--setgid=") and creates an array
391         * containing the remaining args.
392         *
393         * Per security review bug #1112214, duplicate args are disallowed in
394         * critical cases to make injection harder.
395         */
396        private void parseArgs(String args[])
397                throws IllegalArgumentException {
398            int curArg = 0;
399
400            for ( /* curArg */ ; curArg < args.length; curArg++) {
401                String arg = args[curArg];
402
403                if (arg.equals("--")) {
404                    curArg++;
405                    break;
406                } else if (arg.startsWith("--setuid=")) {
407                    if (uidSpecified) {
408                        throw new IllegalArgumentException(
409                                "Duplicate arg specified");
410                    }
411                    uidSpecified = true;
412                    uid = Integer.parseInt(
413                            arg.substring(arg.indexOf('=') + 1));
414                } else if (arg.startsWith("--setgid=")) {
415                    if (gidSpecified) {
416                        throw new IllegalArgumentException(
417                                "Duplicate arg specified");
418                    }
419                    gidSpecified = true;
420                    gid = Integer.parseInt(
421                            arg.substring(arg.indexOf('=') + 1));
422                } else if (arg.startsWith("--target-sdk-version=")) {
423                    if (targetSdkVersionSpecified) {
424                        throw new IllegalArgumentException(
425                                "Duplicate target-sdk-version specified");
426                    }
427                    targetSdkVersionSpecified = true;
428                    targetSdkVersion = Integer.parseInt(
429                            arg.substring(arg.indexOf('=') + 1));
430                } else if (arg.equals("--enable-debugger")) {
431                    debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
432                } else if (arg.equals("--enable-safemode")) {
433                    debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
434                } else if (arg.equals("--enable-checkjni")) {
435                    debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
436                } else if (arg.equals("--enable-jni-logging")) {
437                    debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
438                } else if (arg.equals("--enable-assert")) {
439                    debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
440                } else if (arg.equals("--peer-wait")) {
441                    peerWait = true;
442                } else if (arg.equals("--runtime-init")) {
443                    runtimeInit = true;
444                } else if (arg.startsWith("--seinfo=")) {
445                    if (seInfoSpecified) {
446                        throw new IllegalArgumentException(
447                                "Duplicate arg specified");
448                    }
449                    seInfoSpecified = true;
450                    seInfo = arg.substring(arg.indexOf('=') + 1);
451                } else if (arg.startsWith("--capabilities=")) {
452                    if (capabilitiesSpecified) {
453                        throw new IllegalArgumentException(
454                                "Duplicate arg specified");
455                    }
456                    capabilitiesSpecified = true;
457                    String capString = arg.substring(arg.indexOf('=')+1);
458
459                    String[] capStrings = capString.split(",", 2);
460
461                    if (capStrings.length == 1) {
462                        effectiveCapabilities = Long.decode(capStrings[0]);
463                        permittedCapabilities = effectiveCapabilities;
464                    } else {
465                        permittedCapabilities = Long.decode(capStrings[0]);
466                        effectiveCapabilities = Long.decode(capStrings[1]);
467                    }
468                } else if (arg.startsWith("--rlimit=")) {
469                    // Duplicate --rlimit arguments are specifically allowed.
470                    String[] limitStrings
471                            = arg.substring(arg.indexOf('=')+1).split(",");
472
473                    if (limitStrings.length != 3) {
474                        throw new IllegalArgumentException(
475                                "--rlimit= should have 3 comma-delimited ints");
476                    }
477                    int[] rlimitTuple = new int[limitStrings.length];
478
479                    for(int i=0; i < limitStrings.length; i++) {
480                        rlimitTuple[i] = Integer.parseInt(limitStrings[i]);
481                    }
482
483                    if (rlimits == null) {
484                        rlimits = new ArrayList();
485                    }
486
487                    rlimits.add(rlimitTuple);
488                } else if (arg.equals("-classpath")) {
489                    if (classpath != null) {
490                        throw new IllegalArgumentException(
491                                "Duplicate arg specified");
492                    }
493                    try {
494                        classpath = args[++curArg];
495                    } catch (IndexOutOfBoundsException ex) {
496                        throw new IllegalArgumentException(
497                                "-classpath requires argument");
498                    }
499                } else if (arg.startsWith("--setgroups=")) {
500                    if (gids != null) {
501                        throw new IllegalArgumentException(
502                                "Duplicate arg specified");
503                    }
504
505                    String[] params
506                            = arg.substring(arg.indexOf('=') + 1).split(",");
507
508                    gids = new int[params.length];
509
510                    for (int i = params.length - 1; i >= 0 ; i--) {
511                        gids[i] = Integer.parseInt(params[i]);
512                    }
513                } else if (arg.equals("--invoke-with")) {
514                    if (invokeWith != null) {
515                        throw new IllegalArgumentException(
516                                "Duplicate arg specified");
517                    }
518                    try {
519                        invokeWith = args[++curArg];
520                    } catch (IndexOutOfBoundsException ex) {
521                        throw new IllegalArgumentException(
522                                "--invoke-with requires argument");
523                    }
524                } else if (arg.startsWith("--nice-name=")) {
525                    if (niceName != null) {
526                        throw new IllegalArgumentException(
527                                "Duplicate arg specified");
528                    }
529                    niceName = arg.substring(arg.indexOf('=') + 1);
530                } else if (arg.equals("--mount-external-multiuser")) {
531                    mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
532                } else if (arg.equals("--mount-external-multiuser-all")) {
533                    mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL;
534                } else {
535                    break;
536                }
537            }
538
539            if (runtimeInit && classpath != null) {
540                throw new IllegalArgumentException(
541                        "--runtime-init and -classpath are incompatible");
542            }
543
544            remainingArgs = new String[args.length - curArg];
545
546            System.arraycopy(args, curArg, remainingArgs, 0,
547                    remainingArgs.length);
548        }
549    }
550
551    /**
552     * Reads an argument list from the command socket/
553     * @return Argument list or null if EOF is reached
554     * @throws IOException passed straight through
555     */
556    private String[] readArgumentList()
557            throws IOException {
558
559        /**
560         * See android.os.Process.zygoteSendArgsAndGetPid()
561         * Presently the wire format to the zygote process is:
562         * a) a count of arguments (argc, in essence)
563         * b) a number of newline-separated argument strings equal to count
564         *
565         * After the zygote process reads these it will write the pid of
566         * the child or -1 on failure.
567         */
568
569        int argc;
570
571        try {
572            String s = mSocketReader.readLine();
573
574            if (s == null) {
575                // EOF reached.
576                return null;
577            }
578            argc = Integer.parseInt(s);
579        } catch (NumberFormatException ex) {
580            Log.e(TAG, "invalid Zygote wire format: non-int at argc");
581            throw new IOException("invalid wire format");
582        }
583
584        // See bug 1092107: large argc can be used for a DOS attack
585        if (argc > MAX_ZYGOTE_ARGC) {
586            throw new IOException("max arg count exceeded");
587        }
588
589        String[] result = new String[argc];
590        for (int i = 0; i < argc; i++) {
591            result[i] = mSocketReader.readLine();
592            if (result[i] == null) {
593                // We got an unexpected EOF.
594                throw new IOException("truncated request");
595            }
596        }
597
598        return result;
599    }
600
601    /**
602     * Applies zygote security policy per bugs #875058 and #1082165.
603     * Based on the credentials of the process issuing a zygote command:
604     * <ol>
605     * <li> uid 0 (root) may specify any uid, gid, and setgroups() list
606     * <li> uid 1000 (Process.SYSTEM_UID) may specify any uid &gt; 1000 in normal
607     * operation. It may also specify any gid and setgroups() list it chooses.
608     * In factory test mode, it may specify any UID.
609     * <li> Any other uid may not specify any uid, gid, or setgroups list. The
610     * uid and gid will be inherited from the requesting process.
611     * </ul>
612     *
613     * @param args non-null; zygote spawner arguments
614     * @param peer non-null; peer credentials
615     * @throws ZygoteSecurityException
616     */
617    private static void applyUidSecurityPolicy(Arguments args, Credentials peer,
618            String peerSecurityContext)
619            throws ZygoteSecurityException {
620
621        int peerUid = peer.getUid();
622
623        if (peerUid == 0) {
624            // Root can do what it wants
625        } else if (peerUid == Process.SYSTEM_UID ) {
626            // System UID is restricted, except in factory test mode
627            String factoryTest = SystemProperties.get("ro.factorytest");
628            boolean uidRestricted;
629
630            /* In normal operation, SYSTEM_UID can only specify a restricted
631             * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
632             */
633            uidRestricted
634                 = !(factoryTest.equals("1") || factoryTest.equals("2"));
635
636            if (uidRestricted
637                    && args.uidSpecified && (args.uid < Process.SYSTEM_UID)) {
638                throw new ZygoteSecurityException(
639                        "System UID may not launch process with UID < "
640                                + Process.SYSTEM_UID);
641            }
642        } else {
643            // Everything else
644            if (args.uidSpecified || args.gidSpecified
645                || args.gids != null) {
646                throw new ZygoteSecurityException(
647                        "App UIDs may not specify uid's or gid's");
648            }
649        }
650
651        if (args.uidSpecified || args.gidSpecified || args.gids != null) {
652            boolean allowed = SELinux.checkSELinuxAccess(peerSecurityContext,
653                                                         peerSecurityContext,
654                                                         "zygote",
655                                                         "specifyids");
656            if (!allowed) {
657                throw new ZygoteSecurityException(
658                        "Peer may not specify uid's or gid's");
659            }
660        }
661
662        // If not otherwise specified, uid and gid are inherited from peer
663        if (!args.uidSpecified) {
664            args.uid = peer.getUid();
665            args.uidSpecified = true;
666        }
667        if (!args.gidSpecified) {
668            args.gid = peer.getGid();
669            args.gidSpecified = true;
670        }
671    }
672
673
674    /**
675     * Applies debugger system properties to the zygote arguments.
676     *
677     * If "ro.debuggable" is "1", all apps are debuggable. Otherwise,
678     * the debugger state is specified via the "--enable-debugger" flag
679     * in the spawn request.
680     *
681     * @param args non-null; zygote spawner args
682     */
683    public static void applyDebuggerSystemProperty(Arguments args) {
684        if ("1".equals(SystemProperties.get("ro.debuggable"))) {
685            args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
686        }
687    }
688
689    /**
690     * Applies zygote security policy per bug #1042973. Based on the credentials
691     * of the process issuing a zygote command:
692     * <ol>
693     * <li> peers of  uid 0 (root) and uid 1000 (Process.SYSTEM_UID)
694     * may specify any rlimits.
695     * <li> All other uids may not specify rlimits.
696     * </ul>
697     * @param args non-null; zygote spawner arguments
698     * @param peer non-null; peer credentials
699     * @throws ZygoteSecurityException
700     */
701    private static void applyRlimitSecurityPolicy(
702            Arguments args, Credentials peer, String peerSecurityContext)
703            throws ZygoteSecurityException {
704
705        int peerUid = peer.getUid();
706
707        if (!(peerUid == 0 || peerUid == Process.SYSTEM_UID)) {
708            // All peers with UID other than root or SYSTEM_UID
709            if (args.rlimits != null) {
710                throw new ZygoteSecurityException(
711                        "This UID may not specify rlimits.");
712            }
713        }
714
715        if (args.rlimits != null) {
716            boolean allowed = SELinux.checkSELinuxAccess(peerSecurityContext,
717                                                         peerSecurityContext,
718                                                         "zygote",
719                                                         "specifyrlimits");
720            if (!allowed) {
721                throw new ZygoteSecurityException(
722                        "Peer may not specify rlimits");
723            }
724         }
725    }
726
727    /**
728     * Applies zygote security policy per bug #1042973. A root peer may
729     * spawn an instance with any capabilities. All other uids may spawn
730     * instances with any of the capabilities in the peer's permitted set
731     * but no more.
732     *
733     * @param args non-null; zygote spawner arguments
734     * @param peer non-null; peer credentials
735     * @throws ZygoteSecurityException
736     */
737    private static void applyCapabilitiesSecurityPolicy(
738            Arguments args, Credentials peer, String peerSecurityContext)
739            throws ZygoteSecurityException {
740
741        if (args.permittedCapabilities == 0
742                && args.effectiveCapabilities == 0) {
743            // nothing to check
744            return;
745        }
746
747        boolean allowed = SELinux.checkSELinuxAccess(peerSecurityContext,
748                                                     peerSecurityContext,
749                                                     "zygote",
750                                                     "specifycapabilities");
751        if (!allowed) {
752            throw new ZygoteSecurityException(
753                    "Peer may not specify capabilities");
754        }
755
756        if (peer.getUid() == 0) {
757            // root may specify anything
758            return;
759        }
760
761        long permittedCaps;
762
763        try {
764            permittedCaps = ZygoteInit.capgetPermitted(peer.getPid());
765        } catch (IOException ex) {
766            throw new ZygoteSecurityException(
767                    "Error retrieving peer's capabilities.");
768        }
769
770        /*
771         * Ensure that the client did not specify an effective set larger
772         * than the permitted set. The kernel will enforce this too, but we
773         * do it here to make the following check easier.
774         */
775        if (((~args.permittedCapabilities) & args.effectiveCapabilities) != 0) {
776            throw new ZygoteSecurityException(
777                    "Effective capabilities cannot be superset of "
778                            + " permitted capabilities" );
779        }
780
781        /*
782         * Ensure that the new permitted (and thus the new effective) set is
783         * a subset of the peer process's permitted set
784         */
785
786        if (((~permittedCaps) & args.permittedCapabilities) != 0) {
787            throw new ZygoteSecurityException(
788                    "Peer specified unpermitted capabilities" );
789        }
790    }
791
792    /**
793     * Applies zygote security policy.
794     * Based on the credentials of the process issuing a zygote command:
795     * <ol>
796     * <li> uid 0 (root) may specify --invoke-with to launch Zygote with a
797     * wrapper command.
798     * <li> Any other uid may not specify any invoke-with argument.
799     * </ul>
800     *
801     * @param args non-null; zygote spawner arguments
802     * @param peer non-null; peer credentials
803     * @throws ZygoteSecurityException
804     */
805    private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer,
806            String peerSecurityContext)
807            throws ZygoteSecurityException {
808        int peerUid = peer.getUid();
809
810        if (args.invokeWith != null && peerUid != 0) {
811            throw new ZygoteSecurityException("Peer is not permitted to specify "
812                    + "an explicit invoke-with wrapper command");
813        }
814
815        if (args.invokeWith != null) {
816            boolean allowed = SELinux.checkSELinuxAccess(peerSecurityContext,
817                                                         peerSecurityContext,
818                                                         "zygote",
819                                                         "specifyinvokewith");
820            if (!allowed) {
821                throw new ZygoteSecurityException("Peer is not permitted to specify "
822                    + "an explicit invoke-with wrapper command");
823            }
824        }
825    }
826
827    /**
828     * Applies zygote security policy for SEAndroid information.
829     *
830     * @param args non-null; zygote spawner arguments
831     * @param peer non-null; peer credentials
832     * @throws ZygoteSecurityException
833     */
834    private static void applyseInfoSecurityPolicy(
835            Arguments args, Credentials peer, String peerSecurityContext)
836            throws ZygoteSecurityException {
837        int peerUid = peer.getUid();
838
839        if (args.seInfo == null) {
840            // nothing to check
841            return;
842        }
843
844        if (!(peerUid == 0 || peerUid == Process.SYSTEM_UID)) {
845            // All peers with UID other than root or SYSTEM_UID
846            throw new ZygoteSecurityException(
847                    "This UID may not specify SEAndroid info.");
848        }
849
850        boolean allowed = SELinux.checkSELinuxAccess(peerSecurityContext,
851                                                     peerSecurityContext,
852                                                     "zygote",
853                                                     "specifyseinfo");
854        if (!allowed) {
855            throw new ZygoteSecurityException(
856                    "Peer may not specify SEAndroid info");
857        }
858
859        return;
860    }
861
862    /**
863     * Applies invoke-with system properties to the zygote arguments.
864     *
865     * @param parsedArgs non-null; zygote args
866     */
867    public static void applyInvokeWithSystemProperty(Arguments args) {
868        if (args.invokeWith == null && args.niceName != null) {
869            if (args.niceName != null) {
870                String property = "wrap." + args.niceName;
871                if (property.length() > 31) {
872                    property = property.substring(0, 31);
873                }
874                args.invokeWith = SystemProperties.get(property);
875                if (args.invokeWith != null && args.invokeWith.length() == 0) {
876                    args.invokeWith = null;
877                }
878            }
879        }
880    }
881
882    /**
883     * Handles post-fork setup of child proc, closing sockets as appropriate,
884     * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
885     * if successful or returning if failed.
886     *
887     * @param parsedArgs non-null; zygote args
888     * @param descriptors null-ok; new file descriptors for stdio if available.
889     * @param pipeFd null-ok; pipe for communication back to Zygote.
890     * @param newStderr null-ok; stream to use for stderr until stdio
891     * is reopened.
892     *
893     * @throws ZygoteInit.MethodAndArgsCaller on success to
894     * trampoline to code that invokes static main.
895     */
896    private void handleChildProc(Arguments parsedArgs,
897            FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
898            throws ZygoteInit.MethodAndArgsCaller {
899
900        /*
901         * Close the socket, unless we're in "peer wait" mode, in which
902         * case it's used to track the liveness of this process.
903         */
904
905        if (parsedArgs.peerWait) {
906            try {
907                ZygoteInit.setCloseOnExec(mSocket.getFileDescriptor(), true);
908                sPeerWaitSocket = mSocket;
909            } catch (IOException ex) {
910                Log.e(TAG, "Zygote Child: error setting peer wait "
911                        + "socket to be close-on-exec", ex);
912            }
913        } else {
914            closeSocket();
915            ZygoteInit.closeServerSocket();
916        }
917
918        if (descriptors != null) {
919            try {
920                ZygoteInit.reopenStdio(descriptors[0],
921                        descriptors[1], descriptors[2]);
922
923                for (FileDescriptor fd: descriptors) {
924                    IoUtils.closeQuietly(fd);
925                }
926                newStderr = System.err;
927            } catch (IOException ex) {
928                Log.e(TAG, "Error reopening stdio", ex);
929            }
930        }
931
932        if (parsedArgs.niceName != null) {
933            Process.setArgV0(parsedArgs.niceName);
934        }
935
936        if (parsedArgs.runtimeInit) {
937            if (parsedArgs.invokeWith != null) {
938                WrapperInit.execApplication(parsedArgs.invokeWith,
939                        parsedArgs.niceName, parsedArgs.targetSdkVersion,
940                        pipeFd, parsedArgs.remainingArgs);
941            } else {
942                RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
943                        parsedArgs.remainingArgs);
944            }
945        } else {
946            String className;
947            try {
948                className = parsedArgs.remainingArgs[0];
949            } catch (ArrayIndexOutOfBoundsException ex) {
950                logAndPrintError(newStderr,
951                        "Missing required class name argument", null);
952                return;
953            }
954
955            String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1];
956            System.arraycopy(parsedArgs.remainingArgs, 1,
957                    mainArgs, 0, mainArgs.length);
958
959            if (parsedArgs.invokeWith != null) {
960                WrapperInit.execStandalone(parsedArgs.invokeWith,
961                        parsedArgs.classpath, className, mainArgs);
962            } else {
963                ClassLoader cloader;
964                if (parsedArgs.classpath != null) {
965                    cloader = new PathClassLoader(parsedArgs.classpath,
966                            ClassLoader.getSystemClassLoader());
967                } else {
968                    cloader = ClassLoader.getSystemClassLoader();
969                }
970
971                try {
972                    ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
973                } catch (RuntimeException ex) {
974                    logAndPrintError(newStderr, "Error starting.", ex);
975                }
976            }
977        }
978    }
979
980    /**
981     * Handles post-fork cleanup of parent proc
982     *
983     * @param pid != 0; pid of child if &gt; 0 or indication of failed fork
984     * if &lt; 0;
985     * @param descriptors null-ok; file descriptors for child's new stdio if
986     * specified.
987     * @param pipeFd null-ok; pipe for communication with child.
988     * @param parsedArgs non-null; zygote args
989     * @return true for "exit command loop" and false for "continue command
990     * loop"
991     */
992    private boolean handleParentProc(int pid,
993            FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) {
994
995        if (pid > 0) {
996            setChildPgid(pid);
997        }
998
999        if (descriptors != null) {
1000            for (FileDescriptor fd: descriptors) {
1001                IoUtils.closeQuietly(fd);
1002            }
1003        }
1004
1005        boolean usingWrapper = false;
1006        if (pipeFd != null && pid > 0) {
1007            DataInputStream is = new DataInputStream(new FileInputStream(pipeFd));
1008            int innerPid = -1;
1009            try {
1010                innerPid = is.readInt();
1011            } catch (IOException ex) {
1012                Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex);
1013            } finally {
1014                try {
1015                    is.close();
1016                } catch (IOException ex) {
1017                }
1018            }
1019
1020            // Ensure that the pid reported by the wrapped process is either the
1021            // child process that we forked, or a descendant of it.
1022            if (innerPid > 0) {
1023                int parentPid = innerPid;
1024                while (parentPid > 0 && parentPid != pid) {
1025                    parentPid = Process.getParentPid(parentPid);
1026                }
1027                if (parentPid > 0) {
1028                    Log.i(TAG, "Wrapped process has pid " + innerPid);
1029                    pid = innerPid;
1030                    usingWrapper = true;
1031                } else {
1032                    Log.w(TAG, "Wrapped process reported a pid that is not a child of "
1033                            + "the process that we forked: childPid=" + pid
1034                            + " innerPid=" + innerPid);
1035                }
1036            }
1037        }
1038
1039        try {
1040            mSocketOutStream.writeInt(pid);
1041            mSocketOutStream.writeBoolean(usingWrapper);
1042        } catch (IOException ex) {
1043            Log.e(TAG, "Error reading from command socket", ex);
1044            return true;
1045        }
1046
1047        /*
1048         * If the peer wants to use the socket to wait on the
1049         * newly spawned process, then we're all done.
1050         */
1051        if (parsedArgs.peerWait) {
1052            try {
1053                mSocket.close();
1054            } catch (IOException ex) {
1055                Log.e(TAG, "Zygote: error closing sockets", ex);
1056            }
1057            return true;
1058        }
1059        return false;
1060    }
1061
1062    private void setChildPgid(int pid) {
1063        // Try to move the new child into the peer's process group.
1064        try {
1065            ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid()));
1066        } catch (IOException ex) {
1067            // This exception is expected in the case where
1068            // the peer is not in our session
1069            // TODO get rid of this log message in the case where
1070            // getsid(0) != getsid(peer.getPid())
1071            Log.i(TAG, "Zygote: setpgid failed. This is "
1072                + "normal if peer is not in our session");
1073        }
1074    }
1075
1076    /**
1077     * Logs an error message and prints it to the specified stream, if
1078     * provided
1079     *
1080     * @param newStderr null-ok; a standard error stream
1081     * @param message non-null; error message
1082     * @param ex null-ok an exception
1083     */
1084    private static void logAndPrintError (PrintStream newStderr,
1085            String message, Throwable ex) {
1086        Log.e(TAG, message, ex);
1087        if (newStderr != null) {
1088            newStderr.println(message + (ex == null ? "" : ex));
1089        }
1090    }
1091}
1092