8282 */
8383@ Internal
8484public class TrustedCertificateStore implements ConscryptCertStore {
85+ private static final String PREFIX_MANAGED = "managed:" ;
8586 private static String PREFIX_SYSTEM = "system:" ;
8687 private static final String PREFIX_USER = "user:" ;
8788
89+ public static final boolean isManaged (String alias ) {
90+ return alias .startsWith (PREFIX_MANAGED );
91+ }
8892 public static final boolean isSystem (String alias ) {
8993 return alias .startsWith (PREFIX_SYSTEM );
9094 }
@@ -93,6 +97,7 @@ public static final boolean isUser(String alias) {
9397 }
9498
9599 private static class PreloadHolder {
100+ private static File defaultCaCertsManagedDir ;
96101 private static File defaultCaCertsSystemDir ;
97102 private static File defaultCaCertsAddedDir ;
98103 private static File defaultCaCertsDeletedDir ;
@@ -101,6 +106,7 @@ private static class PreloadHolder {
101106 String ANDROID_ROOT = System .getenv ("ANDROID_ROOT" );
102107 String ANDROID_DATA = System .getenv ("ANDROID_DATA" );
103108 File updatableDir = new File ("/apex/com.android.conscrypt/cacerts" );
109+ defaultCaCertsManagedDir = new File (ANDROID_DATA + "/misc/cacerts_managed" );
104110 if (shouldUseApex (updatableDir )) {
105111 defaultCaCertsSystemDir = updatableDir ;
106112 } else {
@@ -147,20 +153,23 @@ public static void setDefaultUserDirectory(File root) {
147153 PreloadHolder .defaultCaCertsDeletedDir = new File (root , "cacerts-removed" );
148154 }
149155
156+ private final File managedDir ;
150157 private final File systemDir ;
151158 private final File addedDir ;
152159 private final File deletedDir ;
153160
154161 public TrustedCertificateStore () {
155- this (PreloadHolder .defaultCaCertsSystemDir , PreloadHolder .defaultCaCertsAddedDir ,
156- PreloadHolder .defaultCaCertsDeletedDir );
162+ this (PreloadHolder .defaultCaCertsManagedDir , PreloadHolder .defaultCaCertsSystemDir ,
163+ PreloadHolder .defaultCaCertsAddedDir , PreloadHolder . defaultCaCertsDeletedDir );
157164 }
158165
159166 public TrustedCertificateStore (File baseDir ) {
160- this (baseDir , PreloadHolder .defaultCaCertsAddedDir , PreloadHolder .defaultCaCertsDeletedDir );
167+ this (PreloadHolder .defaultCaCertsManagedDir , baseDir , PreloadHolder .defaultCaCertsAddedDir ,
168+ PreloadHolder .defaultCaCertsDeletedDir );
161169 }
162170
163- public TrustedCertificateStore (File systemDir , File addedDir , File deletedDir ) {
171+ public TrustedCertificateStore (File managedDir , File systemDir , File addedDir , File deletedDir ) {
172+ this .managedDir = managedDir ;
164173 this .systemDir = systemDir ;
165174 this .addedDir = addedDir ;
166175 this .deletedDir = deletedDir ;
@@ -173,7 +182,7 @@ public Certificate getCertificate(String alias) {
173182 public Certificate getCertificate (String alias , boolean includeDeletedSystem ) {
174183
175184 File file = fileForAlias (alias );
176- if (file == null || (isUser (alias ) && isTombstone (file ))) {
185+ if (file == null || (isUser (alias ) && isTombstone (file )) || ( isManaged ( alias ) && isTombstone ( file )) ) {
177186 return null ;
178187 }
179188 X509Certificate cert = readCertificate (file );
@@ -193,6 +202,8 @@ private File fileForAlias(String alias) {
193202 File file ;
194203 if (isSystem (alias )) {
195204 file = new File (systemDir , alias .substring (PREFIX_SYSTEM .length ()));
205+ } else if (isManaged (alias )) {
206+ file = new File (managedDir , alias .substring (PREFIX_MANAGED .length ()));
196207 } else if (isUser (alias )) {
197208 file = new File (addedDir , alias .substring (PREFIX_USER .length ()));
198209 } else {
@@ -268,6 +279,7 @@ public Date getCreationDate(String alias) {
268279
269280 public Set <String > aliases () {
270281 Set <String > result = new HashSet <String >();
282+ addAliases (result , PREFIX_MANAGED , managedDir );
271283 addAliases (result , PREFIX_USER , addedDir );
272284 addAliases (result , PREFIX_SYSTEM , systemDir );
273285 return result ;
@@ -292,6 +304,21 @@ private void addAliases(Set<String> result, String prefix, File dir) {
292304 }
293305 }
294306
307+ public Set <String > allManagedAliases () {
308+ Set <String > result = new HashSet <String >();
309+ String [] files = managedDir .list ();
310+ if (files == null ) {
311+ return result ;
312+ }
313+ for (String filename : files ) {
314+ String alias = PREFIX_MANAGED + filename ;
315+ if (containsAlias (alias , true )) {
316+ result .add (alias );
317+ }
318+ }
319+ return result ;
320+ }
321+
295322 public Set <String > allSystemAliases () {
296323 Set <String > result = new HashSet <String >();
297324 String [] files = systemDir .list ();
@@ -324,6 +351,10 @@ public String getCertificateAlias(Certificate c, boolean includeDeletedSystem) {
324351 return null ;
325352 }
326353 X509Certificate x = (X509Certificate ) c ;
354+ File managed = getCertificateFile (managedDir , x );
355+ if (managed .exists ()) {
356+ return PREFIX_MANAGED + managed .getName ();
357+ }
327358 File user = getCertificateFile (addedDir , x );
328359 if (user .exists ()) {
329360 return PREFIX_USER + user .getName ();
@@ -338,6 +369,14 @@ public String getCertificateAlias(Certificate c, boolean includeDeletedSystem) {
338369 return null ;
339370 }
340371
372+ /**
373+ * Returns true to indicate that the certificate was added by the
374+ * device owner, false otherwise.
375+ */
376+ public boolean isManagedCertificate (X509Certificate cert ) {
377+ return getCertificateFile (managedDir , cert ).exists ();
378+ }
379+
341380 /**
342381 * Returns true to indicate that the certificate was added by the
343382 * user, false otherwise.
@@ -383,6 +422,13 @@ public boolean match(X509Certificate ca) {
383422 return ca .getPublicKey ().equals (c .getPublicKey ());
384423 }
385424 };
425+ X509Certificate managed = findCert (managedDir ,
426+ c .getSubjectX500Principal (),
427+ selector ,
428+ X509Certificate .class );
429+ if (managed != null ) {
430+ return managed ;
431+ }
386432 X509Certificate user = findCert (addedDir ,
387433 c .getSubjectX500Principal (),
388434 selector ,
@@ -419,6 +465,10 @@ public boolean match(X509Certificate ca) {
419465 }
420466 };
421467 X500Principal issuer = c .getIssuerX500Principal ();
468+ X509Certificate managed = findCert (managedDir , issuer , selector , X509Certificate .class );
469+ if (managed != null ) {
470+ return managed ;
471+ }
422472 X509Certificate user = findCert (addedDir , issuer , selector , X509Certificate .class );
423473 if (user != null ) {
424474 return user ;
@@ -445,6 +495,10 @@ public boolean match(X509Certificate ca) {
445495 }
446496 };
447497 X500Principal issuer = c .getIssuerX500Principal ();
498+ Set <X509Certificate > managedCerts = findCertSet (managedDir , issuer , selector );
499+ if (managedCerts != null ) {
500+ issuers = managedCerts ;
501+ }
448502 Set <X509Certificate > userAddedCerts = findCertSet (addedDir , issuer , selector );
449503 if (userAddedCerts != null ) {
450504 issuers = userAddedCerts ;
@@ -609,7 +663,7 @@ private File file(File dir, String hash, int index) {
609663 * silently ignores the certificate if it already exists in the
610664 * store.
611665 */
612- public void installCertificate (X509Certificate cert ) throws IOException , CertificateException {
666+ public void installCertificate (boolean isManaged , X509Certificate cert ) throws IOException , CertificateException {
613667 if (cert == null ) {
614668 throw new NullPointerException ("cert == null" );
615669 }
@@ -628,6 +682,13 @@ public void installCertificate(X509Certificate cert) throws IOException, Certifi
628682 // return taking no further action.
629683 return ;
630684 }
685+ File managed = getCertificateFile (managedDir , cert );
686+ if (isManaged ) {
687+ if (!managed .exists ()) {
688+ writeCertificate (managed , cert );
689+ }
690+ return ;
691+ }
631692 File user = getCertificateFile (addedDir , cert );
632693 if (user .exists ()) {
633694 // we have an already installed user cert, bail.
@@ -667,7 +728,7 @@ public void deleteCertificateEntry(String alias) throws IOException, Certificate
667728 writeCertificate (deleted , cert );
668729 return ;
669730 }
670- if (isUser (alias )) {
731+ if (isUser (alias ) || isManaged ( alias ) ) {
671732 // truncate the file to make a tombstone by opening and closing.
672733 // we need ensure that we don't leave a gap before a valid cert.
673734 new FileOutputStream (file ).close ();
@@ -678,22 +739,32 @@ public void deleteCertificateEntry(String alias) throws IOException, Certificate
678739 }
679740
680741 private void removeUnnecessaryTombstones (String alias ) throws IOException {
681- if (!isUser (alias )) {
742+ if (!isUser (alias ) && ! isManaged ( alias ) ) {
682743 throw new AssertionError (alias );
683744 }
684745 int dotIndex = alias .lastIndexOf ('.' );
685746 if (dotIndex == -1 ) {
686747 throw new AssertionError (alias );
687748 }
688749
689- String hash = alias .substring (PREFIX_USER .length (), dotIndex );
750+ File dir = null ;
751+ String hash = null ;
752+ if (isUser (alias )) {
753+ dir = addedDir ;
754+ hash = alias .substring (PREFIX_USER .length (), dotIndex );
755+ } else if (isManaged (alias )) {
756+ dir = managedDir ;
757+ hash = alias .substring (PREFIX_MANAGED .length (), dotIndex );
758+ } else {
759+ throw new AssertionError (alias );
760+ }
690761 int lastTombstoneIndex = Integer .parseInt (alias .substring (dotIndex + 1 ));
691762
692- if (file (addedDir , hash , lastTombstoneIndex + 1 ).exists ()) {
763+ if (file (dir , hash , lastTombstoneIndex + 1 ).exists ()) {
693764 return ;
694765 }
695766 while (lastTombstoneIndex >= 0 ) {
696- File file = file (addedDir , hash , lastTombstoneIndex );
767+ File file = file (dir , hash , lastTombstoneIndex );
697768 if (!isTombstone (file )) {
698769 break ;
699770 }
0 commit comments