Преглед на файлове

Merge remote-tracking branch 'origin/master' into ng-helpdesk

jalbr74 преди 7 години
родител
ревизия
3d8d7dab36

+ 4 - 0
server/src/main/java/password/pwm/AppProperty.java

@@ -38,6 +38,10 @@ public enum AppProperty
     AUDIT_EVENTS_EMAILFROM                          ( "audit.events.emailFrom" ),
     AUDIT_EVENTS_EMAILSUBJECT                       ( "audit.events.emailSubject" ),
     AUDIT_EVENTS_LOCALDB_MAX_BULK_REMOVALS          ( "audit.events.localdb.maxBulkRemovals" ),
+    AUDIT_SYSLOG_CEF_EXTENSIONS                     ( "audit.syslog.cef.extensions" ),
+    AUDIT_SYSLOG_CEF_HEADER_PRODUCT                 ( "audit.syslog.cef.header.product" ),
+    AUDIT_SYSLOG_CEF_HEADER_SEVERITY                ( "audit.syslog.cef.header.severity" ),
+    AUDIT_SYSLOG_CEF_HEADER_VENDOR                  ( "audit.syslog.cef.header.vendor" ),
     AUDIT_SYSLOG_MAX_MESSAGE_LENGTH                 ( "audit.syslog.message.length" ),
     AUDIT_SYSLOG_TRUNCATE_MESSAGE                   ( "audit.syslog.message.truncateMsg" ),
     BACKUP_LOCATION                                 ( "backup.path" ),

+ 133 - 56
server/src/main/java/password/pwm/svc/event/SyslogAuditService.java

@@ -40,25 +40,30 @@ import org.graylog2.syslog4j.impl.net.udp.UDPNetSyslogConfig;
 import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
+import password.pwm.bean.SessionLabel;
 import password.pwm.config.Configuration;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.option.SyslogOutputFormat;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmOperationalException;
+import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.health.HealthRecord;
 import password.pwm.health.HealthStatus;
 import password.pwm.health.HealthTopic;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsManager;
+import password.pwm.util.LocaleHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.localdb.LocalDBException;
 import password.pwm.util.localdb.LocalDBStoredQueue;
 import password.pwm.util.localdb.WorkQueueProcessor;
 import password.pwm.util.logging.PwmLogger;
+import password.pwm.util.macro.MacroMachine;
 import password.pwm.util.secure.X509Utils;
 
 import javax.net.SocketFactory;
@@ -69,8 +74,11 @@ import java.security.KeyManagementException;
 import java.security.NoSuchAlgorithmException;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 
 
 public class SyslogAuditService
@@ -81,6 +89,17 @@ public class SyslogAuditService
     private static final String SYSLOG_INSTANCE_NAME = "syslog-audit";
     private static final int LENGTH_OVERSIZE = 1024;
 
+    private static final Map<String, String> CEF_VALUE_ESCAPES;
+
+
+    static
+    {
+        final Map<String, String> map = new LinkedHashMap<>( );
+        map.put( "\\", "\\\\" );
+        map.put( "=", "\\=" );
+        map.put( "|", "\"" );
+        CEF_VALUE_ESCAPES = Collections.unmodifiableMap( map );
+    }
 
     private SyslogIF syslogInstance = null;
     private ErrorInformation lastError = null;
@@ -92,6 +111,7 @@ public class SyslogAuditService
     private final Configuration configuration;
     private final PwmApplication pwmApplication;
     private final SyslogOutputFormat syslogOutputFormat;
+    private final Map<String, String> syslogCefExtensions;
 
     public SyslogAuditService( final PwmApplication pwmApplication )
             throws LocalDBException
@@ -100,6 +120,7 @@ public class SyslogAuditService
         this.pwmApplication = pwmApplication;
         this.configuration = pwmApplication.getConfig();
         this.certificates = configuration.readSettingAsCertificate( PwmSetting.AUDIT_SYSLOG_CERTIFICATES );
+        this.syslogCefExtensions = figureCefSyslogExtensions( configuration );
 
         final List<String> syslogConfigStringArray = configuration.readSettingAsStringArray( PwmSetting.AUDIT_SYSLOG_SERVERS );
         try
@@ -196,21 +217,31 @@ public class SyslogAuditService
 
         final String syslogMsg;
 
