summaryrefslogtreecommitdiffstats
path: root/base/server/cms/src
diff options
context:
space:
mode:
authorChristina Fu <cfu@redhat.com>2015-05-07 12:14:19 -0700
committerChristina Fu <cfu@redhat.com>2015-05-13 09:05:38 -0700
commitccf2eb507471a9f19a1768befadeff404c96635e (patch)
tree98a40027631ce6c577558c563906f9a28ac49c25 /base/server/cms/src
parenta21f3139a3fa2cecf7a0f782e2a40b83279a80fa (diff)
downloadpki-ccf2eb507471a9f19a1768befadeff404c96635e.tar.gz
pki-ccf2eb507471a9f19a1768befadeff404c96635e.tar.xz
pki-ccf2eb507471a9f19a1768befadeff404c96635e.zip
Ticket 1160 audit logging needed: REST API auth/authz; kra for getKeyInfo
- (1) REST API auth/authz - this patch addresses the first part of this ticket where auditing is completely missing for authentication and authorization at the REST interface.
Diffstat (limited to 'base/server/cms/src')
-rw-r--r--base/server/cms/src/com/netscape/cms/realm/PKIRealm.java91
-rw-r--r--base/server/cms/src/org/dogtagpki/server/rest/ACLInterceptor.java224
2 files changed, 277 insertions, 38 deletions
diff --git a/base/server/cms/src/com/netscape/cms/realm/PKIRealm.java b/base/server/cms/src/com/netscape/cms/realm/PKIRealm.java
index bd64de148..73fae47fd 100644
--- a/base/server/cms/src/com/netscape/cms/realm/PKIRealm.java
+++ b/base/server/cms/src/com/netscape/cms/realm/PKIRealm.java
@@ -9,6 +9,7 @@ import java.util.List;
import netscape.security.x509.X509CertImpl;
import org.apache.catalina.realm.RealmBase;
+import org.apache.commons.lang.StringUtils;
import com.netscape.certsrv.apps.CMS;
import com.netscape.certsrv.authentication.IAuthManager;
@@ -16,6 +17,8 @@ import com.netscape.certsrv.authentication.IAuthSubsystem;
import com.netscape.certsrv.authentication.IAuthToken;
import com.netscape.certsrv.authentication.ICertUserDBAuthentication;
import com.netscape.certsrv.authentication.IPasswdUserDBAuthentication;
+import com.netscape.certsrv.base.SessionContext;
+import com.netscape.certsrv.logging.ILogger;
import com.netscape.certsrv.usrgrp.EUsrGrpException;
import com.netscape.certsrv.usrgrp.IGroup;
import com.netscape.certsrv.usrgrp.IUGSubsystem;
@@ -31,6 +34,11 @@ import com.netscape.cms.servlet.common.AuthCredentials;
*/
public class PKIRealm extends RealmBase {
+ protected ILogger signedAuditLogger = CMS.getSignedAuditLogger();
+ 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";
@Override
protected String getName() {
@@ -40,6 +48,9 @@ public class PKIRealm extends RealmBase {
@Override
public Principal authenticate(String username, String password) {
logDebug("Authenticating username "+username+" with password.");
+ String auditMessage = null;
+ String auditSubjectID = ILogger.UNIDENTIFIED;
+ String attemptedAuditUID = username;
try {
IAuthSubsystem authSub = (IAuthSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_AUTH);
@@ -50,10 +61,28 @@ public class PKIRealm extends RealmBase {
creds.set(IPasswdUserDBAuthentication.CRED_PWD, password);
IAuthToken authToken = authMgr.authenticate(creds); // throws exception if authentication fails
+ authToken.set(SessionContext.AUTH_MANAGER_ID,IAuthSubsystem.PASSWDUSERDB_AUTHMGR_ID);
+ auditSubjectID = authToken.getInString(IAuthToken.USER_ID);
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_AUTH_SUCCESS,
+ auditSubjectID,
+ ILogger.SUCCESS,
+ IAuthSubsystem.PASSWDUSERDB_AUTHMGR_ID);
+
+ audit(auditMessage);
return getPrincipal(username, authToken);
} catch (Throwable e) {
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_AUTH_FAIL,
+ auditSubjectID,
+ ILogger.FAILURE,
+ IAuthSubsystem.PASSWDUSERDB_AUTHMGR_ID,
+ attemptedAuditUID);
+ audit(auditMessage);
e.printStackTrace();
}
@@ -64,6 +93,14 @@ public class PKIRealm extends RealmBase {
public Principal authenticate(final X509Certificate certs[]) {
logDebug("Authenticating certificate chain:");
+ String auditMessage = null;
+ // get the cert from the ssl client auth
+ // in cert based auth, subject id from cert has already passed SSL authentication
+ // what remains is to see if the user exists in the internal user db
+ // therefore both auditSubjectID and attemptedAuditUID are the same
+ String auditSubjectID = getAuditUserfromCert(certs[0]);
+ String attemptedAuditUID = auditSubjectID;
+
try {
X509CertImpl certImpls[] = new X509CertImpl[certs.length];
for (int i=0; i<certs.length; i++) {
@@ -73,7 +110,6 @@ public class PKIRealm extends RealmBase {
// Convert sun.security.x509.X509CertImpl to netscape.security.x509.X509CertImpl
certImpls[i] = new X509CertImpl(cert.getEncoded());
}
-
IAuthSubsystem authSub = (IAuthSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_AUTH);
IAuthManager authMgr = authSub.getAuthManager(IAuthSubsystem.CERTUSERDB_AUTHMGR_ID);
@@ -81,19 +117,45 @@ public class PKIRealm extends RealmBase {
creds.set(ICertUserDBAuthentication.CRED_CERT, certImpls);
IAuthToken authToken = authMgr.authenticate(creds); // throws exception if authentication fails
+ authToken.set(SessionContext.AUTH_MANAGER_ID,IAuthSubsystem.CERTUSERDB_AUTHMGR_ID);
String username = authToken.getInString(ICertUserDBAuthentication.TOKEN_USERID);
- logDebug("User ID: "+username);
+ // reset it to the one authenticated with authManager
+ auditSubjectID = authToken.getInString(IAuthToken.USER_ID);
+ logDebug("User ID: "+username);
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_AUTH_SUCCESS,
+ auditSubjectID,
+ ILogger.SUCCESS,
+ IAuthSubsystem.CERTUSERDB_AUTHMGR_ID);
+
+ audit(auditMessage);
return getPrincipal(username, authToken);
} catch (Throwable e) {
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_AUTH_FAIL,
+ auditSubjectID,
+ ILogger.FAILURE,
+ IAuthSubsystem.CERTUSERDB_AUTHMGR_ID,
+ attemptedAuditUID);
+ audit(auditMessage);
e.printStackTrace();
}
return null;
}
+ private String getAuditUserfromCert(X509Certificate clientCert) {
+ String certUID = clientCert.getSubjectDN().getName();
+ CMS.debug("PKIRealm.getAuditUserfromCert: certUID=" + certUID);
+
+ return StringUtils.stripToNull(certUID);
+ }
+
@Override
protected Principal getPrincipal(String username) {
return getPrincipal(username, (IAuthToken)null);
@@ -152,9 +214,34 @@ public class PKIRealm extends RealmBase {
*/
public void logErr(String msg) {
System.err.println(msg);
+ CMS.debug("PKIRealm.logErr: " + msg);
}
public void logDebug(String msg) {
System.out.println("PKIRealm: "+msg);
+ CMS.debug("PKIRealm.logDebug: " + msg);
+ }
+
+ /**
+ * Signed Audit Log
+ *
+ * This method is called to store messages to the signed audit log.
+ * <P>
+ *
+ * @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 (signedAuditLogger == null) {
+ return;
+ }
+
+ signedAuditLogger.log(ILogger.EV_SIGNED_AUDIT,
+ null,
+ ILogger.S_SIGNED_AUDIT,
+ ILogger.LL_SECURITY,
+ msg);
}
}
diff --git a/base/server/cms/src/org/dogtagpki/server/rest/ACLInterceptor.java b/base/server/cms/src/org/dogtagpki/server/rest/ACLInterceptor.java
index 7ea5d74aa..490011681 100644
--- a/base/server/cms/src/org/dogtagpki/server/rest/ACLInterceptor.java
+++ b/base/server/cms/src/org/dogtagpki/server/rest/ACLInterceptor.java
@@ -42,6 +42,7 @@ import com.netscape.certsrv.authorization.EAuthzAccessDenied;
import com.netscape.certsrv.authorization.IAuthzSubsystem;
import com.netscape.certsrv.base.EBaseException;
import com.netscape.certsrv.base.ForbiddenException;
+import com.netscape.certsrv.logging.ILogger;
import com.netscape.cms.realm.PKIPrincipal;
/**
@@ -49,6 +50,17 @@ import com.netscape.cms.realm.PKIPrincipal;
*/
@Provider
public class ACLInterceptor implements ContainerRequestFilter {
+ protected ILogger signedAuditLogger = CMS.getSignedAuditLogger();
+ private final static String LOGGING_SIGNED_AUDIT_AUTHZ_FAIL =
+ "LOGGING_SIGNED_AUDIT_AUTHZ_FAIL_5";
+ private final static String LOGGING_SIGNED_AUDIT_AUTHZ_SUCCESS =
+ "LOGGING_SIGNED_AUDIT_AUTHZ_SUCCESS_5";
+
+ private final static String LOGGING_ACL_PARSING_ERROR = "internal error: ACL parsing error";
+ private final static String LOGGING_NO_ACL_ACCESS_ALLOWED = "no ACL configured; OK";
+ private final static String LOGGING_MISSING_AUTH_TOKEN = "auth token not found";
+ private final static String LOGGING_MISSING_ACL_MAPPING = "ACL mapping not found; OK";
+ private final static String LOGGING_INVALID_ACL_MAPPING = "internal error: invalid ACL mapping";
Properties properties;
@@ -93,71 +105,149 @@ public class ACLInterceptor implements ContainerRequestFilter {
.getProperty("org.jboss.resteasy.core.ResourceMethodInvoker");
Method method = methodInvoker.getMethod();
Class<?> clazz = methodInvoker.getResourceClass();
-
- CMS.debug("ACLInterceptor: " + clazz.getSimpleName() + "." + method.getName() + "()");
-
+ String auditInfo = clazz.getSimpleName() + "." + method.getName();
+
+ CMS.debug("ACLInterceptor: " + auditInfo + "()");
+ String auditMessage = null;
+ String auditSubjectID = ILogger.UNIDENTIFIED;
+
+ /*
+ * when aclMapping is null, it's either of the following :
+ * - only authentication needed
+ * - allows anonymous, i.e. no authentication or authorization needed
+ * use authzRequired to track when aclMapping is not null for ease of following the code
+ */
+ boolean authzRequired = true;
ACLMapping aclMapping = method.getAnnotation(ACLMapping.class);
-
// If not available, get ACL mapping for the class.
if (aclMapping == null) {
aclMapping = clazz.getAnnotation(ACLMapping.class);
}
-
- // If still not available, it's unprotected, allow request.
if (aclMapping == null) {
- CMS.debug("ACLInterceptor: No ACL mapping.");
- return;
+ CMS.debug("ACLInterceptor.filter: no authorization required");
+ authzRequired = false;
}
- String name = aclMapping.value();
- CMS.debug("ACLInterceptor: mapping: " + name);
-
- Principal principal = securityContext.getUserPrincipal();
+ Principal principal = null;
+ principal = securityContext.getUserPrincipal();
// If unauthenticated, reject request.
- if (principal == null) {
+ if (principal == null && authzRequired) {
CMS.debug("ACLInterceptor: No user principal provided.");
+ // audit comment: no Principal, no one to blame here
throw new ForbiddenException("No user principal provided.");
}
-
- CMS.debug("ACLInterceptor: principal: " + principal.getName());
+ if (principal != null)
+ CMS.debug("ACLInterceptor: principal: " + principal.getName());
// If unrecognized principal, reject request.
- if (!(principal instanceof PKIPrincipal)) {
+ if (principal != null && !(principal instanceof PKIPrincipal)) {
CMS.debug("ACLInterceptor: Invalid user principal.");
+ // audit comment: no Principal, no one to blame here
throw new ForbiddenException("Invalid user principal.");
}
- PKIPrincipal pkiPrincipal = (PKIPrincipal) principal;
- IAuthToken authToken = pkiPrincipal.getAuthToken();
+ PKIPrincipal pkiPrincipal = null;
+ IAuthToken authToken = null;
+ if (principal != null) {
+ pkiPrincipal = (PKIPrincipal) principal;
+ authToken = pkiPrincipal.getAuthToken();
+ }
// If missing auth token, reject request.
- if (authToken == null) {
- CMS.debug("ACLInterceptor: No authorization token present.");
+ if (authToken == null && authzRequired) {
+ CMS.debug("ACLInterceptor: No authentication token present.");
+ // store a message in the signed audit log file
+ // although if it didn't pass authentication, it should not have gotten here
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_AUTHZ_FAIL,
+ auditSubjectID,
+ ILogger.FAILURE,
+ null, // resource
+ null, // operation
+ LOGGING_MISSING_AUTH_TOKEN + ":" + auditInfo);
+ audit(auditMessage);
throw new ForbiddenException("No authorization token present.");
}
+ if (authToken != null)
+ auditSubjectID = authToken.getInString(IAuthToken.USER_ID);
+
+ // If still not available, it's unprotected, allow request.
+ if (!authzRequired) {
+ CMS.debug("ACLInterceptor: No ACL mapping; authz not required.");
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_AUTHZ_SUCCESS,
+ auditSubjectID,
+ ILogger.SUCCESS,
+ null, //resource
+ null, //operation
+ LOGGING_MISSING_ACL_MAPPING + ":" + auditInfo); //info
+ audit(auditMessage);
+ return;
+ }
+
+ // we know aclMapping is not null now (!noAuthzRequired); authz game on...
+ String name = aclMapping.value();
+ CMS.debug("ACLInterceptor: mapping: " + name);
+ String values[] = null;
+ String value = null;
try {
loadProperties();
- String value = properties.getProperty(name);
+ value = properties.getProperty(name);
- // If no property defined, allow request.
- if (value == null) {
- CMS.debug("ACLInterceptor: No ACL configuration.");
- return;
- }
+ } catch (IOException e) {
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_AUTHZ_FAIL,
+ auditSubjectID,
+ ILogger.FAILURE,
+ null, //resource
+ null, //operation
+ LOGGING_ACL_PARSING_ERROR + ":" + auditInfo);
- String values[] = value.split(",");
+ audit(auditMessage);
+ e.printStackTrace();
+ throw new Failure(e);
+ }
- // If invalid mapping, reject request.
- if (values.length != 2) {
- CMS.debug("ACLInterceptor: Invalid ACL mapping.");
- throw new ForbiddenException("Invalid ACL mapping.");
- }
+ // If no property defined, allow request.
+ if (value == null) {
+ CMS.debug("ACLInterceptor: No ACL configuration.");
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_AUTHZ_SUCCESS,
+ auditSubjectID,
+ ILogger.SUCCESS,
+ null, //resource
+ null, //operation
+ LOGGING_NO_ACL_ACCESS_ALLOWED + ":" + auditInfo);
+ return;
+ }
- CMS.debug("ACLInterceptor: ACL: " + value);
+ values = value.split(",");
+
+ // If invalid mapping, reject request.
+ if (values.length != 2) {
+ CMS.debug("ACLInterceptor: Invalid ACL mapping.");
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_AUTHZ_FAIL,
+ auditSubjectID,
+ ILogger.FAILURE,
+ null, //resource
+ null, //operation
+ LOGGING_INVALID_ACL_MAPPING + ":" + auditInfo);
+
+ audit(auditMessage);
+ throw new ForbiddenException("Invalid ACL mapping.");
+ }
+ CMS.debug("ACLInterceptor: ACL: " + value);
+
+ try {
// Check authorization.
IAuthzSubsystem mAuthz = (IAuthzSubsystem) CMS.getSubsystem(CMS.SUBSYSTEM_AUTHZ);
AuthzToken authzToken = mAuthz.authorize(
@@ -168,22 +258,84 @@ public class ACLInterceptor implements ContainerRequestFilter {
// If not authorized, reject request.
if (authzToken == null) {
- CMS.debug("ACLInterceptor: No authorization token present.");
+ String info = "No authorization token present.";
+ CMS.debug("ACLInterceptor: " + info);
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_AUTHZ_FAIL,
+ auditSubjectID,
+ ILogger.FAILURE,
+ values[0], // resource
+ values[1], // operation
+ info);
+ audit(auditMessage);
throw new ForbiddenException("No authorization token present.");
}
CMS.debug("ACLInterceptor: access granted");
} catch (EAuthzAccessDenied e) {
- CMS.debug("ACLInterceptor: " + e.getMessage());
+ String info = e.getMessage();
+ CMS.debug("ACLInterceptor: " + info);
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_AUTHZ_FAIL,
+ auditSubjectID,
+ ILogger.FAILURE,
+ values[0], // resource
+ values[1], // operation
+ info);
+ audit(auditMessage);
throw new ForbiddenException(e.toString());
- } catch (IOException | EBaseException e) {
+ } catch (EBaseException e) {
+ String info = e.getMessage();
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_AUTHZ_FAIL,
+ auditSubjectID,
+ ILogger.FAILURE,
+ values[0], // resource
+ values[1], // operation
+ info);
+ audit(auditMessage);
e.printStackTrace();
throw new Failure(e);
}
// Allow request.
+ // store a message in the signed audit log file
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_AUTHZ_SUCCESS,
+ auditSubjectID,
+ ILogger.SUCCESS,
+ values[0], // resource
+ values[1], // operation
+ auditInfo);
+ audit(auditMessage);
return;
}
+
+ /**
+ * Signed Audit Log
+ *
+ * This method is called to store messages to the signed audit log.
+ * <P>
+ *
+ * @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 (signedAuditLogger == null) {
+ return;
+ }
+
+ signedAuditLogger.log(ILogger.EV_SIGNED_AUDIT,
+ null,
+ ILogger.S_SIGNED_AUDIT,
+ ILogger.LL_SECURITY,
+ msg);
+ }
}