diff options
author | Endi Sukma Dewata <edewata@redhat.com> | 2012-03-24 02:27:47 -0500 |
---|---|---|
committer | Endi Sukma Dewata <edewata@redhat.com> | 2012-03-26 11:43:54 -0500 |
commit | 621d9e5c413e561293d7484b93882d985b3fe15f (patch) | |
tree | 638f3d75761c121d9a8fb50b52a12a6686c5ac5c /base/common/src/com/netscape/cms/publish/mappers | |
parent | 40d3643b8d91886bf210aa27f711731c81a11e49 (diff) | |
download | pki-621d9e5c413e561293d7484b93882d985b3fe15f.tar.gz pki-621d9e5c413e561293d7484b93882d985b3fe15f.tar.xz pki-621d9e5c413e561293d7484b93882d985b3fe15f.zip |
Removed unnecessary pki folder.
Previously the source code was located inside a pki folder.
This folder was created during svn migration and is no longer
needed. This folder has now been removed and the contents have
been moved up one level.
Ticket #131
Diffstat (limited to 'base/common/src/com/netscape/cms/publish/mappers')
13 files changed, 4445 insertions, 0 deletions
diff --git a/base/common/src/com/netscape/cms/publish/mappers/AVAPattern.java b/base/common/src/com/netscape/cms/publish/mappers/AVAPattern.java new file mode 100644 index 000000000..7f70722d0 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/AVAPattern.java @@ -0,0 +1,594 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +// package statement // +/////////////////////// + +package com.netscape.cms.publish.mappers; + +/////////////////////// +// import statements // +/////////////////////// + +/* cert server imports */ +import java.io.IOException; +import java.io.PushbackReader; +import java.io.StringReader; +import java.util.Enumeration; +import java.util.StringTokenizer; +import java.util.Vector; + +import netscape.ldap.LDAPDN; +import netscape.security.x509.CertificateExtensions; +import netscape.security.x509.Extension; +import netscape.security.x509.GeneralName; +import netscape.security.x509.GeneralNameInterface; +import netscape.security.x509.GeneralNames; +import netscape.security.x509.LdapV3DNStrConverter; +import netscape.security.x509.OIDMap; +import netscape.security.x509.SubjectAlternativeNameExtension; +import netscape.security.x509.X500Name; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.publish.ECompSyntaxErr; +import com.netscape.certsrv.request.IRequest; + +////////////////////// +// class definition // +////////////////////// + +/** + * avaPattern is a string representing an ldap + * attribute formulated from the certificate + * subject name, extension or request attributes. + * <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$, $Date$ + */ +class AVAPattern { + //////////////// + // parameters // + //////////////// + + /* the value type of the dn component */ + public static final String TYPE_REQ = "$req"; + public static final String TYPE_SUBJ = "$subj"; + public static final String TYPE_EXT = "$ext"; + public static final String TYPE_CONSTANT = "constant"; + + public static final String[] GENERAL_NAME_TYPE = { "ANY", + "RFC822Name", + "DNSName", + "X400Name", + "DIRECTORYName", + "EDIName", + "URIName", + "IPAddress", + "OIDName" }; + + private static final char[] endChars = new char[] { '+', ',' }; + + private static final LdapV3DNStrConverter mLdapDNStrConverter = + new LdapV3DNStrConverter(); + + /* the list of request attributes needed by this AVA */ + protected String[] mReqAttrs = null; + + /* the list of cert attributes needed by this AVA*/ + protected String[] mCertAttrs = null; + + /* value type */ + protected String mType = null; + + /* value - could be name of a request attribute or + * cert subject attribute or extension name. + */ + protected String mValue = null; + + /* value type - general name type of an + * extension attribute if any. + */ + protected String mGNType = null; + + /* prefix - prefix of a request attribute if any. */ + protected String mPrefix = null; + + /* nth value of the ldap or dn attribute */ + protected int mElement = 0; + + protected String mTestDN = null; + + ///////////// + // methods // + ///////////// + + public AVAPattern(String component) + throws ELdapException { + if (component == null || component.length() == 0) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", component)); + } + + parse(new PushbackReader(new StringReader(component))); + } + + public AVAPattern(PushbackReader in) + throws ELdapException { + parse(in); + } + + private void parse(PushbackReader in) + throws ELdapException { + int c; + + // skip spaces + //System.out.println("============ AVAPattern Begin ==========="); + //System.out.println("skip spaces"); + + try { + while ((c = in.read()) == ' ' || c == '\t') {//System.out.println("spaces read "+(char)c); + ; + } + } catch (IOException e) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", "All blank")); + } + + if (c == -1) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", "All blank")); + } + + if (c == '$') { + // check for $subj $ext or $req + try { + c = in.read(); + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + + if (c == -1) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "expecting $subj $ext or $req in ava pattern")); + } + + if (c == 'r') { + try { + if (in.read() != 'e' || + in.read() != 'q' || + in.read() != '.') { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "expecting $req in ava pattern")); + } + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + + mType = TYPE_REQ; + //System.out.println("---- mtype $req"); + } else if (c == 's') { + try { + if (in.read() != 'u' || + in.read() != 'b' || + in.read() != 'j' || + in.read() != '.') { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "expecting $subj in ava pattern")); + } + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + + mType = TYPE_SUBJ; + //System.out.println("----- mtype $subj"); + } else if (c == 'e') { + try { + if (in.read() != 'x' || + in.read() != 't' || + in.read() != '.') { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "expecting $ext in ava pattern")); + } + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + + mType = TYPE_EXT; + //System.out.println("----- mtype $ext"); + } else { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "unknown keyword. expecting $subj $ext or $req.")); + } + + // get request attribute or + // cert subject or + // extension attribute + + StringBuffer valueBuf = new StringBuffer(); + + try { + while ((c = in.read()) != ',' && + c != -1 && c != '.' && c != '+') { + //System.out.println("mValue read "+(char)c); + valueBuf.append((char) c); + } + + if (c == '+' || c == ',') { // either ',' or '+' + in.unread(c); // pushback last , or + + } + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + + mValue = valueBuf.toString().trim(); + if (mValue.length() == 0) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "$subj $ext or $req attribute name expected")); + } + //System.out.println("----- mValue "+mValue); + + // get nth dn xxx not nth request attribute . + if (c == '.') { + StringBuffer attrNumberBuf = new StringBuffer(); + + try { + while ((c = in.read()) != ',' && c != -1 && c != '.' + && c != '+') { + //System.out.println("mElement read "+(char)c); + attrNumberBuf.append((char) c); + } + + if (c == ',' || c == '+') { // either ',' or '+' + in.unread(c); // pushback last , or + + } + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + + String attrNumber = attrNumberBuf.toString().trim(); + + if (attrNumber.length() == 0) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "nth element $req $ext or $subj expected")); + } + + try { + mElement = Integer.parseInt(attrNumber) - 1; + } catch (NumberFormatException e) { + + if (TYPE_REQ.equals(mType)) { + mPrefix = mValue; + mValue = attrNumber; + } else if (TYPE_EXT.equals(mType)) { + mGNType = attrNumber; + } else { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "Invalid format in nth element " + + "$req $ext or $subj")); + } + + // get nth request attribute . + if (c == '.') { + StringBuffer attrNumberBuf1 = new StringBuffer(); + + try { + while ((c = in.read()) != ',' && + c != -1 && c != '+') { + //System.out.println("mElement read "+ + // (char)c); + attrNumberBuf1.append((char) c); + } + + if (c != -1) { // either ',' or '+' + in.unread(c); // pushback last , or + + } + } catch (IOException ex) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", ex.toString())); + } + + String attrNumber1 = + attrNumberBuf1.toString().trim(); + + if (attrNumber1.length() == 0) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "nth element $req or $ext expected")); + } + + try { + mElement = Integer.parseInt(attrNumber1) - 1; + } catch (NumberFormatException ex) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "Invalid format in nth element " + + "$req or $ext.")); + } + } + } + } + //System.out.println("----- mElement "+mElement); + } else { + // value is constant. treat as regular ava. + mType = TYPE_CONSTANT; + + // parse ava value. + StringBuffer valueBuf = new StringBuffer(); + + valueBuf.append((char) c); + + // read forward to get attribute value + try { + while ((c = in.read()) != ',' && c != -1) { + valueBuf.append((char) c); + } + + if (c == '+' || c == ',') { // either ',' or '+' + in.unread(c); // pushback last , or + + } + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + + mValue = valueBuf.toString().trim(); + + /* try { + * AVA ava = mLdapDNStrConverter.parseAVA( + * valueBuf.toString()); + * mValue = ava.toLdapDNString(); + * //System.out.println("----- mValue "+mValue); + * } catch (IOException e) { + * throw new ECompSyntaxErr(e.toString()); + * } + */ + } + } + + public String formAVA(IRequest req, + X500Name subject, + CertificateExtensions extensions) + throws ELdapException { + if (TYPE_CONSTANT.equals(mType)) { + return mValue; + } + + if (TYPE_SUBJ.equals(mType)) { + String dn = subject.toString(); + + if (mTestDN != null) { + dn = mTestDN; + } + + //System.out.println("AVAPattern Using dn "+mTestDN); + String[] rdns = LDAPDN.explodeDN(dn, false); + + String value = null; + + int nFound = -1; + + for (int i = 0; i < rdns.length; i++) { + String[] avas = explodeRDN(rdns[i]); + + for (int j = 0; j < avas.length; j++) { + String[] exploded = explodeAVA(avas[j]); + + if (exploded[0].equalsIgnoreCase(mValue) && + ++nFound == mElement) { + value = exploded[1]; + break; + } + } + } + + if (value == null) { + return null; + } + + return value; + } + + if (TYPE_EXT.equals(mType)) { + + if (extensions != null) { + + for (int i = 0; i < extensions.size(); i++) { + Extension ext = (Extension) + extensions.elementAt(i); + + String extName = + OIDMap.getName(ext.getExtensionId()); + + int index = extName.lastIndexOf("."); + + if (index != -1) { + extName = extName.substring(index + 1); + } + + if (extName.equals(mValue)) { + // Check the extensions one by one. + // For now, just give subjectAltName + // as an example. + if (mValue.equalsIgnoreCase( + SubjectAlternativeNameExtension.NAME)) { + try { + GeneralNames subjectNames = (GeneralNames) + ((SubjectAlternativeNameExtension) + ext).get( + SubjectAlternativeNameExtension.SUBJECT_NAME); + + if (subjectNames.size() == 0) { + break; + } + + int j = 0; + + for (Enumeration<GeneralNameInterface> 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<String> avas = new Vector<String>(); + + StringTokenizer token = new StringTokenizer(rdn, "+"); + + while (token.hasMoreTokens()) { + avas.addElement(token.nextToken()); + } + + String[] theAvas = new String[avas.size()]; + + avas.copyInto(theAvas); + + return theAvas; + } + + /** + * Explode AVA into name and value. + * Does not handle escaped '=' + * If AVA is malformed empty array is returned. + */ + public static String[] explodeAVA(String ava) { + int equals = ava.indexOf('='); + + if (equals == -1) { + return null; + } + + return new String[] { ava.substring(0, equals).trim(), + ava.substring(equals + 1).trim() }; + } +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/LdapCaSimpleMap.java b/base/common/src/com/netscape/cms/publish/mappers/LdapCaSimpleMap.java new file mode 100644 index 000000000..bbf641540 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/LdapCaSimpleMap.java @@ -0,0 +1,372 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cms.publish.mappers; + +import java.io.IOException; +import java.security.cert.X509Certificate; +import java.util.Locale; +import java.util.Vector; + +import netscape.ldap.LDAPAttribute; +import netscape.ldap.LDAPAttributeSet; +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPSearchResults; +import netscape.ldap.LDAPv2; +import netscape.ldap.LDAPv3; +import netscape.ldap.util.DN; +import netscape.security.x509.CertificateExtensions; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CRLImpl; +import netscape.security.x509.X509CertImpl; +import netscape.security.x509.X509CertInfo; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.base.IExtendedPluginInfo; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.ldap.ELdapServerDownException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapMapper; +import com.netscape.certsrv.request.IRequest; + +/** + * Maps a request to an entry in the LDAP server. + * Takes a dnPattern to form the baseDN from the request attributes + * and certificate subject name.Do a base search for the entry + * in the directory to publish the cert or crl. + * The restriction of this mapper is that the ldap dn components must + * be part of certificate subject name or request attributes or constant. + * + * @version $Revision$, $Date$ + */ +public class LdapCaSimpleMap implements ILdapMapper, IExtendedPluginInfo { + protected static final String PROP_DNPATTERN = "dnPattern"; + protected static final String PROP_CREATECA = "createCAEntry"; + protected String mDnPattern = null; + protected boolean mCreateCAEntry = true; + + private ILogger mLogger = CMS.getLogger(); + private boolean mInited = false; + protected IConfigStore mConfig = null; + + /* the subject DN pattern */ + protected MapDNPattern mPattern = null; + + /* the list of request attriubutes to retrieve*/ + protected String[] mReqAttrs = null; + + /* the list of cert attriubutes to retrieve*/ + protected String[] mCertAttrs = null; + + /* default dn pattern if left blank or not set in the config */ + public static final String DEFAULT_DNPATTERN = + "UID=$req.HTTP_PARAMS.UID, OU=people, O=$subj.o, C=$subj.c"; + + /** + * Constructor. + * + * @param dnPattern The base DN. + */ + public LdapCaSimpleMap(String dnPattern) { + try { + init(dnPattern); + } catch (EBaseException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("OPERATION_ERROR", e.toString())); + } + + } + + /** + * constructor if initializing from config store. + */ + public LdapCaSimpleMap() { + } + + public String[] getExtendedPluginInfo(Locale locale) { + String params[] = { + "dnPattern;string;Describes how to form the Ldap Subject name in" + + " the directory. Example 1: 'uid=CertMgr, o=Fedora'. Example 2:" + + " 'uid=$req.HTTP_PARAMS.uid, E=$ext.SubjectAlternativeName.RFC822Name, ou=$subj.ou'. " + + "$req means: take the attribute from the request. " + + "$subj means: take the attribute from the certificate subject name. " + + "$ext means: take the attribute from the certificate extension", + "createCAEntry;boolean;If checked, CA entry will be created automatically", + IExtendedPluginInfo.HELP_TOKEN + ";configuration-ldappublish-mapper-casimplemapper", + IExtendedPluginInfo.HELP_TEXT + ";Describes how to form the LDAP DN of the entry to publish to" + }; + + return params; + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + /** + * for initializing from config store. + */ + public void init(IConfigStore config) + throws EBaseException { + mConfig = config; + String dnPattern = mConfig.getString(PROP_DNPATTERN); + + mCreateCAEntry = mConfig.getBoolean(PROP_CREATECA, true); + init(dnPattern); + } + + /** + * common initialization routine. + */ + protected void init(String dnPattern) + throws EBaseException { + if (mInited) + return; + + mDnPattern = dnPattern; + if (mDnPattern == null || mDnPattern.length() == 0) + mDnPattern = DEFAULT_DNPATTERN; + try { + mPattern = new MapDNPattern(mDnPattern); + } catch (ELdapException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_DN_PATTERN_INIT", dnPattern, e.toString())); + throw new EBaseException("falied to init with pattern " + + dnPattern + " " + e); + } + + mInited = true; + } + + /** + * Maps a X500 subject name to LDAP entry. + * Uses DN pattern to form a DN for a LDAP base search. + * + * @param conn the LDAP connection. + * @param obj the object to map. + * @exception ELdapException if any LDAP exceptions occured. + */ + public String map(LDAPConnection conn, Object obj) + throws ELdapException { + return map(conn, null, obj); + } + + /** + * Maps a X500 subject name to LDAP entry. + * Uses DN pattern to form a DN for a LDAP base search. + * + * @param conn the LDAP connection. + * @param req the request to map. + * @param obj the object to map. + * @exception ELdapException if any LDAP exceptions occured. + */ + public String map(LDAPConnection conn, IRequest req, Object obj) + throws ELdapException { + if (conn == null) + return null; + String dn = null; + + try { + dn = formDN(req, obj); + if (dn == null) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_DN_NOT_FORMED")); + String s1 = ""; + + if (req != null) + s1 = req.getRequestId().toString(); + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_NO_DN_MATCH", s1)); + } + int scope = LDAPv2.SCOPE_BASE; + String filter = "(objectclass=*)"; + + // search for entry + String[] attrs = new String[] { LDAPv3.NO_ATTRS }; + + log(ILogger.LL_INFO, "searching for dn: " + dn + " filter:" + + filter + " scope: base"); + + LDAPSearchResults results = + conn.search(dn, scope, filter, attrs, false); + LDAPEntry entry = results.next(); + + if (results.hasMoreElements()) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_MORE_THAN_ONE_ENTRY", dn, + ((req == null) ? "" : req.getRequestId().toString()))); + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_MORE_THAN_ONE_ENTRY", + ((req == null) ? "" : req.getRequestId().toString()))); + } + if (entry != null) + return entry.getDN(); + else { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_ENTRY_NOT_FOUND", dn, + ((req == null) ? "" : req.getRequestId().toString()))); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", + "null entry")); + } + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else if (e.getLDAPResultCode() == LDAPException.NO_SUCH_OBJECT && mCreateCAEntry) { + try { + createCAEntry(conn, dn); + log(ILogger.LL_INFO, "CA Entry " + dn + " Created"); + return dn; + } catch (LDAPException e1) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_DN_MAP_EXCEPTION", dn, e1.toString())); + if (e1.getLDAPResultCode() == LDAPException.CONSTRAINT_VIOLATION) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CA_ENTRY_NOT_CREATED")); + } else { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CA_ENTRY_NOT_CREATED1")); + } + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_CREATE_CA_FAILED", dn)); + } + } else { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_DN_MAP_EXCEPTION", dn, e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", e.toString())); + } + } catch (EBaseException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_EXCEPTION_CAUGHT", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", e.toString())); + } + } + + private void createCAEntry(LDAPConnection conn, String dn) + throws LDAPException { + LDAPAttributeSet attrs = new LDAPAttributeSet(); + // OID 2.5.6.16 + String caOc[] = new String[] { "top", + "person", + "organizationalPerson", + "inetOrgPerson" }; + + DN dnobj = new DN(dn); + String attrval[] = dnobj.explodeDN(true); + + attrs.add(new LDAPAttribute("cn", attrval[0])); + attrs.add(new LDAPAttribute("sn", attrval[0])); + attrs.add(new LDAPAttribute("objectclass", caOc)); + LDAPEntry entry = new LDAPEntry(dn, attrs); + + conn.add(entry); + } + + /** + * form a dn from component in the request and cert subject name + * + * @param req The request + * @param obj The certificate or crl + */ + private String formDN(IRequest req, Object obj) throws EBaseException { + X500Name subjectDN = null; + CertificateExtensions certExt = null; + + try { + X509Certificate cert = (X509Certificate) obj; + + subjectDN = + (X500Name) ((X509Certificate) cert).getSubjectDN(); + + CMS.debug("LdapCaSimpleMap: cert subject dn:" + subjectDN.toString()); + X509CertInfo info = (X509CertInfo) + ((X509CertImpl) cert).get( + X509CertImpl.NAME + "." + X509CertImpl.INFO); + + certExt = (CertificateExtensions) info.get( + CertificateExtensions.NAME); + } catch (java.security.cert.CertificateParsingException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CANT_GET_EXT", e.toString())); + } catch (IOException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CANT_GET_EXT", e.toString())); + } catch (java.security.cert.CertificateException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CANT_GET_EXT", e.toString())); + } catch (ClassCastException e) { + try { + X509CRLImpl crl = (X509CRLImpl) obj; + + subjectDN = + (X500Name) ((X509CRLImpl) crl).getIssuerDN(); + + CMS.debug("LdapCaSimpleMap: crl issuer dn: " + + subjectDN.toString()); + } catch (ClassCastException ex) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_PUBLISH_OBJ_NOT_SUPPORTED", + ((req == null) ? "" : req.getRequestId().toString()))); + return null; + } + } + try { + String dn = mPattern.formDN(req, subjectDN, certExt); + + return dn; + } catch (ELdapException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_FORM_DN", + ((req == null) ? "" : req.getRequestId().toString()), e.toString())); + throw new EBaseException("falied to form dn for request: " + + ((req == null) ? "" : req.getRequestId().toString()) + " " + e); + } + } + + public String getImplName() { + return "LdapCaSimpleMap"; + } + + public String getDescription() { + return "LdapCaSimpleMap"; + } + + public Vector<String> getDefaultParams() { + Vector<String> v = new Vector<String>(); + + v.addElement(PROP_DNPATTERN + "="); + v.addElement(PROP_CREATECA + "=true"); + return v; + } + + public Vector<String> getInstanceParams() { + Vector<String> v = new Vector<String>(); + + try { + if (mDnPattern == null) { + v.addElement(PROP_DNPATTERN + "="); + } else { + v.addElement(PROP_DNPATTERN + "=" + + mConfig.getString(PROP_DNPATTERN)); + } + v.addElement(PROP_CREATECA + "=" + mConfig.getBoolean(PROP_CREATECA, true)); + } catch (Exception e) { + } + return v; + } + + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapCaSimpleMapper: " + msg); + } + +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/LdapCertCompsMap.java b/base/common/src/com/netscape/cms/publish/mappers/LdapCertCompsMap.java new file mode 100644 index 000000000..2373e3c66 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/LdapCertCompsMap.java @@ -0,0 +1,178 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cms.publish.mappers; + +import java.security.cert.CRLException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.Vector; + +import netscape.ldap.LDAPConnection; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CRLImpl; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapMapper; +import com.netscape.certsrv.request.IRequest; + +/** + * Maps a X509 certificate to a LDAP entry using AVAs in the certificate's + * subject name to form the ldap search dn and filter. + * Takes a optional root search dn. + * The DN comps are used to form a LDAP entry to begin a subtree search. + * The filter comps are used to form a search filter for the subtree. + * If none of the DN comps matched, baseDN is used for the subtree. + * If the baseDN is null and none of the DN comps matched, it is an error. + * If none of the DN comps and filter comps matched, it is an error. + * If just the filter comps is null, a base search is performed. + * + * @version $Revision$, $Date$ + */ +public class LdapCertCompsMap + extends LdapDNCompsMap implements ILdapMapper { + ILogger mLogger = CMS.getLogger(); + + public LdapCertCompsMap() { + // need to support baseDN, dnComps, and filterComps + // via configuration + } + + /** + * Constructor. + * + * The DN comps are used to form a LDAP entry to begin a subtree search. + * The filter comps are used to form a search filter for the subtree. + * If none of the DN comps matched, baseDN is used for the subtree. + * If the baseDN is null and none of the DN comps matched, it is an error. + * If none of the DN comps and filter comps matched, it is an error. + * If just the filter comps is null, a base search is performed. + * + * @param baseDN The base DN. + * @param dnComps Components to form the LDAP base dn for search. + * @param filterComps Components to form the LDAP search filter. + */ + public LdapCertCompsMap(String baseDN, ObjectIdentifier[] dnComps, + ObjectIdentifier[] filterComps) { + init(baseDN, dnComps, filterComps); + } + + public String getImplName() { + return "LdapCertCompsMap"; + } + + public String getDescription() { + return "LdapCertCompsMap"; + } + + public Vector<String> getDefaultParams() { + Vector<String> v = super.getDefaultParams(); + + return v; + } + + public Vector<String> getInstanceParams() { + Vector<String> v = super.getInstanceParams(); + + return v; + } + + /** + * constructor using non-standard certificate attribute. + */ + public LdapCertCompsMap(String certAttr, String baseDN, + ObjectIdentifier[] dnComps, + ObjectIdentifier[] filterComps) { + super(certAttr, baseDN, dnComps, filterComps); + } + + protected void init(String baseDN, ObjectIdentifier[] dnComps, + ObjectIdentifier[] filterComps) { + super.init(baseDN, dnComps, filterComps); + } + + /** + * Maps a certificate to LDAP entry. + * Uses DN components and filter components to form a DN and + * filter for a LDAP search. + * If the formed DN is null the baseDN will be used. + * If the formed DN is null and baseDN is null an error is thrown. + * If the filter is null a base search is performed. + * If both are null an error is thrown. + * + * @param conn - the LDAP connection. + * @param obj - the X509Certificate. + */ + public String + map(LDAPConnection conn, Object obj) + throws ELdapException { + if (conn == null) + return null; + try { + X509Certificate cert = (X509Certificate) obj; + String result = null; + // form dn and filter for search. + X500Name subjectDN = + (X500Name) ((X509Certificate) cert).getSubjectDN(); + + CMS.debug("LdapCertCompsMap: " + subjectDN.toString()); + + byte[] certbytes = cert.getEncoded(); + + result = super.map(conn, subjectDN, certbytes); + return result; + } catch (CertificateEncodingException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CANT_DECODE_CERT", e.toString())); + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_GET_DER_ENCODED_CERT_FAILED", e.toString())); + } catch (ClassCastException e) { + try { + X509CRLImpl crl = (X509CRLImpl) obj; + String result = null; + X500Name issuerDN = + (X500Name) ((X509CRLImpl) crl).getIssuerDN(); + + CMS.debug("LdapCertCompsMap: " + issuerDN.toString()); + + byte[] crlbytes = crl.getEncoded(); + + result = super.map(conn, issuerDN, crlbytes); + return result; + } catch (CRLException ex) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CANT_DECODE_CRL", ex.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_GET_DER_ENCODED_CRL_FAILED", ex.toString())); + } catch (ClassCastException ex) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_NOT_SUPPORTED_OBJECT")); + return null; + } + } + } + + public String map(LDAPConnection conn, IRequest req, Object obj) + throws ELdapException { + return map(conn, obj); + } + + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapCertCompsMap: " + msg); + } + +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/LdapCertExactMap.java b/base/common/src/com/netscape/cms/publish/mappers/LdapCertExactMap.java new file mode 100644 index 000000000..11b53a797 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/LdapCertExactMap.java @@ -0,0 +1,199 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cms.publish.mappers; + +import java.security.cert.X509Certificate; +import java.util.Locale; +import java.util.Vector; + +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPSearchResults; +import netscape.ldap.LDAPv2; +import netscape.ldap.LDAPv3; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CRLImpl; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.base.IExtendedPluginInfo; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.ldap.ELdapServerDownException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapMapper; +import com.netscape.certsrv.request.IRequest; + +/** + * Maps a X509 certificate to a LDAP entry by using the subject name + * of the certificate as the LDAP entry DN. + * + * @version $Revision$, $Date$ + */ +public class LdapCertExactMap implements ILdapMapper, IExtendedPluginInfo { + private ILogger mLogger = CMS.getLogger(); + protected IConfigStore mConfig = null; + boolean mInited = false; + + /** + * constructs a certificate subject name mapper with search base. + */ + public LdapCertExactMap() { + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + public void init(IConfigStore config) + throws EBaseException { + if (mInited == true) + return; + mConfig = config; + mInited = true; + } + + public String[] getExtendedPluginInfo(Locale locale) { + String[] params = { + IExtendedPluginInfo.HELP_TOKEN + + ";configuration-ldappublish-mapper-certexactmapper", + IExtendedPluginInfo.HELP_TEXT + + ";Literally uses the subject name of the certificate as the DN to publish to" + }; + + return params; + } + + public String getImplName() { + return "LdapCertExactMap"; + } + + public String getDescription() { + return "LdapCertExactMap"; + } + + public Vector<String> getDefaultParams() { + Vector<String> v = new Vector<String>(); + + return v; + } + + public Vector<String> getInstanceParams() { + Vector<String> v = new Vector<String>(); + + return v; + } + + /** + * Finds the entry for the certificate by looking for the cert + * subject name in the subject name attribute. + * + * @param conn - the LDAP connection. + * @param obj - the X509Certificate. + */ + public String + map(LDAPConnection conn, Object obj) + throws ELdapException { + if (conn == null) + return null; + + X500Name subjectDN = null; + + try { + X509Certificate cert = (X509Certificate) obj; + + subjectDN = + (X500Name) ((X509Certificate) cert).getSubjectDN(); + + CMS.debug("LdapCertExactMap: cert subject dn:" + subjectDN.toString()); + } catch (ClassCastException e) { + try { + X509CRLImpl crl = (X509CRLImpl) obj; + + subjectDN = + (X500Name) ((X509CRLImpl) crl).getIssuerDN(); + + CMS.debug("LdapCertExactMap: crl issuer dn: " + + subjectDN.toString()); + } catch (ClassCastException ex) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_NOT_SUPPORTED_OBJECT")); + return null; + } + } + try { + String[] attrs = new String[] { LDAPv3.NO_ATTRS }; + + log(ILogger.LL_INFO, "Searching for " + subjectDN.toString()); + + LDAPSearchResults results = + conn.search(subjectDN.toString(), LDAPv2.SCOPE_BASE, + "(objectclass=*)", attrs, false); + + LDAPEntry entry = results.next(); + + if (results.hasMoreElements()) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_MORE_THAN_ONE_ENTRY", "", subjectDN.toString())); + } + if (entry != null) { + log(ILogger.LL_INFO, "entry found"); + return entry.getDN(); + } + return null; + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_DN_MAP_EXCEPTION", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", e.toString())); + } + } + + /* + catch (IOException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_GET_SUBJECT", e.toString())); + throw new ELdapException( + LdapResources.GET_CERT_SUBJECT_DN_FAILED, e); + } + catch (CertificateEncodingException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_DECODE_CERT", e.toString())); + throw new ELdapException( + LdapResources.GET_DER_ENCODED_CERT_FAILED, e); + } + */ + } + + public String map(LDAPConnection conn, IRequest req, Object obj) + throws ELdapException { + return map(conn, obj); + } + + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapCertExactMap: " + msg); + } + +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/LdapCertSubjMap.java b/base/common/src/com/netscape/cms/publish/mappers/LdapCertSubjMap.java new file mode 100644 index 000000000..4d5ff38c8 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/LdapCertSubjMap.java @@ -0,0 +1,343 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cms.publish.mappers; + +import java.security.cert.X509Certificate; +import java.util.Locale; +import java.util.Vector; + +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPSearchResults; +import netscape.ldap.LDAPv2; +import netscape.ldap.LDAPv3; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CRLImpl; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.base.IExtendedPluginInfo; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.ldap.ELdapServerDownException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapMapper; +import com.netscape.certsrv.request.IRequest; + +/** + * Maps a X509 certificate to a LDAP entry by finding an LDAP entry + * which has an attribute whose contents are equal to the cert subject name. + * + * @version $Revision$, $Date$ + */ +public class LdapCertSubjMap implements ILdapMapper, IExtendedPluginInfo { + public static final String LDAP_CERTSUBJNAME_ATTR = "certSubjectName"; + protected String mSearchBase = null; + protected String mCertSubjNameAttr = LDAP_CERTSUBJNAME_ATTR; + protected boolean mUseAllEntries = false; + + private ILogger mLogger = CMS.getLogger(); + protected IConfigStore mConfig = null; + boolean mInited = false; + + public LdapCertSubjMap() { + // need to setup the mSearchBase via configuration + } + + /** + * constructs a certificate subject name mapper with search base. + * + * @param searchBase the dn to start searching for the certificate + * subject name. + */ + public LdapCertSubjMap(String searchBase) { + if (searchBase == null) + throw new IllegalArgumentException( + "a null argument to constructor " + this.getClass().getName()); + mSearchBase = searchBase; + mInited = true; + } + + /** + * Constructor using non-ES cert map attribute name. + * + * @param searchBase entry to start search. + * @param certSubjNameAttr attribute for certificate subject names. + * @param certAttr attribute to find certificate. + */ + public LdapCertSubjMap(String searchBase, + String certSubjNameAttr, String certAttr) { + if (searchBase == null || + certSubjNameAttr == null || certAttr == null) + throw new IllegalArgumentException( + "a null argument to constructor " + this.getClass().getName()); + mCertSubjNameAttr = certSubjNameAttr; + mSearchBase = searchBase; + mInited = true; + } + + public LdapCertSubjMap(String searchBase, + String certSubjNameAttr, String certAttr, boolean useAllEntries) { + if (searchBase == null || + certSubjNameAttr == null || certAttr == null) + throw new IllegalArgumentException( + "a null argument to constructor " + this.getClass().getName()); + mCertSubjNameAttr = certSubjNameAttr; + mSearchBase = searchBase; + mUseAllEntries = useAllEntries; + mInited = true; + } + + public String getImplName() { + return "LdapCertSubjMap"; + } + + public String getDescription() { + return "LdapCertSubjMap"; + } + + public Vector<String> getDefaultParams() { + Vector<String> v = new Vector<String>(); + + v.addElement("certSubjNameAttr=" + mCertSubjNameAttr); + v.addElement("searchBase="); + v.addElement("useAllEntries=" + mUseAllEntries); + return v; + } + + public String[] getExtendedPluginInfo(Locale locale) { + String[] params = { + "certSubjNameAttr;string;Name of Ldap attribute containing cert subject name", + "searchBase;string;Base DN to search from", + "useAllEntries;boolean;Use all entries for publishing", + IExtendedPluginInfo.HELP_TOKEN + + ";configuration-ldappublish-mapper-certsubjmapper", + IExtendedPluginInfo.HELP_TEXT + + ";This plugin assumes you want to publish to an LDAP entry which has " + + "an attribute whose contents are equal to the cert subject name" + }; + + return params; + } + + public Vector<String> getInstanceParams() { + Vector<String> v = new Vector<String>(); + + if (mCertSubjNameAttr == null) { + v.addElement("certSubjNameAttr="); + } else { + v.addElement("certSubjNameAttr=" + mCertSubjNameAttr); + } + if (mSearchBase == null) { + v.addElement("searchBase="); + } else { + v.addElement("searchBase=" + mSearchBase); + } + v.addElement("useAllEntries=" + mUseAllEntries); + return v; + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + public void init(IConfigStore config) + throws EBaseException { + if (mInited == true) + return; + mConfig = config; + mCertSubjNameAttr = config.getString("certSubjNameAttr", + LDAP_CERTSUBJNAME_ATTR); + mSearchBase = config.getString("searchBase"); + mUseAllEntries = config.getBoolean("useAllEntries", false); + mInited = true; + } + + /** + * Finds the entry for the certificate by looking for the cert + * subject name in the subject name attribute. + * + * @param conn - the LDAP connection. + * @param obj - the X509Certificate. + */ + public String + map(LDAPConnection conn, Object obj) + throws ELdapException { + if (conn == null) + return null; + X500Name subjectDN = null; + + try { + X509Certificate cert = (X509Certificate) obj; + + subjectDN = + (X500Name) ((X509Certificate) cert).getSubjectDN(); + + CMS.debug("LdapCertSubjMap: cert subject dn:" + subjectDN.toString()); + } catch (ClassCastException e) { + try { + X509CRLImpl crl = (X509CRLImpl) obj; + + subjectDN = + (X500Name) ((X509CRLImpl) crl).getIssuerDN(); + + CMS.debug("LdapCertSubjMap: crl issuer dn: " + + subjectDN.toString()); + } catch (ClassCastException ex) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_NOT_SUPPORTED_OBJECT")); + return null; + } + } + try { + String[] attrs = new String[] { LDAPv3.NO_ATTRS }; + + log(ILogger.LL_INFO, "search " + mSearchBase + + " (" + mCertSubjNameAttr + "=" + subjectDN + ") " + mCertSubjNameAttr); + + LDAPSearchResults results = + conn.search(mSearchBase, LDAPv2.SCOPE_SUB, + "(" + mCertSubjNameAttr + "=" + subjectDN + ")", attrs, false); + + LDAPEntry entry = results.next(); + + if (results.hasMoreElements()) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_MORE_THAN_ONE_ENTRY", "", subjectDN.toString())); + } + if (entry != null) { + log(ILogger.LL_INFO, "entry found"); + return entry.getDN(); + } + return null; + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_DN_MAP_EXCEPTION", "LDAPException", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", e.toString())); + } + } + + /* + catch (IOException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_GET_SUBJECT", e.toString())); + throw new ELdapException( + LdapResources.GET_CERT_SUBJECT_DN_FAILED, e); + } + catch (CertificateEncodingException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_DECODE_CERT", e.toString())); + throw new ELdapException( + LdapResources.GET_DER_ENCODED_CERT_FAILED, e); + } + */ + } + + public String map(LDAPConnection conn, IRequest req, Object obj) + throws ELdapException { + return map(conn, obj); + } + + public Vector<String> mapAll(LDAPConnection conn, Object obj) + throws ELdapException { + Vector<String> v = new Vector<String>(); + + if (conn == null) + return null; + X500Name subjectDN = null; + + try { + X509Certificate cert = (X509Certificate) obj; + subjectDN = (X500Name) ((X509Certificate) cert).getSubjectDN(); + CMS.debug("LdapCertSubjMap: cert subject dn:" + subjectDN.toString()); + } catch (ClassCastException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_NOT_SUPPORTED_OBJECT")); + return v; + } + try { + String[] attrs = new String[] { LDAPv3.NO_ATTRS }; + + log(ILogger.LL_INFO, "search " + mSearchBase + + " (" + mCertSubjNameAttr + "=" + subjectDN + ") " + mCertSubjNameAttr); + + LDAPSearchResults results = + conn.search(mSearchBase, LDAPv2.SCOPE_SUB, + "(" + mCertSubjNameAttr + "=" + subjectDN + ")", attrs, false); + + while (results.hasMoreElements()) { + LDAPEntry entry = results.next(); + String dn = entry.getDN(); + v.addElement(dn); + CMS.debug("LdapCertSubjMap: dn=" + dn); + } + CMS.debug("LdapCertSubjMap: Number of entries: " + v.size()); + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_DN_MAP_EXCEPTION", "LDAPException", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", e.toString())); + } + } + + return v; + } + + public Vector<String> mapAll(LDAPConnection conn, IRequest req, Object obj) + throws ELdapException { + return mapAll(conn, obj); + } + + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapCertSubjMap: " + msg); + } + + /** + * return search base + */ + public String getSearchBase() { + return mSearchBase; + } + + /** + * return certificate subject attribute + */ + public String getCertSubjNameAttr() { + return mCertSubjNameAttr; + } + + public boolean useAllEntries() { + return mUseAllEntries; + } + +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/LdapCrlIssuerCompsMap.java b/base/common/src/com/netscape/cms/publish/mappers/LdapCrlIssuerCompsMap.java new file mode 100644 index 000000000..654de5d30 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/LdapCrlIssuerCompsMap.java @@ -0,0 +1,156 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cms.publish.mappers; + +import java.security.cert.CRLException; +import java.util.Vector; + +import netscape.ldap.LDAPConnection; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CRLImpl; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapMapper; +import com.netscape.certsrv.request.IRequest; + +/** + * Default crl mapper. + * maps the crl to a ldap entry by using components in the issuer name + * to find the CA's entry. + * + * @version $Revision$, $Date$ + */ +public class LdapCrlIssuerCompsMap + extends LdapDNCompsMap implements ILdapMapper { + ILogger mLogger = CMS.getLogger(); + + public LdapCrlIssuerCompsMap() { + // need to support baseDN, dnComps, and filterComps + // via configuration + } + + /** + * Constructor. + * + * The DN comps are used to form a LDAP entry to begin a subtree search. + * The filter comps are used to form a search filter for the subtree. + * If none of the DN comps matched, baseDN is used for the subtree. + * If the baseDN is null and none of the DN comps matched, it is an error. + * If none of the DN comps and filter comps matched, it is an error. + * If just the filter comps is null, a base search is performed. + * + * @param baseDN The base DN. + * @param dnComps Components to form the LDAP base dn for search. + * @param filterComps Components to form the LDAP search filter. + */ + public LdapCrlIssuerCompsMap(String baseDN, ObjectIdentifier[] dnComps, + ObjectIdentifier[] filterComps) { + init(baseDN, dnComps, filterComps); + } + + /** + * constructor using non-standard certificate attribute. + */ + public LdapCrlIssuerCompsMap(String crlAttr, String baseDN, + ObjectIdentifier[] dnComps, + ObjectIdentifier[] filterComps) { + super(crlAttr, baseDN, dnComps, filterComps); + } + + public String getImplName() { + return "LdapCrlIssuerCompsMap"; + } + + public String getDescription() { + return "LdapCrlIssuerCompsMap"; + } + + public Vector<String> getDefaultParams() { + Vector<String> v = super.getDefaultParams(); + + //v.addElement("crlAttr=" + LdapCrlPublisher.LDAP_CRL_ATTR); + return v; + } + + public Vector<String> getInstanceParams() { + Vector<String> v = super.getInstanceParams(); + + return v; + } + + protected void init(String baseDN, ObjectIdentifier[] dnComps, + ObjectIdentifier[] filterComps) { + //mLdapAttr = LdapCrlPublisher.LDAP_CRL_ATTR; + super.init(baseDN, dnComps, filterComps); + } + + /** + * Maps a crl to LDAP entry. + * Uses issuer DN components and filter components to form a DN and + * filter for a LDAP search. + * If the formed DN is null the baseDN will be used. + * If the formed DN is null and baseDN is null an error is thrown. + * If the filter is null a base search is performed. + * If both are null an error is thrown. + * + * @param conn - the LDAP connection. + * @param obj - the X509Certificate. + * @return the result. LdapCertMapResult is also used for CRL. + */ + public String + map(LDAPConnection conn, Object obj) + throws ELdapException { + if (conn == null) + return null; + X509CRLImpl crl = (X509CRLImpl) obj; + + try { + String result = null; + X500Name issuerDN = + (X500Name) ((X509CRLImpl) crl).getIssuerDN(); + + CMS.debug("LdapCrlIssuerCompsMap: " + issuerDN.toString()); + + byte[] crlbytes = crl.getEncoded(); + + result = super.map(conn, issuerDN, crlbytes); + return result; + } catch (CRLException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_DECODE_CRL", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_GET_DER_ENCODED_CRL_FAILED", e.toString())); + } + } + + public String map(LDAPConnection conn, IRequest req, Object obj) + throws ELdapException { + return map(conn, obj); + } + + /** + * overrides super's log(). + */ + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapCrlCompsMap: " + msg); + } + +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/LdapDNCompsMap.java b/base/common/src/com/netscape/cms/publish/mappers/LdapDNCompsMap.java new file mode 100644 index 000000000..73549f1b5 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/LdapDNCompsMap.java @@ -0,0 +1,457 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cms.publish.mappers; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Locale; +import java.util.StringTokenizer; +import java.util.Vector; + +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPSearchResults; +import netscape.ldap.LDAPv2; +import netscape.ldap.LDAPv3; +import netscape.security.util.DerValue; +import netscape.security.util.ObjectIdentifier; +import netscape.security.x509.AVA; +import netscape.security.x509.RDN; +import netscape.security.x509.X500Name; +import netscape.security.x509.X500NameAttrMap; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.base.IExtendedPluginInfo; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.ldap.ELdapServerDownException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapPlugin; + +/** + * Maps a Subject name to an entry in the LDAP server. + * subject name to form the ldap search dn and filter. + * Takes a optional root search dn. + * The DN comps are used to form a LDAP entry to begin a subtree search. + * The filter comps are used to form a search filter for the subtree. + * If none of the DN comps matched, baseDN is used for the subtree. + * If the baseDN is null and none of the DN comps matched, it is an error. + * If none of the DN comps and filter comps matched, it is an error. + * If just the filter comps is null, a base search is performed. + * + * @version $Revision$, $Date$ + */ +public class LdapDNCompsMap + implements ILdapPlugin, IExtendedPluginInfo { + //protected String mLdapAttr = null; + protected String mBaseDN = null; + protected ObjectIdentifier[] mDnComps = null; + protected ObjectIdentifier[] mFilterComps = null; + + private ILogger mLogger = CMS.getLogger(); + private boolean mInited = false; + protected IConfigStore mConfig = null; + + /** + * Constructor. + * + * The DN comps are used to form a LDAP entry to begin a subtree search. + * The filter comps are used to form a search filter for the subtree. + * If none of the DN comps matched, baseDN is used for the subtree. + * If the baseDN is null and none of the DN comps matched, it is an error. + * If none of the DN comps and filter comps matched, it is an error. + * If just the filter comps is null, a base search is performed. + * + * @param baseDN The base DN. + * @param dnComps Components to form the LDAP base dn for search. + * @param filterComps Components to form the LDAP search filter. + */ + public LdapDNCompsMap(String ldapAttr, String baseDN, + ObjectIdentifier[] dnComps, + ObjectIdentifier[] filterComps) { + //mLdapAttr = ldapAttr; + init(baseDN, dnComps, filterComps); + } + + /** + * constructor if initializing from config store. + */ + public LdapDNCompsMap() { + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + /** + * for initializing from config store. + */ + public void init(IConfigStore config) + throws EBaseException { + mConfig = config; + String baseDN = mConfig.getString("baseDN"); + ObjectIdentifier[] dnComps = + getCompsFromString(mConfig.getString("dnComps")); + ObjectIdentifier[] filterComps = + getCompsFromString(mConfig.getString("filterComps")); + + init(baseDN, dnComps, filterComps); + } + + public String getImplName() { + return "LdapDNCompsMap"; + } + + public String getDescription() { + return "LdapDNCompsMap"; + } + + public String[] getExtendedPluginInfo(Locale locale) { + String[] s = { + "baseDN;string;Base to search from. E.g ou=Engineering,o=Fedora", + "dnComps;string;Comma-separated list of attributes to put in the DN", + "filterComps;string;Comma-separated list of attributes to form the filter", + IExtendedPluginInfo.HELP_TOKEN + + ";configuration-ldappublish-mapper-dncompsmapper", + IExtendedPluginInfo.HELP_TEXT + + ";More complex mapper. Used when there is not enough information " + + "in the cert request to form the complete LDAP DN. Using this " + + "plugin, you can specify additional LDAP filters to narrow down the " + + "search" + }; + + return s; + } + + public Vector<String> getDefaultParams() { + Vector<String> v = new Vector<String>(); + + v.addElement("baseDN="); + v.addElement("dnComps="); + v.addElement("filterComps="); + return v; + } + + public Vector<String> getInstanceParams() { + Vector<String> v = new Vector<String>(); + + 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<RDN> dnRdns = new Vector<RDN>(); + 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<RDN> 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<Object> { + private static final long serialVersionUID = 4210302171279891828L; + + public String toFilterString() { + StringBuffer buf = new StringBuffer(); + + if (elementCount == 0) { + return null; + } + if (elementCount == 1) { + buf.append("(" + (String) elementData[0] + ")"); + return buf.toString(); + } + buf.append("(&"); + for (int i = 0; i < elementCount; i++) { + buf.append("(" + (String) elementData[i] + ")"); + } + buf.append(")"); + return buf.toString(); + } + } + + /** + * useful routine for parsing components given as string to + * arrays of objectidentifiers. + * The string is expected to be comma separated AVA attribute names. + * For example, "uid,cn,o,ou". Attribute names are case insensitive. + * + * @param val the string specifying the comps + * @exception ELdapException if any error occurs. + */ + public static ObjectIdentifier[] getCompsFromString(String val) + throws ELdapException { + StringTokenizer tokens; + ObjectIdentifier[] comps; + String attr; + ObjectIdentifier oid; + + if (val == null || val.length() == 0) + return new ObjectIdentifier[0]; + + tokens = new StringTokenizer(val, ", \t\n\r"); + comps = new ObjectIdentifier[tokens.countTokens()]; + if (comps.length == 0) { + return new ObjectIdentifier[0]; + } + int i = 0; + + while (tokens.hasMoreTokens()) { + attr = tokens.nextToken().trim(); + // mail -> E hack to look for E in subject names. + if (attr.equalsIgnoreCase("mail")) + attr = "E"; + oid = X500NameAttrMap.getDefault().getOid(attr); + if (oid != null) { + comps[i++] = oid; + } else { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_UNKNOWN_ATTR_IN_DN_FILTER_COMPS", attr)); + } + } + return comps; + } + +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/LdapEnhancedMap.java b/base/common/src/com/netscape/cms/publish/mappers/LdapEnhancedMap.java new file mode 100644 index 000000000..c9a7f867c --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/LdapEnhancedMap.java @@ -0,0 +1,640 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +// package statement // +/////////////////////// + +package com.netscape.cms.publish.mappers; + +/////////////////////// +// import statements // +/////////////////////// + +/* cert server imports */ +import java.io.IOException; +import java.security.cert.X509Certificate; +import java.util.Locale; +import java.util.Vector; + +import netscape.ldap.LDAPAttribute; +import netscape.ldap.LDAPAttributeSet; +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPSearchResults; +import netscape.ldap.LDAPv2; +import netscape.ldap.LDAPv3; +import netscape.ldap.util.DN; +import netscape.security.x509.CertificateExtensions; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CRLImpl; +import netscape.security.x509.X509CertImpl; +import netscape.security.x509.X509CertInfo; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.base.IExtendedPluginInfo; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.ldap.ELdapServerDownException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapMapper; +import com.netscape.certsrv.request.IRequest; + +////////////////////// +// class definition // +////////////////////// + +/** + * Maps a request to an entry in the LDAP server. + * Takes a dnPattern to form the baseDN from the + * request attributes and certificate subject name. + * Does a base search for the entry in the directory + * to publish the cert or crl. The restriction of + * this mapper is that the ldap dn components must + * be part of certificate subject name or request + * attributes or constant. The difference of this + * mapper and LdapSimpleMap is that if the ldap + * entry is not found, it has the option to create + * the ldap entry given the dn and attributes + * formulated. + * + * @version $Revision$, $Date$ + */ +public class LdapEnhancedMap + implements ILdapMapper, IExtendedPluginInfo { + //////////////////////// + // default parameters // + //////////////////////// + + ////////////////////////////////////// + // local LdapEnhancedMap parameters // + ////////////////////////////////////// + + private boolean mInited = false; + + // the subject DN pattern + protected MapDNPattern mPattern = null; + + // the list of request attriubutes to retrieve + protected String[] mReqAttrs = null; + + // the list of cert attributes to retrieve + protected String[] mCertAttrs = null; + + protected String[] mLdapValues = null; + + //////////////////////////// + // ILdapMapper parameters // + //////////////////////////// + + /* mapper plug-in fields */ + protected static final String PROP_DNPATTERN = "dnPattern"; + protected static final String PROP_CREATE = "createEntry"; + // the object class of the entry to be created. xxxx not done yet + protected static final String PROP_OBJCLASS = "objectClass"; + // req/cert/ext attribute --> directory attribute table + protected static final String PROP_ATTRNUM = "attrNum"; + protected static final String PROP_ATTR_NAME = "attrName"; + protected static final String PROP_ATTR_PATTERN = "attrPattern"; + + /* mapper plug-in fields initialization values */ + private static final int DEFAULT_NUM_ATTRS = 1; + + /* Holds mapper plug-in fields accepted by this implementation. + * This list is passed to the configuration console so configuration + * for instances of this implementation can be configured through the + * console. + */ + private static Vector<String> defaultParams = new Vector<String>(); + + static { + defaultParams.addElement(PROP_DNPATTERN + "="); + defaultParams.addElement(PROP_CREATE + "=true"); + defaultParams.addElement(PROP_ATTRNUM + "=" + DEFAULT_NUM_ATTRS); + for (int i = 0; i < DEFAULT_NUM_ATTRS; i++) { + defaultParams.addElement(PROP_ATTR_NAME + i + "="); + defaultParams.addElement(PROP_ATTR_PATTERN + i + "="); + } + } + + /* mapper plug-in values */ + protected String mDnPattern = null; + protected boolean mCreateEntry = true; + private int mNumAttrs = DEFAULT_NUM_ATTRS; + protected String[] mLdapNames = null; + protected String[] mLdapPatterns = null; + + /* miscellaneous constants local to this mapper plug-in */ + // default dn pattern if left blank or not set in the config + public static final String DEFAULT_DNPATTERN = + "UID=$req.HTTP_PARAMS.UID, " + + "OU=people, O=$subj.o, C=$subj.c"; + private static final int MAX_ATTRS = 10; + protected static final int DEFAULT_ATTRNUM = 1; + + /* miscellaneous variables local to this mapper plug-in */ + protected IConfigStore mConfig = null; + protected AVAPattern[] mPatterns = null; + + //////////////////////////////////// + // IExtendedPluginInfo parameters // + //////////////////////////////////// + + /////////////////////// + // Logger parameters // + /////////////////////// + + private ILogger mLogger = CMS.getLogger(); + + ///////////////////// + // default methods // + ///////////////////// + + /** + * Default constructor, initialization must follow. + */ + public LdapEnhancedMap() { + } + + /////////////////////////////////// + // local LdapEnhancedMap methods // + /////////////////////////////////// + + /** + * common initialization routine. + */ + protected void init(String dnPattern) + throws EBaseException { + if (mInited) { + return; + } + + mDnPattern = dnPattern; + if (mDnPattern == null || + mDnPattern.length() == 0) { + mDnPattern = DEFAULT_DNPATTERN; + } + + try { + mPattern = new MapDNPattern(mDnPattern); + } catch (ELdapException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_DN_PATTERN_INIT", + dnPattern, e.toString())); + throw new EBaseException( + "falied to init with pattern " + + dnPattern + " " + e); + } + + mInited = true; + } + + /** + * form a dn from component in the request and cert subject name + * + * @param req The request + * @param obj The certificate or crl + */ + private String formDN(IRequest req, Object obj) + throws EBaseException { + CertificateExtensions certExt = null; + X500Name subjectDN = null; + + try { + X509Certificate cert = (X509Certificate) obj; + + subjectDN = + (X500Name) ((X509Certificate) cert).getSubjectDN(); + CMS.debug( + "LdapEnhancedMap: cert subject dn:" + + subjectDN.toString()); + + //certExt = (CertificateExtensions) + // ((X509CertImpl)cert).get( + // X509CertInfo.EXTENSIONS); + X509CertInfo info = (X509CertInfo) + ((X509CertImpl) cert).get( + X509CertImpl.NAME + + "." + + X509CertImpl.INFO); + + certExt = (CertificateExtensions) + info.get(CertificateExtensions.NAME); + } catch (java.security.cert.CertificateParsingException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_GET_EXT", e.toString())); + } catch (IOException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_GET_EXT", e.toString())); + } catch (java.security.cert.CertificateException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_GET_EXT", e.toString())); + } catch (ClassCastException e) { + + try { + X509CRLImpl crl = (X509CRLImpl) obj; + + subjectDN = (X500Name) + ((X509CRLImpl) crl).getIssuerDN(); + + CMS.debug( + "LdapEnhancedMap: crl issuer dn: " + + + subjectDN.toString()); + } catch (ClassCastException ex) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_PUBLISH_OBJ_NOT_SUPPORTED", + ((req == null) ? "" + : req.getRequestId().toString()))); + return null; + } + } + + try { + mLdapValues = new String[mNumAttrs]; + + for (int i = 0; i < mNumAttrs; i++) { + if (mPatterns[i] != null) { + mLdapValues[i] = mPatterns[i].formAVA( + req, + subjectDN, + certExt); + } + } + + String dn = mPattern.formDN(req, subjectDN, certExt); + + return dn; + } catch (ELdapException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_CANT_FORM_DN", + ((req == null) ? "" + : req.getRequestId().toString()), e.toString())); + + throw new EBaseException( + "failed to form dn for request: " + + ((req == null) ? "" + : req.getRequestId().toString()) + + " " + e); + } + } + + private void createEntry(LDAPConnection conn, String dn) + throws LDAPException { + LDAPAttributeSet attrs = new LDAPAttributeSet(); + + // OID 2.5.6.16 + String caOc[] = { "top", + "person", + "organizationalPerson", + "inetOrgPerson" }; + + DN dnobj = new DN(dn); + String attrval[] = dnobj.explodeDN(true); + + attrs.add(new LDAPAttribute("cn", attrval[0])); + attrs.add(new LDAPAttribute("sn", attrval[0])); + attrs.add(new LDAPAttribute("objectclass", caOc)); + + for (int i = 0; i < mNumAttrs; i++) { + if (mLdapNames[i] != null && + !mLdapNames[i].trim().equals("") && + mLdapValues[i] != null && + !mLdapValues[i].trim().equals("")) { + attrs.add(new LDAPAttribute(mLdapNames[i], + mLdapValues[i])); + } + } + + LDAPEntry entry = new LDAPEntry(dn, attrs); + + conn.add(entry); + } + + ///////////////////////// + // ILdapMapper methods // + ///////////////////////// + + /** + * for initializing from config store. + * + * implementation for extended + * ILdapPlugin interface method + */ + public void init(IConfigStore config) + throws EBaseException { + mConfig = config; + + mDnPattern = mConfig.getString(PROP_DNPATTERN, + DEFAULT_DNPATTERN); + + mCreateEntry = mConfig.getBoolean(PROP_CREATE, + true); + + mNumAttrs = mConfig.getInteger(PROP_ATTRNUM, + 0); + + mLdapNames = new String[mNumAttrs]; + + mLdapPatterns = new String[mNumAttrs]; + + mPatterns = new AVAPattern[mNumAttrs]; + for (int i = 0; i < mNumAttrs; i++) { + mLdapNames[i] = + mConfig.getString(PROP_ATTR_NAME + + Integer.toString(i), + ""); + + mLdapPatterns[i] = + mConfig.getString(PROP_ATTR_PATTERN + + Integer.toString(i), + ""); + + if (mLdapPatterns[i] != null && + !mLdapPatterns[i].trim().equals("")) { + mPatterns[i] = new AVAPattern(mLdapPatterns[i]); + } + } + + init(mDnPattern); + } + + /** + * implementation for extended + * ILdapPlugin interface method + */ + public IConfigStore getConfigStore() { + return mConfig; + } + + public String getImplName() { + return "LdapEnhancedMap"; + } + + public String getDescription() { + return "LdapEnhancedMap"; + } + + public Vector<String> getDefaultParams() { + return defaultParams; + } + + public Vector<String> getInstanceParams() { + Vector<String> v = new Vector<String>(); + + 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<String> v = new Vector<String>(); + + v.addElement(PROP_DNPATTERN + + ";string;Describes how to form the Ldap " + + "Subject name in the directory. " + + "Example 1: 'uid=CertMgr, o=Fedora'. " + + "Example 2: 'uid=$req.HTTP_PARAMS.uid, " + + "E=$ext.SubjectAlternativeName.RFC822Name, " + + "ou=$subj.ou'. " + + "$req means: take the attribute from the " + + "request. " + + "$subj means: take the attribute from the " + + "certificate subject name. " + + "$ext means: take the attribute from the " + + "certificate extension"); + v.addElement(PROP_CREATE + + ";boolean;If checked, An entry will be " + + "created automatically"); + v.addElement(PROP_ATTRNUM + + ";string;How many attributes to add."); + v.addElement(IExtendedPluginInfo.HELP_TOKEN + + ";configuration-ldappublish-mapper-enhancedmapper"); + v.addElement(IExtendedPluginInfo.HELP_TEXT + + ";Describes how to form the LDAP DN of the " + + "entry to publish to"); + + for (int i = 0; i < MAX_ATTRS; i++) { + v.addElement(PROP_ATTR_NAME + + Integer.toString(i) + + ";string;" + + "The name of LDAP attribute " + + "to be added. e.g. mail"); + v.addElement(PROP_ATTR_PATTERN + + Integer.toString(i) + + ";string;" + + "How to create the LDAP attribute value. " + + "e.g. $req.HTTP_PARAMS.csrRequestorEmail, " + + "$subj.E or " + + "$ext.SubjectAlternativeName.RFC822Name"); + } + + String params[] = + com.netscape.cmsutil.util.Utils.getStringArrayFromVector(v); + + return params; + } + + //////////////////// + // Logger methods // + //////////////////// + + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapEnhancedMapper: " + msg); + } +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/LdapSimpleMap.java b/base/common/src/com/netscape/cms/publish/mappers/LdapSimpleMap.java new file mode 100644 index 000000000..642729673 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/LdapSimpleMap.java @@ -0,0 +1,332 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cms.publish.mappers; + +import java.io.IOException; +import java.security.cert.X509Certificate; +import java.util.Locale; +import java.util.Vector; + +import netscape.ldap.LDAPConnection; +import netscape.ldap.LDAPEntry; +import netscape.ldap.LDAPException; +import netscape.ldap.LDAPSearchResults; +import netscape.ldap.LDAPv2; +import netscape.ldap.LDAPv3; +import netscape.security.x509.CertificateExtensions; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CRLImpl; +import netscape.security.x509.X509CertImpl; +import netscape.security.x509.X509CertInfo; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.base.IExtendedPluginInfo; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.ldap.ELdapServerDownException; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.publish.ILdapMapper; +import com.netscape.certsrv.request.IRequest; + +/** + * Maps a request to an entry in the LDAP server. + * Takes a dnPattern to form the baseDN from the request attributes + * and certificate subject name.Do a base search for the entry + * in the directory to publish the cert or crl. + * The restriction of this mapper is that the ldap dn components must + * be part of certificate subject name or request attributes or constant. + * + * @version $Revision$, $Date$ + */ +public class LdapSimpleMap implements ILdapMapper, IExtendedPluginInfo { + protected static final String PROP_DNPATTERN = "dnPattern"; + protected String mDnPattern = null; + + private ILogger mLogger = CMS.getLogger(); + private boolean mInited = false; + protected IConfigStore mConfig = null; + + /* the subject DN pattern */ + protected MapDNPattern mPattern = null; + + /* the list of request attriubutes to retrieve*/ + protected String[] mReqAttrs = null; + + /* the list of cert attriubutes to retrieve*/ + protected String[] mCertAttrs = null; + + /* default dn pattern if left blank or not set in the config */ + public static final String DEFAULT_DNPATTERN = + "UID=$req.HTTP_PARAMS.UID, OU=people, O=$subj.o, C=$subj.c"; + + /** + * Constructor. + * + * @param dnPattern The base DN. + */ + public LdapSimpleMap(String dnPattern) { + try { + init(dnPattern); + } catch (EBaseException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("OPERATION_ERROR", e.toString())); + } + + } + + /** + * constructor if initializing from config store. + */ + public LdapSimpleMap() { + } + + public String[] getExtendedPluginInfo(Locale locale) { + String params[] = { + "dnPattern;string;Describes how to form the Ldap Subject name in" + + " the directory. Example 1: 'uid=CertMgr, o=Fedora'. Example 2:" + + " 'uid=$req.HTTP_PARAMS.uid, E=$ext.SubjectAlternativeName.RFC822Name, ou=$subj.ou'. " + + "$req means: take the attribute from the request. " + + "$subj means: take the attribute from the certificate subject name. " + + "$ext means: take the attribute from the certificate extension", + IExtendedPluginInfo.HELP_TOKEN + ";configuration-ldappublish-mapper-simplemapper", + IExtendedPluginInfo.HELP_TEXT + ";Describes how to form the LDAP DN of the entry to publish to" + }; + + return params; + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + /** + * for initializing from config store. + */ + public void init(IConfigStore config) + throws EBaseException { + mConfig = config; + String dnPattern = mConfig.getString(PROP_DNPATTERN); + + init(dnPattern); + } + + /** + * common initialization routine. + */ + protected void init(String dnPattern) + throws EBaseException { + if (mInited) + return; + + mDnPattern = dnPattern; + if (mDnPattern == null || mDnPattern.length() == 0) + mDnPattern = DEFAULT_DNPATTERN; + try { + mPattern = new MapDNPattern(mDnPattern); + } catch (ELdapException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_DN_PATTERN_INIT", + dnPattern, e.toString())); + throw new EBaseException("falied to init with pattern " + + dnPattern + " " + e); + } + + mInited = true; + } + + /** + * Maps a X500 subject name to LDAP entry. + * Uses DN pattern to form a DN for a LDAP base search. + * + * @param conn the LDAP connection. + * @param obj the object to map. + * @exception ELdapException if any LDAP exceptions occured. + */ + public String map(LDAPConnection conn, Object obj) + throws ELdapException { + return map(conn, null, obj); + } + + /** + * Maps a X500 subject name to LDAP entry. + * Uses DN pattern to form a DN for a LDAP base search. + * + * @param conn the LDAP connection. + * @param req the request to map. + * @param obj the object to map. + * @exception ELdapException if any LDAP exceptions occured. + */ + public String map(LDAPConnection conn, IRequest req, Object obj) + throws ELdapException { + if (conn == null) + return null; + String dn = null; + + try { + dn = formDN(req, obj); + if (dn == null) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_DN_NOT_FORMED")); + String s1 = ""; + + if (req != null) + s1 = req.getRequestId().toString(); + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_NO_DN_MATCH", s1)); + } + int scope = LDAPv2.SCOPE_BASE; + String filter = "(objectclass=*)"; + + // search for entry + String[] attrs = new String[] { LDAPv3.NO_ATTRS }; + + log(ILogger.LL_INFO, "searching for dn: " + dn + " filter:" + + filter + " scope: base"); + + LDAPSearchResults results = + conn.search(dn, scope, filter, attrs, false); + LDAPEntry entry = results.next(); + + if (results.hasMoreElements()) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_MORE_THAN_ONE_ENTRY", dn, ((req == null) ? "" : + req.getRequestId().toString()))); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_MORE_THAN_ONE_ENTRY", + ((req == null) ? "" : req.getRequestId().toString()))); + } + if (entry != null) + return entry.getDN(); + else { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_ENTRY_NOT_FOUND", dn, ((req == null) ? "" : req.getRequestId() + .toString()))); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", + "null entry")); + } + } catch (ELdapException e) { + throw e; + } catch (LDAPException e) { + if (e.getLDAPResultCode() == LDAPException.UNAVAILABLE) { + // need to intercept this because message from LDAP is + // "DSA is unavailable" which confuses with DSA PKI. + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_NO_LDAP_SERVER")); + throw new ELdapServerDownException(CMS.getUserMessage("CMS_LDAP_SERVER_UNAVAILABLE", conn.getHost(), "" + + conn.getPort())); + } else { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_DN_MAP_EXCEPTION", "", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", e.toString())); + } + } catch (EBaseException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_EXCEPTION_CAUGHT", e.toString())); + throw new ELdapException(CMS.getUserMessage("CMS_LDAP_NO_MATCH_FOUND", e.toString())); + } + } + + /** + * form a dn from component in the request and cert subject name + * + * @param req The request + * @param obj The certificate or crl + */ + private String formDN(IRequest req, Object obj) throws + EBaseException, ELdapException { + X500Name subjectDN = null; + CertificateExtensions certExt = null; + + try { + X509Certificate cert = (X509Certificate) obj; + + subjectDN = + (X500Name) ((X509Certificate) cert).getSubjectDN(); + + CMS.debug("LdapSimpleMap: cert subject dn:" + subjectDN.toString()); + //certExt = (CertificateExtensions) + // ((X509CertImpl)cert).get(X509CertInfo.EXTENSIONS); + X509CertInfo info = (X509CertInfo) + ((X509CertImpl) cert).get( + X509CertImpl.NAME + "." + X509CertImpl.INFO); + + certExt = (CertificateExtensions) info.get( + CertificateExtensions.NAME); + } catch (java.security.cert.CertificateParsingException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CANT_GET_EXT", e.toString())); + } catch (IOException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CANT_GET_EXT", e.toString())); + } catch (java.security.cert.CertificateException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CANT_GET_EXT", e.toString())); + } catch (ClassCastException e) { + try { + X509CRLImpl crl = (X509CRLImpl) obj; + + subjectDN = + (X500Name) ((X509CRLImpl) crl).getIssuerDN(); + + CMS.debug("LdapSimpleMap: crl issuer dn: " + + subjectDN.toString()); + } catch (ClassCastException ex) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("PUBLISH_PUBLISH_OBJ_NOT_SUPPORTED", + ((req == null) ? "" : req.getRequestId().toString()))); + return null; + } + } + try { + String dn = mPattern.formDN(req, subjectDN, certExt); + + return dn; + } catch (ELdapException e) { + log(ILogger.LL_FAILURE, CMS.getLogMessage("PUBLISH_CANT_FORM_DN", + ((req == null) ? "" : req.getRequestId().toString()), e.toString())); + throw e; + } + } + + public String getImplName() { + return "LdapSimpleMap"; + } + + public String getDescription() { + return "LdapSimpleMap"; + } + + public Vector<String> getDefaultParams() { + Vector<String> v = new Vector<String>(); + + v.addElement(PROP_DNPATTERN + "="); + return v; + } + + public Vector<String> getInstanceParams() { + Vector<String> v = new Vector<String>(); + + try { + if (mDnPattern == null) { + v.addElement(PROP_DNPATTERN + "="); + } else { + v.addElement(PROP_DNPATTERN + "=" + + mConfig.getString(PROP_DNPATTERN)); + } + } catch (Exception e) { + } + return v; + } + + private void log(int level, String msg) { + mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level, + "LdapSimpleMapper: " + msg); + } + +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/MapAVAPattern.java b/base/common/src/com/netscape/cms/publish/mappers/MapAVAPattern.java new file mode 100644 index 000000000..7aeb672d0 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/MapAVAPattern.java @@ -0,0 +1,652 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cms.publish.mappers; + +import java.io.IOException; +import java.io.PushbackReader; +import java.io.StringReader; +import java.util.Enumeration; +import java.util.StringTokenizer; +import java.util.Vector; + +import netscape.ldap.LDAPDN; +import netscape.security.x509.AVA; +import netscape.security.x509.CertificateExtensions; +import netscape.security.x509.Extension; +import netscape.security.x509.GeneralName; +import netscape.security.x509.GeneralNameInterface; +import netscape.security.x509.GeneralNames; +import netscape.security.x509.LdapV3DNStrConverter; +import netscape.security.x509.OIDMap; +import netscape.security.x509.SubjectAlternativeNameExtension; +import netscape.security.x509.X500Name; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.publish.ECompSyntaxErr; +import com.netscape.certsrv.request.IRequest; + +/** + * class for parsing a DN pattern used to construct a ldap dn from + * request attributes and cert subject name. + * <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$, $Date$ + */ +class MapAVAPattern { + + /* the value type of the dn component */ + public static final String TYPE_REQ = "$req"; + public static final String TYPE_SUBJ = "$subj"; + public static final String TYPE_EXT = "$ext"; + public static final String TYPE_RDN = "$rdn"; + public static final String TYPE_CONSTANT = "constant"; + + public static final String[] GENERAL_NAME_TYPE = { "ANY", + "RFC822Name", + "DNSName", + "X400Name", + "DIRECTORYName", + "EDIName", + "URIName", + "IPAddress", + "OIDName" }; + private static final char[] endChars = new char[] { '+', ',' }; + + private static final LdapV3DNStrConverter mLdapDNStrConverter = + new LdapV3DNStrConverter(); + + /* the list of request attributes needed by this AVA */ + protected String[] mReqAttrs = null; + + /* the list of cert attributes needed by this AVA*/ + protected String[] mCertAttrs = null; + + /* value type */ + protected String mType = null; + + /* the attribute in the AVA pair */ + protected String mAttr = null; + + /* value - could be name of a request attribute or + * cert subject dn attribute. */ + protected String mValue = null; + + /* value type - general name type of an extension attribute if any. */ + protected String mGNType = null; + + /* prefix - prefix of a request attribute if any. */ + protected String mPrefix = null; + + /* nth value of the ldap or dn attribute */ + protected int mElement = 0; + + protected String mTestDN = null; + + public MapAVAPattern(String component) + throws ELdapException { + if (component == null || component.length() == 0) + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", component)); + parse(new PushbackReader(new StringReader(component))); + } + + public MapAVAPattern(PushbackReader in) + throws ELdapException { + parse(in); + } + + private void parse(PushbackReader in) + throws ELdapException { + int c; + + // mark ava beginning. + + // skip spaces + //System.out.println("============ AVAPattern Begin ==========="); + //System.out.println("skip spaces"); + + try { + while ((c = in.read()) == ' ' || c == '\t') {//System.out.println("spaces read "+(char)c); + ; + } + } catch (IOException e) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", "All blank")); + } + if (c == -1) + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", "All blank")); + + // $rdn "." number syntax. + + if (c == '$') { + //System.out.println("$rdn syntax"); + mType = TYPE_RDN; + try { + if (in.read() != 'r' || + in.read() != 'd' || + in.read() != 'n' || + in.read() != '.') + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "Invalid $ syntax, expecting $rdn")); + } catch (IOException e) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "Invalid $ syntax, expecting $rdn")); + } + + StringBuffer rdnNumberBuf = new StringBuffer(); + + try { + while ((c = in.read()) != ',' && c != -1 && c != '+') { + //System.out.println("rdnNumber read "+(char)c); + rdnNumberBuf.append((char) c); + } + if (c != -1) // either ',' or '+' + in.unread(c); + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + + String rdnNumber = rdnNumberBuf.toString().trim(); + + if (rdnNumber.length() == 0) + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "$rdn number not set in ava pattern")); + try { + mElement = Integer.parseInt(rdnNumber) - 1; + } catch (NumberFormatException e) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "Invalid $rdn number in ava pattern")); + } + return; + } + + // name "=" ... syntax. + + // read name + //System.out.println("reading name"); + + StringBuffer attrBuf = new StringBuffer(); + + try { + while (c != '=' && c != -1 && c != ',' && c != '+') { + attrBuf.append((char) c); + c = in.read(); + //System.out.println("name read "+(char)c); + } + if (c == ',' || c == '+') + in.unread(c); + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + if (c != '=') + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "Missing \"=\" in ava pattern")); + + // read value + //System.out.println("reading value"); + + // skip spaces + //System.out.println("skip spaces for value"); + try { + while ((c = in.read()) == ' ' || c == '\t') {//System.out.println("spaces2 read "+(char)c); + ; + } + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + if (c == -1) + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "no value after = in ava pattern")); + + if (c == '$') { + // check for $subj $ext or $req + try { + c = in.read(); + //System.out.println("check $dn or $attr read "+(char)c); + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + if (c == -1) + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "expecting $subj or $req in ava pattern")); + if (c == 'r') { + try { + if (in.read() != 'e' || + in.read() != 'q' || + in.read() != '.') + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "expecting $req in ava pattern")); + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + mType = TYPE_REQ; + //System.out.println("---- mtype $req"); + } else if (c == 's') { + try { + if (in.read() != 'u' || + in.read() != 'b' || + in.read() != 'j' || + in.read() != '.') + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "expecting $subj in ava pattern")); + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + mType = TYPE_SUBJ; + //System.out.println("----- mtype $subj"); + } else if (c == 'e') { + try { + if (in.read() != 'x' || + in.read() != 't' || + in.read() != '.') + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "expecting $ext in ava pattern")); + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + mType = TYPE_EXT; + //System.out.println("----- mtype $ext"); + } else { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "unknown keyword. expecting $subj $ext or $req.")); + } + + // get request attr name of subject dn pattern from above. + String attrName = attrBuf.toString().trim(); + + //System.out.println("----- attrName "+attrName); + if (attrName.length() == 0) + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "attribute name expected")); + mAttr = attrName; + + /* + try { + ObjectIdentifier attrOid = + mLdapDNStrConverter.parseAVAKeyword(attrName); + mAttr = mLdapDNStrConverter.encodeOID(attrOid); + //System.out.println("----- mAttr "+mAttr); + } + catch (IOException e) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", e.toString())); + } + */ + + // get request attribute or cert subject dn attribute + + StringBuffer valueBuf = new StringBuffer(); + + try { + while ((c = in.read()) != ',' && + c != -1 && c != '.' && c != '+') { + //System.out.println("mValue read "+(char)c); + valueBuf.append((char) c); + } + if (c == '+' || c == ',') // either ',' or '+' + in.unread(c); // pushback last , or + + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + + mValue = valueBuf.toString().trim(); + if (mValue.length() == 0) + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "$subj or $req attribute name expected")); + //System.out.println("----- mValue "+mValue); + + // get nth dn xxx not nth request attribute . + if (c == '.') { + StringBuffer attrNumberBuf = new StringBuffer(); + + try { + while ((c = in.read()) != ',' && c != -1 && c != '.' + && c != '+') { + //System.out.println("mElement read "+(char)c); + attrNumberBuf.append((char) c); + } + if (c == ',' || c == '+') // either ',' or '+' + in.unread(c); // pushback last , or + + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + String attrNumber = attrNumberBuf.toString().trim(); + + if (attrNumber.length() == 0) + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "nth element $req $ext or $subj expected")); + try { + mElement = Integer.parseInt(attrNumber) - 1; + } catch (NumberFormatException e) { + if (TYPE_REQ.equals(mType)) { + mPrefix = mValue; + mValue = attrNumber; + } else if (TYPE_EXT.equals(mType)) { + mGNType = attrNumber; + } else + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "Invalid format in nth element $req $ext or $subj")); + + // get nth request attribute . + if (c == '.') { + StringBuffer attrNumberBuf1 = new StringBuffer(); + + try { + while ((c = in.read()) != ',' && c != -1 && c != '+') { + //System.out.println("mElement read "+(char)c); + attrNumberBuf1.append((char) c); + } + if (c != -1) // either ',' or '+' + in.unread(c); // pushback last , or + + } catch (IOException ex) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", ex.toString())); + } + String attrNumber1 = attrNumberBuf1.toString().trim(); + + if (attrNumber1.length() == 0) + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "nth element $req expected")); + try { + mElement = Integer.parseInt(attrNumber1) - 1; + } catch (NumberFormatException ex) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", + "Invalid format in nth element $req.")); + + } + } + } + } + //System.out.println("----- mElement "+mElement); + } else { + // value is constant. treat as regular ava. + mType = TYPE_CONSTANT; + //System.out.println("----- mType constant"); + // parse ava value. + StringBuffer valueBuf = new StringBuffer(); + + valueBuf.append((char) c); + // read forward to get attribute value + try { + while ((c = in.read()) != ',' && + c != -1) { + valueBuf.append((char) c); + } + if (c == '+' || c == ',') { // either ',' or '+' + in.unread(c); // pushback last , or + + } + } catch (IOException e) { + throw new ELdapException( + CMS.getUserMessage("CMS_LDAP_INTERNAL_ERROR", e.toString())); + } + try { + AVA ava = mLdapDNStrConverter.parseAVA(attrBuf + "=" + valueBuf); + + mValue = ava.toLdapDNString(); + //System.out.println("----- mValue "+mValue); + } catch (IOException e) { + throw new ECompSyntaxErr(CMS.getUserMessage("CMS_AUTHENTICATION_COMPONENT_SYNTAX", e.toString())); + } + } + } + + public String formAVA(IRequest req, X500Name subject, CertificateExtensions extensions) + throws ELdapException { + if (TYPE_CONSTANT.equals(mType)) + return mValue; + + if (TYPE_RDN.equals(mType)) { + String dn = subject.toString(); + + if (mTestDN != null) + dn = mTestDN; + //System.out.println("AVAPattern Using dn "+mTestDN); + String[] rdns = LDAPDN.explodeDN(dn, false); + + if (mElement >= rdns.length) + return null; + return rdns[mElement]; + } + + if (TYPE_SUBJ.equals(mType)) { + String dn = subject.toString(); + + if (mTestDN != null) + dn = mTestDN; + //System.out.println("AVAPattern Using dn "+mTestDN); + String[] rdns = LDAPDN.explodeDN(dn, false); + String value = null; + int nFound = -1; + + for (int i = 0; i < rdns.length; i++) { + String[] avas = explodeRDN(rdns[i]); + + for (int j = 0; j < avas.length; j++) { + String[] exploded = explodeAVA(avas[j]); + + if (exploded[0].equalsIgnoreCase(mValue) && + ++nFound == mElement) { + value = exploded[1]; + break; + } + } + } + if (value == null) { + CMS.debug( + "MapAVAPattern: attr " + mAttr + + " not formed from: cert subject " + + dn + + "-- no subject component : " + mValue); + return null; + } + return mAttr + "=" + value; + } + + if (TYPE_EXT.equals(mType)) { + if (extensions != null) { + for (int i = 0; i < extensions.size(); i++) { + Extension ext = (Extension) + extensions.elementAt(i); + String extName = OIDMap.getName(ext.getExtensionId()); + int index = extName.lastIndexOf("."); + + if (index != -1) + extName = extName.substring(index + 1); + if (extName.equals(mValue)) { + // Check the extensions one by one. + // For now, just give subjectAltName as an example. + if (mValue.equalsIgnoreCase(SubjectAlternativeNameExtension.NAME)) { + try { + GeneralNames subjectNames = + (GeneralNames) + ((SubjectAlternativeNameExtension) ext) + .get(SubjectAlternativeNameExtension.SUBJECT_NAME); + + if (subjectNames.size() == 0) + break; + int j = 0; + + for (Enumeration<GeneralNameInterface> 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<String> avas = new Vector<String>(); + StringTokenizer token = new StringTokenizer(rdn, "+"); + + while (token.hasMoreTokens()) + avas.addElement(token.nextToken()); + String[] theAvas = new String[avas.size()]; + + avas.copyInto(theAvas); + return theAvas; + } + + /** + * Explode AVA into name and value. + * Does not handle escaped '=' + * If AVA is malformed empty array is returned. + */ + public static String[] explodeAVA(String ava) { + int equals = ava.indexOf('='); + + if (equals == -1) + return null; + return new String[] { + ava.substring(0, equals).trim(), ava.substring(equals + 1).trim() }; + } +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/MapDNPattern.java b/base/common/src/com/netscape/cms/publish/mappers/MapDNPattern.java new file mode 100644 index 000000000..7a9025b1d --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/MapDNPattern.java @@ -0,0 +1,201 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cms.publish.mappers; + +import java.io.IOException; +import java.io.PushbackReader; +import java.io.StringReader; +import java.util.Vector; + +import netscape.security.x509.CertificateExtensions; +import netscape.security.x509.X500Name; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.request.IRequest; + +/** + * class for parsing a DN pattern used to construct a ldap dn from + * request attributes and cert subject name. + * <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$, $Date$ + */ +public class MapDNPattern { + + /* the list of request attriubutes to retrieve*/ + protected String[] mReqAttrs = null; + + /* the list of cert attriubutes to retrieve*/ + protected String[] mCertAttrs = null; + + /* rdn patterns */ + protected MapRDNPattern[] mRDNPatterns = null; + + /* original pattern string */ + protected String mPatternString = null; + + protected String mTestDN = null; + + /** + * Construct a DN pattern by parsing a pattern string. + * + * @param pattern the DN pattern + * @exception EBaseException If parsing error occurs. + */ + public MapDNPattern(String pattern) + throws ELdapException { + if (pattern == null || pattern.equals("")) { + CMS.debug( + "MapDNPattern: null pattern"); + } else { + mPatternString = pattern; + PushbackReader in = new PushbackReader(new StringReader(pattern)); + + parse(in); + } + } + + public MapDNPattern(PushbackReader in) + throws ELdapException { + parse(in); + } + + private void parse(PushbackReader in) + throws ELdapException { + Vector<MapRDNPattern> rdnPatterns = new Vector<MapRDNPattern>(); + 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<String> reqAttrs = new Vector<String>(); + + 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<String> certAttrs = new Vector<String>(); + + for (int i = 0; i < mRDNPatterns.length; i++) { + String[] rdnAttrs = mRDNPatterns[i].getCertAttrs(); + + if (rdnAttrs != null && rdnAttrs.length > 0) + for (int j = 0; j < rdnAttrs.length; j++) + certAttrs.addElement(rdnAttrs[j]); + } + mCertAttrs = new String[certAttrs.size()]; + certAttrs.copyInto(mCertAttrs); + } + + /** + * Form a Ldap v3 DN string from a request and a cert subject name. + * + * @param req the request for (un)publish + * @param subject the subjectDN of the certificate + * @return Ldap v3 DN string to use for base ldap search. + */ + public String formDN(IRequest req, X500Name subject, CertificateExtensions ext) + throws ELdapException { + StringBuffer formedDN = new StringBuffer(); + + for (int i = 0; i < mRDNPatterns.length; i++) { + if (mTestDN != null) + mRDNPatterns[i].mTestDN = mTestDN; + String rdn = mRDNPatterns[i].formRDN(req, subject, ext); + + if (rdn != null && rdn.length() != 0) { + if (formedDN.length() != 0) + formedDN.append(","); + formedDN.append(rdn); + } else { + throw new ELdapException("pattern not matched"); + } + } + return formedDN.toString(); + } + + public String[] getReqAttrs() { + return (String[]) mReqAttrs.clone(); + } + + public String[] getCertAttrs() { + return (String[]) mCertAttrs.clone(); + } +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/MapRDNPattern.java b/base/common/src/com/netscape/cms/publish/mappers/MapRDNPattern.java new file mode 100644 index 000000000..c1688345b --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/MapRDNPattern.java @@ -0,0 +1,217 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cms.publish.mappers; + +import java.io.IOException; +import java.io.PushbackReader; +import java.io.StringReader; +import java.util.Vector; + +import netscape.security.x509.CertificateExtensions; +import netscape.security.x509.X500Name; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.request.IRequest; + +/** + * class for parsing a DN pattern used to construct a ldap dn from + * request attributes and cert subject name. + * <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$, $Date$ + */ +class MapRDNPattern { + + /* the list of request attributes needed by this RDN */ + protected String[] mReqAttrs = null; + + /* the list of cert attributes needed by this RDN */ + protected String[] mCertAttrs = null; + + /* AVA patterns */ + protected MapAVAPattern[] mAVAPatterns = null; + + /* original pattern string */ + protected String mPatternString = null; + + protected String mTestDN = null; + + /** + * Construct a DN pattern by parsing a pattern string. + * + * @param pattenr the DN pattern + * @exception ELdapException If parsing error occurs. + */ + public MapRDNPattern(String pattern) + throws ELdapException { + if (pattern == null || pattern.equals("")) { + CMS.debug( + "MapDNPattern: null pattern"); + } else { + mPatternString = pattern; + PushbackReader in = new PushbackReader(new StringReader(pattern)); + + parse(in); + } + } + + /** + * Construct a DN pattern from a input stream of pattern + */ + public MapRDNPattern(PushbackReader in) + throws ELdapException { + parse(in); + } + + private void parse(PushbackReader in) + throws ELdapException { + //System.out.println("_________ begin rdn _________"); + Vector<MapAVAPattern> avaPatterns = new Vector<MapAVAPattern>(); + 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<String> reqAttrs = new Vector<String>(); + + 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<String> certAttrs = new Vector<String>(); + + for (int i = 0; i < mAVAPatterns.length; i++) { + String avaAttr = mAVAPatterns[i].getCertAttr(); + + if (avaAttr == null || avaAttr.length() == 0) + continue; + certAttrs.addElement(avaAttr); + } + mCertAttrs = new String[certAttrs.size()]; + certAttrs.copyInto(mCertAttrs); + } + + /** + * Form a Ldap v3 DN string from a request and a cert subject name. + * + * @param req the request for (un)publish + * @param subject the subjectDN of the certificate + * @return Ldap v3 DN string to use for base ldap search. + */ + public String formRDN(IRequest req, X500Name subject, CertificateExtensions ext) + throws ELdapException { + StringBuffer formedRDN = new StringBuffer(); + + for (int i = 0; i < mAVAPatterns.length; i++) { + if (mTestDN != null) + mAVAPatterns[i].mTestDN = mTestDN; + String ava = mAVAPatterns[i].formAVA(req, subject, ext); + + if (ava != null && ava.length() > 0) { + if (formedRDN.length() != 0) + formedRDN.append("+"); + formedRDN.append(ava); + } + } + //System.out.println("formed RDN "+formedRDN.toString()); + return formedRDN.toString(); + } + + public String[] getReqAttrs() { + return (String[]) mReqAttrs.clone(); + } + + public String[] getCertAttrs() { + return (String[]) mCertAttrs.clone(); + } +} diff --git a/base/common/src/com/netscape/cms/publish/mappers/NoMap.java b/base/common/src/com/netscape/cms/publish/mappers/NoMap.java new file mode 100644 index 000000000..155c54ce0 --- /dev/null +++ b/base/common/src/com/netscape/cms/publish/mappers/NoMap.java @@ -0,0 +1,104 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cms.publish.mappers; + +import java.util.Locale; +import java.util.Vector; + +import netscape.ldap.LDAPConnection; + +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.base.IExtendedPluginInfo; +import com.netscape.certsrv.ldap.ELdapException; +import com.netscape.certsrv.publish.ILdapMapper; +import com.netscape.certsrv.request.IRequest; + +/** + * No Map + * + * @version $Revision$, $Date$ + */ +public class NoMap implements ILdapMapper, IExtendedPluginInfo { + + public IConfigStore mConfig = null; + + /** + * constructor if initializing from config store. + */ + public NoMap() { + } + + public String[] getExtendedPluginInfo(Locale locale) { + String params[] = { + IExtendedPluginInfo.HELP_TOKEN + ";configuration-ldappublish-mapper-simplemapper", + IExtendedPluginInfo.HELP_TEXT + ";Describes how to form the name of the entry to publish to" + }; + + return params; + } + + public IConfigStore getConfigStore() { + return mConfig; + } + + /** + * for initializing from config store. + */ + public void init(IConfigStore config) + throws EBaseException { + mConfig = config; + } + + /** + * Maps a X500 subject name to LDAP entry. + * Uses DN pattern to form a DN for a LDAP base search. + * + * @param conn the LDAP connection. + * @param obj the object to map. + * @exception ELdapException if any LDAP exceptions occured. + */ + public String map(LDAPConnection conn, Object obj) + throws ELdapException { + return null; + } + + public String map(LDAPConnection conn, IRequest req, Object obj) + throws ELdapException { + return null; + } + + public String getImplName() { + return "NoMap"; + } + + public String getDescription() { + return "NoMap"; + } + + public Vector<String> getDefaultParams() { + Vector<String> v = new Vector<String>(); + return v; + } + + public Vector<String> getInstanceParams() { + Vector<String> v = new Vector<String>(); + return v; + } + +} |