-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

>>>>> "Mark" == Mark Wielaard <[EMAIL PROTECTED]> writes:

Mark> [...]

Mark> As you can see very little coding, reviewing or even testing of
Mark> code :( My new year resolution will be: Less talking, More
Mark> coding!

There's a few things relating to the security/crypto bits of Classpath
and friends that I'd like to mention, and get opinions on:

  - Jessie looks like it will become a GNU package someday, but there
    is still some question of where it should go. Suggestions include

       * Classpathx, but Nic has indicated that he thinks Classpathx
         would be better off in Classpath proper.

       * Classpath, possibly as an optional/sub- project.

       * On its own, all by its lonesome.

    Above all I'd like to see this project keep some level of
    independence from Classpath, so it can evolve at its own pace.

  - I've been extending the X.509 classes I put into Classpath a while
    ago to complete the PKI platform with things such as
    CertPathValidator implementations, and have started implementing
    NIST's giant PKI test suite [1] around Mauve. There is a *totally*
    unofficial tarball of this at [2] (the username is "pki" and the
    password "gnupki" -- I don't want Joe Searchengine downloading
    this quite yet). I currently have 73 tests (out of hundreds)
    implemented, and they all pass.

    The questions here are similar to those for Jessie: where should
    this go?

    The PKIX standard is giant and complicated. Anyone with copious
    spare time (...) is welcome to help.

  - Classpath, Jessie, and the GNU PKI packages all have their own
    copies of some simple ASN.1 DER codec classes. This isn't good.
    Some sort of grand unified ASN.1 library would be nice to have.

  - KeyStores. We have a brand-new keyring format in GNU Crypto, and
    attached is a minimal read-only version for public keyrings.

  - Policy files. I have been working on and off on an implementation
    of java.security.Policy that reads JDK-style policy files. I've
    attached my current version.

  - javax.crypto and javax.net.ssl should go into Classpath, in my
    opinion.

  - My own list for future additions include

      * Kerberos and GSSAPI.
      * OpenPGP.
      * Keytool and jarsigner equivalents.

- ---
[1] http://csrc.nist.gov/pki/testing/x509paths.html
[2] http://metastatic.org/source/gnu-pki/

- -- 
Casey Marshall || [EMAIL PROTECTED]
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.3 (GNU/Linux)
Comment: Processed by Mailcrypt 3.5.7 <http://mailcrypt.sourceforge.net/>

iD8DBQE/349wgAuWMgRGsWsRAt6OAJ9G+nmGO5e2LNtlg8Fzk3XDr9JgLgCfXUed
dX6Agjacd3S2QrBD+uf+Qxo=
=RMwn
-----END PGP SIGNATURE-----
/* GnuKeyring.java -- minimal, read-only GNU Keyring implementation.
   Copyright (C) 2003 Free Software Foundation, Inc.

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */


package gnu.java.security.provider;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;

import java.security.Key;
import java.security.KeyStoreSpi;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;

import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import java.util.zip.InflaterInputStream;

public class GnuKeyring extends KeyStoreSpi
{

  // Constants and fields.
  // -------------------------------------------------------------------------

  // The value "GKR" | 0x01
  private static final int MAGIC = 0x474b5201;

  // Keyring usage type 2: trusted certificates only.
  private static final int USAGE = 04;

  // Packet types. Types not appearing in public keyrings are omitted.
  private static final int TYPE_PBMAC      = 3;
  private static final int TYPE_COMPRESSED = 4;
  private static final int TYPE_CERT       = 5;

  private final HashMap entries;

  // Constructor.
  // -------------------------------------------------------------------------

  public GnuKeyring()
  {
    entries = new HashMap();
  }

  // Instance methods.
  // -------------------------------------------------------------------------

  public Key engineGetKey(String alias, char[] password)
  {
    throw new UnsupportedOperationException("this is a public keyring");
  }

  public Certificate[] engineGetCertificateChain(String alias)
  {
    throw new UnsupportedOperationException("this is a public keyring");
  }

  public Certificate engineGetCertificate(String alias)
  {
    Entry e = (Entry) entries.get(canonicalize(alias));
    if (e == null)
      {
        return null;
      }
    return e.getCertificate();
  }

  public Date engineGetCreationDate(String alias)
  {
    Entry e = (Entry) entries.get(canonicalize(alias));
    if (e == null)
      {
        return null;
      }
    return e.getCreationDate();
  }

  public void engineSetKeyEntry(String alias, Key key, char[] passwd, Certificate[] 
chain)
  {
    throw new UnsupportedOperationException("this is a read-only keyring");
  }

  public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain)
  {
    throw new UnsupportedOperationException("this is a read-only keyring");
  }

  public void engineSetCertificateEntry(String alias, Certificate certificate)
  {
    throw new UnsupportedOperationException("this is a read-only keyring");
  }

  public void engineDeleteEntry(String alias)
  {
    throw new UnsupportedOperationException("this is a read-only keyring");
  }

  public Enumeration engineAliases()
  {
    return new Vector(entries.keySet()).elements();
  }

  public boolean engineContainsAlias(String alias)
  {
    return entries.containsKey(canonicalize(alias));
  }

  public int engineSize()
  {
    return entries.size();
  }

  public boolean engineIsKeyEntry(String alias)
  {
    return false;
  }

  public boolean engineIsCertificateEntry(String alias)
  {
    return engineContainsAlias(alias);
  }

  public String engineGetCertificateAlias(Certificate certificate)
  {
    for (Iterator it = entries.entrySet().iterator(); it.hasNext(); )
      {
        Map.Entry e = (Map.Entry) it.next();
        if (certificate.equals(e.getValue()))
          {
            return (String) e.getKey();
          }
      }
    return null;
  }

  public void engineStore(OutputStream out, char[] password)
  {
    throw new UnsupportedOperationException("this is a read-only keyring");
  }

  public synchronized void engineLoad(InputStream _in, char[] password)
    throws IOException, NoSuchAlgorithmException, CertificateException
  {
    entries.clear();
    DataInputStream din = new DataInputStream(_in);
    if (din.readInt() != MAGIC)
      {
        throw new IOException("expecting magic bytes");
      }
    if (din.read() != USAGE)
      {
        throw new IOException("not a public keyring");
      }

    // Top-level is a password-based MAC entry.
    if (din.read() != TYPE_PBMAC)
      {
        throw new IOException("expecting password-based MAC");
      }

    Map props = readProperties(din);
    int macLen = 0;
    try
      {
        macLen = Integer.parseInt((String) props.get("maclen"));
        if (macLen <= 0)
          {
            throw new NumberFormatException("must be positive");
          }
      }
    catch (Exception x)
      {
        throw new IOException("malformed mac length: " + x.toString());
      }

    int len = din.readInt();
    byte[] payload = new byte[len - macLen];
    byte[] mac = new byte[macLen];
    din.readFully(payload);
    din.readFully(mac);

    if (!verifyHash(props, payload, mac, password))
      {
        throw new IOException("MAC could not be verified");
      }

    DataInputStream in2 = new DataInputStream(new ByteArrayInputStream(payload));
    CertificateFactory factory = null;
    Certificate cert = null;
    Date date = null;

    while (in2.available() > 0)
      {
        int type = in2.read();
        switch (type)
          {
          case TYPE_COMPRESSED:
            props = readProperties(in2);
            String comp = (String) props.get("algorithm");
            if (comp == null || !comp.equalsIgnoreCase("DEFLATE"))
              {
                throw new NoSuchAlgorithmException(comp);
              }
            payload = new byte[in2.readInt()];
            in2.readFully(payload);
            in2 = new DataInputStream(new InflaterInputStream(
              new ByteArrayInputStream(payload)));
            break;

          case TYPE_CERT:
            props = readProperties(in2);
            String alias = (String) props.get("alias");
            if (alias == null || alias.length() == 0)
              {
                throw new IOException("no alias for certificate entry");
              }
            date = null;
            try
              {
                String s = (String) props.get("creation-date");
                if (s == null)
                  {
                    throw new IOException("missing creation date");
                  }
                date = new Date(Long.parseLong(s));
              }
            catch (NumberFormatException x)
              {
                throw new IOException("malformed date: " + x.toString());
              }
            factory = CertificateFactory.getInstance((String) props.get("type"));
            payload = new byte[in2.readInt()];
            in2.readFully(payload);
            cert = factory.generateCertificate(new ByteArrayInputStream(payload));
            entries.put(alias, new Entry(cert, date));
            break;

          case -1:
            return;
          default:
            throw new IOException("unknown packet type: " + type);
          }
      }
  }

  // Own methods.
  // -------------------------------------------------------------------------

  private String canonicalize(String key)
  {
    if (key == null)
      {
        return null;
      }
    return key.toLowerCase();
  }

  private boolean verifyHash(Map props, byte[] payload, byte[] hash, char[] password)
    throws IOException, NoSuchAlgorithmException
  {
    // If no password is supplied, don't check the hash.
    if (password == null)
      {
        return true;
      }

    String macName = (String) props.get("mac");
    if (macName == null || !macName.toLowerCase().startsWith("hmac-"))
      {
        throw new NoSuchAlgorithmException("invalid MAC");
      }
    HMac mac = new HMac(MessageDigest.getInstance(macName.substring(5), new Gnu()));

    // Generate a key from the password.
    byte[] mackey = new byte[mac.getDigestLength()];
    HMac kdfmac = new HMac(MessageDigest.getInstance("SHA-1", new Gnu()));
    kdfmac.setup(new String(password).getBytes("UTF-8"));
    byte[] salt = decodeSalt((String) props.get("salt"));

    int limit = salt.length;
    byte[] in = new byte[limit + 4];
    System.arraycopy(salt, 0, in, 0, salt.length);
    // The count is appended to 'in', but in this case it is always 1.
    in[in.length - 1] = 1;
    for (int i = 0; i < 1000; i++)
      {
        kdfmac.reset();
        kdfmac.update(in, 0, in.length);
        in = kdfmac.digest();
        for (int j = 0; j < mackey.length; j++)
          {
            mackey[j] ^= in[j];
          }
      }

    // Compute and check the MAC.
    mac.setup(mackey);
    mac.update(payload, 0, payload.length);
    byte[] d = mac.digest();

    for (int i = 0; i < d.length && i < hash.length; i++)
      {
        if (d[i] != hash[i])
          {
            return false;
          }
      }
    return true;
  }

  private Map readProperties(DataInputStream in) throws IOException
  {
    HashMap props = new HashMap();
    int len = in.readInt();
    byte[] buf = new byte[len];
    in.readFully(buf);
    DataInputStream in2 = new DataInputStream(new ByteArrayInputStream(buf));
    while (in2.available() > 0)
      {
        String key = in2.readUTF();
        String value = in2.readUTF();
        props.put(canonicalize(key), value);
      }
    return props;
  }

  private byte[] decodeSalt(String salt)
  {
    int limit = salt.length();
    byte[] result = new byte[((limit + 1) / 2)];
    int i = 0, j = 0;
    if ((limit & 1) == 1)
      {
        result[j++] = (byte) Character.digit(salt.charAt(i++), 16);
      }
    while (i < limit)
      {
        result[j  ]  = (byte) (Character.digit(salt.charAt(i++), 16) << 4);
        result[j++] |= (byte)  Character.digit(salt.charAt(i++), 16);
      }
    return result;
  }

  // Inner classes.
  // -------------------------------------------------------------------------

  /**
   * A single certificate entry.
   */
  private static final class Entry
  {

    // Fields.
    // -----------------------------------------------------------------------

    private final Certificate certificate;
    private final Date creationDate;

    // Constructor.
    // -----------------------------------------------------------------------

    Entry(Certificate certificate, Date creationDate)
    {
      this.certificate = certificate;
      this.creationDate = creationDate;
    }

    // Instance methods.
    // -----------------------------------------------------------------------

    Certificate getCertificate()
    {
      return certificate;
    }

    Date getCreationDate()
    {
      return (Date) creationDate.clone();
    }
  }

  /**
   * Minimal HMac implemnentation, derived from GNU Crypto.
   */
  private static final class HMac
  {

    // Constants and fields.
    // -----------------------------------------------------------------------

    private MessageDigest md, ipadHash, opadHash;
    private byte[] ipad;

    // Constructor.
    // -----------------------------------------------------------------------

    HMac(MessageDigest md)
    {
      this.md = md;
    }

    // Instance methods.
    // -----------------------------------------------------------------------

    void setup(byte[] key)
    {
      if (key.length > 64)
        {
          key = md.digest(key);
        }
      if (key.length < 64)
        {
          byte[] b = new byte[64];
          System.arraycopy(key, 0, b, 0, key.length);
          key = b;
        }

      ipad = new byte[64];
      for (int i = 0; i < ipad.length; i++)
        {
          ipad[i] = (byte) (key[i] ^ 0x36);
        }

      md.reset();
      try
        {
          opadHash = (MessageDigest) md.clone();
        }
      catch (CloneNotSupportedException shouldNotHappen)
        {
          throw new Error(shouldNotHappen);
        }
      for (int i = 0; i < key.length; i++)
        {
          opadHash.update((byte) (key[i] ^ 0x5C));
        }
      md.update(ipad);
      try
        {
          ipadHash = (MessageDigest) md.clone();
        }
      catch (CloneNotSupportedException shouldNotHappen)
        {
          throw new Error(shouldNotHappen);
        }
    }

    void update(byte[] buf, int off, int len)
    {
      md.update(buf, off, len);
    }

    byte[] digest()
    {
      byte[] result = md.digest();
      try
        {
          md = (MessageDigest) opadHash.clone();
        }
      catch (CloneNotSupportedException shouldNotHappen)
        {
          throw new Error(shouldNotHappen);
        }
      result = md.digest(result);
      md.reset();
      return result;
    }

    void reset()
    {
      md.reset();
      if (ipad == null)
        {
          return;
        }
      md.update(ipad);
      try
        {
          ipadHash = (MessageDigest) md.clone();
        }
      catch (CloneNotSupportedException shouldNotHappen)
        {
          throw new Error(shouldNotHappen);
        }
    }

    int getDigestLength()
    {
      return md.getDigestLength();
    }
  }
}
/* PolicyFile.java -- policy file reader.
   Copyright (C) 2003  Free Software Foundation, Inc.

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */


