• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.internal.os;
18 
19 import android.net.Credentials;
20 import android.net.LocalSocket;
21 import android.os.Process;
22 import android.os.SELinux;
23 import android.os.SystemProperties;
24 import android.util.Log;
25 
26 import dalvik.system.PathClassLoader;
27 import dalvik.system.Zygote;
28 
29 import java.io.BufferedReader;
30 import java.io.DataInputStream;
31 import java.io.DataOutputStream;
32 import java.io.FileDescriptor;
33 import java.io.FileInputStream;
34 import java.io.FileOutputStream;
35 import java.io.IOException;
36 import java.io.InputStreamReader;
37 import java.io.PrintStream;
38 import java.util.ArrayList;
39 
40 import libcore.io.ErrnoException;
41 import libcore.io.IoUtils;
42 import libcore.io.Libcore;
43 
44 /**
45  * A connection that can make spawn requests.
46  */
47 class 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      */
ZygoteConnection(LocalSocket socket)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      */
getFileDesciptor()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      */
run()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      */
runOnce()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      */
closeSocket()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          */
Arguments(String args[])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          */
parseArgs(String args[])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      */
readArgumentList()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      */
applyUidSecurityPolicy(Arguments args, Credentials peer, String peerSecurityContext)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      */
applyDebuggerSystemProperty(Arguments args)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      */
applyRlimitSecurityPolicy( Arguments args, Credentials peer, String peerSecurityContext)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      */
applyCapabilitiesSecurityPolicy( Arguments args, Credentials peer, String peerSecurityContext)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      */
applyInvokeWithSecurityPolicy(Arguments args, Credentials peer, String peerSecurityContext)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 SEAndroid information.
807      *
808      * @param args non-null; zygote spawner arguments
809      * @param peer non-null; peer credentials
810      * @throws ZygoteSecurityException
811      */
applyseInfoSecurityPolicy( Arguments args, Credentials peer, String peerSecurityContext)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 SEAndroid 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 SEAndroid 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      */
applyInvokeWithSystemProperty(Arguments args)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      */
handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)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      */
handleParentProc(int pid, FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs)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 
setChildPgid(int pid)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      */
logAndPrintError(PrintStream newStderr, String message, Throwable ex)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