// --- 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.admin;
import java.io.*;
import java.util.*;
import java.net.*;
import java.util.*;
import java.text.*;
import java.math.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.security.cert.X509Certificate;
import netscape.security.x509.X509CertImpl;
import com.netscape.certsrv.common.*;
import com.netscape.certsrv.base.*;
import com.netscape.certsrv.authentication.*;
import com.netscape.certsrv.authorization.*;
import com.netscape.certsrv.logging.*;
import com.netscape.certsrv.usrgrp.*;
import com.netscape.certsrv.apps.*;
import com.netscape.cms.servlet.base.*;
/**
* A class represents an administration servlet that
* is responsible to serve administrative
* operation such as configuration parameter updates.
*
* Since each administration servlet needs to perform
* authentication information parsing and response
* formulation, it makes sense to encapsulate the
* commonalities into this class.
*
* By extending this serlvet, the subclass does not
* need to re-implement the request parsing code
* (i.e. authentication information parsing).
*
* If a subsystem needs to expose configuration
* parameters management, it should create an
* administration servlet (i.e. CAAdminServlet)
* and register it to RemoteAdmin subsystem.
*
*
* public class CAAdminServlet extends AdminServlet {
* ...
* }
*
*
* @version $Revision$, $Date$
*/
public class AdminServlet extends HttpServlet {
private final static String HDR_AUTHORIZATION = "Authorization";
private final static String HDR_LANG = "accept-language";
private final static String HDR_CONTENT_LEN = "Content-Length";
protected ILogger mLogger = CMS.getLogger();
protected ILogger mSignedAuditLogger = CMS.getSignedAuditLogger();
private IUGSubsystem mUG = null;
protected IConfigStore mConfig = null;
protected IAuthzSubsystem mAuthz = null;
// we don't allow to switch authz db mid-way, for now
protected String mAclMethod = null;
protected String mOp = "";
protected static String AUTHZ_RES_NAME = "certServer";
protected AuthzToken mToken;
private String mServletID = null;
public final static String PROP_AUTHZ_MGR = "AuthzMgr";
public final static String PROP_ACL = "ACLinfo";
public final static String AUTHZ_MGR_BASIC = "BasicAclAuthz";
public final static String AUTHZ_MGR_LDAP = "DirAclAuthz";
public final static String PROP_ID = "ID";
public final static String AUTHZ_CONFIG_STORE = "authz";
public final static String AUTHZ_SRC_TYPE = "sourceType";
public final static String AUTHZ_SRC_LDAP = "ldap";
public final static String AUTHZ_SRC_XML = "web.xml";
public static final String CERT_ATTR =
"javax.servlet.request.X509Certificate";
public final static String SIGNED_AUDIT_SCOPE = "Scope";
public final static String SIGNED_AUDIT_OPERATION = "Operation";
public final static String SIGNED_AUDIT_RESOURCE = "Resource";
public final static String SIGNED_AUDIT_RULENAME = "RULENAME";
public final static String SIGNED_AUDIT_PASSWORD_VALUE = "********";
public final static String SIGNED_AUDIT_EMPTY_NAME_VALUE_PAIR = "Unknown";
public final static String SIGNED_AUDIT_NAME_VALUE_DELIMITER = ";;";
public final static String SIGNED_AUDIT_NAME_VALUE_PAIRS_DELIMITER = "+";
private final static String LOGGING_SIGNED_AUDIT_AUTH_FAIL =
"LOGGING_SIGNED_AUDIT_AUTH_FAIL_4";
private final static String LOGGING_SIGNED_AUDIT_AUTH_SUCCESS =
"LOGGING_SIGNED_AUDIT_AUTH_SUCCESS_3";
private final static String LOGGING_SIGNED_AUDIT_AUTHZ_FAIL =
"LOGGING_SIGNED_AUDIT_AUTHZ_FAIL_4";
private final static String LOGGING_SIGNED_AUDIT_AUTHZ_SUCCESS =
"LOGGING_SIGNED_AUDIT_AUTHZ_SUCCESS_4";
private final static String LOGGING_SIGNED_AUDIT_ROLE_ASSUME =
"LOGGING_SIGNED_AUDIT_ROLE_ASSUME_3";
private final static String CERTUSERDB =
IAuthSubsystem.CERTUSERDB_AUTHMGR_ID;
private final static String PASSWDUSERDB =
IAuthSubsystem.PASSWDUSERDB_AUTHMGR_ID;
/**
* Constructs generic administration servlet.
*/
public AdminServlet() {
}
/**
* Initializes the servlet.
*/
public void init(ServletConfig sc) throws ServletException {
super.init(sc);
mUG = (IUGSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_UG);
mConfig = CMS.getConfigStore();
String srcType = AUTHZ_SRC_LDAP;
try {
IConfigStore authzConfig = mConfig.getSubStore(AUTHZ_CONFIG_STORE);
srcType = authzConfig.getString(AUTHZ_SRC_TYPE, AUTHZ_SRC_LDAP);
} catch (EBaseException e) {
CMS.debug("AdminServlet: " + CMS.getLogMessage("ADMIN_SRVLT_FAIL_SRC_TYPE"));
}
mAuthz =
(IAuthzSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_AUTHZ);
mServletID = getSCparam(sc, PROP_ID, "servlet id unknown");
CMS.debug("AdminServlet: " + CMS.getLogMessage("ADMIN_SRVLT_AUTHZ_INITED", mServletID));
if (srcType.equalsIgnoreCase(AUTHZ_SRC_XML)) {
CMS.debug("AdminServlet: " + CMS.getLogMessage("ADMIN_SRVLT_AUTHZ_INITED", ""));
// get authz mgr from xml file; if not specified, use
// ldap by default
mAclMethod = getSCparam(sc, PROP_AUTHZ_MGR, AUTHZ_MGR_LDAP);
if (mAclMethod.equalsIgnoreCase(AUTHZ_MGR_BASIC)) {
String aclInfo = sc.getInitParameter(PROP_ACL);
if (aclInfo != null) {
try {
addACLInfo(aclInfo);
//mAuthz.authzMgrAccessInit(mAclMethod, aclInfo);
} catch (EBaseException e) {
log(ILogger.LL_FAILURE, CMS.getLogMessage("ADMIN_SRVLT_AUTHZ_MGR_INIT_FAIL"));
throw new ServletException("failed to init authz info from xml config file");
}
CMS.debug("AdminServlet: " + CMS.getLogMessage("ADMIN_SRVLT_AUTHZ_MGR_INIT_DONE", mServletID));
} else { // PROP_AUTHZ_MGR not specified, use default authzmgr
CMS.debug("AdminServlet: " + CMS.getLogMessage("ADMIN_SRVLT_PROP_ACL_NOT_SPEC", PROP_ACL, mServletID, AUTHZ_MGR_LDAP));
}
} else { // PROP_AUTHZ_MGR not specified, use default authzmgr
CMS.debug("AdminServlet: " + CMS.getLogMessage("ADMIN_SRVLT_PROP_ACL_NOT_SPEC", PROP_AUTHZ_MGR, mServletID, AUTHZ_MGR_LDAP));
}
} else {
mAclMethod = AUTHZ_MGR_LDAP;
CMS.debug("AdminServlet: " + CMS.getLogMessage("ADMIN_SRVLT_AUTH_LDAP_NOT_XML", mServletID));
}
}
public void outputHttpParameters(HttpServletRequest httpReq)
{
CMS.debug("AdminServlet:service() uri = " + httpReq.getRequestURI());
Enumeration paramNames = httpReq.getParameterNames();
while (paramNames.hasMoreElements()) {
String pn = (String)paramNames.nextElement();
// added this facility so that password can be hidden,
// all sensitive parameters should be prefixed with
// __ (double underscores); however, in the event that
// a security parameter slips through, we perform multiple
// additional checks to insure that it is NOT displayed
if( pn.startsWith("__") ||
pn.endsWith("password") ||
pn.endsWith("passwd") ||
pn.endsWith("pwd") ||
pn.equalsIgnoreCase("admin_password_again") ||
pn.equalsIgnoreCase("directoryManagerPwd") ||
pn.equalsIgnoreCase("bindpassword") ||
pn.equalsIgnoreCase("bindpwd") ||
pn.equalsIgnoreCase("passwd") ||
pn.equalsIgnoreCase("password") ||
pn.equalsIgnoreCase("pin") ||
pn.equalsIgnoreCase("pwd") ||
pn.equalsIgnoreCase("pwdagain") ||
pn.equalsIgnoreCase("uPasswd") ) {
CMS.debug("AdminServlet::service() param name='" + pn +
"' value='(sensitive)'" );
} else {
CMS.debug("AdminServlet::service() param name='" + pn +
"' value='" + httpReq.getParameter(pn) + "'" );
}
}
}
/**
* Serves HTTP admin request.
*/
public void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
boolean running_state = CMS.isInRunningState();
if (!running_state)
throw new IOException(
"CMS server is not ready to serve.");
if (CMS.debugOn()) {
outputHttpParameters(req);
}
}
private void addACLInfo(String info) throws EBaseException {
StringTokenizer tokenizer = new StringTokenizer(info, "#");
while (tokenizer.hasMoreTokens()) {
String acl = (String) tokenizer.nextToken();
mAuthz.authzMgrAccessInit(mAclMethod, acl);
}
}
private String getSCparam(ServletConfig sc, String param, String defVal) {
String val = sc.getInitParameter(param);
if (val == null)
return defVal;
else
return val;
}
/**
* Authenticates to the identity scope with the given
* userid and password via identity manager.
*
* *
* *
* * @param createBackup true if a backup file should be created */ protected void commit(boolean createBackup) throws EBaseException { mConfig.commit(createBackup); } private void log(int level, String msg) { if (mLogger == null) return; mLogger.log(ILogger.EV_SYSTEM, null, ILogger.S_ADMIN, level, "AdminServlet: " + msg); } /** * Signed Audit Log * * This method is inherited by all extended admin servlets * and is called to store messages to the signed audit log. *
* * @param msg signed audit log message */ protected 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); } /** * Signed Audit Log Subject ID * * This method is inherited by all extended "CMSServlet"s, * and is called to obtain the "SubjectID" for * a signed audit log message. *
* * @return id string containing the signed audit log message SubjectID */ protected 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; } /** * Signed Audit Parameters * * This method is inherited by all extended admin servlets and * is called to extract parameters from the HttpServletRequest * and return a string of name;;value pairs separated by a '+' * if more than one name;;value pair exists. *
* * @param req HTTP servlet request * @return a delimited string of one or more delimited name/value pairs */ protected String auditParams(HttpServletRequest req) { // if no signed audit object exists, bail if (mSignedAuditLogger == null) { return null; } String parameters = SIGNED_AUDIT_EMPTY_NAME_VALUE_PAIR; String value = null; // always identify the scope of the request if (req.getParameter(Constants.OP_SCOPE) != null) { parameters = SIGNED_AUDIT_SCOPE + SIGNED_AUDIT_NAME_VALUE_DELIMITER + req.getParameter(Constants.OP_SCOPE); } // identify the operation type of the request if (req.getParameter(Constants.OP_TYPE) != null) { parameters += SIGNED_AUDIT_NAME_VALUE_PAIRS_DELIMITER; parameters += SIGNED_AUDIT_OPERATION + SIGNED_AUDIT_NAME_VALUE_DELIMITER + req.getParameter(Constants.OP_TYPE); } // identify the resource type of the request if (req.getParameter(Constants.RS_ID) != null) { parameters += SIGNED_AUDIT_NAME_VALUE_PAIRS_DELIMITER; parameters += SIGNED_AUDIT_RESOURCE + SIGNED_AUDIT_NAME_VALUE_DELIMITER + req.getParameter(Constants.RS_ID); } // identify any remaining request parameters Enumeration e = req.getParameterNames(); while (e.hasMoreElements()) { String name = (String) e.nextElement(); // skip previously extracted parameters if (name.equals(Constants.OP_SCOPE)) { continue; } if (name.equals(Constants.OP_TYPE)) { continue; } if (name.equals(Constants.RS_ID)) { continue; } // skip "RULENAME" parameter if (name.equals(SIGNED_AUDIT_RULENAME)) { continue; } parameters += SIGNED_AUDIT_NAME_VALUE_PAIRS_DELIMITER; value = req.getParameter(name); if (value != null) { value = value.trim(); if (value.equals("")) { parameters += name + SIGNED_AUDIT_NAME_VALUE_DELIMITER + ILogger.SIGNED_AUDIT_EMPTY_VALUE; } else { // // To fix Blackflag Bug # 613800: // // Check "com.netscape.certsrv.common.Constants" for // case-insensitive "password", "pwd", and "passwd" // name fields, and hide any password values: // /* "password" */ if( name.equals( Constants.PASSWORDTYPE ) || name.equals( Constants.TYPE_PASSWORD ) || name.equals( Constants.PR_USER_PASSWORD ) || name.equals( Constants.PT_OLD_PASSWORD ) || name.equals( Constants.PT_NEW_PASSWORD ) || name.equals( Constants.PT_DIST_STORE ) || name.equals( Constants.PT_DIST_EMAIL ) || /* "pwd" */ name.equals( Constants.PR_AUTH_ADMIN_PWD ) || // ignore this one name.equals( Constants.PR_BINDPWD_PROMPT ) || name.equals( Constants.PR_DIRECTORY_MANAGER_PWD ) || name.equals( Constants.PR_OLD_AGENT_PWD ) || name.equals( Constants.PR_AGENT_PWD ) || name.equals( Constants.PT_PUBLISH_PWD ) || /* "passwd" */ name.equals( Constants.PR_BIND_PASSWD ) || name.equals( Constants.PR_BIND_PASSWD_AGAIN ) || name.equals( Constants.PR_TOKEN_PASSWD ) ) { // hide password value parameters += name + SIGNED_AUDIT_NAME_VALUE_DELIMITER + SIGNED_AUDIT_PASSWORD_VALUE; } else { // process normally parameters += name + SIGNED_AUDIT_NAME_VALUE_DELIMITER + value; } } } else { parameters += name + SIGNED_AUDIT_NAME_VALUE_DELIMITER + ILogger.SIGNED_AUDIT_EMPTY_VALUE; } } return parameters; } /** * Signed Audit Groups * * This method is called to extract all "groups" associated * with the "auditSubjectID()". *
* * @param SubjectID string containing the signed audit log message SubjectID * @return a delimited string of groups associated * with the "auditSubjectID()" */ private String auditGroups(String SubjectID) { // if no signed audit object exists, bail if (mSignedAuditLogger == null) { return null; } if ((SubjectID == null) || (SubjectID.equals(ILogger.UNIDENTIFIED))) { return ILogger.SIGNED_AUDIT_EMPTY_VALUE; } Enumeration groups = null; try { groups = mUG.findGroups("*"); } catch (Exception e) { return ILogger.SIGNED_AUDIT_EMPTY_VALUE; } StringBuffer membersString = new StringBuffer(); while (groups.hasMoreElements()) { IGroup group = (IGroup) groups.nextElement(); if (group.isMember(SubjectID) == true) { if (membersString.length()!=0) { membersString.append(", "); } membersString.append(group.getGroupID()); } } if (membersString.length()!= 0) { return membersString.toString(); } else { return ILogger.SIGNED_AUDIT_EMPTY_VALUE; } } protected NameValuePairs convertStringArrayToNVPairs(String[] s) { if (s == null) return null; NameValuePairs nvps = new NameValuePairs(); int i; for (i = 0; i < s.length; i++) { int j = s[i].indexOf(";"); String paramName = s[i].substring(0, j); String args = s[i].substring(j + 1); nvps.add(paramName, args); } return nvps; } protected static IExtendedPluginInfo getClassByNameAsExtendedPluginInfo(String className) { IExtendedPluginInfo epi = null; try { // here is the new dummy obj created Object o = Class.forName(className).newInstance(); epi = (IExtendedPluginInfo) o; } catch (Exception e) { } return epi; } }