@@ -234,26 +234,69 @@ export async function getRootCommand(adbClient: Adb.DeviceClient): Promise<RootC
234234export 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+
257300export 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