From 010c7ce55988016cfd9b00b0e191b76bc5b58d9e Mon Sep 17 00:00:00 2001
From: Jack Magne <jmagne@localhost.localdomain>
Date: Tue, 12 May 2015 13:49:00 -0700
Subject: [PATCH] Fix XSS attacks on the dogtag administration page #1373.

Porting this set of fixes over from last downstream release upstream.
---
 .../com/netscape/cms/servlet/cert/DisplayCRL.java  |  2 +-
 .../cms/servlet/cert/EnrollmentProcessor.java      |  9 ++--
 .../cms/servlet/cert/GetCertFromRequest.java       |  5 +-
 .../cms/servlet/cert/RenewalProcessor.java         | 13 +++--
 .../netscape/cms/servlet/common/CMSTemplate.java   | 63 +++++++++++++++++-----
 .../cms/servlet/profile/ProfileApproveServlet.java |  4 +-
 .../cms/servlet/profile/ProfileProcessServlet.java |  4 +-
 .../cms/servlet/profile/ProfileReviewServlet.java  |  6 +--
 .../cms/servlet/profile/ProfileSelectServlet.java  |  2 +-
 .../servlet/profile/ProfileSubmitCMCServlet.java   |  4 +-
 .../netscape/cms/servlet/request/CheckRequest.java |  6 +--
 11 files changed, 81 insertions(+), 37 deletions(-)

diff --git a/base/server/cms/src/com/netscape/cms/servlet/cert/DisplayCRL.java b/base/server/cms/src/com/netscape/cms/servlet/cert/DisplayCRL.java
index 3c9d577..e42deee 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/cert/DisplayCRL.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/cert/DisplayCRL.java
@@ -231,7 +231,7 @@ public class DisplayCRL extends CMSServlet {
         }
         if (crlIssuingPointId == null) {
             header.addStringValue("error",
-                    "Request to unspecified or non-existing CRL issuing point: " + ipId);
+                    "Request to unspecified or non-existing CRL issuing point: " + CMSTemplate.escapeJavaScriptStringHTML(ipId));
             return;
         }
 
diff --git a/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java b/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java
index ee56f01..b3bd141 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java
@@ -38,6 +38,7 @@ import com.netscape.certsrv.profile.ProfileAttribute;
 import com.netscape.certsrv.profile.ProfileInput;
 import com.netscape.certsrv.request.IRequest;
 import com.netscape.cms.servlet.common.CMSRequest;
+import com.netscape.cms.servlet.common.CMSTemplate;
 import com.netscape.cms.servlet.profile.SSLClientCertProvider;
 import com.netscape.cmsutil.ldap.LDAPUtil;
 