package gnu.java.security;

import java.io.File;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StreamTokenizer;

import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.MalformedURLException;

import java.security.AccessController;
import java.security.CodeSource;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.Permission;
import java.security.Permissions;
import java.security.PermissionCollection;
import java.security.Policy;
import java.security.Principal;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.Security;
import java.security.UnresolvedPermission;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

/**
 * An implementation of a [EMAIL PROTECTED] java.security.Policy} object whose
 * permissions are specified by a <em>policy file</em>.
 *
 * <p>The approximate syntax of policy files is:</p>
 *
 * <pre>
 * policyFile ::= keystoreOrGrantEntries ;
 *
 * keystoreOrGrantEntries ::= keystoreOrGrantEntry |
 *                            keystoreOrGrantEntries keystoreOrGrantEntry |
 *                            EMPTY ;
 *
 * keystoreOrGrantEntry ::= keystoreEntry | grantEntry ;
 *
 * keystoreEntry ::= "keystore" keystoreUrl ';' |
 *                   "keystore" keystoreUrl ',' keystoreAlgorithm ';' ;
 *
 * keystoreUrl ::= URL ;
 * keystoreAlgorithm ::= STRING ;
 *
 * grantEntry ::= "grant" domainParameters '{' permissions '}' ';'
 *
 * domainParameters ::= domainParameter |
 *                      domainParameter ',' domainParameters ;
 *
 * domainParameter ::= "signedBy" signerNames |
 *                     "codeBase" codeBaseUrl |
 *                     "principal" principalClassName principalName |
 *                     "principal" principalName ;
 *
 * signerNames ::= quotedString ;
 * codeBaseUrl ::= URL ;
 * principalClassName ::= STRING ;
 * principalName ::= quotedString ;
 *
 * quotedString ::= quoteChar STRING quoteChar ;
 * quoteChar ::= '"' | '\'';
 *
 * permissions ::= permission | permissions permission ;
 *
 * permission ::= "permission" permissionClassName permissionTarget permissionAction |
 *                "permission" permissionClassName permissionTarget |
 *                "permission" permissionClassName;
 * </pre>
 *
 * <p>Comments are either form of Java comments. Keystore entries only
 * affect subsequent grant entries, so if a grant entry preceeds a
 * keystore entry, that grant entry is not affected by that keystore
 * entry. Certian instances of <code>${property-name}</code> will be
 * replaced with <code>System.getProperty("property-name")</code> in
 * quoted strings.</p>
 *
 * <p>This class will load the following files when created or
 * refreshed, in order:</p>
 *
 * <ol>
 * <li>The file <code>${java.home}/lib/security/java.policy</code>.</li>
 * <li>All URLs specified by security properties
 * <code>"policy.file.<i>n</i>"</code>, for increasing <i>n</i>
 * starting from 1. The sequence stops at the first undefined
 * property, so you must set <code>"policy.file.1"</code> if you also
 * set <code>"policy.file.2"</code>, and so on.</li>
 * <li>The URL specified by the property
 * <code>"java.security.policy"</code>.</li>
 * </ol>
 *
 * @author Casey Marshall ([EMAIL PROTECTED])
 * @see java.security.Policy
 */
