Source for java.security.CodeSource

   1: /* CodeSource.java -- Code location and certifcates
   2:    Copyright (C) 1998, 2002, 2004  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package java.security;
  40: 
  41: import gnu.java.lang.CPStringBuilder;
  42: 
  43: import java.io.ByteArrayInputStream;
  44: import java.io.IOException;
  45: import java.io.ObjectInputStream;
  46: import java.io.ObjectOutputStream;
  47: import java.io.Serializable;
  48: import java.net.SocketPermission;
  49: import java.net.URL;
  50: // Note that this overrides Certificate in this package.
  51: import java.security.cert.Certificate;
  52: import java.security.cert.CertificateEncodingException;
  53: import java.security.cert.CertificateException;
  54: import java.security.cert.CertificateFactory;
  55: import java.util.Arrays;
  56: import java.util.HashSet;
  57: import java.util.Iterator;
  58: 
  59: /**
  60:  * This class represents a location from which code is loaded (as
  61:  * represented by a URL), and the list of certificates that are used to
  62:  * check the signatures of signed code loaded from this source.
  63:  *
  64:  * @author Aaron M. Renn (arenn@urbanophile.com)
  65:  * @author Eric Blake (ebb9@email.byu.edu)
  66:  * @since 1.1
  67:  * @status updated to 1.4
  68:  */
  69: public class CodeSource implements Serializable
  70: {
  71:   /**
  72:    * Compatible with JDK 1.1+.
  73:    */
  74:   private static final long serialVersionUID = 4977541819976013951L;
  75: 
  76:   /**
  77:    * This is the URL that represents the code base from which code will
  78:    * be loaded.
  79:    *
  80:    * @serial the code location
  81:    */
  82:   private final URL location;
  83: 
  84:   /** The set of certificates for this code base. */
  85:   private transient HashSet certs;
  86: 
  87:   /**
  88:    * This creates a new instance of <code>CodeSource</code> that loads code
  89:    * from the specified URL location and which uses the specified certificates
  90:    * for verifying signatures.
  91:    *
  92:    * @param location the location from which code will be loaded
  93:    * @param certs the list of certificates
  94:    */
  95:   public CodeSource(URL location, Certificate[] certs)
  96:   {
  97:     this.location = location;
  98:     if (certs != null)
  99:       this.certs = new HashSet(Arrays.asList(certs));
 100:   }
 101: 
 102:   /**
 103:    * This method returns a hash value for this object.
 104:    *
 105:    * @return a hash value for this object
 106:    */
 107:   public int hashCode()
 108:   {
 109:     return (location == null ? 0 : location.hashCode())
 110:       ^ (certs == null ? 0 : certs.hashCode());
 111:   }
 112: 
 113:   /**
 114:    * This method tests the specified <code>Object</code> for equality with
 115:    * this object.  This will be true if and only if the locations are equal
 116:    * and the certificate sets are identical (ignoring order).
 117:    *
 118:    * @param obj the <code>Object</code> to test against
 119:    * @return true if the specified object is equal to this one
 120:    */
 121:   public boolean equals(Object obj)
 122:   {
 123:     if (! (obj instanceof CodeSource))
 124:       return false;
 125:     CodeSource cs = (CodeSource) obj;
 126:     return (certs == null ? cs.certs == null : certs.equals(cs.certs))
 127:       && (location == null ? cs.location == null
 128:           : location.equals(cs.location));
 129:   }
 130: 
 131:   /**
 132:    * This method returns the URL specifying the location from which code
 133:    * will be loaded under this <code>CodeSource</code>.
 134:    *
 135:    * @return the code location for this <code>CodeSource</code>
 136:    */
 137:   public final URL getLocation()
 138:   {
 139:     return location;
 140:   }
 141: 
 142:   /**
 143:    * This method returns the list of digital certificates that can be used
 144:    * to verify the signatures of code loaded under this
 145:    * <code>CodeSource</code>.
 146:    *
 147:    * @return the certifcate list for this <code>CodeSource</code>
 148:    */
 149:   public final Certificate[] getCertificates()
 150:   {
 151:     if (certs == null)
 152:       return null;
 153:     Certificate[] c = new Certificate[certs.size()];
 154:     certs.toArray(c);
 155:     return c;
 156:   }
 157: 
 158:   /**
 159:    * This method tests to see if a specified <code>CodeSource</code> is
 160:    * implied by this object.  Effectively, to meet this test, the specified
 161:    * object must have all the certifcates this object has (but may have more),
 162:    * and must have a location that is a subset of this object's.  In order
 163:    * for this object to imply the specified object, the following must be
 164:    * true:
 165:    *
 166:    * <ol>
 167:    * <li><em>codesource</em> must not be <code>null</code>.</li>
 168:    * <li>If <em>codesource</em> has a certificate list, all of it's
 169:    *     certificates must be present in the certificate list of this
 170:    *     code source.</li>
 171:    * <li>If this object does not have a <code>null</code> location, then
 172:    *     the following addtional tests must be passed.
 173:    *
 174:    *     <ol>
 175:    *     <li><em>codesource</em> must not have a <code>null</code>
 176:    *         location.</li>
 177:    *     <li><em>codesource</em>'s location must be equal to this object's
 178:    *         location, or
 179:    *         <ul>
 180:    *         <li><em>codesource</em>'s location protocol, port, and ref (aka,
 181:    *             anchor) must equal this objects</li>
 182:    *         <li><em>codesource</em>'s location host must imply this object's
 183:    *             location host, as determined by contructing
 184:    *             <code>SocketPermission</code> objects from each with no
 185:    *             action list and using that classes's <code>implies</code>
 186:    *             method</li>
 187:    *         <li>If this object's location file ends with a '/', then the
 188:    *             specified object's location file must start with this
 189:    *             object's location file. Otherwise, the specified object's
 190:    *             location file must start with this object's location file
 191:    *             with the '/' character appended to it.</li>
 192:    *         </ul></li>
 193:    *     </ol></li>
 194:    * </ol>
 195:    *
 196:    * <p>For example, each of these locations imply the location
 197:    * "http://java.sun.com/classes/foo.jar":</p>
 198:    *
 199:    * <pre>
 200:    * http:
 201:    * http://*.sun.com/classes/*
 202:    * http://java.sun.com/classes/-
 203:    * http://java.sun.com/classes/foo.jar
 204:    * </pre>
 205:    *
 206:    * <p>Note that the code source with null location and null certificates implies
 207:    * all other code sources.</p>
 208:    *
 209:    * @param cs the <code>CodeSource</code> to test against this object
 210:    * @return true if this specified <code>CodeSource</code> is implied
 211:    */
 212:   public boolean implies(CodeSource cs)
 213:   {
 214:     if (cs == null)
 215:       return false;
 216:     // First check the certificate list.
 217:     if (certs != null && (cs.certs == null || ! certs.containsAll(cs.certs)))
 218:       return false;
 219:     // Next check the location.
 220:     if (location == null)
 221:       return true;
 222:     if (cs.location == null
 223:         || ! location.getProtocol().equals(cs.location.getProtocol())
 224:         || (location.getPort() != -1
 225:             && location.getPort() != cs.location.getPort())
 226:         || (location.getRef() != null
 227:             && ! location.getRef().equals(cs.location.getRef())))
 228:       return false;
 229:     if (location.getHost() != null)
 230:       {
 231:         String their_host = cs.location.getHost();
 232:         if (their_host == null)
 233:           return false;
 234:         SocketPermission our_sockperm =
 235:           new SocketPermission(location.getHost(), "accept");
 236:         SocketPermission their_sockperm =
 237:           new SocketPermission(their_host, "accept");
 238:         if (! our_sockperm.implies(their_sockperm))
 239:           return false;
 240:       }
 241:     String our_file = location.getFile();
 242:     if (our_file != null)
 243:       {
 244:         if (! our_file.endsWith("/"))
 245:           our_file += "/";
 246:         String their_file = cs.location.getFile();
 247:         if (their_file == null
 248:             || ! their_file.startsWith(our_file))
 249:           return false;
 250:       }
 251:     return true;
 252:   }
 253: 
 254:   /**
 255:    * This method returns a <code>String</code> that represents this object.
 256:    * The result is in the format <code>"(" + getLocation()</code> followed
 257:    * by a space separated list of certificates (or "&lt;no certificates&gt;"),
 258:    * followed by <code>")"</code>.
 259:    *
 260:    * @return a <code>String</code> for this object
 261:    */
 262:   public String toString()
 263:   {
 264:     CPStringBuilder sb = new CPStringBuilder("(").append(location);
 265:     if (certs == null || certs.isEmpty())
 266:       sb.append(" <no certificates>");
 267:     else
 268:       {
 269:         Iterator iter = certs.iterator();
 270:         for (int i = certs.size(); --i >= 0; )
 271:           sb.append(' ').append(iter.next());
 272:       }
 273:     return sb.append(")").toString();
 274:   }
 275: 
 276:   /**
 277:    * Reads this object from a serialization stream.
 278:    *
 279:    * @param s the input stream
 280:    * @throws IOException if reading fails
 281:    * @throws ClassNotFoundException if deserialization fails
 282:    * @serialData this reads the location, then expects an int indicating the
 283:    *             number of certificates. Each certificate is a String type
 284:    *             followed by an int encoding length, then a byte[] encoding
 285:    */
 286:   private void readObject(ObjectInputStream s)
 287:     throws IOException, ClassNotFoundException
 288:   {
 289:     s.defaultReadObject();
 290:     int count = s.readInt();
 291:     certs = new HashSet();
 292:     while (--count >= 0)
 293:       {
 294:         String type = (String) s.readObject();
 295:         int bytes = s.readInt();
 296:         byte[] encoded = new byte[bytes];
 297:         for (int i = 0; i < bytes; i++)
 298:           encoded[i] = s.readByte();
 299:         ByteArrayInputStream stream = new ByteArrayInputStream(encoded);
 300:         try
 301:           {
 302:             CertificateFactory factory = CertificateFactory.getInstance(type);
 303:             certs.add(factory.generateCertificate(stream));
 304:           }
 305:         catch (CertificateException e)
 306:           {
 307:             // XXX Should we ignore this certificate?
 308:           }
 309:       }
 310:   }
 311: 
 312:   /**
 313:    * Writes this object to a serialization stream.
 314:    *
 315:    * @param s the output stream
 316:    * @throws IOException if writing fails
 317:    * @serialData this writes the location, then writes an int indicating the
 318:    *             number of certificates. Each certificate is a String type
 319:    *             followed by an int encoding length, then a byte[] encoding
 320:    */
 321:   private void writeObject(ObjectOutputStream s) throws IOException
 322:   {
 323:     s.defaultWriteObject();
 324:     if (certs == null)
 325:       s.writeInt(0);
 326:     else
 327:       {
 328:         int count = certs.size();
 329:         s.writeInt(count);
 330:         Iterator iter = certs.iterator();
 331:         while (--count >= 0)
 332:           {
 333:             Certificate c = (Certificate) iter.next();
 334:             s.writeObject(c.getType());
 335:             byte[] encoded;
 336:             try
 337:               {
 338:                 encoded = c.getEncoded();
 339:               }
 340:             catch (CertificateEncodingException e)
 341:               {
 342:                 // XXX Should we ignore this certificate?
 343:                 encoded = null;
 344:               }
 345:             if (encoded == null)
 346:               s.writeInt(0);
 347:             else
 348:               {
 349:                 s.writeInt(encoded.length);
 350:                 for (int i = 0; i < encoded.length; i++)
 351:                   s.writeByte(encoded[i]);
 352:               }
 353:           }
 354:       }
 355:   }
 356: } // class CodeSource