-        switch ( syslogOutputFormat )
+        try
         {
-            case JSON:
-                syslogMsg = convertAuditRecordToSyslogMessage( event, configuration );
-                break;
+            switch ( syslogOutputFormat )
+            {
+                case JSON:
+                    syslogMsg = convertAuditRecordToSyslogMessage( event, configuration );
+                    break;
 
-            case CEF:
-                syslogMsg = convertAuditRecordToCEFMessage( event, configuration );
-                break;
+                case CEF:
+                    syslogMsg = convertAuditRecordToCEFMessage( event, configuration );
+                    break;
 
-            default:
-                JavaHelper.unhandledSwitchStatement( syslogOutputFormat );
-                throw new IllegalStateException();
+                default:
+                    JavaHelper.unhandledSwitchStatement( syslogOutputFormat );
+                    throw new IllegalStateException();
+            }
+        }
+        catch ( PwmUnrecoverableException e )
+        {
+            final String msg = "error generating syslog message text: " + e.getMessage();
+            final ErrorInformation errorInfo = new ErrorInformation( PwmError.ERROR_SYSLOG_WRITE_ERROR, msg );
+            throw new PwmOperationalException( errorInfo );
         }
 
+
         try
         {
             workQueueProcessor.submit( syslogMsg );
@@ -331,58 +362,93 @@ public class SyslogAuditService
         return message.toString();
     }
 
-    private static String convertAuditRecordToCEFMessage( final AuditRecord auditRecord, final Configuration configuration )
+    private String convertAuditRecordToCEFMessage(
+            final AuditRecord auditRecord,
+            final Configuration configuration
+    )
+            throws PwmUnrecoverableException
     {
+        final Map<String, Object> auditRecordMap = JsonUtil.deserializeMap( JsonUtil.serialize( auditRecord ) );
+        final String separator = "|";
 
-        final String recordType = auditRecord.getType().name();
-        String recordString = "";
-        String translatedString = "";
-        if ( "USER".equalsIgnoreCase( recordType ) )
-        {
-            final UserAuditRecord cefRecord = new UserAuditRecord( auditRecord.timestamp, auditRecord.eventCode, null, null, null,
-                    auditRecord.message, null, null );
-            recordString = JsonUtil.serialize( cefRecord );
-        }
-        else if ( "SYSTEM".equalsIgnoreCase( recordType ) )
-        {
-            final SystemAuditRecord cefRecord = new SystemAuditRecord( auditRecord.eventCode, auditRecord.message, null );
-            recordString = JsonUtil.serialize( cefRecord );
-        }
-        else if ( "HELPDESK".equalsIgnoreCase( recordType ) )
+        final String headerSeverity = configuration.readAppProperty( AppProperty.AUDIT_SYSLOG_CEF_HEADER_SEVERITY );
+        final String headerProduct = configuration.readAppProperty( AppProperty.AUDIT_SYSLOG_CEF_HEADER_PRODUCT );
+        final String headerVendor = configuration.readAppProperty( AppProperty.AUDIT_SYSLOG_CEF_HEADER_VENDOR );
+
+
+        final MacroMachine macroMachine = MacroMachine.forNonUserSpecific( pwmApplication, SessionLabel.SYSTEM_LABEL );
+
+        final String auditFieldName = LocaleHelper.getLocalizedMessage(
+                PwmConstants.DEFAULT_LOCALE,
+                auditRecord.getEventCode().getMessage(),
+                configuration
+        );
+
+        final StringBuilder cefOutput = new StringBuilder(  );
+
+        // cef declaration:version prefix
+        cefOutput.append( "CEF:0" );
+
+        // Device Vendor
+        cefOutput.append( separator );
+        cefOutput.append( macroMachine.expandMacros( headerVendor ) );
+
+        // Device Product
+        cefOutput.append( separator );
+        cefOutput.append( macroMachine.expandMacros( headerProduct ) );
+
+        // Device Version
+        cefOutput.append( separator );
+        cefOutput.append( PwmConstants.SERVLET_VERSION );
+
+        // Device Event Class ID
+        cefOutput.append( separator );
+        cefOutput.append( auditRecord.getEventCode() );
+
+        // field name
+        cefOutput.append( separator );
+        cefOutput.append( auditFieldName );
+
+        // severity
+        cefOutput.append( separator );
+        cefOutput.append( macroMachine.expandMacros( headerSeverity ) );
+
+        boolean extensionAdded = false;
+        for ( final Map.Entry<String, String> entry : syslogCefExtensions.entrySet() )
         {
-            final HelpdeskAuditRecord cefRecord = new HelpdeskAuditRecord( auditRecord.timestamp, auditRecord.eventCode, null, null, null,
-                    auditRecord.message, null, null, null, null, null );
-            recordString = JsonUtil.serialize( cefRecord );
+            final Object value = auditRecordMap.get( entry.getKey() );
+            if ( value != null )
+            {
+                if ( !extensionAdded )
+                {
+                    cefOutput.append( separator );
+                    extensionAdded = true;
+                }
+                else
+                {
+                    cefOutput.append( " " );
+                }
+                cefOutput.append( entry.getValue() );
+                cefOutput.append( "=" );
+                cefOutput.append( escapeCEFValue( value.toString() ) );
+            }
         }
-        else
+
+        return cefOutput.toString();
+    }
+
+
+
+    private static String escapeCEFValue( final String value )
+    {
+        String replacedValue = value;
+        for ( final Map.Entry<String, String> entry : CEF_VALUE_ESCAPES.entrySet() )
         {
-            recordString = JsonUtil.serialize( auditRecord );
+            final String pattern = entry.getKey();
+            final String replacement = entry.getValue();
+            replacedValue = replacedValue.replace( pattern, replacement );
         }
-        recordString = recordString.replace( "\"", "" );
-        recordString = recordString.replace( "\\", "" );
-        recordString = recordString.replace( "{", "" );
-        recordString = recordString.replace( "}", "" );
-
-        recordString = recordString.replace( "type:", " cat | " );
-        recordString = recordString.replace( "eventCode:", " act | " );
-        recordString = recordString.replace( "timestamp:", " rt | " );
-        recordString = recordString.replace( "message:", " msg | " );
-        recordString = recordString.replace( "narrative:", " reason | " );
-        recordString = recordString.replace( "perpetratorID:", " suid | " );
-        recordString = recordString.replace( "perpetratorDN:", " suser | " );
-        recordString = recordString.replace( "sourceAddress:", " dvc | " );
-        recordString = recordString.replace( "sourceHost:", " dvchost | " );
-        recordString = recordString.replace( "targetID:", " duid | " );
-        recordString = recordString.replace( "targetDN:", " duser | " );
-        recordString = recordString.replace( "SSPR:", " sproc | " );
-        recordString = recordString.replace( "PWM:", " sproc | " );
-
-        translatedString = auditRecord.getTimestamp().toString();
-        translatedString = translatedString.concat( " host CEF:0 | security | threatmanager | 1.0 | 100 " );
-        recordString = recordString.replace( ",", " " );
-
-        translatedString = translatedString.concat( recordString );
-        return ( translatedString );
+        return replacedValue;
     }
 
     @Getter
