Bug 1214812 - [mozdevice] - adb.py, adb_android.py - if possible always run adbd as root, set SELinux to Permissive, r=gbrown.

This commit is contained in:
Bob Clary 2015-12-02 08:34:20 -08:00
parent f2a34ba40e
commit 9115f85f5d
2 changed files with 153 additions and 20 deletions

View File

@ -2,7 +2,6 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
from abc import ABCMeta, abstractmethod
import os
import posixpath
import re
@ -11,6 +10,8 @@ import tempfile
import time
import traceback
from abc import ABCMeta, abstractmethod
class ADBProcess(object):
"""ADBProcess encapsulates the data related to executing the adb process."""
@ -535,49 +536,58 @@ class ADBDevice(ADBCommand):
self._have_su = False
self._have_android_su = False
uid = 'uid=0'
# Is shell already running as root?
# Catch exceptions due to the potential for segfaults
# calling su when using an improperly rooted device.
try:
if self.shell_output("id").find(uid) != -1:
self._have_root_shell = True
except ADBError:
self._logger.debug("Check for root shell failed")
# Note this check to see if adbd is running is performed on
# the device in the state it exists in when the ADBDevice is
# initialized. It may be the case that it has been manipulated
# since its last boot and that its current state does not
# match the state the device will have immediately after a
# reboot. For example, if adb root was called manually prior
# to ADBDevice being initialized, then self._have_root_shell
# will not reflect the state of the device after it has been
# rebooted again. Therefore this check will need to be
# performed again after a reboot.
self._check_adb_root(timeout=timeout)
uid = 'uid=0'
# Do we have a 'Superuser' sh like su?
try:
if self.shell_output("su -c id").find(uid) != -1:
if self.shell_output("su -c id", timeout=timeout).find(uid) != -1:
self._have_su = True
self._logger.info("su -c supported")
except ADBError:
self._logger.debug("Check for su failed")
self._logger.debug("Check for su -c failed")
# Do we have Android's su?
try:
if self.shell_output("su 0 id").find(uid) != -1:
if self.shell_output("su 0 id", timeout=timeout).find(uid) != -1:
self._have_android_su = True
self._logger.info("su 0 supported")
except ADBError:
self._logger.debug("Check for Android su failed")
self._logger.debug("Check for su 0 failed")
self._mkdir_p = None
# Force the use of /system/bin/ls or /system/xbin/ls in case
# there is /sbin/ls which embeds ansi escape codes to colorize
# the output. Detect if we are using busybox ls. We want each
# entry on a single line and we don't want . or ..
if self.shell_bool("/system/bin/ls /"):
if self.shell_bool("/system/bin/ls /", timeout=timeout):
self._ls = "/system/bin/ls"
elif self.shell_bool("/system/xbin/ls /"):
elif self.shell_bool("/system/xbin/ls /", timeout=timeout):
self._ls = "/system/xbin/ls"
else:
raise ADBError("ADBDevice.__init__: ls not found")
try:
self.shell_output("%s -1A /" % self._ls)
self.shell_output("%s -1A /" % self._ls, timeout=timeout)
self._ls += " -1A"
except ADBError:
self._ls += " -a"
# Do we have cp?
self._have_cp = self.shell_bool("type cp")
self._have_cp = self.shell_bool("type cp", timeout=timeout)
self._logger.debug("ADBDevice: %s" % self.__dict__)
@ -614,6 +624,28 @@ class ADBDevice(ADBCommand):
raise ValueError("Unable to get device serial")
def _check_adb_root(self, timeout=None):
self._have_root_shell = False
uid = 'uid=0'
# Is shell already running as root?
try:
if self.shell_output("id", timeout=timeout).find(uid) != -1:
self._have_root_shell = True
self._logger.info("adbd running as root")
except ADBError:
self._logger.debug("Check for root shell failed")
# Do we need to run adb root to get a root shell?
try:
if (not self._have_root_shell and
self.command_output(
["root"],
timeout=timeout).find("cannot run as root") == -1):
self._have_root_shell = True
self._logger.info("adbd restarted as root")
except ADBError:
self._logger.debug("Check for root adbd failed")
@staticmethod
def _escape_command_line(cmd):
@ -1859,6 +1891,7 @@ class ADBDevice(ADBCommand):
# 'wait-for-device' arguments which is an error in newer
# versions of adb.
self.command_output([], timeout=timeout)
self._check_adb_root(timeout=timeout)
return self.is_device_ready(timeout=timeout)
@abstractmethod

