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