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