blob: 19924e1f4cff2529e2d41a1f1432081b5639ff63 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.felix.framework.security.verifier;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.apache.felix.framework.cache.BundleArchive;
public final class SignerMatcher
{
private final String m_filter;
private final String m_root;
private final BundleArchive m_archive;
private final BundleDNParser m_parser;
public SignerMatcher(String filter)
{
m_filter = filter;
m_root = null;
m_archive = null;
m_parser = null;
}
public SignerMatcher(String root, BundleArchive archive, BundleDNParser parser)
{
m_filter = null;
m_root = root;
m_archive = archive;
m_parser = parser;
}
public boolean equals(Object o)
{
if (!(o instanceof SignerMatcher))
{
return false;
}
String pattern = ((SignerMatcher) o).m_filter;
if (pattern == null)
{
return true;
}
if (m_archive == null)
{
return pattern.trim().equals("\\*");
}
String[] dns;
try
{
dns = m_parser.getDNChains(m_root + "-" + m_archive.getLastModified(),
m_archive.getRevision(m_archive.getRevisionCount() -1));
}
catch (Exception ex)
{
// TODO: log this or something
ex.printStackTrace();
return false;
}
if (dns == null)
{
return pattern.trim().equals("\\*");
}
for (int i = 0;i < dns.length;i++)
{
if (match(pattern, dns[i]))
{
return true;
}
}
return false;
}
public int hashCode()
{
return 42;
}
// see core spec 2.3
public static boolean match(String pattern, String dn)
{
try
{
return ((pattern != null) && (dn != null)) ?
matchDN(pattern.toCharArray(), 0, dn.toCharArray(), 0) : false;
}
catch (Exception ex)
{
// TODO: log this or something
ex.printStackTrace();
}
return false;
}
private static boolean matchDN(char[] pattern, int pPos, char[] dn, int dPos)
{
pPos = skip(pattern, pPos, ' ');
if (pPos >= pattern.length)
{
return true;
}
int befor = pPos;
if ((pPos < pattern.length -1) && (pattern[pPos] == '\\') && (pattern[pPos + 1] == '*'))
{
pPos = pPos + 1;
}
switch (pattern[pPos++])
{
case '*':
pPos = skip(pattern, pPos, ' ');
if ((pPos < pattern.length) && (pattern[pPos] == ';'))
{
if (matchDN(pattern, ++pPos, dn, dPos))
{
return true;
}
return matchDN(pattern, pPos, dn, skipEscapedUntil(dn, dPos, ';') + 1);
}
if (pPos >= pattern.length)
{
return true;
}
return matchRDN(pattern, befor, dn, dPos);
case '-':
pPos = skip(pattern, pPos, ' ');
if ((pPos < pattern.length) && (pattern[pPos] == ';'))
{
int next = dPos;
pPos++;
do
{
if (matchDN(pattern, pPos, dn, next))
{
return true;
}
next = skipEscapedUntil(dn, next, ';') + 1;
} while (next < dn.length);
return false;
}
if (pPos >= pattern.length)
{
return true;
}
throw new IllegalArgumentException("[" + pPos + "]" + new String(pattern));
default:
break;
}
return matchRDN(pattern, befor, dn, dPos);
}
private static boolean matchRDN(char[] pattern, int pPos, char[] dn, int dPos)
{
pPos = skip(pattern, pPos, ' ');
if (pPos >= pattern.length)
{
return true;
}
if ((pPos < pattern.length -1) && (pattern[pPos] == '\\') && (pattern[pPos + 1] == '*'))
{
pPos = pPos + 1;
}
switch (pattern[pPos++])
{
case '*':
pPos = skip(pattern, pPos, ' ');
if ((pPos < pattern.length) && (pattern[pPos] == ','))
{
pPos++;
do
{
if (matchKV(pattern, pPos, dn, dPos))
{
return true;
}
int comma = skipEscapedUntil(dn, dPos, ',');
int colon = skipEscapedUntil(dn, dPos, ';');
dPos = (comma > colon) ? colon : comma;
} while ((dPos < dn.length) && (dn[dPos++] == ','));
return false;
}
throw new IllegalArgumentException("[" + pPos + "]" + new String(pattern));
default:
break;
}
return matchKV(pattern, pPos - 1, dn, dPos);
}
private static boolean matchKV(char[] pattern, int pPos, char[] dn, int dPos)
{
pPos = skip(pattern, pPos, ' ');
if (pPos >= pattern.length)
{
return false;
}
int equals = skipEscapedUntil(pattern, pPos, '=');
int comma = skipEscapedUntil(pattern, pPos, ',');
int colon = skipEscapedUntil(pattern, pPos, ';');
if (((colon < pattern.length) && (colon < equals)) ||
((comma < pattern.length) && (comma < equals)) ||
(equals >= pattern.length))
{
return false;
}
String key = (String) KEY2OIDSTRING.get(
new String(pattern, pPos, equals - pPos).toLowerCase(Locale.US).trim());
if (key == null)
{
throw new IllegalArgumentException("Bad key [" +
new String(pattern, pPos, equals - pPos) + "] in [" +
new String(pattern) + "]");
}
pPos = equals + 1;
int keylength = key.length();
for (int i = 0;i < keylength;i++)
{
if ((dPos >= dn.length) || (key.charAt(i) != dn[dPos++]))
{
return false;
}
}
if ((dPos >= dn.length) || (dn[dPos++] != '='))
{
return false;
}
pPos = skip(pattern, pPos, ' ');
if ((pPos < pattern.length -1) && (pattern[pPos] == '\\') && (pattern[pPos + 1] == '*'))
{
pPos = skip(pattern, pPos + 2, ' ');
if (pPos >= pattern.length)
{
return true;
}
comma = skipEscapedUntil(dn, dPos, ',');
colon = skipEscapedUntil(dn, dPos, ';');
if ((pattern[pPos] == ',') && (colon > comma))
{
return matchKV(pattern, ++pPos, dn, comma + 1);
}
if (pattern[pPos] == ';' )
{
return matchDN(pattern, ++pPos, dn, colon + 1);
}
return false;
}
boolean escaped = false;
while ((pPos < pattern.length) && (dPos < dn.length))
{
switch (Character.toLowerCase(pattern[pPos++]))
{
case ' ':
if ((pattern[pPos - 2] != ' ') && ((dn[dPos++] != ' ') &&
(dn[--dPos] != ';') && (dn[dPos] != ',')))
{
return false;
}
break;
case '\\':
escaped = !escaped;
break;
case '(':
case ')':
if (escaped)
{
if (dn[dPos++] != pattern[pPos - 1])
{
return false;
}
escaped = false;
break;
}
return false;
case ';':
if (!escaped)
{
if ((dPos < dn.length) && ((dn[dPos] == ',') || (dn[dPos] == ';')))
{
return matchDN(pattern, pPos, dn, skipEscapedUntil(dn, dPos, ';') + 1);
}
return false;
}
case ',':
if (!escaped)
{
if ((dPos < dn.length) && (dn[dPos] == ','))
{
return matchKV(pattern, pPos, dn, dPos + 1);
}
return false;
}
default:
if (escaped)
{
if (dn[dPos++] != '\\')
{
return false;
}
escaped = false;
}
if (dn[dPos++] != Character.toLowerCase(pattern[pPos - 1]))
{
return false;
}
break;
}
}
pPos = skip(pattern, pPos, ' ');
if (pPos >= pattern.length)
{
if ((dPos >= dn.length) || (dn[dPos] == ',') || (dn[dPos] == ';'))
{
return true;
}
}
else
{
switch (pattern[pPos++])
{
case ',':
return matchKV(pattern, pPos, dn, dPos);
case ';':
return matchDN(pattern, pPos, dn, dPos);
default:
break;
}
}
return false;
}
private static final Map KEY2OIDSTRING = new HashMap();
static {
KEY2OIDSTRING.put("2.5.4.3", "cn");
KEY2OIDSTRING.put("cn", "cn");
KEY2OIDSTRING.put("commonname", "cn");
KEY2OIDSTRING.put("2.5.4.4", "sn");
KEY2OIDSTRING.put("sn", "sn");
KEY2OIDSTRING.put("surname", "sn");
KEY2OIDSTRING.put("2.5.4.6", "c");
KEY2OIDSTRING.put("c", "c");
KEY2OIDSTRING.put("countryname", "c");
KEY2OIDSTRING.put("2.5.4.7", "l");
KEY2OIDSTRING.put("l", "l");
KEY2OIDSTRING.put("localityname", "l");
KEY2OIDSTRING.put("2.5.4.8", "st");
KEY2OIDSTRING.put("st", "st");
KEY2OIDSTRING.put("stateorprovincename", "st");
KEY2OIDSTRING.put("2.5.4.10", "o");
KEY2OIDSTRING.put("o", "o");
KEY2OIDSTRING.put("organizationname", "o");
KEY2OIDSTRING.put("2.5.4.11", "ou");
KEY2OIDSTRING.put("ou", "ou");
KEY2OIDSTRING.put("organizationalunitname", "ou");
KEY2OIDSTRING.put("2.5.4.12", "title");
KEY2OIDSTRING.put("t", "title");
KEY2OIDSTRING.put("title", "title");
KEY2OIDSTRING.put("2.5.4.42", "givenname");
KEY2OIDSTRING.put("givenname", "givenname");
KEY2OIDSTRING.put("2.5.4.43", "initials");
KEY2OIDSTRING.put("initials", "initials");
KEY2OIDSTRING.put("2.5.4.44", "generationqualifier");
KEY2OIDSTRING.put("generationqualifier", "generationqualifier");
KEY2OIDSTRING.put("2.5.4.46", "dnqualifier");
KEY2OIDSTRING.put("dnqualifier", "dnqualifier");
KEY2OIDSTRING.put("2.5.4.9", "street");
KEY2OIDSTRING.put("street", "street");
KEY2OIDSTRING.put("streetaddress", "street");
KEY2OIDSTRING.put("0.9.2342.19200300.100.1.25", "dc");
KEY2OIDSTRING.put("dc", "dc");
KEY2OIDSTRING.put("domaincomponent", "dc");
KEY2OIDSTRING.put("0.9.2342.19200300.100.1.1", "uid");
KEY2OIDSTRING.put("uid", "uid");
KEY2OIDSTRING.put("userid", "uid");
KEY2OIDSTRING.put("1.2.840.113549.1.9.1", "emailaddress");
KEY2OIDSTRING.put("emailaddress", "emailaddress");
KEY2OIDSTRING.put("2.5.4.5", "serialnumber");
KEY2OIDSTRING.put("serialnumber", "serialnumber");
}
private static int skipEscapedUntil(char[] string, int pos, char value)
{
boolean escaped = false;
while (pos < string.length)
{
switch (string[pos++])
{
case '\\':
escaped = true;
break;
default:
if (!escaped)
{
if (string[pos - 1] == value)
{
return pos - 1;
}
}
escaped = false;
break;
}
}
return pos;
}
private static int skip(char[] string, int pos, char value)
{
while (pos < string.length)
{
if (string[pos] != value)
{
break;
}
pos++;
}
return pos;
}
}