xref: /aosp_15_r20/system/sepolicy/tools/post_process_mac_perms (revision e4a36f4174b17bbab9dc043f4a65dc8d87377290)
1*e4a36f41SAndroid Build Coastguard Worker#!/usr/bin/env python
2*e4a36f41SAndroid Build Coastguard Worker#
3*e4a36f41SAndroid Build Coastguard Worker# Copyright (C) 2013 The Android Open Source Project
4*e4a36f41SAndroid Build Coastguard Worker#
5*e4a36f41SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*e4a36f41SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*e4a36f41SAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*e4a36f41SAndroid Build Coastguard Worker#
9*e4a36f41SAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*e4a36f41SAndroid Build Coastguard Worker#
11*e4a36f41SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*e4a36f41SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*e4a36f41SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*e4a36f41SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*e4a36f41SAndroid Build Coastguard Worker# limitations under the License.
16*e4a36f41SAndroid Build Coastguard Worker
17*e4a36f41SAndroid Build Coastguard Worker"""
18*e4a36f41SAndroid Build Coastguard WorkerTool to help modify an existing mac_permissions.xml with additional app
19*e4a36f41SAndroid Build Coastguard Workercerts not already found in that policy. This becomes useful when a directory
20*e4a36f41SAndroid Build Coastguard Workercontaining apps is searched and the certs from those apps are added to the
21*e4a36f41SAndroid Build Coastguard Workerpolicy not already explicitly listed.
22*e4a36f41SAndroid Build Coastguard Worker"""
23*e4a36f41SAndroid Build Coastguard Worker
24*e4a36f41SAndroid Build Coastguard Workerimport sys
25*e4a36f41SAndroid Build Coastguard Workerimport os
26*e4a36f41SAndroid Build Coastguard Workerimport argparse
27*e4a36f41SAndroid Build Coastguard Workerfrom base64 import b16encode, b64decode
28*e4a36f41SAndroid Build Coastguard Workerimport fileinput
29*e4a36f41SAndroid Build Coastguard Workerimport re
30*e4a36f41SAndroid Build Coastguard Workerimport subprocess
31*e4a36f41SAndroid Build Coastguard Workerimport zipfile
32*e4a36f41SAndroid Build Coastguard Worker
33*e4a36f41SAndroid Build Coastguard WorkerPEM_CERT_RE = """-----BEGIN CERTIFICATE-----
34*e4a36f41SAndroid Build Coastguard Worker(.+?)
35*e4a36f41SAndroid Build Coastguard Worker-----END CERTIFICATE-----
36*e4a36f41SAndroid Build Coastguard Worker"""
37*e4a36f41SAndroid Build Coastguard Workerdef collect_certs_for_app(filename):
38*e4a36f41SAndroid Build Coastguard Worker  app_certs = set()
39*e4a36f41SAndroid Build Coastguard Worker  with zipfile.ZipFile(filename, 'r') as apkzip:
40*e4a36f41SAndroid Build Coastguard Worker    for info in apkzip.infolist():
41*e4a36f41SAndroid Build Coastguard Worker      name = info.filename
42*e4a36f41SAndroid Build Coastguard Worker      if name.startswith('META-INF/') and name.endswith(('.DSA', '.RSA')):
43*e4a36f41SAndroid Build Coastguard Worker        cmd = ['openssl', 'pkcs7', '-inform', 'DER',
44*e4a36f41SAndroid Build Coastguard Worker               '-outform', 'PEM', '-print_certs']
45*e4a36f41SAndroid Build Coastguard Worker        p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
46*e4a36f41SAndroid Build Coastguard Worker                             stderr=subprocess.PIPE)
47*e4a36f41SAndroid Build Coastguard Worker        pem_string, err = p.communicate(apkzip.read(name))
48*e4a36f41SAndroid Build Coastguard Worker        if err and err.strip():
49*e4a36f41SAndroid Build Coastguard Worker          raise RuntimeError('Problem running openssl on %s (%s)' % (filename, e))
50*e4a36f41SAndroid Build Coastguard Worker
51*e4a36f41SAndroid Build Coastguard Worker        # turn multiline base64 to single line base16
52*e4a36f41SAndroid Build Coastguard Worker        transform = lambda x: b16encode(b64decode(x.replace('\n', ''))).lower()
53*e4a36f41SAndroid Build Coastguard Worker        results = re.findall(PEM_CERT_RE, pem_string, re.DOTALL)
54*e4a36f41SAndroid Build Coastguard Worker        certs = [transform(i) for i in results]
55*e4a36f41SAndroid Build Coastguard Worker
56*e4a36f41SAndroid Build Coastguard Worker        app_certs.update(certs)
57*e4a36f41SAndroid Build Coastguard Worker
58*e4a36f41SAndroid Build Coastguard Worker  return app_certs
59*e4a36f41SAndroid Build Coastguard Worker
60*e4a36f41SAndroid Build Coastguard Workerdef add_leftover_certs(args):
61*e4a36f41SAndroid Build Coastguard Worker  all_app_certs = set()
62*e4a36f41SAndroid Build Coastguard Worker  for dirpath, _, files in os.walk(args.dir):
63*e4a36f41SAndroid Build Coastguard Worker    transform = lambda x: os.path.join(dirpath, x)
64*e4a36f41SAndroid Build Coastguard Worker    condition = lambda x: x.endswith('.apk')
65*e4a36f41SAndroid Build Coastguard Worker    apps = [transform(i) for i in files if condition(i)]
66*e4a36f41SAndroid Build Coastguard Worker
67*e4a36f41SAndroid Build Coastguard Worker    # Collect certs for each app found
68*e4a36f41SAndroid Build Coastguard Worker    for app in apps:
69*e4a36f41SAndroid Build Coastguard Worker      app_certs = collect_certs_for_app(app)
70*e4a36f41SAndroid Build Coastguard Worker      all_app_certs.update(app_certs)
71*e4a36f41SAndroid Build Coastguard Worker
72*e4a36f41SAndroid Build Coastguard Worker  if all_app_certs:
73*e4a36f41SAndroid Build Coastguard Worker    policy_certs = set()
74*e4a36f41SAndroid Build Coastguard Worker    with open(args.policy, 'r') as f:
75*e4a36f41SAndroid Build Coastguard Worker      cert_pattern = 'signature="([a-fA-F0-9]+)"'
76*e4a36f41SAndroid Build Coastguard Worker      policy_certs = re.findall(cert_pattern, f.read())
77*e4a36f41SAndroid Build Coastguard Worker
78*e4a36f41SAndroid Build Coastguard Worker    cert_diff = all_app_certs.difference(policy_certs)
79*e4a36f41SAndroid Build Coastguard Worker
80*e4a36f41SAndroid Build Coastguard Worker    # Build xml stanzas
81*e4a36f41SAndroid Build Coastguard Worker    inner_tag = '<seinfo value="%s"/>' % args.seinfo
82*e4a36f41SAndroid Build Coastguard Worker    stanza = '<signer signature="%s">%s</signer>'
83*e4a36f41SAndroid Build Coastguard Worker    new_stanzas = [stanza % (cert, inner_tag) for cert in cert_diff]
84*e4a36f41SAndroid Build Coastguard Worker    mac_perms_string = ''.join(new_stanzas)
85*e4a36f41SAndroid Build Coastguard Worker    mac_perms_string += '</policy>'
86*e4a36f41SAndroid Build Coastguard Worker
87*e4a36f41SAndroid Build Coastguard Worker    # Inline replace with new policy stanzas
88*e4a36f41SAndroid Build Coastguard Worker    for line in fileinput.input(args.policy, inplace=True):
89*e4a36f41SAndroid Build Coastguard Worker      sys.stdout.write(line.replace('</policy>', mac_perms_string))
90*e4a36f41SAndroid Build Coastguard Worker
91*e4a36f41SAndroid Build Coastguard Workerdef main(argv):
92*e4a36f41SAndroid Build Coastguard Worker  parser = argparse.ArgumentParser(description=__doc__)
93*e4a36f41SAndroid Build Coastguard Worker
94*e4a36f41SAndroid Build Coastguard Worker  parser.add_argument('-s', '--seinfo', dest='seinfo', required=True,
95*e4a36f41SAndroid Build Coastguard Worker                      help='seinfo tag for each generated stanza')
96*e4a36f41SAndroid Build Coastguard Worker  parser.add_argument('-d', '--dir', dest='dir', required=True,
97*e4a36f41SAndroid Build Coastguard Worker                      help='Directory to search for apks')
98*e4a36f41SAndroid Build Coastguard Worker  parser.add_argument('-f', '--file', dest='policy', required=True,
99*e4a36f41SAndroid Build Coastguard Worker                      help='mac_permissions.xml policy file')
100*e4a36f41SAndroid Build Coastguard Worker
101*e4a36f41SAndroid Build Coastguard Worker  parser.set_defaults(func=add_leftover_certs)
102*e4a36f41SAndroid Build Coastguard Worker  args = parser.parse_args()
103*e4a36f41SAndroid Build Coastguard Worker  args.func(args)
104*e4a36f41SAndroid Build Coastguard Worker
105*e4a36f41SAndroid Build Coastguard Workerif __name__ == '__main__':
106*e4a36f41SAndroid Build Coastguard Worker  main(sys.argv)
107