From 621d9e5c413e561293d7484b93882d985b3fe15f Mon Sep 17 00:00:00 2001 From: Endi Sukma Dewata Date: Sat, 24 Mar 2012 02:27:47 -0500 Subject: Removed unnecessary pki folder. Previously the source code was located inside a pki folder. This folder was created during svn migration and is no longer needed. This folder has now been removed and the contents have been moved up one level. Ticket #131 --- .../netscape/cms/publish/mappers/AVAPattern.java | 594 +++++++++++++++++++ .../cms/publish/mappers/LdapCaSimpleMap.java | 372 ++++++++++++ .../cms/publish/mappers/LdapCertCompsMap.java | 178 ++++++ .../cms/publish/mappers/LdapCertExactMap.java | 199 +++++++ .../cms/publish/mappers/LdapCertSubjMap.java | 343 +++++++++++ .../cms/publish/mappers/LdapCrlIssuerCompsMap.java | 156 +++++ .../cms/publish/mappers/LdapDNCompsMap.java | 457 +++++++++++++++ .../cms/publish/mappers/LdapEnhancedMap.java | 640 ++++++++++++++++++++ .../cms/publish/mappers/LdapSimpleMap.java | 332 +++++++++++ .../cms/publish/mappers/MapAVAPattern.java | 652 +++++++++++++++++++++ .../netscape/cms/publish/mappers/MapDNPattern.java | 201 +++++++ .../cms/publish/mappers/MapRDNPattern.java | 217 +++++++ .../com/netscape/cms/publish/mappers/NoMap.java | 104 ++++ .../cms/publish/publishers/FileBasedPublisher.java | 443 ++++++++++++++ .../publish/publishers/LdapCaCertPublisher.java | 421 +++++++++++++ .../publish/publishers/LdapCertSubjPublisher.java | 345 +++++++++++ .../publishers/LdapCertificatePairPublisher.java | 318 ++++++++++ .../cms/publish/publishers/LdapCrlPublisher.java | 379 ++++++++++++ .../publishers/LdapEncryptCertPublisher.java | 359 ++++++++++++ .../publish/publishers/LdapUserCertPublisher.java | 333 +++++++++++ .../cms/publish/publishers/OCSPPublisher.java | 355 +++++++++++ .../cms/publish/publishers/PublisherUtils.java | 136 +++++ 22 files changed, 7534 insertions(+) create mode 100644 base/common/src/com/netscape/cms/publish/mappers/AVAPattern.java create mode 100644 base/common/src/com/netscape/cms/publish/mappers/LdapCaSimpleMap.java create mode 100644 base/common/src/com/netscape/cms/publish/mappers/LdapCertCompsMap.java create mode 100644 base/common/src/com/netscape/cms/publish/mappers/LdapCertExactMap.java create mode 100644 base/common/src/com/netscape/cms/publish/mappers/LdapCertSubjMap.java create mode 100644 base/common/src/com/netscape/cms/publish/mappers/LdapCrlIssuerCompsMap.java create mode 100644 base/common/src/com/netscape/cms/publish/mappers/LdapDNCompsMap.java create mode 100644 base/common/src/com/netscape/cms/publish/mappers/LdapEnhancedMap.java create mode 100644 base/common/src/com/netscape/cms/publish/mappers/LdapSimpleMap.java create mode 100644 base/common/src/com/netscape/cms/publish/mappers/MapAVAPattern.java create mode 100644 base/common/src/com/netscape/cms/publish/mappers/MapDNPattern.java create mode 100644 base/common/src/com/netscape/cms/publish/mappers/MapRDNPattern.java create mode 100644 base/common/src/com/netscape/cms/publish/mappers/NoMap.java create mode 100644 base/common/src/com/netscape/cms/publish/publishers/FileBasedPublisher.java create mode 100644 base/common/src/com/netscape/cms/publish/publishers/LdapCaCertPublisher.java create mode 100644 base/common/src/com/netscape/cms/publish/publishers/LdapCertSubjPublisher.java create mode 100644 base/common/src/com/netscape/cms/publish/publishers/LdapCertificatePairPublisher.java create mode 100644 base/common/src/com/netscape/cms/publish/publishers/LdapCrlPublisher.java create mode 100644 base/common/src/com/netscape/cms/publish/publishers/LdapEncryptCertPublisher.java create mode 100644 base/common/src/com/netscape/cms/publish/publishers/LdapUserCertPublisher.java create mode 100644 base/common/src/com/netscape/cms/publish/publishers/OCSPPublisher.java create mode 100644 base/common/src/com/netscape/cms/publish/publishers/PublisherUtils.java (limited to 'base/common/src/com/netscape/cms/publish') diff --git a/base/common/src/com/netscape/cms/publish/mappers/AVAPattern.java b/base/common/src/com/netscape/cms/publish/mappers/AVAPattern.java new file mode 100644 index 000000000..7f70722d0 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/AVAPattern.java @@ -0,0 +1,594 @@ +// --- 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 statement // +/////////////////////// + +package com.netscape.cms.publish.mappers; + +/////////////////////// +// import statements // +/////////////////////// + +/* cert server imports */ +import java.io.IOException; +import java.io.PushbackReader; +import java.io.StringReader; +import java.util.Enumeration; +import java.util.StringTokenizer; +import java.util.Vector; + +import netscape.ldap.LDAPDN; +import netscape.security.x509.CertificateExtensions; +import netscape.security.x509.Extension; +import netscape.security.x509.GeneralName; +import netscape.security.x509.GeneralNameInterface; +import netscape.security.x509.GeneralNames; +import netscape.security.x509.LdapV3DNStrConverter; +import netscape.security.x509.OIDMap; +import netscape.security.x509.SubjectAlternativeNameExtension; +import netscape.security.x509.X500Name; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.publish.ECompSyntaxErr; +import com.netscape.certsrv.request.IRequest; + +////////////////////// +// class definition // +////////////////////// + +/** + * avaPattern is a string representing an ldap + * attribute formulated from the certificate + * subject name, extension or request attributes. + *

+ * + * The syntax is + * + *

+ *     avaPattern := constant-value | 
+ *                   "$subj" "." attrName [ "." attrNumber ] | 
+ *                   "$req" "." [ prefix .] attrName [ "." attrNumber ] | 
+ *                   "$ext" "." extName [ "." nameType ] [ "." attrNumber ]
+ * 
+ * + *
+ * Example: $ext.SubjectAlternativeName.RFC822Name.1
+ * cert subjectAltName is rfc822Name: jjames@mcom.com
+ * 

+ * The ldap attribute formulated will be :
+ * jjames@mcom.com + *

+ * The first rfc822name value in the subjAltName extension.
+ *

+ *