public final class PolicyFile extends Policy
{

  // Constants and fields.
  // -------------------------------------------------------------------------

  private static final boolean DEBUG = true;
  private static void debug(String msg)
  {
    if (DEBUG)
      {
        System.err.print(">> PolicyFile: ");
        System.err.println(msg);
      }
  }

  private static void debug(Throwable t)
  {
    if (DEBUG)
      {
        System.err.println(">> PolicyFile");
        t.printStackTrace(System.err);
      }
  }

  private static final String DEFAULT_POLICY = System.getProperty("java.home")
    + System.getProperty("file.separator") + "lib"
    + System.getProperty("file.separator") + "security"
    + System.getProperty("file.separator") + "java.policy";

  private final Map cs2pc;

  // Constructors.
  // -------------------------------------------------------------------------

  public PolicyFile()
  {
    cs2pc = new HashMap();
    refresh();
  }

  // Instance methods.
  // -------------------------------------------------------------------------

  public PermissionCollection getPermissions(CodeSource codeSource)
  {
    Permissions perms = new Permissions();
    for (Iterator it = cs2pc.entrySet().iterator(); it.hasNext(); )
      {
        Map.Entry e = (Map.Entry) it.next();
        CodeSource cs = (CodeSource) e.getKey();
        if (cs.implies(codeSource))
          {
            debug(cs+" -> "+codeSource);
            PermissionCollection pc = (PermissionCollection) e.getValue();
            for (Enumeration ee = pc.elements(); ee.hasMoreElements(); )
              {
                perms.add((Permission) ee.nextElement());
              }
          }
        else
          debug(cs+" !-> "+codeSource);
      }
    perms.setReadOnly();
    return perms;
  }

