From a4682ceae6774956461edd03b2485bbacea445f4 Mon Sep 17 00:00:00 2001 From: mharmsen Date: Tue, 4 Oct 2011 01:17:41 +0000 Subject: Bugzilla Bug #688225 - (dogtagIPAv2.1) TRACKER: of the Dogtag fixes for freeIPA 2.1 git-svn-id: svn+ssh://svn.fedorahosted.org/svn/pki/tags/IPA_v2_RHEL_6_2_20111003@2252 c9f7a03b-bd48-0410-a16d-cbbf54688b0b --- .../cms/publish/mappers/MapAVAPattern.java | 634 +++++++++++++++++++++ 1 file changed, 634 insertions(+) create mode 100644 pki/base/common/src/com/netscape/cms/publish/mappers/MapAVAPattern.java (limited to 'pki/base/common/src/com/netscape/cms/publish/mappers/MapAVAPattern.java') diff --git a/pki/base/common/src/com/netscape/cms/publish/mappers/MapAVAPattern.java b/pki/base/common/src/com/netscape/cms/publish/mappers/MapAVAPattern.java new file mode 100644 index 000000000..129c12659 --- /dev/null +++ b/pki/base/common/src/com/netscape/cms/publish/mappers/MapAVAPattern.java @@ -0,0 +1,634 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cms.publish.mappers; + + +import java.util.*; +import com.netscape.certsrv.logging.*; +import java.io.*; +import java.security.*; +import netscape.security.x509.*; +import netscape.security.util.*; +import netscape.ldap.*; +import com.netscape.certsrv.request.IRequest; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.ldap.*; +import com.netscape.certsrv.publish.*; + + +/** + * class for parsing a DN pattern used to construct a ldap dn from + * request attributes and cert subject name.

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

+ * + * The syntax is + *

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

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

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

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

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

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

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

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

+ *

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