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