diff options
author | PKI Team <PKI Team@c9f7a03b-bd48-0410-a16d-cbbf54688b0b> | 2008-03-18 22:36:57 +0000 |
---|---|---|
committer | PKI Team <PKI Team@c9f7a03b-bd48-0410-a16d-cbbf54688b0b> | 2008-03-18 22:36:57 +0000 |
commit | d0f2e4efbd3eb0f1d7f5a28e7f97c1fb4ec027bb (patch) | |
tree | 7e7473fae8af5ad7e6cda7eabbef787093fc59a7 /pki/base/common/src/com/netscape/cms/publish | |
parent | 273f8d85df5c31293a908185622b378c8f3cf7e8 (diff) | |
download | pki-d0f2e4efbd3eb0f1d7f5a28e7f97c1fb4ec027bb.tar.gz pki-d0f2e4efbd3eb0f1d7f5a28e7f97c1fb4ec027bb.tar.xz pki-d0f2e4efbd3eb0f1d7f5a28e7f97c1fb4ec027bb.zip |
Initial open source version based upon proprietary Red Hat Certificate System (RHCS) 7.3.
git-svn-id: svn+ssh://svn.fedorahosted.org/svn/pki/trunk@2 c9f7a03b-bd48-0410-a16d-cbbf54688b0b
Diffstat (limited to 'pki/base/common/src/com/netscape/cms/publish')
22 files changed, 6847 insertions, 0 deletions
diff --git a/pki/base/common/src/com/netscape/cms/publish/mappers/AVAPattern.java b/pki/base/common/src/com/netscape/cms/publish/mappers/AVAPattern.java new file mode 100644 index 000000000..65febcf15 --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/mappers/AVAPattern.java @@ -0,0 +1,592 @@ +// --- 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 com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.request.IRequest; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + +/* LDAP imports */ +import netscape.ldap.*; + +/* security imports */ +import netscape.security.util.*; +import netscape.security.x509.*; + +/* java sdk imports */ +import java.io.*; +import java.security.*; +import java.util.*; + + +////////////////////// +// class definition // +////////////////////// + +/** + * avaPattern is a string representing an ldap + * attribute formulated from the certificate + * subject name, extension or request attributes. + * <p> + * + * The syntax is + * <pre> + * avaPattern := constant-value | + * "$subj" "." attrName [ "." attrNumber ] | + * "$req" "." [ prefix .] attrName [ "." attrNumber ] | + * "$ext" "." extName [ "." nameType ] [ "." attrNumber ] + * </pre> + * <pre> + * Example: <i>$ext.SubjectAlternativeName.RFC822Name.1</i> + * cert subjectAltName is rfc822Name: jjames@mcom.com + * <p> + * The ldap attribute formulated will be : <br> + * jjames@mcom.com + * <p> + * The first rfc822name value in the subjAltName extension. <br> + * <p> + * </pre> + * If a request attribute or subject DN component does not exist, + * the attribute is skipped. + * + * @version $Revision: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +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/pki/base/common/src/com/netscape/cms/publish/mappers/LdapCaSimpleMap.java b/pki/base/common/src/com/netscape/cms/publish/mappers/LdapCaSimpleMap.java new file mode 100644 index 000000000..24076afd4 --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/mappers/LdapCaSimpleMap.java @@ -0,0 +1,396 @@ +// --- 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 netscape.ldap.*; +import netscape.ldap.util.*; +import java.io.*; +import java.util.*; +import java.security.*; +import java.security.cert.*; +import netscape.security.x509.*; +import netscape.security.util.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.request.IRequest; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + + +/** + * 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: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +public class LdapCaSimpleMap implements ILdapMapper, IExtendedPluginInfo { + protected static final String PROP_DNPATTERN = "dnPattern"; + protected static final String PROP_CREATECA = "createCAEntry"; + protected static final String PROP_CA_V2 = "CAEntryV2"; + 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", + "CAEntryV2;boolean;If checked, CA entry will be created using certificationAuthority-v2", + 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); + String[] mReqAttrs = mPattern.getReqAttrs(); + String[] mCertAttrs = mPattern.getCertAttrs(); + } 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[] = null; + boolean isV2 = false; + try { + isV2 = mConfig.getBoolean(PROP_CA_V2, false); + } catch (EBaseException e) { + // do nothing; default false + } + if (isV2) { + caOc = new String[] {"top", + "person", + "organizationalPerson", + "inetOrgPerson", + "certificationAuthority-v2"}; + } else { + caOc = new String[] {"top", + "person", + "organizationalPerson", + "inetOrgPerson", + "certificationAuthority"}; + } + + String oOc[] = {"top", + "organization"}; + String oiOc[] = {"top", + "organizationalunit"}; + + 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)); + attrs.add(new LDAPAttribute( + "authorityRevocationList;binary", "")); + attrs.add(new LDAPAttribute( + "cACertificate;binary", "")); + attrs.add(new LDAPAttribute( + "certificateRevocationList;binary", "")); + if (isV2) { + attrs.add(new LDAPAttribute( + "deltaRevocationList;binary", "")); + } + 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"); + v.addElement(PROP_CA_V2 + "=false"); + 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)); + v.addElement(PROP_CA_V2 + "=" + mConfig.getBoolean(PROP_CA_V2, false)); + } 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/pki/base/common/src/com/netscape/cms/publish/mappers/LdapCertCompsMap.java b/pki/base/common/src/com/netscape/cms/publish/mappers/LdapCertCompsMap.java new file mode 100644 index 000000000..a3a00a032 --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/mappers/LdapCertCompsMap.java @@ -0,0 +1,179 @@ +// --- 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 netscape.ldap.*; +import java.io.*; +import java.util.*; +import java.security.*; +import java.security.cert.*; +import netscape.security.x509.*; +import netscape.security.util.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.request.IRequest; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + + +/** + * 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: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +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/pki/base/common/src/com/netscape/cms/publish/mappers/LdapCertExactMap.java b/pki/base/common/src/com/netscape/cms/publish/mappers/LdapCertExactMap.java new file mode 100644 index 000000000..0812945a8 --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/mappers/LdapCertExactMap.java @@ -0,0 +1,193 @@ +// --- 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.*; +import java.util.*; +import java.security.*; +import java.security.cert.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.request.IRequest; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.apps.*; +import netscape.security.x509.*; +import netscape.ldap.*; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + + +/** + * Maps a X509 certificate to a LDAP entry by using the subject name + * of the certificate as the LDAP entry DN. + * + * @version $Revision: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +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 { + boolean hasCert = false; + boolean hasSubjectName = false; + 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/pki/base/common/src/com/netscape/cms/publish/mappers/LdapCertSubjMap.java b/pki/base/common/src/com/netscape/cms/publish/mappers/LdapCertSubjMap.java new file mode 100644 index 000000000..ae111b933 --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/mappers/LdapCertSubjMap.java @@ -0,0 +1,259 @@ +// --- 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.*; +import java.util.*; +import java.security.*; +import java.security.cert.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.request.IRequest; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.apps.*; +import netscape.security.x509.*; +import netscape.ldap.*; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + + +/** + * 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: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +public class LdapCertSubjMap implements ILdapMapper, IExtendedPluginInfo { + public static final String LDAP_CERTSUBJNAME_ATTR = "certSubjectName"; + protected String mSearchBase = null; + protected String mCertSubjNameAttr = LDAP_CERTSUBJNAME_ATTR; + + 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 String getImplName() { + return "LdapCertSubjMap"; + } + + public String getDescription() { + return "LdapCertSubjMap"; + } + + public Vector getDefaultParams() { + Vector v = new Vector(); + + v.addElement("certSubjNameAttr=" + mCertSubjNameAttr); + v.addElement("searchBase="); + 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", + 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); + } + 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"); + 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 { + boolean hasCert = false; + boolean hasSubjectName = false; + 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); + } + + 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; + } + +} + diff --git a/pki/base/common/src/com/netscape/cms/publish/mappers/LdapCrlIssuerCompsMap.java b/pki/base/common/src/com/netscape/cms/publish/mappers/LdapCrlIssuerCompsMap.java new file mode 100644 index 000000000..0357f5d31 --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/mappers/LdapCrlIssuerCompsMap.java @@ -0,0 +1,159 @@ +// --- 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 netscape.ldap.*; +import java.io.*; +import java.util.*; +import java.security.*; +import java.security.cert.*; +import netscape.security.x509.*; +import netscape.security.util.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.request.IRequest; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + + +/** + * 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: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +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/pki/base/common/src/com/netscape/cms/publish/mappers/LdapDNCompsMap.java b/pki/base/common/src/com/netscape/cms/publish/mappers/LdapDNCompsMap.java new file mode 100644 index 000000000..8e6ce1fc0 --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/mappers/LdapDNCompsMap.java @@ -0,0 +1,439 @@ +// --- 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 netscape.ldap.*; +import java.io.*; +import java.util.*; +import java.security.*; +import java.security.cert.*; +import netscape.security.x509.*; +import netscape.security.util.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + + +/** + * 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: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +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 { + 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/pki/base/common/src/com/netscape/cms/publish/mappers/LdapEnhancedMap.java b/pki/base/common/src/com/netscape/cms/publish/mappers/LdapEnhancedMap.java new file mode 100644 index 000000000..3d3f2fbbb --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/mappers/LdapEnhancedMap.java @@ -0,0 +1,639 @@ +// --- 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 com.netscape.certsrv.base.*; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.request.IRequest; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + +/* LDAP imports */ +import netscape.ldap.*; +import netscape.ldap.util.*; + +/* security imports */ +import netscape.security.util.*; +import netscape.security.x509.*; + +/* java sdk imports */ +import java.io.*; +import java.security.*; +import java.security.cert.*; +import java.util.*; + + +////////////////////// +// 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: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +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); + String[] mReqAttrs = mPattern.getReqAttrs(); + String[] mCertAttrs = mPattern.getCertAttrs(); + } 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/pki/base/common/src/com/netscape/cms/publish/mappers/LdapSimpleMap.java b/pki/base/common/src/com/netscape/cms/publish/mappers/LdapSimpleMap.java new file mode 100644 index 000000000..5a6117202 --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/mappers/LdapSimpleMap.java @@ -0,0 +1,320 @@ +// --- 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 netscape.ldap.*; +import java.io.*; +import java.util.*; +import java.security.*; +import java.security.cert.*; +import netscape.security.x509.*; +import netscape.security.util.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.request.IRequest; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + + +/** + * 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: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +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); + String[] mReqAttrs = mPattern.getReqAttrs(); + String[] mCertAttrs = mPattern.getCertAttrs(); + } 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/pki/base/common/src/com/netscape/cms/publish/mappers/MapAVAPattern.java b/pki/base/common/src/com/netscape/cms/publish/mappers/MapAVAPattern.java new file mode 100644 index 000000000..46967d27a --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/mappers/MapAVAPattern.java @@ -0,0 +1,634 @@ +// --- 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.*; +import com.netscape.certsrv.logging.*; +import java.io.*; +import java.security.*; +import netscape.security.x509.*; +import netscape.security.util.*; +import netscape.ldap.*; +import com.netscape.certsrv.request.IRequest; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + + +/** + * class for parsing a DN pattern used to construct a ldap dn from + * request attributes and cert subject name.<p> + * + * 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. <p> + * + * The syntax is + * <pre> + * dnPattern := rdnPattern *[ "," rdnPattern ] + * rdnPattern := avaPattern *[ "+" avaPattern ] + * avaPattern := name "=" value | + * name "=" "$subj" "." attrName [ "." attrNumber ] | + * name "=" "$ext" "." extName [ "." nameType ] [ "." attrNumber ] + * name "=" "$req" "." attrName [ "." attrNumber ] | + * "$rdn" "." number + * </pre> + * <pre> + * Example1: <i>cn=Certificate Manager,ou=people,o=mcom.com</i> + * cert subject name: dn: CN=Certificate Manager, OU=people, O=mcom.com + * request attributes: uid: cmanager + * <p> + * The dn formulated will be : <br> + * CN=Certificate Manager, OU=people, O=mcom.com + * <p> + * note: Subordinate ca enrollment will use ca mapper. Use predicate + * to distinguish the ca itself and the subordinates. + * + * Example2: <i>UID=$req.HTTP_PARAMS.uid, OU=$subj.ou, OU=people, , O=mcom.com</i> + * cert subject name: dn: UID=jjames, OU=IS, OU=people, , O=mcom.com + * request attributes: uid: cmanager + * <p> + * The dn formulated will be : <br> + * UID=jjames, OU=IS, OU=people, O=mcom.com + * <p> + * UID = the 'uid' attribute value in the request. <br> + * OU = the 'ou' value in the cert subject name. <br> + * O = the string mcom.com. <br> + * <p> + * Example3: <i>UID=$req.HTTP_PARAMS.uid, E=$ext.SubjectAlternativeName.RFC822Name.1, O=mcom.com</i> + * cert subject name: dn: UID=jjames, OU=IS, OU=people, O=mcom.com + * cert subjectAltName is rfc822Name: jjames@mcom.com + * request attributes: uid: cmanager + * <p> + * The dn formulated will be : <br> + * UID=jjames, E=jjames@mcom.com, O=mcom.com + * <p> + * UID = the 'uid' attribute value in the request. <br> + * E = The first rfc822name value in the subjAltName extension. <br> + * O = the string mcom.com. <br> + * <p> + * </pre> + * 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: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +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/pki/base/common/src/com/netscape/cms/publish/mappers/MapDNPattern.java b/pki/base/common/src/com/netscape/cms/publish/mappers/MapDNPattern.java new file mode 100644 index 000000000..b74ba0261 --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/mappers/MapDNPattern.java @@ -0,0 +1,202 @@ +// --- 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 com.netscape.certsrv.logging.*; +import java.security.*; +import java.security.cert.*; +import netscape.security.x509.*; +import netscape.security.util.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.apps.*; +import netscape.ldap.*; +import java.io.*; +import java.util.*; +import com.netscape.certsrv.request.IRequest; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + + +/** + * class for parsing a DN pattern used to construct a ldap dn from + * request attributes and cert subject name.<p> + * + * 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. <p> + * + * The syntax is + * <pre> + * dnPattern := rdnPattern *[ "," rdnPattern ] + * rdnPattern := avaPattern *[ "+" avaPattern ] + * avaPattern := name "=" value | + * name "=" "$subj" "." attrName [ "." attrNumber ] | + * name "=" "$req" "." attrName [ "." attrNumber ] | + * "$rdn" "." number + * </pre> + * <pre> + * Example1: <i>cn=Certificate Manager,ou=people,o=mcom.com</i> + * cert subject name: dn: CN=Certificate Manager, OU=people, O=mcom.com + * request attributes: uid: cmanager + * <p> + * The dn formulated will be : <br> + * CN=Certificate Manager, OU=people, O=mcom.com + * <p> + * note: Subordinate ca enrollment will use ca mapper. Use predicate + * to distinguish the ca itself and the subordinates. + * + * Example2: <i>UID=$req.HTTP_PARAMS.uid, OU=$subj.ou, O=people, , O=mcom.com</i> + * cert subject name: dn: UID=jjames, OU=IS, O=people, , O=mcom.com + * request attributes: uid: cmanager + * <p> + * The dn formulated will be : <br> + * UID=jjames, OU=IS, OU=people, O=mcom.com + * <p> + * UID = the 'uid' attribute value in the request. <br> + * OU = the 'ou' value in the cert subject name. <br> + * O = the string people, mcom.com. <br> + * <p> + * </pre> + * 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: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +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/pki/base/common/src/com/netscape/cms/publish/mappers/MapRDNPattern.java b/pki/base/common/src/com/netscape/cms/publish/mappers/MapRDNPattern.java new file mode 100644 index 000000000..c7bb8610a --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/mappers/MapRDNPattern.java @@ -0,0 +1,218 @@ +// --- 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 com.netscape.certsrv.base.*; +import com.netscape.certsrv.logging.*; +import netscape.ldap.*; +import java.io.*; +import java.util.*; +import java.security.*; +import java.security.cert.*; +import netscape.security.x509.*; +import netscape.security.util.*; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.request.IRequest; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + + +/** + * class for parsing a DN pattern used to construct a ldap dn from + * request attributes and cert subject name.<p> + * + * 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. <p> + * + * The syntax is + * <pre> + * dnPattern := rdnPattern *[ "," rdnPattern ] + * rdnPattern := avaPattern *[ "+" avaPattern ] + * avaPattern := name "=" value | + * name "=" "$subj" "." attrName [ "." attrNumber ] | + * name "=" "$req" "." attrName [ "." attrNumber ] | + * "$rdn" "." number + * </pre> + * <pre> + * Example1: <i>cn=Certificate Manager,ou=people,o=mcom.com</i> + * cert subject name: dn: CN=Certificate Manager, OU=people, O=mcom.com + * request attributes: uid: cmanager + * <p> + * The dn formulated will be : <br> + * CN=Certificate Manager, OU=people, O=mcom.com + * <p> + * note: Subordinate ca enrollment will use ca mapper. Use predicate + * to distinguish the ca itself and the subordinates. + * + * Example2: <i>UID=$req.HTTP_PARAMS.uid, OU=$subj.ou, O=people, , O=mcom.com</i> + * cert subject name: dn: UID=jjames, OU=IS, O=people, , O=mcom.com + * request attributes: uid: cmanager + * <p> + * The dn formulated will be : <br> + * UID=jjames, OU=IS, OU=people, O=mcom.com + * <p> + * UID = the 'uid' attribute value in the request. <br> + * OU = the 'ou' value in the cert subject name. <br> + * O = the string people, mcom.com. <br> + * <p> + * </pre> + * 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: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +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/pki/base/common/src/com/netscape/cms/publish/mappers/NoMap.java b/pki/base/common/src/com/netscape/cms/publish/mappers/NoMap.java new file mode 100644 index 000000000..d42dd7ecc --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/mappers/NoMap.java @@ -0,0 +1,108 @@ +// --- 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 netscape.ldap.*; +import java.io.*; +import java.util.*; +import java.security.*; +import java.security.cert.*; +import netscape.security.x509.*; +import netscape.security.util.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.request.IRequest; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + + +/** + * No Map + * + * @version $Revision: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +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/pki/base/common/src/com/netscape/cms/publish/publishers/FileBasedPublisher.java b/pki/base/common/src/com/netscape/cms/publish/publishers/FileBasedPublisher.java new file mode 100644 index 000000000..aced40a1e --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/publishers/FileBasedPublisher.java @@ -0,0 +1,312 @@ +// --- 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.math.*; +import java.io.*; +import java.security.cert.*; +import java.util.*; +import netscape.ldap.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + + +/** + * This publisher writes certificate and CRL into + * a directory. + * + * @version $Revision: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +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 IConfigStore mConfig = null; + private String mDir = null; + private ILogger mLogger = CMS.getLogger(); + private String mcrlIssuingPointId; + protected boolean mDerAttr = true; + protected boolean mB64Attr = false; + + 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 CRLs into *.der files", + PROP_B64 + ";boolean;Store CRLs into *.b64 files", + IExtendedPluginInfo.HELP_TOKEN + + ";configuration-ldappublish-publisher-filepublisher", + IExtendedPluginInfo.HELP_TEXT + + ";Stores the certificates or CRLs into files. Certificate is named as <IssuingPoint>-<serialno>.der, and CRL is named as *.der or *.b64." + }; + + return params; + } + + /** + * Returns the current instance parameters. + */ + public Vector getInstanceParams() { + Vector v = new Vector(); + String dir = ""; + + try { + dir = mConfig.getString(PROP_DIR); + } catch (EBaseException e) { + } + try { + v.addElement("directory=" + dir); + + v.addElement(PROP_DER+"=" + mConfig.getBoolean(PROP_DER,true)); + v.addElement(PROP_B64+"=" + mConfig.getBoolean(PROP_B64,false)); + } catch (Exception e) { + } + return v; + } + + /** + * Returns the initial default parameters. + */ + public Vector getDefaultParams() { + Vector v = new Vector(); + + v.addElement("directory="); + v.addElement(PROP_DER+"=true"); + v.addElement(PROP_B64+"=false"); + 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); + } 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; + } + + /** + * 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 { + try { + if (object instanceof X509Certificate) { + X509Certificate cert = (X509Certificate) object; + BigInteger sno = cert.getSerialNumber(); + String fileName = mDir + + File.separator + "cert-" + + sno.toString() + ".der"; + FileOutputStream fos = new FileOutputStream( + fileName); + + fos.write(cert.getEncoded()); + fos.close(); + } else if (object instanceof X509CRL) { + X509CRL crl = (X509CRL) object; + java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("yyMMddHHmmss"); + TimeZone tz = TimeZone.getTimeZone("GMT"); + format.setTimeZone(tz); + String GMTTime = "20" + format.format(crl.getThisUpdate()) ; + String prefix; + String deltaNumber = null; + if(mcrlIssuingPointId!=null && mcrlIssuingPointId.length()!=0) + { + com.netscape.certsrv.ca.ICertificateAuthority ca = + (com.netscape.certsrv.ca.ICertificateAuthority)CMS.getSubsystem(CMS.SUBSYSTEM_CA); + com.netscape.certsrv.ca.ICRLIssuingPoint currentIssuingPoint = + ca.getCRLIssuingPoint(mcrlIssuingPointId); + if(currentIssuingPoint.isDeltaCRLEnabled()){ + deltaNumber = currentIssuingPoint.getCRLNumber().toString(); + } + prefix = mcrlIssuingPointId+ "-"; + }else + prefix = "crl-"; + + + + + String baseName = mDir + File.separator + prefix + GMTTime; + if(deltaNumber!=null && deltaNumber.length()!=0) + baseName = baseName + "." + deltaNumber; + String tempFile = baseName + ".temp"; + FileOutputStream fos; + byte [] encodedArray = null; + File destFile = null; + String destName = null; + File renameFile = null; + if(mDerAttr==true) + { + fos = new FileOutputStream( + tempFile); + encodedArray = crl.getEncoded(); + fos.write(encodedArray); + fos.close(); + destName = baseName + ".der"; + destFile = new File(destName); + + if(destFile.exists()) + destFile.delete(); + renameFile = new File(tempFile); + renameFile.renameTo(destFile); + } + + // output base64 file + if(mB64Attr==true) + { + if (encodedArray ==null) + encodedArray = crl.getEncoded(); + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + fos = new FileOutputStream(tempFile); + fos.write(com.netscape.osutil.OSUtil.BtoA(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 { + if (object instanceof X509Certificate) { + X509Certificate cert = (X509Certificate) object; + BigInteger sno = cert.getSerialNumber(); + String fileName = mDir + + File.separator + "cert-" + + sno.toString() + ".der"; + File f = new File(fileName); + + f.delete(); + } else if (object instanceof X509CRL) { + X509CRL crl = (X509CRL) object; + String fileName = mDir + + File.separator + "crl-" + + crl.getThisUpdate().getTime() + ".der"; + File 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/pki/base/common/src/com/netscape/cms/publish/publishers/LdapCaCertPublisher.java b/pki/base/common/src/com/netscape/cms/publish/publishers/LdapCaCertPublisher.java new file mode 100644 index 000000000..d928e5ce7 --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/publishers/LdapCaCertPublisher.java @@ -0,0 +1,327 @@ +// --- 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 netscape.ldap.*; +import java.security.cert.*; +import java.io.*; +import java.util.*; +import netscape.security.x509.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + + +/** + * Interface for publishing a CA certificate to + * + * @version $Revision: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +public class LdapCaCertPublisher + implements ILdapPublisher, IExtendedPluginInfo { + public static final String LDAP_CACERT_ATTR = "caCertificate;binary"; + public static final String LDAP_CA_OBJECTCLASS = "certificationAuthority"; + + protected String mCaCertAttr = LDAP_CACERT_ATTR; + protected String mCaObjectclass = LDAP_CA_OBJECTCLASS; + + 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 objectclass which should be " + + "added to this entry, if it does not already exist", + IExtendedPluginInfo.HELP_TOKEN + + ";configuration-ldappublish-publisher-cacertpublisher", + IExtendedPluginInfo.HELP_TEXT + + ";This plugin knows how to publish the CA cert to " + + "'certificateAuthority'-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); + 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; + } + + // 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(); + + // check if entry has CA objectclass. If not use modify set. + LDAPSearchResults res = + conn.search(dn, LDAPv2.SCOPE_BASE, "(objectclass=*)", + new String[] { "objectclass", mCaCertAttr }, false); + LDAPEntry entry = res.next(); + LDAPAttribute ocs = entry.getAttribute("objectclass"); + LDAPAttribute certs = entry.getAttribute(mCaCertAttr); + + boolean hasCert = + LdapUserCertPublisher.ByteValueExists(certs, certEnc); + boolean hasoc = + LdapUserCertPublisher.StringValueExists(ocs, mCaObjectclass); + + LDAPModificationSet modSet = new LDAPModificationSet(); + + if (hasCert) { + log(ILogger.LL_INFO, "publish: CA " + dn + " already has Cert"); + //throw new ELdapException( + // LdapResources.ALREADY_PUBLISHED_1, dn); + return; + } 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"); + } + if (!hasoc) { + log(ILogger.LL_INFO, "adding CA objectclass to " + dn); + modSet.add(LDAPModification.ADD, + new LDAPAttribute("objectclass", mCaObjectclass)); + } + } + 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 { + 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); + boolean hasOC = + LdapUserCertPublisher.StringValueExists(ocs, mCaObjectclass); + + 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. + log(ILogger.LL_INFO, "unpublish: deleting CA oc from " + dn); + modSet.add(LDAPModification.DELETE, + new LDAPAttribute("objectclass", mCaObjectclass)); + } + 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/pki/base/common/src/com/netscape/cms/publish/publishers/LdapCertSubjPublisher.java b/pki/base/common/src/com/netscape/cms/publish/publishers/LdapCertSubjPublisher.java new file mode 100644 index 000000000..317770025 --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/publishers/LdapCertSubjPublisher.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.publishers; + + +import java.io.*; +import java.util.*; +import netscape.ldap.*; +import java.security.cert.*; +import netscape.security.x509.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + + +/** + * 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: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +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 + Enumeration vals = certs.getByteValues(); + byte[] val = null; + + while (vals.hasMoreElements()) { + val = (byte[]) vals.nextElement(); + if (Utils.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 (CertificateException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_UNPUBLISH_ERROR", e.toString())); + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_DECODING_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/pki/base/common/src/com/netscape/cms/publish/publishers/LdapCertificatePairPublisher.java b/pki/base/common/src/com/netscape/cms/publish/publishers/LdapCertificatePairPublisher.java new file mode 100644 index 000000000..4d79b41f9 --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/publishers/LdapCertificatePairPublisher.java @@ -0,0 +1,214 @@ +// --- 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 netscape.ldap.*; +import java.security.cert.*; +import java.io.*; +import java.util.*; +import netscape.security.x509.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + + +/** + * module for publishing a cross certificate pair to ldap + * crossCertificatePair attribute + * + * @version $Revision: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +public class LdapCertificatePairPublisher + implements ILdapPublisher, IExtendedPluginInfo { + public static final String LDAP_CROSS_CERT_PAIR_ATTR = "crossCertificatePair;binary"; + public static final String LDAP_CA_OBJECTCLASS = "certificationAuthority"; + + protected String mCrossCertPairAttr = LDAP_CROSS_CERT_PAIR_ATTR; + protected String mCaObjectclass = LDAP_CA_OBJECTCLASS; + + 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 objectclass which should be " + + "added to this entry, if it does not already exist", + IExtendedPluginInfo.HELP_TOKEN + + ";configuration-ldappublish-publisher-crosscertpairpublisher", + IExtendedPluginInfo.HELP_TEXT + + ";This plugin knows how to publish the CA cert to " + + "'certificateAuthority'-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 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); + 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 { + // check to see if already published + LDAPSearchResults res = + conn.search(dn, LDAPv2.SCOPE_BASE, "(objectclass=*)", + new String[] { "objectclass", "crosscertificatepair;binary" }, false); + LDAPEntry entry = res.next(); + LDAPAttribute certPairs = entry.getAttribute("crosscertificatepair;binary"); + + if (LdapUserCertPublisher.ByteValueExists(certPairs, pair) + == true) { + CMS.debug("LdapCertificatePairPublisher: cross cert pair bytes exist in publishing directory, do not publish again."); + return; + } + + // publish certificatePair + LDAPModificationSet modSet = new LDAPModificationSet(); + + modSet.add(LDAPModification.ADD, + new LDAPAttribute(mCrossCertPairAttr, pair)); + CMS.debug("LdapCertificatePairPublisher: in publish() about to publish with dn=" + dn); + + 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/pki/base/common/src/com/netscape/cms/publish/publishers/LdapCrlPublisher.java b/pki/base/common/src/com/netscape/cms/publish/publishers/LdapCrlPublisher.java new file mode 100644 index 000000000..b9c5f97d5 --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/publishers/LdapCrlPublisher.java @@ -0,0 +1,221 @@ +// --- 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.*; +import java.util.*; +import java.security.cert.*; +import netscape.ldap.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.apps.*; + +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + + +/** + * For publishing master or global CRL. + * Publishes (replaces) the CRL in the CA's LDAP entry. + * + * @version $Revision: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +public class LdapCrlPublisher implements ILdapPublisher, IExtendedPluginInfo { + private ILogger mLogger = CMS.getLogger(); + protected IConfigStore mConfig = null; + boolean mInited = false; + + public static final String + LDAP_CRL_ATTR = "certificateRevocationList;binary"; + + String mCrlAttr = LDAP_CRL_ATTR; + + /** + * 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", + IExtendedPluginInfo.HELP_TOKEN + + ";configuration-ldappublish-publisher-crlpublisher", + IExtendedPluginInfo.HELP_TEXT + + ";This plugin knows how to publish CRL's to an LDAP directory" + }; + + return params; + } + + public Vector getInstanceParams() { + Vector v = new Vector(); + + v.addElement("crlAttr=" + mCrlAttr); + return v; + } + + public Vector getDefaultParams() { + Vector v = new Vector(); + + v.addElement("crlAttr=" + mCrlAttr); + 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); + mInited = true; + } + + public LdapCrlPublisher(String crlAttr) { + mCrlAttr = crlAttr; + } + + /** + * 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; + } + + // 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); + conn.modify(dn, new LDAPModification(LDAPModification.REPLACE, + new LDAPAttribute(mCrlAttr, crlEnc))); + } 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(); + + LDAPSearchResults res = conn.search(dn, LDAPv2.SCOPE_BASE, + "(objectclass=*)", new String[] { mCrlAttr }, false); + LDAPEntry e = res.next(); + LDAPAttribute crls = e.getAttribute(mCrlAttr); + + if (!LdapUserCertPublisher.ByteValueExists(crls, crlEnc)) { + log(ILogger.LL_INFO, + "unpublish: " + dn + " already has not CRL"); + return; + } + conn.modify(dn, new LDAPModification(LDAPModification.DELETE, + new LDAPAttribute(mCrlAttr, crlEnc))); + } 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/pki/base/common/src/com/netscape/cms/publish/publishers/LdapEncryptCertPublisher.java b/pki/base/common/src/com/netscape/cms/publish/publishers/LdapEncryptCertPublisher.java new file mode 100644 index 000000000..1764d96c5 --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/publishers/LdapEncryptCertPublisher.java @@ -0,0 +1,342 @@ +// --- 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.*; +import java.math.*; +import netscape.ldap.*; +import java.security.cert.*; +import java.util.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.request.*; +import com.netscape.certsrv.publish.*; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.ca.*; +import com.netscape.certsrv.apps.*; +import netscape.security.x509.*; + + +/** + * Interface for mapping a X509 certificate to a LDAP entry + * + * @version $Revision: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +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); + if (at == null) { + return at; + } + + // 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; + } + + Enumeration vals = attr.getByteValues(); + byte[] val = null; + + while (vals.hasMoreElements()) { + val = (byte[]) 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; + } + Enumeration vals = attr.getByteValues(); + byte[] val = null; + + while (vals.hasMoreElements()) { + val = (byte[]) vals.nextElement(); + if (Utils.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; + } + Enumeration vals = attr.getStringValues(); + String val = null; + + while (vals.hasMoreElements()) { + val = (String) vals.nextElement(); + if (val.equalsIgnoreCase(sval)) { + return true; + } + } + return false; + } + +} + diff --git a/pki/base/common/src/com/netscape/cms/publish/publishers/LdapUserCertPublisher.java b/pki/base/common/src/com/netscape/cms/publish/publishers/LdapUserCertPublisher.java new file mode 100644 index 000000000..7c7a094f1 --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/publishers/LdapUserCertPublisher.java @@ -0,0 +1,315 @@ +// --- 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 netscape.ldap.*; +import java.security.cert.*; +import java.util.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + + +/** + * Interface for mapping a X509 certificate to a LDAP entry + * + * @version $Revision: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +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; + } + Enumeration vals = attr.getByteValues(); + byte[] val = null; + + while (vals.hasMoreElements()) { + val = (byte[]) vals.nextElement(); + if (val.length == 0) + continue; + if (Utils.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; + } + Enumeration vals = attr.getStringValues(); + String val = null; + + while (vals.hasMoreElements()) { + val = (String) vals.nextElement(); + if (val.equalsIgnoreCase(sval)) { + return true; + } + } + return false; + } + +} diff --git a/pki/base/common/src/com/netscape/cms/publish/publishers/OCSPPublisher.java b/pki/base/common/src/com/netscape/cms/publish/publishers/OCSPPublisher.java new file mode 100644 index 000000000..4ae744e43 --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/publishers/OCSPPublisher.java @@ -0,0 +1,315 @@ +// --- 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.net.*; +import java.math.*; +import java.io.*; +import java.security.cert.*; +import java.util.*; +import netscape.ldap.*; +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.logging.*; +import org.mozilla.jss.ssl.*; +import org.mozilla.jss.*; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; +import com.netscape.cmsutil.http.*; + + +/** + * This publisher writes certificate and CRL into + * a directory. + * + * @version $Revision: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +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 IConfigStore mConfig = null; + private String mHost = null; + private String mPort = null; + private String mPath = null; + 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 EE service", + PROP_PORT + ";string;Port of CMS's OCSP Secure EE service", + PROP_PATH + ";string;URI of CMS's OCSP Secure EE service", + 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 = ""; + + 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); + return v; + } + + /** + * Returns the initial default parameters. + */ + public Vector getDefaultParams() { + Vector v = new Vector(); + + v.addElement(PROP_HOST + "="); + v.addElement(PROP_PORT + "="); + v.addElement(PROP_PATH + "=/ocsp/ee/ocsp/addCRL"); + 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, ""); + } catch (EBaseException e) { + } + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + protected Socket Connect(String host, boolean secure) + { + 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) { + SSLSocket sec_socket = new SSLSocket(h, p); + sec_socket.setUseClientMode(true); + socket = sec_socket; + } 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")); + query.append(URLEncoder.encode(CMS.BtoA(crl.getEncoded()))); + query.append(URLEncoder.encode("\n-----END CERTIFICATE REVOCATION LIST-----")); + query.append("&noui=true"); + + Socket socket = null; + + 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); + } while (socket == null); + } else { + if (secure) { + SSLSocket sec_socket = new SSLSocket(host, port); + + sec_socket.setUseClientMode(true); + socket = sec_socket; + } 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 line = ""; + 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/pki/base/common/src/com/netscape/cms/publish/publishers/Utils.java b/pki/base/common/src/com/netscape/cms/publish/publishers/Utils.java new file mode 100644 index 000000000..2a1f24031 --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/publishers/Utils.java @@ -0,0 +1,131 @@ +// --- 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.net.*; +import java.io.*; +import java.util.*; +import java.text.*; + + +/** + * Publisher utility class. + * + * @version $Revision: 14561 $, $Date: 2007-05-01 10:28:56 -0700 (Tue, 01 May 2007) $ + */ +public class Utils { + public static void checkHost(String hostname) throws UnknownHostException { + InetAddress addr = 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; + } + +} |