+ * + * If a request attribute or subject DN component does not exist, the attribute is skipped. + * + * @version $Revision$, $Date$ + */ +class AVAPattern { + //////////////// + // parameters // + //////////////// + + /* the value type of the dn component */ + public static final String TYPE_REQ = "$req"; + public static final String TYPE_SUBJ = "$subj"; + public static final String TYPE_EXT = "$ext"; + public static final String TYPE_CONSTANT = "constant"; + + public static final String[] GENERAL_NAME_TYPE = { "ANY", + "RFC822Name", + "DNSName", + "X400Name", + "DIRECTORYName", + "EDIName", + "URIName", + "IPAddress", + "OIDName" }; + + private static final char[] endChars = new char[] { '+', ',' }; + + private static final LdapV3DNStrConverter mLdapDNStrConverter = + new LdapV3DNStrConverter(); + + /* the list of request attributes needed by this AVA */ + protected String[] mReqAttrs = null; + + /* the list of cert attributes needed by this AVA*/ + protected String[] mCertAttrs = null; + + /* value type */ + protected String mType = null; + + /* value - could be name of a request attribute or + * cert subject attribute or extension name. + */ + protected String mValue = null; + + /* value type - general name type of an + * extension attribute if any. + */ + protected String mGNType = null; + + /* prefix - prefix of a request attribute if any. */ + protected String mPrefix = null; + + /* nth value of the ldap or dn attribute */ + protected int mElement = 0; + + protected String mTestDN = null; + + ///////////// + // methods // + ///////////// + + public AVAPattern(String component) + throws ELdapException { + if (component == null || component.length() == 0) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", component)); + } + + parse(new PushbackReader(new StringReader(component))); + } + + public AVAPattern(PushbackReader in) + throws ELdapException { + parse(in); + } + + private void parse(PushbackReader in) + throws ELdapException { + int c; + + // skip spaces + //System.out.println("============ AVAPattern Begin ==========="); + //System.out.println("skip spaces"); + + try { + while ((c = in.read()) == ' ' || c == '\t') {//System.out.println("spaces read "+(char)c); + ; + } + } catch (IOException e) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", "All blank")); + } + + if (c == -1) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", "All blank")); + } + + if (c == '$') { + // check for $subj $ext or $req + try { + c = in.read(); + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + + if (c == -1) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "expecting $subj $ext or $req in ava pattern")); + } + + if (c == 'r') { + try { + if (in.read() != 'e' || + in.read() != 'q' || + in.read() != '.') { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "expecting $req in ava pattern")); + } + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + + mType = TYPE_REQ; + //System.out.println("---- mtype $req"); + } else if (c == 's') { + try { + if (in.read() != 'u' || + in.read() != 'b' || + in.read() != 'j' || + in.read() != '.') { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "expecting $subj in ava pattern")); + } + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + + mType = TYPE_SUBJ; + //System.out.println("----- mtype $subj"); + } else if (c == 'e') { + try { + if (in.read() != 'x' || + in.read() != 't' || + in.read() != '.') { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "expecting $ext in ava pattern")); + } + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + + mType = TYPE_EXT; + //System.out.println("----- mtype $ext"); + } else { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "unknown keyword. expecting $subj $ext or $req.")); + } + + // get request attribute or + // cert subject or + // extension attribute + + StringBuffer valueBuf = new StringBuffer(); + + try { + while ((c = in.read()) != ',' && + c != -1 && c != '.' && c != '+') { + //System.out.println("mValue read "+(char)c); + valueBuf.append((char) c); + } + + if (c == '+' || c == ',') { // either ',' or '+' + in.unread(c); // pushback last , or + + } + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + + mValue = valueBuf.toString().trim(); + if (mValue.length() == 0) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "$subj $ext or $req attribute name expected")); + } + //System.out.println("----- mValue "+mValue); + + // get nth dn xxx not nth request attribute . + if (c == '.') { + StringBuffer attrNumberBuf = new StringBuffer(); + + try { + while ((c = in.read()) != ',' && c != -1 && c != '.' + && c != '+') { + //System.out.println("mElement read "+(char)c); + attrNumberBuf.append((char) c); + } + + if (c == ',' || c == '+') { // either ',' or '+' + in.unread(c); // pushback last , or + + } + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + + String attrNumber = attrNumberBuf.toString().trim(); + + if (attrNumber.length() == 0) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "nth element $req $ext or $subj expected")); + } + + try { + mElement = Integer.parseInt(attrNumber) - 1; + } catch (NumberFormatException e) { + + if (TYPE_REQ.equals(mType)) { + mPrefix = mValue; + mValue = attrNumber; + } else if (TYPE_EXT.equals(mType)) { + mGNType = attrNumber; + } else { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "Invalid format in nth element " + + "$req $ext or $subj")); + } + + // get nth request attribute . + if (c == '.') { + StringBuffer attrNumberBuf1 = new StringBuffer(); + + try { + while ((c = in.read()) != ',' && + c != -1 && c != '+') { + //System.out.println("mElement read "+ + // (char)c); + attrNumberBuf1.append((char) c); + } + + if (c != -1) { // either ',' or '+' + in.unread(c); // pushback last , or + + } + } catch (IOException ex) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", ex.toString())); + } + + String attrNumber1 = + attrNumberBuf1.toString().trim(); + + if (attrNumber1.length() == 0) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "nth element $req or $ext expected")); + } + + try { + mElement = Integer.parseInt(attrNumber1) - 1; + } catch (NumberFormatException ex) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "Invalid format in nth element " + + "$req or $ext.")); + } + } + } + } + //System.out.println("----- mElement "+mElement); + } else { + // value is constant. treat as regular ava. + mType = TYPE_CONSTANT; + + // parse ava value. + StringBuffer valueBuf = new StringBuffer(); + + valueBuf.append((char) c); + + // read forward to get attribute value + try { + while ((c = in.read()) != ',' && c != -1) { + valueBuf.append((char) c); + } + + if (c == '+' || c == ',') { // either ',' or '+' + in.unread(c); // pushback last , or + + } + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + + mValue = valueBuf.toString().trim(); + + /* try { + * AVA ava = mLdapDNStrConverter.parseAVA( + * valueBuf.toString()); + * mValue = ava.toLdapDNString(); + * //System.out.println("----- mValue "+mValue); + * } catch (IOException e) { + * throw new ECompSyntaxErr(e.toString()); + * } + */ + } + } + + public String formAVA(IRequest req, + X500Name subject, + CertificateExtensions extensions) + throws ELdapException { + if (TYPE_CONSTANT.equals(mType)) { + return mValue; + } + + if (TYPE_SUBJ.equals(mType)) { + String dn = subject.toString(); + + if (mTestDN != null) { + dn = mTestDN; + } + + //System.out.println("AVAPattern Using dn "+mTestDN); + String[] rdns = LDAPDN.explodeDN(dn, false); + + String value = null; + + int nFound = -1; + + for (int i = 0; i < rdns.length; i++) { + String[] avas = explodeRDN(rdns[i]); + + for (int j = 0; j < avas.length; j++) { + String[] exploded = explodeAVA(avas[j]); + + if (exploded[0].equalsIgnoreCase(mValue) && + ++nFound == mElement) { + value = exploded[1]; + break; + } + } + } + + if (value == null) { + return null; + } + + return value; + } + + if (TYPE_EXT.equals(mType)) { + + if (extensions != null) { + + for (int i = 0; i < extensions.size(); i++) { + Extension ext = (Extension) + extensions.elementAt(i); + + String extName = + OIDMap.getName(ext.getExtensionId()); + + int index = extName.lastIndexOf("."); + + if (index != -1) { + extName = extName.substring(index + 1); + } + + if (extName.equals(mValue)) { + // Check the extensions one by one. + // For now, just give subjectAltName + // as an example. + if (mValue.equalsIgnoreCase( + SubjectAlternativeNameExtension.NAME)) { + try { + GeneralNames subjectNames = (GeneralNames) + ((SubjectAlternativeNameExtension) + ext).get( + SubjectAlternativeNameExtension.SUBJECT_NAME); + + if (subjectNames.size() == 0) { + break; + } + + int j = 0; + + for (Enumeration n = + subjectNames.elements(); n.hasMoreElements();) { + + GeneralName gn = (GeneralName) + n.nextElement(); + + String gname = gn.toString(); + + index = gname.indexOf(":"); + + if (index == -1) { + break; + } + + String gType = + gname.substring(0, index); + + if (mGNType != null) { + if (mGNType.equalsIgnoreCase(gType)) { + if (mElement == j) { + gname = + gname.substring(index + 2); + return gname; + } else { + j++; + } + } + } else { + if (mElement == j) { + gname = + gname.substring(index + 2); + return gname; + } + j++; + } + } + } catch (IOException e) { + CMS.debug( + "AVAPattern: Publishing attr not formed " + + "from extension " + + "-- no attr : " + + mValue); + } + } + } + } + } + + CMS.debug( + "AVAPattern: Publishing:attr not formed " + + "from extension " + + "-- no attr : " + + mValue); + + return null; + } + + if (TYPE_REQ.equals(mType)) { + // mPrefix and mValue are looked up case-insensitive + String reqAttr = req.getExtDataInString(mPrefix, mValue); + if (reqAttr == null) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_NO_REQUEST", mValue, "")); + } + + return reqAttr; + } + + return null; + } + + public String getReqAttr() { + if (TYPE_REQ.equals(mType)) { + return mValue; + } else { + return null; + } + } + + public String getCertAttr() { + if (TYPE_SUBJ.equals(mType)) { + return mValue; + } else { + return null; + } + } + + /** + * Explode RDN into AVAs. + * Does not handle escaped '+' + * Java ldap library does not yet support multiple avas per rdn. + * If RDN is malformed returns empty array. + */ + public static String[] explodeRDN(String rdn) { + int plus = rdn.indexOf('+'); + + if (plus == -1) { + return new String[] { rdn }; + } + + Vector avas = new Vector(); + + StringTokenizer token = new StringTokenizer(rdn, "+"); + + while (token.hasMoreTokens()) { + avas.addElement(token.nextToken()); + } + + String[] theAvas = new String[avas.size()]; + + avas.copyInto(theAvas); + + return theAvas; + } + + /** + * Explode AVA into name and value. + * Does not handle escaped '=' + * If AVA is malformed empty array is returned. + */ + public static String[] explodeAVA(String ava) { + int equals = ava.indexOf('='); + + if (equals == -1) { + return null; + } + + return new String[] { ava.substring(0, equals).trim(), + ava.substring(equals + 1).trim() }; + } +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/LdapCaSimpleMap.java b/base/common/src/com/netscape/cms/publish/mappers/LdapCaSimpleMap.java new file mode 100644 index 000000000..bbf641540 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/LdapCaSimpleMap.java @@ -0,0 +1,372 @@ +// --- 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.publish.mappers; + +import java.io.IOException; +import java.security.cert.X509Certificate; +import java.util.Locale; +import java.util.Vector; + +import netscape.ldap.LDAPAttribute; +import netscape.ldap.LDAPAttributeSet; +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPSearchResults; +import netscape.ldap.LDAPv2; +import netscape.ldap.LDAPv3; +import netscape.ldap.util.DN; +import netscape.security.x509.CertificateExtensions; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CRLImpl; +import netscape.security.x509.X509CertImpl; +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.ldap.ELdapException; +import com.netscape.certsrv.ldap.ELdapServerDownException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapMapper; +import com.netscape.certsrv.request.IRequest; + +/** + * Maps a request to an entry in the LDAP server. + * Takes a dnPattern to form the baseDN from the request attributes + * and certificate subject name.Do a base search for the entry + * in the directory to publish the cert or crl. + * The restriction of this mapper is that the ldap dn components must + * be part of certificate subject name or request attributes or constant. + * + * @version $Revision$, $Date$ + */ +public class LdapCaSimpleMap implements ILdapMapper, IExtendedPluginInfo { + protected static final String PROP_DNPATTERN = "dnPattern"; + protected static final String PROP_CREATECA = "createCAEntry"; + protected String mDnPattern = null; + protected boolean mCreateCAEntry = true; + + private ILogger mLogger = CMS.getLogger(); + private boolean mInited = false; + protected IConfigStore mConfig = null; + + /* the subject DN pattern */ + protected MapDNPattern mPattern = null; + + /* the list of request attriubutes to retrieve*/ + protected String[] mReqAttrs = null; + + /* the list of cert attriubutes to retrieve*/ + protected String[] mCertAttrs = null; + + /* default dn pattern if left blank or not set in the config */ + public static final String DEFAULT_DNPATTERN = + "UID=$req.HTTP_PARAMS.UID, OU=people, O=$subj.o, C=$subj.c"; + + /** + * Constructor. + * + * @param dnPattern The base DN. + */ + public LdapCaSimpleMap(String dnPattern) { + try { + init(dnPattern); + } catch (EBaseException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("OPERATION_ERROR", e.toString())); + } + + } + + /** + * constructor if initializing from config store. + */ + public LdapCaSimpleMap() { + } + + public String[] getExtendedPluginInfo(Locale locale) { + String params[] = { + "dnPattern;string;Describes how to form the Ldap Subject name in" + + " the directory. Example 1: 'uid=CertMgr, o=Fedora'. Example 2:" + + " 'uid=$req.HTTP_PARAMS.uid, E=$ext.SubjectAlternativeName.RFC822Name, ou=$subj.ou'. " + + "$req means: take the attribute from the request. " + + "$subj means: take the attribute from the certificate subject name. " + + "$ext means: take the attribute from the certificate extension", + "createCAEntry;boolean;If checked, CA entry will be created automatically", + IExtendedPluginInfo.HELP_TOKEN + ";configuration-ldappublish-mapper-casimplemapper", + IExtendedPluginInfo.HELP_TEXT + ";Describes how to form the LDAP DN of the entry to publish to" + }; + + return params; + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + /** + * for initializing from config store. + */ + public void init(IConfigStore config) + throws EBaseException { + mConfig = config; + String dnPattern = mConfig.getString(PROP_DNPATTERN); + + mCreateCAEntry = mConfig.getBoolean(PROP_CREATECA, true); + init(dnPattern); + } + + /** + * common initialization routine. + */ + protected void init(String dnPattern) + throws EBaseException { + if (mInited) + return; + + mDnPattern = dnPattern; + if (mDnPattern == null || mDnPattern.length() == 0) + mDnPattern = DEFAULT_DNPATTERN; + try { + mPattern = new MapDNPattern(mDnPattern); + } catch (ELdapException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_DN_PATTERN_INIT", dnPattern, e.toString())); + throw new EBaseException("falied to init with pattern " + + dnPattern + " " + e); + } + + mInited = true; + } + + /** + * Maps a X500 subject name to LDAP entry. + * Uses DN pattern to form a DN for a LDAP base search. + * + * @param conn the LDAP connection. + * @param obj the object to map. + * @exception ELdapException if any LDAP exceptions occured. + */ + public String map(LDAPConnection conn, Object obj) + throws ELdapException { + return map(conn, null, obj); + } + + /** + * Maps a X500 subject name to LDAP entry. + * Uses DN pattern to form a DN for a LDAP base search. + * + * @param conn the LDAP connection. + * @param req the request to map. + * @param obj the object to map. + * @exception ELdapException if any LDAP exceptions occured. + */ + public String map(LDAPConnection conn, IRequest req, Object obj) + throws ELdapException { + if (conn == null) + return null; + String dn = null; + + try { + dn = formDN(req, obj); + if (dn == null) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_DN_NOT_FORMED")); + String s1 = ""; + + if (req != null) + s1 = req.getRequestId().toString(); + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_NO_DN_MATCH", s1)); + } + int scope = LDAPv2.SCOPE_BASE; + String filter = "(objectclass=*)"; + + // search for entry + String[] attrs = new String[] { LDAPv3.NO_ATTRS }; + + log(ILogger.LL_INFO, "searching for dn: " + dn + " filter:" + + filter + " scope: base"); + + LDAPSearchResults results = + conn.search(dn, scope, filter, attrs, false); + LDAPEntry entry = results.next(); + + if (results.hasMoreElements()) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_MORE_THAN_ONE_ENTRY", dn, + ((req == null) ? "" : req.getRequestId().toString()))); + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_MORE_THAN_ONE_ENTRY", + ((req == null) ? "" : req.getRequestId().toString()))); + } + if (entry != null) + return entry.getDN(); + else { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_ENTRY_NOT_FOUND", dn, + ((req == null) ? "" : req.getRequestId().toString()))); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", + "null entry")); + } + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else if (e.getLDAPResultCode() == LDAPException.NO_SUCH_OBJECT && mCreateCAEntry) { + try { + createCAEntry(conn, dn); + log(ILogger.LL_INFO, "CA Entry " + dn + " Created"); + return dn; + } catch (LDAPException e1) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_DN_MAP_EXCEPTION", dn, e1.toString())); + if (e1.getLDAPResultCode() == LDAPException.CONSTRAINT_VIOLATION) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CA_ENTRY_NOT_CREATED")); + } else { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CA_ENTRY_NOT_CREATED1")); + } + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_CREATE_CA_FAILED", dn)); + } + } else { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_DN_MAP_EXCEPTION", dn, e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", e.toString())); + } + } catch (EBaseException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_EXCEPTION_CAUGHT", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", e.toString())); + } + } + + private void createCAEntry(LDAPConnection conn, String dn) + throws LDAPException { + LDAPAttributeSet attrs = new LDAPAttributeSet(); + // OID 2.5.6.16 + String caOc[] = new String[] { "top", + "person", + "organizationalPerson", + "inetOrgPerson" }; + + DN dnobj = new DN(dn); + String attrval[] = dnobj.explodeDN(true); + + attrs.add(new LDAPAttribute("cn", attrval[0])); + attrs.add(new LDAPAttribute("sn", attrval[0])); + attrs.add(new LDAPAttribute("objectclass", caOc)); + LDAPEntry entry = new LDAPEntry(dn, attrs); + + conn.add(entry); + } + + /** + * form a dn from component in the request and cert subject name + * + * @param req The request + * @param obj The certificate or crl + */ + private String formDN(IRequest req, Object obj) throws EBaseException { + X500Name subjectDN = null; + CertificateExtensions certExt = null; + + try { + X509Certificate cert = (X509Certificate) obj; + + subjectDN = + (X500Name) ((X509Certificate) cert).getSubjectDN(); + + CMS.debug("LdapCaSimpleMap: cert subject dn:" + subjectDN.toString()); + X509CertInfo info = (X509CertInfo) + ((X509CertImpl) cert).get( + X509CertImpl.NAME + "." + X509CertImpl.INFO); + + certExt = (CertificateExtensions) info.get( + CertificateExtensions.NAME); + } catch (java.security.cert.CertificateParsingException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CANT_GET_EXT", e.toString())); + } catch (IOException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CANT_GET_EXT", e.toString())); + } catch (java.security.cert.CertificateException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CANT_GET_EXT", e.toString())); + } catch (ClassCastException e) { + try { + X509CRLImpl crl = (X509CRLImpl) obj; + + subjectDN = + (X500Name) ((X509CRLImpl) crl).getIssuerDN(); + + CMS.debug("LdapCaSimpleMap: crl issuer dn: " + + subjectDN.toString()); + } catch (ClassCastException ex) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_PUBLISH_OBJ_NOT_SUPPORTED", + ((req == null) ? "" : req.getRequestId().toString()))); + return null; + } + } + try { + String dn = mPattern.formDN(req, subjectDN, certExt); + + return dn; + } catch (ELdapException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_FORM_DN", + ((req == null) ? "" : req.getRequestId().toString()), e.toString())); + throw new EBaseException("falied to form dn for request: " + + ((req == null) ? "" : req.getRequestId().toString()) + " " + e); + } + } + + public String getImplName() { + return "LdapCaSimpleMap"; + } + + public String getDescription() { + return "LdapCaSimpleMap"; + } + + public Vector getDefaultParams() { + Vector v = new Vector(); + + v.addElement(PROP_DNPATTERN + "="); + v.addElement(PROP_CREATECA + "=true"); + return v; + } + + public Vector getInstanceParams() { + Vector v = new Vector(); + + try { + if (mDnPattern == null) { + v.addElement(PROP_DNPATTERN + "="); + } else { + v.addElement(PROP_DNPATTERN + "=" + + mConfig.getString(PROP_DNPATTERN)); + } + v.addElement(PROP_CREATECA + "=" + mConfig.getBoolean(PROP_CREATECA, true)); + } catch (Exception e) { + } + return v; + } + + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapCaSimpleMapper: " + msg); + } + +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/LdapCertCompsMap.java b/base/common/src/com/netscape/cms/publish/mappers/LdapCertCompsMap.java new file mode 100644 index 000000000..2373e3c66 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/LdapCertCompsMap.java @@ -0,0 +1,178 @@ +// --- 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.publish.mappers; + +import java.security.cert.CRLException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.Vector; + +import netscape.ldap.LDAPConnection; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CRLImpl; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapMapper; +import com.netscape.certsrv.request.IRequest; + +/** + * Maps a X509 certificate to a LDAP entry using AVAs in the certificate's + * subject name to form the ldap search dn and filter. + * Takes a optional root search dn. + * The DN comps are used to form a LDAP entry to begin a subtree search. + * The filter comps are used to form a search filter for the subtree. + * If none of the DN comps matched, baseDN is used for the subtree. + * If the baseDN is null and none of the DN comps matched, it is an error. + * If none of the DN comps and filter comps matched, it is an error. + * If just the filter comps is null, a base search is performed. + * + * @version $Revision$, $Date$ + */ +public class LdapCertCompsMap + extends LdapDNCompsMap implements ILdapMapper { + ILogger mLogger = CMS.getLogger(); + + public LdapCertCompsMap() { + // need to support baseDN, dnComps, and filterComps + // via configuration + } + + /** + * Constructor. + * + * The DN comps are used to form a LDAP entry to begin a subtree search. + * The filter comps are used to form a search filter for the subtree. + * If none of the DN comps matched, baseDN is used for the subtree. + * If the baseDN is null and none of the DN comps matched, it is an error. + * If none of the DN comps and filter comps matched, it is an error. + * If just the filter comps is null, a base search is performed. + * + * @param baseDN The base DN. + * @param dnComps Components to form the LDAP base dn for search. + * @param filterComps Components to form the LDAP search filter. + */ + public LdapCertCompsMap(String baseDN, ObjectIdentifier[] dnComps, + ObjectIdentifier[] filterComps) { + init(baseDN, dnComps, filterComps); + } + + public String getImplName() { + return "LdapCertCompsMap"; + } + + public String getDescription() { + return "LdapCertCompsMap"; + } + + public Vector getDefaultParams() { + Vector v = super.getDefaultParams(); + + return v; + } + + public Vector getInstanceParams() { + Vector v = super.getInstanceParams(); + + return v; + } + + /** + * constructor using non-standard certificate attribute. + */ + public LdapCertCompsMap(String certAttr, String baseDN, + ObjectIdentifier[] dnComps, + ObjectIdentifier[] filterComps) { + super(certAttr, baseDN, dnComps, filterComps); + } + + protected void init(String baseDN, ObjectIdentifier[] dnComps, + ObjectIdentifier[] filterComps) { + super.init(baseDN, dnComps, filterComps); + } + + /** + * Maps a certificate to LDAP entry. + * Uses DN components and filter components to form a DN and + * filter for a LDAP search. + * If the formed DN is null the baseDN will be used. + * If the formed DN is null and baseDN is null an error is thrown. + * If the filter is null a base search is performed. + * If both are null an error is thrown. + * + * @param conn - the LDAP connection. + * @param obj - the X509Certificate. + */ + public String + map(LDAPConnection conn, Object obj) + throws ELdapException { + if (conn == null) + return null; + try { + X509Certificate cert = (X509Certificate) obj; + String result = null; + // form dn and filter for search. + X500Name subjectDN = + (X500Name) ((X509Certificate) cert).getSubjectDN(); + + CMS.debug("LdapCertCompsMap: " + subjectDN.toString()); + + byte[] certbytes = cert.getEncoded(); + + result = super.map(conn, subjectDN, certbytes); + return result; + } catch (CertificateEncodingException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CANT_DECODE_CERT", e.toString())); + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_GET_DER_ENCODED_CERT_FAILED", e.toString())); + } catch (ClassCastException e) { + try { + X509CRLImpl crl = (X509CRLImpl) obj; + String result = null; + X500Name issuerDN = + (X500Name) ((X509CRLImpl) crl).getIssuerDN(); + + CMS.debug("LdapCertCompsMap: " + issuerDN.toString()); + + byte[] crlbytes = crl.getEncoded(); + + result = super.map(conn, issuerDN, crlbytes); + return result; + } catch (CRLException ex) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CANT_DECODE_CRL", ex.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_GET_DER_ENCODED_CRL_FAILED", ex.toString())); + } catch (ClassCastException ex) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_NOT_SUPPORTED_OBJECT")); + return null; + } + } + } + + public String map(LDAPConnection conn, IRequest req, Object obj) + throws ELdapException { + return map(conn, obj); + } + + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapCertCompsMap: " + msg); + } + +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/LdapCertExactMap.java b/base/common/src/com/netscape/cms/publish/mappers/LdapCertExactMap.java new file mode 100644 index 000000000..11b53a797 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/LdapCertExactMap.java @@ -0,0 +1,199 @@ +// --- 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.publish.mappers; + +import java.security.cert.X509Certificate; +import java.util.Locale; +import java.util.Vector; + +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPSearchResults; +import netscape.ldap.LDAPv2; +import netscape.ldap.LDAPv3; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CRLImpl; + +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.ldap.ELdapException; +import com.netscape.certsrv.ldap.ELdapServerDownException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapMapper; +import com.netscape.certsrv.request.IRequest; + +/** + * Maps a X509 certificate to a LDAP entry by using the subject name + * of the certificate as the LDAP entry DN. + * + * @version $Revision$, $Date$ + */ +public class LdapCertExactMap implements ILdapMapper, IExtendedPluginInfo { + private ILogger mLogger = CMS.getLogger(); + protected IConfigStore mConfig = null; + boolean mInited = false; + + /** + * constructs a certificate subject name mapper with search base. + */ + public LdapCertExactMap() { + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + public void init(IConfigStore config) + throws EBaseException { + if (mInited == true) + return; + mConfig = config; + mInited = true; + } + + public String[] getExtendedPluginInfo(Locale locale) { + String[] params = { + IExtendedPluginInfo.HELP_TOKEN + + ";configuration-ldappublish-mapper-certexactmapper", + IExtendedPluginInfo.HELP_TEXT + + ";Literally uses the subject name of the certificate as the DN to publish to" + }; + + return params; + } + + public String getImplName() { + return "LdapCertExactMap"; + } + + public String getDescription() { + return "LdapCertExactMap"; + } + + public Vector getDefaultParams() { + Vector v = new Vector(); + + return v; + } + + public Vector getInstanceParams() { + Vector v = new Vector(); + + return v; + } + + /** + * Finds the entry for the certificate by looking for the cert + * subject name in the subject name attribute. + * + * @param conn - the LDAP connection. + * @param obj - the X509Certificate. + */ + public String + map(LDAPConnection conn, Object obj) + throws ELdapException { + if (conn == null) + return null; + + X500Name subjectDN = null; + + try { + X509Certificate cert = (X509Certificate) obj; + + subjectDN = + (X500Name) ((X509Certificate) cert).getSubjectDN(); + + CMS.debug("LdapCertExactMap: cert subject dn:" + subjectDN.toString()); + } catch (ClassCastException e) { + try { + X509CRLImpl crl = (X509CRLImpl) obj; + + subjectDN = + (X500Name) ((X509CRLImpl) crl).getIssuerDN(); + + CMS.debug("LdapCertExactMap: crl issuer dn: " + + subjectDN.toString()); + } catch (ClassCastException ex) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_NOT_SUPPORTED_OBJECT")); + return null; + } + } + try { + String[] attrs = new String[] { LDAPv3.NO_ATTRS }; + + log(ILogger.LL_INFO, "Searching for " + subjectDN.toString()); + + LDAPSearchResults results = + conn.search(subjectDN.toString(), LDAPv2.SCOPE_BASE, + "(objectclass=*)", attrs, false); + + LDAPEntry entry = results.next(); + + if (results.hasMoreElements()) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_MORE_THAN_ONE_ENTRY", "", subjectDN.toString())); + } + if (entry != null) { + log(ILogger.LL_INFO, "entry found"); + return entry.getDN(); + } + return null; + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_DN_MAP_EXCEPTION", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", e.toString())); + } + } + + /* + catch (IOException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_GET_SUBJECT", e.toString())); + throw new ELdapException( + LdapResources.GET_CERT_SUBJECT_DN_FAILED, e); + } + catch (CertificateEncodingException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_DECODE_CERT", e.toString())); + throw new ELdapException( + LdapResources.GET_DER_ENCODED_CERT_FAILED, e); + } + */ + } + + public String map(LDAPConnection conn, IRequest req, Object obj) + throws ELdapException { + return map(conn, obj); + } + + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapCertExactMap: " + msg); + } + +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/LdapCertSubjMap.java b/base/common/src/com/netscape/cms/publish/mappers/LdapCertSubjMap.java new file mode 100644 index 000000000..4d5ff38c8 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/LdapCertSubjMap.java @@ -0,0 +1,343 @@ +// --- 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.publish.mappers; + +import java.security.cert.X509Certificate; +import java.util.Locale; +import java.util.Vector; + +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPSearchResults; +import netscape.ldap.LDAPv2; +import netscape.ldap.LDAPv3; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CRLImpl; + +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.ldap.ELdapException; +import com.netscape.certsrv.ldap.ELdapServerDownException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapMapper; +import com.netscape.certsrv.request.IRequest; + +/** + * Maps a X509 certificate to a LDAP entry by finding an LDAP entry + * which has an attribute whose contents are equal to the cert subject name. + * + * @version $Revision$, $Date$ + */ +public class LdapCertSubjMap implements ILdapMapper, IExtendedPluginInfo { + public static final String LDAP_CERTSUBJNAME_ATTR = "certSubjectName"; + protected String mSearchBase = null; + protected String mCertSubjNameAttr = LDAP_CERTSUBJNAME_ATTR; + protected boolean mUseAllEntries = false; + + private ILogger mLogger = CMS.getLogger(); + protected IConfigStore mConfig = null; + boolean mInited = false; + + public LdapCertSubjMap() { + // need to setup the mSearchBase via configuration + } + + /** + * constructs a certificate subject name mapper with search base. + * + * @param searchBase the dn to start searching for the certificate + * subject name. + */ + public LdapCertSubjMap(String searchBase) { + if (searchBase == null) + throw new IllegalArgumentException( + "a null argument to constructor " + this.getClass().getName()); + mSearchBase = searchBase; + mInited = true; + } + + /** + * Constructor using non-ES cert map attribute name. + * + * @param searchBase entry to start search. + * @param certSubjNameAttr attribute for certificate subject names. + * @param certAttr attribute to find certificate. + */ + public LdapCertSubjMap(String searchBase, + String certSubjNameAttr, String certAttr) { + if (searchBase == null || + certSubjNameAttr == null || certAttr == null) + throw new IllegalArgumentException( + "a null argument to constructor " + this.getClass().getName()); + mCertSubjNameAttr = certSubjNameAttr; + mSearchBase = searchBase; + mInited = true; + } + + public LdapCertSubjMap(String searchBase, + String certSubjNameAttr, String certAttr, boolean useAllEntries) { + if (searchBase == null || + certSubjNameAttr == null || certAttr == null) + throw new IllegalArgumentException( + "a null argument to constructor " + this.getClass().getName()); + mCertSubjNameAttr = certSubjNameAttr; + mSearchBase = searchBase; + mUseAllEntries = useAllEntries; + mInited = true; + } + + public String getImplName() { + return "LdapCertSubjMap"; + } + + public String getDescription() { + return "LdapCertSubjMap"; + } + + public Vector getDefaultParams() { + Vector v = new Vector(); + + v.addElement("certSubjNameAttr=" + mCertSubjNameAttr); + v.addElement("searchBase="); + v.addElement("useAllEntries=" + mUseAllEntries); + return v; + } + + public String[] getExtendedPluginInfo(Locale locale) { + String[] params = { + "certSubjNameAttr;string;Name of Ldap attribute containing cert subject name", + "searchBase;string;Base DN to search from", + "useAllEntries;boolean;Use all entries for publishing", + IExtendedPluginInfo.HELP_TOKEN + + ";configuration-ldappublish-mapper-certsubjmapper", + IExtendedPluginInfo.HELP_TEXT + + ";This plugin assumes you want to publish to an LDAP entry which has " + + "an attribute whose contents are equal to the cert subject name" + }; + + return params; + } + + public Vector getInstanceParams() { + Vector v = new Vector(); + + if (mCertSubjNameAttr == null) { + v.addElement("certSubjNameAttr="); + } else { + v.addElement("certSubjNameAttr=" + mCertSubjNameAttr); + } + if (mSearchBase == null) { + v.addElement("searchBase="); + } else { + v.addElement("searchBase=" + mSearchBase); + } + v.addElement("useAllEntries=" + mUseAllEntries); + return v; + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + public void init(IConfigStore config) + throws EBaseException { + if (mInited == true) + return; + mConfig = config; + mCertSubjNameAttr = config.getString("certSubjNameAttr", + LDAP_CERTSUBJNAME_ATTR); + mSearchBase = config.getString("searchBase"); + mUseAllEntries = config.getBoolean("useAllEntries", false); + mInited = true; + } + + /** + * Finds the entry for the certificate by looking for the cert + * subject name in the subject name attribute. + * + * @param conn - the LDAP connection. + * @param obj - the X509Certificate. + */ + public String + map(LDAPConnection conn, Object obj) + throws ELdapException { + if (conn == null) + return null; + X500Name subjectDN = null; + + try { + X509Certificate cert = (X509Certificate) obj; + + subjectDN = + (X500Name) ((X509Certificate) cert).getSubjectDN(); + + CMS.debug("LdapCertSubjMap: cert subject dn:" + subjectDN.toString()); + } catch (ClassCastException e) { + try { + X509CRLImpl crl = (X509CRLImpl) obj; + + subjectDN = + (X500Name) ((X509CRLImpl) crl).getIssuerDN(); + + CMS.debug("LdapCertSubjMap: crl issuer dn: " + + subjectDN.toString()); + } catch (ClassCastException ex) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_NOT_SUPPORTED_OBJECT")); + return null; + } + } + try { + String[] attrs = new String[] { LDAPv3.NO_ATTRS }; + + log(ILogger.LL_INFO, "search " + mSearchBase + + " (" + mCertSubjNameAttr + "=" + subjectDN + ") " + mCertSubjNameAttr); + + LDAPSearchResults results = + conn.search(mSearchBase, LDAPv2.SCOPE_SUB, + "(" + mCertSubjNameAttr + "=" + subjectDN + ")", attrs, false); + + LDAPEntry entry = results.next(); + + if (results.hasMoreElements()) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_MORE_THAN_ONE_ENTRY", "", subjectDN.toString())); + } + if (entry != null) { + log(ILogger.LL_INFO, "entry found"); + return entry.getDN(); + } + return null; + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_DN_MAP_EXCEPTION", "LDAPException", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", e.toString())); + } + } + + /* + catch (IOException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_GET_SUBJECT", e.toString())); + throw new ELdapException( + LdapResources.GET_CERT_SUBJECT_DN_FAILED, e); + } + catch (CertificateEncodingException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_DECODE_CERT", e.toString())); + throw new ELdapException( + LdapResources.GET_DER_ENCODED_CERT_FAILED, e); + } + */ + } + + public String map(LDAPConnection conn, IRequest req, Object obj) + throws ELdapException { + return map(conn, obj); + } + + public Vector mapAll(LDAPConnection conn, Object obj) + throws ELdapException { + Vector v = new Vector(); + + if (conn == null) + return null; + X500Name subjectDN = null; + + try { + X509Certificate cert = (X509Certificate) obj; + subjectDN = (X500Name) ((X509Certificate) cert).getSubjectDN(); + CMS.debug("LdapCertSubjMap: cert subject dn:" + subjectDN.toString()); + } catch (ClassCastException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_NOT_SUPPORTED_OBJECT")); + return v; + } + try { + String[] attrs = new String[] { LDAPv3.NO_ATTRS }; + + log(ILogger.LL_INFO, "search " + mSearchBase + + " (" + mCertSubjNameAttr + "=" + subjectDN + ") " + mCertSubjNameAttr); + + LDAPSearchResults results = + conn.search(mSearchBase, LDAPv2.SCOPE_SUB, + "(" + mCertSubjNameAttr + "=" + subjectDN + ")", attrs, false); + + while (results.hasMoreElements()) { + LDAPEntry entry = results.next(); + String dn = entry.getDN(); + v.addElement(dn); + CMS.debug("LdapCertSubjMap: dn=" + dn); + } + CMS.debug("LdapCertSubjMap: Number of entries: " + v.size()); + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_DN_MAP_EXCEPTION", "LDAPException", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", e.toString())); + } + } + + return v; + } + + public Vector mapAll(LDAPConnection conn, IRequest req, Object obj) + throws ELdapException { + return mapAll(conn, obj); + } + + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapCertSubjMap: " + msg); + } + + /** + * return search base + */ + public String getSearchBase() { + return mSearchBase; + } + + /** + * return certificate subject attribute + */ + public String getCertSubjNameAttr() { + return mCertSubjNameAttr; + } + + public boolean useAllEntries() { + return mUseAllEntries; + } + +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/LdapCrlIssuerCompsMap.java b/base/common/src/com/netscape/cms/publish/mappers/LdapCrlIssuerCompsMap.java new file mode 100644 index 000000000..654de5d30 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/LdapCrlIssuerCompsMap.java @@ -0,0 +1,156 @@ +// --- 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.publish.mappers; + +import java.security.cert.CRLException; +import java.util.Vector; + +import netscape.ldap.LDAPConnection; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CRLImpl; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapMapper; +import com.netscape.certsrv.request.IRequest; + +/** + * Default crl mapper. + * maps the crl to a ldap entry by using components in the issuer name + * to find the CA's entry. + * + * @version $Revision$, $Date$ + */ +public class LdapCrlIssuerCompsMap + extends LdapDNCompsMap implements ILdapMapper { + ILogger mLogger = CMS.getLogger(); + + public LdapCrlIssuerCompsMap() { + // need to support baseDN, dnComps, and filterComps + // via configuration + } + + /** + * Constructor. + * + * The DN comps are used to form a LDAP entry to begin a subtree search. + * The filter comps are used to form a search filter for the subtree. + * If none of the DN comps matched, baseDN is used for the subtree. + * If the baseDN is null and none of the DN comps matched, it is an error. + * If none of the DN comps and filter comps matched, it is an error. + * If just the filter comps is null, a base search is performed. + * + * @param baseDN The base DN. + * @param dnComps Components to form the LDAP base dn for search. + * @param filterComps Components to form the LDAP search filter. + */ + public LdapCrlIssuerCompsMap(String baseDN, ObjectIdentifier[] dnComps, + ObjectIdentifier[] filterComps) { + init(baseDN, dnComps, filterComps); + } + + /** + * constructor using non-standard certificate attribute. + */ + public LdapCrlIssuerCompsMap(String crlAttr, String baseDN, + ObjectIdentifier[] dnComps, + ObjectIdentifier[] filterComps) { + super(crlAttr, baseDN, dnComps, filterComps); + } + + public String getImplName() { + return "LdapCrlIssuerCompsMap"; + } + + public String getDescription() { + return "LdapCrlIssuerCompsMap"; + } + + public Vector getDefaultParams() { + Vector v = super.getDefaultParams(); + + //v.addElement("crlAttr=" + LdapCrlPublisher.LDAP_CRL_ATTR); + return v; + } + + public Vector getInstanceParams() { + Vector v = super.getInstanceParams(); + + return v; + } + + protected void init(String baseDN, ObjectIdentifier[] dnComps, + ObjectIdentifier[] filterComps) { + //mLdapAttr = LdapCrlPublisher.LDAP_CRL_ATTR; + super.init(baseDN, dnComps, filterComps); + } + + /** + * Maps a crl to LDAP entry. + * Uses issuer DN components and filter components to form a DN and + * filter for a LDAP search. + * If the formed DN is null the baseDN will be used. + * If the formed DN is null and baseDN is null an error is thrown. + * If the filter is null a base search is performed. + * If both are null an error is thrown. + * + * @param conn - the LDAP connection. + * @param obj - the X509Certificate. + * @return the result. LdapCertMapResult is also used for CRL. + */ + public String + map(LDAPConnection conn, Object obj) + throws ELdapException { + if (conn == null) + return null; + X509CRLImpl crl = (X509CRLImpl) obj; + + try { + String result = null; + X500Name issuerDN = + (X500Name) ((X509CRLImpl) crl).getIssuerDN(); + + CMS.debug("LdapCrlIssuerCompsMap: " + issuerDN.toString()); + + byte[] crlbytes = crl.getEncoded(); + + result = super.map(conn, issuerDN, crlbytes); + return result; + } catch (CRLException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_DECODE_CRL", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_GET_DER_ENCODED_CRL_FAILED", e.toString())); + } + } + + public String map(LDAPConnection conn, IRequest req, Object obj) + throws ELdapException { + return map(conn, obj); + } + + /** + * overrides super's log(). + */ + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapCrlCompsMap: " + msg); + } + +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/LdapDNCompsMap.java b/base/common/src/com/netscape/cms/publish/mappers/LdapDNCompsMap.java new file mode 100644 index 000000000..73549f1b5 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/LdapDNCompsMap.java @@ -0,0 +1,457 @@ +// --- 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.publish.mappers; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Locale; +import java.util.StringTokenizer; +import java.util.Vector; + +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPSearchResults; +import netscape.ldap.LDAPv2; +import netscape.ldap.LDAPv3; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.AVA; +import netscape.security.x509.RDN; +import netscape.security.x509.X500Name; +import netscape.security.x509.X500NameAttrMap; + +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.ldap.ELdapException; +import com.netscape.certsrv.ldap.ELdapServerDownException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapPlugin; + +/** + * Maps a Subject name to an entry in the LDAP server. + * subject name to form the ldap search dn and filter. + * Takes a optional root search dn. + * The DN comps are used to form a LDAP entry to begin a subtree search. + * The filter comps are used to form a search filter for the subtree. + * If none of the DN comps matched, baseDN is used for the subtree. + * If the baseDN is null and none of the DN comps matched, it is an error. + * If none of the DN comps and filter comps matched, it is an error. + * If just the filter comps is null, a base search is performed. + * + * @version $Revision$, $Date$ + */ +public class LdapDNCompsMap + implements ILdapPlugin, IExtendedPluginInfo { + //protected String mLdapAttr = null; + protected String mBaseDN = null; + protected ObjectIdentifier[] mDnComps = null; + protected ObjectIdentifier[] mFilterComps = null; + + private ILogger mLogger = CMS.getLogger(); + private boolean mInited = false; + protected IConfigStore mConfig = null; + + /** + * Constructor. + * + * The DN comps are used to form a LDAP entry to begin a subtree search. + * The filter comps are used to form a search filter for the subtree. + * If none of the DN comps matched, baseDN is used for the subtree. + * If the baseDN is null and none of the DN comps matched, it is an error. + * If none of the DN comps and filter comps matched, it is an error. + * If just the filter comps is null, a base search is performed. + * + * @param baseDN The base DN. + * @param dnComps Components to form the LDAP base dn for search. + * @param filterComps Components to form the LDAP search filter. + */ + public LdapDNCompsMap(String ldapAttr, String baseDN, + ObjectIdentifier[] dnComps, + ObjectIdentifier[] filterComps) { + //mLdapAttr = ldapAttr; + init(baseDN, dnComps, filterComps); + } + + /** + * constructor if initializing from config store. + */ + public LdapDNCompsMap() { + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + /** + * for initializing from config store. + */ + public void init(IConfigStore config) + throws EBaseException { + mConfig = config; + String baseDN = mConfig.getString("baseDN"); + ObjectIdentifier[] dnComps = + getCompsFromString(mConfig.getString("dnComps")); + ObjectIdentifier[] filterComps = + getCompsFromString(mConfig.getString("filterComps")); + + init(baseDN, dnComps, filterComps); + } + + public String getImplName() { + return "LdapDNCompsMap"; + } + + public String getDescription() { + return "LdapDNCompsMap"; + } + + public String[] getExtendedPluginInfo(Locale locale) { + String[] s = { + "baseDN;string;Base to search from. E.g ou=Engineering,o=Fedora", + "dnComps;string;Comma-separated list of attributes to put in the DN", + "filterComps;string;Comma-separated list of attributes to form the filter", + IExtendedPluginInfo.HELP_TOKEN + + ";configuration-ldappublish-mapper-dncompsmapper", + IExtendedPluginInfo.HELP_TEXT + + ";More complex mapper. Used when there is not enough information " + + "in the cert request to form the complete LDAP DN. Using this " + + "plugin, you can specify additional LDAP filters to narrow down the " + + "search" + }; + + return s; + } + + public Vector getDefaultParams() { + Vector v = new Vector(); + + v.addElement("baseDN="); + v.addElement("dnComps="); + v.addElement("filterComps="); + return v; + } + + public Vector getInstanceParams() { + Vector v = new Vector(); + + try { + if (mBaseDN == null) { + v.addElement("baseDN="); + } else { + v.addElement("baseDN=" + mConfig.getString("baseDN")); + } + if (mDnComps == null) { + v.addElement("dnComps="); + } else { + v.addElement("dnComps=" + + mConfig.getString("dnComps")); + } + if (mFilterComps == null) { + v.addElement("filterComps="); + } else { + v.addElement("filterComps=" + + mConfig.getString("filterComps")); + } + } catch (Exception e) { + } + return v; + } + + /** + * common initialization routine. + */ + protected void init(String baseDN, ObjectIdentifier[] dnComps, + ObjectIdentifier[] filterComps) { + if (mInited) + return; + + mBaseDN = baseDN; + if (dnComps != null) + mDnComps = (ObjectIdentifier[]) dnComps.clone(); + if (filterComps != null) + mFilterComps = (ObjectIdentifier[]) filterComps.clone(); + + // log debug info. + for (int i = 0; i < mDnComps.length; i++) { + CMS.debug( + "LdapDNCompsMap: dnComp " + X500NameAttrMap.getDefault().getName(mDnComps[i])); + } + for (int i = 0; i < mFilterComps.length; i++) { + CMS.debug("LdapDNCompsMap: filterComp " + + X500NameAttrMap.getDefault().getName(mFilterComps[i])); + } + mInited = true; + } + + /** + * Maps a X500 subject name to LDAP entry. + * Uses DN components and filter components to form a DN and + * filter for a LDAP search. + * If the formed DN is null the baseDN will be used. + * If the formed DN is null and baseDN is null an error is thrown. + * If the filter is null a base search is performed. + * If both are null an error is thrown. + * + * @param conn the LDAP connection. + * @param x500name the dn to map. + * @param obj the object + * @exception ELdapException if any LDAP exceptions occured. + * @return the DN of the entry. + */ + public String map(LDAPConnection conn, X500Name x500name, + byte[] obj) + throws ELdapException { + try { + if (conn == null) + return null; + + CMS.debug("LdapDNCompsMap: " + x500name.toString()); + + String[] dnAndFilter = formDNandFilter(x500name); + String dn = dnAndFilter[0]; + String filter = dnAndFilter[1]; + + if (dn == null) { + // #362332 + // if (filter == null) { + // log(ILogger.LL_FAILURE, "No dn and filter formed"); + // throw new ELdapException( + // LdapResources.NO_DN_AND_FILTER_COMPS, + // x500name.toString()); + // } + if (mBaseDN == null) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_BASE")); + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_NO_DN_COMPS_AND_BASEDN", + x500name.toString())); + } + dn = mBaseDN; + } + int scope = LDAPv2.SCOPE_SUB; + + if (filter == null) { + scope = LDAPv2.SCOPE_BASE; + filter = "(objectclass=*)"; + } + + // search for entry + String[] attrs; + + attrs = new String[] { LDAPv3.NO_ATTRS }; + + log(ILogger.LL_INFO, "searching for " + dn + " " + filter + " " + + ((scope == LDAPv2.SCOPE_SUB) ? "sub" : "base")); + + LDAPSearchResults results = + conn.search(dn, scope, filter, attrs, false); + LDAPEntry entry = results.next(); + + if (results.hasMoreElements()) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_MORE_THAN_ONE_ENTRY", "", x500name.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_MORE_THAN_ONE_ENTRY", + x500name.toString())); + } + if (entry != null) { + return entry.getDN(); + } else { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_ENTRY_NOT_FOUND", "", x500name.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", + "null entry")); + } + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_DN_MAP_EXCEPTION", "LDAPException", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", e.toString())); + } + } + } + + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapDNCompsMap: " + msg); + } + + /** + * form a dn and filter from component in the cert subject name + * + * @param subjName subject name + */ + public String[] formDNandFilter(X500Name subjName) + throws ELdapException { + Vector dnRdns = new Vector(); + SearchFilter filter = new SearchFilter(); + X500NameAttrMap attrMap = X500NameAttrMap.getDefault(); + String dnStr = null, filterStr = null; + ObjectIdentifier EOid = attrMap.getOid("E"); + ObjectIdentifier mailOid = attrMap.getOid("MAIL"); + + try { + // get the base DN & filter. + for (Enumeration n = subjName.getRDNs(); n.hasMoreElements();) { + RDN rdn = (RDN) n.nextElement(); + // NOTE assumes one AVA per RDN. + AVA ava = rdn.getAssertion()[0]; + ObjectIdentifier oid = ava.getOid(); + + for (int i = 0; i < mDnComps.length; i++) { + if (mDnComps[i].equals(oid)) { + if (oid == EOid) { + DerValue val = ava.getValue(); + AVA newAVA = new AVA(mailOid, val); + RDN newRDN = new RDN(new AVA[] { newAVA } + ); + + CMS.debug( + "LdapDNCompsMap: Converted " + rdn.toLdapDNString() + " to " + + newRDN.toLdapDNString() + " in DN"); + rdn = newRDN; + } + dnRdns.addElement(rdn); + CMS.debug( + "LdapDNCompsMap: adding dn comp " + rdn.toLdapDNString()); + break; + } + } + for (int i = 0; i < mFilterComps.length; i++) { + if (mFilterComps[i].equals(oid)) { + if (oid == EOid) { + DerValue val = ava.getValue(); + AVA newAVA = new AVA(mailOid, val); + + CMS.debug( + "LdapDNCompsMap: Converted " + ava.toLdapDNString() + " to " + + newAVA.toLdapDNString() + " in filter"); + ava = newAVA; + } + filter.addElement(ava.toLdapDNString()); + CMS.debug( + "LdapDNCompsMap: adding filter comp " + ava.toLdapDNString()); + break; + } + } + + // XXX should be an error when string is null? + // return to caller to decide. + if (dnRdns.size() != 0) { + dnStr = new X500Name(dnRdns).toLdapDNString(); + } + if (filter.size() != 0) { + filterStr = filter.toFilterString(); + } + } + } catch (IOException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_FROM_SUBJ_TO_DN", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_FORM_DN_COMPS_FAILED", e.toString())); + } + + return new String[] { dnStr, filterStr }; + } + + public ObjectIdentifier[] getDnComps() { + return (ObjectIdentifier[]) mDnComps.clone(); + } + + public ObjectIdentifier[] getFilterComps() { + return (ObjectIdentifier[]) mFilterComps.clone(); + } + + /** + * class for forming search filters for ldap searching from + * name=value components. components are anded. + */ + + public static class SearchFilter extends Vector { + private static final long serialVersionUID = 4210302171279891828L; + + public String toFilterString() { + StringBuffer buf = new StringBuffer(); + + if (elementCount == 0) { + return null; + } + if (elementCount == 1) { + buf.append("(" + (String) elementData[0] + ")"); + return buf.toString(); + } + buf.append("(&"); + for (int i = 0; i < elementCount; i++) { + buf.append("(" + (String) elementData[i] + ")"); + } + buf.append(")"); + return buf.toString(); + } + } + + /** + * useful routine for parsing components given as string to + * arrays of objectidentifiers. + * The string is expected to be comma separated AVA attribute names. + * For example, "uid,cn,o,ou". Attribute names are case insensitive. + * + * @param val the string specifying the comps + * @exception ELdapException if any error occurs. + */ + public static ObjectIdentifier[] getCompsFromString(String val) + throws ELdapException { + StringTokenizer tokens; + ObjectIdentifier[] comps; + String attr; + ObjectIdentifier oid; + + if (val == null || val.length() == 0) + return new ObjectIdentifier[0]; + + tokens = new StringTokenizer(val, ", \t\n\r"); + comps = new ObjectIdentifier[tokens.countTokens()]; + if (comps.length == 0) { + return new ObjectIdentifier[0]; + } + int i = 0; + + while (tokens.hasMoreTokens()) { + attr = tokens.nextToken().trim(); + // mail -> E hack to look for E in subject names. + if (attr.equalsIgnoreCase("mail")) + attr = "E"; + oid = X500NameAttrMap.getDefault().getOid(attr); + if (oid != null) { + comps[i++] = oid; + } else { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_UNKNOWN_ATTR_IN_DN_FILTER_COMPS", attr)); + } + } + return comps; + } + +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/LdapEnhancedMap.java b/base/common/src/com/netscape/cms/publish/mappers/LdapEnhancedMap.java new file mode 100644 index 000000000..c9a7f867c --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/LdapEnhancedMap.java @@ -0,0 +1,640 @@ +// --- 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 statement // +/////////////////////// + +package com.netscape.cms.publish.mappers; + +/////////////////////// +// import statements // +/////////////////////// + +/* cert server imports */ +import java.io.IOException; +import java.security.cert.X509Certificate; +import java.util.Locale; +import java.util.Vector; + +import netscape.ldap.LDAPAttribute; +import netscape.ldap.LDAPAttributeSet; +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPSearchResults; +import netscape.ldap.LDAPv2; +import netscape.ldap.LDAPv3; +import netscape.ldap.util.DN; +import netscape.security.x509.CertificateExtensions; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CRLImpl; +import netscape.security.x509.X509CertImpl; +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.ldap.ELdapException; +import com.netscape.certsrv.ldap.ELdapServerDownException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapMapper; +import com.netscape.certsrv.request.IRequest; + +////////////////////// +// class definition // +////////////////////// + +/** + * Maps a request to an entry in the LDAP server. + * Takes a dnPattern to form the baseDN from the + * request attributes and certificate subject name. + * Does a base search for the entry in the directory + * to publish the cert or crl. The restriction of + * this mapper is that the ldap dn components must + * be part of certificate subject name or request + * attributes or constant. The difference of this + * mapper and LdapSimpleMap is that if the ldap + * entry is not found, it has the option to create + * the ldap entry given the dn and attributes + * formulated. + * + * @version $Revision$, $Date$ + */ +public class LdapEnhancedMap + implements ILdapMapper, IExtendedPluginInfo { + //////////////////////// + // default parameters // + //////////////////////// + + ////////////////////////////////////// + // local LdapEnhancedMap parameters // + ////////////////////////////////////// + + private boolean mInited = false; + + // the subject DN pattern + protected MapDNPattern mPattern = null; + + // the list of request attriubutes to retrieve + protected String[] mReqAttrs = null; + + // the list of cert attributes to retrieve + protected String[] mCertAttrs = null; + + protected String[] mLdapValues = null; + + //////////////////////////// + // ILdapMapper parameters // + //////////////////////////// + + /* mapper plug-in fields */ + protected static final String PROP_DNPATTERN = "dnPattern"; + protected static final String PROP_CREATE = "createEntry"; + // the object class of the entry to be created. xxxx not done yet + protected static final String PROP_OBJCLASS = "objectClass"; + // req/cert/ext attribute --> directory attribute table + protected static final String PROP_ATTRNUM = "attrNum"; + protected static final String PROP_ATTR_NAME = "attrName"; + protected static final String PROP_ATTR_PATTERN = "attrPattern"; + + /* mapper plug-in fields initialization values */ + private static final int DEFAULT_NUM_ATTRS = 1; + + /* Holds mapper plug-in fields accepted by this implementation. + * This list is passed to the configuration console so configuration + * for instances of this implementation can be configured through the + * console. + */ + private static Vector defaultParams = new Vector(); + + static { + defaultParams.addElement(PROP_DNPATTERN + "="); + defaultParams.addElement(PROP_CREATE + "=true"); + defaultParams.addElement(PROP_ATTRNUM + "=" + DEFAULT_NUM_ATTRS); + for (int i = 0; i < DEFAULT_NUM_ATTRS; i++) { + defaultParams.addElement(PROP_ATTR_NAME + i + "="); + defaultParams.addElement(PROP_ATTR_PATTERN + i + "="); + } + } + + /* mapper plug-in values */ + protected String mDnPattern = null; + protected boolean mCreateEntry = true; + private int mNumAttrs = DEFAULT_NUM_ATTRS; + protected String[] mLdapNames = null; + protected String[] mLdapPatterns = null; + + /* miscellaneous constants local to this mapper plug-in */ + // default dn pattern if left blank or not set in the config + public static final String DEFAULT_DNPATTERN = + "UID=$req.HTTP_PARAMS.UID, " + + "OU=people, O=$subj.o, C=$subj.c"; + private static final int MAX_ATTRS = 10; + protected static final int DEFAULT_ATTRNUM = 1; + + /* miscellaneous variables local to this mapper plug-in */ + protected IConfigStore mConfig = null; + protected AVAPattern[] mPatterns = null; + + //////////////////////////////////// + // IExtendedPluginInfo parameters // + //////////////////////////////////// + + /////////////////////// + // Logger parameters // + /////////////////////// + + private ILogger mLogger = CMS.getLogger(); + + ///////////////////// + // default methods // + ///////////////////// + + /** + * Default constructor, initialization must follow. + */ + public LdapEnhancedMap() { + } + + /////////////////////////////////// + // local LdapEnhancedMap methods // + /////////////////////////////////// + + /** + * common initialization routine. + */ + protected void init(String dnPattern) + throws EBaseException { + if (mInited) { + return; + } + + mDnPattern = dnPattern; + if (mDnPattern == null || + mDnPattern.length() == 0) { + mDnPattern = DEFAULT_DNPATTERN; + } + + try { + mPattern = new MapDNPattern(mDnPattern); + } catch (ELdapException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_DN_PATTERN_INIT", + dnPattern, e.toString())); + throw new EBaseException( + "falied to init with pattern " + + dnPattern + " " + e); + } + + mInited = true; + } + + /** + * form a dn from component in the request and cert subject name + * + * @param req The request + * @param obj The certificate or crl + */ + private String formDN(IRequest req, Object obj) + throws EBaseException { + CertificateExtensions certExt = null; + X500Name subjectDN = null; + + try { + X509Certificate cert = (X509Certificate) obj; + + subjectDN = + (X500Name) ((X509Certificate) cert).getSubjectDN(); + CMS.debug( + "LdapEnhancedMap: cert subject dn:" + + subjectDN.toString()); + + //certExt = (CertificateExtensions) + // ((X509CertImpl)cert).get( + // X509CertInfo.EXTENSIONS); + X509CertInfo info = (X509CertInfo) + ((X509CertImpl) cert).get( + X509CertImpl.NAME + + "." + + X509CertImpl.INFO); + + certExt = (CertificateExtensions) + info.get(CertificateExtensions.NAME); + } catch (java.security.cert.CertificateParsingException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_GET_EXT", e.toString())); + } catch (IOException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_GET_EXT", e.toString())); + } catch (java.security.cert.CertificateException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_GET_EXT", e.toString())); + } catch (ClassCastException e) { + + try { + X509CRLImpl crl = (X509CRLImpl) obj; + + subjectDN = (X500Name) + ((X509CRLImpl) crl).getIssuerDN(); + + CMS.debug( + "LdapEnhancedMap: crl issuer dn: " + + + subjectDN.toString()); + } catch (ClassCastException ex) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_PUBLISH_OBJ_NOT_SUPPORTED", + ((req == null) ? "" + : req.getRequestId().toString()))); + return null; + } + } + + try { + mLdapValues = new String[mNumAttrs]; + + for (int i = 0; i < mNumAttrs; i++) { + if (mPatterns[i] != null) { + mLdapValues[i] = mPatterns[i].formAVA( + req, + subjectDN, + certExt); + } + } + + String dn = mPattern.formDN(req, subjectDN, certExt); + + return dn; + } catch (ELdapException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_FORM_DN", + ((req == null) ? "" + : req.getRequestId().toString()), e.toString())); + + throw new EBaseException( + "failed to form dn for request: " + + ((req == null) ? "" + : req.getRequestId().toString()) + + " " + e); + } + } + + private void createEntry(LDAPConnection conn, String dn) + throws LDAPException { + LDAPAttributeSet attrs = new LDAPAttributeSet(); + + // OID 2.5.6.16 + String caOc[] = { "top", + "person", + "organizationalPerson", + "inetOrgPerson" }; + + DN dnobj = new DN(dn); + String attrval[] = dnobj.explodeDN(true); + + attrs.add(new LDAPAttribute("cn", attrval[0])); + attrs.add(new LDAPAttribute("sn", attrval[0])); + attrs.add(new LDAPAttribute("objectclass", caOc)); + + for (int i = 0; i < mNumAttrs; i++) { + if (mLdapNames[i] != null && + !mLdapNames[i].trim().equals("") && + mLdapValues[i] != null && + !mLdapValues[i].trim().equals("")) { + attrs.add(new LDAPAttribute(mLdapNames[i], + mLdapValues[i])); + } + } + + LDAPEntry entry = new LDAPEntry(dn, attrs); + + conn.add(entry); + } + + ///////////////////////// + // ILdapMapper methods // + ///////////////////////// + + /** + * for initializing from config store. + * + * implementation for extended + * ILdapPlugin interface method + */ + public void init(IConfigStore config) + throws EBaseException { + mConfig = config; + + mDnPattern = mConfig.getString(PROP_DNPATTERN, + DEFAULT_DNPATTERN); + + mCreateEntry = mConfig.getBoolean(PROP_CREATE, + true); + + mNumAttrs = mConfig.getInteger(PROP_ATTRNUM, + 0); + + mLdapNames = new String[mNumAttrs]; + + mLdapPatterns = new String[mNumAttrs]; + + mPatterns = new AVAPattern[mNumAttrs]; + for (int i = 0; i < mNumAttrs; i++) { + mLdapNames[i] = + mConfig.getString(PROP_ATTR_NAME + + Integer.toString(i), + ""); + + mLdapPatterns[i] = + mConfig.getString(PROP_ATTR_PATTERN + + Integer.toString(i), + ""); + + if (mLdapPatterns[i] != null && + !mLdapPatterns[i].trim().equals("")) { + mPatterns[i] = new AVAPattern(mLdapPatterns[i]); + } + } + + init(mDnPattern); + } + + /** + * implementation for extended + * ILdapPlugin interface method + */ + public IConfigStore getConfigStore() { + return mConfig; + } + + public String getImplName() { + return "LdapEnhancedMap"; + } + + public String getDescription() { + return "LdapEnhancedMap"; + } + + public Vector getDefaultParams() { + return defaultParams; + } + + public Vector getInstanceParams() { + Vector v = new Vector(); + + try { + if (mDnPattern == null) { + v.addElement(PROP_DNPATTERN + "="); + } else { + v.addElement(PROP_DNPATTERN + "=" + + mConfig.getString(PROP_DNPATTERN)); + } + + v.addElement(PROP_CREATE + "=" + + mConfig.getBoolean(PROP_CREATE, + true)); + + v.addElement(PROP_ATTRNUM + "=" + + mConfig.getInteger(PROP_ATTRNUM, + DEFAULT_NUM_ATTRS)); + + for (int i = 0; i < mNumAttrs; i++) { + if (mLdapNames[i] != null) { + v.addElement(PROP_ATTR_NAME + i + + "=" + mLdapNames[i]); + } else { + v.addElement(PROP_ATTR_NAME + i + + "="); + } + + if (mLdapPatterns[i] != null) { + v.addElement(PROP_ATTR_PATTERN + i + + "=" + mLdapPatterns[i]); + } else { + v.addElement(PROP_ATTR_PATTERN + i + + "="); + } + } + } catch (Exception e) { + } + + return v; + } + + /** + * Maps an X500 subject name to an LDAP entry. + * Uses DN pattern to form a DN for an LDAP base search. + * + * @param conn the LDAP connection. + * @param obj the object to map. + * @exception ELdapException if any LDAP exceptions occurred. + */ + public String map(LDAPConnection conn, Object obj) + throws ELdapException { + return map(conn, null, obj); + } + + /** + * Maps an X500 subject name to an LDAP entry. + * Uses DN pattern to form a DN for an LDAP base search. + * + * @param conn the LDAP connection. + * @param req the request to map. + * @param obj the object to map. + * @exception ELdapException if any LDAP exceptions occurred. + */ + public String map(LDAPConnection conn, IRequest req, Object obj) + throws ELdapException { + if (conn == null) { + return null; + } + + String dn = null; + + try { + dn = formDN(req, obj); + if (dn == null) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_DN_NOT_FORMED")); + + String s1 = ""; + + if (req != null) + s1 = req.getRequestId().toString(); + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_NO_DN_MATCH", s1)); + } + + int scope = LDAPv2.SCOPE_BASE; + String filter = "(objectclass=*)"; + + // search for entry + String[] attrs = new String[] { LDAPv3.NO_ATTRS }; + + log(ILogger.LL_INFO, + "searching for dn: " + + dn + " filter:" + + filter + " scope: base"); + + LDAPSearchResults results = conn.search(dn, + scope, + filter, + attrs, + false); + + LDAPEntry entry = results.next(); + + if (results.hasMoreElements()) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_MORE_THAN_ONE_ENTRY", + dn + + ((req == null) ? "" + : req.getRequestId().toString()))); + + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_MORE_THAN_ONE_ENTRY", + ((req == null) ? "" + : req.getRequestId().toString()))); + } + + if (entry != null) { + return entry.getDN(); + } else { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_ENTRY_NOT_FOUND", + dn + + ((req == null) ? "" + : req.getRequestId().toString()))); + + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", + "null entry")); + } + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else if (e.getLDAPResultCode() == + LDAPException.NO_SUCH_OBJECT && mCreateEntry) { + + try { + createEntry(conn, dn); + + log(ILogger.LL_INFO, + "Entry " + + dn + + " Created"); + + return dn; + } catch (LDAPException e1) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_DN_MAP_EXCEPTION", + dn, + e.toString())); + + log(ILogger.LL_FAILURE, + "Entry is not created. " + + "This may because there are " + + "entries in the directory " + + "hierachy not exit."); + + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_CREATE_ENTRY", dn)); + } + } else { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_DN_MAP_EXCEPTION", + dn, + e.toString())); + + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", e.toString())); + } + } catch (EBaseException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_EXCEPTION_CAUGHT", + e.toString())); + + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", e.toString())); + } + } + + ///////////////////////////////// + // IExtendedPluginInfo methods // + ///////////////////////////////// + + public String[] getExtendedPluginInfo(Locale locale) { + Vector v = new Vector(); + + v.addElement(PROP_DNPATTERN + + ";string;Describes how to form the Ldap " + + "Subject name in the directory. " + + "Example 1: 'uid=CertMgr, o=Fedora'. " + + "Example 2: 'uid=$req.HTTP_PARAMS.uid, " + + "E=$ext.SubjectAlternativeName.RFC822Name, " + + "ou=$subj.ou'. " + + "$req means: take the attribute from the " + + "request. " + + "$subj means: take the attribute from the " + + "certificate subject name. " + + "$ext means: take the attribute from the " + + "certificate extension"); + v.addElement(PROP_CREATE + + ";boolean;If checked, An entry will be " + + "created automatically"); + v.addElement(PROP_ATTRNUM + + ";string;How many attributes to add."); + v.addElement(IExtendedPluginInfo.HELP_TOKEN + + ";configuration-ldappublish-mapper-enhancedmapper"); + v.addElement(IExtendedPluginInfo.HELP_TEXT + + ";Describes how to form the LDAP DN of the " + + "entry to publish to"); + + for (int i = 0; i < MAX_ATTRS; i++) { + v.addElement(PROP_ATTR_NAME + + Integer.toString(i) + + ";string;" + + "The name of LDAP attribute " + + "to be added. e.g. mail"); + v.addElement(PROP_ATTR_PATTERN + + Integer.toString(i) + + ";string;" + + "How to create the LDAP attribute value. " + + "e.g. $req.HTTP_PARAMS.csrRequestorEmail, " + + "$subj.E or " + + "$ext.SubjectAlternativeName.RFC822Name"); + } + + String params[] = + com.netscape.cmsutil.util.Utils.getStringArrayFromVector(v); + + return params; + } + + //////////////////// + // Logger methods // + //////////////////// + + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapEnhancedMapper: " + msg); + } +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/LdapSimpleMap.java b/base/common/src/com/netscape/cms/publish/mappers/LdapSimpleMap.java new file mode 100644 index 000000000..642729673 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/LdapSimpleMap.java @@ -0,0 +1,332 @@ +// --- 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.publish.mappers; + +import java.io.IOException; +import java.security.cert.X509Certificate; +import java.util.Locale; +import java.util.Vector; + +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPSearchResults; +import netscape.ldap.LDAPv2; +import netscape.ldap.LDAPv3; +import netscape.security.x509.CertificateExtensions; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CRLImpl; +import netscape.security.x509.X509CertImpl; +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.ldap.ELdapException; +import com.netscape.certsrv.ldap.ELdapServerDownException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapMapper; +import com.netscape.certsrv.request.IRequest; + +/** + * Maps a request to an entry in the LDAP server. + * Takes a dnPattern to form the baseDN from the request attributes + * and certificate subject name.Do a base search for the entry + * in the directory to publish the cert or crl. + * The restriction of this mapper is that the ldap dn components must + * be part of certificate subject name or request attributes or constant. + * + * @version $Revision$, $Date$ + */ +public class LdapSimpleMap implements ILdapMapper, IExtendedPluginInfo { + protected static final String PROP_DNPATTERN = "dnPattern"; + protected String mDnPattern = null; + + private ILogger mLogger = CMS.getLogger(); + private boolean mInited = false; + protected IConfigStore mConfig = null; + + /* the subject DN pattern */ + protected MapDNPattern mPattern = null; + + /* the list of request attriubutes to retrieve*/ + protected String[] mReqAttrs = null; + + /* the list of cert attriubutes to retrieve*/ + protected String[] mCertAttrs = null; + + /* default dn pattern if left blank or not set in the config */ + public static final String DEFAULT_DNPATTERN = + "UID=$req.HTTP_PARAMS.UID, OU=people, O=$subj.o, C=$subj.c"; + + /** + * Constructor. + * + * @param dnPattern The base DN. + */ + public LdapSimpleMap(String dnPattern) { + try { + init(dnPattern); + } catch (EBaseException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("OPERATION_ERROR", e.toString())); + } + + } + + /** + * constructor if initializing from config store. + */ + public LdapSimpleMap() { + } + + public String[] getExtendedPluginInfo(Locale locale) { + String params[] = { + "dnPattern;string;Describes how to form the Ldap Subject name in" + + " the directory. Example 1: 'uid=CertMgr, o=Fedora'. Example 2:" + + " 'uid=$req.HTTP_PARAMS.uid, E=$ext.SubjectAlternativeName.RFC822Name, ou=$subj.ou'. " + + "$req means: take the attribute from the request. " + + "$subj means: take the attribute from the certificate subject name. " + + "$ext means: take the attribute from the certificate extension", + IExtendedPluginInfo.HELP_TOKEN + ";configuration-ldappublish-mapper-simplemapper", + IExtendedPluginInfo.HELP_TEXT + ";Describes how to form the LDAP DN of the entry to publish to" + }; + + return params; + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + /** + * for initializing from config store. + */ + public void init(IConfigStore config) + throws EBaseException { + mConfig = config; + String dnPattern = mConfig.getString(PROP_DNPATTERN); + + init(dnPattern); + } + + /** + * common initialization routine. + */ + protected void init(String dnPattern) + throws EBaseException { + if (mInited) + return; + + mDnPattern = dnPattern; + if (mDnPattern == null || mDnPattern.length() == 0) + mDnPattern = DEFAULT_DNPATTERN; + try { + mPattern = new MapDNPattern(mDnPattern); + } catch (ELdapException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_DN_PATTERN_INIT", + dnPattern, e.toString())); + throw new EBaseException("falied to init with pattern " + + dnPattern + " " + e); + } + + mInited = true; + } + + /** + * Maps a X500 subject name to LDAP entry. + * Uses DN pattern to form a DN for a LDAP base search. + * + * @param conn the LDAP connection. + * @param obj the object to map. + * @exception ELdapException if any LDAP exceptions occured. + */ + public String map(LDAPConnection conn, Object obj) + throws ELdapException { + return map(conn, null, obj); + } + + /** + * Maps a X500 subject name to LDAP entry. + * Uses DN pattern to form a DN for a LDAP base search. + * + * @param conn the LDAP connection. + * @param req the request to map. + * @param obj the object to map. + * @exception ELdapException if any LDAP exceptions occured. + */ + public String map(LDAPConnection conn, IRequest req, Object obj) + throws ELdapException { + if (conn == null) + return null; + String dn = null; + + try { + dn = formDN(req, obj); + if (dn == null) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_DN_NOT_FORMED")); + String s1 = ""; + + if (req != null) + s1 = req.getRequestId().toString(); + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_NO_DN_MATCH", s1)); + } + int scope = LDAPv2.SCOPE_BASE; + String filter = "(objectclass=*)"; + + // search for entry + String[] attrs = new String[] { LDAPv3.NO_ATTRS }; + + log(ILogger.LL_INFO, "searching for dn: " + dn + " filter:" + + filter + " scope: base"); + + LDAPSearchResults results = + conn.search(dn, scope, filter, attrs, false); + LDAPEntry entry = results.next(); + + if (results.hasMoreElements()) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_MORE_THAN_ONE_ENTRY", dn, ((req == null) ? "" : + req.getRequestId().toString()))); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_MORE_THAN_ONE_ENTRY", + ((req == null) ? "" : req.getRequestId().toString()))); + } + if (entry != null) + return entry.getDN(); + else { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_ENTRY_NOT_FOUND", dn, ((req == null) ? "" : req.getRequestId() + .toString()))); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", + "null entry")); + } + } catch (ELdapException e) { + throw e; + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_DN_MAP_EXCEPTION", "", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", e.toString())); + } + } catch (EBaseException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_EXCEPTION_CAUGHT", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", e.toString())); + } + } + + /** + * form a dn from component in the request and cert subject name + * + * @param req The request + * @param obj The certificate or crl + */ + private String formDN(IRequest req, Object obj) throws + EBaseException, ELdapException { + X500Name subjectDN = null; + CertificateExtensions certExt = null; + + try { + X509Certificate cert = (X509Certificate) obj; + + subjectDN = + (X500Name) ((X509Certificate) cert).getSubjectDN(); + + CMS.debug("LdapSimpleMap: cert subject dn:" + subjectDN.toString()); + //certExt = (CertificateExtensions) + // ((X509CertImpl)cert).get(X509CertInfo.EXTENSIONS); + X509CertInfo info = (X509CertInfo) + ((X509CertImpl) cert).get( + X509CertImpl.NAME + "." + X509CertImpl.INFO); + + certExt = (CertificateExtensions) info.get( + CertificateExtensions.NAME); + } catch (java.security.cert.CertificateParsingException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CANT_GET_EXT", e.toString())); + } catch (IOException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CANT_GET_EXT", e.toString())); + } catch (java.security.cert.CertificateException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CANT_GET_EXT", e.toString())); + } catch (ClassCastException e) { + try { + X509CRLImpl crl = (X509CRLImpl) obj; + + subjectDN = + (X500Name) ((X509CRLImpl) crl).getIssuerDN(); + + CMS.debug("LdapSimpleMap: crl issuer dn: " + + subjectDN.toString()); + } catch (ClassCastException ex) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_PUBLISH_OBJ_NOT_SUPPORTED", + ((req == null) ? "" : req.getRequestId().toString()))); + return null; + } + } + try { + String dn = mPattern.formDN(req, subjectDN, certExt); + + return dn; + } catch (ELdapException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CANT_FORM_DN", + ((req == null) ? "" : req.getRequestId().toString()), e.toString())); + throw e; + } + } + + public String getImplName() { + return "LdapSimpleMap"; + } + + public String getDescription() { + return "LdapSimpleMap"; + } + + public Vector getDefaultParams() { + Vector v = new Vector(); + + v.addElement(PROP_DNPATTERN + "="); + return v; + } + + public Vector getInstanceParams() { + Vector v = new Vector(); + + try { + if (mDnPattern == null) { + v.addElement(PROP_DNPATTERN + "="); + } else { + v.addElement(PROP_DNPATTERN + "=" + + mConfig.getString(PROP_DNPATTERN)); + } + } catch (Exception e) { + } + return v; + } + + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapSimpleMapper: " + msg); + } + +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/MapAVAPattern.java b/base/common/src/com/netscape/cms/publish/mappers/MapAVAPattern.java new file mode 100644 index 000000000..7aeb672d0 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/MapAVAPattern.java @@ -0,0 +1,652 @@ +// --- 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.publish.mappers; + +import java.io.IOException; +import java.io.PushbackReader; +import java.io.StringReader; +import java.util.Enumeration; +import java.util.StringTokenizer; +import java.util.Vector; + +import netscape.ldap.LDAPDN; +import netscape.security.x509.AVA; +import netscape.security.x509.CertificateExtensions; +import netscape.security.x509.Extension; +import netscape.security.x509.GeneralName; +import netscape.security.x509.GeneralNameInterface; +import netscape.security.x509.GeneralNames; +import netscape.security.x509.LdapV3DNStrConverter; +import netscape.security.x509.OIDMap; +import netscape.security.x509.SubjectAlternativeNameExtension; +import netscape.security.x509.X500Name; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.publish.ECompSyntaxErr; +import com.netscape.certsrv.request.IRequest; + +/** + * class for parsing a DN pattern used to construct a ldap dn from + * request attributes and cert subject name. + *

+ * + * dnpattern is a string representing a ldap dn pattern to formulate from the certificate subject name attributes and + * request attributes . If empty or not set, the certificate subject name will be used as the ldap dn. + *

+ * + * The syntax is + * + *

+ * 	dnPattern := rdnPattern *[ "," rdnPattern ]
+ * 	rdnPattern := avaPattern *[ "+" avaPattern ]
+ * 		avaPattern := name "=" value | 
+ * 			      name "=" "$subj" "." attrName [ "." attrNumber ] | 
+ * 			      name "=" "$ext" "." extName [ "." nameType ] [ "." attrNumber ] 
+ * 			      name "=" "$req" "." attrName [ "." attrNumber ] | 
+ * 			 	  "$rdn" "." number
+ * 
+ * + *
+ * Example1: cn=Certificate Manager,ou=people,o=mcom.com
+ * cert subject name: dn:  CN=Certificate Manager, OU=people, O=mcom.com
+ * request attributes: uid: cmanager 
+ * 

+ * The dn formulated will be :
+ * CN=Certificate Manager, OU=people, O=mcom.com + *

+ * note: Subordinate ca enrollment will use ca mapper. Use predicate + * to distinguish the ca itself and the subordinates. + * + * Example2: UID=$req.HTTP_PARAMS.uid, OU=$subj.ou, OU=people, , O=mcom.com + * cert subject name: dn: UID=jjames, OU=IS, OU=people, , O=mcom.com + * request attributes: uid: cmanager + *

+ * The dn formulated will be :
+ * UID=jjames, OU=IS, OU=people, O=mcom.com + *

+ * UID = the 'uid' attribute value in the request.
+ * OU = the 'ou' value in the cert subject name.
+ * O = the string mcom.com.
+ *

+ * Example3: UID=$req.HTTP_PARAMS.uid, E=$ext.SubjectAlternativeName.RFC822Name.1, O=mcom.com + * cert subject name: dn: UID=jjames, OU=IS, OU=people, O=mcom.com + * cert subjectAltName is rfc822Name: jjames@mcom.com + * request attributes: uid: cmanager + *

+ * The dn formulated will be :
+ * UID=jjames, E=jjames@mcom.com, O=mcom.com + *

+ * UID = the 'uid' attribute value in the request.
+ * E = The first rfc822name value in the subjAltName extension.
+ * O = the string mcom.com.
+ *

+ *

+ * + * If an request attribute or subject DN component does not exist, the attribute is skipped. There is potential risk + * that a wrong dn will be mapped into. + * + * @version $Revision$, $Date$ + */ +class MapAVAPattern { + + /* the value type of the dn component */ + public static final String TYPE_REQ = "$req"; + public static final String TYPE_SUBJ = "$subj"; + public static final String TYPE_EXT = "$ext"; + public static final String TYPE_RDN = "$rdn"; + public static final String TYPE_CONSTANT = "constant"; + + public static final String[] GENERAL_NAME_TYPE = { "ANY", + "RFC822Name", + "DNSName", + "X400Name", + "DIRECTORYName", + "EDIName", + "URIName", + "IPAddress", + "OIDName" }; + private static final char[] endChars = new char[] { '+', ',' }; + + private static final LdapV3DNStrConverter mLdapDNStrConverter = + new LdapV3DNStrConverter(); + + /* the list of request attributes needed by this AVA */ + protected String[] mReqAttrs = null; + + /* the list of cert attributes needed by this AVA*/ + protected String[] mCertAttrs = null; + + /* value type */ + protected String mType = null; + + /* the attribute in the AVA pair */ + protected String mAttr = null; + + /* value - could be name of a request attribute or + * cert subject dn attribute. */ + protected String mValue = null; + + /* value type - general name type of an extension attribute if any. */ + protected String mGNType = null; + + /* prefix - prefix of a request attribute if any. */ + protected String mPrefix = null; + + /* nth value of the ldap or dn attribute */ + protected int mElement = 0; + + protected String mTestDN = null; + + public MapAVAPattern(String component) + throws ELdapException { + if (component == null || component.length() == 0) + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", component)); + parse(new PushbackReader(new StringReader(component))); + } + + public MapAVAPattern(PushbackReader in) + throws ELdapException { + parse(in); + } + + private void parse(PushbackReader in) + throws ELdapException { + int c; + + // mark ava beginning. + + // skip spaces + //System.out.println("============ AVAPattern Begin ==========="); + //System.out.println("skip spaces"); + + try { + while ((c = in.read()) == ' ' || c == '\t') {//System.out.println("spaces read "+(char)c); + ; + } + } catch (IOException e) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", "All blank")); + } + if (c == -1) + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", "All blank")); + + // $rdn "." number syntax. + + if (c == '$') { + //System.out.println("$rdn syntax"); + mType = TYPE_RDN; + try { + if (in.read() != 'r' || + in.read() != 'd' || + in.read() != 'n' || + in.read() != '.') + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "Invalid $ syntax, expecting $rdn")); + } catch (IOException e) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "Invalid $ syntax, expecting $rdn")); + } + + StringBuffer rdnNumberBuf = new StringBuffer(); + + try { + while ((c = in.read()) != ',' && c != -1 && c != '+') { + //System.out.println("rdnNumber read "+(char)c); + rdnNumberBuf.append((char) c); + } + if (c != -1) // either ',' or '+' + in.unread(c); + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + + String rdnNumber = rdnNumberBuf.toString().trim(); + + if (rdnNumber.length() == 0) + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "$rdn number not set in ava pattern")); + try { + mElement = Integer.parseInt(rdnNumber) - 1; + } catch (NumberFormatException e) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "Invalid $rdn number in ava pattern")); + } + return; + } + + // name "=" ... syntax. + + // read name + //System.out.println("reading name"); + + StringBuffer attrBuf = new StringBuffer(); + + try { + while (c != '=' && c != -1 && c != ',' && c != '+') { + attrBuf.append((char) c); + c = in.read(); + //System.out.println("name read "+(char)c); + } + if (c == ',' || c == '+') + in.unread(c); + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + if (c != '=') + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "Missing \"=\" in ava pattern")); + + // read value + //System.out.println("reading value"); + + // skip spaces + //System.out.println("skip spaces for value"); + try { + while ((c = in.read()) == ' ' || c == '\t') {//System.out.println("spaces2 read "+(char)c); + ; + } + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + if (c == -1) + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "no value after = in ava pattern")); + + if (c == '$') { + // check for $subj $ext or $req + try { + c = in.read(); + //System.out.println("check $dn or $attr read "+(char)c); + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + if (c == -1) + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "expecting $subj or $req in ava pattern")); + if (c == 'r') { + try { + if (in.read() != 'e' || + in.read() != 'q' || + in.read() != '.') + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "expecting $req in ava pattern")); + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + mType = TYPE_REQ; + //System.out.println("---- mtype $req"); + } else if (c == 's') { + try { + if (in.read() != 'u' || + in.read() != 'b' || + in.read() != 'j' || + in.read() != '.') + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "expecting $subj in ava pattern")); + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + mType = TYPE_SUBJ; + //System.out.println("----- mtype $subj"); + } else if (c == 'e') { + try { + if (in.read() != 'x' || + in.read() != 't' || + in.read() != '.') + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "expecting $ext in ava pattern")); + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + mType = TYPE_EXT; + //System.out.println("----- mtype $ext"); + } else { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "unknown keyword. expecting $subj $ext or $req.")); + } + + // get request attr name of subject dn pattern from above. + String attrName = attrBuf.toString().trim(); + + //System.out.println("----- attrName "+attrName); + if (attrName.length() == 0) + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "attribute name expected")); + mAttr = attrName; + + /* + try { + ObjectIdentifier attrOid = + mLdapDNStrConverter.parseAVAKeyword(attrName); + mAttr = mLdapDNStrConverter.encodeOID(attrOid); + //System.out.println("----- mAttr "+mAttr); + } + catch (IOException e) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", e.toString())); + } + */ + + // get request attribute or cert subject dn attribute + + StringBuffer valueBuf = new StringBuffer(); + + try { + while ((c = in.read()) != ',' && + c != -1 && c != '.' && c != '+') { + //System.out.println("mValue read "+(char)c); + valueBuf.append((char) c); + } + if (c == '+' || c == ',') // either ',' or '+' + in.unread(c); // pushback last , or + + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + + mValue = valueBuf.toString().trim(); + if (mValue.length() == 0) + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "$subj or $req attribute name expected")); + //System.out.println("----- mValue "+mValue); + + // get nth dn xxx not nth request attribute . + if (c == '.') { + StringBuffer attrNumberBuf = new StringBuffer(); + + try { + while ((c = in.read()) != ',' && c != -1 && c != '.' + && c != '+') { + //System.out.println("mElement read "+(char)c); + attrNumberBuf.append((char) c); + } + if (c == ',' || c == '+') // either ',' or '+' + in.unread(c); // pushback last , or + + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + String attrNumber = attrNumberBuf.toString().trim(); + + if (attrNumber.length() == 0) + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "nth element $req $ext or $subj expected")); + try { + mElement = Integer.parseInt(attrNumber) - 1; + } catch (NumberFormatException e) { + if (TYPE_REQ.equals(mType)) { + mPrefix = mValue; + mValue = attrNumber; + } else if (TYPE_EXT.equals(mType)) { + mGNType = attrNumber; + } else + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "Invalid format in nth element $req $ext or $subj")); + + // get nth request attribute . + if (c == '.') { + StringBuffer attrNumberBuf1 = new StringBuffer(); + + try { + while ((c = in.read()) != ',' && c != -1 && c != '+') { + //System.out.println("mElement read "+(char)c); + attrNumberBuf1.append((char) c); + } + if (c != -1) // either ',' or '+' + in.unread(c); // pushback last , or + + } catch (IOException ex) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", ex.toString())); + } + String attrNumber1 = attrNumberBuf1.toString().trim(); + + if (attrNumber1.length() == 0) + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "nth element $req expected")); + try { + mElement = Integer.parseInt(attrNumber1) - 1; + } catch (NumberFormatException ex) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "Invalid format in nth element $req.")); + + } + } + } + } + //System.out.println("----- mElement "+mElement); + } else { + // value is constant. treat as regular ava. + mType = TYPE_CONSTANT; + //System.out.println("----- mType constant"); + // parse ava value. + StringBuffer valueBuf = new StringBuffer(); + + valueBuf.append((char) c); + // read forward to get attribute value + try { + while ((c = in.read()) != ',' && + c != -1) { + valueBuf.append((char) c); + } + if (c == '+' || c == ',') { // either ',' or '+' + in.unread(c); // pushback last , or + + } + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + try { + AVA ava = mLdapDNStrConverter.parseAVA(attrBuf + "=" + valueBuf); + + mValue = ava.toLdapDNString(); + //System.out.println("----- mValue "+mValue); + } catch (IOException e) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", e.toString())); + } + } + } + + public String formAVA(IRequest req, X500Name subject, CertificateExtensions extensions) + throws ELdapException { + if (TYPE_CONSTANT.equals(mType)) + return mValue; + + if (TYPE_RDN.equals(mType)) { + String dn = subject.toString(); + + if (mTestDN != null) + dn = mTestDN; + //System.out.println("AVAPattern Using dn "+mTestDN); + String[] rdns = LDAPDN.explodeDN(dn, false); + + if (mElement >= rdns.length) + return null; + return rdns[mElement]; + } + + if (TYPE_SUBJ.equals(mType)) { + String dn = subject.toString(); + + if (mTestDN != null) + dn = mTestDN; + //System.out.println("AVAPattern Using dn "+mTestDN); + String[] rdns = LDAPDN.explodeDN(dn, false); + String value = null; + int nFound = -1; + + for (int i = 0; i < rdns.length; i++) { + String[] avas = explodeRDN(rdns[i]); + + for (int j = 0; j < avas.length; j++) { + String[] exploded = explodeAVA(avas[j]); + + if (exploded[0].equalsIgnoreCase(mValue) && + ++nFound == mElement) { + value = exploded[1]; + break; + } + } + } + if (value == null) { + CMS.debug( + "MapAVAPattern: attr " + mAttr + + " not formed from: cert subject " + + dn + + "-- no subject component : " + mValue); + return null; + } + return mAttr + "=" + value; + } + + if (TYPE_EXT.equals(mType)) { + if (extensions != null) { + for (int i = 0; i < extensions.size(); i++) { + Extension ext = (Extension) + extensions.elementAt(i); + String extName = OIDMap.getName(ext.getExtensionId()); + int index = extName.lastIndexOf("."); + + if (index != -1) + extName = extName.substring(index + 1); + if (extName.equals(mValue)) { + // Check the extensions one by one. + // For now, just give subjectAltName as an example. + if (mValue.equalsIgnoreCase(SubjectAlternativeNameExtension.NAME)) { + try { + GeneralNames subjectNames = + (GeneralNames) + ((SubjectAlternativeNameExtension) ext) + .get(SubjectAlternativeNameExtension.SUBJECT_NAME); + + if (subjectNames.size() == 0) + break; + int j = 0; + + for (Enumeration n = subjectNames.elements(); n.hasMoreElements();) { + GeneralName gn = (GeneralName) n.nextElement(); + String gname = gn.toString(); + + index = gname.indexOf(":"); + if (index == -1) + break; + String gType = gname.substring(0, index); + + if (mGNType != null) { + if (mGNType.equalsIgnoreCase(gType)) { + if (mElement == j) { + gname = + gname.substring(index + 2); + return mAttr + "=" + gname; + } else { + j++; + } + } + } else { + if (mElement == j) { + gname = + gname.substring(index + 2); + return mAttr + "=" + gname; + } + j++; + } + } + } catch (IOException e) { + CMS.debug( + "MapAVAPattern: Publishing attr not formed from extension." + + "-- no attr : " + mValue); + } + } + } + } + } + CMS.debug( + "MapAVAPattern: Publishing:attr not formed from extension " + + "-- no attr : " + mValue); + + return null; + } + + if (TYPE_REQ.equals(mType)) { + // mPrefix and mValue are looked up case-insensitive + String reqAttr = req.getExtDataInString(mPrefix, mValue); + if (reqAttr == null) { + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_REQUEST", + mValue, mAttr)); + } + return mAttr + "=" + reqAttr; + } + + return null; + } + + public String getReqAttr() { + if (TYPE_REQ.equals(mType)) + return mValue; + else + return null; + } + + public String getCertAttr() { + if (TYPE_SUBJ.equals(mType)) + return mValue; + else + return null; + } + + /** + * Explode RDN into AVAs. + * Does not handle escaped '+' + * Java ldap library does not yet support multiple avas per rdn. + * If RDN is malformed returns empty array. + */ + public static String[] explodeRDN(String rdn) { + int plus = rdn.indexOf('+'); + + if (plus == -1) + return new String[] { rdn }; + Vector avas = new Vector(); + StringTokenizer token = new StringTokenizer(rdn, "+"); + + while (token.hasMoreTokens()) + avas.addElement(token.nextToken()); + String[] theAvas = new String[avas.size()]; + + avas.copyInto(theAvas); + return theAvas; + } + + /** + * Explode AVA into name and value. + * Does not handle escaped '=' + * If AVA is malformed empty array is returned. + */ + public static String[] explodeAVA(String ava) { + int equals = ava.indexOf('='); + + if (equals == -1) + return null; + return new String[] { + ava.substring(0, equals).trim(), ava.substring(equals + 1).trim() }; + } +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/MapDNPattern.java b/base/common/src/com/netscape/cms/publish/mappers/MapDNPattern.java new file mode 100644 index 000000000..7a9025b1d --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/MapDNPattern.java @@ -0,0 +1,201 @@ +// --- 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.publish.mappers; + +import java.io.IOException; +import java.io.PushbackReader; +import java.io.StringReader; +import java.util.Vector; + +import netscape.security.x509.CertificateExtensions; +import netscape.security.x509.X500Name; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.request.IRequest; + +/** + * class for parsing a DN pattern used to construct a ldap dn from + * request attributes and cert subject name. + *

