13e70d4793a096cab829c3141491944485e482f9fRobert Craig#!/usr/bin/env python
23e70d4793a096cab829c3141491944485e482f9fRobert Craig#
33e70d4793a096cab829c3141491944485e482f9fRobert Craig# Copyright (C) 2013 The Android Open Source Project
43e70d4793a096cab829c3141491944485e482f9fRobert Craig#
53e70d4793a096cab829c3141491944485e482f9fRobert Craig# Licensed under the Apache License, Version 2.0 (the "License");
63e70d4793a096cab829c3141491944485e482f9fRobert Craig# you may not use this file except in compliance with the License.
73e70d4793a096cab829c3141491944485e482f9fRobert Craig# You may obtain a copy of the License at
83e70d4793a096cab829c3141491944485e482f9fRobert Craig#
93e70d4793a096cab829c3141491944485e482f9fRobert Craig#      http://www.apache.org/licenses/LICENSE-2.0
103e70d4793a096cab829c3141491944485e482f9fRobert Craig#
113e70d4793a096cab829c3141491944485e482f9fRobert Craig# Unless required by applicable law or agreed to in writing, software
123e70d4793a096cab829c3141491944485e482f9fRobert Craig# distributed under the License is distributed on an "AS IS" BASIS,
133e70d4793a096cab829c3141491944485e482f9fRobert Craig# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
143e70d4793a096cab829c3141491944485e482f9fRobert Craig# See the License for the specific language governing permissions and
153e70d4793a096cab829c3141491944485e482f9fRobert Craig# limitations under the License.
163e70d4793a096cab829c3141491944485e482f9fRobert Craig
173e70d4793a096cab829c3141491944485e482f9fRobert Craig"""
183e70d4793a096cab829c3141491944485e482f9fRobert CraigTool to help modify an existing mac_permissions.xml with additional app
193e70d4793a096cab829c3141491944485e482f9fRobert Craigcerts not already found in that policy. This becomes useful when a directory
203e70d4793a096cab829c3141491944485e482f9fRobert Craigcontaining apps is searched and the certs from those apps are added to the
213e70d4793a096cab829c3141491944485e482f9fRobert Craigpolicy not already explicitly listed.
223e70d4793a096cab829c3141491944485e482f9fRobert Craig"""
233e70d4793a096cab829c3141491944485e482f9fRobert Craig
243e70d4793a096cab829c3141491944485e482f9fRobert Craigimport sys
253e70d4793a096cab829c3141491944485e482f9fRobert Craigimport os
263e70d4793a096cab829c3141491944485e482f9fRobert Craigimport argparse
273e70d4793a096cab829c3141491944485e482f9fRobert Craigfrom base64 import b16encode, b64decode
283e70d4793a096cab829c3141491944485e482f9fRobert Craigimport fileinput
293e70d4793a096cab829c3141491944485e482f9fRobert Craigimport re
303e70d4793a096cab829c3141491944485e482f9fRobert Craigimport subprocess
313e70d4793a096cab829c3141491944485e482f9fRobert Craigimport zipfile
323e70d4793a096cab829c3141491944485e482f9fRobert Craig
333e70d4793a096cab829c3141491944485e482f9fRobert CraigPEM_CERT_RE = """-----BEGIN CERTIFICATE-----
343e70d4793a096cab829c3141491944485e482f9fRobert Craig(.+?)
353e70d4793a096cab829c3141491944485e482f9fRobert Craig-----END CERTIFICATE-----
363e70d4793a096cab829c3141491944485e482f9fRobert Craig"""
373e70d4793a096cab829c3141491944485e482f9fRobert Craigdef collect_certs_for_app(filename):
383e70d4793a096cab829c3141491944485e482f9fRobert Craig  app_certs = set()
393e70d4793a096cab829c3141491944485e482f9fRobert Craig  with zipfile.ZipFile(filename, 'r') as apkzip:
403e70d4793a096cab829c3141491944485e482f9fRobert Craig    for info in apkzip.infolist():
413e70d4793a096cab829c3141491944485e482f9fRobert Craig      name = info.filename
423e70d4793a096cab829c3141491944485e482f9fRobert Craig      if name.startswith('META-INF/') and name.endswith(('.DSA', '.RSA')):
433e70d4793a096cab829c3141491944485e482f9fRobert Craig        cmd = ['openssl', 'pkcs7', '-inform', 'DER',
443e70d4793a096cab829c3141491944485e482f9fRobert Craig               '-outform', 'PEM', '-print_certs']
453e70d4793a096cab829c3141491944485e482f9fRobert Craig        p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
463e70d4793a096cab829c3141491944485e482f9fRobert Craig                             stderr=subprocess.PIPE)
473e70d4793a096cab829c3141491944485e482f9fRobert Craig        pem_string, err = p.communicate(apkzip.read(name))
483e70d4793a096cab829c3141491944485e482f9fRobert Craig        if err and err.strip():
493e70d4793a096cab829c3141491944485e482f9fRobert Craig          raise RuntimeError('Problem running openssl on %s (%s)' % (filename, e))
503e70d4793a096cab829c3141491944485e482f9fRobert Craig
513e70d4793a096cab829c3141491944485e482f9fRobert Craig        # turn multiline base64 to single line base16
523e70d4793a096cab829c3141491944485e482f9fRobert Craig        transform = lambda x: b16encode(b64decode(x.replace('\n', ''))).lower()
533e70d4793a096cab829c3141491944485e482f9fRobert Craig        results = re.findall(PEM_CERT_RE, pem_string, re.DOTALL)
543e70d4793a096cab829c3141491944485e482f9fRobert Craig        certs = [transform(i) for i in results]
553e70d4793a096cab829c3141491944485e482f9fRobert Craig
563e70d4793a096cab829c3141491944485e482f9fRobert Craig        app_certs.update(certs)
573e70d4793a096cab829c3141491944485e482f9fRobert Craig
583e70d4793a096cab829c3141491944485e482f9fRobert Craig  return app_certs
593e70d4793a096cab829c3141491944485e482f9fRobert Craig
603e70d4793a096cab829c3141491944485e482f9fRobert Craigdef add_leftover_certs(args):
613e70d4793a096cab829c3141491944485e482f9fRobert Craig  all_app_certs = set()
623e70d4793a096cab829c3141491944485e482f9fRobert Craig  for dirpath, _, files in os.walk(args.dir):
633e70d4793a096cab829c3141491944485e482f9fRobert Craig    transform = lambda x: os.path.join(dirpath, x)
643e70d4793a096cab829c3141491944485e482f9fRobert Craig    condition = lambda x: x.endswith('.apk')
653e70d4793a096cab829c3141491944485e482f9fRobert Craig    apps = [transform(i) for i in files if condition(i)]
663e70d4793a096cab829c3141491944485e482f9fRobert Craig
673e70d4793a096cab829c3141491944485e482f9fRobert Craig    # Collect certs for each app found
683e70d4793a096cab829c3141491944485e482f9fRobert Craig    for app in apps:
693e70d4793a096cab829c3141491944485e482f9fRobert Craig      app_certs = collect_certs_for_app(app)
703e70d4793a096cab829c3141491944485e482f9fRobert Craig      all_app_certs.update(app_certs)
713e70d4793a096cab829c3141491944485e482f9fRobert Craig
723e70d4793a096cab829c3141491944485e482f9fRobert Craig  if all_app_certs:
733e70d4793a096cab829c3141491944485e482f9fRobert Craig    policy_certs = set()
743e70d4793a096cab829c3141491944485e482f9fRobert Craig    with open(args.policy, 'r') as f:
753e70d4793a096cab829c3141491944485e482f9fRobert Craig      cert_pattern = 'signature="([a-fA-F0-9]+)"'
763e70d4793a096cab829c3141491944485e482f9fRobert Craig      policy_certs = re.findall(cert_pattern, f.read())
773e70d4793a096cab829c3141491944485e482f9fRobert Craig
783e70d4793a096cab829c3141491944485e482f9fRobert Craig    cert_diff = all_app_certs.difference(policy_certs)
793e70d4793a096cab829c3141491944485e482f9fRobert Craig
803e70d4793a096cab829c3141491944485e482f9fRobert Craig    # Build xml stanzas
813e70d4793a096cab829c3141491944485e482f9fRobert Craig    inner_tag = '<seinfo value="%s"/>' % args.seinfo
823e70d4793a096cab829c3141491944485e482f9fRobert Craig    stanza = '<signer signature="%s">%s</signer>'
833e70d4793a096cab829c3141491944485e482f9fRobert Craig    new_stanzas = [stanza % (cert, inner_tag) for cert in cert_diff]
843e70d4793a096cab829c3141491944485e482f9fRobert Craig    mac_perms_string = ''.join(new_stanzas)
853e70d4793a096cab829c3141491944485e482f9fRobert Craig    mac_perms_string += '</policy>'
863e70d4793a096cab829c3141491944485e482f9fRobert Craig
873e70d4793a096cab829c3141491944485e482f9fRobert Craig    # Inline replace with new policy stanzas
883e70d4793a096cab829c3141491944485e482f9fRobert Craig    for line in fileinput.input(args.policy, inplace=True):
893ea628fccc5c6276264c221adbfe057cf5df9b87Robert Craig      sys.stdout.write(line.replace('</policy>', mac_perms_string))
903e70d4793a096cab829c3141491944485e482f9fRobert Craig
913e70d4793a096cab829c3141491944485e482f9fRobert Craigdef main(argv):
923e70d4793a096cab829c3141491944485e482f9fRobert Craig  parser = argparse.ArgumentParser(description=__doc__)
933e70d4793a096cab829c3141491944485e482f9fRobert Craig
943e70d4793a096cab829c3141491944485e482f9fRobert Craig  parser.add_argument('-s', '--seinfo', dest='seinfo', required=True,
953e70d4793a096cab829c3141491944485e482f9fRobert Craig                      help='seinfo tag for each generated stanza')
963e70d4793a096cab829c3141491944485e482f9fRobert Craig  parser.add_argument('-d', '--dir', dest='dir', required=True,
973e70d4793a096cab829c3141491944485e482f9fRobert Craig                      help='Directory to search for apks')
983e70d4793a096cab829c3141491944485e482f9fRobert Craig  parser.add_argument('-f', '--file', dest='policy', required=True,
993e70d4793a096cab829c3141491944485e482f9fRobert Craig                      help='mac_permissions.xml policy file')
1003e70d4793a096cab829c3141491944485e482f9fRobert Craig
1013e70d4793a096cab829c3141491944485e482f9fRobert Craig  parser.set_defaults(func=add_leftover_certs)
1023e70d4793a096cab829c3141491944485e482f9fRobert Craig  args = parser.parse_args()
1033e70d4793a096cab829c3141491944485e482f9fRobert Craig  args.func(args)
1043e70d4793a096cab829c3141491944485e482f9fRobert Craig
1053e70d4793a096cab829c3141491944485e482f9fRobert Craigif __name__ == '__main__':
1063e70d4793a096cab829c3141491944485e482f9fRobert Craig  main(sys.argv)
107