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