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