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 * [--] <args for RuntimeInit > 300 * <li> If <code>--runtime-init</code> is absent: 301 * [--] <classname> [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 > 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 > 0 or indication of failed fork 947 * if < 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