+ * + * dnpattern is a string representing a ldap dn pattern to formulate from the certificate subject name attributes and + * request attributes . If empty or not set, the certificate subject name will be used as the ldap dn. + *

+ * + * The syntax is + * + *

+ * 	dnPattern := rdnPattern *[ "," rdnPattern ]
+ * 	rdnPattern := avaPattern *[ "+" avaPattern ]
+ * 		avaPattern := name "=" value | 
+ * 			      name "=" "$subj" "." attrName [ "." attrNumber ] | 
+ * 			      name "=" "$req" "." attrName [ "." attrNumber ] | 
+ *     		 	  "$rdn" "." number
+ * 
+ * + *
+ * Example1: cn=Certificate Manager,ou=people,o=mcom.com
+ * cert subject name: dn:  CN=Certificate Manager, OU=people, O=mcom.com
+ * request attributes: uid: cmanager 
+ * 

+ * The dn formulated will be :
+ * CN=Certificate Manager, OU=people, O=mcom.com + *

+ * note: Subordinate ca enrollment will use ca mapper. Use predicate + * to distinguish the ca itself and the subordinates. + * + * Example2: UID=$req.HTTP_PARAMS.uid, OU=$subj.ou, O=people, , O=mcom.com + * cert subject name: dn: UID=jjames, OU=IS, O=people, , O=mcom.com + * request attributes: uid: cmanager + *

