/**
   * Applies zygote security policy per bug #1042973. A root peer may spawn an instance with any
   * capabilities. All other uids may spawn instances with any of the capabilities in the peer's
   * permitted set but no more.
   *
   * @param args non-null; zygote spawner arguments
   * @param peer non-null; peer credentials
   * @throws ZygoteSecurityException
   */
  private static void applyCapabilitiesSecurityPolicy(
      Arguments args, Credentials peer, String peerSecurityContext) throws ZygoteSecurityException {

    if (args.permittedCapabilities == 0 && args.effectiveCapabilities == 0) {
      // nothing to check
      return;
    }

    boolean allowed =
        SELinux.checkSELinuxAccess(
            peerSecurityContext, peerSecurityContext, "zygote", "specifycapabilities");
    if (!allowed) {
      throw new ZygoteSecurityException("Peer may not specify capabilities");
    }

    if (peer.getUid() == 0) {
      // root may specify anything
      return;
    }

    long permittedCaps;

    try {
      permittedCaps = ZygoteInit.capgetPermitted(peer.getPid());
    } catch (IOException ex) {
      throw new ZygoteSecurityException("Error retrieving peer's capabilities.");
    }

    /*
     * Ensure that the client did not specify an effective set larger
     * than the permitted set. The kernel will enforce this too, but we
     * do it here to make the following check easier.
     */
    if (((~args.permittedCapabilities) & args.effectiveCapabilities) != 0) {
      throw new ZygoteSecurityException(
          "Effective capabilities cannot be superset of " + " permitted capabilities");
    }

    /*
     * Ensure that the new permitted (and thus the new effective) set is
     * a subset of the peer process's permitted set
     */

    if (((~permittedCaps) & args.permittedCapabilities) != 0) {
      throw new ZygoteSecurityException("Peer specified unpermitted capabilities");
    }
  }
  /**
   * Applies zygote security policy per bugs #875058 and #1082165. Based on the credentials of the
   * process issuing a zygote command:
   *
   * <ol>
   *   <li>uid 0 (root) may specify any uid, gid, and setgroups() list
   *   <li>uid 1000 (Process.SYSTEM_UID) may specify any uid &gt; 1000 in normal operation. It may
   *       also specify any gid and setgroups() list it chooses. In factory test mode, it may
   *       specify any UID.
   *   <li>Any other uid may not specify any uid, gid, or setgroups list. The uid and gid will be
   *       inherited from the requesting process.
   * </ul>
   *
   * @param args non-null; zygote spawner arguments
   * @param peer non-null; peer credentials
   * @throws ZygoteSecurityException
   */
  private static void applyUidSecurityPolicy(
      Arguments args, Credentials peer, String peerSecurityContext) throws ZygoteSecurityException {

    int peerUid = peer.getUid();

    if (peerUid == 0) {
      // Root can do what it wants
    } else if (peerUid == Process.SYSTEM_UID) {
      // System UID is restricted, except in factory test mode
      String factoryTest = SystemProperties.get("ro.factorytest");
      boolean uidRestricted;

      /* In normal operation, SYSTEM_UID can only specify a restricted
       * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
       */
      uidRestricted = !(factoryTest.equals("1") || factoryTest.equals("2"));

      if (uidRestricted && args.uidSpecified && (args.uid < Process.SYSTEM_UID)) {
        throw new ZygoteSecurityException(
            "System UID may not launch process with UID < " + Process.SYSTEM_UID);
      }
    } else {
      // Everything else
      if (args.uidSpecified || args.gidSpecified || args.gids != null) {
        throw new ZygoteSecurityException("App UIDs may not specify uid's or gid's");
      }
    }

    if (args.uidSpecified || args.gidSpecified || args.gids != null) {
      boolean allowed =
          SELinux.checkSELinuxAccess(
              peerSecurityContext, peerSecurityContext, "zygote", "specifyids");
      if (!allowed) {
        throw new ZygoteSecurityException("Peer may not specify uid's or gid's");
      }
    }

    // If not otherwise specified, uid and gid are inherited from peer
    if (!args.uidSpecified) {
      args.uid = peer.getUid();
      args.uidSpecified = true;
    }
    if (!args.gidSpecified) {
      args.gid = peer.getGid();
      args.gidSpecified = true;
    }
  }
  /**
   * Applies zygote security policy. Based on the credentials of the process issuing a zygote
   * command:
   *
   * <ol>
   *   <li>uid 0 (root) may specify --invoke-with to launch Zygote with a wrapper command.
   *   <li>Any other uid may not specify any invoke-with argument.
   * </ul>
   *
   * @param args non-null; zygote spawner arguments
   * @param peer non-null; peer credentials
   * @throws ZygoteSecurityException
   */
  private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer)
      throws ZygoteSecurityException {
    int peerUid = peer.getUid();

    if (args.invokeWith != null && peerUid != 0) {
      throw new ZygoteSecurityException(
          "Peer is not permitted to specify " + "an explicit invoke-with wrapper command");
    }
  }
 private void setChildPgid(int pid) {
   // Try to move the new child into the peer's process group.
   try {
     ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid()));
   } catch (IOException ex) {
     // This exception is expected in the case where
     // the peer is not in our session
     // TODO get rid of this log message in the case where
     // getsid(0) != getsid(peer.getPid())
     Log.i(TAG, "Zygote: setpgid failed. This is " + "normal if peer is not in our session");
   }
 }
  /**
   * Applies zygote security policy per bug #1042973. Based on the credentials of the process
   * issuing a zygote command:
   *
   * <ol>
   *   <li>peers of uid 0 (root) and uid 1000 (Process.SYSTEM_UID) may specify any rlimits.
   *   <li>All other uids may not specify rlimits.
   * </ul>
   *
   * @param args non-null; zygote spawner arguments
   * @param peer non-null; peer credentials
   * @throws ZygoteSecurityException
   */
  private static void applyRlimitSecurityPolicy(Arguments args, Credentials peer)
      throws ZygoteSecurityException {

    int peerUid = peer.getUid();

    if (!(peerUid == 0 || peerUid == Process.SYSTEM_UID)) {
      // All peers with UID other than root or SYSTEM_UID
      if (args.rlimits != null) {
        throw new ZygoteSecurityException("This UID may not specify rlimits.");
      }
    }
  }
  /**
   * Applies zygote security policy. Based on the credentials of the process issuing a zygote
   * command:
   *
   * <ol>
   *   <li>uid 0 (root) may specify --invoke-with to launch Zygote with a wrapper command.
   *   <li>Any other uid may not specify any invoke-with argument.
   * </ul>
   *
   * @param args non-null; zygote spawner arguments
   * @param peer non-null; peer credentials
   * @throws ZygoteSecurityException
   */
  private static void applyInvokeWithSecurityPolicy(
      Arguments args, Credentials peer, String peerSecurityContext) throws ZygoteSecurityException {
    int peerUid = peer.getUid();

    if (args.invokeWith != null && peerUid != 0) {
      throw new ZygoteSecurityException(
          "Peer is not permitted to specify " + "an explicit invoke-with wrapper command");
    }

    if (args.invokeWith != null) {
      boolean allowed =
          SELinux.checkSELinuxAccess(
              peerSecurityContext, peerSecurityContext, "zygote", "specifyinvokewith");
      if (!allowed) {
        throw new ZygoteSecurityException(
            "Peer is not permitted to specify " + "an explicit invoke-with wrapper command");
      }
    }
  }
  /**
   * Applies zygote security policy per bug #1042973. Based on the credentials of the process
   * issuing a zygote command:
   *
   * <ol>
   *   <li>peers of uid 0 (root) and uid 1000 (Process.SYSTEM_UID) may specify any rlimits.
   *   <li>All other uids may not specify rlimits.
   * </ul>
   *
   * @param args non-null; zygote spawner arguments
   * @param peer non-null; peer credentials
   * @throws ZygoteSecurityException
   */
  private static void applyRlimitSecurityPolicy(
      Arguments args, Credentials peer, String peerSecurityContext) throws ZygoteSecurityException {

    int peerUid = peer.getUid();

    if (!(peerUid == 0 || peerUid == Process.SYSTEM_UID)) {
      // All peers with UID other than root or SYSTEM_UID
      if (args.rlimits != null) {
        throw new ZygoteSecurityException("This UID may not specify rlimits.");
      }
    }

    if (args.rlimits != null) {
      boolean allowed =
          SELinux.checkSELinuxAccess(
              peerSecurityContext, peerSecurityContext, "zygote", "specifyrlimits");
      if (!allowed) {
        throw new ZygoteSecurityException("Peer may not specify rlimits");
      }
    }
  }
  /**
   * Applies zygote security policy for SEAndroid information.
   *
   * @param args non-null; zygote spawner arguments
   * @param peer non-null; peer credentials
   * @throws ZygoteSecurityException
   */
  private static void applyseInfoSecurityPolicy(
      Arguments args, Credentials peer, String peerSecurityContext) throws ZygoteSecurityException {
    int peerUid = peer.getUid();

    if (args.seInfo == null) {
      // nothing to check
      return;
    }

    if (!(peerUid == 0 || peerUid == Process.SYSTEM_UID)) {
      // All peers with UID other than root or SYSTEM_UID
      throw new ZygoteSecurityException("This UID may not specify SEAndroid info.");
    }

    boolean allowed =
        SELinux.checkSELinuxAccess(
            peerSecurityContext, peerSecurityContext, "zygote", "specifyseinfo");
    if (!allowed) {
      throw new ZygoteSecurityException("Peer may not specify SEAndroid info");
    }

    return;
  }
  public void testLocalConnections() throws IOException {
    // create client and server socket
    LocalServerSocket localServerSocket = new LocalServerSocket(mSockAddr);
    LocalSocket clientSocket = new LocalSocket();

    // establish connection between client and server
    LocalSocketAddress locSockAddr = new LocalSocketAddress(mSockAddr);
    assertFalse(clientSocket.isConnected());
    clientSocket.connect(locSockAddr);
    assertTrue(clientSocket.isConnected());
    LocalSocket serverSocket = localServerSocket.accept();

    Credentials credent = clientSocket.getPeerCredentials();
    assertTrue(0 != credent.getPid());

    // send data from client to server
    OutputStream clientOutStream = clientSocket.getOutputStream();
    clientOutStream.write(12);
    InputStream serverInStream = serverSocket.getInputStream();
    assertEquals(12, serverInStream.read());

    // send data from server to client
    OutputStream serverOutStream = serverSocket.getOutputStream();
    serverOutStream.write(3);
    InputStream clientInStream = clientSocket.getInputStream();
    assertEquals(3, clientInStream.read());

    // Test sending and receiving file descriptors
    clientSocket.setFileDescriptorsForSend(new FileDescriptor[] {FileDescriptor.in});
    clientOutStream.write(32);
    assertEquals(32, serverInStream.read());

    FileDescriptor[] out = serverSocket.getAncillaryFileDescriptors();
    assertEquals(1, out.length);
    FileDescriptor fd = clientSocket.getFileDescriptor();
    assertTrue(fd.valid());

    // shutdown input stream of client
    clientSocket.shutdownInput();
    assertEquals(-1, clientInStream.read());

    // shutdown output stream of client
    clientSocket.shutdownOutput();
    try {
      clientOutStream.write(10);
      fail("testLocalSocket shouldn't come to here");
    } catch (IOException e) {
      // expected
    }

    // shutdown input stream of server
    serverSocket.shutdownInput();
    assertEquals(-1, serverInStream.read());

    // shutdown output stream of server
    serverSocket.shutdownOutput();
    try {
      serverOutStream.write(10);
      fail("testLocalSocket shouldn't come to here");
    } catch (IOException e) {
      // expected
    }

    // close client socket
    clientSocket.close();
    try {
      clientInStream.read();
      fail("testLocalSocket shouldn't come to here");
    } catch (IOException e) {
      // expected
    }

    // close server socket
    serverSocket.close();
    try {
      serverInStream.read();
      fail("testLocalSocket shouldn't come to here");
    } catch (IOException e) {
      // expected
    }
  }