// --- BEGIN COPYRIGHT BLOCK ---
// This program 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; version 2 of the License.
//
// This program 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 this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// (C) 2007 Red Hat, Inc.
// All rights reserved.
// --- END COPYRIGHT BLOCK ---
package com.netscape.cms.policy.extensions;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Vector;
import netscape.security.util.DerValue;
import netscape.security.util.ObjectIdentifier;
import netscape.security.x509.AVAValueConverter;
import netscape.security.x509.Attribute;
import netscape.security.x509.CertificateExtensions;
import netscape.security.x509.CertificateVersion;
import netscape.security.x509.SubjectDirAttributesExtension;
import netscape.security.x509.X500NameAttrMap;
import netscape.security.x509.X509CertInfo;
import com.netscape.certsrv.apps.CMS;
import com.netscape.certsrv.base.EBaseException;
import com.netscape.certsrv.base.IConfigStore;
import com.netscape.certsrv.base.IExtendedPluginInfo;
import com.netscape.certsrv.base.ISubsystem;
import com.netscape.certsrv.logging.ILogger;
import com.netscape.certsrv.policy.IEnrollmentPolicy;
import com.netscape.certsrv.request.IRequest;
import com.netscape.certsrv.request.PolicyResult;
import com.netscape.cms.policy.APolicyRule;
/**
* Policy to add the subject directory attributes extension.
*
*
*
* NOTE: The Policy Framework has been replaced by the Profile Framework.
*
*
*
* @deprecated
* @version $Revision$, $Date$
*/
public class SubjectDirectoryAttributesExt extends APolicyRule
implements IEnrollmentPolicy, IExtendedPluginInfo {
protected static final String PROP_CRITICAL = "critical";
protected static final String PROP_ATTRIBUTE = "attribute";
protected static final String PROP_NUM_ATTRIBUTES = "numAttributes";
protected static final boolean DEF_CRITICAL = false;
protected static final int DEF_NUM_ATTRIBUTES = 3;
protected static final int MAX_NUM_ATTRIBUTES = 10;
protected boolean mCritical;
protected int mNumAttributes;
protected AttributeConfig[] mAttributes = null;
protected IConfigStore mConfig;
protected SubjectDirAttributesExtension mExt = null;
protected Vector mParams = new Vector();
private String[] mEPI = null; // extended plugin info
protected static Vector mDefParams = new Vector();
static {
setDefaultParams();
}
public SubjectDirectoryAttributesExt() {
NAME = "SubjectDirectoryAttributesExtPolicy";
DESC = "Sets Subject Directory Attributes Extension in certificates.";
setExtendedPluginInfo();
}
public void init(ISubsystem owner, IConfigStore config)
throws EBaseException {
boolean enabled = config.getBoolean("enabled", false);
mConfig = config;
mCritical = mConfig.getBoolean(PROP_CRITICAL, false);
mNumAttributes = mConfig.getInteger(PROP_NUM_ATTRIBUTES, DEF_NUM_ATTRIBUTES);
if (mNumAttributes < 1) {
EBaseException ex = new EBaseException(
CMS.getUserMessage("CMS_BASE_MUST_BE_POSITIVE_NUMBER", PROP_NUM_ATTRIBUTES));
log(ILogger.LL_FAILURE, NAME + " Error: " + ex.toString());
throw ex;
}
mAttributes = new AttributeConfig[mNumAttributes];
for (int i = 0; i < mNumAttributes; i++) {
String name = PROP_ATTRIBUTE + i;
IConfigStore c = mConfig.getSubStore(name);
mAttributes[i] = new AttributeConfig(name, c, enabled);
}
if (enabled) {
try {
mExt = formExt(null);
} catch (IOException e) {
log(ILogger.LL_FAILURE, NAME + " Error: " + e.getMessage());
throw new EBaseException(CMS.getUserMessage("CMS_BASE_INTERNAL_ERROR",
"Error forming Subject Directory Attributes Extension. " +
"See log file for details."));
}
}
setInstanceParams();
}
public PolicyResult apply(IRequest req) {
X509CertInfo[] ci =
req.getExtDataInCertInfoArray(IRequest.CERT_INFO);
if (ci == null || ci[0] == null) {
setError(req, CMS.getUserMessage("CMS_POLICY_NO_CERT_INFO"), NAME);
return PolicyResult.REJECTED; // unrecoverable error.
}
for (int i = 0; i < ci.length; i++) {
PolicyResult r = applyCert(req, ci[i]);
if (r == PolicyResult.REJECTED)
return r;
}
return PolicyResult.ACCEPTED;
}
public PolicyResult applyCert(IRequest req, X509CertInfo certInfo) {
CertificateExtensions extensions = null;
try {
// get extension and remove if exists.
extensions = (CertificateExtensions)
certInfo.get(X509CertInfo.EXTENSIONS);
if (extensions == null) {
extensions = new CertificateExtensions();
certInfo.set(X509CertInfo.VERSION,
new CertificateVersion(CertificateVersion.V3));
certInfo.set(X509CertInfo.EXTENSIONS, extensions);
} else {
try {
extensions.delete(SubjectDirAttributesExtension.NAME);
} catch (IOException ee) {
// if name is not found, try deleting the extension using the OID
try {
extensions.delete("2.5.29.9");
} catch (IOException eee) {
}
}
}
// form extension and set.
if (mExt != null) {
extensions.set(SubjectDirAttributesExtension.NAME, mExt);
} else {
SubjectDirAttributesExtension ext = formExt(req);
if (ext != null)
extensions.set(SubjectDirAttributesExtension.NAME, formExt(req));
}
return PolicyResult.ACCEPTED;
} catch (CertificateException e) {
log(ILogger.LL_FAILURE, CMS.getLogMessage("CA_CERT_INFO_ERROR", e.getMessage()));
setError(req, CMS.getUserMessage("CMS_POLICY_UNEXPECTED_POLICY_ERROR"),
NAME, "Certificate Info Error");
return PolicyResult.REJECTED; // unrecoverable error.
} catch (IOException e) {
log(ILogger.LL_FAILURE, CMS.getLogMessage("BASE_IO_ERROR", e.getMessage()));
setError(req, CMS.getUserMessage("CMS_POLICY_UNEXPECTED_POLICY_ERROR"),
NAME, "IOException Error");
return PolicyResult.REJECTED;
}
}
public Vector getInstanceParams() {
return mParams; // inited in init()
}
public Vector getDefaultParams() {
return mDefParams;
}
public String[] getExtendedPluginInfo(Locale locale) {
return mEPI; // inited in the constructor.
}
private void setInstanceParams() {
mParams.addElement(PROP_CRITICAL + "=" + mCritical);
mParams.addElement(PROP_NUM_ATTRIBUTES + "=" + mNumAttributes);
for (int i = 0; i < mNumAttributes; i++) {
mAttributes[i].getInstanceParams(mParams);
}
// clean up others if exists. expensive.
for (int j = mNumAttributes; j < MAX_NUM_ATTRIBUTES; j++) {
mConfig.removeSubStore(PROP_ATTRIBUTE + j);
}
}
private static void setDefaultParams() {
mDefParams.addElement(PROP_CRITICAL + "=" + DEF_CRITICAL);
mDefParams.addElement(PROP_NUM_ATTRIBUTES + "=" + DEF_NUM_ATTRIBUTES);
for (int i = 0; i < DEF_NUM_ATTRIBUTES; i++) {
AttributeConfig.getDefaultParams(PROP_ATTRIBUTE + i, mDefParams);
}
}
private void setExtendedPluginInfo() {
Vector v = new Vector();
v.addElement(PROP_CRITICAL + ";boolean;" +
"RFC 2459 recommendation: MUST be non-critical.");
v.addElement(PROP_NUM_ATTRIBUTES + ";number;" +
"Number of Attributes in the extension.");
for (int i = 0; i < MAX_NUM_ATTRIBUTES; i++) {
AttributeConfig.getExtendedPluginInfo(PROP_ATTRIBUTE + i, v);
}
v.addElement(IExtendedPluginInfo.HELP_TOKEN +
";configuration-policyrules-subjectdirectoryattributes");
v.addElement(IExtendedPluginInfo.HELP_TEXT
+
";Adds Subject Directory Attributes extension. See RFC 2459 (4.2.1.9). It's not recommended as an essential part of the profile, but may be used in local environments.");
mEPI = com.netscape.cmsutil.util.Utils.getStringArrayFromVector(v);
}
private SubjectDirAttributesExtension formExt(IRequest req)
throws IOException {
Vector attrs = new Vector();
// if we're called from init and one attribute is from request attribute
// the ext can't be formed yet.
if (req == null) {
for (int i = 0; i < mNumAttributes; i++) {
if (mAttributes[i].mWhereToGetValue == AttributeConfig.USE_REQUEST_ATTR)
return null;
}
}
// either we're called from apply or all values are fixed.
for (int i = 0; i < mNumAttributes; i++) {
if (mAttributes[i].mAttribute != null) {
attrs.addElement(mAttributes[i].mAttribute);
} else {
// skip attribute if request attribute doesn't exist.
Attribute a = mAttributes[i].formAttr(req);
if (a == null)
continue;
attrs.addElement(a);
}
}
if (attrs.size() == 0)
return null;
Attribute[] attrList = new Attribute[attrs.size()];
attrs.copyInto(attrList);
SubjectDirAttributesExtension ext =
new SubjectDirAttributesExtension(attrList);
return ext;
}
}
class AttributeConfig {
protected static final String PROP_ATTRIBUTE_NAME = "attributeName";
protected static final String PROP_WTG_VALUE = "whereToGetValue";
protected static final String PROP_VALUE = "value";
protected static final String USE_REQUEST_ATTR = "Request Attribute";
protected static final String USE_FIXED = "Fixed Value";
protected String mAttributeName = null;
protected String mWhereToGetValue = null;
protected String mValue = null;
protected String mPrefix = null;
protected String mReqAttr = null;
protected ObjectIdentifier mAttributeOID = null;
protected String mName = null;
protected IConfigStore mConfig = null;
protected Attribute mAttribute = null;
protected static final String ATTRIBUTE_NAME_INFO = "Attribute name.";
protected static final String WTG_VALUE_INFO =
PROP_WTG_VALUE + ";choice(" + USE_REQUEST_ATTR + "," + USE_FIXED + ");" +
"Get value from a request attribute or use a fixed value specified below.";
protected static final String VALUE_INFO =
PROP_VALUE + ";string;" +
"Request attribute name or a fixed value to put into the extension.";
public AttributeConfig(String name, IConfigStore config, boolean enabled)
throws EBaseException {
X500NameAttrMap map = X500NameAttrMap.getDefault();
mName = name;
mConfig = config;
if (enabled) {
mAttributeName = mConfig.getString(PROP_ATTRIBUTE_NAME);
mWhereToGetValue = mConfig.getString(PROP_WTG_VALUE);
mValue = mConfig.getString(PROP_VALUE);
} else {
mAttributeName = mConfig.getString(PROP_ATTRIBUTE_NAME, "");
mWhereToGetValue = mConfig.getString(PROP_WTG_VALUE, USE_REQUEST_ATTR);
mValue = mConfig.getString(PROP_VALUE, "");
}
if (mAttributeName.length() > 0) {
mAttributeOID = map.getOid(mAttributeName);
if (mAttributeOID == null)
throw new EBaseException(
CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", mAttributeName));
}
if (mWhereToGetValue.equalsIgnoreCase(USE_REQUEST_ATTR)) {
mWhereToGetValue = USE_REQUEST_ATTR;
if (enabled && mValue.length() == 0) {
throw new EBaseException(CMS.getUserMessage("CMS_BASE_GET_PROPERTY_FAILED", PROP_VALUE));
}
int dot = mValue.indexOf('.');
if (dot != -1) {
mPrefix = mValue.substring(0, dot);
mReqAttr = mValue.substring(dot + 1);
if (mPrefix == null || mPrefix.length() == 0 ||
mReqAttr == null || mReqAttr.length() == 0) {
throw new EBaseException(
CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", mValue));
}
} else {
mPrefix = null;
mReqAttr = mValue;
}
} else if (mWhereToGetValue.equalsIgnoreCase(USE_FIXED)) {
mWhereToGetValue = USE_FIXED;
if (mAttributeOID != null) {
try {
checkValue(mAttributeOID, mValue);
mAttribute = new Attribute(mAttributeOID, mValue);
} catch (Exception e) {
throw new EBaseException(
CMS.getUserMessage("CMS_BASE_INVALID_ATTR_VALUE",
mAttributeName, e.getMessage()));
}
}
} else if (enabled || mWhereToGetValue.length() > 0) {
throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_VALUE_FOR_TYPE", PROP_WTG_VALUE,
"Must be either '" + USE_REQUEST_ATTR + "' or '" + USE_FIXED + "'."));
}
}
public static void getDefaultParams(String name, Vector v) {
String nameDot = name + ".";
v.addElement(nameDot + PROP_ATTRIBUTE_NAME + "=");
v.addElement(nameDot + PROP_WTG_VALUE + "=" + USE_REQUEST_ATTR);
v.addElement(nameDot + PROP_VALUE + "=");
}
public static void getExtendedPluginInfo(String name, Vector v) {
String nameDot = name + ".";
String attrChoices = getAllNames();
v.addElement(nameDot + PROP_ATTRIBUTE_NAME + ";choice(" + attrChoices + ");" +
ATTRIBUTE_NAME_INFO);
v.addElement(nameDot + WTG_VALUE_INFO);
v.addElement(nameDot + VALUE_INFO);
}
public void getInstanceParams(Vector v) {
String nameDot = mName + ".";
v.addElement(nameDot + PROP_ATTRIBUTE_NAME + "=" + mAttributeName);
v.addElement(nameDot + PROP_WTG_VALUE + "=" + mWhereToGetValue);
v.addElement(nameDot + PROP_VALUE + "=" + mValue);
}
public Attribute formAttr(IRequest req)
throws IOException {
String val = req.getExtDataInString(mPrefix, mReqAttr);
if (val == null || val.length() == 0) {
return null;
}
checkValue(mAttributeOID, val);
return new Attribute(mAttributeOID, val);
}
static private String getAllNames() {
Enumeration n = X500NameAttrMap.getDefault().getAllNames();
StringBuffer sb = new StringBuffer();
sb.append(n.nextElement());
while (n.hasMoreElements()) {
sb.append(",");
sb.append(n.nextElement());
}
return sb.toString();
}
private static void checkValue(ObjectIdentifier oid, String val)
throws IOException {
AVAValueConverter c = X500NameAttrMap.getDefault().getValueConverter(oid);
@SuppressWarnings("unused")
DerValue derval = c.getValue(val); // check for errors
return;
}
}