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