  public void refresh()
  {
    cs2pc.clear();
    List policyFiles = new LinkedList();
    try
      {
        policyFiles.add(new File(DEFAULT_POLICY).toURL());
        policyFiles.addAll((List) AccessController.doPrivileged(
          new PrivilegedExceptionAction()
          {
            public Object run() throws Exception
            {
              LinkedList l = new LinkedList();
              for (int i = 1; ; i++)
                {
                  String s = Security.getProperty("policy.file."+i);
                  debug("policy.file."+i+"="+s);
                  if (s == null)
                    break;
                  l.add(new URL(s));
                }
              String s = System.getProperty("java.security.policy");
              debug("java.security.policy="+s);
              if (s != null)
                l.add(new URL(s));
              return l;
            }
          }));
      }
    catch (PrivilegedActionException pae)
      {
        debug(pae);
      }
    catch (MalformedURLException mue)
      {
        debug(mue);
      }
    for (Iterator it = policyFiles.iterator(); it.hasNext(); )
      {
        try
          {
            URL url = (URL) it.next();
            parse(url);
          }
        catch (IOException ioe)
          {
            debug(ioe);
          }
      }
  }

  public String toString()
  {
    return super.toString() + " [ " + cs2pc.toString() + " ]";
  }

  // Own methods.
  // -------------------------------------------------------------------------

