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 }