blob: e156d6d7106ffccc7d89de13f81704cddb03946f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2017 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.osgi.internal.permadmin;
import java.io.*;
import java.math.BigInteger;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.*;
import java.util.*;
import org.eclipse.osgi.internal.framework.EquinoxBundle;
import org.eclipse.osgi.storage.PermissionData;
import org.osgi.framework.*;
import org.osgi.service.condpermadmin.*;
import org.osgi.service.permissionadmin.PermissionAdmin;
import org.osgi.service.permissionadmin.PermissionInfo;
public final class SecurityAdmin implements PermissionAdmin, ConditionalPermissionAdmin {
private static final PermissionCollection DEFAULT_DEFAULT;
static {
AllPermission allPerm = new AllPermission();
DEFAULT_DEFAULT = allPerm.newPermissionCollection();
if (DEFAULT_DEFAULT != null)
DEFAULT_DEFAULT.add(allPerm);
}
// Base implied permissions for all bundles
private static final String OSGI_BASE_IMPLIED_PERMISSIONS = "implied.permissions"; //$NON-NLS-1$
private static final String ADMIN_IMPLIED_ACTIONS = AdminPermission.RESOURCE + ',' + AdminPermission.METADATA + ',' + AdminPermission.CLASS + ',' + AdminPermission.CONTEXT;
private static final PermissionInfo[] EMPTY_PERM_INFO = new PermissionInfo[0];
/* @GuardedBy(lock) */
private final PermissionAdminTable permAdminTable = new PermissionAdminTable();
/* @GuardedBy(lock) */
private SecurityTable condAdminTable;
/* @GuardedBy(lock) */
private PermissionInfoCollection permAdminDefaults;
/* @GuardedBy(lock) */
private long timeStamp = 0;
/* @GuardedBy(lock) */
private long nextID = System.currentTimeMillis();
/* @GuardedBy(lock) */
private final PermissionData permissionStorage;
private final Object lock = new Object();
//private final EquinoxContainer container;
private final PermissionInfo[] impliedPermissionInfos;
private final EquinoxSecurityManager supportedSecurityManager;
private SecurityAdmin(EquinoxSecurityManager supportedSecurityManager, PermissionInfo[] impliedPermissionInfos, PermissionInfoCollection permAdminDefaults) {
this.supportedSecurityManager = supportedSecurityManager;
this.impliedPermissionInfos = impliedPermissionInfos;
this.permAdminDefaults = permAdminDefaults;
this.permissionStorage = null;
}
public SecurityAdmin(EquinoxSecurityManager supportedSecurityManager, PermissionData permissionStorage) {
this.supportedSecurityManager = supportedSecurityManager;
this.permissionStorage = permissionStorage;
this.impliedPermissionInfos = SecurityAdmin.getPermissionInfos(getClass().getResource(OSGI_BASE_IMPLIED_PERMISSIONS));
String[] encodedDefaultInfos = permissionStorage.getPermissionData(null);
PermissionInfo[] defaultInfos = getPermissionInfos(encodedDefaultInfos);
if (defaultInfos != null)
permAdminDefaults = new PermissionInfoCollection(defaultInfos);
String[] locations = permissionStorage.getLocations();
if (locations != null) {
for (int i = 0; i < locations.length; i++) {
String[] encodedLocationInfos = permissionStorage.getPermissionData(locations[i]);
if (encodedLocationInfos != null) {
PermissionInfo[] locationInfos = getPermissionInfos(encodedLocationInfos);
permAdminTable.setPermissions(locations[i], locationInfos);
}
}
}
String[] encodedCondPermInfos = permissionStorage.getConditionalPermissionInfos();
if (encodedCondPermInfos == null)
condAdminTable = new SecurityTable(this, new SecurityRow[0]);
else {
SecurityRow[] rows = new SecurityRow[encodedCondPermInfos.length];
try {
for (int i = 0; i < rows.length; i++)
rows[i] = SecurityRow.createSecurityRow(this, encodedCondPermInfos[i]);
} catch (IllegalArgumentException e) {
// TODO should log
// bad format persisted in storage; start clean
rows = new SecurityRow[0];
}
condAdminTable = new SecurityTable(this, rows);
}
}
private static PermissionInfo[] getPermissionInfos(String[] encodedInfos) {
if (encodedInfos == null)
return null;
PermissionInfo[] results = new PermissionInfo[encodedInfos.length];
for (int i = 0; i < results.length; i++)
results[i] = new PermissionInfo(encodedInfos[i]);
return results;
}
boolean checkPermission(Permission permission, BundlePermissions bundlePermissions) {
// check permissions by location
PermissionInfoCollection locationCollection;
SecurityTable curCondAdminTable;
PermissionInfoCollection curPermAdminDefaults;
// save off the current state of the world while holding the lock
synchronized (lock) {
// get location the hard way to avoid permission check
Bundle bundle = bundlePermissions.getBundle();
locationCollection = bundle instanceof EquinoxBundle ? permAdminTable.getCollection(((EquinoxBundle) bundle).getModule().getLocation()) : null;
curCondAdminTable = condAdminTable;
curPermAdminDefaults = permAdminDefaults;
}
if (locationCollection != null)
return locationCollection.implies(permission);
// if conditional admin table is empty the fall back to defaults
if (curCondAdminTable.isEmpty())
return curPermAdminDefaults != null ? curPermAdminDefaults.implies(permission) : DEFAULT_DEFAULT.implies(permission);
// check the condition table
int result = curCondAdminTable.evaluate(bundlePermissions, permission);
if ((result & SecurityTable.GRANTED) != 0)
return true;
if ((result & SecurityTable.DENIED) != 0)
return false;
if ((result & SecurityTable.POSTPONED) != 0)
return true;
return false;
}
public PermissionInfo[] getDefaultPermissions() {
synchronized (lock) {
if (permAdminDefaults == null)
return null;
return permAdminDefaults.getPermissionInfos();
}
}
public String[] getLocations() {
synchronized (lock) {
String[] results = permAdminTable.getLocations();
return results.length == 0 ? null : results;
}
}
public PermissionInfo[] getPermissions(String location) {
synchronized (lock) {
return permAdminTable.getPermissions(location);
}
}
public void setDefaultPermissions(PermissionInfo[] permissions) {
checkAllPermission();
synchronized (lock) {
if (permissions == null)
permAdminDefaults = null;
else
permAdminDefaults = new PermissionInfoCollection(permissions);
permissionStorage.setPermissionData(null, getEncodedPermissionInfos(permissions));
}
}
private static void checkAllPermission() {
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(new AllPermission());
}
private static String[] getEncodedPermissionInfos(PermissionInfo[] permissions) {
if (permissions == null)
return null;
String[] encoded = new String[permissions.length];
for (int i = 0; i < encoded.length; i++)
encoded[i] = permissions[i].getEncoded();
return encoded;
}
public void setPermissions(String location, PermissionInfo[] permissions) {
checkAllPermission();
synchronized (lock) {
permAdminTable.setPermissions(location, permissions);
permissionStorage.setPermissionData(location, getEncodedPermissionInfos(permissions));
}
}
void delete(SecurityRow securityRow, boolean firstTry) {
ConditionalPermissionUpdate update = newConditionalPermissionUpdate();
List<ConditionalPermissionInfo> rows = update.getConditionalPermissionInfos();
for (Iterator<ConditionalPermissionInfo> iRows = rows.iterator(); iRows.hasNext();) {
ConditionalPermissionInfo info = iRows.next();
if (securityRow.getName().equals(info.getName())) {
iRows.remove();
synchronized (lock) {
if (!update.commit()) {
if (firstTry)
// try again
delete(securityRow, false);
}
}
break;
}
}
}
/**
* @deprecated
*/
public ConditionalPermissionInfo addConditionalPermissionInfo(ConditionInfo[] conds, PermissionInfo[] perms) {
return setConditionalPermissionInfo(null, conds, perms, true);
}
public ConditionalPermissionInfo newConditionalPermissionInfo(String name, ConditionInfo[] conditions, PermissionInfo[] permissions, String decision) {
return new SecurityRowSnapShot(name, conditions, permissions, decision);
}
public ConditionalPermissionInfo newConditionalPermissionInfo(String encoded) {
return SecurityRow.createSecurityRowSnapShot(encoded);
}
public ConditionalPermissionUpdate newConditionalPermissionUpdate() {
synchronized (lock) {
return new SecurityTableUpdate(this, condAdminTable.getRows(), timeStamp);
}
}
public AccessControlContext getAccessControlContext(String[] signers) {
SecurityAdmin snapShot = getSnapShot();
return new AccessControlContext(new ProtectionDomain[] {createProtectionDomain(createMockBundle(signers), snapShot)});
}
/**
* @deprecated
*/
public ConditionalPermissionInfo getConditionalPermissionInfo(String name) {
synchronized (lock) {
return condAdminTable.getRow(name);
}
}
/**
* @deprecated
*/
public Enumeration<ConditionalPermissionInfo> getConditionalPermissionInfos() {
// could implement our own Enumeration, but we don't care about performance here. Just do something simple:
synchronized (lock) {
SecurityRow[] rows = condAdminTable.getRows();
List<ConditionalPermissionInfo> vRows = new ArrayList<>(rows.length);
for (int i = 0; i < rows.length; i++)
vRows.add(rows[i]);
return Collections.enumeration(vRows);
}
}
/**
* @deprecated
*/
public ConditionalPermissionInfo setConditionalPermissionInfo(String name, ConditionInfo[] conds, PermissionInfo[] perms) {
return setConditionalPermissionInfo(name, conds, perms, true);
}
private SecurityAdmin getSnapShot() {
SecurityAdmin sa;
synchronized (lock) {
sa = new SecurityAdmin(supportedSecurityManager, impliedPermissionInfos, permAdminDefaults);
SecurityRow[] rows = condAdminTable.getRows();
SecurityRow[] rowsSnapShot = new SecurityRow[rows.length];
for (int i = 0; i < rows.length; i++)
rowsSnapShot[i] = new SecurityRow(sa, rows[i].getName(), rows[i].getConditionInfos(), rows[i].getPermissionInfos(), rows[i].getAccessDecision());
sa.condAdminTable = new SecurityTable(sa, rowsSnapShot);
}
return sa;
}
private ConditionalPermissionInfo setConditionalPermissionInfo(String name, ConditionInfo[] conds, PermissionInfo[] perms, boolean firstTry) {
ConditionalPermissionUpdate update = newConditionalPermissionUpdate();
List<ConditionalPermissionInfo> rows = update.getConditionalPermissionInfos();
ConditionalPermissionInfo newInfo = newConditionalPermissionInfo(name, conds, perms, ConditionalPermissionInfo.ALLOW);
int index = -1;
if (name != null) {
for (int i = 0; i < rows.size() && index < 0; i++) {
ConditionalPermissionInfo info = rows.get(i);
if (name.equals(info.getName())) {
index = i;
}
}
}
if (index < 0) {
// must always add to the beginning (bug 303930)
rows.add(0, newInfo);
index = 0;
} else {
rows.set(index, newInfo);
}
synchronized (lock) {
if (!update.commit()) {
if (firstTry)
// try again
setConditionalPermissionInfo(name, conds, perms, false);
}
return condAdminTable.getRow(index);
}
}
boolean commit(List<ConditionalPermissionInfo> rows, long updateStamp) {
checkAllPermission();
synchronized (lock) {
if (updateStamp != timeStamp)
return false;
SecurityRow[] newRows = new SecurityRow[rows.size()];
Collection<String> names = new ArrayList<>();
for (int i = 0; i < newRows.length; i++) {
Object rowObj = rows.get(i);
if (!(rowObj instanceof ConditionalPermissionInfo))
throw new IllegalStateException("Invalid type \"" + rowObj.getClass().getName() + "\" at row: " + i); //$NON-NLS-1$//$NON-NLS-2$
ConditionalPermissionInfo infoBaseRow = (ConditionalPermissionInfo) rowObj;
String name = infoBaseRow.getName();
if (name == null)
name = generateName();
if (names.contains(name))
throw new IllegalStateException("Duplicate name \"" + name + "\" at row: " + i); //$NON-NLS-1$//$NON-NLS-2$
names.add(name);
newRows[i] = new SecurityRow(this, name, infoBaseRow.getConditionInfos(), infoBaseRow.getPermissionInfos(), infoBaseRow.getAccessDecision());
}
condAdminTable = new SecurityTable(this, newRows);
permissionStorage.saveConditionalPermissionInfos(condAdminTable.getEncodedRows());
timeStamp += 1;
return true;
}
}
/* GuardedBy(lock) */
private String generateName() {
return "generated_" + Long.toString(nextID++); //$NON-NLS-1$;
}
public ProtectionDomain createProtectionDomain(Bundle bundle) {
return createProtectionDomain(bundle, this);
}
private ProtectionDomain createProtectionDomain(Bundle bundle, SecurityAdmin sa) {
PermissionInfoCollection impliedPermissions = getImpliedPermission(bundle);
URL permEntry = null;
try {
permEntry = bundle.getEntry("OSGI-INF/permissions.perm"); //$NON-NLS-1$
} catch (IllegalStateException e) {
// bundle may be uninstalled
}
PermissionInfo[] restrictedInfos = getFileRelativeInfos(SecurityAdmin.getPermissionInfos(permEntry), bundle);
PermissionInfoCollection restrictedPermissions = restrictedInfos == null ? null : new PermissionInfoCollection(restrictedInfos);
BundlePermissions bundlePermissions = new BundlePermissions(bundle, sa, impliedPermissions, restrictedPermissions);
return new ProtectionDomain(null, bundlePermissions);
}
private PermissionInfoCollection getImpliedPermission(Bundle bundle) {
if (impliedPermissionInfos == null)
return null;
// create the implied AdminPermission actions for this bundle
PermissionInfo impliedAdminPermission = new PermissionInfo(AdminPermission.class.getName(), "(id=" + bundle.getBundleId() + ")", ADMIN_IMPLIED_ACTIONS); //$NON-NLS-1$ //$NON-NLS-2$
PermissionInfo[] bundleImpliedInfos = new PermissionInfo[impliedPermissionInfos.length + 1];
System.arraycopy(impliedPermissionInfos, 0, bundleImpliedInfos, 0, impliedPermissionInfos.length);
bundleImpliedInfos[impliedPermissionInfos.length] = impliedAdminPermission;
return new PermissionInfoCollection(getFileRelativeInfos(bundleImpliedInfos, bundle));
}
private PermissionInfo[] getFileRelativeInfos(PermissionInfo[] permissionInfos, Bundle bundle) {
if (permissionInfos == null)
return permissionInfos;
PermissionInfo[] results = new PermissionInfo[permissionInfos.length];
for (int i = 0; i < permissionInfos.length; i++) {
results[i] = permissionInfos[i];
if ("java.io.FilePermission".equals(permissionInfos[i].getType())) { //$NON-NLS-1$
if (!"<<ALL FILES>>".equals(permissionInfos[i].getName())) { //$NON-NLS-1$
File file = new File(permissionInfos[i].getName());
if (!file.isAbsolute()) { // relative name
try {
File target = bundle.getDataFile(permissionInfos[i].getName());
if (target != null)
results[i] = new PermissionInfo(permissionInfos[i].getType(), target.getPath(), permissionInfos[i].getActions());
} catch (IllegalStateException e) {
// can happen if the bundle has been uninstalled;
// we just keep the original permission in this case.
}
}
}
}
}
return results;
}
public void clearCaches() {
PermissionInfoCollection[] permAdminCollections;
SecurityRow[] condAdminRows;
synchronized (lock) {
permAdminCollections = permAdminTable.getCollections();
condAdminRows = condAdminTable.getRows();
}
for (int i = 0; i < permAdminCollections.length; i++)
permAdminCollections[i].clearPermissionCache();
for (int i = 0; i < condAdminRows.length; i++)
condAdminRows[i].clearCaches();
}
EquinoxSecurityManager getSupportedSecurityManager() {
return supportedSecurityManager != null ? supportedSecurityManager : getSupportedSystemSecurityManager();
}
static private EquinoxSecurityManager getSupportedSystemSecurityManager() {
try {
EquinoxSecurityManager equinoxManager = (EquinoxSecurityManager) System.getSecurityManager();
return equinoxManager != null && equinoxManager.inCheckPermission() ? equinoxManager : null;
} catch (ClassCastException e) {
return null;
}
}
private static PermissionInfo[] getPermissionInfos(URL resource) {
if (resource == null)
return null;
PermissionInfo[] info = EMPTY_PERM_INFO;
DataInputStream in = null;
try {
in = new DataInputStream(resource.openStream());
List<PermissionInfo> permissions = new ArrayList<>();
BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
while (true) {
String line = reader.readLine();
if (line == null) /* EOF */
break;
line = line.trim();
if ((line.length() == 0) || line.startsWith("#") || line.startsWith("//")) /* comments *///$NON-NLS-1$ //$NON-NLS-2$
continue;
try {
permissions.add(new PermissionInfo(line));
} catch (IllegalArgumentException iae) {
/* incorrectly encoded permission */
// TODO used to publish an event here
}
}
int size = permissions.size();
if (size > 0)
info = permissions.toArray(new PermissionInfo[size]);
} catch (IOException e) {
// do nothing
} finally {
try {
if (in != null)
in.close();
} catch (IOException ee) {
// do nothing
}
}
return info;
}
private static Bundle createMockBundle(String[] signers) {
Map<X509Certificate, List<X509Certificate>> signersMap = new HashMap<>();
for (int i = 0; i < signers.length; i++) {
List<String> chain = parseDNchain(signers[i]);
List<X509Certificate> signersList = new ArrayList<>();
Principal subject = null, issuer = null;
X509Certificate first = null;
for (Iterator<String> iChain = chain.iterator(); iChain.hasNext();) {
subject = issuer == null ? new MockPrincipal(iChain.next()) : issuer;
issuer = iChain.hasNext() ? new MockPrincipal(iChain.next()) : subject;
X509Certificate cert = new MockX509Certificate(subject, issuer);
if (first == null)
first = cert;
signersList.add(cert);
}
if (subject != issuer)
signersList.add(new MockX509Certificate(issuer, issuer));
signersMap.put(first, signersList);
}
return new MockBundle(signersMap);
}
static class MockBundle implements Bundle {
private final Map<X509Certificate, List<X509Certificate>> signers;
MockBundle(Map<X509Certificate, List<X509Certificate>> signers) {
this.signers = signers;
}
public Enumeration<URL> findEntries(String path, String filePattern, boolean recurse) {
return null;
}
public BundleContext getBundleContext() {
return null;
}
public long getBundleId() {
return -1;
}
public URL getEntry(String path) {
return null;
}
public Enumeration<String> getEntryPaths(String path) {
return null;
}
public Dictionary<String, String> getHeaders() {
return new Hashtable<>();
}
public Dictionary<String, String> getHeaders(String locale) {
return getHeaders();
}
public long getLastModified() {
return 0;
}
public String getLocation() {
return ""; //$NON-NLS-1$
}
public ServiceReference<?>[] getRegisteredServices() {
return null;
}
public URL getResource(String name) {
return null;
}
/**
* @throws IOException
*/
public Enumeration<URL> getResources(String name) throws IOException {
return null;
}
public ServiceReference<?>[] getServicesInUse() {
return null;
}
public Map<X509Certificate, List<X509Certificate>> getSignerCertificates(int signersType) {
return new HashMap<>(signers);
}
public int getState() {
return Bundle.UNINSTALLED;
}
public String getSymbolicName() {
return null;
}
public Version getVersion() {
return Version.emptyVersion;
}
public boolean hasPermission(Object permission) {
return false;
}
/**
* @throws ClassNotFoundException
*/
public Class<?> loadClass(String name) throws ClassNotFoundException {
throw new IllegalStateException();
}
/**
* @throws BundleException
*/
public void start(int options) throws BundleException {
throw new IllegalStateException();
}
/**
* @throws BundleException
*/
public void start() throws BundleException {
throw new IllegalStateException();
}
/**
* @throws BundleException
*/
public void stop(int options) throws BundleException {
throw new IllegalStateException();
}
/**
* @throws BundleException
*/
public void stop() throws BundleException {
throw new IllegalStateException();
}
/**
* @throws BundleException
*/
public void uninstall() throws BundleException {
throw new IllegalStateException();
}
/**
* @throws BundleException
*/
public void update() throws BundleException {
throw new IllegalStateException();
}
/**
* @throws BundleException
*/
public void update(InputStream in) throws BundleException {
throw new IllegalStateException();
}
public int compareTo(Bundle o) {
return 0;
}
public <A> A adapt(Class<A> type) {
throw new IllegalStateException();
}
public File getDataFile(String filename) {
return null;
}
}
private static class MockX509Certificate extends X509Certificate {
private final Principal subject;
private final Principal issuer;
MockX509Certificate(Principal subject, Principal issuer) {
this.subject = subject;
this.issuer = issuer;
}
public Principal getSubjectDN() {
return subject;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj instanceof MockX509Certificate)
return subject.equals(((MockX509Certificate) obj).subject) && issuer.equals(((MockX509Certificate) obj).issuer);
return false;
}
public int hashCode() {
return subject.hashCode() + issuer.hashCode();
}
public String toString() {
return subject.toString();
}
/**
* @throws CertificateExpiredException
* @throws java.security.cert.CertificateNotYetValidException
*/
public void checkValidity() throws CertificateExpiredException, java.security.cert.CertificateNotYetValidException {
throw new UnsupportedOperationException();
}
/**
* @throws java.security.cert.CertificateExpiredException
* @throws java.security.cert.CertificateNotYetValidException
*/
public void checkValidity(Date var0) throws java.security.cert.CertificateExpiredException, java.security.cert.CertificateNotYetValidException {
throw new UnsupportedOperationException();
}
public int getBasicConstraints() {
throw new UnsupportedOperationException();
}
public Principal getIssuerDN() {
return issuer;
}
public boolean[] getIssuerUniqueID() {
throw new UnsupportedOperationException();
}
public boolean[] getKeyUsage() {
throw new UnsupportedOperationException();
}
public Date getNotAfter() {
throw new UnsupportedOperationException();
}
public Date getNotBefore() {
throw new UnsupportedOperationException();
}
public BigInteger getSerialNumber() {
throw new UnsupportedOperationException();
}
public String getSigAlgName() {
throw new UnsupportedOperationException();
}
public String getSigAlgOID() {
throw new UnsupportedOperationException();
}
public byte[] getSigAlgParams() {
throw new UnsupportedOperationException();
}
public byte[] getSignature() {
throw new UnsupportedOperationException();
}
public boolean[] getSubjectUniqueID() {
throw new UnsupportedOperationException();
}
/**
* @throws CertificateEncodingException
*/
public byte[] getTBSCertificate() throws CertificateEncodingException {
throw new UnsupportedOperationException();
}
public int getVersion() {
throw new UnsupportedOperationException();
}
/**
* @throws CertificateEncodingException
*/
public byte[] getEncoded() throws CertificateEncodingException {
throw new UnsupportedOperationException();
}
public PublicKey getPublicKey() {
throw new UnsupportedOperationException();
}
/**
* @throws java.security.InvalidKeyException
* @throws java.security.NoSuchAlgorithmException
* @throws java.security.NoSuchProviderException
* @throws java.security.SignatureException
* @throws java.security.cert.CertificateException
*/
public void verify(PublicKey var0) throws java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException, java.security.cert.CertificateException {
throw new UnsupportedOperationException();
}
/**
* @throws InvalidKeyException
* @throws NoSuchAlgorithmException
* @throws NoSuchProviderException
* @throws SignatureException
* @throws CertificateException
*/
public void verify(PublicKey var0, String var1) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, SignatureException, CertificateException {
throw new UnsupportedOperationException();
}
public Set<String> getCriticalExtensionOIDs() {
throw new UnsupportedOperationException();
}
public byte[] getExtensionValue(String var0) {
throw new UnsupportedOperationException();
}
public Set<String> getNonCriticalExtensionOIDs() {
throw new UnsupportedOperationException();
}
public boolean hasUnsupportedCriticalExtension() {
throw new UnsupportedOperationException();
}
}
private static class MockPrincipal implements Principal {
private final String name;
MockPrincipal(String name) {
this.name = name;
}
public String getName() {
return name;
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof MockPrincipal) {
return name.equals(((MockPrincipal) obj).name);
}
return false;
}
public int hashCode() {
return name.hashCode();
}
public String toString() {
return getName();
}
}
private static List<String> parseDNchain(String dnChain) {
if (dnChain == null) {
throw new IllegalArgumentException("The DN chain must not be null."); //$NON-NLS-1$
}
List<String> parsed = new ArrayList<>();
int startIndex = 0;
startIndex = skipSpaces(dnChain, startIndex);
while (startIndex < dnChain.length()) {
int endIndex = startIndex;
boolean inQuote = false;
out: while (endIndex < dnChain.length()) {
char c = dnChain.charAt(endIndex);
switch (c) {
case '"' :
inQuote = !inQuote;
break;
case '\\' :
endIndex++; // skip the escaped char
break;
case ';' :
if (!inQuote)
break out;
}
endIndex++;
}
if (endIndex > dnChain.length()) {
throw new IllegalArgumentException("unterminated escape"); //$NON-NLS-1$
}
parsed.add(dnChain.substring(startIndex, endIndex));
startIndex = endIndex + 1;
startIndex = skipSpaces(dnChain, startIndex);
}
return parsed;
}
private static int skipSpaces(String dnChain, int startIndex) {
while (startIndex < dnChain.length() && dnChain.charAt(startIndex) == ' ') {
startIndex++;
}
return startIndex;
}
}