diff options
Diffstat (limited to 'base/server/cms/src/com')
9 files changed, 1832 insertions, 152 deletions
diff --git a/base/server/cms/src/com/netscape/cms/authentication/CMCUserSignedAuth.java b/base/server/cms/src/com/netscape/cms/authentication/CMCUserSignedAuth.java new file mode 100644 index 000000000..a72ce5863 --- /dev/null +++ b/base/server/cms/src/com/netscape/cms/authentication/CMCUserSignedAuth.java @@ -0,0 +1,1140 @@ +// --- 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.authentication; + +/////////////////////// +// import statements // +/////////////////////// + +/* cert server imports */ +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.PublicKey; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Locale; +import java.util.Vector; + +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.CryptoManager.NotInitializedException; +import org.mozilla.jss.asn1.ASN1Util; +import org.mozilla.jss.asn1.INTEGER; +import org.mozilla.jss.asn1.InvalidBERException; +import org.mozilla.jss.asn1.OBJECT_IDENTIFIER; +import org.mozilla.jss.asn1.OCTET_STRING; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.asn1.SET; +import org.mozilla.jss.crypto.CryptoToken; +import org.mozilla.jss.crypto.DigestAlgorithm; +import org.mozilla.jss.crypto.PrivateKey; +import org.mozilla.jss.pkcs10.CertificationRequest; +import org.mozilla.jss.pkcs11.PK11ECPublicKey; +import org.mozilla.jss.pkcs11.PK11PubKey; +import org.mozilla.jss.pkix.cert.Certificate; +import org.mozilla.jss.pkix.cert.CertificateInfo; +import org.mozilla.jss.pkix.cmc.PKIData; +import org.mozilla.jss.pkix.cmc.TaggedAttribute; +import org.mozilla.jss.pkix.cmc.TaggedCertificationRequest; +import org.mozilla.jss.pkix.cmc.TaggedRequest; +import org.mozilla.jss.pkix.cms.EncapsulatedContentInfo; +import org.mozilla.jss.pkix.cms.IssuerAndSerialNumber; +import org.mozilla.jss.pkix.cms.SignedData; +import org.mozilla.jss.pkix.cms.SignerIdentifier; +import org.mozilla.jss.pkix.crmf.CertReqMsg; +import org.mozilla.jss.pkix.crmf.CertRequest; +import org.mozilla.jss.pkix.crmf.CertTemplate; +import org.mozilla.jss.pkix.primitive.AlgorithmIdentifier; +import org.mozilla.jss.pkix.primitive.Name; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.authentication.AuthToken; +import com.netscape.certsrv.authentication.EInvalidCredentials; +import com.netscape.certsrv.authentication.EMissingCredential; +import com.netscape.certsrv.authentication.IAuthCredentials; +import com.netscape.certsrv.authentication.IAuthManager; +import com.netscape.certsrv.authentication.IAuthToken; +import com.netscape.certsrv.base.EBaseException; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.base.IExtendedPluginInfo; +import com.netscape.certsrv.base.SessionContext; +import com.netscape.certsrv.logging.AuditEvent; +import com.netscape.certsrv.logging.ILogger; +import com.netscape.certsrv.profile.EProfileException; +import com.netscape.certsrv.profile.IProfile; +import com.netscape.certsrv.profile.IProfileAuthenticator; +import com.netscape.certsrv.property.Descriptor; +import com.netscape.certsrv.property.IDescriptor; +import com.netscape.certsrv.request.IRequest; +import com.netscape.cmsutil.crypto.CryptoUtil; +import com.netscape.cmsutil.util.Utils; + +import netscape.security.pkcs.PKCS10; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CertImpl; +import netscape.security.x509.X509CertInfo; +import netscape.security.x509.X509Key; + +//import com.netscape.cmscore.util.*; +////////////////////// +// class definition // +////////////////////// + +/** + * User Signed CMC authentication plug-in + * note: + * - this version differs from CMCAuth in that it allows non-agent users + * to sign own cmc requests; It is expected to be used with + * CMCUserSignedSubjectNameDefault and CMCUserSignedSubjectNameConstraint + * so that the resulting cert will bear the same subjectDN of that of the CMC + * signing cert + * - it originates from CMCAuth with modification for user-signed cmc + * @author cfu - user signed cmc authentication + * <P> + * + * @version $Revision$, $Date$ + */ +public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + IProfileAuthenticator { + + //////////////////////// + // default parameters // + //////////////////////// + + ///////////////////////////// + // IAuthManager parameters // + ///////////////////////////// + + /* authentication plug-in configuration store */ + private IConfigStore mConfig; + private static final String HEADER = "-----BEGIN NEW CERTIFICATE REQUEST-----"; + private static final String TRAILER = "-----END NEW CERTIFICATE REQUEST-----"; + public static final String TOKEN_CERT_SERIAL = "certSerialToRevoke"; + public static final String REASON_CODE = "reasonCode"; + /* authentication plug-in name */ + private String mImplName = null; + + /* authentication plug-in instance name */ + private String mName = null; + + /* authentication plug-in fields */ + + /* Holds authentication 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. + */ + protected static String[] mConfigParams = + new String[] {}; + + /* authentication plug-in values */ + + /* authentication plug-in properties */ + + /* required credentials to authenticate. UID and CMC are strings. */ + public static final String CRED_CMC = "cmcRequest"; + + protected static String[] mRequiredCreds = {}; + + //////////////////////////////////// + // IExtendedPluginInfo parameters // + //////////////////////////////////// + + /* Vector of extendedPluginInfo strings */ + protected static Vector<String> mExtendedPluginInfo = null; + //public static final String AGENT_AUTHMGR_ID = "agentAuthMgr"; + //public static final String AGENT_PLUGIN_ID = "agentAuthPlugin"; + + /* actual help messages */ + static { + mExtendedPluginInfo = new Vector<String>(); + + mExtendedPluginInfo + .add(IExtendedPluginInfo.HELP_TEXT + + ";Authenticate the CMC request. The \"Authentication Instance ID\" must be named \"CMCUserSignedAuth\""); + mExtendedPluginInfo.add(IExtendedPluginInfo.HELP_TOKEN + + ";configuration-authentication"); + } + + /////////////////////// + // Logger parameters // + /////////////////////// + + /* the system's logger */ + private ILogger mLogger = CMS.getLogger(); + + /* signed audit parameters */ + private ILogger mSignedAuditLogger = CMS.getSignedAuditLogger(); + private final static String SIGNED_AUDIT_ENROLLMENT_REQUEST_TYPE = + "enrollment"; + private final static String SIGNED_AUDIT_REVOCATION_REQUEST_TYPE = + "revocation"; + + ///////////////////// + // default methods // + ///////////////////// + + /** + * Default constructor, initialization must follow. + */ + public CMCUserSignedAuth() { + } + + ////////////////////////// + // IAuthManager methods // + ////////////////////////// + + /** + * Initializes the CMCUserSignedAuth authentication plug-in. + * <p> + * + * @param name The name for this authentication plug-in instance. + * @param implName The name of the authentication plug-in. + * @param config - The configuration store for this instance. + * @exception EBaseException If an error occurs during initialization. + */ + public void init(String name, String implName, IConfigStore config) + throws EBaseException { + mName = name; + mImplName = implName; + mConfig = config; + + log(ILogger.LL_INFO, "Initialization complete!"); + } + + /** + * Authenticates user by their CMC; + * resulting AuthToken sets a TOKEN_SUBJECT for the subject name. + * <P> + * + * <ul> + * <li>signed.audit LOGGING_SIGNED_AUDIT_CMC_USER_SIGNED_REQUEST_SIG_VERIFY used when CMC (user-pre-signed) cert + * requests or revocation requests are submitted and signature is verified + * </ul> + * + * @param authCred Authentication credentials, CRED_UID and CRED_CMC. + * @return an AuthToken + * @exception com.netscape.certsrv.authentication.EMissingCredential + * If a required authentication credential is missing. + * @exception com.netscape.certsrv.authentication.EInvalidCredentials + * If credentials failed authentication. + * @exception com.netscape.certsrv.base.EBaseException + * If an internal error occurred. + * @see com.netscape.certsrv.authentication.AuthToken + */ + public IAuthToken authenticate(IAuthCredentials authCred) throws EMissingCredential, EInvalidCredentials, + EBaseException { + String method = "CMCUserSignedAuth: authenticate: "; + CMS.debug(method + "begins"); + + String auditMessage = null; + String auditSubjectID = auditSubjectID(); + String auditReqType = ILogger.UNIDENTIFIED; + String auditCertSubject = ILogger.UNIDENTIFIED; + String auditSignerInfo = ILogger.UNIDENTIFIED; + + // ensure that any low-level exceptions are reported + // to the signed audit log and stored as failures + try { + // get the CMC. + + Object argblock = authCred.getArgBlock(); + Object returnVal = null; + if (argblock == null) { + returnVal = authCred.get("cert_request"); + if (returnVal == null) + returnVal = authCred.get(CRED_CMC); + } else { + returnVal = authCred.get("cert_request"); + if (returnVal == null) + returnVal = authCred.getArgBlock().get(CRED_CMC); + } + String cmc = (String) returnVal; + if (cmc == null) { + CMS.debug(method + " Authentication failed. Missing CMC."); + + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY, + auditSubjectID, + ILogger.FAILURE, + auditReqType, + auditCertSubject, + auditSignerInfo); + + audit(auditMessage); + + throw new EMissingCredential(CMS.getUserMessage( + "CMS_AUTHENTICATION_NULL_CREDENTIAL", CRED_CMC)); + } + + if (cmc.equals("")) { + log(ILogger.LL_FAILURE, + "cmc : attempted login with empty CMC."); + + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY, + auditSubjectID, + ILogger.FAILURE, + auditReqType, + auditCertSubject, + auditSignerInfo); + + audit(auditMessage); + + throw new EInvalidCredentials(CMS.getUserMessage( + "CMS_AUTHENTICATION_INVALID_CREDENTIAL")); + } + + // authenticate by checking CMC. + + // everything OK. + // now formulate the certificate info. + // set the subject name at a minimum. + // set anything else like version, extensions, etc. + // if nothing except subject name is set the rest of + // cert info will be filled in by policies and CA defaults. + + AuthToken authToken = new AuthToken(this); + + try { + String asciiBASE64Blob; + + int startIndex = cmc.indexOf(HEADER); + int endIndex = cmc.indexOf(TRAILER); + if (startIndex != -1 && endIndex != -1) { + startIndex = startIndex + HEADER.length(); + asciiBASE64Blob = cmc.substring(startIndex, endIndex); + } else + asciiBASE64Blob = cmc; + + byte[] cmcBlob = CMS.AtoB(asciiBASE64Blob); + ByteArrayInputStream cmcBlobIn = new + ByteArrayInputStream(cmcBlob); + + org.mozilla.jss.pkix.cms.ContentInfo cmcReq = + (org.mozilla.jss.pkix.cms.ContentInfo) + org.mozilla.jss.pkix.cms.ContentInfo.getTemplate().decode( + cmcBlobIn); + + if (!cmcReq.getContentType().equals( + org.mozilla.jss.pkix.cms.ContentInfo.SIGNED_DATA) || + !cmcReq.hasContent()) { + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY, + auditSubjectID, + ILogger.FAILURE, + auditReqType, + auditCertSubject, + auditSignerInfo); + + audit(auditMessage); + + // throw new ECMSGWException(CMSGWResources.NO_CMC_CONTENT); + + throw new EBaseException("NO_CMC_CONTENT"); + } + + SignedData cmcFullReq = (SignedData) + cmcReq.getInterpretedContent(); + + IConfigStore cmc_config = CMS.getConfigStore(); + boolean checkSignerInfo = + cmc_config.getBoolean("cmc.signerInfo.verify", true); + String userid = "defUser"; + String uid = "defUser"; + if (checkSignerInfo) { + IAuthToken userToken = verifySignerInfo(authToken, cmcFullReq); + if (userToken == null) { + CMS.debug(method + " authenticate() userToken null"); + throw new EBaseException(method + " verifySignerInfo failure"); + } + userid = userToken.getInString("userid"); + uid = userToken.getInString("cn"); + } else { + CMS.debug(method + " authenticate() signerInfo verification bypassed"); + } + // reset value of auditSignerInfo + if (uid != null) { + auditSignerInfo = uid.trim(); + } + + EncapsulatedContentInfo ci = cmcFullReq.getContentInfo(); + + OBJECT_IDENTIFIER id = ci.getContentType(); + + if (!id.equals(OBJECT_IDENTIFIER.id_cct_PKIData) || + !ci.hasContent()) { + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY, + auditSubjectID, + ILogger.FAILURE, + auditReqType, + auditCertSubject, + auditSignerInfo); + + audit(auditMessage); + + // throw new ECMSGWException( + // CMSGWResources.NO_PKIDATA); + + throw new EBaseException("NO_PKIDATA"); + } + + OCTET_STRING content = ci.getContent(); + + ByteArrayInputStream s = new + ByteArrayInputStream(content.toByteArray()); + PKIData pkiData = (PKIData) (new PKIData.Template()).decode(s); + + SEQUENCE reqSequence = pkiData.getReqSequence(); + + int numReqs = reqSequence.size(); + + if (numReqs == 0) { + CMS.debug(method + "numReqs 0, assume revocation request"); + // revocation request + + // reset value of auditReqType + auditReqType = SIGNED_AUDIT_REVOCATION_REQUEST_TYPE; + + SEQUENCE controlSequence = pkiData.getControlSequence(); + int controlSize = controlSequence.size(); + + if (controlSize > 0) { + for (int i = 0; i < controlSize; i++) { + TaggedAttribute taggedAttribute = + (TaggedAttribute) controlSequence.elementAt(i); + OBJECT_IDENTIFIER type = taggedAttribute.getType(); + + if (type.equals( + OBJECT_IDENTIFIER.id_cmc_revokeRequest)) { +/* TODO: user-signed revocation to be handled in next ticket + // if( i ==1 ) { + // taggedAttribute.getType() == + // OBJECT_IDENTIFIER.id_cmc_revokeRequest + // } + + SET values = taggedAttribute.getValues(); + int numVals = values.size(); + BigInteger[] bigIntArray = null; + + bigIntArray = new BigInteger[numVals]; + for (int j = 0; j < numVals; j++) { + // serialNumber INTEGER + + // SEQUENCE RevRequest = (SEQUENCE) + // values.elementAt(j); + byte[] encoded = ASN1Util.encode( + values.elementAt(j)); + org.mozilla.jss.asn1.ASN1Template template = new + org.mozilla.jss.pkix.cmmf.RevRequest.Template(); + org.mozilla.jss.pkix.cmmf.RevRequest revRequest = + (org.mozilla.jss.pkix.cmmf.RevRequest) + ASN1Util.decode(template, encoded); + + // SEQUENCE RevRequest = (SEQUENCE) + // ASN1Util.decode( + // SEQUENCE.getTemplate(), + // ASN1Util.encode( + // values.elementAt(j))); + + // SEQUENCE RevRequest = + // values.elementAt(j); + // int revReqSize = RevRequest.size(); + // if( revReqSize > 3 ) { + // INTEGER serialNumber = + // new INTEGER((long)0); + // } + + INTEGER temp = revRequest.getSerialNumber(); + + bigIntArray[j] = temp; + authToken.set(TOKEN_CERT_SERIAL, bigIntArray); + + long reasonCode = revRequest.getReason().getValue(); + Integer IntObject = Integer.valueOf((int) reasonCode); + authToken.set(REASON_CODE, IntObject); + + authToken.set("uid", uid); + authToken.set("userid", userid); + } +*/ + } + } + + } + } else { + CMS.debug(method + "numReqs not 0, assume enrollment request"); + // enrollment request + + // reset value of auditReqType + auditReqType = SIGNED_AUDIT_ENROLLMENT_REQUEST_TYPE; + + X509CertInfo[] certInfoArray = new X509CertInfo[numReqs]; + String[] reqIdArray = new String[numReqs]; + + for (int i = 0; i < numReqs; i++) { + // decode message. + TaggedRequest taggedRequest = + (TaggedRequest) reqSequence.elementAt(i); + + TaggedRequest.Type type = taggedRequest.getType(); + + if (type.equals(TaggedRequest.PKCS10)) { + CMS.debug(method + " type is PKCS10"); + authToken.set("cert_request_type", "cmc-pkcs10"); + + TaggedCertificationRequest tcr = + taggedRequest.getTcr(); + int p10Id = tcr.getBodyPartID().intValue(); + + reqIdArray[i] = String.valueOf(p10Id); + + CertificationRequest p10 = + tcr.getCertificationRequest(); + + // transfer to sun class + ByteArrayOutputStream ostream = + new ByteArrayOutputStream(); + + p10.encode(ostream); + boolean sigver = true; + boolean tokenSwitched = false; + CryptoManager cm = null; + CryptoToken signToken = null; + CryptoToken savedToken = null; + + // for PKCS10, "sigver" would offer the POP + sigver = CMS.getConfigStore().getBoolean("ca.requestVerify.enabled", true); + try { + cm = CryptoManager.getInstance(); + if (sigver == true) { + String tokenName = + CMS.getConfigStore().getString("ca.requestVerify.token", CryptoUtil.INTERNAL_TOKEN_NAME); + savedToken = cm.getThreadToken(); + signToken = CryptoUtil.getCryptoToken(tokenName); + if (!savedToken.getName().equals(signToken.getName())) { + cm.setThreadToken(signToken); + tokenSwitched = true; + } + } + + PKCS10 pkcs10 = + new PKCS10(ostream.toByteArray(), sigver); + + // xxx do we need to do anything else? + X509CertInfo certInfo = + CMS.getDefaultX509CertInfo(); + + // fillPKCS10(certInfo,pkcs10,authToken,null); + + // authToken.set( + // pkcs10.getSubjectPublicKeyInfo()); + + X500Name tempName = pkcs10.getSubjectName(); + + // reset value of auditCertSubject + if (tempName != null) { + auditCertSubject = + tempName.toString().trim(); + if (auditCertSubject.equals("")) { + auditCertSubject = + ILogger.SIGNED_AUDIT_EMPTY_VALUE; + } + authToken.set(AuthToken.TOKEN_CERT_SUBJECT, + tempName.toString()); + } + + authToken.set("uid", uid); + authToken.set("userid", userid); + + certInfoArray[i] = certInfo; + } catch (Exception e) { + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY, + auditSubjectID, + ILogger.FAILURE, + auditReqType, + auditCertSubject, + auditSignerInfo); + + audit(auditMessage); + + //throw new ECMSGWException( + //CMSGWResources.ERROR_PKCS101, e.toString()); + + e.printStackTrace(); + throw new EBaseException(e.toString()); + } finally { + if ((sigver == true) && (tokenSwitched == true)){ + cm.setThreadToken(savedToken); + } + } + } else if (type.equals(TaggedRequest.CRMF)) { + + CMS.debug(method + " type is CRMF"); + authToken.set("cert_request_type", "cmc-crmf"); + try { + CertReqMsg crm = + taggedRequest.getCrm(); + CertRequest certReq = crm.getCertReq(); + INTEGER reqID = certReq.getCertReqId(); + reqIdArray[i] = reqID.toString(); + CertTemplate template = certReq.getCertTemplate(); + Name name = template.getSubject(); + + // xxx do we need to do anything else? + X509CertInfo certInfo = + CMS.getDefaultX509CertInfo(); + + // reset value of auditCertSubject + if (name != null) { + String ss = name.getRFC1485(); + + auditCertSubject = ss; + if (auditCertSubject.equals("")) { + auditCertSubject = + ILogger.SIGNED_AUDIT_EMPTY_VALUE; + } + + authToken.set(AuthToken.TOKEN_CERT_SUBJECT, ss); + authToken.set("uid", uid); + authToken.set("userid", userid); + } + certInfoArray[i] = certInfo; + } catch (Exception e) { + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY, + auditSubjectID, + ILogger.FAILURE, + auditReqType, + auditCertSubject, + auditSignerInfo); + + audit(auditMessage); + + //throw new ECMSGWException( + //CMSGWResources.ERROR_PKCS101, e.toString()); + + e.printStackTrace(); + throw new EBaseException(e.toString()); + } + } + + // authToken.set(AgentAuthentication.CRED_CERT, new + // com.netscape.certsrv.usrgrp.Certificates( + // x509Certs)); + } + } + } catch (Exception e) { + CMS.debug(method + e); + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY, + auditSubjectID, + ILogger.FAILURE, + auditReqType, + auditCertSubject, + auditSignerInfo); + + audit(auditMessage); + + //Debug.printStackTrace(e); + throw new EInvalidCredentials(CMS.getUserMessage( + "CMS_AUTHENTICATION_INVALID_CREDENTIAL")); + } + + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY, + auditSubjectID, + ILogger.SUCCESS, + auditReqType, + auditCertSubject, + auditSignerInfo); + + audit(auditMessage); + + CMS.debug(method + "ends successfully; returning authToken"); + return authToken; + } catch (EMissingCredential eAudit1) { + CMS.debug(method + eAudit1); + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY, + auditSubjectID, + ILogger.FAILURE, + auditReqType, + auditCertSubject, + auditSignerInfo); + + audit(auditMessage); + + // rethrow the specific exception to be handled later + throw eAudit1; + } catch (EInvalidCredentials eAudit2) { + CMS.debug(method + eAudit2); + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY, + auditSubjectID, + ILogger.FAILURE, + auditReqType, + auditCertSubject, + auditSignerInfo); + + audit(auditMessage); + + // rethrow the specific exception to be handled later + throw eAudit2; + } catch (EBaseException eAudit3) { + CMS.debug(method + eAudit3); + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY, + auditSubjectID, + ILogger.FAILURE, + auditReqType, + auditCertSubject, + auditSignerInfo); + + audit(auditMessage); + + // rethrow the specific exception to be handled later + throw eAudit3; + } + } + + /** + * Returns a list of configuration parameter names. + * The list is passed to the configuration console so instances of + * this implementation can be configured through the console. + * <p> + * + * @return String array of configuration parameter names. + */ + public String[] getConfigParams() { + return (mConfigParams); + } + + /** + * gets the configuration substore used by this authentication + * plug-in + * <p> + * + * @return configuration store + */ + public IConfigStore getConfigStore() { + return mConfig; + } + + /** + * gets the plug-in name of this authentication plug-in. + */ + public String getImplName() { + return mImplName; + } + + /** + * gets the name of this authentication plug-in instance + */ + public String getName() { + return mName; + } + + /** + * get the list of required credentials. + * <p> + * + * @return list of required credentials as strings. + */ + public String[] getRequiredCreds() { + return (mRequiredCreds); + } + + /** + * prepares for shutdown. + */ + public void shutdown() { + } + + ///////////////////////////////// + // IExtendedPluginInfo methods // + ///////////////////////////////// + + /** + * Activate the help system. + * <p> + * + * @return help messages + */ + public String[] getExtendedPluginInfo() { + String method = "CMCUserSignedAuth: getExtendedPluginInfo: "; + CMS.debug(method + " begins"); + String[] s = Utils.getStringArrayFromVector(mExtendedPluginInfo); + + CMS.debug(method + " s.length = " + s.length); + for (int i = 0; i < s.length; i++) { + CMS.debug("" + i + " " + s[i]); + } + return s; + } + + //////////////////// + // Logger methods // + //////////////////// + + /** + * Logs a message for this class in the system log file. + * <p> + * + * @param level The log level. + * @param msg The message to log. + * @see com.netscape.certsrv.logging.ILogger + */ + protected void log(int level, String msg) { + if (mLogger == null) + return; + mLogger.log(ILogger.EV_SYSTEM, null, ILogger.S_AUTHENTICATION, + level, "CMC User Signed Authentication: " + msg); + } + + protected IAuthToken verifySignerInfo(AuthToken authToken, SignedData cmcFullReq) throws EBaseException { + String method = "CMCUserSignedAuth: verifySignerInfo: "; + CMS.debug(method + "begins"); + + EncapsulatedContentInfo ci = cmcFullReq.getContentInfo(); + OBJECT_IDENTIFIER id = ci.getContentType(); + OCTET_STRING content = ci.getContent(); + + boolean tokenSwitched = false; + CryptoToken signToken = null; + CryptoToken savedToken = null; + CryptoManager cm = null; + try { + cm = CryptoManager.getInstance(); + ByteArrayInputStream s = new ByteArrayInputStream(content.toByteArray()); + PKIData pkiData = (PKIData) (new PKIData.Template()).decode(s); + + SET dais = cmcFullReq.getDigestAlgorithmIdentifiers(); + int numDig = dais.size(); + Hashtable<String, byte[]> digs = new Hashtable<String, byte[]>(); + + //if request key is used for signing, there MUST be only one signerInfo + //object in the signedData object. + for (int i = 0; i < numDig; i++) { + AlgorithmIdentifier dai = + (AlgorithmIdentifier) dais.elementAt(i); + String name = + DigestAlgorithm.fromOID(dai.getOID()).toString(); + + MessageDigest md = + MessageDigest.getInstance(name); + + byte[] digest = md.digest(content.toByteArray()); + + digs.put(name, digest); + } + + SET sis = cmcFullReq.getSignerInfos(); + int numSis = sis.size(); + + for (int i = 0; i < numSis; i++) { + org.mozilla.jss.pkix.cms.SignerInfo si = (org.mozilla.jss.pkix.cms.SignerInfo) sis.elementAt(i); + + String name = si.getDigestAlgorithm().toString(); + byte[] digest = digs.get(name); + + if (digest == null) { + MessageDigest md = MessageDigest.getInstance(name); + ByteArrayOutputStream ostream = new ByteArrayOutputStream(); + + pkiData.encode(ostream); + digest = md.digest(ostream.toByteArray()); + + } + // signed by previously certified signature key + SignerIdentifier sid = si.getSignerIdentifier(); + // TODO: need to handle signing key being the matching key from + // the request + if (sid.getType().equals(SignerIdentifier.ISSUER_AND_SERIALNUMBER)) { + IssuerAndSerialNumber issuerAndSerialNumber = sid.getIssuerAndSerialNumber(); + // find from the certs in the signedData + java.security.cert.X509Certificate cert = null; + + if (cmcFullReq.hasCertificates()) { + SET certs = cmcFullReq.getCertificates(); + int numCerts = certs.size(); + java.security.cert.X509Certificate[] x509Certs = new java.security.cert.X509Certificate[1]; + byte[] certByteArray = new byte[0]; + for (int j = 0; j < numCerts; j++) { + Certificate certJss = (Certificate) certs.elementAt(j); + CertificateInfo certI = certJss.getInfo(); + Name issuer = certI.getIssuer(); + + byte[] issuerB = ASN1Util.encode(issuer); +CMS.debug(method + "issuer = " + new String(issuerB)); + INTEGER sn = certI.getSerialNumber(); + // if this cert is the signer cert, not a cert in the chain + if (new String(issuerB).equals(new String( + ASN1Util.encode(issuerAndSerialNumber.getIssuer()))) + && sn.toString().equals(issuerAndSerialNumber.getSerialNumber().toString())) { + ByteArrayOutputStream os = new + ByteArrayOutputStream(); + + certJss.encode(os); + certByteArray = os.toByteArray(); + + X509CertImpl tempcert = new X509CertImpl(os.toByteArray()); + + cert = tempcert; + x509Certs[0] = cert; + // xxx validate the cert length + + } + } + CMS.debug(method + "start checking signature"); + if (cert == null) { + // find from certDB + CMS.debug(method + "verifying signature"); + si.verify(digest, id); + } else { + CMS.debug(method + "found signing cert... verifying"); + PublicKey signKey = cert.getPublicKey(); + PrivateKey.Type keyType = null; + String alg = signKey.getAlgorithm(); + + PK11PubKey pubK = null; + if (alg.equals("RSA")) { + CMS.debug(method + "signing key alg=RSA"); + keyType = PrivateKey.RSA; + pubK = PK11PubKey.fromRaw(keyType, ((X509Key) signKey).getKey()); + } else if (alg.equals("EC")) { + CMS.debug(method + "signing key alg=EC"); + keyType = PrivateKey.EC; + byte publicKeyData[] = ((X509Key) signKey).getEncoded(); + pubK = PK11ECPublicKey.fromSPKI(/*keyType,*/ publicKeyData); + } else { + CMS.debug(method + "unsupported signature algorithm: " + alg); + throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); + } + + String tokenName = + CMS.getConfigStore().getString("ca.requestVerify.token", CryptoUtil.INTERNAL_TOKEN_NAME); + // by default JSS will use internal crypto token + if (!CryptoUtil.isInternalToken(tokenName)) { + savedToken = cm.getThreadToken(); + signToken = CryptoUtil.getCryptoToken(tokenName); + if(signToken != null) { + cm.setThreadToken(signToken); + tokenSwitched = true; + CMS.debug(method + "verifySignerInfo token switched:"+ tokenName); + } else { + CMS.debug(method + "verifySignerInfo token not found:"+ tokenName+ ", trying internal"); + } + } + + CMS.debug(method + "verifying signature with public key"); + si.verify(digest, id, pubK); + } + CMS.debug(method + "finished checking signature"); + // verify signer's certificate using the revocator + if (!cm.isCertValid(certByteArray, true, CryptoManager.CertUsage.SSLClient)) { + CMS.debug(method + "CMC signature failed to be verified"); + throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); + } else { + CMS.debug(method + "CMC signature verified; but signer not yet;"); + } + // At this point, the signature has been verified; + + IAuthToken tempToken = new AuthToken(null); + netscape.security.x509.X500Name tempPrincipal = (X500Name) x509Certs[0].getSubjectDN(); + String CN = tempPrincipal.getCommonName(); //tempToken.get("userid"); + CMS.debug(method + " Principal name = " + CN); + + BigInteger certSerial = x509Certs[0].getSerialNumber(); + CMS.debug(method + " verified cert serial=" + certSerial.toString()); + authToken.set(IAuthManager.CRED_CMC_SIGNING_CERT, certSerial.toString()); + tempToken.set("cn", CN); + + return tempToken; + + } + + } else { + CMS.debug(method + "unsupported SignerIdentifier type"); + } + } + } catch (InvalidBERException e) { + CMS.debug(method + e.toString()); + } catch (IOException e) { + CMS.debug(method + e.toString()); + } catch (NotInitializedException e) { + CMS.debug(method + e.toString()); + } catch (Exception e) { + CMS.debug(method + e.toString()); + throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); + } finally { + if ((tokenSwitched == true) && (savedToken != null)){ + cm.setThreadToken(savedToken); + CMS.debug(method + "verifySignerInfo token restored"); + } + } + return null; + + } + + public String[] getExtendedPluginInfo(Locale locale) { + return null; + } + + // Profile-related methods + + public void init(IProfile profile, IConfigStore config) + throws EProfileException { + } + + /** + * Retrieves the localizable name of this policy. + */ + public String getName(Locale locale) { + return CMS.getUserMessage(locale, "CMS_AUTHENTICATION_CMS_SIGN_NAME"); + } + + /** + * Retrieves the localizable description of this policy. + */ + public String getText(Locale locale) { + return CMS.getUserMessage(locale, "CMS_AUTHENTICATION_CMS_SIGN_TEXT"); + } + + /** + * Retrieves a list of names of the value parameter. + */ + public Enumeration<String> getValueNames() { + Vector<String> v = new Vector<String>(); + v.addElement("cert_request"); + return v.elements(); + } + + public boolean isValueWriteable(String name) { + return false; + } + + /** + * Retrieves the descriptor of the given value + * parameter by name. + */ + public IDescriptor getValueDescriptor(Locale locale, String name) { + if (name.equals(CRED_CMC)) { + return new Descriptor(IDescriptor.STRING_LIST, null, null, + "CMC request"); + } + return null; + } + + public void populate(IAuthToken token, IRequest request) + throws EProfileException { + request.setExtData(IProfileAuthenticator.AUTHENTICATED_NAME, + token.getInString(AuthToken.TOKEN_CERT_SUBJECT)); + } + + public boolean isSSLClientRequired() { + return false; + } + + /** + * Signed Audit Log + * + * This method is called to store messages to the signed audit log. + * <P> + * + * @param msg signed audit log message + */ + private void audit(String msg) { + // in this case, do NOT strip preceding/trailing whitespace + // from passed-in String parameters + + if (mSignedAuditLogger == null) { + return; + } + + mSignedAuditLogger.log(ILogger.EV_SIGNED_AUDIT, + null, + ILogger.S_SIGNED_AUDIT, + ILogger.LL_SECURITY, + msg); + } + + protected void audit(AuditEvent event) { + + String template = event.getMessage(); + Object[] params = event.getParameters(); + + String message = CMS.getLogMessage(template, params); + + audit(message); + } + + /** + * Signed Audit Log Subject ID + * + * This method is called to obtain the "SubjectID" for + * a signed audit log message. + * <P> + * + * @return id string containing the signed audit log message SubjectID + */ + private String auditSubjectID() { + // if no signed audit object exists, bail + if (mSignedAuditLogger == null) { + return null; + } + + String subjectID = null; + + // Initialize subjectID + SessionContext auditContext = SessionContext.getExistingContext(); + + if (auditContext != null) { + subjectID = (String) + auditContext.get(SessionContext.USER_ID); + + if (subjectID != null) { + subjectID = subjectID.trim(); + } else { + subjectID = ILogger.NONROLEUSER; + } + } else { + subjectID = ILogger.UNIDENTIFIED; + } + + return subjectID; + } +} diff --git a/base/server/cms/src/com/netscape/cms/profile/common/BasicProfile.java b/base/server/cms/src/com/netscape/cms/profile/common/BasicProfile.java index e6fc045e5..e47c72295 100644 --- a/base/server/cms/src/com/netscape/cms/profile/common/BasicProfile.java +++ b/base/server/cms/src/com/netscape/cms/profile/common/BasicProfile.java @@ -783,6 +783,8 @@ public abstract class BasicProfile implements IProfile { boolean createConfig) throws EProfileException { + String method = "BasicProfile: createProfilePolicy: "; + CMS.debug(method + "begins"); // String setId ex: policyset.set1 // String id Id of policy : examples: p1,p2,p3 // String defaultClassId : id of the default plugin ex: validityDefaultImpl @@ -911,19 +913,18 @@ public abstract class BasicProfile implements IProfile { } } } - String defaultRoot = id + "." + PROP_DEFAULT; String constraintRoot = id + "." + PROP_CONSTRAINT; IPluginInfo defInfo = mRegistry.getPluginInfo("defaultPolicy", defaultClassId); if (defInfo == null) { - CMS.debug("BasicProfile: Cannot find " + defaultClassId); + CMS.debug(method + " Cannot find " + defaultClassId); throw new EProfileException("Cannot find " + defaultClassId); } String defaultClass = defInfo.getClassName(); - CMS.debug("BasicProfile: loading default class " + defaultClass); + CMS.debug(method + " loading default class " + defaultClass); IPolicyDefault def = null; try { @@ -931,7 +932,7 @@ public abstract class BasicProfile implements IProfile { Class.forName(defaultClass).newInstance(); } catch (Exception e) { // throw Exception - CMS.debug("BasicProfile: default policy " + + CMS.debug(method + " default policy " + defaultClass + " " + e.toString()); } if (def == null) { @@ -941,24 +942,30 @@ public abstract class BasicProfile implements IProfile { defStore = policyStore.getSubStore(defaultRoot); def.init(this, defStore); + CMS.debug(method + " default class initialized."); } IPluginInfo conInfo = mRegistry.getPluginInfo("constraintPolicy", constraintClassId); + if (conInfo == null) { + CMS.debug(method + " Cannot find " + constraintClassId); + throw new EProfileException("Cannot find " + constraintClassId); + } String constraintClass = conInfo.getClassName(); - IPolicyConstraint constraint = null; + CMS.debug(method + " loading constraint class " + constraintClass); + IPolicyConstraint constraint = null; try { constraint = (IPolicyConstraint) Class.forName(constraintClass).newInstance(); } catch (Exception e) { // throw Exception - CMS.debug("BasicProfile: constraint policy " + + CMS.debug(method + " constraint policy " + constraintClass + " " + e.toString()); } ProfilePolicy policy = null; if (constraint == null) { - CMS.debug("BasicProfile: failed to create " + constraintClass); + CMS.debug(method + " failed to create " + constraintClass); } else { IConfigStore conStore = null; @@ -966,9 +973,11 @@ public abstract class BasicProfile implements IProfile { constraint.init(this, conStore); policy = new ProfilePolicy(id, def, constraint); policies.addElement(policy); + CMS.debug(method + " constraint class initialized."); } if (createConfig) { + CMS.debug(method + " createConfig true; creating..."); String list = null; try { @@ -996,8 +1005,10 @@ public abstract class BasicProfile implements IProfile { CMS.debug("BasicProfile: commiting config store " + e.toString()); } + CMS.debug(method + " config created."); } + CMS.debug(method + "ends"); return policy; } @@ -1091,9 +1102,10 @@ public abstract class BasicProfile implements IProfile { */ public void populate(IRequest request) throws EProfileException { + String method = "BasicProfile: populate: "; String setId = getPolicySetId(request); Vector<IProfilePolicy> policies = getPolicies(setId); - CMS.debug("BasicProfile: populate() policy setid =" + setId); + CMS.debug(method + "policy setid =" + setId); for (int i = 0; i < policies.size(); i++) { IProfilePolicy policy = policies.elementAt(i); diff --git a/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java b/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java index 1c44e2cd8..57f07d1bd 100644 --- a/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java +++ b/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java @@ -71,6 +71,7 @@ import org.mozilla.jss.pkix.primitive.Name; import org.mozilla.jss.pkix.primitive.SubjectPublicKeyInfo; import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.authentication.IAuthManager; import com.netscape.certsrv.authentication.IAuthToken; import com.netscape.certsrv.authentication.ISharedToken; import com.netscape.certsrv.authority.IAuthority; @@ -110,6 +111,7 @@ import netscape.security.x509.CertificateX509Key; import netscape.security.x509.Extension; import netscape.security.x509.Extensions; import netscape.security.x509.X500Name; +import netscape.security.x509.X509CertImpl; import netscape.security.x509.X509CertInfo; import netscape.security.x509.X509Key; @@ -144,7 +146,8 @@ public abstract class EnrollProfile extends BasicProfile */ public IRequest[] createRequests(IProfileContext ctx, Locale locale) throws EProfileException { - String method = "EnrollProfile: createRequests"; + + String method = "EnrollProfile: createRequests: "; CMS.debug(method + "begins"); // determine how many requests should be created @@ -171,13 +174,20 @@ public abstract class EnrollProfile extends BasicProfile } TaggedRequest[] cmc_msgs = null; if (cert_request_type != null && cert_request_type.startsWith("cmc")) { - /* - * TODO: cfu: Phase 2: check if CMCAuth pre-signed request passed. - * if not, identityProofV2 and/or identification controls - * are required; - */ + + // donePOI true means Proof-Of-Identity is already done. + // if the auth manager is the CMCUserSignedAuth, then + // the new cert will eventually have the same subject as the + // user signing cert + // if the auth manager is the CMCAuth (agent pre-approved), + // then no changes + boolean donePOI = false; + String signingUserSerial = ctx.get(IAuthManager.CRED_CMC_SIGNING_CERT); + if (signingUserSerial != null) { + donePOI = true; + } // catch for invalid request - cmc_msgs = parseCMC(locale, cert_request); + cmc_msgs = parseCMC(locale, cert_request, donePOI); if (cmc_msgs == null) { CMS.debug(method + "parseCMC returns cmc_msgs null"); return null; @@ -209,7 +219,7 @@ public abstract class EnrollProfile extends BasicProfile } else { result[i].setExtData(REQUEST_SEQ_NUM, Integer.valueOf(i)); if ((cmc_msgs != null) && (cmc_msgs[i] != null)) { - CMS.debug("EnrollProfile: createRequests: setting cmc TaggedRequest in request"); + CMS.debug(method + "setting cmc TaggedRequest in request"); result[i].setExtData( CTX_CERT_REQUEST, ASN1Util.encode(cmc_msgs[i])); @@ -221,9 +231,6 @@ public abstract class EnrollProfile extends BasicProfile // set requested CA result[i].setExtData(IRequest.AUTHORITY_ID, ctx.get(REQUEST_AUTHORITY_ID)); - - // set user data - result[i].setExtData(IRequest.USER_DATA, ctx.get(REQUEST_USER_DATA)); } return result; } @@ -300,7 +307,7 @@ public abstract class EnrollProfile extends BasicProfile req.setExtData(REQUEST_EXTENSIONS, new CertificateExtensions()); - CMS.debug("EnrollProfile: createRequest " + + CMS.debug("EnrollProfile: createEnrollmentRequest " + req.getRequestId()); } catch (EBaseException e) { // raise exception? @@ -469,6 +476,7 @@ public abstract class EnrollProfile extends BasicProfile IRequestQueue queue = getRequestQueue(); String msg = ""; + CMS.debug(method + "begins"); boolean popChallengeRequired = request.getExtDataInBoolean("cmc_POPchallengeRequired", false); @@ -485,7 +493,7 @@ public abstract class EnrollProfile extends BasicProfile } if (token == null){ - CMS.debug(method + " auth token is null"); + CMS.debug(method + " auth token is null; agent manual approval required;"); CMS.debug(method + " validating request"); validate(request); try { @@ -500,6 +508,7 @@ public abstract class EnrollProfile extends BasicProfile // this is encryptedPOP case; defer to require decryptedPOP CMS.debug(method + " popChallengeRequired, defer to enforce decryptedPOP"); validate(request); + CMS.debug(method + " about to call setPOPchallenge"); try { setPOPchallenge(request); @@ -521,40 +530,38 @@ public abstract class EnrollProfile extends BasicProfile } } - /* - * parseCMC - * @throws EProfileException in case of error - * note: returing "null" doesn't mean failure + /** + * getPKIDataFromCMCblob + * + * @param certReqBlob cmc b64 encoded blob + * @return PKIData */ - public TaggedRequest[] parseCMC(Locale locale, String certreq) + public PKIData getPKIDataFromCMCblob(Locale locale, String certReqBlob) throws EProfileException { - String method = "EnrollProfile: parseCMC: "; + String method = "EnrollProfile: getPKIDataFromCMCblob: "; String msg = ""; // for capturing debug and throw info /* cert request must not be null */ - if (certreq == null) { - msg = method + "certreq null"; + if (certReqBlob == null) { + msg = method + "certReqBlob null"; CMS.debug(msg); throw new EProfileException( CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST") + msg); } - //CMS.debug(method + " Start parseCMC(): " + certreq); + //CMS.debug(method + " Start: " + certReqBlob); CMS.debug(method + "starts"); - TaggedRequest msgs[] = null; - - String creq = normalizeCertReq(certreq); + String creq = normalizeCertReq(certReqBlob); try { byte data[] = CMS.AtoB(creq); - ByteArrayInputStream cmcBlobIn = - new ByteArrayInputStream(data); + ByteArrayInputStream cmcBlobIn = new ByteArrayInputStream(data); - org.mozilla.jss.pkix.cms.ContentInfo cmcReq = (org.mozilla.jss.pkix.cms.ContentInfo) - org.mozilla.jss.pkix.cms.ContentInfo.getTemplate().decode(cmcBlobIn); - org.mozilla.jss.pkix.cms.SignedData cmcFullReq = - (org.mozilla.jss.pkix.cms.SignedData) cmcReq.getInterpretedContent(); + org.mozilla.jss.pkix.cms.ContentInfo cmcReq = (org.mozilla.jss.pkix.cms.ContentInfo) org.mozilla.jss.pkix.cms.ContentInfo + .getTemplate().decode(cmcBlobIn); + org.mozilla.jss.pkix.cms.SignedData cmcFullReq = (org.mozilla.jss.pkix.cms.SignedData) cmcReq + .getInterpretedContent(); org.mozilla.jss.pkix.cms.EncapsulatedContentInfo ci = cmcFullReq.getContentInfo(); OCTET_STRING content = ci.getContent(); @@ -564,6 +571,104 @@ public abstract class EnrollProfile extends BasicProfile mCMCData = pkiData; //PKIData pkiData = (PKIData) // (new PKIData.Template()).decode(cmcBlobIn); + + return pkiData; + } catch (Exception e) { + CMS.debug(method + e); + throw new EProfileException( + CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST"), e); + } + } + + public static CertificateSubjectName getCMCSigningCertSNfromCertSerial( + String certSerial) throws Exception { + X509CertImpl userCert = getCMCSigningCertFromCertSerial(certSerial); + + if (userCert != null) { + return userCert.getSubjectObj(); + } else { + return null; + } + } + + /** + * getCMCSigningCertFromCertSerial is to be used when authentication + * was done with CMCUserSignedAuth where the resulting + * authToken contains + * IAuthManager.CRED_CMC_SIGNING_CERT, serial number + * This method takes the serial number + * and finds the cert from the CA's certdb + */ + public static X509CertImpl getCMCSigningCertFromCertSerial( + String certSerial) throws Exception { + String method = "EnrollProfile: getCMCSigningCertFromCertSerial: "; + String msg = ""; + + X509CertImpl userCert = null; + + if (certSerial == null || certSerial.equals("")) { + msg = method + "certSerial empty"; + CMS.debug(msg); + throw new Exception(msg); + } + + // for CMCUserSignedAuth, the signing user is the subject of + // the new cert + ICertificateAuthority authority = (ICertificateAuthority) CMS.getSubsystem(CMS.SUBSYSTEM_CA); + try { + BigInteger serialNo = new BigInteger(certSerial); + userCert = authority.getCertificateRepository().getX509Certificate(serialNo); + } catch (NumberFormatException e) { + msg = method + e; + CMS.debug(msg); + throw new Exception(msg); + } catch (EBaseException e) { + msg = method + e + "; signing user cert not found: serial=" + certSerial; + CMS.debug(msg); + throw new Exception(msg); + } + + if (userCert != null) { + msg = method + "signing user cert found; serial=" + certSerial; + CMS.debug(msg); + } else { + msg = method + "signing user cert not found: serial=" + certSerial; + CMS.debug(msg); + throw new Exception(msg); + } + + return userCert; + } + + /* + * parseCMC + * @throws EProfileException in case of error + * note: returing "null" doesn't mean failure + */ + public TaggedRequest[] parseCMC(Locale locale, String certreq) + throws EProfileException { + return parseCMC(locale, certreq, false); + } + public TaggedRequest[] parseCMC(Locale locale, String certreq, boolean donePOI) + throws EProfileException { + + String method = "EnrollProfile: parseCMC: "; + String msg = ""; // for capturing debug and throw info + //CMS.debug(method + " Start parseCMC(): " + certreq); + CMS.debug(method + "starts"); + + /* cert request must not be null */ + if (certreq == null) { + msg = method + "certreq null"; + CMS.debug(msg); + throw new EProfileException( + CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST") + + msg); + } + + TaggedRequest msgs[] = null; + try { + PKIData pkiData = getPKIDataFromCMCblob(locale, certreq); SEQUENCE controlSeq = pkiData.getControlSequence(); int numcontrols = controlSeq.size(); SEQUENCE reqSeq = pkiData.getReqSequence(); @@ -571,6 +676,7 @@ public abstract class EnrollProfile extends BasicProfile UTF8String ident_s = null; SessionContext context = SessionContext.getContext(); if (!context.containsKey("numOfControls")) { + CMS.debug(method + "numcontrols="+ numcontrols); if (numcontrols > 0) { context.put("numOfControls", Integer.valueOf(numcontrols)); TaggedAttribute[] attributes = new TaggedAttribute[numcontrols]; @@ -587,56 +693,45 @@ public abstract class EnrollProfile extends BasicProfile boolean id_cmc_idPOPLinkRandom = false; SET vals = null; + /** + * pre-process all controls -- + * the postponed processing is so that we can capture + * the identification, if included + */ + CMS.debug(method + "about to pre-process controls"); for (int i = 0; i < numcontrols; i++) { attributes[i] = (TaggedAttribute) controlSeq.elementAt(i); OBJECT_IDENTIFIER oid = attributes[i].getType(); if (oid.equals(OBJECT_IDENTIFIER.id_cmc_decryptedPOP)) { - CMS.debug(method + " decryptedPOP found"); + CMS.debug(method + " id_cmc_decryptedPOP found"); id_cmc_decryptedPOP = true; decPopVals = attributes[i].getValues(); } else if (oid.equals(OBJECT_IDENTIFIER.id_cmc_identification)) { + CMS.debug(method + " id_cmc_identification found"); id_cmc_identification = true; ident = attributes[i].getValues(); } else if (oid.equals(OBJECT_IDENTIFIER.id_cmc_identityProofV2)) { + CMS.debug(method + " id_cmc_identityProofV2 found"); id_cmc_identityProofV2 = true; attr = attributes[i]; } else if (oid.equals(OBJECT_IDENTIFIER.id_cmc_identityProof)) { + CMS.debug(method + " id_cmc_identityProof found"); id_cmc_identityProof = true; attr = attributes[i]; } else if (oid.equals(OBJECT_IDENTIFIER.id_cmc_idPOPLinkRandom)) { - CMS.debug(method + "id_cmc_idPOPLinkRandom true"); + CMS.debug(method + "id_cmc_idPOPLinkRandom found"); id_cmc_idPOPLinkRandom = true; vals = attributes[i].getValues(); } else { + CMS.debug(method + "unknown control found"); context.put(attributes[i].getType(), attributes[i]); } } //for /** * now do the actual control processing - * (the postponed processing is so that we can capture - * the identification, if included) */ - - if (id_cmc_decryptedPOP) { - if (decPopVals != null) { - - DecryptedPOP decPop = (DecryptedPOP) (ASN1Util.decode(DecryptedPOP.getTemplate(), - ASN1Util.encode(decPopVals.elementAt(0)))); - CMS.debug(method + "DecryptedPOP encoded"); - - Integer reqId = verifyDecryptedPOP(locale, decPop); - if (reqId != null) { - context.put("decryptedPopReqId", reqId); - } - } else { //decPopVals == null - msg = "id_cmc_decryptedPOP contains invalid DecryptedPOP"; - CMS.debug(method + msg); - SEQUENCE bpids = getRequestBpids(reqSeq); - context.put("decryptedPOP", bpids); - } - return null; - } + CMS.debug(method + "processing controls..."); if (id_cmc_identification) { if (ident == null) { @@ -666,8 +761,22 @@ public abstract class EnrollProfile extends BasicProfile } } - // either V2 or not V2; can't be both - if (id_cmc_identityProofV2 && (attr != null)) { + // checking Proof Of Identity, if not pre-signed + + if (donePOI) { + // for logging purposes + if (id_cmc_identityProofV2) { + CMS.debug(method + + "pre-signed CMC request, but id_cmc_identityProofV2 found...ignore; no further proof of identification check"); + } else if (id_cmc_identityProof) { + CMS.debug(method + + "pre-signed CMC request, but id_cmc_identityProof found...ignore; no further proof of identification check"); + } else { + CMS.debug(method + "pre-signed CMC request; no further proof of identification check"); + } + } else if (id_cmc_identityProofV2 && (attr != null)) { + // either V2 or not V2; can't be both + CMS.debug(method + "not pre-signed CMC request; calling verifyIdentityProofV2;"); if (!id_cmc_identification) { SEQUENCE bpids = getRequestBpids(reqSeq); context.put("identification", bpids); @@ -685,23 +794,57 @@ public abstract class EnrollProfile extends BasicProfile SEQUENCE bpids = getRequestBpids(reqSeq); context.put("identityProofV2", bpids); - msg = " in verifyIdentityProofV2"; + msg = " after verifyIdentityProofV2"; CMS.debug(method + msg); throw new EProfileException(CMS.getUserMessage(locale, - "CMS_POI_VERIFICATION_ERROR")+ msg); + "CMS_POI_VERIFICATION_ERROR") + msg); + } else { + CMS.debug(method + "passed verifyIdentityProofV2; Proof of Identity successful;"); } } else if (id_cmc_identityProof && (attr != null)) { + CMS.debug(method + "not pre-signed CMC request; calling verifyIdentityProof;"); boolean valid = verifyIdentityProof(attr, reqSeq); if (!valid) { SEQUENCE bpids = getRequestBpids(reqSeq); context.put("identityProof", bpids); - msg = " in verifyIdentityProof"; + msg = " after verifyIdentityProof"; CMS.debug(method + msg); throw new EProfileException(CMS.getUserMessage(locale, - "CMS_POI_VERIFICATION_ERROR")+ msg); + "CMS_POI_VERIFICATION_ERROR") + msg); + } else { + CMS.debug(method + "passed verifyIdentityProof; Proof of Identity successful;"); } + } else { + msg = "not pre-signed CMC request; missing Proof of Identification control"; + CMS.debug(method + msg); + throw new EProfileException(CMS.getUserMessage(locale, + "CMS_POI_VERIFICATION_ERROR") + ":" + method + msg); + } + + if (id_cmc_decryptedPOP) { + if (decPopVals != null) { + + DecryptedPOP decPop = (DecryptedPOP) (ASN1Util.decode(DecryptedPOP.getTemplate(), + ASN1Util.encode(decPopVals.elementAt(0)))); + CMS.debug(method + "DecryptedPOP encoded"); + + Integer reqId = verifyDecryptedPOP(locale, decPop); + if (reqId != null) { + context.put("cmcDecryptedPopReqId", reqId); + } + } else { //decPopVals == null + msg = "id_cmc_decryptedPOP contains invalid DecryptedPOP"; + CMS.debug(method + msg); + SEQUENCE bpids = getRequestBpids(reqSeq); + context.put("decryptedPOP", bpids); + } + + // decryptedPOP is expected to return null; + // POPLinkWitnessV2 would have to be checked in + // round one, if required + return null; } if (id_cmc_idPOPLinkRandom && vals != null) { @@ -725,61 +868,65 @@ public abstract class EnrollProfile extends BasicProfile } } - int nummsgs = reqSeq.size(); - if (nummsgs > 0) { + /** + * in CS.cfg, cmc.popLinkWitnessRequired=true + * will enforce popLinkWitness (or V2); + */ + boolean popLinkWitnessRequired = false; + try { + String configName = "cmc.popLinkWitnessRequired"; + CMS.debug(method + "getting :" + configName); + popLinkWitnessRequired = CMS.getConfigStore().getBoolean(configName, false); + } catch (Exception e) { + // unlikely to get here + msg = method + " Failed to retrieve cmc.popLinkWitnessRequired"; + CMS.debug(msg); + throw new EProfileException(method + msg); + } + int nummsgs = reqSeq.size(); + if (!popLinkWitnessRequired) { + CMS.debug(method + "popLinkWitnessRequired false, skip check"); + } else if (nummsgs > 0) { + CMS.debug(method + "cmc.popLinkWitnessRequired is true"); + CMS.debug(method + "nummsgs =" + nummsgs); msgs = new TaggedRequest[reqSeq.size()]; SEQUENCE bpids = new SEQUENCE(); - /* TODO: add this in CS.cfg later: cmc.popLinkWitnessRequired=true - // enforce popLinkWitness (or V2) - boolean popLinkWitnessRequired = true; - try { - String configName = "cmc.popLinkWitnessRequired"; - CMS.debug(method + "getting :" + configName); - popLinkWitnessRequired = CMS.getConfigStore().getBoolean(configName, true); - CMS.debug(method + "cmc.popLinkWitnessRequired is " + popLinkWitnessRequired); - } catch (Exception e) { - // unlikely to get here - msg = method + " Failed to retrieve cmc.popLinkWitnessRequired"; - CMS.debug(msg); - throw new EProfileException(method + msg); - } -*/ - boolean valid = true; for (int i = 0; i < nummsgs; i++) { msgs[i] = (TaggedRequest) reqSeq.elementAt(i); if (!context.containsKey("POPLinkWitnessV2") && !context.containsKey("POPLinkWitness")) { - if (randomSeed != null) { - // verifyPOPLinkWitness() will determine if this is - // POPLinkWitnessV2 or POPLinkWitness - // If failure, context is set in verifyPOPLinkWitness - valid = verifyPOPLinkWitness(ident_s, randomSeed, msgs[i], bpids, context); - if (valid == false) { - if (context.containsKey("POPLinkWitnessV2")) - msg = " in POPLinkWitnessV2"; - else if (context.containsKey("POPLinkWitness")) - msg = " in POPLinkWitness"; - else - msg = " unspecified failure from verifyPOPLinkWitness"; - - CMS.debug(method + msg); - throw new EProfileException(CMS.getUserMessage(locale, - "MS_POP_LINK_WITNESS_VERIFICATION_ERROR")+ msg); - } - /* TODO: for next cmc ticket, eliminate the extra trip of parseCMC if possible, or figure a way out to bypass this on 2nd trip - } else if (popLinkWitnessRequired == true) { - //popLinkWitnessRequired == true, must have randomSeed - CMS.debug(method + "popLinkWitness(V2) required; no randomSeed found"); + CMS.debug(method + "popLinkWitness(V2) required"); + if (randomSeed == null) { + CMS.debug(method + "no randomSeed found"); context.put("POPLinkWitnessV2", bpids); - return null;*/ - } //randomSeed != null + return null; + } + + // verifyPOPLinkWitness() will determine if this is + // POPLinkWitnessV2 or POPLinkWitness + // If failure, context is set in verifyPOPLinkWitness + valid = verifyPOPLinkWitness(ident_s, randomSeed, msgs[i], bpids, context); + if (valid == false) { + if (context.containsKey("POPLinkWitnessV2")) + msg = " in POPLinkWitnessV2"; + else if (context.containsKey("POPLinkWitness")) + msg = " in POPLinkWitness"; + else + msg = " unspecified failure from verifyPOPLinkWitness"; + + CMS.debug(method + msg); + throw new EProfileException(CMS.getUserMessage(locale, + "CMS_POP_LINK_WITNESS_VERIFICATION_ERROR") + msg); + } } - } - } else + } //for + } else { + CMS.debug(method + "nummsgs 0; returning..."); return null; + } CMS.debug(method + "ends"); return msgs; @@ -1398,6 +1545,9 @@ public abstract class EnrollProfile extends BasicProfile public void fillTaggedRequest(Locale locale, TaggedRequest tagreq, X509CertInfo info, IRequest req) throws EProfileException { + String auditMessage = null; + String auditSubjectID = auditSubjectID(); + String method = "EnrollProfile: fillTaggedRequest: "; CMS.debug(method + "begins"); TaggedRequest.Type type = tagreq.getType(); @@ -1409,16 +1559,19 @@ public abstract class EnrollProfile extends BasicProfile } if (type.equals(TaggedRequest.PKCS10)) { - CMS.debug(method + " TaggedRequest type == pkcs10"); + String methodPos = method + "PKCS10: "; + CMS.debug(methodPos + " TaggedRequest type == pkcs10"); boolean sigver = true; boolean tokenSwitched = false; CryptoManager cm = null; CryptoToken signToken = null; CryptoToken savedToken = null; try { + // for PKCS10, "sigver" would provide the POP sigver = CMS.getConfigStore().getBoolean("ca.requestVerify.enabled", true); cm = CryptoManager.getInstance(); if (sigver == true) { + CMS.debug(methodPos + "sigver true, POP is to be verified"); String tokenName = CMS.getConfigStore().getString("ca.requestVerify.token", CryptoUtil.INTERNAL_TOKEN_NAME); savedToken = cm.getThreadToken(); @@ -1427,6 +1580,12 @@ public abstract class EnrollProfile extends BasicProfile cm.setThreadToken(signToken); tokenSwitched = true; } + } else { + // normally, you would not get here, as you almost always + // would want to verify the PKCS10 signature when it's + // already there instead of taking a 2nd trip + CMS.debug(methodPos + "sigver false, POP is not to be verified now, but instead will be challenged"); + req.setExtData("cmc_POPchallengeRequired", "true"); } TaggedCertificationRequest tcr = tagreq.getTcr(); @@ -1440,13 +1599,17 @@ public abstract class EnrollProfile extends BasicProfile fillPKCS10(locale, pkcs10, info, req); } catch (Exception e) { CMS.debug(method + e); + // this will throw + popFailed(locale, auditSubjectID, auditMessage, e); } finally { if ((sigver == true) && (tokenSwitched == true)){ cm.setThreadToken(savedToken); } } + CMS.debug(methodPos + "done"); } else if (type.equals(TaggedRequest.CRMF)) { - CMS.debug(method + " TaggedRequest type == crmf"); + String methodPos = method + "CRMF: "; + CMS.debug(methodPos + " TaggedRequest type == crmf"); CertReqMsg crm = tagreq.getCrm(); SessionContext context = SessionContext.getContext(); Integer nums = (Integer) (context.get("numOfControls")); @@ -1454,12 +1617,12 @@ public abstract class EnrollProfile extends BasicProfile boolean verifyAllow = false; //disable RA by default try { String configName = "cmc.lraPopWitness.verify.allow"; - CMS.debug(method + "getting :" + configName); + CMS.debug(methodPos + "getting :" + configName); verifyAllow = CMS.getConfigStore().getBoolean(configName, false); - CMS.debug(method + "cmc.lraPopWitness.verify.allow is " + verifyAllow); + CMS.debug(methodPos + "cmc.lraPopWitness.verify.allow is " + verifyAllow); } catch (Exception e) { // unlikely to get here - String msg = method + " Failed to retrieve cmc.lraPopWitness.verify.allow"; + String msg = methodPos + " Failed to retrieve cmc.lraPopWitness.verify.allow"; CMS.debug(msg); throw new EProfileException(method + msg); } @@ -1471,23 +1634,23 @@ public abstract class EnrollProfile extends BasicProfile parseLRAPopWitness(locale, crm, attr); } else { CMS.debug( - method + " verify POP in CMC because LRA POP Witness control attribute doesnt exist in the CMC request."); + methodPos + " verify POP in CMC because LRA POP Witness control attribute doesnt exist in the CMC request."); if (crm.hasPop()) { - CMS.debug(method + " hasPop true"); + CMS.debug(methodPos + " hasPop true"); verifyPOP(locale, crm); } else { // no signing POP, then do it the hard way - CMS.debug(method + "hasPop false, need to challenge"); + CMS.debug(methodPos + "hasPop false, need to challenge"); req.setExtData("cmc_POPchallengeRequired", "true"); } } } else { CMS.debug( - method + " verify POP in CMC because LRA POP Witness control attribute doesnt exist in the CMC request."); + methodPos + " verify POP in CMC because LRA POP Witness control attribute doesnt exist in the CMC request."); if (crm.hasPop()) { - CMS.debug(method + " hasPop true"); + CMS.debug(methodPos + " hasPop true"); verifyPOP(locale, crm); } else { // no signing POP, then do it the hard way - CMS.debug(method + "hasPop false, need to challenge"); + CMS.debug(methodPos + "hasPop false, need to challenge"); req.setExtData("cmc_POPchallengeRequired", "true"); } } @@ -1495,10 +1658,10 @@ public abstract class EnrollProfile extends BasicProfile } else { //!verifyAllow if (crm.hasPop()) { - CMS.debug(method + " hasPop true"); + CMS.debug(methodPos + " hasPop true"); verifyPOP(locale, crm); } else { // no signing POP, then do it the hard way - CMS.debug(method + "hasPop false, need to challenge"); + CMS.debug(methodPos + "hasPop false, need to challenge"); req.setExtData("cmc_POPchallengeRequired", "true"); } } @@ -1835,6 +1998,8 @@ public abstract class EnrollProfile extends BasicProfile public void fillPKCS10(Locale locale, PKCS10 pkcs10, X509CertInfo info, IRequest req) throws EProfileException { + String method = "EnrollProfile: fillPKCS10: "; + CMS.debug(method + "begins"); X509Key key = pkcs10.getSubjectPublicKeyInfo(); try { @@ -1869,7 +2034,7 @@ public abstract class EnrollProfile extends BasicProfile PKCS10Attribute p10Attr = p10Attrs.getAttribute(CertificateExtensions.NAME); if (p10Attr != null && p10Attr.getAttributeId().equals( PKCS9Attribute.EXTENSION_REQUEST_OID)) { - CMS.debug("Found PKCS10 extension"); + CMS.debug(method + "Found PKCS10 extension"); Extensions exts0 = (Extensions) (p10Attr.getAttributeValue()); DerOutputStream extOut = new DerOutputStream(); @@ -1879,24 +2044,22 @@ public abstract class EnrollProfile extends BasicProfile DerInputStream extIn = new DerInputStream(extB); CertificateExtensions exts = new CertificateExtensions(extIn); if (exts != null) { - CMS.debug("Set extensions " + exts); + CMS.debug(method + "Set extensions " + exts); // info.set(X509CertInfo.EXTENSIONS, exts); req.setExtData(REQUEST_EXTENSIONS, exts); } } else { - CMS.debug("PKCS10 extension Not Found"); + CMS.debug(method + "PKCS10 extension Not Found"); } } - CMS.debug("Finish parsePKCS10 - " + pkcs10.getSubjectName()); + CMS.debug(method + "Finish parsePKCS10 - " + pkcs10.getSubjectName()); } catch (IOException e) { - CMS.debug("EnrollProfile: Unable to fill PKCS #10: " + e); - CMS.debug(e); + CMS.debug(method + "Unable to fill PKCS #10: " + e); throw new EProfileException( CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST"), e); } catch (CertificateException e) { - CMS.debug("EnrollProfile: Unable to fill PKCS #10: " + e); - CMS.debug(e); + CMS.debug(method + "Unable to fill PKCS #10: " + e); throw new EProfileException( CMS.getUserMessage(locale, "CMS_PROFILE_INVALID_REQUEST"), e); } @@ -2074,8 +2237,11 @@ public abstract class EnrollProfile extends BasicProfile public void populate(IRequest request) throws EProfileException { - super.populate(request); + String method = "EnrollProfile: populate: "; + CMS.debug(method + "begins"); + + super.populate(request); } /** @@ -2240,7 +2406,7 @@ public abstract class EnrollProfile extends BasicProfile public void verifyPOP(Locale locale, CertReqMsg certReqMsg) throws EProfileException { String method = "EnrollProfile: verifyPOP: "; - CMS.debug(method + "for signing keys"); + CMS.debug(method + "for signing keys begins."); String auditMessage = null; String auditSubjectID = auditSubjectID(); @@ -2261,10 +2427,10 @@ public abstract class EnrollProfile extends BasicProfile CryptoToken verifyToken = null; String tokenName = CMS.getConfigStore().getString("ca.requestVerify.token", CryptoUtil.INTERNAL_TOKEN_NAME); if (CryptoUtil.isInternalToken(tokenName)) { - CMS.debug("POP verification using internal token"); + CMS.debug(method + "POP verification using internal token"); certReqMsg.verify(); } else { - CMS.debug("POP verification using token:" + tokenName); + CMS.debug(method + "POP verification using token:" + tokenName); verifyToken = CryptoUtil.getCryptoToken(tokenName); certReqMsg.verify(verifyToken); } @@ -2279,7 +2445,7 @@ public abstract class EnrollProfile extends BasicProfile CMS.debug(method + "Unable to verify POP: " + e); popFailed(locale, auditSubjectID, auditMessage, e); } - CMS.debug(method + "ends."); + CMS.debug(method + "done."); } private void popFailed(Locale locale, String auditSubjectID, String auditMessage) diff --git a/base/server/cms/src/com/netscape/cms/profile/constraint/CMCUserSignedSubjectNameConstraint.java b/base/server/cms/src/com/netscape/cms/profile/constraint/CMCUserSignedSubjectNameConstraint.java new file mode 100644 index 000000000..c71b6704e --- /dev/null +++ b/base/server/cms/src/com/netscape/cms/profile/constraint/CMCUserSignedSubjectNameConstraint.java @@ -0,0 +1,141 @@ +// --- 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) 2013 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cms.profile.constraint; + +import java.util.Locale; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.authentication.IAuthManager; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.profile.EProfileException; +import com.netscape.certsrv.profile.ERejectException; +import com.netscape.certsrv.profile.IPolicyDefault; +import com.netscape.certsrv.profile.IProfile; +import com.netscape.certsrv.property.IDescriptor; +import com.netscape.certsrv.request.IRequest; +import com.netscape.cms.profile.common.EnrollProfile; +import com.netscape.cms.profile.def.CMCUserSignedSubjectNameDefault; + +import netscape.security.x509.CertificateSubjectName; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CertInfo; + +/** + * This class implements the user subject name constraint for user-signed cmc requests. + * It makes sure the signing cert's subjectDN and the rsulting cert match + * + * @author cfu + * @version $Revision$, $Date$ + */ +public class CMCUserSignedSubjectNameConstraint extends EnrollConstraint { + + public CMCUserSignedSubjectNameConstraint() { + } + + public void init(IProfile profile, IConfigStore config) + throws EProfileException { + super.init(profile, config); + } + + public IDescriptor getConfigDescriptor(Locale locale, String name) { + return null; + } + + public String getDefaultConfig(String name) { + return null; + } + + /** + * Validates the request. The request is not modified + * during the validation. User encoded subject name + * is copied into the certificate template. + */ + public void validate(IRequest request, X509CertInfo info) + throws ERejectException { + String method = "CMCUserSignedSubjectNameConstraint: "; + String msg = ""; + + CMS.debug(method + "validate start"); + CertificateSubjectName infoCertSN = null; + CertificateSubjectName authTokenCertSN = null; + + + try { + infoCertSN = (CertificateSubjectName) info.get(X509CertInfo.SUBJECT); + if (infoCertSN == null) { + msg = method + "infoCertSN null"; + CMS.debug(msg); + throw new Exception(msg); + } + CMS.debug(method + "validate user subject ="+ + infoCertSN.toString()); + String certSerial = request.getExtDataInString(IAuthManager.CRED_CMC_SIGNING_CERT); + if (certSerial == null) { + msg = method + "certSerial null"; + CMS.debug(msg); + throw new Exception(msg); + } + authTokenCertSN = + EnrollProfile.getCMCSigningCertSNfromCertSerial(certSerial); + if (authTokenCertSN == null) { + msg = method + "authTokenCertSN null"; + CMS.debug(msg); + throw new Exception(msg); + } + X500Name infoCertName = (X500Name) infoCertSN.get(CertificateSubjectName.DN_NAME); + if (infoCertName == null) { + msg = method + "infoCertName null"; + CMS.debug(msg); + throw new Exception(msg); + } + X500Name authTokenCertName = (X500Name) authTokenCertSN.get(CertificateSubjectName.DN_NAME); + if (authTokenCertName == null) { + msg = method + "authTokenCertName null"; + CMS.debug(msg); + throw new Exception(msg); + } + if (infoCertName.equals(authTokenCertName)) { + CMS.debug(method + "names match"); + } else { + msg = method + "names do not match"; + CMS.debug(msg); + throw new Exception(msg); + } + + } catch (Exception e) { + throw new ERejectException( + CMS.getUserMessage(getLocale(request), + "CMS_PROFILE_SUBJECT_NAME_NOT_MATCHED") + e); + } + } + + public String getText(Locale locale) { + return CMS.getUserMessage(locale, + "CMS_PROFILE_CONSTRAINT_CMC_USER_SIGNED_SUBJECT_NAME_TEXT"); + } + + public boolean isApplicable(IPolicyDefault def) { + String method = "CMCUserSignedSubjectNameConstraint: isApplicable: "; + if (def instanceof CMCUserSignedSubjectNameDefault) { + CMS.debug(method + "true"); + return true; + } + CMS.debug(method + "false"); + return false; + } +} diff --git a/base/server/cms/src/com/netscape/cms/profile/def/CMCUserSignedSubjectNameDefault.java b/base/server/cms/src/com/netscape/cms/profile/def/CMCUserSignedSubjectNameDefault.java new file mode 100644 index 000000000..a0816ea7e --- /dev/null +++ b/base/server/cms/src/com/netscape/cms/profile/def/CMCUserSignedSubjectNameDefault.java @@ -0,0 +1,159 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package com.netscape.cms.profile.def; + +import java.io.IOException; +import java.util.Locale; + +import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.authentication.IAuthManager; +import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.profile.EProfileException; +import com.netscape.certsrv.profile.IEnrollProfile; +import com.netscape.certsrv.profile.IProfile; +import com.netscape.certsrv.property.Descriptor; +import com.netscape.certsrv.property.EPropertyException; +import com.netscape.certsrv.property.IDescriptor; +import com.netscape.certsrv.request.IRequest; +import com.netscape.cms.profile.common.EnrollProfile; + +import netscape.security.x509.CertificateSubjectName; +import netscape.security.x509.X500Name; +import netscape.security.x509.X509CertInfo; + +/** + * This class implements an enrollment default policy + * that populates a CMC signing user's subject name + * into the certificate template. + * + * @author cfu + * @version $Revision$, $Date$ + */ +public class CMCUserSignedSubjectNameDefault extends EnrollDefault { + + public static final String VAL_NAME = "name"; + + public CMCUserSignedSubjectNameDefault() { + super(); + addValueName(VAL_NAME); + } + + public void init(IProfile profile, IConfigStore config) + throws EProfileException { + super.init(profile, config); + } + + public IDescriptor getValueDescriptor(Locale locale, String name) { + if (name.equals(VAL_NAME)) { + return new Descriptor(IDescriptor.STRING, null, null, + CMS.getUserMessage(locale, "CMS_PROFILE_SUBJECT_NAME")); + } else { + return null; + } + } + + public void setValue(String name, Locale locale, + X509CertInfo info, String value) + throws EPropertyException { + if (name == null) { + throw new EPropertyException(CMS.getUserMessage( + locale, "CMS_INVALID_PROPERTY", name)); + } + if (name.equals(VAL_NAME)) { + X500Name x500name = null; + + try { + x500name = new X500Name(value); + } catch (IOException e) { + CMS.debug(e.toString()); + // failed to build x500 name + } + CMS.debug("SubjectNameDefault: setValue name=" + x500name); + try { + info.set(X509CertInfo.SUBJECT, + new CertificateSubjectName(x500name)); + } catch (Exception e) { + // failed to insert subject name + CMS.debug("CMCUserSignedSubjectNameDefault: setValue " + e.toString()); + throw new EPropertyException(CMS.getUserMessage( + locale, "CMS_INVALID_PROPERTY", name)); + } + } else { + throw new EPropertyException(CMS.getUserMessage( + locale, "CMS_INVALID_PROPERTY", name)); + } + } + + public String getValue(String name, Locale locale, + X509CertInfo info) + throws EPropertyException { + if (name == null) { + throw new EPropertyException(CMS.getUserMessage( + locale, "CMS_INVALID_PROPERTY", name)); + } + if (name.equals(VAL_NAME)) { + CertificateSubjectName sn = null; + + try { + sn = (CertificateSubjectName) + info.get(X509CertInfo.SUBJECT); + return sn.toString(); + } catch (Exception e) { + // nothing + } + throw new EPropertyException(CMS.getUserMessage( + locale, "CMS_INVALID_PROPERTY", name)); + } else { + throw new EPropertyException(CMS.getUserMessage( + locale, "CMS_INVALID_PROPERTY", name)); + } + } + + public String getText(Locale locale) { + return CMS.getUserMessage(locale, "CMS_PROFILE_DEF_CMC_USER_SIGNED_SUBJECT_NAME"); + } + + /** + * Populates the request with this policy default. + */ + public void populate(IRequest request, X509CertInfo info) + throws EProfileException { + String method = "CMCUserSignedSubjectNameDefault: populate: "; + String msg = ""; + CMS.debug(method + "begins"); + + String signingUserSerial = request.getExtDataInString(IAuthManager.CRED_CMC_SIGNING_CERT); + if (info == null) { + msg = method + "info null"; + CMS.debug(msg); + throw new EProfileException(msg); + } + + CertificateSubjectName certSN = null; + try { + certSN = EnrollProfile.getCMCSigningCertSNfromCertSerial(signingUserSerial); + info.set(X509CertInfo.SUBJECT, certSN); + CMS.debug(method + "subjectDN set in X509CertInfo"); + } catch (Exception e) { + msg = method + "exception thrown:" + e; + throw new EProfileException(e.toString()); + } + request.setExtData(IEnrollProfile.REQUEST_CERTINFO, info); + CMS.debug(method + "ends"); + } +} diff --git a/base/server/cms/src/com/netscape/cms/profile/def/EnrollDefault.java b/base/server/cms/src/com/netscape/cms/profile/def/EnrollDefault.java index 00d669e37..1d5bfc4f8 100644 --- a/base/server/cms/src/com/netscape/cms/profile/def/EnrollDefault.java +++ b/base/server/cms/src/com/netscape/cms/profile/def/EnrollDefault.java @@ -214,17 +214,18 @@ public abstract class EnrollDefault implements IPolicyDefault, ICertInfoPolicyDe */ public void populate(IRequest request) throws EProfileException { + String method = "EnrollDefault: populate: "; String name = getClass().getName(); name = name.substring(name.lastIndexOf('.') + 1); - CMS.debug(name + ": populate start"); + CMS.debug(method + name + ": start"); X509CertInfo info = request.getExtDataInCertInfo(IEnrollProfile.REQUEST_CERTINFO); populate(request, info); request.setExtData(IEnrollProfile.REQUEST_CERTINFO, info); - CMS.debug(name + ": populate end"); + CMS.debug(method + name + ": end"); } public void addValueName(String name) { diff --git a/base/server/cms/src/com/netscape/cms/profile/input/CMCCertReqInput.java b/base/server/cms/src/com/netscape/cms/profile/input/CMCCertReqInput.java index a62d6e9f7..0a9cae173 100644 --- a/base/server/cms/src/com/netscape/cms/profile/input/CMCCertReqInput.java +++ b/base/server/cms/src/com/netscape/cms/profile/input/CMCCertReqInput.java @@ -21,6 +21,8 @@ import java.util.Locale; import netscape.security.x509.X509CertInfo; +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.pkix.cmc.PKIData; import org.mozilla.jss.pkix.cmc.TaggedRequest; import com.netscape.certsrv.apps.CMS; @@ -85,19 +87,32 @@ public class CMCCertReqInput extends EnrollInput implements IProfileInput { */ public void populate(IProfileContext ctx, IRequest request) throws EProfileException { + String method = "CMCCertReqInput: populate: "; + CMS.debug(method + "begins"); + String cert_request = ctx.get(VAL_CERT_REQUEST); X509CertInfo info = request.getExtDataInCertInfo(EnrollProfile.REQUEST_CERTINFO); if (cert_request == null) { - CMS.debug("CMCCertReqInput: populate - invalid certificate request"); + CMS.debug(method + "invalid certificate request"); throw new EProfileException(CMS.getUserMessage( getLocale(request), "CMS_PROFILE_NO_CERT_REQ")); } - TaggedRequest msgs[] = mEnrollProfile.parseCMC(getLocale(request), cert_request); + // cfu: getPKIDataFromCMCblob() is extracted from parseCMC + // so it's less confusing + //TaggedRequest msgs[] = mEnrollProfile.parseCMC(getLocale(request), cert_request); + PKIData pkiData = mEnrollProfile.getPKIDataFromCMCblob(getLocale(request), cert_request); + SEQUENCE reqSeq = pkiData.getReqSequence(); + int nummsgs = reqSeq.size(); // for now we only handle one anyways + CMS.debug(method + "pkiData.getReqSequence() called; nummsgs =" + nummsgs); + TaggedRequest[] msgs = new TaggedRequest[reqSeq.size()]; + for (int i = 0; i < nummsgs; i++) { + msgs[i] = (TaggedRequest) reqSeq.elementAt(i); + } if (msgs == null) { - CMS.debug("CMCCertReqInput: populate - parseCMC returns null TaggedRequest msgs"); + CMS.debug(method + "TaggedRequest msgs null after getPKIDataFromCMCblob"); return; } // This profile only handle the first request in CRMF diff --git a/base/server/cms/src/com/netscape/cms/profile/input/CertReqInput.java b/base/server/cms/src/com/netscape/cms/profile/input/CertReqInput.java index e67f5b501..fabd2aa7c 100644 --- a/base/server/cms/src/com/netscape/cms/profile/input/CertReqInput.java +++ b/base/server/cms/src/com/netscape/cms/profile/input/CertReqInput.java @@ -19,10 +19,8 @@ package com.netscape.cms.profile.input; import java.util.Locale; -import netscape.security.pkcs.PKCS10; -import netscape.security.util.DerInputStream; -import netscape.security.x509.X509CertInfo; - +import org.mozilla.jss.asn1.SEQUENCE; +import org.mozilla.jss.pkix.cmc.PKIData; import org.mozilla.jss.pkix.cmc.TaggedRequest; import org.mozilla.jss.pkix.crmf.CertReqMsg; @@ -37,6 +35,10 @@ import com.netscape.certsrv.property.IDescriptor; import com.netscape.certsrv.request.IRequest; import com.netscape.cms.profile.common.EnrollProfile; +import netscape.security.pkcs.PKCS10; +import netscape.security.util.DerInputStream; +import netscape.security.x509.X509CertInfo; + /** * This class implements the certificate request input. * This input populates 2 main fields to the enrollment page: @@ -89,13 +91,16 @@ public class CertReqInput extends EnrollInput implements IProfileInput { */ public void populate(IProfileContext ctx, IRequest request) throws EProfileException { + String method = "CertReqInput: populate: "; + CMS.debug(method + "begins"); + String cert_request_type = ctx.get(VAL_CERT_REQUEST_TYPE); String cert_request = ctx.get(VAL_CERT_REQUEST); X509CertInfo info = request.getExtDataInCertInfo(EnrollProfile.REQUEST_CERTINFO); if (cert_request_type == null) { - CMS.debug("CertReqInput: populate - invalid cert request type " + + CMS.debug(method + "invalid cert request type " + ""); throw new EProfileException( CMS.getUserMessage(getLocale(request), @@ -103,12 +108,14 @@ public class CertReqInput extends EnrollInput implements IProfileInput { "")); } if (cert_request == null) { - CMS.debug("CertReqInput: populate - invalid certificate request"); + CMS.debug(method + "invalid certificate request"); throw new EProfileException(CMS.getUserMessage( getLocale(request), "CMS_PROFILE_NO_CERT_REQ")); } if (cert_request_type.equals(EnrollProfile.REQ_TYPE_PKCS10)) { + CMS.debug(method + "cert_request_type= REQ_TYPE_PKCS10"); + PKCS10 pkcs10 = mEnrollProfile.parsePKCS10(getLocale(request), cert_request); if (pkcs10 == null) { @@ -118,6 +125,7 @@ public class CertReqInput extends EnrollInput implements IProfileInput { mEnrollProfile.fillPKCS10(getLocale(request), pkcs10, info, request); } else if (cert_request_type.startsWith(EnrollProfile.REQ_TYPE_KEYGEN)) { + CMS.debug(method + "cert_request_type= REQ_TYPE_KEYGEN"); DerInputStream keygen = mEnrollProfile.parseKeyGen(getLocale(request), cert_request); if (keygen == null) { @@ -127,6 +135,7 @@ public class CertReqInput extends EnrollInput implements IProfileInput { mEnrollProfile.fillKeyGen(getLocale(request), keygen, info, request); } else if (cert_request_type.startsWith(EnrollProfile.REQ_TYPE_CRMF)) { + CMS.debug(method + "cert_request_type= REQ_TYPE_CRMF"); CertReqMsg msgs[] = mEnrollProfile.parseCRMF(getLocale(request), cert_request); if (msgs == null) { @@ -142,7 +151,18 @@ public class CertReqInput extends EnrollInput implements IProfileInput { mEnrollProfile.fillCertReqMsg(getLocale(request), msgs[seqNum.intValue()], info, request ); } else if (cert_request_type.startsWith(EnrollProfile.REQ_TYPE_CMC)) { - TaggedRequest msgs[] = mEnrollProfile.parseCMC(getLocale(request), cert_request); + CMS.debug(method + "cert_request_type= REQ_TYPE_CMC"); + // cfu: getPKIDataFromCMCblob() is extracted from parseCMC + // so it's less confusing + //TaggedRequest msgs[] = mEnrollProfile.parseCMC(getLocale(request), cert_request); + PKIData pkiData = mEnrollProfile.getPKIDataFromCMCblob(getLocale(request), cert_request); + SEQUENCE reqSeq = pkiData.getReqSequence(); + int nummsgs = reqSeq.size(); // for now we only handle one anyways + CMS.debug(method + "pkiData.getReqSequence() called; nummsgs =" + nummsgs); + TaggedRequest[] msgs = new TaggedRequest[reqSeq.size()]; + for (int i = 0; i < nummsgs; i++) { + msgs[i] = (TaggedRequest) reqSeq.elementAt(i); + } if (msgs == null) { throw new EProfileException(CMS.getUserMessage( @@ -159,7 +179,7 @@ public class CertReqInput extends EnrollInput implements IProfileInput { mEnrollProfile.fillTaggedRequest(getLocale(request), msgs[seqNum.intValue()], info, request); } else { // error - CMS.debug("CertReqInput: populate - invalid cert request type " + + CMS.debug(method + "invalid cert request type " + cert_request_type); throw new EProfileException( CMS.getUserMessage(getLocale(request), diff --git a/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSubmitCMCServlet.java b/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSubmitCMCServlet.java index 26ca2a4cc..1e128d0a2 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSubmitCMCServlet.java +++ b/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSubmitCMCServlet.java @@ -39,6 +39,7 @@ import org.mozilla.jss.pkix.cmc.OtherInfo; import org.mozilla.jss.pkix.cmc.TaggedAttribute; import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.authentication.IAuthManager; import com.netscape.certsrv.authentication.IAuthToken; import com.netscape.certsrv.authorization.AuthzToken; import com.netscape.certsrv.base.EBaseException; @@ -443,6 +444,18 @@ public class ProfileSubmitCMCServlet extends ProfileServlet { /////////////////////////////////////////////// // create request /////////////////////////////////////////////// + String tmpCertSerialS = ctx.get(IAuthManager.CRED_CMC_SIGNING_CERT); + if (tmpCertSerialS != null) { + // unlikely to happenm, but do this just in case + CMS.debug("ProfileSubmitCMCServlet: found existing CRED_CMC_SIGNING_CERT in ctx for CMCUserSignedAuth:" + tmpCertSerialS); + CMS.debug("ProfileSubmitCMCServlet: null it out"); + ctx.set(IAuthManager.CRED_CMC_SIGNING_CERT, ""); + } + String signingCertSerialS = (String) authToken.get(IAuthManager.CRED_CMC_SIGNING_CERT); + if (signingCertSerialS != null) { + CMS.debug("ProfileSubmitCMCServlet: setting CRED_CMC_SIGNING_CERT in ctx for CMCUserSignedAuth"); + ctx.set(IAuthManager.CRED_CMC_SIGNING_CERT, signingCertSerialS); + } try { reqs = profile.createRequests(ctx, locale); } catch (EProfileException e) { @@ -512,7 +525,7 @@ public class ProfileSubmitCMCServlet extends ProfileServlet { IRequest provedReq = null; if (reqs == null) { // handling DecryptedPOP request here - Integer reqID = (Integer) context.get("decryptedPopReqId"); + Integer reqID = (Integer) context.get("cmcDecryptedPopReqId"); provedReq = profile.getRequestQueue().findRequest(new RequestId(reqID.toString())); if (provedReq == null) { @@ -568,6 +581,19 @@ public class ProfileSubmitCMCServlet extends ProfileServlet { } } } + + tmpCertSerialS = reqs[k].getExtDataInString(IAuthManager.CRED_CMC_SIGNING_CERT); + if (tmpCertSerialS != null) { + // unlikely to happenm, but do this just in case + CMS.debug("ProfileSubmitCMCServlet: found existing CRED_CMC_SIGNING_CERT in request for CMCUserSignedAuth:" + tmpCertSerialS); + CMS.debug("ProfileSubmitCMCServlet: null it out"); + reqs[k].setExtData(IAuthManager.CRED_CMC_SIGNING_CERT, ""); + } + // put CMCUserSignedAuth authToken in request + if (signingCertSerialS != null) { + CMS.debug("ProfileSubmitCMCServlet: setting CRED_CMC_SIGNING_CERT in request for CMCUserSignedAuth"); + reqs[k].setExtData(IAuthManager.CRED_CMC_SIGNING_CERT, signingCertSerialS); + } } // put profile framework parameters into the request |