Skip to content

Commit f05c380

Browse files
committed
Check for apex cert when deciding if ADB cert reinstall is required
Aiming to avoid issues where upgrades/remounts/other weirdness results in a broken setup (no APEX cert even though it's used) because the /system cert hides the issue.
1 parent fb5cb5b commit f05c380

File tree

1 file changed

+58
-11
lines changed

1 file changed

+58
-11
lines changed

src/interceptors/android/adb-commands.ts

Lines changed: 58 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -234,26 +234,69 @@ export async function getRootCommand(adbClient: Adb.DeviceClient): Promise<RootC
234234
export async function hasCertInstalled(
235235
adbClient: Adb.DeviceClient,
236236
certHash: string,
237-
certFingerprint: string
237+
expectedFingerprint: string
238238
) {
239-
try {
240-
const certPath = `/system/etc/security/cacerts/${certHash}.0`;
241-
const certStream = await adbClient.pull(certPath);
239+
// We have to check both of these paths. If /system exists but /apex does not, then something
240+
// has gone wrong and we need to reinstall the cert to fix it.
241+
const systemCertPath = `/system/etc/security/cacerts/${certHash}.0`;
242+
const apexCertPath = `/apex/com.android.conscrypt/cacerts/${certHash}.0`;
242243

243-
// Wait until it's clear that the read is successful
244-
const data = await streamToBuffer(certStream);
244+
try {
245+
const existingCertChecks = await Promise.all([
246+
adbClient.pull(systemCertPath)
247+
.then(async (certStream) => {
248+
if (await isMatchingCert(certStream, expectedFingerprint)) {
249+
console.log('Matching /system cacert exists');
250+
return true;
251+
} else {
252+
console.log('/system cacert exists but mismatched');
253+
return false;
254+
}
255+
}),
256+
257+
run(adbClient, ['ls', '/apex/com.android.conscrypt'])
258+
.then(async (lsOutput) => {
259+
if (lsOutput.includes('cacerts')) {
260+
const certStream = await adbClient.pull(apexCertPath);
261+
if (await isMatchingCert(certStream, expectedFingerprint)) {
262+
console.log('Matching /apex cacert exists');
263+
return true;
264+
} else {
265+
console.log('/apex cacert exists but mismatched');
266+
return false;
267+
}
268+
} else {
269+
console.log('No need for /apex cacerts injection');
270+
// If apex dir doesn't exist, we don't need to inject anything
271+
return true;
272+
}
273+
})
274+
]);
245275

246-
// The device already has an HTTP Toolkit cert. But is it the right one?
247-
const existingCert = parseCert(data.toString('utf8'));
248-
const existingFingerprint = getCertificateFingerprint(existingCert);
249-
return certFingerprint === existingFingerprint;
250-
} catch (e) {
276+
return existingCertChecks.every(result => result === true);
277+
} catch (e: any) {
251278
// Couldn't read the cert, or some other error - either way, we probably
252279
// don't have a working system cert installed.
280+
console.log(`Couldn't detect cert via ADB: ${e.message}`);
253281
return false;
254282
}
255283
}
256284

285+
// The device already has an HTTP Toolkit cert. But is it the right one?
286+
const isMatchingCert = async (certStream: stream.Readable, expectedFingerprint: string) => {
287+
// Wait until it's clear that the read is successful
288+
const data = await streamToBuffer(certStream);
289+
290+
// Note that due to https://github.com/DeviceFarmer/adbkit/issues/464 we may see
291+
// 'empty' data for files that are actually missing entirely.
292+
if (data.byteLength === 0) return false;
293+
294+
const certData = data.toString('utf8');
295+
const existingCert = parseCert(certData);
296+
const existingFingerprint = getCertificateFingerprint(existingCert);
297+
return expectedFingerprint === existingFingerprint;
298+
}
299+
257300
export async function injectSystemCertificate(
258301
adbClient: Adb.DeviceClient,
259302
runAsRoot: RootCmd,
@@ -306,6 +349,10 @@ export async function injectSystemCertificate(
306349
# this globally as APEX mounts are namespaced per process, so we need to inject a
307350
# bind mount for this directory into every mount namespace.
308351
352+
# First we mount for the shell itself, for completeness and so we can see this
353+
# when we check for correct installation on later runs
354+
mount --bind /system/etc/security/cacerts /apex/com.android.conscrypt/cacerts
355+
309356
# First we get the Zygote process(es), which launch each app
310357
ZYGOTE_PID=$(pidof zygote || true)
311358
ZYGOTE64_PID=$(pidof zygote64 || true)

0 commit comments

Comments
 (0)