  private static final int STATE_BEGIN = 0;
  private static final int STATE_GRANT = 1;
  private static final int STATE_PERMS = 2;

  /**
   * Parse a policy file, incorporating the permission definitions
   * described therein.
   *
   * @param url The URL of the policy file to read.
   * @throws IOException if an I/O error occurs, or if the policy file
   * cannot be parsed.
   */
  private void parse(final URL url) throws IOException
  {
    final StreamTokenizer in = new StreamTokenizer(new 
InputStreamReader(url.openStream()));
    in.resetSyntax();
    in.slashSlashComments(true);
    in.slashStarComments(true);
    in.wordChars('A', 'Z');
    in.wordChars('a', 'z');
    in.wordChars('0', '9');
    in.wordChars('.', '.');
    in.wordChars('_', '_');
    in.wordChars('$', '$');
    in.whitespaceChars(' ', ' ');
    in.whitespaceChars('\t', '\t');
    in.whitespaceChars('\f', '\f');
    in.whitespaceChars('\n', '\n');
    in.whitespaceChars('\r', '\r');
    in.quoteChar('\'');
    in.quoteChar('"');

    int tok;
    int state = STATE_BEGIN;
    List keystores = new LinkedList();
    URL currentBase = null;
    List currentCerts = new LinkedList();
    Permissions currentPerms = new Permissions();
    while ((tok = in.nextToken()) != StreamTokenizer.TT_EOF)
      {
        switch (tok)
          {
          case '{':
            if (state != STATE_GRANT)
              error(url, in, "spurious '{'");
            state = STATE_PERMS;
            tok = in.nextToken();
            break;
          case '}':
            if (state != STATE_PERMS)
              error(url, in, "spurious '}'");
            state = STATE_BEGIN;
            currentPerms.setReadOnly();
            Certificate[] c = null;
            if (!currentCerts.isEmpty())
              c = (Certificate[]) currentCerts.toArray(new 
Certificate[currentCerts.size()]);
            cs2pc.put(new CodeSource(currentBase, c), currentPerms);
            currentCerts.clear();
            currentPerms = new Permissions();
            currentBase = null;
            tok = in.nextToken();
            if (tok != ';')
              in.pushBack();
            continue;
          }
        if (tok != StreamTokenizer.TT_WORD)
          {
            error(url, in, "expecting word token");
          }

        // keystore "<keystore-path>" [',' "<keystore-type>"] ';'
        if (in.sval.equalsIgnoreCase("keystore"))
          {
            String alg = KeyStore.getDefaultType();
            tok = in.nextToken();
            if (tok != '"' && tok != '\'')
              error(url, in, "expecting key store URL");
            String store = in.sval;
            tok = in.nextToken();
            if (tok == ',')
              {
                tok = in.nextToken();
                if (tok != '"' && tok != '\'')
                  error(url, in, "expecting key store type");
                alg = in.sval;
                tok = in.nextToken();
              }
            if (tok != ';')
              error(url, in, "expecting semicolon");
            try
              {
                KeyStore keystore = KeyStore.getInstance(alg);
                keystore.load(new URL(url, store).openStream(), null);
                keystores.add(keystore);
              }
            catch (Exception x)
              {
                error(url, in, x.toString());
              }
          }
        else if (in.sval.equalsIgnoreCase("grant"))
          {
            if (state != STATE_BEGIN)
              error(url, in, "extraneous grant keyword");
            state = STATE_GRANT;
          }
        else if (in.sval.equalsIgnoreCase("signedBy"))
          {
            if (state != STATE_GRANT && state != STATE_PERMS)
              error(url, in, "spurious 'signedBy'");
            if (keystores.isEmpty())
              error(url, in, "'signedBy' with no keystores");
            tok = in.nextToken();
            if (tok != '"' && tok != '\'')
              error(url, in, "expecting signedBy name");
            StringTokenizer st = new StringTokenizer(in.sval, ",");
            while (st.hasMoreTokens())
              {
                String alias = st.nextToken();
                for (Iterator it = keystores.iterator(); it.hasNext(); )
                  {
                    KeyStore keystore = (KeyStore) it.next();
                    try
                      {
                        if (keystore.isCertificateEntry(alias))
                          currentCerts.add(keystore.getCertificate(alias));
                      }
                    catch (KeyStoreException kse)
                      {
                        error(url, in, kse.toString());
                      }
                  }
              }
            tok = in.nextToken();
            if (tok != ',')
              {
                if (state != STATE_GRANT)
                  error(url, in, "spurious ','");
                in.pushBack();
              }
          }
        else if (in.sval.equalsIgnoreCase("codeBase"))
          {
            if (state != STATE_GRANT)
              error(url, in, "spurious 'codeBase'");
            tok = in.nextToken();
            if (tok != '"' && tok != '\'')
              error(url, in, "expecting code base URL");
            String base = expand(in.sval);
            if (File.separatorChar != '/')
              base = base.replace(File.separatorChar, '/');
            try
              {
                currentBase = new URL(base);
              }
            catch (MalformedURLException mue)
              {
                error(url, in, mue.toString());
              }
            tok = in.nextToken();
            if (tok != ',')
              in.pushBack();
          }
        else if (in.sval.equalsIgnoreCase("principal"))
          {
            if (state != STATE_GRANT)
              error(url, in, "spurious 'principal'");
            tok = in.nextToken();
            if (tok == StreamTokenizer.TT_WORD)
              {
                tok = in.nextToken();
                if (tok != '"' && tok != '\'')
                  error(url, in, "expecting principal name");
                String name = in.sval;
                Principal p = null;
                try
                  {
                    Class pclass = Class.forName(in.sval);
                    Constructor c =
                      pclass.getConstructor(new Class[] { String.class });
                    p = (Principal) c.newInstance(new Object[] { name });
                  }
                catch (Exception x)
                  {
                    error(url, in, x.toString());
                  }
                for (Iterator it = keystores.iterator(); it.hasNext(); )
                  {
                    KeyStore ks = (KeyStore) it.next();
                    try
                      {
                        for (Enumeration e = ks.aliases(); e.hasMoreElements(); )
                          {
                            String alias = (String) e.nextElement();
                            if (ks.isCertificateEntry(alias))
                              {
                                Certificate cert = ks.getCertificate(alias);
                                if (!(cert instanceof X509Certificate))
                                  continue;
                                if (p.equals(((X509Certificate) cert).getSubjectDN()) 
||
                                    p.equals(((X509Certificate) 
cert).getSubjectX500Principal()))
                                  currentCerts.add(cert);
                              }
                          }
                      }
                    catch (KeyStoreException kse)
                      {
                        error(url, in, kse.toString());
                      }
                  }
              }
            else if (tok == '"' || tok == '\'')
              {
                String alias = in.sval;
                for (Iterator it = keystores.iterator(); it.hasNext(); )
                  {
                    KeyStore ks = (KeyStore) it.next();
                    try
                      {
                        if (ks.isCertificateEntry(alias))
                          currentCerts.add(ks.getCertificate(alias));
                      }
                    catch (KeyStoreException kse)
                      {
                        error(url, in, kse.toString());
                      }
                  }
              }
            else
              error(url, in, "expecting principal");
            tok = in.nextToken();
            if (tok != ',')
              in.pushBack();
          }
        else if (in.sval.equalsIgnoreCase("permission"))
          {
            if (state != STATE_PERMS)
              error(url, in, "spurious 'permission'");
            tok = in.nextToken();
            if (tok != StreamTokenizer.TT_WORD)
              error(url, in, "expecting permission class name");
            String className = in.sval;
            Class clazz = null;
            try
              {
                clazz = Class.forName(className);
              }
            catch (ClassNotFoundException cnfe)
              {
              }
            tok = in.nextToken();
            if (tok == ';')
              {
                if (clazz == null)
                  {
                    currentPerms.add(new UnresolvedPermission(className,
                      null, null, (Certificate[]) currentCerts.toArray(new 
Certificate[0])));
                    continue;
                  }
                try
                  {
                    currentPerms.add((Permission) clazz.newInstance());
                  }
                catch (Exception x)
                  {
                    error(url, in, x.toString());
                  }
                continue;
              }
            if (tok != '"' && tok != '\'')
              error(url, in, "expecting permission target");
            String target = expand(in.sval);
            tok = in.nextToken();
            if (tok == ';')
              {
                if (clazz == null)
                  {
                    currentPerms.add(new UnresolvedPermission(className,
                      target, null, (Certificate[]) currentCerts.toArray(new 
Certificate[0])));
                    continue;
                  }
                try
                  {
                    Constructor c =
                      clazz.getConstructor(new Class[] { String.class });
                    currentPerms.add((Permission) c.newInstance(
                      new Object[] { target }));
                  }
                catch (Exception x)
                  {
                    error(url, in, x.toString());
                  }
                continue;
              }
            if (tok != ',')
              error(url, in, "expecting ','");
            tok = in.nextToken();
            if (tok == StreamTokenizer.TT_WORD)
              {
                if (!in.sval.equalsIgnoreCase("signedBy"))
                  error(url, in, "expecting 'signedBy'");
                try
                  {
                    Constructor c =
                      clazz.getConstructor(new Class[] { String.class });
                    currentPerms.add((Permission) c.newInstance(
                      new Object[] { target }));
                  }
                catch (Exception x)
                  {
                    error(url, in, x.toString());
                  }
                in.pushBack();
                continue;
              }
            if (tok != '"' && tok != '\'')
              error(url, in, "expecting permission action");
            String action = in.sval;
            if (clazz == null)
              {
                currentPerms.add(new UnresolvedPermission(className,
                  target, action, (Certificate[]) currentCerts.toArray(new 
Certificate[0])));
                continue;
              }
            else
              {
                try
                  {
                    Constructor c = clazz.getConstructor(
                      new Class[] { String.class, String.class });
                    currentPerms.add((Permission) c.newInstance(
                      new Object[] { target, action }));
                  }
                catch (Exception x)
                  {
                    error(url, in, x.toString());
                  }
              }
            tok = in.nextToken();
            if (tok != ';' && tok != ',')
              error(url, in, "expecting ';' or ','");
          }
      }
  }

