diff options
Diffstat (limited to 'base/server')
4 files changed, 281 insertions, 39 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); + } } diff --git a/base/server/cmsbundle/src/LogMessages.properties b/base/server/cmsbundle/src/LogMessages.properties index 10d9ae5ca..6fbd43404 100644 --- a/base/server/cmsbundle/src/LogMessages.properties +++ b/base/server/cmsbundle/src/LogMessages.properties @@ -2131,6 +2131,7 @@ LOGGING_SIGNED_AUDIT_CERT_STATUS_CHANGE_REQUEST_PROCESSED_7=<type=CERT_STATUS_CH # e.g. "read" for an ACL statement containing "(read,write)" # LOGGING_SIGNED_AUDIT_AUTHZ_SUCCESS_4=<type=AUTHZ_SUCCESS>:[AuditEvent=AUTHZ_SUCCESS][SubjectID={0}][Outcome={1}][aclResource={2}][Op={3}] authorization success +LOGGING_SIGNED_AUDIT_AUTHZ_SUCCESS_5=<type=AUTHZ_SUCCESS>:[AuditEvent=AUTHZ_SUCCESS][SubjectID={0}][Outcome={1}][aclResource={2}][Op={3}][Info={4}] authorization success # # LOGGING_SIGNED_AUDIT_AUTHZ_FAIL # - used when authorization has failed @@ -2140,6 +2141,7 @@ LOGGING_SIGNED_AUDIT_AUTHZ_SUCCESS_4=<type=AUTHZ_SUCCESS>:[AuditEvent=AUTHZ_SUCC # e.g. "read" for an ACL statement containing "(read,write)" # LOGGING_SIGNED_AUDIT_AUTHZ_FAIL_4=<type=AUTHZ_FAIL>:[AuditEvent=AUTHZ_FAIL][SubjectID={0}][Outcome={1}][aclResource={2}][Op={3}] authorization failure +LOGGING_SIGNED_AUDIT_AUTHZ_FAIL_5=<type=AUTHZ_FAIL>:[AuditEvent=AUTHZ_FAIL][SubjectID={0}][Outcome={1}][aclResource={2}][Op={3}][Info={4}] authorization failure # # LOGGING_SIGNED_AUDIT_INTER_BOUNDARY_SUCCESS # - used when inter-CIMC_Boundary data transfer is successful diff --git a/base/server/cmscore/src/com/netscape/cmscore/authentication/CertUserDBAuthentication.java b/base/server/cmscore/src/com/netscape/cmscore/authentication/CertUserDBAuthentication.java index 573b736d4..998d7e261 100644 --- a/base/server/cmscore/src/com/netscape/cmscore/authentication/CertUserDBAuthentication.java +++ b/base/server/cmscore/src/com/netscape/cmscore/authentication/CertUserDBAuthentication.java @@ -168,6 +168,7 @@ public class CertUserDBAuthentication implements IAuthManager, ICertUserDBAuthen try { user = (User) mCULocator.locateUser(certs); } catch (EUsrGrpException e) { + CMS.debug("CertUserDBAuthentication: cannot map certificate to any user"); log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_AUTH_AGENT_AUTH_FAILED", x509Certs[0].getSerialNumber() .toString(16), x509Certs[0].getSubjectDN().toString(), e.toString())); throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); @@ -179,7 +180,7 @@ public class CertUserDBAuthentication implements IAuthManager, ICertUserDBAuthen // any unexpected error occurs like internal db down, // UGSubsystem only returns null for user. if (user == null) { - CMS.debug("Authentication: cannot map certificate to user"); + CMS.debug("CertUserDBAuthentication: cannot map certificate to any user"); log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_AUTH_AGENT_USER_NOT_FOUND")); throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); } |