@@ -489,4 +555,15 @@ public class SyslogAuditService
             return newClass;
         }
     }
+
+    private Map<String, String> figureCefSyslogExtensions( final Configuration config )
+    {
+        final String pairSplitter = ":";
+        final String keyValueSplitter = ",";
+        final String configuredString = config.readAppProperty( AppProperty.AUDIT_SYSLOG_CEF_EXTENSIONS );
+
+        final List<String> pairs = Arrays.asList( configuredString.split( pairSplitter ) );
+        final Map<String, String> map = StringUtil.convertStringListToNameValuePair( pairs, keyValueSplitter );
+        return Collections.unmodifiableMap( map );
+    }
 }

+ 4 - 0
server/src/main/resources/password/pwm/AppProperty.properties

@@ -29,6 +29,10 @@ application.wordlistRetryImportSeconds=600
 audit.events.emailFrom=Audit Event Notification <@DefaultEmailFromAddress@>
 audit.events.emailSubject=@PwmAppName@ - Audit Event - %EVENT%
 audit.events.localdb.maxBulkRemovals=301
+audit.syslog.cef.extensions=type,cat:eventCode,act:timestamp,rt:message,msg:narrative,reason:perpetratorID,suid:perpetratorDN,suser:sourceAddress,dvc:sourceHost,dvchost:targetID,duid:targetDN,duser
+audit.syslog.cef.header.product=@PwmAppName@
+audit.syslog.cef.header.severity=Unknown
+audit.syslog.cef.header.vendor=@PwmAppName@
 audit.syslog.message.length=900
 audit.syslog.message.truncateMsg=[truncated]
 backup.path=backup

+ 2 - 2
server/src/main/webapp/WEB-INF/jsp/fragment/displayelement-row.jsp

@@ -29,11 +29,11 @@
         <%= displayElement.getLabel() %>
     </td>
     <% if (displayElement.getType() == DisplayElement.Type.timestamp) { %>
-    <td class="timestamp">
+    <td class="timestamp" id="<%=StringUtil.escapeHtml(displayElement.getKey())%>">
         <%= StringUtil.escapeHtml(displayElement.getValue()) %>
     </td>
     <% } else { %>
-    <td>
+    <td  id="<%=StringUtil.escapeHtml(displayElement.getKey())%>">
         <%= StringUtil.escapeHtml(displayElement.getValue()) %>
     </td>
     <% } %>