From ccf2eb507471a9f19a1768befadeff404c96635e Mon Sep 17 00:00:00 2001 From: Christina Fu Date: Thu, 7 May 2015 12:14:19 -0700 Subject: 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. --- .../cms/src/com/netscape/cms/realm/PKIRealm.java | 91 ++++++++- .../org/dogtagpki/server/rest/ACLInterceptor.java | 224 +++++++++++++++++---- 2 files changed, 277 insertions(+), 38 deletions(-) (limited to 'base/server/cms/src') 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 + * + * @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. + *

+ * + * @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); + } } -- cgit