+ * The dn formulated will be :
+ * UID=jjames, OU=IS, OU=people, O=mcom.com + *

+ * UID = the 'uid' attribute value in the request.
+ * OU = the 'ou' value in the cert subject name.
+ * O = the string people, mcom.com.
+ *

+ *

+ * + * If an request attribute or subject DN component does not exist, the attribute is skipped. There is potential risk + * that a wrong dn will be mapped into. + * + * @version $Revision$, $Date$ + */ +public class MapDNPattern { + + /* the list of request attriubutes to retrieve*/ + protected String[] mReqAttrs = null; + + /* the list of cert attriubutes to retrieve*/ + protected String[] mCertAttrs = null; + + /* rdn patterns */ + protected MapRDNPattern[] mRDNPatterns = null; + + /* original pattern string */ + protected String mPatternString = null; + + protected String mTestDN = null; + + /** + * Construct a DN pattern by parsing a pattern string. + * + * @param pattern the DN pattern + * @exception EBaseException If parsing error occurs. + */ + public MapDNPattern(String pattern) + throws ELdapException { + if (pattern == null || pattern.equals("")) { + CMS.debug( + "MapDNPattern: null pattern"); + } else { + mPatternString = pattern; + PushbackReader in = new PushbackReader(new StringReader(pattern)); + + parse(in); + } + } + + public MapDNPattern(PushbackReader in) + throws ELdapException { + parse(in); + } + + private void parse(PushbackReader in) + throws ELdapException { + Vector rdnPatterns = new Vector(); + MapRDNPattern rdnPattern = null; + int lastChar = -1; + + do { + rdnPattern = new MapRDNPattern(in); + rdnPatterns.addElement(rdnPattern); + try { + lastChar = in.read(); + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + } while (lastChar == ','); + + mRDNPatterns = new MapRDNPattern[rdnPatterns.size()]; + rdnPatterns.copyInto(mRDNPatterns); + + Vector reqAttrs = new Vector(); + + for (int i = 0; i < mRDNPatterns.length; i++) { + String[] rdnAttrs = mRDNPatterns[i].getReqAttrs(); + + if (rdnAttrs != null && rdnAttrs.length > 0) + for (int j = 0; j < rdnAttrs.length; j++) + reqAttrs.addElement(rdnAttrs[j]); + } + mReqAttrs = new String[reqAttrs.size()]; + reqAttrs.copyInto(mReqAttrs); + + Vector certAttrs = new Vector(); + + for (int i = 0; i < mRDNPatterns.length; i++) { + String[] rdnAttrs = mRDNPatterns[i].getCertAttrs(); + + if (rdnAttrs != null && rdnAttrs.length > 0) + for (int j = 0; j < rdnAttrs.length; j++) + certAttrs.addElement(rdnAttrs[j]); + } + mCertAttrs = new String[certAttrs.size()]; + certAttrs.copyInto(mCertAttrs); + } + + /** + * Form a Ldap v3 DN string from a request and a cert subject name. + * + * @param req the request for (un)publish + * @param subject the subjectDN of the certificate + * @return Ldap v3 DN string to use for base ldap search. + */ + public String formDN(IRequest req, X500Name subject, CertificateExtensions ext) + throws ELdapException { + StringBuffer formedDN = new StringBuffer(); + + for (int i = 0; i < mRDNPatterns.length; i++) { + if (mTestDN != null) + mRDNPatterns[i].mTestDN = mTestDN; + String rdn = mRDNPatterns[i].formRDN(req, subject, ext); + + if (rdn != null && rdn.length() != 0) { + if (formedDN.length() != 0) + formedDN.append(","); + formedDN.append(rdn); + } else { + throw new ELdapException("pattern not matched"); + } + } + return formedDN.toString(); + } + + public String[] getReqAttrs() { + return (String[]) mReqAttrs.clone(); + } + + public String[] getCertAttrs() { + return (String[]) mCertAttrs.clone(); + } +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/MapRDNPattern.java b/base/common/src/com/netscape/cms/publish/mappers/MapRDNPattern.java new file mode 100644 index 000000000..c1688345b --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/MapRDNPattern.java @@ -0,0 +1,217 @@ +// --- 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.publish.mappers; + +import java.io.IOException; +import java.io.PushbackReader; +import java.io.StringReader; +import java.util.Vector; + +import netscape.security.x509.CertificateExtensions; +import netscape.security.x509.X500Name; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.request.IRequest; + +/** + * class for parsing a DN pattern used to construct a ldap dn from + * request attributes and cert subject name. + *

+ * + * dnpattern is a string representing a ldap dn pattern to formulate from the certificate subject name attributes and + * request attributes . If empty or not set, the certificate subject name will be used as the ldap dn. + *

+ * + * The syntax is + * + *

+ * 	dnPattern := rdnPattern *[ "," rdnPattern ]
+ * 	rdnPattern := avaPattern *[ "+" avaPattern ]
+ * 		avaPattern := name "=" value | 
+ * 			      name "=" "$subj" "." attrName [ "." attrNumber ] | 
+ * 			      name "=" "$req" "." attrName [ "." attrNumber ] | 
+ * 			 	  "$rdn" "." number
+ * 
+ * + *
+ * Example1: cn=Certificate Manager,ou=people,o=mcom.com
+ * cert subject name: dn:  CN=Certificate Manager, OU=people, O=mcom.com
+ * request attributes: uid: cmanager 
+ * 

+ * The dn formulated will be :
+ * CN=Certificate Manager, OU=people, O=mcom.com + *

+ * note: Subordinate ca enrollment will use ca mapper. Use predicate + * to distinguish the ca itself and the subordinates. + * + * Example2: UID=$req.HTTP_PARAMS.uid, OU=$subj.ou, O=people, , O=mcom.com + * cert subject name: dn: UID=jjames, OU=IS, O=people, , O=mcom.com + * request attributes: uid: cmanager + *

+ * The dn formulated will be :
+ * UID=jjames, OU=IS, OU=people, O=mcom.com + *

+ * UID = the 'uid' attribute value in the request.
+ * OU = the 'ou' value in the cert subject name.
+ * O = the string people, mcom.com.
+ *

+ *

+ * + * If an request attribute or subject DN component does not exist, the attribute is skipped.There is potential risk that + * a wrong dn will be mapped into. + * + * @version $Revision$, $Date$ + */ +class MapRDNPattern { + + /* the list of request attributes needed by this RDN */ + protected String[] mReqAttrs = null; + + /* the list of cert attributes needed by this RDN */ + protected String[] mCertAttrs = null; + + /* AVA patterns */ + protected MapAVAPattern[] mAVAPatterns = null; + + /* original pattern string */ + protected String mPatternString = null; + + protected String mTestDN = null; + + /** + * Construct a DN pattern by parsing a pattern string. + * + * @param pattenr the DN pattern + * @exception ELdapException If parsing error occurs. + */ + public MapRDNPattern(String pattern) + throws ELdapException { + if (pattern == null || pattern.equals("")) { + CMS.debug( + "MapDNPattern: null pattern"); + } else { + mPatternString = pattern; + PushbackReader in = new PushbackReader(new StringReader(pattern)); + + parse(in); + } + } + + /** + * Construct a DN pattern from a input stream of pattern + */ + public MapRDNPattern(PushbackReader in) + throws ELdapException { + parse(in); + } + + private void parse(PushbackReader in) + throws ELdapException { + //System.out.println("_________ begin rdn _________"); + Vector avaPatterns = new Vector(); + MapAVAPattern avaPattern = null; + int lastChar; + + do { + avaPattern = new MapAVAPattern(in); + avaPatterns.addElement(avaPattern); + //System.out.println("added AVAPattern"+ + //" mType "+avaPattern.mType+ + //" mAttr "+avaPattern.mAttr+ + //" mValue "+avaPattern.mValue+ + //" mElement "+avaPattern.mElement); + try { + lastChar = in.read(); + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + } while (lastChar == '+'); + + if (lastChar != -1) { + try { + in.unread(lastChar); // pushback last , + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + } + + mAVAPatterns = new MapAVAPattern[avaPatterns.size()]; + avaPatterns.copyInto(mAVAPatterns); + + Vector reqAttrs = new Vector(); + + for (int i = 0; i < mAVAPatterns.length; i++) { + String avaAttr = mAVAPatterns[i].getReqAttr(); + + if (avaAttr == null || avaAttr.length() == 0) + continue; + reqAttrs.addElement(avaAttr); + } + mReqAttrs = new String[reqAttrs.size()]; + reqAttrs.copyInto(mReqAttrs); + + Vector certAttrs = new Vector(); + + for (int i = 0; i < mAVAPatterns.length; i++) { + String avaAttr = mAVAPatterns[i].getCertAttr(); + + if (avaAttr == null || avaAttr.length() == 0) + continue; + certAttrs.addElement(avaAttr); + } + mCertAttrs = new String[certAttrs.size()]; + certAttrs.copyInto(mCertAttrs); + } + + /** + * Form a Ldap v3 DN string from a request and a cert subject name. + * + * @param req the request for (un)publish + * @param subject the subjectDN of the certificate + * @return Ldap v3 DN string to use for base ldap search. + */ + public String formRDN(IRequest req, X500Name subject, CertificateExtensions ext) + throws ELdapException { + StringBuffer formedRDN = new StringBuffer(); + + for (int i = 0; i < mAVAPatterns.length; i++) { + if (mTestDN != null) + mAVAPatterns[i].mTestDN = mTestDN; + String ava = mAVAPatterns[i].formAVA(req, subject, ext); + + if (ava != null && ava.length() > 0) { + if (formedRDN.length() != 0) + formedRDN.append("+"); + formedRDN.append(ava); + } + } + //System.out.println("formed RDN "+formedRDN.toString()); + return formedRDN.toString(); + } + + public String[] getReqAttrs() { + return (String[]) mReqAttrs.clone(); + } + + public String[] getCertAttrs() { + return (String[]) mCertAttrs.clone(); + } +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/NoMap.java b/base/common/src/com/netscape/cms/publish/mappers/NoMap.java new file mode 100644 index 000000000..155c54ce0 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/NoMap.java @@ -0,0 +1,104 @@ +// --- 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.publish.mappers; + +import java.util.Locale; +import java.util.Vector; + +import netscape.ldap.LDAPConnection; + +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.base.IExtendedPluginInfo; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.publish.ILdapMapper; +import com.netscape.certsrv.request.IRequest; + +/** + * No Map + * + * @version $Revision$, $Date$ + */ +public class NoMap implements ILdapMapper, IExtendedPluginInfo { + + public IConfigStore mConfig = null; + + /** + * constructor if initializing from config store. + */ + public NoMap() { + } + + public String[] getExtendedPluginInfo(Locale locale) { + String params[] = { + IExtendedPluginInfo.HELP_TOKEN + ";configuration-ldappublish-mapper-simplemapper", + IExtendedPluginInfo.HELP_TEXT + ";Describes how to form the name of the entry to publish to" + }; + + return params; + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + /** + * for initializing from config store. + */ + public void init(IConfigStore config) + throws EBaseException { + mConfig = config; + } + + /** + * Maps a X500 subject name to LDAP entry. + * Uses DN pattern to form a DN for a LDAP base search. + * + * @param conn the LDAP connection. + * @param obj the object to map. + * @exception ELdapException if any LDAP exceptions occured. + */ + public String map(LDAPConnection conn, Object obj) + throws ELdapException { + return null; + } + + public String map(LDAPConnection conn, IRequest req, Object obj) + throws ELdapException { + return null; + } + + public String getImplName() { + return "NoMap"; + } + + public String getDescription() { + return "NoMap"; + } + + public Vector getDefaultParams() { + Vector v = new Vector(); + return v; + } + + public Vector getInstanceParams() { + Vector v = new Vector(); + return v; + } + +} diff --git a/base/common/src/com/netscape/cms/publish/publishers/FileBasedPublisher.java b/base/common/src/com/netscape/cms/publish/publishers/FileBasedPublisher.java new file mode 100644 index 000000000..cb13b2452 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/publishers/FileBasedPublisher.java @@ -0,0 +1,443 @@ +// --- 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.publish.publishers; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.math.BigInteger; +import java.security.cert.CRLException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; +import java.util.Locale; +import java.util.TimeZone; +import java.util.Vector; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import netscape.ldap.LDAPConnection; + +import org.mozilla.jss.util.Base64OutputStream; + +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.ldap.ELdapException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapPublisher; +import com.netscape.cmsutil.util.Utils; + +/** + * This publisher writes certificate and CRL into + * a directory. + * + * @version $Revision$, $Date$ + */ +public class FileBasedPublisher implements ILdapPublisher, IExtendedPluginInfo { + private static final String PROP_DIR = "directory"; + private static final String PROP_DER = "Filename.der"; + private static final String PROP_B64 = "Filename.b64"; + private static final String PROP_LNK = "latestCrlLink"; + private static final String PROP_GMT = "timeStamp"; + private static final String PROP_EXT = "crlLinkExt"; + private static final String PROP_ZIP = "zipCRLs"; + private static final String PROP_LEV = "zipLevel"; + private IConfigStore mConfig = null; + private String mDir = null; + private ILogger mLogger = CMS.getLogger(); + private String mCrlIssuingPointId; + protected boolean mDerAttr = true; + protected boolean mB64Attr = false; + protected boolean mLatestCRL = false; + protected boolean mZipCRL = false; + protected String mTimeStamp = null; + protected String mLinkExt = null; + protected int mZipLevel = 9; + + public void setIssuingPointId(String crlIssuingPointId) { + mCrlIssuingPointId = crlIssuingPointId; + } + + /** + * Returns the implementation name. + */ + public String getImplName() { + return "FileBasedPublisher"; + } + + /** + * Returns the description of the ldap publisher. + */ + + public String getDescription() { + return "This publisher writes the Certificates and CRLs into files."; + } + + public String[] getExtendedPluginInfo(Locale locale) { + String[] params = { + PROP_DIR + + ";string;Directory in which to put the files (absolute path or relative path to cert-* instance directory).", + PROP_DER + ";boolean;Store certificates or CRLs into *.der files.", + PROP_B64 + ";boolean;Store certificates or CRLs into *.b64 files.", + PROP_GMT + + ";choice(LocalTime,GMT);Use local time or GMT to time stamp CRL file name with CRL's 'thisUpdate' field.", + PROP_LNK + + ";boolean;Generate link to the latest binary CRL. It requires '" + PROP_DER + + "' to be enabled.", + PROP_EXT + + ";string;Name extension used by link to the latest CRL. Default name extension is 'der'.", + PROP_ZIP + ";boolean;Generate compressed CRLs.", + PROP_LEV + ";choice(0,1,2,3,4,5,6,7,8,9);Set compression level from 0 to 9.", + IExtendedPluginInfo.HELP_TOKEN + + ";configuration-ldappublish-publisher-filepublisher", + IExtendedPluginInfo.HELP_TEXT + + + ";Stores the certificates or CRLs into files. Certificate is named as cert-.der or *.b64, and CRL is named as -.der or *.b64." + }; + + return params; + } + + /** + * Returns the current instance parameters. + */ + public Vector getInstanceParams() { + Vector v = new Vector(); + String dir = ""; + String ext = ""; + + try { + dir = mConfig.getString(PROP_DIR); + } catch (EBaseException e) { + } + try { + ext = mConfig.getString(PROP_EXT); + } catch (EBaseException e) { + } + try { + mTimeStamp = mConfig.getString(PROP_GMT); + } catch (EBaseException e) { + } + try { + mZipLevel = mConfig.getInteger(PROP_LEV, 9); + } catch (EBaseException e) { + } + try { + if (mTimeStamp == null || (!mTimeStamp.equals("GMT"))) + mTimeStamp = "LocalTime"; + v.addElement(PROP_DIR + "=" + dir); + v.addElement(PROP_DER + "=" + mConfig.getBoolean(PROP_DER, true)); + v.addElement(PROP_B64 + "=" + mConfig.getBoolean(PROP_B64, false)); + v.addElement(PROP_GMT + "=" + mTimeStamp); + v.addElement(PROP_LNK + "=" + mConfig.getBoolean(PROP_LNK, false)); + v.addElement(PROP_EXT + "=" + ext); + v.addElement(PROP_ZIP + "=" + mConfig.getBoolean(PROP_ZIP, false)); + v.addElement(PROP_LEV + "=" + mZipLevel); + } catch (Exception e) { + } + return v; + } + + /** + * Returns the initial default parameters. + */ + public Vector getDefaultParams() { + Vector v = new Vector(); + + v.addElement(PROP_DIR + "="); + v.addElement(PROP_DER + "=true"); + v.addElement(PROP_B64 + "=false"); + v.addElement(PROP_GMT + "=LocalTime"); + v.addElement(PROP_LNK + "=false"); + v.addElement(PROP_EXT + "="); + v.addElement(PROP_ZIP + "=false"); + v.addElement(PROP_LEV + "=9"); + return v; + } + + /** + * Initializes this plugin. + */ + public void init(IConfigStore config) { + mConfig = config; + String dir = null; + + try { + dir = mConfig.getString(PROP_DIR, null); + mDerAttr = mConfig.getBoolean(PROP_DER, true); + mB64Attr = mConfig.getBoolean(PROP_B64, false); + mTimeStamp = mConfig.getString(PROP_GMT, "LocalTime"); + mLatestCRL = mConfig.getBoolean(PROP_LNK, false); + mLinkExt = mConfig.getString(PROP_EXT, null); + mZipCRL = mConfig.getBoolean(PROP_ZIP, false); + mZipLevel = mConfig.getInteger(PROP_LEV, 9); + } catch (EBaseException e) { + } + if (dir == null) { + throw new RuntimeException("No Directory Specified"); + } + + // convert to forward slash + dir = dir.replace('\\', '/'); + config.putString(PROP_DIR, dir); + + File dirCheck = new File(dir); + + if (dirCheck.isDirectory()) { + mDir = dir; + } else { + // maybe it is relative path + String mInstanceRoot = null; + + try { + mInstanceRoot = CMS.getConfigStore().getString("instanceRoot"); + } catch (Exception e) { + throw new RuntimeException("Invalid Instance Dir " + e); + } + dirCheck = new File(mInstanceRoot + + File.separator + dir); + if (dirCheck.isDirectory()) { + mDir = mInstanceRoot + File.separator + dir; + } else { + throw new RuntimeException("Invalid Directory " + dir); + } + } + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + private String[] getCrlNamePrefix(X509CRL crl, boolean useGMT) { + String[] namePrefix = { "crl", "crl" }; + + if (mCrlIssuingPointId != null && mCrlIssuingPointId.length() != 0) { + namePrefix[0] = mCrlIssuingPointId; + namePrefix[1] = mCrlIssuingPointId; + } + java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("yyyyMMdd-HHmmss"); + TimeZone tz = TimeZone.getTimeZone("GMT"); + if (useGMT) + format.setTimeZone(tz); + String timeStamp = format.format(crl.getThisUpdate()).toString(); + namePrefix[0] += "-" + timeStamp; + if (((netscape.security.x509.X509CRLImpl) crl).isDeltaCRL()) { + namePrefix[0] += "-delta"; + namePrefix[1] += "-delta"; + } + + return namePrefix; + } + + private void createLink(String linkName, String fileName) { + String cmd = "ln -s " + fileName + " " + linkName + ".new"; + if (com.netscape.cmsutil.util.Utils.exec(cmd)) { + File oldLink = new File(linkName + ".old"); + if (oldLink.exists()) { // remove old link if exists + oldLink.delete(); + } + File link = new File(linkName); + if (link.exists()) { // current link becomes an old link + link.renameTo(new File(linkName + ".old")); + } + File newLink = new File(linkName + ".new"); + if (newLink.exists()) { // new link becomes current link + newLink.renameTo(new File(linkName)); + } + oldLink = new File(linkName + ".old"); + if (oldLink.exists()) { // remove a new old link + oldLink.delete(); + } + } else { + CMS.debug("FileBasedPublisher: createLink: '" + cmd + "' --- failed"); + } + } + + /** + * Publishs a object to the ldap directory. + * + * @param conn a Ldap connection + * (null if LDAP publishing is not enabled) + * @param dn dn of the ldap entry to publish cert + * (null if LDAP publishing is not enabled) + * @param object object to publish + * (java.security.cert.X509Certificate or, + * java.security.cert.X509CRL) + */ + public void publish(LDAPConnection conn, String dn, Object object) + throws ELdapException { + CMS.debug("FileBasedPublisher: publish"); + try { + if (object instanceof X509Certificate) { + X509Certificate cert = (X509Certificate) object; + BigInteger sno = cert.getSerialNumber(); + String name = mDir + + File.separator + "cert-" + + sno.toString(); + if (mDerAttr) { + String fileName = name + ".der"; + FileOutputStream fos = new FileOutputStream(fileName); + fos.write(cert.getEncoded()); + fos.close(); + } + if (mB64Attr) { + String fileName = name + ".b64"; + FileOutputStream fos = new FileOutputStream(fileName); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + Base64OutputStream b64 = + new Base64OutputStream(new PrintStream(new FilterOutputStream(output))); + b64.write(cert.getEncoded()); + b64.flush(); + (new PrintStream(fos)).print(output.toString("8859_1")); + fos.close(); + } + } else if (object instanceof X509CRL) { + X509CRL crl = (X509CRL) object; + String[] namePrefix = getCrlNamePrefix(crl, mTimeStamp.equals("GMT")); + String baseName = mDir + File.separator + namePrefix[0]; + String tempFile = baseName + ".temp"; + FileOutputStream fos; + ZipOutputStream zos; + byte[] encodedArray = null; + File destFile = null; + String destName = null; + File renameFile = null; + + if (mDerAttr) { + fos = new FileOutputStream(tempFile); + encodedArray = crl.getEncoded(); + fos.write(encodedArray); + fos.close(); + if (mZipCRL) { + zos = new ZipOutputStream(new FileOutputStream(baseName + ".zip")); + zos.setLevel(mZipLevel); + zos.putNextEntry(new ZipEntry(baseName + ".der")); + zos.write(encodedArray, 0, encodedArray.length); + zos.closeEntry(); + zos.close(); + } + destName = baseName + ".der"; + destFile = new File(destName); + + if (destFile.exists()) + destFile.delete(); + renameFile = new File(tempFile); + renameFile.renameTo(destFile); + + if (mLatestCRL) { + String linkExt = "."; + if (mLinkExt != null && mLinkExt.length() > 0) { + linkExt += mLinkExt; + } else { + linkExt += "der"; + } + String linkName = mDir + File.separator + namePrefix[1] + linkExt; + createLink(linkName, destName); + if (mZipCRL) { + linkName = mDir + File.separator + namePrefix[1] + ".zip"; + createLink(linkName, baseName + ".zip"); + } + } + } + + // output base64 file + if (mB64Attr == true) { + if (encodedArray == null) + encodedArray = crl.getEncoded(); + + fos = new FileOutputStream(tempFile); + fos.write(Utils.base64encode(encodedArray).getBytes()); + fos.close(); + destName = baseName + ".b64"; + destFile = new File(destName); + + if (destFile.exists()) + destFile.delete(); + renameFile = new File(tempFile); + renameFile.renameTo(destFile); + } + } + } catch (IOException e) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_OTHER, + ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_FILE_PUBLISHER_ERROR", e.toString())); + } catch (CertificateEncodingException e) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_OTHER, + ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_FILE_PUBLISHER_ERROR", e.toString())); + } catch (CRLException e) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_OTHER, + ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_FILE_PUBLISHER_ERROR", e.toString())); + } + } + + /** + * Unpublishs a object to the ldap directory. + * + * @param conn the Ldap connection + * (null if LDAP publishing is not enabled) + * @param dn dn of the ldap entry to unpublish cert + * (null if LDAP publishing is not enabled) + * @param object object to unpublish + * (java.security.cert.X509Certificate) + */ + public void unpublish(LDAPConnection conn, String dn, Object object) + throws ELdapException { + CMS.debug("FileBasedPublisher: unpublish"); + String name = mDir + File.separator; + String fileName; + + if (object instanceof X509Certificate) { + X509Certificate cert = (X509Certificate) object; + BigInteger sno = cert.getSerialNumber(); + name += "cert-" + sno.toString(); + } else if (object instanceof X509CRL) { + X509CRL crl = (X509CRL) object; + String[] namePrefix = getCrlNamePrefix(crl, mTimeStamp.equals("GMT")); + name += namePrefix[0]; + + fileName = name + ".zip"; + File f = new File(fileName); + f.delete(); + } + fileName = name + ".der"; + File f = new File(fileName); + f.delete(); + + fileName = name + ".b64"; + f = new File(fileName); + f.delete(); + } + + /** + * returns the Der attribute where it'll be published. + */ + public boolean getDerAttr() { + return mDerAttr; + } + + /** + * returns the B64 attribute where it'll be published. + */ + public boolean getB64Attr() { + return mB64Attr; + } +} diff --git a/base/common/src/com/netscape/cms/publish/publishers/LdapCaCertPublisher.java b/base/common/src/com/netscape/cms/publish/publishers/LdapCaCertPublisher.java new file mode 100644 index 000000000..e47318b76 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/publishers/LdapCaCertPublisher.java @@ -0,0 +1,421 @@ +// --- 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.publish.publishers; + +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.Locale; +import java.util.Vector; + +import netscape.ldap.LDAPAttribute; +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPModification; +import netscape.ldap.LDAPModificationSet; +import netscape.ldap.LDAPSSLSocketFactoryExt; +import netscape.ldap.LDAPSearchResults; +import netscape.ldap.LDAPv2; + +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.ldap.ELdapException; +import com.netscape.certsrv.ldap.ELdapServerDownException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapPublisher; + +/** + * Interface for publishing a CA certificate to + * + * @version $Revision$, $Date$ + */ +public class LdapCaCertPublisher + implements ILdapPublisher, IExtendedPluginInfo { + public static final String LDAP_CACERT_ATTR = "caCertificate;binary"; + public static final String LDAP_CA_OBJECTCLASS = "pkiCA"; + public static final String LDAP_ARL_ATTR = "authorityRevocationList;binary"; + public static final String LDAP_CRL_ATTR = "certificateRevocationList;binary"; + + protected String mCaCertAttr = LDAP_CACERT_ATTR; + protected String mCaObjectclass = LDAP_CA_OBJECTCLASS; + protected String mObjAdded = ""; + protected String mObjDeleted = ""; + + private ILogger mLogger = CMS.getLogger(); + private boolean mInited = false; + protected IConfigStore mConfig = null; + private String mcrlIssuingPointId; + + /** + * constructor constructs default values. + */ + public LdapCaCertPublisher() { + } + + public String[] getExtendedPluginInfo(Locale locale) { + String s[] = { + "caCertAttr;string;Name of Ldap attribute in which to store certificate", + "caObjectClass;string;The name of the objectclasses which should be " + + "added to this entry, if they do not already exist. This can be " + + "'certificationAuthority' (if using RFC 2256) or 'pkiCA' (if using RFC 4523)", + IExtendedPluginInfo.HELP_TOKEN + + ";configuration-ldappublish-publisher-cacertpublisher", + IExtendedPluginInfo.HELP_TEXT + + ";This plugin knows how to publish the CA cert to " + + "'certificateAuthority' and 'pkiCA' -type entries" + }; + + return s; + } + + public String getImplName() { + return "LdapCaCertPublisher"; + } + + public String getDescription() { + return "LdapCaCertPublisher"; + } + + public Vector getInstanceParams() { + Vector v = new Vector(); + + v.addElement("caCertAttr=" + mCaCertAttr); + v.addElement("caObjectClass=" + mCaObjectclass); + return v; + } + + public Vector getDefaultParams() { + Vector v = new Vector(); + + v.addElement("caCertAttr=" + mCaCertAttr); + v.addElement("caObjectClass=" + mCaObjectclass); + return v; + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + public void init(IConfigStore config) + throws EBaseException { + if (mInited) + return; + mConfig = config; + mCaCertAttr = mConfig.getString("caCertAttr", LDAP_CACERT_ATTR); + mCaObjectclass = mConfig.getString("caObjectClass", + LDAP_CA_OBJECTCLASS); + mObjAdded = mConfig.getString("caObjectClassAdded", ""); + mObjDeleted = mConfig.getString("caObjectClassDeleted", ""); + mInited = true; + } + + // don't think anyone would ever use this but just in case. + public LdapCaCertPublisher(String caCertAttr, String caObjectclass) { + mCaCertAttr = caCertAttr; + mCaObjectclass = caObjectclass; + mInited = true; + } + + /** + * Gets the CA object class to convert to. + */ + public String getCAObjectclass() { + return mCaObjectclass; + } + + /** + * returns the ca cert attribute where it'll be published. + */ + public String getCaCertAttrName() { + return mCaCertAttr; + } + + /** + * publish a CA certificate + * Adds the cert to the multi-valued certificate attribute as a + * DER encoded binary blob. Does not check if cert already exists. + * Converts the class to certificateAuthority. + * + * @param conn the LDAP connection + * @param dn dn of the entry to publish the certificate + * @param certObj the certificate object. + */ + public void publish(LDAPConnection conn, String dn, Object certObj) + throws ELdapException { + if (conn == null) { + log(ILogger.LL_INFO, "LdapCaCertPublisher: no LDAP connection"); + return; + } + + try { + mCaCertAttr = mConfig.getString("caCertAttr", LDAP_CACERT_ATTR); + mCaObjectclass = mConfig.getString("caObjectClass", LDAP_CA_OBJECTCLASS); + } catch (EBaseException e) { + } + + // Bugscape #56124 - support multiple publishing directory + // see if we should create local connection + LDAPConnection altConn = null; + try { + String host = mConfig.getString("host", null); + String port = mConfig.getString("port", null); + if (host != null && port != null) { + int portVal = Integer.parseInt(port); + int version = Integer.parseInt(mConfig.getString("version", "2")); + String cert_nick = mConfig.getString("clientCertNickname", null); + LDAPSSLSocketFactoryExt sslSocket = null; + if (cert_nick != null) { + sslSocket = CMS.getLdapJssSSLSocketFactory(cert_nick); + } + String mgr_dn = mConfig.getString("bindDN", null); + String mgr_pwd = mConfig.getString("bindPWD", null); + + altConn = CMS.getBoundConnection(host, portVal, + version, + sslSocket, mgr_dn, mgr_pwd); + conn = altConn; + } + } catch (LDAPException e) { + CMS.debug("Failed to create alt connection " + e); + } catch (EBaseException e) { + CMS.debug("Failed to create alt connection " + e); + } + + if (!(certObj instanceof X509Certificate)) + throw new IllegalArgumentException("Illegal arg to publish"); + + X509Certificate cert = (X509Certificate) certObj; + + try { + byte[] certEnc = cert.getEncoded(); + + /* search for attribute names to determine existence of attributes */ + LDAPSearchResults res = + conn.search(dn, LDAPv2.SCOPE_BASE, "(objectclass=*)", + new String[] { LDAP_CRL_ATTR, LDAP_ARL_ATTR }, true); + LDAPEntry entry = res.next(); + LDAPAttribute arls = entry.getAttribute(LDAP_ARL_ATTR); + LDAPAttribute crls = entry.getAttribute(LDAP_CRL_ATTR); + + /* search for objectclass and caCert values */ + LDAPSearchResults res1 = + conn.search(dn, LDAPv2.SCOPE_BASE, "(objectclass=*)", + new String[] { "objectclass", mCaCertAttr }, false); + LDAPEntry entry1 = res1.next(); + LDAPAttribute ocs = entry1.getAttribute("objectclass"); + LDAPAttribute certs = entry1.getAttribute(mCaCertAttr); + + boolean hasCert = + LdapUserCertPublisher.ByteValueExists(certs, certEnc); + + LDAPModificationSet modSet = new LDAPModificationSet(); + + if (hasCert) { + log(ILogger.LL_INFO, "publish: CA " + dn + " already has Cert"); + } else { + /* + fix for 360458 - if no cert, use add, if has cert but + not equal, use replace + */ + if (certs == null) { + modSet.add(LDAPModification.ADD, + new LDAPAttribute(mCaCertAttr, certEnc)); + log(ILogger.LL_INFO, "CA cert added"); + } else { + modSet.add(LDAPModification.REPLACE, + new LDAPAttribute(mCaCertAttr, certEnc)); + log(ILogger.LL_INFO, "CA cert replaced"); + } + } + + String[] oclist = mCaObjectclass.split(","); + + boolean attrsAdded = false; + for (int i = 0; i < oclist.length; i++) { + String oc = oclist[i].trim(); + boolean hasoc = LdapUserCertPublisher.StringValueExists(ocs, oc); + if (!hasoc) { + log(ILogger.LL_INFO, "adding CA objectclass " + oc + " to " + dn); + modSet.add(LDAPModification.ADD, + new LDAPAttribute("objectclass", oc)); + + if ((!attrsAdded) && oc.equalsIgnoreCase("certificationAuthority")) { + // add MUST attributes + if (arls == null) + modSet.add(LDAPModification.ADD, + new LDAPAttribute(LDAP_ARL_ATTR, "")); + if (crls == null) + modSet.add(LDAPModification.ADD, + new LDAPAttribute(LDAP_CRL_ATTR, "")); + attrsAdded = true; + } + } + } + + // delete objectclasses that have been deleted from config + String[] delList = mObjDeleted.split(","); + if (delList.length > 0) { + for (int i = 0; i < delList.length; i++) { + String deloc = delList[i].trim(); + boolean hasoc = LdapUserCertPublisher.StringValueExists(ocs, deloc); + boolean match = false; + for (int j = 0; j < oclist.length; j++) { + if ((oclist[j].trim()).equals(deloc)) { + match = true; + break; + } + } + if (!match && hasoc) { + log(ILogger.LL_INFO, "deleting CA objectclass " + deloc + " from " + dn); + modSet.add(LDAPModification.DELETE, + new LDAPAttribute("objectclass", deloc)); + } + } + } + + // reset mObjAdded and mObjDeleted, if needed + if ((!mObjAdded.equals("")) || (!mObjDeleted.equals(""))) { + mObjAdded = ""; + mObjDeleted = ""; + mConfig.putString("caObjectClassAdded", ""); + mConfig.putString("caObjectClassDeleted", ""); + try { + mConfig.commit(false); + } catch (Exception e) { + log(ILogger.LL_INFO, "Failure in updating mObjAdded and mObjDeleted"); + } + } + + if (modSet.size() > 0) + conn.modify(dn, modSet); + } catch (CertificateEncodingException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CANT_DECODE_CERT", dn)); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_GET_DER_ENCODED_CERT_FAILED", e.toString())); + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_PUBLISHER_EXCEPTION", "", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_PUBLISH_CACERT_ERROR", e.toString())); + } + } finally { + if (altConn != null) { + try { + altConn.disconnect(); + } catch (LDAPException e) { + // safely ignored + } + } + } + + return; + } + + /** + * deletes the certificate from CA's certificate attribute. + * if it's the last cert will also remove the certificateAuthority + * objectclass. + */ + public void unpublish(LDAPConnection conn, String dn, Object certObj) + throws ELdapException { + if (!(certObj instanceof X509Certificate)) + throw new IllegalArgumentException("Illegal arg to publish"); + + X509Certificate cert = (X509Certificate) certObj; + + try { + mCaCertAttr = mConfig.getString("caCertAttr", LDAP_CACERT_ATTR); + mCaObjectclass = mConfig.getString("caObjectClass", LDAP_CA_OBJECTCLASS); + } catch (EBaseException e) { + } + + try { + byte[] certEnc = cert.getEncoded(); + + LDAPSearchResults res = + conn.search(dn, LDAPv2.SCOPE_BASE, "(objectclass=*)", + new String[] { mCaCertAttr, "objectclass" }, false); + + LDAPEntry entry = res.next(); + LDAPAttribute certs = entry.getAttribute(mCaCertAttr); + LDAPAttribute ocs = entry.getAttribute("objectclass"); + + boolean hasCert = + LdapUserCertPublisher.ByteValueExists(certs, certEnc); + + if (!hasCert) { + log(ILogger.LL_INFO, "unpublish: " + dn + " has not cert already"); + //throw new ELdapException( + // LdapResources.ALREADY_UNPUBLISHED_1, dn); + return; + } + + LDAPModificationSet modSet = new LDAPModificationSet(); + + modSet.add(LDAPModification.DELETE, + new LDAPAttribute(mCaCertAttr, certEnc)); + if (certs.size() == 1) { + // if last ca cert, remove oc also. + + String[] oclist = mCaObjectclass.split(","); + for (int i = 0; i < oclist.length; i++) { + String oc = oclist[i].trim(); + boolean hasOC = LdapUserCertPublisher.StringValueExists(ocs, oc); + if (hasOC) { + log(ILogger.LL_INFO, "unpublish: deleting CA oc" + oc + " from " + dn); + modSet.add(LDAPModification.DELETE, + new LDAPAttribute("objectclass", oc)); + } + } + } + conn.modify(dn, modSet); + } catch (CertificateEncodingException e) { + CMS.debug("LdapCaCertPublisher: unpublish: Cannot decode cert for " + dn); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_GET_DER_ENCODED_CERT_FAILED", e.toString())); + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_UNPUBLISH_ERROR", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_UNPUBLISH_CACERT_ERROR", e.toString())); + } + } + return; + } + + /** + * handy routine for logging in this class. + */ + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapCaPublisher: " + msg); + } + +} diff --git a/base/common/src/com/netscape/cms/publish/publishers/LdapCertSubjPublisher.java b/base/common/src/com/netscape/cms/publish/publishers/LdapCertSubjPublisher.java new file mode 100644 index 000000000..9000f6834 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/publishers/LdapCertSubjPublisher.java @@ -0,0 +1,345 @@ +// --- 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.publish.publishers; + +import java.io.IOException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Enumeration; +import java.util.Vector; + +import netscape.ldap.LDAPAttribute; +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPModification; +import netscape.ldap.LDAPModificationSet; +import netscape.ldap.LDAPSearchResults; +import netscape.ldap.LDAPv2; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CertImpl; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.ldap.ELdapServerDownException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapPublisher; + +/** + * Interface for mapping a X509 certificate to a LDAP entry + * Publishes a certificate as binary and its subject name. + * there is one subject name value for each certificate. + * + * @version $Revision$, $Date$ + */ +public class LdapCertSubjPublisher implements ILdapPublisher { + public static final String LDAP_CERTSUBJNAME_ATTR = "certSubjectName"; + protected String mCertAttr = LdapUserCertPublisher.LDAP_USERCERT_ATTR; + protected String mSubjNameAttr = LDAP_CERTSUBJNAME_ATTR; + + private ILogger mLogger = CMS.getLogger(); + private boolean mInited = false; + protected IConfigStore mConfig = null; + + /** + * constructor using default certificate subject name and attribute for + * publishing subject name. + */ + public LdapCertSubjPublisher() { + } + + public String getImplName() { + return "LdapCertSubjPublisher"; + } + + public String getDescription() { + return "LdapCertSubjPublisher"; + } + + public Vector getInstanceParams() { + Vector v = new Vector(); + + v.addElement("certAttr=" + mCertAttr); + v.addElement("subjectNameAttr=" + mSubjNameAttr); + return v; + } + + public Vector getDefaultParams() { + Vector v = new Vector(); + + v.addElement("certAttr=" + mCertAttr); + v.addElement("subjectNameAttr=" + mSubjNameAttr); + return v; + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + public void init(IConfigStore config) + throws EBaseException { + if (mInited) + return; + mConfig = config; + mCertAttr = mConfig.getString("certAttr", + LdapUserCertPublisher.LDAP_USERCERT_ATTR); + mSubjNameAttr = mConfig.getString("certSubjectName", + LDAP_CERTSUBJNAME_ATTR); + mInited = true; + } + + /** + * constrcutor using specified certificate attribute and + * certificate subject name attribute. + */ + public LdapCertSubjPublisher(String certAttr, String subjNameAttr) { + mCertAttr = certAttr; + mSubjNameAttr = subjNameAttr; + } + + public String getCertAttr() { + return mCertAttr; + } + + public String getSubjNameAttr() { + return mSubjNameAttr; + } + + public void setSubjNameAttr(String subjNameAttr) { + mSubjNameAttr = subjNameAttr; + } + + public void setCertAttr(String certAttr) { + mCertAttr = certAttr; + } + + /** + * publish a user certificate + * Adds the cert to the multi-valued certificate attribute as a + * DER encoded binary blob. Does not check if cert already exists. + * Then adds the subject name of the cert to the subject name attribute. + * + * @param conn the LDAP connection + * @param dn dn of the entry to publish the certificate + * @param certObj the certificate object. + * @exception ELdapException if cert or subject name already exists, + * if cert encoding fails, if getting cert subject name fails. + * Use ELdapException.getException() to find underlying exception. + */ + public void publish(LDAPConnection conn, String dn, Object certObj) + throws ELdapException { + if (conn == null) { + log(ILogger.LL_INFO, "LdapCertSubjPublisher: no LDAP connection"); + return; + } + + if (!(certObj instanceof X509Certificate)) + throw new IllegalArgumentException("Illegal arg to publish"); + + X509Certificate cert = (X509Certificate) certObj; + + try { + boolean hasCert = false, hasSubjname = false; + byte[] certEnc = cert.getEncoded(); + String subjName = ((X500Name) cert.getSubjectDN()).toLdapDNString(); + + LDAPSearchResults res = + conn.search(dn, LDAPv2.SCOPE_BASE, "(objectclass=*)", + new String[] { mCertAttr, mSubjNameAttr }, false); + + LDAPEntry entry = res.next(); + LDAPAttribute certs = entry.getAttribute(mCertAttr); + LDAPAttribute subjnames = entry.getAttribute(mSubjNameAttr); + + // check if has cert already. + if (certs != null) { + hasCert = LdapUserCertPublisher.ByteValueExists(certs, certEnc); + } + + // check if has subject name already. + if (subjnames != null) { + hasSubjname = + LdapUserCertPublisher.StringValueExists(subjnames, subjName); + } + + // if has both, done. + if (hasCert && hasSubjname) { + log(ILogger.LL_INFO, + "publish: " + subjName + " already has cert & subject name"); + return; + } + + // add cert if not already there. + LDAPModificationSet modSet = new LDAPModificationSet(); + + if (!hasCert) { + log(ILogger.LL_INFO, "publish: adding cert to " + subjName); + modSet.add(LDAPModification.ADD, + new LDAPAttribute(mCertAttr, certEnc)); + } + // add subject name if not already there. + if (!hasSubjname) { + log(ILogger.LL_INFO, "publish: adding " + subjName + " to " + dn); + modSet.add(LDAPModification.ADD, + new LDAPAttribute(mSubjNameAttr, subjName)); + } + conn.modify(dn, modSet); + } catch (CertificateEncodingException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_PUBLISH_ERROR", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_GET_DER_ENCODED_CERT_FAILED", e.toString())); + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_PUBLISHER_EXCEPTION", "", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_PUBLISH_USERCERT_ERROR", e.toString())); + } + } catch (IOException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_PUBLISH_ERROR", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_PUBLISH_USERCERT_ERROR", e.toString())); + } + } + + /** + * deletes the certificate from the list of certificates. + * does not check if certificate is already there. + * also takes out the subject name if no other certificate remain + * with the same subject name. + */ + public void unpublish(LDAPConnection conn, String dn, Object certObj) + throws ELdapException { + if (!(certObj instanceof X509Certificate)) + throw new IllegalArgumentException("Illegal arg to publish"); + + try { + boolean hasCert = false, hasSubjname = false; + boolean hasAnotherCert = false; + X509Certificate cert = (X509Certificate) certObj; + String subjName = ((X500Name) cert.getSubjectDN()).toLdapDNString(); + + byte[] certEnc = cert.getEncoded(); + + LDAPSearchResults res = + conn.search(dn, LDAPv2.SCOPE_BASE, "(objectclass=*)", + new String[] { mCertAttr, mSubjNameAttr }, false); + + LDAPEntry entry = res.next(); + LDAPAttribute certs = entry.getAttribute(mCertAttr); + LDAPAttribute subjnames = entry.getAttribute(mSubjNameAttr); + + // check for cert and other certs with same subject name. + if (certs != null) { + hasCert = LdapUserCertPublisher.ByteValueExists(certs, certEnc); + // check for other certs with the same subject name + @SuppressWarnings("unchecked") + Enumeration vals = certs.getByteValues(); + byte[] val = null; + + while (vals.hasMoreElements()) { + val = vals.nextElement(); + if (PublisherUtils.byteArraysAreEqual(certEnc, val)) { + hasCert = true; + continue; + } + try { + X509CertImpl certval = new X509CertImpl(val); + // XXX use some sort of X500name equals function here. + String subjnam = + ((X500Name) certval.getSubjectDN()).toLdapDNString(); + + if (subjnam.equalsIgnoreCase(subjName)) { + hasAnotherCert = true; + } + } catch (CertificateEncodingException e) { + // ignore this certificate. + CMS.debug( + "LdapCertSubjPublisher: unpublish: an invalid cert in dn entry encountered"); + } catch (CertificateException e) { + // ignore this certificate. + CMS.debug( + "LdapCertSubjPublisher: unpublish: an invalid cert in dn entry encountered"); + } + } + } + + // check if doesn't have subject name already. + if (subjnames != null) { + hasSubjname = + LdapUserCertPublisher.StringValueExists(subjnames, subjName); + } + + // if doesn't have both, done. + if (!hasCert && !hasSubjname) { + log(ILogger.LL_INFO, + "unpublish: " + subjName + " already has not cert & subjname"); + return; + } + + // delete cert if there. + LDAPModificationSet modSet = new LDAPModificationSet(); + + if (hasCert) { + log(ILogger.LL_INFO, + "unpublish: deleting cert " + subjName + " from " + dn); + modSet.add(LDAPModification.DELETE, + new LDAPAttribute(mCertAttr, certEnc)); + } + // delete subject name if no other cert has the same name. + if (hasSubjname && !hasAnotherCert) { + log(ILogger.LL_INFO, + "unpublish: deleting subject name " + subjName + " from " + dn); + modSet.add(LDAPModification.DELETE, + new LDAPAttribute(mSubjNameAttr, subjName)); + } + conn.modify(dn, modSet); + } catch (CertificateEncodingException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_UNPUBLISH_ERROR", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_GET_DER_ENCODED_CERT_FAILED", e.toString())); + } catch (IOException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_UNPUBLISH_ERROR", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_GET_LDAP_DN_STRING_FAILED", e.toString())); + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_UNPUBLISH_ERROR", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_UNPUBLISH_USERCERT_ERROR", e.toString())); + } + } + return; + } + + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapCertSubjPublisher: " + msg); + } + +} diff --git a/base/common/src/com/netscape/cms/publish/publishers/LdapCertificatePairPublisher.java b/base/common/src/com/netscape/cms/publish/publishers/LdapCertificatePairPublisher.java new file mode 100644 index 000000000..c65ff79d5 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/publishers/LdapCertificatePairPublisher.java @@ -0,0 +1,318 @@ +// --- 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.publish.publishers; + +import java.util.Locale; +import java.util.Vector; + +import netscape.ldap.LDAPAttribute; +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPModification; +import netscape.ldap.LDAPModificationSet; +import netscape.ldap.LDAPSearchResults; +import netscape.ldap.LDAPv2; + +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.ldap.ELdapException; +import com.netscape.certsrv.ldap.ELdapServerDownException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapPublisher; + +/** + * module for publishing a cross certificate pair to ldap + * crossCertificatePair attribute + * + * @version $Revision$, $Date$ + */ +public class LdapCertificatePairPublisher + implements ILdapPublisher, IExtendedPluginInfo { + public static final String LDAP_CROSS_CERT_PAIR_ATTR = "crossCertificatePair;binary"; + public static final String LDAP_CA_OBJECTCLASS = "pkiCA"; + public static final String LDAP_ARL_ATTR = "authorityRevocationList;binary"; + public static final String LDAP_CRL_ATTR = "certificateRevocationList;binary"; + public static final String LDAP_CACERT_ATTR = "caCertificate;binary"; + + protected String mCrossCertPairAttr = LDAP_CROSS_CERT_PAIR_ATTR; + protected String mCaObjectclass = LDAP_CA_OBJECTCLASS; + protected String mObjAdded = ""; + protected String mObjDeleted = ""; + + private ILogger mLogger = CMS.getLogger(); + private boolean mInited = false; + protected IConfigStore mConfig = null; + + /** + * constructor constructs default values. + */ + public LdapCertificatePairPublisher() { + } + + public String[] getExtendedPluginInfo(Locale locale) { + String s[] = { + "crossCertPairAttr;string;Name of Ldap attribute in which to store cross certificates", + "caObjectClass;string;The name of the objectclasses which should be " + + "added to this entry, if they do not already exist. This can be " + + "'certificationAuthority' (if using RFC 2256) or 'pkiCA' (if using RFC 4523)", + IExtendedPluginInfo.HELP_TOKEN + + ";configuration-ldappublish-publisher-crosscertpairpublisher", + IExtendedPluginInfo.HELP_TEXT + + ";This plugin knows how to publish the CA cert to " + + "'certificateAuthority' and 'pkiCA' -type entries" + }; + + return s; + } + + public String getImplName() { + return "LdapCertificatePairPublisher"; + } + + public String getDescription() { + return "LdapCertificatePairPublisher"; + } + + public Vector getInstanceParams() { + Vector v = new Vector(); + + v.addElement("crossCertPairAttr=" + mCrossCertPairAttr); + v.addElement("caObjectClass=" + mCaObjectclass); + return v; + } + + public Vector getInstanceParamsWithExtras() { + return getInstanceParams(); + } + + public Vector getDefaultParams() { + Vector v = new Vector(); + + v.addElement("crossCertPairAttr=" + mCrossCertPairAttr); + v.addElement("caObjectClass=" + mCaObjectclass); + return v; + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + public void init(IConfigStore config) + throws EBaseException { + if (mInited) + return; + mConfig = config; + mCrossCertPairAttr = mConfig.getString("crossCertPairAttr", LDAP_CROSS_CERT_PAIR_ATTR); + mCaObjectclass = mConfig.getString("caObjectClass", + LDAP_CA_OBJECTCLASS); + mObjAdded = mConfig.getString("caObjectClassAdded", ""); + mObjDeleted = mConfig.getString("caObjectClassDeleted", ""); + + mInited = true; + } + + // don't think anyone would ever use this but just in case. + public LdapCertificatePairPublisher(String crossCertPairAttr, String caObjectclass) { + mCrossCertPairAttr = crossCertPairAttr; + mCaObjectclass = caObjectclass; + mInited = true; + } + + /** + * Gets the Certificate Authority object class to convert to. + */ + public String getCAObjectclass() { + return mCaObjectclass; + } + + /** + * returns the cross cert pair attribute where it'll be published. + */ + public String getXCertAttrName() { + return mCrossCertPairAttr; + } + + /** + * publish a certificatePair + * -should not be called from listeners. + * + * @param conn the LDAP connection + * @param dn dn of the entry to publish the XcertificatePair + * @param pair the Xcertificate bytes object. + */ + public synchronized void publish(LDAPConnection conn, String dn, Object pair) + throws ELdapException { + publish(conn, dn, (byte[]) pair); + } + + /** + * publish a certificatePair + * -should not be called from listeners. + * + * @param conn the LDAP connection + * @param dn dn of the entry to publish the XcertificatePair + * @param pair the cross cert bytes + */ + public synchronized void publish(LDAPConnection conn, String dn, + byte[] pair) + throws ELdapException { + + if (conn == null) { + log(ILogger.LL_INFO, "LdapCertificatePairPublisher: no LDAP connection"); + return; + } + + try { + mCrossCertPairAttr = mConfig.getString("crossCertPairAttr", LDAP_CROSS_CERT_PAIR_ATTR); + mCaObjectclass = mConfig.getString("caObjectClass", LDAP_CA_OBJECTCLASS); + } catch (EBaseException e) { + } + + try { + // search for attributes to determine if they exist + LDAPSearchResults res = + conn.search(dn, LDAPv2.SCOPE_BASE, "(objectclass=*)", + new String[] { LDAP_CACERT_ATTR, LDAP_CRL_ATTR, LDAP_ARL_ATTR }, true); + LDAPEntry entry = res.next(); + LDAPAttribute certs = entry.getAttribute(LDAP_CACERT_ATTR); + LDAPAttribute arls = entry.getAttribute(LDAP_ARL_ATTR); + LDAPAttribute crls = entry.getAttribute(LDAP_CRL_ATTR); + + // search for objectclass and crosscertpair attributes and values + LDAPSearchResults res1 = + conn.search(dn, LDAPv2.SCOPE_BASE, "(objectclass=*)", + new String[] { "objectclass", mCrossCertPairAttr }, false); + LDAPEntry entry1 = res1.next(); + LDAPAttribute ocs = entry1.getAttribute("objectclass"); + LDAPAttribute certPairs = entry1.getAttribute("crosscertificatepair;binary"); + + LDAPModificationSet modSet = new LDAPModificationSet(); + + boolean hasCert = LdapUserCertPublisher.ByteValueExists(certPairs, pair); + if (LdapUserCertPublisher.ByteValueExists(certPairs, pair)) { + CMS.debug("LdapCertificatePairPublisher: cross cert pair bytes exist in publishing directory, do not publish again."); + return; + } + if (hasCert) { + log(ILogger.LL_INFO, "publish: CA " + dn + " already has cross cert pair bytes"); + } else { + modSet.add(LDAPModification.ADD, + new LDAPAttribute(mCrossCertPairAttr, pair)); + log(ILogger.LL_INFO, "cross cert pair published with dn=" + dn); + } + + String[] oclist = mCaObjectclass.split(","); + + boolean attrsAdded = false; + for (int i = 0; i < oclist.length; i++) { + String oc = oclist[i].trim(); + boolean hasoc = LdapUserCertPublisher.StringValueExists(ocs, oc); + if (!hasoc) { + log(ILogger.LL_INFO, "adding CA objectclass " + oc + " to " + dn); + modSet.add(LDAPModification.ADD, + new LDAPAttribute("objectclass", oc)); + + if ((!attrsAdded) && oc.equalsIgnoreCase("certificationAuthority")) { + // add MUST attributes + if (arls == null) + modSet.add(LDAPModification.ADD, + new LDAPAttribute(LDAP_ARL_ATTR, "")); + if (crls == null) + modSet.add(LDAPModification.ADD, + new LDAPAttribute(LDAP_CRL_ATTR, "")); + if (certs == null) + modSet.add(LDAPModification.ADD, + new LDAPAttribute(LDAP_CACERT_ATTR, "")); + attrsAdded = true; + } + } + } + + // delete objectclasses that have been deleted from config + String[] delList = mObjDeleted.split(","); + if (delList.length > 0) { + for (int i = 0; i < delList.length; i++) { + String deloc = delList[i].trim(); + boolean hasoc = LdapUserCertPublisher.StringValueExists(ocs, deloc); + boolean match = false; + for (int j = 0; j < oclist.length; j++) { + if ((oclist[j].trim()).equals(deloc)) { + match = true; + break; + } + } + if (!match && hasoc) { + log(ILogger.LL_INFO, "deleting CRL objectclass " + deloc + " from " + dn); + modSet.add(LDAPModification.DELETE, + new LDAPAttribute("objectclass", deloc)); + } + } + } + + // reset mObjAdded and mObjDeleted, if needed + if ((!mObjAdded.equals("")) || (!mObjDeleted.equals(""))) { + mObjAdded = ""; + mObjDeleted = ""; + mConfig.putString("caObjectClassAdded", ""); + mConfig.putString("caObjectClassDeleted", ""); + try { + mConfig.commit(false); + } catch (Exception e) { + log(ILogger.LL_INFO, "Failure in updating mObjAdded and mObjDeleted"); + } + } + + if (modSet.size() > 0) + conn.modify(dn, modSet); + CMS.debug("LdapCertificatePairPublisher: in publish() just published"); + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_PUBLISHER_EXCEPTION", "", e.toString())); + throw new ELdapException("error publishing cross cert pair:" + e.toString()); + } + } + return; + } + + /** + * unsupported + */ + public void unpublish(LDAPConnection conn, String dn, Object certObj) + throws ELdapException { + CMS.debug("LdapCertificatePairPublisher: unpublish() is unsupported in this revision"); + } + + /** + * handy routine for logging in this class. + */ + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapCertificatePairPublisher: " + msg); + } + +} diff --git a/base/common/src/com/netscape/cms/publish/publishers/LdapCrlPublisher.java b/base/common/src/com/netscape/cms/publish/publishers/LdapCrlPublisher.java new file mode 100644 index 000000000..6826cc801 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/publishers/LdapCrlPublisher.java @@ -0,0 +1,379 @@ +// --- 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.publish.publishers; + +import java.security.cert.CRLException; +import java.security.cert.X509CRL; +import java.util.Locale; +import java.util.Vector; + +import netscape.ldap.LDAPAttribute; +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPModification; +import netscape.ldap.LDAPModificationSet; +import netscape.ldap.LDAPSSLSocketFactoryExt; +import netscape.ldap.LDAPSearchResults; +import netscape.ldap.LDAPv2; + +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.ldap.ELdapException; +import com.netscape.certsrv.ldap.ELdapServerDownException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapPublisher; + +/** + * For publishing master or global CRL. + * Publishes (replaces) the CRL in the CA's LDAP entry. + * + * @version $Revision$, $Date$ + */ +public class LdapCrlPublisher implements ILdapPublisher, IExtendedPluginInfo { + private ILogger mLogger = CMS.getLogger(); + protected IConfigStore mConfig = null; + boolean mInited = false; + + public static final String LDAP_CACERT_ATTR = "caCertificate;binary"; + public static final String LDAP_ARL_ATTR = "authorityRevocationList;binary"; + public static final String LDAP_CRL_ATTR = "certificateRevocationList;binary"; + public static final String LDAP_CRL_OBJECTCLASS = "pkiCA,deltaCRL"; + + protected String mCrlAttr = LDAP_CRL_ATTR; + protected String mCrlObjectClass = LDAP_CRL_OBJECTCLASS; + protected String mObjAdded = ""; + protected String mObjDeleted = ""; + + /** + * constructs ldap crl publisher with default values + */ + public LdapCrlPublisher() { + } + + public String getImplName() { + return "LdapCrlPublisher"; + } + + public String getDescription() { + return "LdapCrlPublisher"; + } + + public String[] getExtendedPluginInfo(Locale locale) { + String[] params = { + "crlAttr;string;Name of Ldap attribute in which to store the CRL", + "crlObjectClass;string;The name of the objectclasses which should be " + + "added to this entry, if they do not already exist. This can be a comma-" + + "separated list such as 'certificationAuthority,certificationAuthority-V2' " + + "(if using RFC 2256) or 'pkiCA, deltaCRL' (if using RFC 4523)", + IExtendedPluginInfo.HELP_TOKEN + + ";configuration-ldappublish-publisher-crlpublisher", + IExtendedPluginInfo.HELP_TEXT + + ";This plugin knows how to publish CRL's to " + + "'certificateAuthority' and 'pkiCA' -type entries" + }; + + return params; + } + + public Vector getInstanceParams() { + Vector v = new Vector(); + + v.addElement("crlAttr=" + mCrlAttr); + v.addElement("crlObjectClass=" + mCrlObjectClass); + return v; + } + + public Vector getDefaultParams() { + Vector v = new Vector(); + + v.addElement("crlAttr=" + mCrlAttr); + v.addElement("crlObjectClass=" + mCrlObjectClass); + return v; + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + public void init(IConfigStore config) + throws EBaseException { + if (mInited) + return; + mConfig = config; + mCrlAttr = mConfig.getString("crlAttr", LDAP_CRL_ATTR); + mCrlObjectClass = mConfig.getString("crlObjectClass", + LDAP_CRL_OBJECTCLASS); + mObjAdded = mConfig.getString("crlObjectClassAdded", ""); + mObjDeleted = mConfig.getString("crlObjectClassDeleted", ""); + + mInited = true; + } + + public LdapCrlPublisher(String crlAttr, String crlObjectClass) { + mCrlAttr = crlAttr; + mCrlObjectClass = crlObjectClass; + } + + /** + * Gets the CA object class to convert to. + */ + public String getCRLObjectclass() { + return mCrlObjectClass; + } + + /** + * Replaces the CRL in the certificateRevocationList attribute. + * CRL's are published as a DER encoded blob. + */ + public void publish(LDAPConnection conn, String dn, Object crlObj) + throws ELdapException { + if (conn == null) { + log(ILogger.LL_INFO, "publish CRL: no LDAP connection"); + return; + } + + try { + mCrlAttr = mConfig.getString("crlAttr", LDAP_CRL_ATTR); + mCrlObjectClass = mConfig.getString("crlObjectClass", LDAP_CRL_OBJECTCLASS); + } catch (EBaseException e) { + } + + // Bugscape #56124 - support multiple publishing directory + // see if we should create local connection + LDAPConnection altConn = null; + try { + String host = mConfig.getString("host", null); + String port = mConfig.getString("port", null); + if (host != null && port != null) { + int portVal = Integer.parseInt(port); + int version = Integer.parseInt(mConfig.getString("version", "2")); + String cert_nick = mConfig.getString("clientCertNickname", null); + LDAPSSLSocketFactoryExt sslSocket = null; + if (cert_nick != null) { + sslSocket = CMS.getLdapJssSSLSocketFactory(cert_nick); + } + String mgr_dn = mConfig.getString("bindDN", null); + String mgr_pwd = mConfig.getString("bindPWD", null); + + altConn = CMS.getBoundConnection(host, portVal, + version, + sslSocket, mgr_dn, mgr_pwd); + conn = altConn; + } + } catch (LDAPException e) { + CMS.debug("Failed to create alt connection " + e); + } catch (EBaseException e) { + CMS.debug("Failed to create alt connection " + e); + } + + try { + byte[] crlEnc = ((X509CRL) crlObj).getEncoded(); + log(ILogger.LL_INFO, "publish CRL: " + dn); + + /* search for attribute names to determine existence of attributes */ + LDAPSearchResults res = null; + if (mCrlAttr.equals(LDAP_CRL_ATTR)) { + res = conn.search(dn, LDAPv2.SCOPE_BASE, "(objectclass=*)", + new String[] { LDAP_CACERT_ATTR, LDAP_ARL_ATTR }, true); + } else { + res = conn.search(dn, LDAPv2.SCOPE_BASE, "(objectclass=*)", + new String[] { LDAP_CRL_ATTR, LDAP_CACERT_ATTR, LDAP_ARL_ATTR }, true); + } + + LDAPEntry entry = res.next(); + LDAPAttribute crls = entry.getAttribute(LDAP_CRL_ATTR); + LDAPAttribute certs = entry.getAttribute(LDAP_CACERT_ATTR); + LDAPAttribute arls = entry.getAttribute(LDAP_ARL_ATTR); + + /* get object class values */ + LDAPSearchResults res1 = null; + res1 = conn.search(dn, LDAPv2.SCOPE_BASE, "(objectclass=*)", + new String[] { "objectclass" }, false); + LDAPEntry entry1 = res1.next(); + LDAPAttribute ocs = entry1.getAttribute("objectclass"); + + LDAPModificationSet modSet = new LDAPModificationSet(); + + String[] oclist = mCrlObjectClass.split(","); + boolean attrsAdded = false; + for (int i = 0; i < oclist.length; i++) { + String oc = oclist[i].trim(); + boolean hasoc = LdapUserCertPublisher.StringValueExists(ocs, oc); + if (!hasoc) { + log(ILogger.LL_INFO, "adding CRL objectclass " + oc + " to " + dn); + modSet.add(LDAPModification.ADD, + new LDAPAttribute("objectclass", oc)); + + if ((!attrsAdded) && oc.equalsIgnoreCase("certificationAuthority")) { + // add MUST attributes + if (arls == null) + modSet.add(LDAPModification.ADD, + new LDAPAttribute(LDAP_ARL_ATTR, "")); + if (certs == null) + modSet.add(LDAPModification.ADD, + new LDAPAttribute(LDAP_CACERT_ATTR, "")); + + if ((crls == null) && (!mCrlAttr.equals(LDAP_CRL_ATTR))) + modSet.add(LDAPModification.ADD, + new LDAPAttribute(LDAP_CRL_ATTR, "")); + attrsAdded = true; + } + } + } + + modSet.add(LDAPModification.REPLACE, new LDAPAttribute(mCrlAttr, crlEnc)); + + // delete objectclasses that have been deleted from config + String[] delList = mObjDeleted.split(","); + if (delList.length > 0) { + for (int i = 0; i < delList.length; i++) { + String deloc = delList[i].trim(); + boolean hasoc = LdapUserCertPublisher.StringValueExists(ocs, deloc); + boolean match = false; + for (int j = 0; j < oclist.length; j++) { + if ((oclist[j].trim()).equals(deloc)) { + match = true; + break; + } + } + if (!match && hasoc) { + log(ILogger.LL_INFO, "deleting CRL objectclass " + deloc + " from " + dn); + modSet.add(LDAPModification.DELETE, + new LDAPAttribute("objectclass", deloc)); + } + } + } + + // reset mObjAdded and mObjDeleted, if needed + if ((!mObjAdded.equals("")) || (!mObjDeleted.equals(""))) { + mObjAdded = ""; + mObjDeleted = ""; + mConfig.putString("crlObjectClassAdded", ""); + mConfig.putString("crlObjectClassDeleted", ""); + try { + mConfig.commit(false); + } catch (Exception e) { + log(ILogger.LL_INFO, "Failure in updating mObjAdded and mObjDeleted"); + } + } + + conn.modify(dn, modSet); + } catch (CRLException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_PUBLISH_ERROR", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_PUBLISH_CRL_ERROR", e.toString())); + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_PUBLISH_ERROR", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_PUBLISH_CRL_ERROR", e.toString())); + } + } finally { + if (altConn != null) { + try { + altConn.disconnect(); + } catch (LDAPException e) { + // safely ignored + } + } + } + + } + + /** + * There shouldn't be a need to call this. + * CRLs are always replaced but this is implemented anyway in case + * there is ever a reason to remove a global CRL. + */ + public void unpublish(LDAPConnection conn, String dn, Object crlObj) + throws ELdapException { + try { + byte[] crlEnc = ((X509CRL) crlObj).getEncoded(); + + try { + mCrlAttr = mConfig.getString("crlAttr", LDAP_CRL_ATTR); + mCrlObjectClass = mConfig.getString("crlObjectClass", LDAP_CRL_OBJECTCLASS); + } catch (EBaseException e) { + } + + LDAPSearchResults res = conn.search(dn, LDAPv2.SCOPE_BASE, + "(objectclass=*)", new String[] { mCrlAttr, "objectclass" }, false); + LDAPEntry e = res.next(); + LDAPAttribute crls = e.getAttribute(mCrlAttr); + LDAPAttribute ocs = e.getAttribute("objectclass"); + + LDAPModificationSet modSet = new LDAPModificationSet(); + + boolean hasOC = false; + boolean hasCRL = + LdapUserCertPublisher.ByteValueExists(crls, crlEnc); + + if (hasCRL) { + modSet.add(LDAPModification.DELETE, + new LDAPAttribute(mCrlAttr, crlEnc)); + } + + String[] oclist = mCrlObjectClass.split(","); + for (int i = 0; i < oclist.length; i++) { + String oc = oclist[i].trim(); + if (LdapUserCertPublisher.StringValueExists(ocs, oc)) { + log(ILogger.LL_INFO, "unpublish: deleting CRL object class " + oc + " from " + dn); + modSet.add(LDAPModification.DELETE, + new LDAPAttribute("objectClass", oc)); + hasOC = true; + } + } + + if (hasCRL || hasOC) { + conn.modify(dn, modSet); + } else { + log(ILogger.LL_INFO, + "unpublish: " + dn + " already has not CRL"); + } + } catch (CRLException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_UNPUBLISH_ERROR", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_PUBLISH_CRL_ERROR", e.toString())); + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_UNPUBLISH_ERROR", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_UNPUBLISH_CRL_ERROR", e.toString())); + } + } + return; + } + + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapCrlPublisher: " + msg); + } +} diff --git a/base/common/src/com/netscape/cms/publish/publishers/LdapEncryptCertPublisher.java b/base/common/src/com/netscape/cms/publish/publishers/LdapEncryptCertPublisher.java new file mode 100644 index 000000000..d2c488620 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/publishers/LdapEncryptCertPublisher.java @@ -0,0 +1,359 @@ +// --- 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.publish.publishers; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.Date; +import java.util.Enumeration; +import java.util.Locale; +import java.util.Vector; + +import netscape.ldap.LDAPAttribute; +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPModification; +import netscape.ldap.LDAPSearchResults; +import netscape.ldap.LDAPv2; +import netscape.security.x509.CRLExtensions; +import netscape.security.x509.CRLReasonExtension; +import netscape.security.x509.RevocationReason; +import netscape.security.x509.RevokedCertImpl; +import netscape.security.x509.X509CertImpl; + +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.ca.ICAService; +import com.netscape.certsrv.ca.ICertificateAuthority; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.ldap.ELdapServerDownException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapPublisher; + +/** + * Interface for mapping a X509 certificate to a LDAP entry + * + * @version $Revision$, $Date$ + */ +public class LdapEncryptCertPublisher implements ILdapPublisher, IExtendedPluginInfo { + public static final String LDAP_USERCERT_ATTR = "userCertificate;binary"; + public static final String PROP_REVOKE_CERT = "revokeCert"; + + protected String mCertAttr = LDAP_USERCERT_ATTR; + private ILogger mLogger = CMS.getLogger(); + private IConfigStore mConfig = null; + private boolean mInited = false; + private boolean mRevokeCert; + + public LdapEncryptCertPublisher() { + } + + public String getImplName() { + return "LdapEncryptCertPublisher"; + } + + public String getDescription() { + return "LdapEncryptCertPublisher"; + } + + public String[] getExtendedPluginInfo(Locale locale) { + String[] params = { + "certAttr;string;LDAP attribute in which to store the certificate", + IExtendedPluginInfo.HELP_TOKEN + + ";configuration-ldappublish-publisher-usercertpublisher", + IExtendedPluginInfo.HELP_TEXT + + ";This plugin knows how to publish user certificates" + }; + + return params; + + } + + public Vector getInstanceParams() { + Vector v = new Vector(); + + v.addElement("certAttr=" + mCertAttr); + return v; + } + + public Vector getDefaultParams() { + Vector v = new Vector(); + + v.addElement("certAttr=" + mCertAttr); + return v; + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + public void init(IConfigStore config) + throws EBaseException { + if (mInited) + return; + mConfig = config; + mCertAttr = mConfig.getString("certAttr", LDAP_USERCERT_ATTR); + mRevokeCert = mConfig.getBoolean(PROP_REVOKE_CERT, true); + mInited = true; + } + + public LdapEncryptCertPublisher(String certAttr) { + mCertAttr = certAttr; + } + + /** + * publish a user certificate + * Adds the cert to the multi-valued certificate attribute as a + * DER encoded binary blob. Does not check if cert already exists. + * + * @param conn the LDAP connection + * @param dn dn of the entry to publish the certificate + * @param certObj the certificate object. + */ + public void publish(LDAPConnection conn, String dn, Object certObj) + throws ELdapException { + if (conn == null) + return; + + if (!(certObj instanceof X509Certificate)) + throw new IllegalArgumentException("Illegal arg to publish"); + + X509Certificate cert = (X509Certificate) certObj; + + log(ILogger.LL_INFO, "Publishing " + cert); + try { + byte[] certEnc = cert.getEncoded(); + + // check if cert already exists. + LDAPSearchResults res = conn.search(dn, LDAPv2.SCOPE_BASE, + "(objectclass=*)", new String[] { mCertAttr }, false); + LDAPEntry entry = res.next(); + LDAPAttribute attr = getModificationAttribute(entry.getAttribute(mCertAttr), certEnc); + + if (attr == null) { + log(ILogger.LL_INFO, "publish: " + dn + " already has cert."); + return; + } + + // publish + LDAPModification mod = new LDAPModification(LDAPModification.REPLACE, attr); + + conn.modify(dn, mod); + } catch (CertificateEncodingException e) { + CMS.debug("LdapEncryptCertPublisher: error in publish: " + e.toString()); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_GET_DER_ENCODED_CERT_FAILED", e.toString())); + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_PUBLISH_ERROR", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_PUBLISH_USERCERT_ERROR", e.toString())); + } + } + return; + } + + /** + * unpublish a user certificate + * deletes the certificate from the list of certificates. + * does not check if certificate is already there. + */ + public void unpublish(LDAPConnection conn, String dn, Object certObj) + throws ELdapException { + if (!(certObj instanceof X509Certificate)) + throw new IllegalArgumentException("Illegal arg to publish"); + + X509Certificate cert = (X509Certificate) certObj; + + try { + byte[] certEnc = cert.getEncoded(); + + // check if cert already deleted. + LDAPSearchResults res = conn.search(dn, LDAPv2.SCOPE_BASE, + "(objectclass=*)", new String[] { mCertAttr }, false); + LDAPEntry entry = res.next(); + + if (!ByteValueExists(entry.getAttribute(mCertAttr), certEnc)) { + log(ILogger.LL_INFO, dn + " already has not cert"); + return; + } + + LDAPModification mod = new LDAPModification(LDAPModification.DELETE, + new LDAPAttribute(mCertAttr, certEnc)); + + conn.modify(dn, mod); + } catch (CertificateEncodingException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_UNPUBLISH_ERROR", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_GET_DER_ENCODED_CERT_FAILED", e.toString())); + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_UNPUBLISH_ERROR", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_UNPUBLISH_USERCERT_ERROR", e.toString())); + } + } + return; + } + + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapUserCertPublisher: " + msg); + } + + public LDAPAttribute getModificationAttribute( + LDAPAttribute attr, byte[] bval) { + + LDAPAttribute at = new LDAPAttribute(attr.getName(), bval); + // determine if the given cert is a signing or an encryption + // certificate + X509CertImpl thisCert = null; + + try { + thisCert = new X509CertImpl(bval); + } catch (Exception e) { + } + if (thisCert == null) { + return at; + } + + @SuppressWarnings("unchecked") + Enumeration vals = attr.getByteValues(); + byte[] val = null; + + while (vals.hasMoreElements()) { + val = vals.nextElement(); + try { + X509CertImpl cert = new X509CertImpl(val); + + log(ILogger.LL_INFO, "Checking " + cert); + if (CMS.isEncryptionCert(thisCert) && + CMS.isEncryptionCert(cert)) { + // skip + log(ILogger.LL_INFO, "SKIP ENCRYPTION " + cert); + revokeCert(cert); + } else if (CMS.isSigningCert(thisCert) && + CMS.isSigningCert(cert)) { + // skip + log(ILogger.LL_INFO, "SKIP SIGNING " + cert); + revokeCert(cert); + } else { + at.addValue(val); + } + } catch (Exception e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CHECK_FAILED", e.toString())); + } + } + return at; + } + + private RevokedCertImpl formCRLEntry( + BigInteger serialNo, RevocationReason reason) + throws EBaseException { + CRLReasonExtension reasonExt = new CRLReasonExtension(reason); + CRLExtensions crlentryexts = new CRLExtensions(); + + try { + crlentryexts.set(CRLReasonExtension.NAME, reasonExt); + } catch (IOException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_SET_CRL_REASON", reason.toString(), e.toString())); + + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + RevokedCertImpl crlentry = + new RevokedCertImpl(serialNo, new Date(), crlentryexts); + + return crlentry; + } + + private void revokeCert(X509CertImpl cert) + throws EBaseException { + try { + if (mConfig.getBoolean(PROP_REVOKE_CERT, true) == false) { + return; + } + } catch (EBaseException e) { + return; + } + BigInteger serialNum = cert.getSerialNumber(); + // need to revoke certificate also + ICertificateAuthority ca = (ICertificateAuthority) + CMS.getSubsystem("ca"); + ICAService service = (ICAService) ca.getCAService(); + RevokedCertImpl crlEntry = formCRLEntry( + serialNum, RevocationReason.KEY_COMPROMISE); + + service.revokeCert(crlEntry); + } + + /** + * checks if a byte attribute has a certain value. + */ + public static boolean ByteValueExists(LDAPAttribute attr, byte[] bval) { + if (attr == null) { + return false; + } + @SuppressWarnings("unchecked") + Enumeration vals = attr.getByteValues(); + byte[] val = null; + + while (vals.hasMoreElements()) { + val = (byte[]) vals.nextElement(); + if (PublisherUtils.byteArraysAreEqual(val, bval)) { + return true; + } + } + return false; + } + + /** + * checks if a attribute has a string value. + */ + public static boolean StringValueExists(LDAPAttribute attr, String sval) { + if (attr == null) { + return false; + } + @SuppressWarnings("unchecked") + Enumeration vals = attr.getStringValues(); + String val = null; + + while (vals.hasMoreElements()) { + val = vals.nextElement(); + if (val.equalsIgnoreCase(sval)) { + return true; + } + } + return false; + } + +} diff --git a/base/common/src/com/netscape/cms/publish/publishers/LdapUserCertPublisher.java b/base/common/src/com/netscape/cms/publish/publishers/LdapUserCertPublisher.java new file mode 100644 index 000000000..e31ce674c --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/publishers/LdapUserCertPublisher.java @@ -0,0 +1,333 @@ +// --- 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.publish.publishers; + +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.Enumeration; +import java.util.Locale; +import java.util.Vector; + +import netscape.ldap.LDAPAttribute; +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPModification; +import netscape.ldap.LDAPSSLSocketFactoryExt; +import netscape.ldap.LDAPSearchResults; +import netscape.ldap.LDAPv2; + +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.ldap.ELdapException; +import com.netscape.certsrv.ldap.ELdapServerDownException; +import com.netscape.certsrv.logging.AuditFormat; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapPublisher; + +/** + * Interface for mapping a X509 certificate to a LDAP entry + * + * @version $Revision$, $Date$ + */ +public class LdapUserCertPublisher implements ILdapPublisher, IExtendedPluginInfo { + public static final String LDAP_USERCERT_ATTR = "userCertificate;binary"; + + protected String mCertAttr = LDAP_USERCERT_ATTR; + private ILogger mLogger = CMS.getLogger(); + private IConfigStore mConfig = null; + private boolean mInited = false; + + public LdapUserCertPublisher() { + } + + public String getImplName() { + return "LdapUserCertPublisher"; + } + + public String getDescription() { + return "LdapUserCertPublisher"; + } + + public String[] getExtendedPluginInfo(Locale locale) { + String[] params = { + "certAttr;string;LDAP attribute in which to store the certificate", + IExtendedPluginInfo.HELP_TOKEN + + ";configuration-ldappublish-publisher-usercertpublisher", + IExtendedPluginInfo.HELP_TEXT + + ";This plugin knows how to publish user certificates" + }; + + return params; + + } + + public Vector getInstanceParams() { + Vector v = new Vector(); + + v.addElement("certAttr=" + mCertAttr); + return v; + } + + public Vector getDefaultParams() { + Vector v = new Vector(); + + v.addElement("certAttr=" + mCertAttr); + return v; + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + public void init(IConfigStore config) + throws EBaseException { + if (mInited) + return; + mConfig = config; + mCertAttr = mConfig.getString("certAttr", LDAP_USERCERT_ATTR); + mInited = true; + } + + public LdapUserCertPublisher(String certAttr) { + mCertAttr = certAttr; + } + + /** + * publish a user certificate + * Adds the cert to the multi-valued certificate attribute as a + * DER encoded binary blob. Does not check if cert already exists. + * + * @param conn the LDAP connection + * @param dn dn of the entry to publish the certificate + * @param certObj the certificate object. + */ + public void publish(LDAPConnection conn, String dn, Object certObj) + throws ELdapException { + if (conn == null) + return; + + // Bugscape #56124 - support multiple publishing directory + // see if we should create local connection + LDAPConnection altConn = null; + try { + String host = mConfig.getString("host", null); + String port = mConfig.getString("port", null); + if (host != null && port != null) { + int portVal = Integer.parseInt(port); + int version = Integer.parseInt(mConfig.getString("version", "2")); + String cert_nick = mConfig.getString("clientCertNickname", null); + LDAPSSLSocketFactoryExt sslSocket = null; + if (cert_nick != null) { + sslSocket = CMS.getLdapJssSSLSocketFactory(cert_nick); + } + String mgr_dn = mConfig.getString("bindDN", null); + String mgr_pwd = mConfig.getString("bindPWD", null); + + altConn = CMS.getBoundConnection(host, portVal, + version, + sslSocket, mgr_dn, mgr_pwd); + conn = altConn; + } + } catch (LDAPException e) { + CMS.debug("Failed to create alt connection " + e); + } catch (EBaseException e) { + CMS.debug("Failed to create alt connection " + e); + } + + if (!(certObj instanceof X509Certificate)) + throw new IllegalArgumentException("Illegal arg to publish"); + + X509Certificate cert = (X509Certificate) certObj; + + boolean deleteCert = false; + try { + deleteCert = mConfig.getBoolean("deleteCert", false); + } catch (Exception e) { + } + + try { + byte[] certEnc = cert.getEncoded(); + + // check if cert already exists. + LDAPSearchResults res = conn.search(dn, LDAPv2.SCOPE_BASE, + "(objectclass=*)", new String[] { mCertAttr }, false); + LDAPEntry entry = res.next(); + + if (ByteValueExists(entry.getAttribute(mCertAttr), certEnc)) { + log(ILogger.LL_INFO, "publish: " + dn + " already has cert."); + return; + } + + // publish + LDAPModification mod = null; + if (deleteCert) { + mod = new LDAPModification(LDAPModification.REPLACE, + new LDAPAttribute(mCertAttr, certEnc)); + } else { + mod = new LDAPModification(LDAPModification.ADD, + new LDAPAttribute(mCertAttr, certEnc)); + } + + conn.modify(dn, mod); + + // log a successful message to the "transactions" log + mLogger.log(ILogger.EV_AUDIT, + ILogger.S_LDAP, + ILogger.LL_INFO, + AuditFormat.LDAP_PUBLISHED_FORMAT, + new Object[] { "LdapUserCertPublisher", + cert.getSerialNumber().toString(16), + cert.getSubjectDN() }); + } catch (CertificateEncodingException e) { + CMS.debug("LdapUserCertPublisher: error in publish: " + e.toString()); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_GET_DER_ENCODED_CERT_FAILED", e.toString())); + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_PUBLISH_ERROR", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_PUBLISH_USERCERT_ERROR", e.toString())); + } + } finally { + if (altConn != null) { + try { + altConn.disconnect(); + } catch (LDAPException e) { + // safely ignored + } + } + } + return; + } + + /** + * unpublish a user certificate + * deletes the certificate from the list of certificates. + * does not check if certificate is already there. + */ + public void unpublish(LDAPConnection conn, String dn, Object certObj) + throws ELdapException { + + boolean disableUnpublish = false; + try { + disableUnpublish = mConfig.getBoolean("disableUnpublish", false); + } catch (Exception e) { + } + + if (disableUnpublish) { + CMS.debug("UserCertPublisher: disable unpublish"); + return; + } + + if (!(certObj instanceof X509Certificate)) + throw new IllegalArgumentException("Illegal arg to publish"); + + X509Certificate cert = (X509Certificate) certObj; + + try { + byte[] certEnc = cert.getEncoded(); + + // check if cert already deleted. + LDAPSearchResults res = conn.search(dn, LDAPv2.SCOPE_BASE, + "(objectclass=*)", new String[] { mCertAttr }, false); + LDAPEntry entry = res.next(); + + if (!ByteValueExists(entry.getAttribute(mCertAttr), certEnc)) { + log(ILogger.LL_INFO, dn + " already has not cert"); + return; + } + + LDAPModification mod = new LDAPModification(LDAPModification.DELETE, + new LDAPAttribute(mCertAttr, certEnc)); + + conn.modify(dn, mod); + } catch (CertificateEncodingException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_UNPUBLISH_ERROR", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_GET_DER_ENCODED_CERT_FAILED", e.toString())); + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_UNPUBLISH_ERROR")); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_UNPUBLISH_USERCERT_ERROR", e.toString())); + } + } + return; + } + + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapUserCertPublisher: " + msg); + } + + /** + * checks if a byte attribute has a certain value. + */ + public static boolean ByteValueExists(LDAPAttribute attr, byte[] bval) { + if (attr == null) { + return false; + } + @SuppressWarnings("unchecked") + Enumeration vals = attr.getByteValues(); + byte[] val = null; + + while (vals.hasMoreElements()) { + val = vals.nextElement(); + if (val.length == 0) + continue; + if (PublisherUtils.byteArraysAreEqual(val, bval)) { + return true; + } + } + return false; + } + + /** + * checks if a attribute has a string value. + */ + public static boolean StringValueExists(LDAPAttribute attr, String sval) { + if (attr == null) { + return false; + } + @SuppressWarnings("unchecked") + Enumeration vals = attr.getStringValues(); + String val = null; + + while (vals.hasMoreElements()) { + val = vals.nextElement(); + if (val.equalsIgnoreCase(sval)) { + return true; + } + } + return false; + } + +} diff --git a/base/common/src/com/netscape/cms/publish/publishers/OCSPPublisher.java b/base/common/src/com/netscape/cms/publish/publishers/OCSPPublisher.java new file mode 100644 index 000000000..600bbd110 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/publishers/OCSPPublisher.java @@ -0,0 +1,355 @@ +// --- 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.publish.publishers; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.Socket; +import java.net.URLEncoder; +import java.security.cert.CRLException; +import java.security.cert.X509CRL; +import java.util.Locale; +import java.util.StringTokenizer; +import java.util.Vector; + +import netscape.ldap.LDAPConnection; + +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.ldap.ELdapException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapPublisher; +import com.netscape.cmsutil.http.HttpRequest; +import com.netscape.cmsutil.http.JssSSLSocketFactory; + +/** + * This publisher writes certificate and CRL into + * a directory. + * + * @version $Revision$, $Date$ + */ +public class OCSPPublisher implements ILdapPublisher, IExtendedPluginInfo { + private static final String PROP_HOST = "host"; + private static final String PROP_PORT = "port"; + private static final String PROP_PATH = "path"; + private static final String PROP_NICK = "nickName"; + private static final String PROP_CLIENT_AUTH_ENABLE = "enableClientAuth"; + + private IConfigStore mConfig = null; + private String mHost = null; + private String mPort = null; + private String mPath = null; + private String mNickname = null; + private boolean mClientAuthEnabled = true; + private ILogger mLogger = CMS.getLogger(); + + /** + * Returns the implementation name. + */ + public String getImplName() { + return "OCSPPublisher"; + } + + /** + * Returns the description of the ldap publisher. + */ + public String getDescription() { + return "This publisher writes the CRL to CMS's OCSP server."; + } + + public String[] getExtendedPluginInfo(Locale locale) { + String[] params = { + PROP_HOST + ";string;Host of CMS's OCSP Secure agent service", + PROP_PORT + ";string;Port of CMS's OCSP Secure agent service", + PROP_PATH + ";string;URI of CMS's OCSP Secure agent service", + PROP_NICK + ";string;Nickname of cert used for client authentication", + PROP_CLIENT_AUTH_ENABLE + ";boolean;Client Authentication enabled", + IExtendedPluginInfo.HELP_TOKEN + + ";configuration-ldappublish-publisher-ocsppublisher", + IExtendedPluginInfo.HELP_TEXT + + ";Publishes CRLs to a Online Certificate Status Manager, an OCSP responder provided by CMS." + }; + + return params; + } + + /** + * Returns the current instance parameters. + */ + public Vector getInstanceParams() { + Vector v = new Vector(); + String host = ""; + String port = ""; + String path = ""; + String nickname = ""; + String clientAuthEnabled = ""; + + try { + host = mConfig.getString(PROP_HOST); + } catch (EBaseException e) { + } + v.addElement(PROP_HOST + "=" + host); + try { + port = mConfig.getString(PROP_PORT); + } catch (EBaseException e) { + } + v.addElement(PROP_PORT + "=" + port); + try { + path = mConfig.getString(PROP_PATH); + } catch (EBaseException e) { + } + v.addElement(PROP_PATH + "=" + path); + try { + nickname = mConfig.getString(PROP_NICK); + } catch (EBaseException e) { + } + v.addElement(PROP_NICK + "=" + nickname); + try { + clientAuthEnabled = mConfig.getString(PROP_CLIENT_AUTH_ENABLE); + } catch (EBaseException e) { + } + v.addElement(PROP_CLIENT_AUTH_ENABLE + "=" + clientAuthEnabled); + return v; + } + + /** + * Returns the initial default parameters. + */ + public Vector getDefaultParams() { + Vector v = new Vector(); + + IConfigStore config = CMS.getConfigStore(); + String nickname = ""; + // get subsystem cert nickname as default for client auth + try { + nickname = config.getString("ca.subsystem.nickname", ""); + String tokenname = config.getString("ca.subsystem.tokenname", ""); + if (!tokenname.equals("internal") && !tokenname.equals("Internal Key Storage Token")) + nickname = tokenname + ":" + nickname; + } catch (Exception e) { + } + + v.addElement(PROP_HOST + "="); + v.addElement(PROP_PORT + "="); + v.addElement(PROP_PATH + "=/ocsp/agent/ocsp/addCRL"); + v.addElement(PROP_CLIENT_AUTH_ENABLE + "=true"); + v.addElement(PROP_NICK + "=" + nickname); + return v; + } + + /** + * Initializes this plugin. + */ + public void init(IConfigStore config) { + mConfig = config; + try { + mHost = mConfig.getString(PROP_HOST, ""); + mPort = mConfig.getString(PROP_PORT, ""); + mPath = mConfig.getString(PROP_PATH, ""); + mNickname = mConfig.getString(PROP_NICK, ""); + mClientAuthEnabled = mConfig.getBoolean(PROP_CLIENT_AUTH_ENABLE, true); + } catch (EBaseException e) { + } + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + protected Socket Connect(String host, boolean secure, JssSSLSocketFactory factory) { + Socket socket = null; + StringTokenizer st = new StringTokenizer(host, " "); + while (st.hasMoreTokens()) { + String hp = st.nextToken(); // host:port + StringTokenizer st1 = new StringTokenizer(hp, ":"); + String h = st1.nextToken(); + int p = Integer.parseInt(st1.nextToken()); + try { + if (secure) { + socket = factory.makeSocket(h, p); + } else { + socket = new Socket(h, p); + } + return socket; + } catch (Exception e) { + } + try { + Thread.sleep(5000); // 5 seconds delay + } catch (Exception e) { + } + } + return null; + } + + /** + * Publishs a object to the ldap directory. + * + * @param conn a Ldap connection + * (null if LDAP publishing is not enabled) + * @param dn dn of the ldap entry to publish cert + * (null if LDAP publishing is not enabled) + * @param object object to publish + * (java.security.cert.X509Certificate or, + * java.security.cert.X509CRL) + */ + public synchronized void publish(LDAPConnection conn, String dn, Object object) + throws ELdapException { + try { + if (!(object instanceof X509CRL)) + return; + X509CRL crl = (X509CRL) object; + + // talk to agent port of CMS + + // open the connection and prepare it to POST + boolean secure = true; + + String host = mHost; + int port = Integer.parseInt(mPort); + String path = mPath; + + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_OTHER, + ILogger.LL_INFO, "OCSPPublisher: " + + "Host='" + host + "' Port='" + port + + "' URL='" + path + "'"); + CMS.debug("OCSPPublisher: " + + "Host='" + host + "' Port='" + port + + "' URL='" + path + "'"); + + StringBuffer query = new StringBuffer(); + query.append("crl="); + query.append(URLEncoder.encode("-----BEGIN CERTIFICATE REVOCATION LIST-----\n", "UTF-8")); + query.append(URLEncoder.encode(CMS.BtoA(crl.getEncoded()), "UTF-8")); + query.append(URLEncoder.encode("\n-----END CERTIFICATE REVOCATION LIST-----", "UTF-8")); + query.append("&noui=true"); + + Socket socket = null; + JssSSLSocketFactory factory; + + if (mClientAuthEnabled) { + factory = new JssSSLSocketFactory(mNickname); + } else { + factory = new JssSSLSocketFactory(); + } + + if (mHost != null && mHost.indexOf(' ') != -1) { + // support failover hosts configuration + // host parameter can be + // "directory.knowledge.com:1050 people.catalog.com 199.254.1.2" + do { + socket = Connect(mHost, secure, factory); + } while (socket == null); + } else { + if (secure) { + socket = factory.makeSocket(host, port); + } else { + socket = new Socket(host, port); + } + } + + if (socket == null) { + CMS.debug("OCSPPublisher::publish() - socket is null!"); + throw new ELdapException("socket is null"); + } + + // use HttpRequest and POST + HttpRequest httpReq = new HttpRequest(); + + httpReq.setMethod("POST"); + httpReq.setURI(path); + httpReq.setHeader("Connection", "Keep-Alive"); + + httpReq.setHeader("Content-Type", + "application/x-www-form-urlencoded"); + httpReq.setHeader("Content-Transfer-Encoding", "7bit"); + + httpReq.setHeader("Content-Length", + Integer.toString(query.length())); + httpReq.setContent(query.toString()); + OutputStream os = socket.getOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(os, "UTF8"); + + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_OTHER, + ILogger.LL_INFO, "OCSPPublisher: start sending CRL"); + long startTime = CMS.getCurrentDate().getTime(); + CMS.debug("OCSPPublisher: start CRL sending startTime=" + startTime); + httpReq.write(outputStreamWriter); + long endTime = CMS.getCurrentDate().getTime(); + CMS.debug("OCSPPublisher: done CRL sending endTime=" + endTime + " diff=" + (endTime - startTime)); + + // Read the response + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_OTHER, + ILogger.LL_INFO, "OCSPPublisher: start getting response"); + DataInputStream dis = new DataInputStream(socket.getInputStream()); + String nextline; + String error = ""; + boolean status = false; + + while ((nextline = dis.readLine()) != null) { + if (nextline.startsWith("status=")) { + if (nextline.substring(7, nextline.length()).equals("0")) { + status = true; + } + } + if (nextline.startsWith("error=")) { + error = nextline.substring(6, nextline.length()); + } + } + dis.close(); + if (status) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_OTHER, + ILogger.LL_INFO, "OCSPPublisher: successful"); + } else { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_OTHER, + ILogger.LL_INFO, "OCSPPublisher: failed - " + error); + } + + } catch (IOException e) { + CMS.debug("OCSPPublisher: publish failed " + e.toString()); + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_OTHER, + ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_OCSP_PUBLISHER_ERROR", e.toString())); + } catch (CRLException e) { + CMS.debug("OCSPPublisher: publish failed " + e.toString()); + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_OTHER, + ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_OCSP_PUBLISHER_ERROR", e.toString())); + } catch (Exception e) { + CMS.debug("OCSPPublisher: publish failed " + e.toString()); + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_OTHER, + ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_OCSP_PUBLISHER_ERROR", e.toString())); + } + } + + /** + * Unpublishs a object to the ldap directory. + * + * @param conn the Ldap connection + * (null if LDAP publishing is not enabled) + * @param dn dn of the ldap entry to unpublish cert + * (null if LDAP publishing is not enabled) + * @param object object to unpublish + * (java.security.cert.X509Certificate) + */ + public void unpublish(LDAPConnection conn, String dn, Object object) + throws ELdapException { + // NOT USED + } +} diff --git a/base/common/src/com/netscape/cms/publish/publishers/PublisherUtils.java b/base/common/src/com/netscape/cms/publish/publishers/PublisherUtils.java new file mode 100644 index 000000000..af8d283dd --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/publishers/PublisherUtils.java @@ -0,0 +1,136 @@ +// --- 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.publish.publishers; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Vector; + +/** + * Publisher utility class. + * + * @version $Revision$, $Date$ + */ +public class PublisherUtils { + public static void checkHost(String hostname) throws UnknownHostException { + InetAddress.getByName(hostname); + } + + public static void copyStream(InputStream in, OutputStream out) throws IOException { + byte[] buf = new byte[4096]; + int len; + + while ((len = in.read(buf)) != -1) { + out.write(buf, 0, len); + } + } + + public static void copyStream(BufferedReader in, OutputStreamWriter out) throws IOException { + char[] buf = new char[4096]; + int len; + + while ((len = in.read(buf)) != -1) { + out.write(buf, 0, len); + } + } + + /// Sorts an array of Strings. + // Java currently has no general sort function. Sorting Strings is + // common enough that it's worth making a special case. + public static void sortStrings(String[] strings) { + // Just does a bubblesort. + for (int i = 0; i < strings.length - 1; ++i) { + for (int j = i + 1; j < strings.length; ++j) { + if (strings[i].compareTo(strings[j]) > 0) { + String t = strings[i]; + + strings[i] = strings[j]; + strings[j] = t; + } + } + } + } + + /// Returns a date string formatted in Unix ls style - if it's within + // six months of now, Mmm dd hh:ss, else Mmm dd yyyy. + public static String lsDateStr(Date date) { + long dateTime = date.getTime(); + + if (dateTime == -1L) + return "------------"; + long nowTime = System.currentTimeMillis(); + SimpleDateFormat formatter = new SimpleDateFormat(); + + if (Math.abs(nowTime - dateTime) < 183L * 24L * 60L * 60L * 1000L) + formatter.applyPattern("MMM dd hh:ss"); + else + formatter.applyPattern("MMM dd yyyy"); + return formatter.format(date); + } + + /** + * compares contents two byte arrays returning true if exactly same. + */ + static public boolean byteArraysAreEqual(byte[] a, byte[] b) { + if (a.length != b.length) + return false; + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) + return false; + } + return true; + } + + /** + * strips out double quotes around String parameter + * + * @param s the string potentially bracketed with double quotes + * @return string stripped of surrounding double quotes + */ + public static String stripQuotes(String s) { + if (s == null) { + return s; + } + + if ((s.startsWith("\"")) && (s.endsWith("\""))) { + return (s.substring(1, (s.length() - 1))); + } + + return s; + } + + /** + * returns an array of strings from a vector of Strings + * there'll be trouble if the Vector contains something other + * than just Strings + */ + public static String[] getStringArrayFromVector(Vector v) { + String s[] = new String[v.size()]; + + v.copyInto(s); + return s; + } + +} -- cgit