  /**
   * Expand all instances of <code>"${property-name}"</code> into
   * <code>System.getProperty("property-name")</code>.
   */
  private static String expand(final String s)
  {
    final StringBuffer result = new StringBuffer();
    final StringBuffer prop = new StringBuffer();
    int state = 0;
    for (int i = 0; i < s.length(); i++)
      {
        switch (state)
          {
          case 0:
            if (s.charAt(i) == '$')
              state = 1;
            else
              result.append(s.charAt(i));
            break;
          case 1:
            if (s.charAt(i) == '{')
              state = 2;
            else
              {
                state = 0;
                result.append('$').append(s.charAt(i));
              }
            break;
          case 2:
            if (s.charAt(i) == '}')
              {
                String p = prop.toString();
                if (p.equals("/"))
                  p = "file.separator";
                p = System.getProperty(p);
                if (p == null)
                  p = "";
                result.append(p);
                prop.setLength(0);
                state = 0;
              }
            else
              prop.append(s.charAt(i));
            break;
          }
      }
    if (state != 0)
      result.append('$').append('{').append(prop);
    return result.toString();
  }

  /**
   * I miss macros.
   */
  private static void error(URL base, StreamTokenizer in, String msg)
    throws IOException
  {
    throw new IOException(base+":"+in.lineno()+": "+msg);
  }
}
_______________________________________________
Classpath mailing list
[EMAIL PROTECTED]
http://mail.gnu.org/mailman/listinfo/classpath

Reply via email to