1 /* 2 * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.security; 27 28 29 import java.net.URL; 30 import java.net.SocketPermission; 31 import java.util.ArrayList; 32 import java.util.List; 33 import java.util.Hashtable; 34 import java.io.ByteArrayInputStream; 35 import java.io.IOException; 36 import java.security.cert.*; 37 import sun.net.util.URLUtil; 38 import sun.security.util.IOUtils; 39 40 /** 41 * 42 * <p>This class extends the concept of a codebase to 43 * encapsulate not only the location (URL) but also the certificate chains 44 * that were used to verify signed code originating from that location. 45 * 46 * @author Li Gong 47 * @author Roland Schemers 48 * @since 1.2 49 */ 50 51 public class CodeSource implements java.io.Serializable { 52 53 private static final long serialVersionUID = 4977541819976013951L; 54 55 /** 56 * The code location. 57 * 58 * @serial 59 */ 60 private URL location; 61 62 /* 63 * The code signers. 64 */ 65 private transient CodeSigner[] signers = null; 66 67 /* 68 * The code signers. Certificate chains are concatenated. 69 */ 70 private transient java.security.cert.Certificate[] certs = null; 71 72 // cached SocketPermission used for matchLocation 73 private transient SocketPermission sp; 74 75 // for generating cert paths 76 private transient CertificateFactory factory = null; 77 78 /** 79 * A String form of the URL for use as a key in HashMaps/Sets. The String 80 * form should be behave in the same manner as the URL when compared for 81 * equality in a HashMap/Set, except that no nameservice lookup is done 82 * on the hostname (only string comparison), and the fragment is not 83 * considered. 84 */ 85 private transient String locationNoFragString; 86 87 /** 88 * Constructs a CodeSource and associates it with the specified 89 * location and set of certificates. 90 * 91 * @param url the location (URL). It may be {@code null}. 92 * @param certs the certificate(s). It may be {@code null}. The contents 93 * of the array are copied to protect against subsequent modification. 94 */ 95 public CodeSource(URL url, java.security.cert.Certificate[] certs) { 96 this.location = url; 97 if (url != null) { 98 this.locationNoFragString = URLUtil.urlNoFragString(url); 99 } 100 101 // Copy the supplied certs 102 if (certs != null) { 103 this.certs = certs.clone(); 104 } 105 } 106 107 /** 108 * Constructs a CodeSource and associates it with the specified 109 * location and set of code signers. 110 * 111 * @param url the location (URL). It may be {@code null}. 112 * @param signers the code signers. It may be {@code null}. The contents 113 * of the array are copied to protect against subsequent modification. 114 * 115 * @since 1.5 116 */ 117 public CodeSource(URL url, CodeSigner[] signers) { 118 this.location = url; 119 if (url != null) { 120 this.locationNoFragString = URLUtil.urlNoFragString(url); 121 } 122 123 // Copy the supplied signers 124 if (signers != null) { 125 this.signers = signers.clone(); 126 } 127 } 128 129 /** 130 * Returns the hash code value for this object. 131 * 132 * @return a hash code value for this object. 133 */ 134 @Override 135 public int hashCode() { 136 if (location != null) 137 return location.hashCode(); 138 else 139 return 0; 140 } 141 142 /** 143 * Tests for equality between the specified object and this 144 * object. Two CodeSource objects are considered equal if their 145 * locations are of identical value and if their signer certificate 146 * chains are of identical value. It is not required that 147 * the certificate chains be in the same order. 148 * 149 * @param obj the object to test for equality with this object. 150 * 151 * @return true if the objects are considered equal, false otherwise. 152 */ 153 @Override 154 public boolean equals(Object obj) { 155 if (obj == this) 156 return true; 157 158 // objects types must be equal 159 if (!(obj instanceof CodeSource)) 160 return false; 161 162 CodeSource cs = (CodeSource) obj; 163 164 // URLs must match 165 if (location == null) { 166 // if location is null, then cs.location must be null as well 167 if (cs.location != null) return false; 168 } else { 169 // if location is not null, then it must equal cs.location 170 if (!location.equals(cs.location)) return false; 171 } 172 173 // certs must match 174 return matchCerts(cs, true); 175 } 176 177 /** 178 * Returns the location associated with this CodeSource. 179 * 180 * @return the location (URL), or {@code null} if no URL was supplied 181 * during construction. 182 */ 183 public final URL getLocation() { 184 /* since URL is practically immutable, returning itself is not 185 a security problem */ 186 return this.location; 187 } 188 189 /** 190 * Returns a String form of the URL for use as a key in HashMaps/Sets. 191 */ 192 String getLocationNoFragString() { 193 return locationNoFragString; 194 } 195 196 /** 197 * Returns the certificates associated with this CodeSource. 198 * <p> 199 * If this CodeSource object was created using the 200 * {@link #CodeSource(URL url, CodeSigner[] signers)} 201 * constructor then its certificate chains are extracted and used to 202 * create an array of Certificate objects. Each signer certificate is 203 * followed by its supporting certificate chain (which may be empty). 204 * Each signer certificate and its supporting certificate chain is ordered 205 * bottom-to-top (i.e., with the signer certificate first and the (root) 206 * certificate authority last). 207 * 208 * @return a copy of the certificate array, or {@code null} if there 209 * is none. 210 */ 211 public final java.security.cert.Certificate[] getCertificates() { 212 if (certs != null) { 213 return certs.clone(); 214 215 } else if (signers != null) { 216 // Convert the code signers to certs 217 ArrayList<java.security.cert.Certificate> certChains = 218 new ArrayList<>(); 219 for (int i = 0; i < signers.length; i++) { 220 certChains.addAll( 221 signers[i].getSignerCertPath().getCertificates()); 222 } 223 certs = certChains.toArray( 224 new java.security.cert.Certificate[certChains.size()]); 225 return certs.clone(); 226 227 } else { 228 return null; 229 } 230 } 231 232 /** 233 * Returns the code signers associated with this CodeSource. 234 * <p> 235 * If this CodeSource object was created using the 236 * {@link #CodeSource(URL url, java.security.cert.Certificate[] certs)} 237 * constructor then its certificate chains are extracted and used to 238 * create an array of CodeSigner objects. Note that only X.509 certificates 239 * are examined - all other certificate types are ignored. 240 * 241 * @return a copy of the code signer array, or {@code null} if there 242 * is none. 243 * 244 * @since 1.5 245 */ 246 public final CodeSigner[] getCodeSigners() { 247 if (signers != null) { 248 return signers.clone(); 249 250 } else if (certs != null) { 251 // Convert the certs to code signers 252 signers = convertCertArrayToSignerArray(certs); 253 return signers.clone(); 254 255 } else { 256 return null; 257 } 258 } 259 260 /** 261 * Returns true if this CodeSource object "implies" the specified CodeSource. 262 * <p> 263 * More specifically, this method makes the following checks. 264 * If any fail, it returns false. If they all succeed, it returns true. 265 * <ul> 266 * <li> <i>codesource</i> must not be null. 267 * <li> If this object's certificates are not null, then all 268 * of this object's certificates must be present in <i>codesource</i>'s 269 * certificates. 270 * <li> If this object's location (getLocation()) is not null, then the 271 * following checks are made against this object's location and 272 * <i>codesource</i>'s: 273 * <ul> 274 * <li> <i>codesource</i>'s location must not be null. 275 * 276 * <li> If this object's location 277 * equals <i>codesource</i>'s location, then return true. 278 * 279 * <li> This object's protocol (getLocation().getProtocol()) must be 280 * equal to <i>codesource</i>'s protocol, ignoring case. 281 * 282 * <li> If this object's host (getLocation().getHost()) is not null, 283 * then the SocketPermission 284 * constructed with this object's host must imply the 285 * SocketPermission constructed with <i>codesource</i>'s host. 286 * 287 * <li> If this object's port (getLocation().getPort()) is not 288 * equal to -1 (that is, if a port is specified), it must equal 289 * <i>codesource</i>'s port or default port 290 * (codesource.getLocation().getDefaultPort()). 291 * 292 * <li> If this object's file (getLocation().getFile()) doesn't equal 293 * <i>codesource</i>'s file, then the following checks are made: 294 * If this object's file ends with "/-", 295 * then <i>codesource</i>'s file must start with this object's 296 * file (exclusive the trailing "-"). 297 * If this object's file ends with a "/*", 298 * then <i>codesource</i>'s file must start with this object's 299 * file and must not have any further "/" separators. 300 * If this object's file doesn't end with a "/", 301 * then <i>codesource</i>'s file must match this object's 302 * file with a '/' appended. 303 * 304 * <li> If this object's reference (getLocation().getRef()) is 305 * not null, it must equal <i>codesource</i>'s reference. 306 * 307 * </ul> 308 * </ul> 309 * <p> 310 * For example, the codesource objects with the following locations 311 * and null certificates all imply 312 * the codesource with the location "http://www.example.com/classes/foo.jar" 313 * and null certificates: 314 * <pre> 315 * http: 316 * http://*.example.com/classes/* 317 * http://www.example.com/classes/- 318 * http://www.example.com/classes/foo.jar 319 * </pre> 320 * 321 * Note that if this CodeSource has a null location and a null 322 * certificate chain, then it implies every other CodeSource. 323 * 324 * @param codesource CodeSource to compare against. 325 * 326 * @return true if the specified codesource is implied by this codesource, 327 * false if not. 328 */ 329 public boolean implies(CodeSource codesource) 330 { 331 if (codesource == null) 332 return false; 333 334 return matchCerts(codesource, false) && matchLocation(codesource); 335 } 336 337 /** 338 * Returns true if all the certs in this 339 * CodeSource are also in <i>that</i>. 340 * 341 * @param that the CodeSource to check against. 342 * @param strict if true then a strict equality match is performed. 343 * Otherwise a subset match is performed. 344 */ 345 boolean matchCerts(CodeSource that, boolean strict) 346 { 347 boolean match; 348 349 // match any key 350 if (certs == null && signers == null) { 351 if (strict) { 352 return (that.certs == null && that.signers == null); 353 } else { 354 return true; 355 } 356 // both have signers 357 } else if (signers != null && that.signers != null) { 358 if (strict && signers.length != that.signers.length) { 359 return false; 360 } 361 for (int i = 0; i < signers.length; i++) { 362 match = false; 363 for (int j = 0; j < that.signers.length; j++) { 364 if (signers[i].equals(that.signers[j])) { 365 match = true; 366 break; 367 } 368 } 369 if (!match) return false; 370 } 371 return true; 372 373 // both have certs 374 } else if (certs != null && that.certs != null) { 375 if (strict && certs.length != that.certs.length) { 376 return false; 377 } 378 for (int i = 0; i < certs.length; i++) { 379 match = false; 380 for (int j = 0; j < that.certs.length; j++) { 381 if (certs[i].equals(that.certs[j])) { 382 match = true; 383 break; 384 } 385 } 386 if (!match) return false; 387 } 388 return true; 389 } 390 391 return false; 392 } 393 394 395 /** 396 * Returns true if two CodeSource's have the "same" location. 397 * 398 * @param that CodeSource to compare against 399 */ 400 private boolean matchLocation(CodeSource that) { 401 if (location == null) 402 return true; 403 404 if ((that == null) || (that.location == null)) 405 return false; 406 407 if (location.equals(that.location)) 408 return true; 409 410 if (!location.getProtocol().equalsIgnoreCase(that.location.getProtocol())) 411 return false; 412 413 int thisPort = location.getPort(); 414 if (thisPort != -1) { 415 int thatPort = that.location.getPort(); 416 int port = thatPort != -1 ? thatPort 417 : that.location.getDefaultPort(); 418 if (thisPort != port) 419 return false; 420 } 421 422 if (location.getFile().endsWith("/-")) { 423 // Matches the directory and (recursively) all files 424 // and subdirectories contained in that directory. 425 // For example, "/a/b/-" implies anything that starts with 426 // "/a/b/" 427 String thisPath = location.getFile().substring(0, 428 location.getFile().length()-1); 429 if (!that.location.getFile().startsWith(thisPath)) 430 return false; 431 } else if (location.getFile().endsWith("/*")) { 432 // Matches the directory and all the files contained in that 433 // directory. 434 // For example, "/a/b/*" implies anything that starts with 435 // "/a/b/" but has no further slashes 436 int last = that.location.getFile().lastIndexOf('/'); 437 if (last == -1) 438 return false; 439 String thisPath = location.getFile().substring(0, 440 location.getFile().length()-1); 441 String thatPath = that.location.getFile().substring(0, last+1); 442 if (!thatPath.equals(thisPath)) 443 return false; 444 } else { 445 // Exact matches only. 446 // For example, "/a/b" and "/a/b/" both imply "/a/b/" 447 if ((!that.location.getFile().equals(location.getFile())) 448 && (!that.location.getFile().equals(location.getFile()+"/"))) { 449 return false; 450 } 451 } 452 453 if (location.getRef() != null 454 && !location.getRef().equals(that.location.getRef())) { 455 return false; 456 } 457 458 String thisHost = location.getHost(); 459 String thatHost = that.location.getHost(); 460 if (thisHost != null) { 461 if (("".equals(thisHost) || "localhost".equals(thisHost)) && 462 ("".equals(thatHost) || "localhost".equals(thatHost))) { 463 // ok 464 } else if (!thisHost.equals(thatHost)) { 465 if (thatHost == null) { 466 return false; 467 } 468 if (this.sp == null) { 469 this.sp = new SocketPermission(thisHost, "resolve"); 470 } 471 if (that.sp == null) { 472 that.sp = new SocketPermission(thatHost, "resolve"); 473 } 474 if (!this.sp.implies(that.sp)) { 475 return false; 476 } 477 } 478 } 479 // everything matches 480 return true; 481 } 482 483 /** 484 * Returns a string describing this CodeSource, telling its 485 * URL and certificates. 486 * 487 * @return information about this CodeSource. 488 */ 489 @Override 490 public String toString() { 491 StringBuilder sb = new StringBuilder(); 492 sb.append("("); 493 sb.append(this.location); 494 495 if (this.certs != null && this.certs.length > 0) { 496 for (int i = 0; i < this.certs.length; i++) { 497 sb.append( " " + this.certs[i]); 498 } 499 500 } else if (this.signers != null && this.signers.length > 0) { 501 for (int i = 0; i < this.signers.length; i++) { 502 sb.append( " " + this.signers[i]); 503 } 504 } else { 505 sb.append(" <no signer certificates>"); 506 } 507 sb.append(")"); 508 return sb.toString(); 509 } 510 511 /** 512 * Writes this object out to a stream (i.e., serializes it). 513 * 514 * @serialData An initial {@code URL} is followed by an 515 * {@code int} indicating the number of certificates to follow 516 * (a value of "zero" denotes that there are no certificates associated 517 * with this object). 518 * Each certificate is written out starting with a {@code String} 519 * denoting the certificate type, followed by an 520 * {@code int} specifying the length of the certificate encoding, 521 * followed by the certificate encoding itself which is written out as an 522 * array of bytes. Finally, if any code signers are present then the array 523 * of code signers is serialized and written out too. 524 */ 525 private void writeObject(java.io.ObjectOutputStream oos) 526 throws IOException 527 { 528 oos.defaultWriteObject(); // location 529 530 // Serialize the array of certs 531 if (certs == null || certs.length == 0) { 532 oos.writeInt(0); 533 } else { 534 // write out the total number of certs 535 oos.writeInt(certs.length); 536 // write out each cert, including its type 537 for (int i = 0; i < certs.length; i++) { 538 java.security.cert.Certificate cert = certs[i]; 539 try { 540 oos.writeUTF(cert.getType()); 541 byte[] encoded = cert.getEncoded(); 542 oos.writeInt(encoded.length); 543 oos.write(encoded); 544 } catch (CertificateEncodingException cee) { 545 throw new IOException(cee.getMessage()); 546 } 547 } 548 } 549 550 // Serialize the array of code signers (if any) 551 if (signers != null && signers.length > 0) { 552 oos.writeObject(signers); 553 } 554 } 555 556 /** 557 * Restores this object from a stream (i.e., deserializes it). 558 */ 559 private void readObject(java.io.ObjectInputStream ois) 560 throws IOException, ClassNotFoundException 561 { 562 CertificateFactory cf; 563 Hashtable<String, CertificateFactory> cfs = null; 564 List<java.security.cert.Certificate> certList = null; 565 566 ois.defaultReadObject(); // location 567 568 // process any new-style certs in the stream (if present) 569 int size = ois.readInt(); 570 if (size > 0) { 571 // we know of 3 different cert types: X.509, PGP, SDSI, which 572 // could all be present in the stream at the same time 573 cfs = new Hashtable<>(3); 574 certList = new ArrayList<>(size > 20 ? 20 : size); 575 } else if (size < 0) { 576 throw new IOException("size cannot be negative"); 577 } 578 579 for (int i = 0; i < size; i++) { 580 // read the certificate type, and instantiate a certificate 581 // factory of that type (reuse existing factory if possible) 582 String certType = ois.readUTF(); 583 if (cfs.containsKey(certType)) { 584 // reuse certificate factory 585 cf = cfs.get(certType); 586 } else { 587 // create new certificate factory 588 try { 589 cf = CertificateFactory.getInstance(certType); 590 } catch (CertificateException ce) { 591 throw new ClassNotFoundException 592 ("Certificate factory for " + certType + " not found"); 593 } 594 // store the certificate factory so we can reuse it later 595 cfs.put(certType, cf); 596 } 597 // parse the certificate 598 byte[] encoded = IOUtils.readNBytes(ois, ois.readInt()); 599 ByteArrayInputStream bais = new ByteArrayInputStream(encoded); 600 try { 601 certList.add(cf.generateCertificate(bais)); 602 } catch (CertificateException ce) { 603 throw new IOException(ce.getMessage()); 604 } 605 bais.close(); 606 } 607 608 if (certList != null) { 609 this.certs = certList.toArray( 610 new java.security.cert.Certificate[size]); 611 } 612 // Deserialize array of code signers (if any) 613 try { 614 this.signers = ((CodeSigner[])ois.readObject()).clone(); 615 } catch (IOException ioe) { 616 // no signers present 617 } 618 619 if (location != null) { 620 locationNoFragString = URLUtil.urlNoFragString(location); 621 } 622 } 623 624 /* 625 * Convert an array of certificates to an array of code signers. 626 * The array of certificates is a concatenation of certificate chains 627 * where the initial certificate in each chain is the end-entity cert. 628 * 629 * @return an array of code signers or null if none are generated. 630 */ 631 private CodeSigner[] convertCertArrayToSignerArray( 632 java.security.cert.Certificate[] certs) { 633 634 if (certs == null) { 635 return null; 636 } 637 638 try { 639 // Initialize certificate factory 640 if (factory == null) { 641 factory = CertificateFactory.getInstance("X.509"); 642 } 643 644 // Iterate through all the certificates 645 int i = 0; 646 List<CodeSigner> signers = new ArrayList<>(); 647 while (i < certs.length) { 648 List<java.security.cert.Certificate> certChain = 649 new ArrayList<>(); 650 certChain.add(certs[i++]); // first cert is an end-entity cert 651 int j = i; 652 653 // Extract chain of certificates 654 // (loop while certs are not end-entity certs) 655 while (j < certs.length && 656 certs[j] instanceof X509Certificate && 657 ((X509Certificate)certs[j]).getBasicConstraints() != -1) { 658 certChain.add(certs[j]); 659 j++; 660 } 661 i = j; 662 CertPath certPath = factory.generateCertPath(certChain); 663 signers.add(new CodeSigner(certPath, null)); 664 } 665 666 if (signers.isEmpty()) { 667 return null; 668 } else { 669 return signers.toArray(new CodeSigner[signers.size()]); 670 } 671 672 } catch (CertificateException e) { 673 return null; //TODO - may be better to throw an ex. here 674 } 675 } 676 }