1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.pm; 18 19import com.android.internal.util.XmlUtils; 20 21import org.xmlpull.v1.XmlPullParser; 22import org.xmlpull.v1.XmlPullParserException; 23import org.xmlpull.v1.XmlSerializer; 24 25import android.annotation.NonNull; 26import android.content.pm.PackageParser; 27import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion; 28import android.content.pm.Signature; 29import android.util.Log; 30 31import java.io.IOException; 32import java.security.cert.CertificateException; 33import java.util.ArrayList; 34 35class PackageSignatures { 36 37 @NonNull PackageParser.SigningDetails mSigningDetails; 38 39 PackageSignatures(PackageSignatures orig) { 40 if (orig != null && orig.mSigningDetails != PackageParser.SigningDetails.UNKNOWN) { 41 mSigningDetails = new PackageParser.SigningDetails(orig.mSigningDetails); 42 } else { 43 mSigningDetails = PackageParser.SigningDetails.UNKNOWN; 44 } 45 } 46 47 PackageSignatures(PackageParser.SigningDetails signingDetails) { 48 mSigningDetails = signingDetails; 49 } 50 51 PackageSignatures() { 52 mSigningDetails = PackageParser.SigningDetails.UNKNOWN; 53 } 54 55 void writeXml(XmlSerializer serializer, String tagName, 56 ArrayList<Signature> writtenSignatures) throws IOException { 57 if (mSigningDetails.signatures == null) { 58 return; 59 } 60 serializer.startTag(null, tagName); 61 serializer.attribute(null, "count", Integer.toString(mSigningDetails.signatures.length)); 62 serializer.attribute(null, "schemeVersion", 63 Integer.toString(mSigningDetails.signatureSchemeVersion)); 64 writeCertsListXml(serializer, writtenSignatures, mSigningDetails.signatures, null); 65 66 // if we have past signer certificate information, write it out 67 if (mSigningDetails.pastSigningCertificates != null) { 68 serializer.startTag(null, "pastSigs"); 69 serializer.attribute(null, "count", 70 Integer.toString(mSigningDetails.pastSigningCertificates.length)); 71 writeCertsListXml( 72 serializer, writtenSignatures, mSigningDetails.pastSigningCertificates, 73 mSigningDetails.pastSigningCertificatesFlags); 74 serializer.endTag(null, "pastSigs"); 75 } 76 serializer.endTag(null, tagName); 77 } 78 79 private void writeCertsListXml(XmlSerializer serializer, ArrayList<Signature> writtenSignatures, 80 Signature[] signatures, int[] flags) throws IOException { 81 for (int i=0; i<signatures.length; i++) { 82 serializer.startTag(null, "cert"); 83 final Signature sig = signatures[i]; 84 final int sigHash = sig.hashCode(); 85 final int numWritten = writtenSignatures.size(); 86 int j; 87 for (j=0; j<numWritten; j++) { 88 Signature writtenSig = writtenSignatures.get(j); 89 if (writtenSig.hashCode() == sigHash && writtenSig.equals(sig)) { 90 serializer.attribute(null, "index", Integer.toString(j)); 91 break; 92 } 93 } 94 if (j >= numWritten) { 95 writtenSignatures.add(sig); 96 serializer.attribute(null, "index", Integer.toString(numWritten)); 97 serializer.attribute(null, "key", sig.toCharsString()); 98 } 99 if (flags != null) { 100 serializer.attribute(null, "flags", Integer.toString(flags[i])); 101 } 102 serializer.endTag(null, "cert"); 103 } 104 } 105 106 void readXml(XmlPullParser parser, ArrayList<Signature> readSignatures) 107 throws IOException, XmlPullParserException { 108 PackageParser.SigningDetails.Builder builder = 109 new PackageParser.SigningDetails.Builder(); 110 111 String countStr = parser.getAttributeValue(null, "count"); 112 if (countStr == null) { 113 PackageManagerService.reportSettingsProblem(Log.WARN, 114 "Error in package manager settings: <sigs> has" 115 + " no count at " + parser.getPositionDescription()); 116 XmlUtils.skipCurrentTag(parser); 117 } 118 final int count = Integer.parseInt(countStr); 119 120 String schemeVersionStr = parser.getAttributeValue(null, "schemeVersion"); 121 int signatureSchemeVersion; 122 if (schemeVersionStr == null) { 123 PackageManagerService.reportSettingsProblem(Log.WARN, 124 "Error in package manager settings: <sigs> has no schemeVersion at " 125 + parser.getPositionDescription()); 126 signatureSchemeVersion = SignatureSchemeVersion.UNKNOWN; 127 } else { 128 signatureSchemeVersion = Integer.parseInt(schemeVersionStr); 129 } 130 builder.setSignatureSchemeVersion(signatureSchemeVersion); 131 Signature[] signatures = new Signature[count]; 132 int pos = readCertsListXml(parser, readSignatures, signatures, null, builder); 133 builder.setSignatures(signatures); 134 if (pos < count) { 135 // Should never happen -- there is an error in the written 136 // settings -- but if it does we don't want to generate 137 // a bad array. 138 Signature[] newSigs = new Signature[pos]; 139 System.arraycopy(signatures, 0, newSigs, 0, pos); 140 builder = builder.setSignatures(newSigs); 141 PackageManagerService.reportSettingsProblem(Log.WARN, 142 "Error in package manager settings: <sigs> count does not match number of " 143 + " <cert> entries" + parser.getPositionDescription()); 144 } 145 146 try { 147 mSigningDetails = builder.build(); 148 } catch (CertificateException e) { 149 PackageManagerService.reportSettingsProblem(Log.WARN, 150 "Error in package manager settings: <sigs> " 151 + "unable to convert certificate(s) to public key(s)."); 152 mSigningDetails = PackageParser.SigningDetails.UNKNOWN; 153 } 154 } 155 156 private int readCertsListXml(XmlPullParser parser, ArrayList<Signature> readSignatures, 157 Signature[] signatures, int[] flags, PackageParser.SigningDetails.Builder builder) 158 throws IOException, XmlPullParserException { 159 int count = signatures.length; 160 int pos = 0; 161 162 int outerDepth = parser.getDepth(); 163 int type; 164 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 165 && (type != XmlPullParser.END_TAG 166 || parser.getDepth() > outerDepth)) { 167 if (type == XmlPullParser.END_TAG 168 || type == XmlPullParser.TEXT) { 169 continue; 170 } 171 172 String tagName = parser.getName(); 173 if (tagName.equals("cert")) { 174 if (pos < count) { 175 String index = parser.getAttributeValue(null, "index"); 176 if (index != null) { 177 try { 178 int idx = Integer.parseInt(index); 179 String key = parser.getAttributeValue(null, "key"); 180 if (key == null) { 181 if (idx >= 0 && idx < readSignatures.size()) { 182 Signature sig = readSignatures.get(idx); 183 if (sig != null) { 184 signatures[pos] = readSignatures.get(idx); 185 } else { 186 PackageManagerService.reportSettingsProblem(Log.WARN, 187 "Error in package manager settings: <cert> " 188 + "index " + index + " is not defined at " 189 + parser.getPositionDescription()); 190 } 191 } else { 192 PackageManagerService.reportSettingsProblem(Log.WARN, 193 "Error in package manager settings: <cert> " 194 + "index " + index + " is out of bounds at " 195 + parser.getPositionDescription()); 196 } 197 } else { 198 while (readSignatures.size() <= idx) { 199 readSignatures.add(null); 200 } 201 Signature sig = new Signature(key); 202 readSignatures.set(idx, sig); 203 signatures[pos] = sig; 204 } 205 } catch (NumberFormatException e) { 206 PackageManagerService.reportSettingsProblem(Log.WARN, 207 "Error in package manager settings: <cert> " 208 + "index " + index + " is not a number at " 209 + parser.getPositionDescription()); 210 } catch (IllegalArgumentException e) { 211 PackageManagerService.reportSettingsProblem(Log.WARN, 212 "Error in package manager settings: <cert> " 213 + "index " + index + " has an invalid signature at " 214 + parser.getPositionDescription() + ": " 215 + e.getMessage()); 216 } 217 218 if (flags != null) { 219 String flagsStr = parser.getAttributeValue(null, "flags"); 220 if (flagsStr != null) { 221 try { 222 flags[pos] = Integer.parseInt(flagsStr); 223 } catch (NumberFormatException e) { 224 PackageManagerService.reportSettingsProblem(Log.WARN, 225 "Error in package manager settings: <cert> " 226 + "flags " + flagsStr + " is not a number at " 227 + parser.getPositionDescription()); 228 } 229 } else { 230 PackageManagerService.reportSettingsProblem(Log.WARN, 231 "Error in package manager settings: <cert> has no" 232 + " flags at " + parser.getPositionDescription()); 233 } 234 } 235 } else { 236 PackageManagerService.reportSettingsProblem(Log.WARN, 237 "Error in package manager settings: <cert> has" 238 + " no index at " + parser.getPositionDescription()); 239 } 240 } else { 241 PackageManagerService.reportSettingsProblem(Log.WARN, 242 "Error in package manager settings: too " 243 + "many <cert> tags, expected " + count 244 + " at " + parser.getPositionDescription()); 245 } 246 pos++; 247 XmlUtils.skipCurrentTag(parser); 248 } else if (tagName.equals("pastSigs")) { 249 if (flags == null) { 250 // we haven't encountered pastSigs yet, go ahead 251 String countStr = parser.getAttributeValue(null, "count"); 252 if (countStr == null) { 253 PackageManagerService.reportSettingsProblem(Log.WARN, 254 "Error in package manager settings: <pastSigs> has" 255 + " no count at " + parser.getPositionDescription()); 256 XmlUtils.skipCurrentTag(parser); 257 } 258 try { 259 final int pastSigsCount = Integer.parseInt(countStr); 260 Signature[] pastSignatures = new Signature[pastSigsCount]; 261 int[] pastSignaturesFlags = new int[pastSigsCount]; 262 int pastSigsPos = readCertsListXml(parser, readSignatures, pastSignatures, 263 pastSignaturesFlags, builder); 264 builder = builder 265 .setPastSigningCertificates(pastSignatures) 266 .setPastSigningCertificatesFlags(pastSignaturesFlags); 267 268 if (pastSigsPos < pastSigsCount) { 269 // Should never happen -- there is an error in the written 270 // settings -- but if it does we don't want to generate 271 // a bad array. 272 Signature[] newSigs = new Signature[pastSigsPos]; 273 System.arraycopy(pastSignatures, 0, newSigs, 0, pastSigsPos); 274 int[] newFlags = new int[pastSigsPos]; 275 System.arraycopy(pastSignaturesFlags, 0, newFlags, 0, pastSigsPos); 276 builder = builder 277 .setPastSigningCertificates(newSigs) 278 .setPastSigningCertificatesFlags(newFlags); 279 PackageManagerService.reportSettingsProblem(Log.WARN, 280 "Error in package manager settings: <pastSigs> count does not " 281 + "match number of <cert> entries " 282 + parser.getPositionDescription()); 283 } 284 } catch (NumberFormatException e) { 285 PackageManagerService.reportSettingsProblem(Log.WARN, 286 "Error in package manager settings: <pastSigs> " 287 + "count " + countStr + " is not a number at " 288 + parser.getPositionDescription()); 289 } 290 } else { 291 PackageManagerService.reportSettingsProblem(Log.WARN, 292 "<pastSigs> encountered multiple times under the same <sigs> at " 293 + parser.getPositionDescription()); 294 XmlUtils.skipCurrentTag(parser); 295 } 296 } else { 297 PackageManagerService.reportSettingsProblem(Log.WARN, 298 "Unknown element under <sigs>: " 299 + parser.getName()); 300 XmlUtils.skipCurrentTag(parser); 301 } 302 } 303 return pos; 304 } 305 306 @Override 307 public String toString() { 308 StringBuffer buf = new StringBuffer(128); 309 buf.append("PackageSignatures{"); 310 buf.append(Integer.toHexString(System.identityHashCode(this))); 311 buf.append(" version:"); 312 buf.append(mSigningDetails.signatureSchemeVersion); 313 buf.append(", signatures:["); 314 if (mSigningDetails.signatures != null) { 315 for (int i = 0; i < mSigningDetails.signatures.length; i++) { 316 if (i > 0) buf.append(", "); 317 buf.append(Integer.toHexString( 318 mSigningDetails.signatures[i].hashCode())); 319 } 320 } 321 buf.append("]"); 322 buf.append(", past signatures:["); 323 if (mSigningDetails.pastSigningCertificates != null) { 324 for (int i = 0; i < mSigningDetails.pastSigningCertificates.length; i++) { 325 if (i > 0) buf.append(", "); 326 buf.append(Integer.toHexString( 327 mSigningDetails.pastSigningCertificates[i].hashCode())); 328 buf.append(" flags: "); 329 buf.append(Integer.toHexString(mSigningDetails.pastSigningCertificatesFlags[i])); 330 } 331 } 332 buf.append("]}"); 333 return buf.toString(); 334 } 335} 336