@@ -92,8 +93,8 @@ public class EnrollmentProcessor extends CertProcessor {
         IProfile profile = ps.getProfile(profileId);
 
         if (profile == null) {
-            CMS.debug(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND", profileId));
-            throw new BadRequestDataException(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND", profileId));
+            CMS.debug(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND", CMSTemplate.escapeJavaScriptString(profileId)));
+            throw new BadRequestDataException(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND",CMSTemplate.escapeJavaScriptString(profileId)));
         }
 
         CertEnrollmentRequest data = CertEnrollmentRequestFactory.create(cmsReq, profile, locale);
@@ -136,8 +137,8 @@ public class EnrollmentProcessor extends CertProcessor {
 
             IProfile profile = ps.getProfile(profileId);
             if (profile == null) {
-                CMS.debug(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND", profileId));
-                throw new BadRequestDataException(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND", profileId));
+                CMS.debug(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND", CMSTemplate.escapeJavaScriptString(profileId)));
+                throw new BadRequestDataException(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND", CMSTemplate.escapeJavaScriptString(profileId)));
             }
             if (!ps.isProfileEnable(profileId)) {
                 CMS.debug("EnrollmentSubmitter: Profile " + profileId + " not enabled");
diff --git a/base/server/cms/src/com/netscape/cms/servlet/cert/GetCertFromRequest.java b/base/server/cms/src/com/netscape/cms/servlet/cert/GetCertFromRequest.java
index af8b3cc..afba866 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/cert/GetCertFromRequest.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/cert/GetCertFromRequest.java
@@ -18,8 +18,8 @@
 package com.netscape.cms.servlet.cert;
 
 import java.io.IOException;
-import java.util.Locale;
 import java.math.BigInteger;
+import java.util.Locale;
 
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
@@ -49,6 +49,7 @@ import com.netscape.certsrv.request.RequestId;
 import com.netscape.certsrv.request.RequestStatus;
 import com.netscape.cms.servlet.base.CMSServlet;
 import com.netscape.cms.servlet.common.CMSRequest;
+import com.netscape.cms.servlet.common.CMSTemplate;
 import com.netscape.cms.servlet.common.CMSTemplateParams;
 import com.netscape.cms.servlet.common.ECMSGWException;
 import com.netscape.cms.servlet.common.ICMSTemplateFiller;
@@ -175,7 +176,7 @@ public class GetCertFromRequest extends CMSServlet {
         } catch (NumberFormatException e) {
             log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSGW_INVALID_REQ_ID_FORMAT", requestId));
             throw new EBaseException(
-                    CMS.getUserMessage(getLocale(httpReq), "CMS_BASE_INVALID_NUMBER_FORMAT_1", requestId));
+                    CMS.getUserMessage(getLocale(httpReq), "CMS_BASE_INVALID_NUMBER_FORMAT_1", CMSTemplate.escapeJavaScriptStringHTML(requestId)));
         }
 
         IRequest r = mQueue.findRequest(new RequestId(requestId));
diff --git a/base/server/cms/src/com/netscape/cms/servlet/cert/RenewalProcessor.java b/base/server/cms/src/com/netscape/cms/servlet/cert/RenewalProcessor.java
index 7daad6c..e9766a3 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/cert/RenewalProcessor.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/cert/RenewalProcessor.java
@@ -46,6 +46,7 @@ import com.netscape.certsrv.profile.IProfileContext;
 import com.netscape.certsrv.profile.IProfileInput;
 import com.netscape.certsrv.request.IRequest;
 import com.netscape.cms.servlet.common.CMSRequest;
+import com.netscape.cms.servlet.common.CMSTemplate;
 import com.netscape.cms.servlet.profile.SSLClientCertProvider;
 
 public class RenewalProcessor extends CertProcessor {
@@ -59,7 +60,8 @@ public class RenewalProcessor extends CertProcessor {
         String profileId = (this.profileID == null) ? req.getParameter("profileId") : this.profileID;
         IProfile profile = ps.getProfile(profileId);
         if (profile == null) {
-            throw new BadRequestDataException(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND", profileId));
+            throw new BadRequestDataException(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND",
+                    CMSTemplate.escapeJavaScriptString(profileId)));
         }
 
         CertEnrollmentRequest data = CertEnrollmentRequestFactory.create(cmsReq, profile, locale);
@@ -83,7 +85,7 @@ public class RenewalProcessor extends CertProcessor {
             throws EBaseException {
         try {
             if (CMS.debugOn()) {
-                HashMap<String,String> params = data.toParams();
+                HashMap<String, String> params = data.toParams();
                 printParameterValues(params);
             }
             CMS.debug("RenewalSubmitter: isRenewal true");
@@ -98,8 +100,9 @@ public class RenewalProcessor extends CertProcessor {
 
             IProfile renewProfile = ps.getProfile(renewProfileId);
             if (renewProfile == null) {
-                CMS.debug(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND", renewProfileId));
-                throw new BadRequestDataException(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND", renewProfileId));
+                CMS.debug(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND",
+                        CMSTemplate.escapeJavaScriptString(renewProfileId)));
+                throw new BadRequestDataException(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND",CMSTemplate.escapeJavaScriptString(renewProfileId)));
             }
             if (!ps.isProfileEnable(renewProfileId)) {
                 CMS.debug("RenewalSubmitter: Profile " + renewProfileId + " not enabled");
@@ -171,7 +174,7 @@ public class RenewalProcessor extends CertProcessor {
             Integer origSeqNum = origReq.getExtDataInInteger(IEnrollProfile.REQUEST_SEQ_NUM);
             IProfile profile = ps.getProfile(profileId);
             if (profile == null) {
-                CMS.debug(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND", profileId));
+                CMS.debug(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND",CMSTemplate.escapeJavaScriptString(profileId)));
                 throw new EBaseException(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND", profileId));
             }
             if (!ps.isProfileEnable(profileId)) {
diff --git a/base/server/cms/src/com/netscape/cms/servlet/common/CMSTemplate.java b/base/server/cms/src/com/netscape/cms/servlet/common/CMSTemplate.java
index dc8cef6..efc0eea 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/common/CMSTemplate.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/common/CMSTemplate.java
@@ -145,7 +145,7 @@ public class CMSTemplate extends CMSFile {
         CMSTemplateParams data = input;
 
         try (HTTPOutputStreamWriter http_out = (mCharset == null ?
-                new HTTPOutputStreamWriter(rout): new HTTPOutputStreamWriter(rout, mCharset))) {
+                new HTTPOutputStreamWriter(rout) : new HTTPOutputStreamWriter(rout, mCharset))) {
             templateLine out = new templateLine();
 
             // Output the prolog
@@ -347,6 +347,7 @@ public class CMSTemplate extends CMSFile {
      * portion of an HTML document.
      * stevep - performance improvements - about 4 times faster than before.
      */
+
     public static String escapeJavaScriptString(String v) {
         int l = v.length();
         char in[] = new char[l];
@@ -358,7 +359,7 @@ public class CMSTemplate extends CMSFile {
         for (int i = 0; i < l; i++) {
             char c = in[i];
 
-            if ((c > 0x23) && (c != 0x5c) && (c != 0x3c) && (c != 0x3e)) {
+            if ((c > 0x23) && (c != 0x5c) && (c != 0x3c) && (c != 0x3e) && (c != 0x3b)) {
                 out[j++] = c;
                 continue;
             }
@@ -366,6 +367,7 @@ public class CMSTemplate extends CMSFile {
             if ((c == 0x5c) && ((i + 1) < l) && (in[i + 1] == 'n' ||
                     in[i + 1] == 'r' || in[i + 1] == 'f' || in[i + 1] == 't' ||
                     in[i + 1] == '<' || in[i + 1] == '>' ||
+                    in[i + 1] == 'x' || in[i + 1] == ';' ||
                     in[i + 1] == '\"' || in[i + 1] == '\'' || in[i + 1] == '\\')) {
                 if (in[i + 1] == 'x' && ((i + 3) < l) && in[i + 2] == '3' &&
                         (in[i + 3] == 'c' || in[i + 3] == 'e')) {
@@ -438,10 +440,11 @@ public class CMSTemplate extends CMSFile {
      * Like escapeJavaScriptString(String s) but also escape '[' for
      * HTML processing.
      */
+
     public static String escapeJavaScriptStringHTML(String v) {
         int l = v.length();
         char in[] = new char[l];
-        char out[] = new char[l * 4];
+        char out[] = new char[l * 5];
         int j = 0;
 
         v.getChars(0, l, in, 0);
@@ -457,6 +460,7 @@ public class CMSTemplate extends CMSFile {
             if ((c == 0x5c) && ((i + 1) < l) && (in[i + 1] == 'n' ||
                     in[i + 1] == 'r' || in[i + 1] == 'f' || in[i + 1] == 't' ||
                     in[i + 1] == '<' || in[i + 1] == '>' ||
+                    in[i + 1] == 'x' || in[i + 1] == ';' ||
                     in[i + 1] == '\"' || in[i + 1] == '\'' || in[i + 1] == '\\')) {
                 if (in[i + 1] == 'x' && ((i + 3) < l) && in[i + 2] == '3' &&
                         (in[i + 3] == 'c' || in[i + 3] == 'e')) {
@@ -465,12 +469,38 @@ public class CMSTemplate extends CMSFile {
                     out[j++] = in[i + 2];
                     out[j++] = in[i + 3];
                     i += 3;
+
+                    continue;
+                } else if (in[i + 1] == '<' || in[i + 1] == '>') {
+                    c = in[i + 1];
+                    i++;
+                } else if (in[i + 1] == ';') {
+                    out[j++] = in[i + 1];
+                    i++;
+                    continue;
                 } else {
                     out[j++] = '\\';
                     out[j++] = in[i + 1];
                     i++;
+                    continue;
+                }
+            }
+            if (c == '&') {
+                int k;
+                for (k = 0; k < 8 && (i + k) < l; k++) {
+                    out[j + k] = in[i + k];
+                    if (in[i + k] == ';')
+                        break;
+                    if (in[i + k] == '<' || in[i + k] == '>') {
+                        k = 8;
+                        break;
+                    }
+                }
+                if (k < 8) {
+                    i += k;
+                    j += k + 1;
+                    continue;
                 }
-                continue;
             }
 
             switch (c) {
@@ -505,16 +535,25 @@ public class CMSTemplate extends CMSFile {
                 break;
 
             case '<':
-                out[j++] = '\\';
-                out[j++] = 'x';
-                out[j++] = '3';
-                out[j++] = 'c';
+                out[j++] = '&';
+                out[j++] = 'l';
+                out[j++] = 't';
+                out[j++] = ';';
                 break;
+
             case '>':
-                out[j++] = '\\';
-                out[j++] = 'x';
-                out[j++] = '3';
-                out[j++] = 'e';
+                out[j++] = '&';
+                out[j++] = 'g';
+                out[j++] = 't';
+                out[j++] = ';';
+                break;
+
+            case '&':
+                out[j++] = '&';
+                out[j++] = 'a';
+                out[j++] = 'm';
+                out[j++] = 'p';
+                out[j++] = ';';
                 break;
 
             default:
diff --git a/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileApproveServlet.java b/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileApproveServlet.java
index 7b53430..d82ecc5 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileApproveServlet.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileApproveServlet.java
@@ -359,14 +359,14 @@ public class ProfileApproveServlet extends ProfileServlet {
             args.set(ARG_ERROR_CODE, "1");
             args.set(ARG_ERROR_REASON, e.toString());
             args.set(ARG_ERROR_REASON, CMS.getUserMessage(locale,
-                    "CMS_PROFILE_NOT_FOUND", profileId));
+                    "CMS_PROFILE_NOT_FOUND", escapeJavaScriptString(profileId)));
             outputTemplate(request, response, args);
             return;
         }
         if (profile == null) {
             args.set(ARG_ERROR_CODE, "1");
             args.set(ARG_ERROR_REASON, CMS.getUserMessage(locale,
-                    "CMS_PROFILE_NOT_FOUND", profileId));
+                    "CMS_PROFILE_NOT_FOUND", escapeJavaScriptString(profileId)));
             outputTemplate(request, response, args);
             return;
         }
diff --git a/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileProcessServlet.java b/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileProcessServlet.java
index 82b168e..fcbb70f 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileProcessServlet.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileProcessServlet.java
@@ -89,14 +89,14 @@ public class ProfileProcessServlet extends ProfileServlet {
 
         IRequest req = processor.getRequest(requestId);
         if (req == null) {
-            setError(args, CMS.getUserMessage(locale, "CMS_REQUEST_NOT_FOUND", requestId), request, response);
+            setError(args, CMS.getUserMessage(locale, "CMS_REQUEST_NOT_FOUND", escapeJavaScriptString(requestId)), request, response);
             return;
         }
 
         String profileId = req.getExtDataInString("profileId");
         if (profileId == null || profileId.equals("")) {
             CMS.debug("ProfileProcessServlet: Profile Id not found");
-            setError(args, CMS.getUserMessage(locale, "CMS_PROFILE_ID_NOT_FOUND"), request, response);
+            setError(args, CMS.getUserMessage(locale, "CMS_PROFILE_ID_NOT_FOUND",escapeJavaScriptString(profileId)), request, response);
             return;
         }
         CMS.debug("ProfileProcessServlet: profileId=" + profileId);
diff --git a/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileReviewServlet.java b/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileReviewServlet.java
index 2b3ef83..d409435 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileReviewServlet.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileReviewServlet.java
@@ -201,7 +201,7 @@ public class ProfileReviewServlet extends ProfileServlet {
         if (req == null) {
             args.set(ARG_ERROR_CODE, "1");
             args.set(ARG_ERROR_REASON, CMS.getUserMessage(locale,
-                    "CMS_REQUEST_NOT_FOUND", requestId));
+                    "CMS_REQUEST_NOT_FOUND", escapeJavaScriptString(requestId)));
             outputTemplate(request, response, args);
             return;
         }
@@ -222,7 +222,7 @@ public class ProfileReviewServlet extends ProfileServlet {
         if (profile == null) {
             args.set(ARG_ERROR_CODE, "1");
             args.set(ARG_ERROR_REASON, CMS.getUserMessage(locale,
-                    "CMS_PROFILE_NOT_FOUND", profileId));
+                    "CMS_PROFILE_NOT_FOUND",escapeJavaScriptString(profileId)));
             outputTemplate(request, response, args);
             return;
         }
@@ -287,7 +287,7 @@ public class ProfileReviewServlet extends ProfileServlet {
             args.set(ARG_REQUEST_NOTES, "");
         } else {
             args.set(ARG_REQUEST_NOTES,
-                    req.getExtDataInString("requestNotes"));
+                    escapeJavaScriptString(req.getExtDataInString("requestNotes")));
         }
 
         args.set(ARG_RECORD, list);
diff --git a/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSelectServlet.java b/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSelectServlet.java
index 10013c8..d7c1054 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSelectServlet.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSelectServlet.java
@@ -183,7 +183,7 @@ public class ProfileSelectServlet extends ProfileServlet {
         if (profile == null) {
             args.set(ARG_ERROR_CODE, "1");
             args.set(ARG_ERROR_REASON, CMS.getUserMessage(locale,
-                    "CMS_PROFILE_NOT_FOUND", profileId));
+                    "CMS_PROFILE_NOT_FOUND", escapeJavaScriptString(profileId)));
             outputTemplate(request, response, args);
             return;
         }
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 1ee527c..97613f1 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
@@ -331,7 +331,7 @@ public class ProfileSubmitCMCServlet extends ProfileServlet {
             seq.addElement(new INTEGER(0));
             UTF8String s = null;
             try {
-                s = new UTF8String(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND", profileId));
+                s = new UTF8String(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND",escapeJavaScriptString(profileId)));
             } catch (Exception ee) {
             }
             template.createFullResponseWithFailedStatus(response, seq,
@@ -347,7 +347,7 @@ public class ProfileSubmitCMCServlet extends ProfileServlet {
             seq.addElement(new INTEGER(0));
             UTF8String s = null;
             try {
-                s = new UTF8String(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND", profileId));
+                s = new UTF8String(CMS.getUserMessage(locale, "CMS_PROFILE_NOT_FOUND",escapeJavaScriptString(profileId)));
             } catch (Exception ee) {
             }
             template.createFullResponseWithFailedStatus(response, seq,
diff --git a/base/server/cms/src/com/netscape/cms/servlet/request/CheckRequest.java b/base/server/cms/src/com/netscape/cms/servlet/request/CheckRequest.java
index 246cefd..cba79c3 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/request/CheckRequest.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/request/CheckRequest.java
@@ -279,9 +279,9 @@ public class CheckRequest extends CMSServlet {
         try {
             new BigInteger(requestId);
         } catch (NumberFormatException e) {
-            log(ILogger.LL_FAILURE, CMS.getLogMessage("BASE_INVALID_NUMBER_FORMAT_1", requestId));
+            log(ILogger.LL_FAILURE, CMS.getLogMessage("BASE_INVALID_NUMBER_FORMAT_1",  requestId));
             throw new EBaseException(
-                    CMS.getUserMessage(getLocale(req), "CMS_BASE_INVALID_NUMBER_FORMAT_1", requestId));
+                    CMS.getUserMessage(getLocale(req), "CMS_BASE_INVALID_NUMBER_FORMAT_1",CMSTemplate.escapeJavaScriptStringHTML( requestId)));
         }
 
         IRequest r = mQueue.findRequest(new RequestId(requestId));
@@ -321,7 +321,7 @@ public class CheckRequest extends CMSServlet {
         header.addLongValue(CREATE_ON, r.getCreationTime().getTime() / 1000);
         header.addLongValue(UPDATE_ON, r.getModificationTime().getTime() / 1000);
         if (note != null && note.length() > 0)
-            header.addStringValue("requestNotes", note);
+            header.addStringValue("requestNotes",CMSTemplate.escapeJavaScriptStringHTML(note));
 
         String type = r.getRequestType();
         Integer result = r.getExtDataInInteger(IRequest.RESULT);
-- 
2.1.0

