123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106 |
- #!/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):
- sys.stdout.write(line.replace('</policy>', mac_perms_string))
- 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)
|