def test_ReplaceCerts_skipNonExistentCerts(self): cert1_path = os.path.join(self.testdata_dir, 'platform.x509.pem') with open(cert1_path) as cert1_fp: cert1 = cert1_fp.read() cert2_path = os.path.join(self.testdata_dir, 'media.x509.pem') with open(cert2_path) as cert2_fp: cert2 = cert2_fp.read() cert3_path = os.path.join(self.testdata_dir, 'testkey.x509.pem') with open(cert3_path) as cert3_fp: cert3 = cert3_fp.read() input_xml = self.MAC_PERMISSIONS_XML.format( base64.b16encode(common.ParseCertificate(cert1)).lower(), base64.b16encode(common.ParseCertificate(cert2)).lower()) output_xml = self.MAC_PERMISSIONS_XML.format( base64.b16encode(common.ParseCertificate(cert3)).lower(), base64.b16encode(common.ParseCertificate(cert2)).lower()) common.OPTIONS.key_map = { cert1_path[:-9]: cert3_path[:-9], 'non-existent': cert3_path[:-9], cert2_path[:-9]: 'non-existent', } self.assertEqual(output_xml, ReplaceCerts(input_xml))
def ReplaceCerts(data): """Given a string of data, replace all occurences of a set of X509 certs with a newer set of X509 certs and return the updated data string.""" for old, new in OPTIONS.key_map.iteritems(): try: if OPTIONS.verbose: print " Replacing %s.x509.pem with %s.x509.pem" % (old, new) f = open(old + ".x509.pem") old_cert16 = base64.b16encode(common.ParseCertificate(f.read())).lower() f.close() f = open(new + ".x509.pem") new_cert16 = base64.b16encode(common.ParseCertificate(f.read())).lower() f.close() # Only match entire certs. pattern = "\\b"+old_cert16+"\\b" (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE) if OPTIONS.verbose: print " Replaced %d occurence(s) of %s.x509.pem with " \ "%s.x509.pem" % (num, old, new) except IOError as e: if e.errno == errno.ENOENT and not OPTIONS.verbose: continue print " Error accessing %s. %s. Skip replacing %s.x509.pem " \ "with %s.x509.pem." % (e.filename, e.strerror, old, new) return data
def ReplaceCerts(data): """Replaces all the occurences of X.509 certs with the new ones. The mapping info is read from OPTIONS.key_map. Non-existent certificate will be skipped. After the replacement, it additionally checks for duplicate entries, which would otherwise fail the policy loading code in frameworks/base/services/core/java/com/android/server/pm/SELinuxMMAC.java. Args: data: Input string that contains a set of X.509 certs. Returns: A string after the replacement. Raises: AssertionError: On finding duplicate entries. """ for old, new in OPTIONS.key_map.iteritems(): if OPTIONS.verbose: print(" Replacing %s.x509.pem with %s.x509.pem" % (old, new)) try: with open(old + ".x509.pem") as old_fp: old_cert16 = base64.b16encode( common.ParseCertificate(old_fp.read())).lower() with open(new + ".x509.pem") as new_fp: new_cert16 = base64.b16encode( common.ParseCertificate(new_fp.read())).lower() except IOError as e: if OPTIONS.verbose or e.errno != errno.ENOENT: print( " Error accessing %s: %s.\nSkip replacing %s.x509.pem with " "%s.x509.pem." % (e.filename, e.strerror, old, new)) continue # Only match entire certs. pattern = "\\b" + old_cert16 + "\\b" (data, num) = re.subn(pattern, new_cert16, data, flags=re.IGNORECASE) if OPTIONS.verbose: print( " Replaced %d occurence(s) of %s.x509.pem with %s.x509.pem" % (num, old, new)) # Verify that there're no duplicate entries after the replacement. Note that # it's only checking entries with global seinfo at the moment (i.e. ignoring # the ones with inner packages). (Bug: 69479366) root = ElementTree.fromstring(data) signatures = [ signer.attrib['signature'] for signer in root.findall('signer') ] assert len(signatures) == len(set(signatures)), \ "Found duplicate entries after cert replacement: {}".format(data) return data
def test_ReplaceCerts_duplicateEntries(self): cert1_path = os.path.join(self.testdata_dir, 'platform.x509.pem') with open(cert1_path) as cert1_fp: cert1 = cert1_fp.read() cert2_path = os.path.join(self.testdata_dir, 'media.x509.pem') with open(cert2_path) as cert2_fp: cert2 = cert2_fp.read() # Replace cert1 with cert2, which leads to duplicate entries. input_xml = self.MAC_PERMISSIONS_XML.format( base64.b16encode(common.ParseCertificate(cert1)).lower(), base64.b16encode(common.ParseCertificate(cert2)).lower()) common.OPTIONS.key_map = { cert1_path[:-9]: cert2_path[:-9], } self.assertRaises(AssertionError, ReplaceCerts, input_xml)
def test_ParseCertificate(self): cert = os.path.join(self.testdata_dir, 'testkey.x509.pem') cmd = ['openssl', 'x509', '-in', cert, '-outform', 'DER'] proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) expected, _ = proc.communicate() self.assertEqual(0, proc.returncode) with open(cert) as cert_fp: actual = common.ParseCertificate(cert_fp.read()) self.assertEqual(expected, actual)
def FindLocalCerts(self): to_load = [] for top in OPTIONS.local_cert_dirs: for dirpath, _, filenames in os.walk(top): certs = [os.path.join(dirpath, i) for i in filenames if i.endswith(".x509.pem")] if certs: to_load.extend(certs) for i in to_load: with open(i) as f: cert = common.ParseCertificate(f.read()) name, _ = os.path.splitext(i) name, _ = os.path.splitext(name) self.Add(cert, name)
def CertFromPKCS7(data, filename): """Read the cert out of a PKCS#7-format file (which is what is stored in a signed .apk).""" Push(filename + ":") try: p = common.Run(["openssl", "pkcs7", "-inform", "DER", "-outform", "PEM", "-print_certs"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) out, err = p.communicate(data) if err and not err.strip(): AddProblem("error reading cert:\n" + err) return None cert = common.ParseCertificate(out) if not cert: AddProblem("error parsing cert output") return None return cert finally: Pop()