diff options
Diffstat (limited to 'base/common/src/com/netscape/cms/servlet/ocsp/AddCRLServlet.java')
-rw-r--r-- | base/common/src/com/netscape/cms/servlet/ocsp/AddCRLServlet.java | 607 |
1 files changed, 607 insertions, 0 deletions
diff --git a/base/common/src/com/netscape/cms/servlet/ocsp/AddCRLServlet.java b/base/common/src/com/netscape/cms/servlet/ocsp/AddCRLServlet.java new file mode 100644 index 000000000..9bf6c0b66 --- /dev/null +++ b/base/common/src/com/netscape/cms/servlet/ocsp/AddCRLServlet.java @@ -0,0 +1,607 @@ +// --- 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.servlet.ocsp; + + +import com.netscape.cms.servlet.common.*; +import com.netscape.cms.servlet.base.*; + +import java.math.*; +import java.util.Vector; +import java.io.InputStream; +import java.io.IOException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.ServletException; + +import org.mozilla.jss.*; +import org.mozilla.jss.asn1.INTEGER; +import org.mozilla.jss.pkix.cert.Certificate; +import org.mozilla.jss.pkix.primitive.AlgorithmIdentifier; +import org.mozilla.jss.asn1.BIT_STRING; + +import com.netscape.certsrv.base.*; +import com.netscape.certsrv.util.*; +import com.netscape.certsrv.authority.*; +import com.netscape.certsrv.ocsp.*; +import com.netscape.certsrv.logging.*; +import com.netscape.certsrv.dbs.crldb.*; +import com.netscape.certsrv.dbs.certdb.*; +import com.netscape.certsrv.dbs.repository.*; +import com.netscape.certsrv.apps.*; +import com.netscape.certsrv.authentication.*; +import com.netscape.certsrv.authorization.*; +import com.netscape.cms.servlet.*; +import com.netscape.cmsutil.util.*; + +import netscape.security.pkcs.*; +import netscape.security.x509.*; +import java.security.cert.*; +import java.util.*; +import javax.servlet.*; +import javax.servlet.http.*; + + +/** + * Update the OCSP responder with a new CRL + * + * @version $Revision$ $Date$ + */ +public class AddCRLServlet extends CMSServlet { + + public static final String BEGIN_HEADER = + "-----BEGIN CERTIFICATE REVOCATION LIST-----"; + public static final String END_HEADER = + "-----END CERTIFICATE REVOCATION LIST-----"; + + private final static String TPL_FILE = "addCRL.template"; + private String mFormPath = null; + private IOCSPAuthority mOCSPAuthority = null; + + private final static String LOGGING_SIGNED_AUDIT_CRL_RETRIEVAL = + "LOGGING_SIGNED_AUDIT_CRL_RETRIEVAL_3"; + private final static String LOGGING_SIGNED_AUDIT_CRL_VALIDATION = + "LOGGING_SIGNED_AUDIT_CRL_VALIDATION_2"; + + public AddCRLServlet() { + super(); + } + + /** + * initialize the servlet. This servlet uses the template file + * "addCRL.template" to process the response. + * + * @param sc servlet configuration, read from the web.xml file + */ + public void init(ServletConfig sc) throws ServletException { + super.init(sc); + // override success to display own output. + + mFormPath = "/" + mAuthority.getId() + "/" + TPL_FILE; + mTemplates.remove(CMSRequest.SUCCESS); + mOCSPAuthority = (IOCSPAuthority) mAuthority; + if (mOutputTemplatePath != null) + mFormPath = mOutputTemplatePath; + } + + /** + * Process the HTTP request. + * <P> + * + * <ul> + * <li>http.param crl certificate revocation list, base-64, DER encoded + * wrapped in -----BEGIN CERTIFICATE REVOCATION LIST-----, + * -----END CERTIFICATE REVOCATION LIST----- strings + * <li>http.param noui if true, use minimal hardcoded text response + * <li>signed.audit LOGGING_SIGNED_AUDIT_CRL_RETRIEVAL used when CRLs are + * retrieved by the OCSP Responder ("agent" or "EE") + * <li>signed.audit LOGGING_SIGNED_AUDIT_CRL_VALIDATION used when CRL is + * retrieved and validation process occurs ("agent" or "EE") + * </ul> + * @param cmsReq the object holding the request and response information + * @exception EBaseException an error has occurred + */ + protected synchronized void process(CMSRequest cmsReq) + throws EBaseException { + boolean CRLFetched = false; + boolean CRLValidated = false; + String auditMessage = null; + String auditSubjectID = auditSubjectID(); + String auditCRLNum = ILogger.SIGNED_AUDIT_EMPTY_VALUE; + + IStatsSubsystem statsSub = (IStatsSubsystem)CMS.getSubsystem("stats"); + if (statsSub != null) { + statsSub.startTiming("add_crl", true /* main action */); + } + + try { + HttpServletRequest req = cmsReq.getHttpReq(); + HttpServletResponse resp = cmsReq.getHttpResp(); + + IAuthToken authToken = authenticate(cmsReq); + + AuthzToken authzToken = null; + + try { + authzToken = authorize(mAclMethod, authToken, + mAuthzResourceName, "add"); + } catch (Exception e) { + // do nothing for now + } + + if (authzToken == null) { + cmsReq.setStatus(CMSRequest.UNAUTHORIZED); + + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_CRL_RETRIEVAL, + auditSubjectID, + ILogger.FAILURE, + auditCRLNum ); + + audit( auditMessage ); + + return; + } + + if (auditSubjectID.equals(ILogger.NONROLEUSER) || + auditSubjectID.equals(ILogger.UNIDENTIFIED)) { + if (authToken != null) { + String uid = authToken.getInString(IAuthToken.USER_ID); + if (uid != null) { + CMS.debug("AddCAServlet: auditSubjectID set to "+uid); + auditSubjectID = uid; + } + } + } + log(ILogger.LL_INFO, "AddCRLServlet"); + String b64 = cmsReq.getHttpReq().getParameter("crl"); + if (CMS.debugOn()) CMS.debug("AddCRLServlet: b64=" + b64); + + if (b64 == null) { + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_CRL_RETRIEVAL, + auditSubjectID, + ILogger.FAILURE, + auditCRLNum ); + + audit( auditMessage ); + + throw new ECMSGWException( + CMS.getUserMessage("CMS_GW_MISSING_CRL")); + } + + String nouiParm = cmsReq.getHttpReq().getParameter("noui"); + boolean noUI = false; + + if (nouiParm != null && nouiParm.equals("true")) { + noUI = true; + CMS.debug("AddCRLServlet: noUI=true"); + } else { + CMS.debug("AddCRLServlet: noUI=false"); + } + + CMSTemplate form = null; + Locale[] locale = new Locale[1]; + + try { + if (!noUI) { + form = getTemplate(mFormPath, req, locale); + } + } catch (IOException e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSGW_ERR_GET_TEMPLATE", mFormPath, + e.toString())); + + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_CRL_RETRIEVAL, + auditSubjectID, + ILogger.FAILURE, + auditCRLNum ); + + audit( auditMessage ); + + throw new ECMSGWException( + CMS.getUserMessage("CMS_GW_DISPLAY_TEMPLATE_ERROR")); + } + + IArgBlock header = CMS.createArgBlock(); + IArgBlock fixed = CMS.createArgBlock(); + CMSTemplateParams argSet = new CMSTemplateParams(header, fixed); + + if (b64.indexOf(BEGIN_HEADER) == -1) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSGW_MISSING_CRL_HEADER")); + + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_CRL_RETRIEVAL, + auditSubjectID, + ILogger.FAILURE, + auditCRLNum ); + + audit( auditMessage ); + + throw new ECMSGWException(CMS.getUserMessage(getLocale(req), + "CMS_GW_MISSING_CRL_HEADER")); + } + if (b64.indexOf(END_HEADER) == -1) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSGW_MISSING_CRL_FOOTER")); + + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_CRL_RETRIEVAL, + auditSubjectID, + ILogger.FAILURE, + auditCRLNum ); + + audit( auditMessage ); + + throw new ECMSGWException(CMS.getUserMessage(getLocale(req), + "CMS_GW_MISSING_CRL_FOOTER")); + } + + IDefStore defStore = mOCSPAuthority.getDefaultStore(); + + X509CRLImpl crl = null; + + try { + long startTime = CMS.getCurrentDate().getTime(); + CMS.debug("AddCRLServlet: mapCRL start startTime=" + startTime); + if (statsSub != null) { + statsSub.startTiming("decode_crl"); + } + crl = mapCRL1( b64 ); + if (statsSub != null) { + statsSub.endTiming("decode_crl"); + } + long endTime = CMS.getCurrentDate().getTime(); + CMS.debug("AddCRLServlet: mapCRL done endTime=" + endTime + + " diff=" + (endTime - startTime)); + + // Retrieve the actual CRL number + BigInteger crlNum = crl.getCRLNumber(); + if( crlNum != null ) { + auditCRLNum = crlNum.toString(); + } + + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_CRL_RETRIEVAL, + auditSubjectID, + ILogger.SUCCESS, + auditCRLNum ); + + audit( auditMessage ); + + // acknowledge that the CRL has been retrieved + CRLFetched = true; + } catch (Exception e) { + // error + + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_CRL_RETRIEVAL, + auditSubjectID, + ILogger.FAILURE, + auditCRLNum ); + + audit( auditMessage ); + + throw new ECMSGWException( + CMS.getUserMessage("CMS_GW_DECODING_CRL_ERROR")); + } + log(ILogger.LL_INFO, "AddCRLServlet: CRL Issuer DN " + + crl.getIssuerDN().getName()); + + // update the CRLIssuingPoint record + if (crl == null) { + // error + + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_CRL_VALIDATION, + auditSubjectID, + ILogger.FAILURE ); + + audit( auditMessage ); + + throw new ECMSGWException( + CMS.getUserMessage("CMS_GW_DECODING_CRL_ERROR")); + } + + ICRLIssuingPointRecord pt = null; + + try { + pt = defStore.readCRLIssuingPoint( + crl.getIssuerDN().getName()); + } catch (Exception e) { + log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSGW_NO_CRL_ISSUING_POINT_FOUND", + crl.getIssuerDN().getName())); + + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_CRL_VALIDATION, + auditSubjectID, + ILogger.FAILURE ); + + audit( auditMessage ); + + throw new ECMSGWException( + CMS.getUserMessage("CMS_GW_DECODING_CRL_ERROR")); + } + log(ILogger.LL_INFO, "AddCRLServlet: IssuingPoint " + + pt.getThisUpdate()); + + // verify CRL + byte caCertData[] = pt.getCACert(); + if (caCertData != null) { + try { + X509CertImpl caCert = new X509CertImpl(caCertData); + CMS.debug("AddCRLServlet: start verify"); + + CryptoManager cmanager = CryptoManager.getInstance(); + org.mozilla.jss.crypto.X509Certificate jssCert = null; + try { + jssCert = cmanager.importCACertPackage( + caCert.getEncoded()); + } catch (Exception e2) { + CMS.debug("AddCRLServlet: importCACertPackage " + + e2.toString()); + throw new EBaseException( e2.toString() ); + } + + if (statsSub != null) { + statsSub.startTiming("verify_crl"); + } + crl.verify(jssCert.getPublicKey(), "Mozilla-JSS"); + if (statsSub != null) { + statsSub.endTiming("verify_crl"); + } + CMS.debug("AddCRLServlet: done verify"); + + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_CRL_VALIDATION, + auditSubjectID, + ILogger.SUCCESS ); + + audit( auditMessage ); + + // acknowledge that the CRL has been validated + CRLValidated = true; + } catch (Exception e) { + CMS.debug("AddCRLServlet: failed to verify CRL " + e.toString()); + CMS.debug(e); + log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSGW_NO_CRL_ISSUING_POINT_FOUND", + crl.getIssuerDN().getName())); + + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_CRL_VALIDATION, + auditSubjectID, + ILogger.FAILURE ); + + audit( auditMessage ); + + throw new ECMSGWException( + CMS.getUserMessage("CMS_GW_DECODING_CRL_ERROR")); + } + } + + if ((pt.getThisUpdate() != null) && + (pt.getThisUpdate().getTime() >= + crl.getThisUpdate().getTime())) { + // error, the uploaded CRL is older than the current + CMS.debug("AddCRLServlet: no update, CRL is older"); + log(ILogger.LL_INFO, + "AddCRLServlet: no update, received CRL is older " + + "than current CRL"); + if (noUI) { + try { + resp.setContentType("application/text"); + resp.getOutputStream().write("status=1\n".getBytes()); + resp.getOutputStream().write( + "error=Sent CRL is older than the current CRL\n".getBytes()); + resp.getOutputStream().flush(); + cmsReq.setStatus(CMSRequest.SUCCESS); + + // NOTE: The signed audit events + // LOGGING_SIGNED_AUDIT_CRL_RETRIEVAL and + // LOGGING_SIGNED_AUDIT_CRL_VALIDATION have + // already been logged at this point! + + return; + } catch (Exception e) { + } + } else { + CMS.debug("AddCRLServlet: CRL is older"); + + // NOTE: The signed audit events + // LOGGING_SIGNED_AUDIT_CRL_RETRIEVAL and + // LOGGING_SIGNED_AUDIT_CRL_VALIDATION have + // already been logged at this point! + + throw new ECMSGWException(CMS.getUserMessage( + "CMS_GW_OLD_CRL_ERROR")); + } + } + + if (crl.isDeltaCRL()) { + CMS.debug("AddCRLServlet: no update, Delta CRLs are not supported."); + log(ILogger.LL_INFO, "AddCRLServlet: no update, "+ + CMS.getUserMessage("CMS_GW_DELTA_CRL_NOT_SUPPORTED")); + if (noUI) { + try { + resp.setContentType("application/text"); + resp.getOutputStream().write("status=1\n".getBytes()); + resp.getOutputStream().write( + "error=Delta CRLs are not supported.\n".getBytes()); + resp.getOutputStream().flush(); + cmsReq.setStatus(CMSRequest.SUCCESS); + + return; + } catch (Exception e) { + } + } else { + throw new ECMSGWException(CMS.getUserMessage("CMS_GW_DELTA_CRL_NOT_SUPPORTED")); + } + } + + CMS.debug("AddCRLServlet: strt committing CRL"); + log(ILogger.LL_INFO, "AddCRLServlet: Start Committing CRL"); + + // ***************************************************** + // The commit transaction may take long time and + // there may have a system crash during the transaction + // ***************************************************** + + IRepositoryRecord repRec = defStore.createRepositoryRecord(); + + repRec.set(IRepositoryRecord.ATTR_SERIALNO, + new BigInteger(Long.toString(crl.getThisUpdate().getTime()))); + try { + defStore.addRepository( + crl.getIssuerDN().getName(), + Long.toString(crl.getThisUpdate().getTime()), + repRec); + log(ILogger.EV_AUDIT, AuditFormat.LEVEL, "Added CRL Updated " + + Long.toString(crl.getThisUpdate().getTime())); + } catch (Exception e) { + CMS.debug("AddCRLServlet: add repository e=" + e.toString()); + } + log(ILogger.LL_INFO, "AddCRLServlet: Created CRL Repository " + + Long.toString(crl.getThisUpdate().getTime())); + + if (defStore.waitOnCRLUpdate()) { + defStore.updateCRL(crl); + } else { + // when the CRL large, the thread is terminiated by the + // servlet framework before it can finish its work + UpdateCRLThread uct = new UpdateCRLThread(defStore, crl); + + uct.start(); + } + + try { + ServletOutputStream out = resp.getOutputStream(); + + if (noUI) { + CMS.debug("AddCRLServlet: return result noUI=true"); + resp.setContentType("application/text"); + resp.getOutputStream().write("status=0".getBytes()); + resp.getOutputStream().flush(); + cmsReq.setStatus(CMSRequest.SUCCESS); + } else { + CMS.debug("AddCRLServlet: return result noUI=false"); + String xmlOutput = req.getParameter("xml"); + if (xmlOutput != null && xmlOutput.equals("true")) { + outputXML(resp, argSet); + } else { + resp.setContentType("text/html"); + form.renderOutput(out, argSet); + cmsReq.setStatus(CMSRequest.SUCCESS); + } + } + } catch (IOException e) { + CMS.debug("AddCRLServlet: return result error=" + e.toString()); + mOCSPAuthority.log(ILogger.LL_FAILURE, + CMS.getLogMessage("CMSGW_ERR_STREAM_TEMPLATE", + e.toString())); + + // NOTE: The signed audit events + // LOGGING_SIGNED_AUDIT_CRL_RETRIEVAL and + // LOGGING_SIGNED_AUDIT_CRL_VALIDATION have + // already been logged at this point! + + throw new ECMSGWException( + CMS.getUserMessage("CMS_GW_DISPLAY_TEMPLATE_ERROR")); + } + } catch( EBaseException eAudit1 ) { + if( !CRLFetched ) { + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_CRL_RETRIEVAL, + auditSubjectID, + ILogger.FAILURE, + auditCRLNum ); + + audit( auditMessage ); + } else { + if( !CRLValidated ) { + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + LOGGING_SIGNED_AUDIT_CRL_VALIDATION, + auditSubjectID, + ILogger.FAILURE ); + + audit( auditMessage ); + } + } + throw eAudit1; + } + if (statsSub != null) { + statsSub.endTiming("add_crl"); + } + } + + public X509CRLImpl mapCRL1(String mime64) + throws IOException { + mime64 = Cert.stripCRLBrackets(mime64.trim()); + + byte rawPub[] = CMS.AtoB(mime64); + X509CRLImpl crl = null; + + try { + crl = new X509CRLImpl(rawPub, false); + } catch (Exception e) { + throw new IOException(e.toString()); + } + return crl; + } +} + + +class UpdateCRLThread extends Thread { + private IDefStore mDefStore = null; + private X509CRL mCRL = null; + + public UpdateCRLThread( + IDefStore defStore, X509CRL crl) { + mDefStore = defStore; + mCRL = crl; + } + + public void run() { + try { + if (!((X509CRLImpl)mCRL).areEntriesIncluded()) + mCRL = new X509CRLImpl(((X509CRLImpl)mCRL).getEncoded()); + mDefStore.updateCRL(mCRL); + } catch (CRLException e) { + } catch (X509ExtensionException e) { + } catch (EBaseException e) { + // ignore + } + } +} |