View File

@ -6,6 +6,8 @@ import os
import re
import time
from abc import ABCMeta
import version_codes
from adb import ADBDevice, ADBError
@ -24,6 +26,99 @@ class ADBAndroid(ADBDevice):
if adbdevice.process_exist("org.mozilla.fennec"):
print "Fennec is running"
"""
__metaclass__ = ABCMeta
def __init__(self,
device=None,
adb='adb',
adb_host=None,
adb_port=None,
test_root='',
logger_name='adb',
timeout=300,
verbose=False,
device_ready_retry_wait=20,
device_ready_retry_attempts=3):
"""Initializes the ADBAndroid object.
:param device: When a string is passed, it is interpreted as the
device serial number. This form is not compatible with
devices containing a ":" in the serial; in this case
ValueError will be raised.
When a dictionary is passed it must have one or both of
the keys "device_serial" and "usb". This is compatible
with the dictionaries in the list returned by
ADBHost.devices(). If the value of device_serial is a
valid serial not containing a ":" it will be used to
identify the device, otherwise the value of the usb key,
prefixed with "usb:" is used.
If None is passed and there is exactly one device attached
to the host, that device is used. If there is more than one
device attached, ValueError is raised. If no device is
attached the constructor will block until a device is
attached or the timeout is reached.
:type device: dict, str or None
:param adb_host: host of the adb server to connect to.
:type adb_host: str or None
:param adb_port: port of the adb server to connect to.
:type adb_port: integer or None
:param str logger_name: logging logger name. Defaults to 'adb'.
:param integer device_ready_retry_wait: number of seconds to wait
between attempts to check if the device is ready after a
reboot.
:param integer device_ready_retry_attempts: number of attempts when
checking if a device is ready.
:raises: * ADBError
* ADBTimeoutError
* ValueError
"""
ADBDevice.__init__(self, device=device, adb=adb,
adb_host=adb_host, adb_port=adb_port,
logger_name=logger_name, timeout=timeout,
verbose=verbose,
device_ready_retry_wait=device_ready_retry_wait,
device_ready_retry_attempts=device_ready_retry_attempts)
# https://source.android.com/devices/tech/security/selinux/index.html
# setenforce
# usage: setenforce [ Enforcing | Permissive | 1 | 0 ]
# getenforce returns either Enforcing or Permissive
try:
self.selinux = True
if self.shell_output('getenforce', timeout=timeout) != 'Permissive':
self._logger.info('Setting SELinux Permissive Mode')
self.shell_output("setenforce Permissive", timeout=timeout, root=True)
except ADBError:
self.selinux = False
def reboot(self, timeout=None):
"""Reboots the device.
:param timeout: optional integer specifying the maximum time in
seconds for any spawned adb process to complete before
throwing an ADBTimeoutError.
This timeout is per adb call. The total time spent
may exceed this value. If it is not specified, the value
set in the ADB constructor is used.
:raises: * ADBTimeoutError
* ADBError
reboot() reboots the device, issues an adb wait-for-device in order to
wait for the device to complete rebooting, then calls is_device_ready()
to determine if the device has completed booting.
If the device supports running adbd as root, adbd will be
restarted running as root. Then, if the device supports
SELinux, setenforce Permissive will be called to change
SELinux to permissive. This must be done after adbd is
restarted in order for the SELinux Permissive setting to
persist.
"""
ready = ADBDevice.reboot(self, timeout=timeout)
self._check_adb_root(timeout=timeout)
return ready
# Informational methods
@ -99,10 +194,15 @@ class ADBAndroid(ADBDevice):
failure = "Device state: %s" % state
success = False
else:
if self.is_dir(ready_path, timeout=timeout):
self.rmdir(ready_path, timeout=timeout)
self.mkdir(ready_path, timeout=timeout)
self.rmdir(ready_path, timeout=timeout)
if (self.selinux and
self.shell_output('getenforce',
timeout=timeout) != 'Permissive'):
self._logger.info('Setting SELinux Permissive Mode')
self.shell_output("setenforce Permissive", timeout=timeout, root=True)
if self.is_dir(ready_path, timeout=timeout, root=True):
self.rmdir(ready_path, timeout=timeout, root=True)
self.mkdir(ready_path, timeout=timeout, root=True)
self.rmdir(ready_path, timeout=timeout, root=True)
# Invoke the pm list commands to see if it is up and
# running.
for pm_list_cmd in pm_list_commands: