2014-03-12 15:56:33 +01:00
|
|
|
#!/usr/bin/env python
|
|
|
|
#
|
|
|
|
# Copyright (C) 2013 The Android Open Source Project
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
|
|
|
|
"""
|
|
|
|
Tool to help modify an existing mac_permissions.xml with additional app
|
|
|
|
certs not already found in that policy. This becomes useful when a directory
|
|
|
|
containing apps is searched and the certs from those apps are added to the
|
|
|
|
policy not already explicitly listed.
|
|
|
|
"""
|
|
|
|
|
|
|
|
import sys
|
|
|
|
import os
|
|
|
|
import argparse
|
|
|
|
from base64 import b16encode, b64decode
|
|
|
|
import fileinput
|
|
|
|
import re
|
|
|
|
import subprocess
|
|
|
|
import zipfile
|
|
|
|
|
|
|
|
PEM_CERT_RE = """-----BEGIN CERTIFICATE-----
|
|
|
|
(.+?)
|
|
|
|
-----END CERTIFICATE-----
|
|
|
|
"""
|
|
|
|
def collect_certs_for_app(filename):
|
|
|
|
app_certs = set()
|
|
|
|
with zipfile.ZipFile(filename, 'r') as apkzip:
|
|
|
|
for info in apkzip.infolist():
|
|
|
|
name = info.filename
|
|
|
|
if name.startswith('META-INF/') and name.endswith(('.DSA', '.RSA')):
|
|
|
|
cmd = ['openssl', 'pkcs7', '-inform', 'DER',
|
|
|
|
'-outform', 'PEM', '-print_certs']
|
|
|
|
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.PIPE)
|
|
|
|
pem_string, err = p.communicate(apkzip.read(name))
|
|
|
|
if err and err.strip():
|
|
|
|
raise RuntimeError('Problem running openssl on %s (%s)' % (filename, e))
|
|
|
|
|
|
|
|
# turn multiline base64 to single line base16
|
|
|
|
transform = lambda x: b16encode(b64decode(x.replace('\n', ''))).lower()
|
|
|
|
results = re.findall(PEM_CERT_RE, pem_string, re.DOTALL)
|
|
|
|
certs = [transform(i) for i in results]
|
|
|
|
|
|
|
|
app_certs.update(certs)
|
|
|
|
|
|
|
|
return app_certs
|
|
|
|
|
|
|
|
def add_leftover_certs(args):
|
|
|
|
all_app_certs = set()
|
|
|
|
for dirpath, _, files in os.walk(args.dir):
|
|
|
|
transform = lambda x: os.path.join(dirpath, x)
|
|
|
|
condition = lambda x: x.endswith('.apk')
|
|
|
|
apps = [transform(i) for i in files if condition(i)]
|
|
|
|
|
|
|
|
# Collect certs for each app found
|
|
|
|
for app in apps:
|
|
|
|
app_certs = collect_certs_for_app(app)
|
|
|
|
all_app_certs.update(app_certs)
|
|
|
|
|
|
|
|
if all_app_certs:
|
|
|
|
policy_certs = set()
|
|
|
|
with open(args.policy, 'r') as f:
|
|
|
|
cert_pattern = 'signature="([a-fA-F0-9]+)"'
|
|
|
|
policy_certs = re.findall(cert_pattern, f.read())
|
|
|
|
|
|
|
|
cert_diff = all_app_certs.difference(policy_certs)
|
|
|
|
|
|
|
|
# Build xml stanzas
|
|
|
|
inner_tag = '<seinfo value="%s"/>' % args.seinfo
|
|
|
|
stanza = '<signer signature="%s">%s</signer>'
|
|
|
|
new_stanzas = [stanza % (cert, inner_tag) for cert in cert_diff]
|
|
|
|
mac_perms_string = ''.join(new_stanzas)
|
|
|
|
mac_perms_string += '</policy>'
|
|
|
|
|
|
|
|
# Inline replace with new policy stanzas
|
|
|
|
for line in fileinput.input(args.policy, inplace=True):
|
2014-04-01 16:54:15 +02:00
|
|
|
sys.stdout.write(line.replace('</policy>', mac_perms_string))
|
2014-03-12 15:56:33 +01:00
|
|
|
|
|
|
|
def main(argv):
|
|
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
|
|
|
|
|
|
parser.add_argument('-s', '--seinfo', dest='seinfo', required=True,
|
|
|
|
help='seinfo tag for each generated stanza')
|
|
|
|
parser.add_argument('-d', '--dir', dest='dir', required=True,
|
|
|
|
help='Directory to search for apks')
|
|
|
|
parser.add_argument('-f', '--file', dest='policy', required=True,
|
|
|
|
help='mac_permissions.xml policy file')
|
|
|
|
|
|
|
|
parser.set_defaults(func=add_leftover_certs)
|
|
|
|
args = parser.parse_args()
|
|
|
|
args.func(args)
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main(sys.argv)
|