1 /* 2 * Copyright © [2008-2009] Novell, Inc. All Rights Reserved. 3 * 4 * USE AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO THE DEVELOPER LICENSE AGREEMENT 5 * OR OTHER AGREEMENT THROUGH WHICH NOVELL, INC. MAKES THE WORK AVAILABLE. THIS WORK 6 * MAY NOT BE ADAPTED WITHOUT NOVELL'S PRIOR WRITTEN CONSENT. 7 * 8 * NOVELL PROVIDES THE WORK "AS IS," WITHOUT ANY EXPRESS OR IMPLIED WARRANTY, 9 * INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR 10 * A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. NOVELL, THE AUTHORS OF THE WORK, AND THE 11 * OWNERS OF COPYRIGHT IN THE WORK ARE NOT LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER 12 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, 13 * OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS IN THE WORK. 14 */ 15 16 /** 17 * @fileoverview 18 * This file defines the Collector class, which represents the Collector object itself in ESM. 19 * In the context of the Collector script itself, it also represents the execution environment 20 * and stores variables and parameters that control the Collector. 21 * @name Collector Class 22 */ 23 24 25 /** 26 * This class represents the Collector ESM object. 27 * The Collector class provides methods used to manipulate Collectors in ESM such as starting and stopping, 28 * and is also used to contain the execution environment for a running Collector script. 29 * @class 30 * The Collector template creates an instance of this class (called <code>instance</code>) to contain 31 * a number of utility data objects and methods. These data objects control operation of the Collector, 32 * define the environment, and help locate files on disk. 33 * The template handles most things to do with the Collector object, but if you need to reference 34 * parameters to affect parsing, or use the Session multi-line record handling routines, you will 35 * use this class. 36 * @author Novell Engineering 37 * @version 6.1 38 * @param {String} uuid ID of the Collector you want to access. 39 * @constructor 40 */ 41 42 function Collector(uuid) { 43 44 if (typeof uuid != "undefined" && typeof ESM != "undefined") { 45 /** 46 * The UUID of this Collector. 47 * @type UUID 48 */ 49 this.UUID = uuid; 50 this.collectorObject = ESM.collectorForId(this.UUID); 51 } 52 53 if ( typeof scriptEnv == "undefined" ) { // this is a Collector 54 55 // importPackage( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util ); 56 // Bug in importPackage - import classes separately 57 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util.AgentInfo ); 58 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util.BaseEngine ); 59 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util.BSFEngine ); 60 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util.CollectorConfiguration ); 61 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util.Constants ); 62 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util.ContextInfoSender ); 63 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util.DBCollectorHelper ); 64 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util.EventData ); 65 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util.DebugInfoSender ); 66 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util.EventDataDebugger ); 67 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util.FormatDate ); 68 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util.InfoBlockSession ); 69 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util.InfoSender ); 70 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util.MatchResult ); 71 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util.OSEnv ); 72 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util.ScriptEngineContext ); 73 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util.ScriptEngineUtil ); 74 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util.TagValue ); 75 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.collector.util.FileUtil ); 76 77 // importPackage( Packages.esecurity.ccs.comp.evtsrcmgt.connectorapi ); 78 // Bug in importPackage - import classes separately 79 importClass( Packages.esecurity.ccs.comp.evtsrcmgt.connectorapi.ConnectorData ); 80 81 importClass( java.util.logging.Level ); 82 importClass( java.util.Map ); 83 importClass( java.util.HashMap ); 84 85 /** 86 * An object that contains various configuration information about this Collector 87 * like parameters, maps, etc. 88 * <p>Here are the usual contents: 89 * <ul> 90 * <li><code>instance.CONFIG.params.<parameter></code>: An object that contains the parameters 91 * set during configuration of this Collector. 92 * <li><code>instance.CONFIG.scriptContext</code>: An instance of the script context, which is 93 * the Java representation of the running script. 94 * <li><code>instance.CONFIG.collDir</code>: The directory in which this Collector is located. 95 * Can be used to find external files to load. 96 * <li><code>instance.CONFIG.commonDir</code>: The directory used to hold files that can be 97 * referenced by all Collectors. 98 * @type Object 99 */ 100 this.CONFIG = {}; 101 102 /** 103 * This object contains the runtime configuration parameters for this Collector, as 104 * defined via the ESM panel. 105 * Look up the rutime parameters by using: 106 * <code>instance.CONFIG.params.<parameter></code> 107 * @type Object 108 */ 109 this.CONFIG.params = {}; 110 111 /** 112 * Object representing the context in which the script is running. 113 * @type ScriptEngineContext (Java) 114 */ 115 this.CONFIG.scriptContext = ScriptEngineUtil.getContext(); 116 117 ScriptEngineUtil.log( Level.FINEST, "Initializing collector"); 118 119 /** 120 * The UUID of this Collector. 121 * @type UUID 122 */ 123 this.UUID=String(this.CONFIG.scriptContext.getCollectorID()); 124 125 /** 126 * Flag used to set the destination of audit output to a log 127 * @type Flag 128 */ 129 this.LOG = 0x1; 130 /** 131 * Flag used to set the destination of audit output to an event. 132 * @type Flag 133 */ 134 this.EVENT = 0x2; 135 136 /** 137 * The directory in which the unpacked plugin files reside. 138 * @type String 139 */ 140 this.CONFIG.collDir=String(this.CONFIG.scriptContext.getLocalDir()) + "/"; 141 /** 142 * The directory containing the Sentinel program 143 * @type String 144 */ 145 this.CONFIG.esecDir=this.CONFIG.collDir.slice(0,this.CONFIG.collDir.indexOf("data")); 146 /** 147 * The directory in which common files (custom code, common maps) reside. 148 * @type String 149 */ 150 this.CONFIG.commonDir=this.CONFIG.esecDir + "data/collector_common/"; 151 /** 152 * The ESEC_HOME/log directory. 153 * @type String 154 */ 155 this.CONFIG.logDir=this.CONFIG.esecDir + "log/"; 156 157 } 158 159 /** 160 * Starts this Collector in ESM. 161 */ 162 this.start = function() { 163 if( typeof this.collectorObject != "undefined" ) { 164 this.collectorObject.start(); 165 } 166 }; 167 168 /** 169 * Stops this Collector in ESM. 170 */ 171 this.stop = function() { 172 if( typeof this.collectorObject != "undefined" ) { 173 this.collectorObject.stop(); 174 } else { 175 ScriptEngineUtil.setStopFlag(this.CONFIG.scriptContext.getCollectorID()); 176 } 177 }; 178 179 180 } 181 182 /** 183 * The loadMaps method loads in a standard set of parameters and 184 * maps that will be used by this Collector to control operations and 185 * to transform objects. 186 * @return {Boolean} Result 187 * @see DataMap 188 * @see KeyMap 189 */ 190 Collector.prototype.scriptInit = function() { 191 192 /** 193 * A prototype Event object that is used to construct a new output event each time through 194 * the processing loop. 195 * Note that you can pre-set static attributes on this prototype object by editing the protoEvt.map 196 * file or by adding code to set attributes directly. Make sure you use the standard field labels. 197 * @type Event 198 */ 199 this.protoEvt = new Event(); 200 201 /** 202 * Flag which indicates whether the current event should be sent or not. Your code should set this 203 * to "true" once you've determined if the input record is a valid event and you have completed 204 * parsing. 205 * @type Boolean 206 */ 207 this.SEND_EVENT=false; 208 209 /** 210 * Storage area for partial event sessions. 211 * Put partial records in sessions stored here and attach expiration timers and parsers to them. 212 * @type Hash 213 * @see Session 214 */ 215 this.STORE = {}; 216 217 /** 218 * Storage area for anonymous parsing routines - can then be attached to sessions. 219 * @type Hash 220 * @see Session 221 * @see SQLQuery 222 */ 223 this.PARSER = {}; 224 225 /** 226 * Area where SQLQuery objects are stored. 227 * The SQLQuery objects stored here should be named based on the event source UUIDs with which they are associated; 228 * ordinarily this will be handled automatically by the Connector when it detects new DB event sources, but you 229 * can manipulate these manually if necessary. 230 * @type Hash 231 * @see SQLQuery 232 */ 233 this.QUERIES = {}; 234 235 // Load in package.xml to set the standard stuff 236 var packageFile=new File(this.CONFIG.collDir + "/package.xml"); 237 var packageText=packageFile.readFile(); 238 packageFile.close(); 239 var packageXML=new XML(packageText.substr(packageText.indexOf("<",1))); 240 this.CONFIG.collectorScript=String(packageXML.DisplayName); 241 this.CONFIG.collectorScriptSafe=String(packageXML.Name); 242 this.protoEvt.CollectorScript=this.CONFIG.collectorScript; 243 if (this.CONFIG.collectorScript == "Generic Event") { 244 this.CONFIG.params.UseConnectorName = true; 245 } 246 this.CONFIG.collectorPluginId=String(packageXML.ID); 247 this.protoEvt.CollectorPluginId=this.CONFIG.collectorPluginId; 248 var protoMap = new KeyMap(this.CONFIG.collDir + "/protoEvt.map"); 249 for (var field in protoMap) { 250 if ( field.hasOwnProperty && typeof protoMap[field] != "function" && field != "SourceFiles" ) { 251 this.protoEvt[field] = protoMap.lookup(field)[0]; 252 } 253 } 254 255 // Store the parameters in the global CONFIG object 256 var paramFile=new File(this.CONFIG.collDir + "/parameters.csv"); 257 var linePair=[]; 258 for (var inputLine = paramFile.readLine(); typeof inputLine != 'undefined'; inputLine=paramFile.readLine()) { 259 linePair=inputLine.safesplit(",","\""); 260 this.CONFIG.params[linePair[0]]=linePair[1]; 261 } 262 paramFile.close(); 263 264 // Default Connector retry behavior 265 if ( typeof this.CONFIG.params.Conn_Retry == "undefined" ) { 266 this.CONFIG.params.Conn_Retry = "falloff"; 267 } 268 269 // read params and inject into prototype event obj 270 this.protoEvt.MSSPCustomerName=this.CONFIG.params.MSSP_Customer; 271 this.protoEvt.Severity=this.CONFIG.params.Default_Severity; 272 273 /** 274 * Default for database suboffset behavior; false 275 * @see SQLQuery 276 */ 277 this.CONFIG.params.SubOffsets = false; 278 279 280 // Load record-to-event conversion map 281 /** 282 * This object stores the maps that will be used to control record conversion and for looking 283 * up external business-relevance data. For example, the instance.MAPS.Rec2Evt map defines how normal 284 * records will be converted to output events. 285 * <p>Example: 286 * <pre> 287 * // in instance.initialize() method: 288 * this.MAPS.SeverityMap = new KeyMap(this.CONFIG.collDir + "/Severity.map"); 289 * // When you need to apply it in rec.normalize(): 290 * var srcSev = this.s_RXBufferString.substr(x,y); 291 * this.sentinelSev = instance.MAPS.SeverityMap.lookup(srcSev); 292 * </pre> 293 * @type Object 294 * 295 */ 296 this.MAPS = {}; 297 this.MAPS.Rec2Evt = new DataMap(this.CONFIG.collDir + "/Rec2Evt.map"); 298 this.MAPS.Rec2Evt.makesafe(); // Escape the rec object references 299 this.MAPS.Rec2Evt.compile(); // Precompile for speed 300 this.MAPS.UnsupEvt = new DataMap(); // Blank map 301 this.MAPS.UnsupEvt.compile(); // Precompile for speed 302 303 // Create map to convert public JS map to Java EventData map 304 this.MAPS.Evt2EvtData = new DataMap(this.CONFIG.collDir + "/Evt2EvtData.map"); 305 306 // Load taxonomy KeyMaps 307 this.MAPS.tax = new KeyMap(this.CONFIG.collDir + "/taxonomy.map"); 308 this.MAPS.taxcode = new KeyMap(this.CONFIG.collDir + "/xdas_tax.map"); 309 this.MAPS.taxout = new KeyMap(this.CONFIG.collDir + "/xdas_out.map"); 310 311 // Load IP-to-hostname map 312 if (this.CONFIG.params.Resolve_IP_and_Hostname == "yes") { 313 this.MAPS.ip2n = new KeyMap(this.CONFIG.commonDir + "/ip2n.map"); 314 this.MAPS.n2ip = new KeyMap(this.CONFIG.commonDir + "/n2ip.map"); 315 } 316 317 // Load IP-to-user map 318 if (this.CONFIG.params.Resolve_IP_To_User == "yes") { 319 this.MAPS.ip2u = new KeyMap(this.CONFIG.commonDir + "/ip2u.map"); 320 } 321 322 /** 323 * Cache for Timezone calculations 324 * @type Array 325 */ 326 this.MAPS.TZMap=[]; 327 328 // Load custom parsing code 329 if (this.CONFIG.params.ExecutionMode == "custom") { 330 var customFile=new File(this.CONFIG.collDir + "custom.js"); 331 if (customFile.isOpen()) { 332 try { 333 eval(customFile.readFile()); // will overwrite stubs from above 334 customFile.close(); 335 } catch(err) { 336 log("Could not read from custom parsing file:" + err, 4, this.EVT); 337 } 338 } 339 if (typeof this.customInit == "undefined" || typeof Record.prototype.customPreparse == "undefined" || typeof Record.prototype.customParse == "undefined" ) { 340 this.CONFIG.params.ExecutionMode = "release"; 341 log("Custom parsing file not found or not complete; reverting to release mode", 4, this.EVENT & this.LOG); 342 } 343 // Call the customInit method 344 this.customInit(); 345 } 346 if ( this.CONFIG.params.ExecutionMode != "custom") { // can't be an 'else'; this is here to provide the API docs 347 348 /** 349 * This method is used to provide locally-defined custom initialization of the Collector. 350 * <p>Useful variables: 351 * <dl> 352 * <dt>instance.CONFIG.collDir</dt><dd>The directory that will contain all the Collector plugin files</dd> 353 * <dt>this.CONFIG.commonDir</dt><dd>The directory where this custom code resides - you can add additional files as necessary</dd> 354 * </dl> 355 * @return {Boolean} Result 356 */ 357 Collector.prototype.customInit = function() { return true; } 358 359 /** 360 * This method is used to provide locally-defined custom pre-parsing of the input record. 361 * You might use this method if something in your environment modifies the normal input format, 362 * for example if the event is tunneled through some other protocol, you might strip off 363 * any additional headers that were added. 364 * @param {Object} e The output event 365 */ 366 Record.prototype.customPreparse = function(e) { return true; } 367 368 /** 369 * This method is used to provide locally-defined custom parsing of the input record. 370 * NOTE: There are two types of modifications that are typically peformed: 371 * <ul> 372 * <li>Modifications to the output of existing event fields: in this case, you may need to 373 * debug the Collector to determine which Record attribute is used to hold the data, and then 374 * perform your custom transformation on that data. For example:<br/> 375 * <pre> 376 * // Main code sets rec.evt to raw event name from device, but these overlap with 377 * // event names from other devices in our environment so are hard to distinguish. 378 * // We will add a prefix to help identify the events 379 * this.evt = "FW: " + this.evt; 380 * </pre></li> 381 * <li>Additional custom parsing that pulls more specific pieces of information out of the event, 382 * for example if a free-text field contains some info that you want to use to categorize events 383 * in your environment. In this scenario, you should: 384 * <ul> 385 * <li>Only use CustomerVars to hold the parsed-out data</li> 386 * <li>You will need to manipulate the Event object directly, as new additions to the Record object 387 * will be lost. The 'e' variable is used to access the Event object.</li> 388 * </ul> 389 * Use the 'CustomerVar1' through 'CustomerVar300' to hold your data. Refer to the documentation 390 * for the datatypes of those variables. 391 * <pre> 392 * // Want to extract the Department name that is injected into the "message" field 393 * e.CustomerVar21 = rec.message.substr(12,34); 394 * </pre> 395 * @param {Object} e The output event 396 */ 397 Record.prototype.customParse = function(e) { return true; } 398 } 399 400 // Open debug file 401 if (this.CONFIG.params.ExecutionMode == "debug") { 402 this.CONFIG.debugFile=new File(this.CONFIG.logDir + this.UUID + "-" + new Date().getTime() + ".json", "w"); 403 } 404 405 return true; 406 }; 407 408 /** 409 * The sleep method simply puts the Collector to sleep for the specified number of seconds. 410 * @param {Number} sec Number of seconds to sleep 411 * @return {Boolean} Result 412 */ 413 Collector.prototype.sleep = function(sec) { 414 var msec = sec * 1000; 415 try { 416 java.lang.Thread.sleep(msec); 417 } catch (e) { 418 log(e); 419 } 420 return true; 421 }; 422 423 /** 424 * The reset method resets the Collector after processing is completed for the previous 425 * record. It will create a new, empty record to receive the next input, and also a new 426 * Event object that is a clone of the prototype event (so that static fields can be preset). 427 * <p> 428 * Side effects: 429 * <ul> 430 * <li><code>instance.SEND_EVENT</code> is set to false 431 * <li><code>rec</code> is created as an empty Record object 432 * <li><code>curEvt</code> is created as a clone of the prototype Event object 433 * <li>Host/IP resolution maps are refreshed 434 * <li>IP/User resolution maps are refreshed 435 * <li>Any stored Session objects that are ready for processing are processed 436 * </ul> 437 * @return {Boolean} Result 438 */ 439 Collector.prototype.reset = function() { 440 441 // Default to sending an event only if the code explicitly sets SEND_EVENT to true 442 this.SEND_EVENT=false; 443 444 if (this.CONFIG.params.Resolve_IP_and_Hostname == "yes") { 445 this.MAPS.ip2n.refresh(); 446 this.MAPS.n2ip.refresh(); 447 } 448 if (instance.CONFIG.params.Resolve_IP_To_User == "yes") { 449 this.MAPS.ip2u.refresh(); 450 } 451 452 // Before getting the next record, check if there are any waiting DB queries to send 453 for each ( var query in this.QUERIES ) { 454 if (query.needQuery && new Date().isAfter(query.queryScheduled)) { 455 conn.send(query); 456 query.needQuery = false; 457 } 458 } 459 460 // Before getting the next record, check if there are any waiting sessions to be processed 461 for each ( var key in this.STORE ) { 462 if ( key.retrieve().length >= key.MaxRecs || new Date().getTime() > key.WallTimer ) { 463 try { 464 key.Parser(); 465 } catch(err) { 466 log("Parsing failed in Session", 5, instance.LOG|instance.EVENT); 467 } 468 key.clear(); 469 } 470 } 471 472 return true; 473 }; 474