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 Sentinel schema version in use on the platform this Collector is running on 121 * @type Number 122 */ 123 if (ScriptEngineUtil.hasFeature("Schema/7.0.0")) { 124 this.CONFIG.params.schemaVersion = 700; 125 } else { 126 this.CONFIG.params.schemaVersion = 600; 127 } 128 129 /** 130 * The UUID of this Collector. 131 * @type UUID 132 */ 133 this.UUID=String(this.CONFIG.scriptContext.getCollectorID()); 134 135 /** 136 * Flag used to set the destination of audit output to a log 137 * @type Flag 138 */ 139 this.LOG = 0x1; 140 /** 141 * Flag used to set the destination of audit output to an event. 142 * @type Flag 143 */ 144 this.EVENT = 0x2; 145 146 /** 147 * The directory in which the unpacked plugin files reside. 148 * @type String 149 */ 150 this.CONFIG.collDir=String(this.CONFIG.scriptContext.getLocalDir()) + "/"; 151 /** 152 * The directory containing the Sentinel program 153 * @type String 154 */ 155 this.CONFIG.esecDir=this.CONFIG.collDir.slice(0,this.CONFIG.collDir.indexOf("data")); 156 /** 157 * The directory in which common files (custom code, common maps) reside. 158 * @type String 159 */ 160 this.CONFIG.commonDir=this.CONFIG.esecDir + "data/collector_common/"; 161 /** 162 * The ESEC_HOME/log directory. 163 * @type String 164 */ 165 this.CONFIG.logDir=this.CONFIG.esecDir + "log/"; 166 167 } 168 169 /** 170 * Starts this Collector in ESM. 171 */ 172 this.start = function() { 173 if( typeof this.collectorObject != "undefined" ) { 174 this.collectorObject.start(); 175 } 176 }; 177 178 /** 179 * Stops this Collector in ESM. 180 */ 181 this.stop = function() { 182 if( typeof this.collectorObject != "undefined" ) { 183 this.collectorObject.stop(); 184 } else { 185 ScriptEngineUtil.setStopFlag(this.CONFIG.scriptContext.getCollectorID()); 186 } 187 }; 188 189 190 } 191 192 /** 193 * The loadMaps method loads in a standard set of parameters and 194 * maps that will be used by this Collector to control operations and 195 * to transform objects. 196 * @return {Boolean} Result 197 * @see DataMap 198 * @see KeyMap 199 */ 200 Collector.prototype.scriptInit = function() { 201 202 /** 203 * A prototype Event object that is used to construct a new output event each time through 204 * the processing loop. 205 * Note that you can pre-set static attributes on this prototype object by editing the protoEvt.map 206 * file or by adding code to set attributes directly. Make sure you use the standard field labels. 207 * @type Event 208 */ 209 this.protoEvt = new Event(); 210 211 /** 212 * Flag which indicates whether the current event should be sent or not. Your code should set this 213 * to "true" once you've determined if the input record is a valid event and you have completed 214 * parsing. 215 * @type Boolean 216 */ 217 this.SEND_EVENT=false; 218 219 /** 220 * Storage area for partial event sessions. 221 * Put partial records in sessions stored here and attach expiration timers and parsers to them. 222 * @type Hash 223 * @see Session 224 */ 225 this.STORE = {}; 226 227 /** 228 * Storage area for anonymous parsing routines - can then be attached to sessions. 229 * @type Hash 230 * @see Session 231 * @see SQLQuery 232 */ 233 this.PARSER = {}; 234 235 /** 236 * Area where SQLQuery objects are stored. 237 * The SQLQuery objects stored here should be named based on the event source UUIDs with which they are associated; 238 * ordinarily this will be handled automatically by the Connector when it detects new DB event sources, but you 239 * can manipulate these manually if necessary. 240 * @type Hash 241 * @see SQLQuery 242 */ 243 this.QUERIES = {}; 244 245 // Load in package.xml to set the standard stuff 246 var packageFile=new File(this.CONFIG.collDir + "/package.xml"); 247 var packageText=packageFile.readFile(); 248 packageFile.close(); 249 var packageXML=new XML(packageText.substr(packageText.indexOf("<",1))); 250 this.CONFIG.collectorScript=String(packageXML.DisplayName); 251 this.CONFIG.collectorScriptSafe=String(packageXML.Name); 252 this.protoEvt.CollectorPluginName=this.CONFIG.collectorScript; 253 if (this.CONFIG.collectorScript == "Generic Event") { 254 this.CONFIG.params.UseConnectorName = true; 255 } 256 this.CONFIG.collectorPluginId=String(packageXML.ID); 257 this.protoEvt.CollectorPluginID=this.CONFIG.collectorPluginId; 258 var protoMap = new KeyMap(this.CONFIG.collDir + "/protoEvt.map"); 259 for (var field in protoMap) { 260 if ( field.hasOwnProperty && typeof protoMap[field] != "function" && field != "SourceFiles" ) { 261 this.protoEvt[field] = protoMap.lookup(field)[0]; 262 } 263 } 264 265 // Store the parameters in the global CONFIG object 266 var paramFile=new File(this.CONFIG.collDir + "/parameters.csv"); 267 var linePair=[]; 268 for (var inputLine = paramFile.readLine(); typeof inputLine != 'undefined'; inputLine=paramFile.readLine()) { 269 linePair=inputLine.safesplit(",","\""); 270 // True Boolean parameters are not supported, but convert any parameters set to 'yes/no' or 'true/false' to a real Boolean 271 if (linePair[1] == "yes" || linePair[1] == "true") { 272 linePair[1] = true; 273 } else if (linePair[1] == "no" || linePair[1] == "false") { 274 linePair[1] = false; 275 } 276 this.CONFIG.params[linePair[0]]=linePair[1]; 277 } 278 paramFile.close(); 279 280 // Default Connector retry behavior 281 if ( typeof this.CONFIG.params.Conn_Retry == "undefined" ) { 282 this.CONFIG.params.Conn_Retry = "falloff"; 283 } 284 285 // read params and inject into prototype event obj 286 this.protoEvt.TenantName=this.CONFIG.params.TenantName; 287 this.protoEvt.Severity=this.CONFIG.params.Default_Severity; 288 289 /** 290 * Default for database suboffset behavior; false 291 * @see SQLQuery 292 */ 293 this.CONFIG.params.SubOffsets = false; 294 this.CONFIG.params.batchLimit = 5000; 295 296 // Load record-to-event conversion map 297 /** 298 * This object stores the maps that will be used to control record conversion and for looking 299 * up external business-relevance data. For example, the instance.MAPS.Rec2Evt map defines how normal 300 * records will be converted to output events. 301 * <p>Example: 302 * <pre> 303 * // in instance.initialize() method: 304 * this.MAPS.SeverityMap = new KeyMap(this.CONFIG.collDir + "/Severity.map"); 305 * // When you need to apply it in rec.normalize(): 306 * var srcSev = this.s_RXBufferString.substr(x,y); 307 * this.sentinelSev = instance.MAPS.SeverityMap.lookup(srcSev); 308 * </pre> 309 * @type Object 310 * 311 */ 312 this.MAPS = {}; 313 this.MAPS.Rec2Evt = new DataMap(this.CONFIG.collDir + "/Rec2Evt.map"); 314 this.MAPS.UnsupEvt = new DataMap(); // Blank map 315 316 // Create map to convert public JS map to Java EventData map 317 this.MAPS.Evt2EvtData = new DataMap(this.CONFIG.collDir + "/Evt2EvtData.map", true); 318 319 // Load taxonomy KeyMaps 320 this.MAPS.tax = new KeyMap(this.CONFIG.collDir + "/taxonomy.map"); 321 this.MAPS.taxcode = new KeyMap(this.CONFIG.collDir + "/xdas_tax.map"); 322 this.MAPS.taxout = new KeyMap(this.CONFIG.collDir + "/xdas_out.map"); 323 324 // Load IP-to-hostname map 325 if (this.CONFIG.params.Resolve_IP_and_Hostname) { 326 this.MAPS.ip2n = new KeyMap(this.CONFIG.commonDir + "/ip2n.map"); 327 this.MAPS.n2ip = new KeyMap(this.CONFIG.commonDir + "/n2ip.map"); 328 } 329 330 // Load IP-to-user map 331 if (this.CONFIG.params.Resolve_IP_To_User) { 332 this.MAPS.ip2u = new KeyMap(this.CONFIG.commonDir + "/ip2u.map"); 333 } 334 335 // Load privileged user map 336 this.CONFIG.params.lookupPriv = false; 337 this.MAPS.privLevel = new KeyMap(this.CONFIG.collDir + "/privlevel.map"); 338 if (this.MAPS.privLevel.length > 0) { 339 this.CONFIG.params.lookupPriv = true; 340 } 341 342 // Load data sensitivity map 343 this.CONFIG.params.lookupSens = false; 344 this.MAPS.sensitivity = new KeyMap(this.CONFIG.collDir + "/datasensitivity.map"); 345 if (this.MAPS.sensitivity.length > 0) { 346 this.CONFIG.params.lookupSens = true; 347 } 348 349 /** 350 * Cache for Timezone calculations 351 * @type Array 352 */ 353 this.MAPS.TZMap=[]; 354 355 // Load custom parsing code 356 if (this.CONFIG.params.ExecutionMode == "custom") { 357 var customFile=new File(this.CONFIG.collDir + "custom.js"); 358 if (customFile.isOpen()) { 359 try { 360 eval(customFile.readFile()); // will overwrite stubs from above 361 customFile.close(); 362 } catch(err) { 363 log("Could not read from custom parsing file:" + err, 4, this.EVT); 364 } 365 } 366 if (typeof this.customInit == "undefined" || typeof Record.prototype.customPreparse == "undefined" || typeof Record.prototype.customParse == "undefined" ) { 367 this.CONFIG.params.ExecutionMode = "release"; 368 log("Custom parsing file not found or not complete; reverting to release mode", 4, this.EVENT & this.LOG); 369 } 370 } 371 if ( this.CONFIG.params.ExecutionMode != "custom") { // can't be an 'else'; this is here to provide the API docs 372 373 /** 374 * This method is used to provide locally-defined custom initialization of the Collector. 375 * <p>Useful variables: 376 * <dl> 377 * <dt>instance.CONFIG.collDir</dt><dd>The directory that will contain all the Collector plugin files</dd> 378 * <dt>this.CONFIG.commonDir</dt><dd>The directory where this custom code resides - you can add additional files as necessary</dd> 379 * </dl> 380 * @return {Boolean} Result 381 */ 382 Collector.prototype.customInit = function() { return true; } 383 384 /** 385 * This method is used to provide locally-defined custom pre-parsing of the input record. 386 * You might use this method if something in your environment modifies the normal input format, 387 * for example if the event is tunneled through some other protocol, you might strip off 388 * any additional headers that were added. 389 * @param {Object} e The output event 390 */ 391 Record.prototype.customPreparse = function(e) { return true; } 392 393 /** 394 * This method is used to provide locally-defined custom parsing of the input record. 395 * NOTE: There are two types of modifications that are typically peformed: 396 * <ul> 397 * <li>Modifications to the output of existing event fields: in this case, you may need to 398 * debug the Collector to determine which Record attribute is used to hold the data, and then 399 * perform your custom transformation on that data. For example:<br/> 400 * <pre> 401 * // Main code sets rec.evt to raw event name from device, but these overlap with 402 * // event names from other devices in our environment so are hard to distinguish. 403 * // We will add a prefix to help identify the events 404 * this.evt = "FW: " + this.evt; 405 * </pre></li> 406 * <li>Additional custom parsing that pulls more specific pieces of information out of the event, 407 * for example if a free-text field contains some info that you want to use to categorize events 408 * in your environment. In this scenario, you should: 409 * <ul> 410 * <li>Only use CustomerVars to hold the parsed-out data</li> 411 * <li>You will need to manipulate the Event object directly, as new additions to the Record object 412 * will be lost. The 'e' variable is used to access the Event object.</li> 413 * </ul> 414 * Use the 'CustomerVar1' through 'CustomerVar300' to hold your data. Refer to the documentation 415 * for the datatypes of those variables. 416 * <pre> 417 * // Want to extract the Department name that is injected into the "message" field 418 * e.CustomerVar21 = rec.message.substr(12,34); 419 * </pre> 420 * @param {Object} e The output event 421 */ 422 Record.prototype.customParse = function(e) { return true; } 423 } 424 425 // Open debug file 426 if (this.CONFIG.params.ExecutionMode == "debug") { 427 this.CONFIG.debugFile=new File(this.CONFIG.logDir + this.UUID + "-" + new Date().getTime() + ".json", "w"); 428 } 429 430 return true; 431 }; 432 433 /** 434 * The sleep method simply puts the Collector to sleep for the specified number of seconds. 435 * @param {Number} sec Number of seconds to sleep 436 * @return {Boolean} Result 437 */ 438 Collector.prototype.sleep = function(sec) { 439 var msec = sec * 1000; 440 try { 441 java.lang.Thread.sleep(msec); 442 } catch (e) { 443 log(e); 444 } 445 return true; 446 }; 447 448 /** 449 * The reset method resets the Collector after processing is completed for the previous 450 * record. It will create a new, empty record to receive the next input, and also a new 451 * Event object that is a clone of the prototype event (so that static fields can be preset). 452 * <p> 453 * Side effects: 454 * <ul> 455 * <li><code>instance.SEND_EVENT</code> is set to false 456 * <li><code>rec</code> is created as an empty Record object 457 * <li><code>curEvt</code> is created as a clone of the prototype Event object 458 * <li>Host/IP resolution maps are refreshed 459 * <li>IP/User resolution maps are refreshed 460 * <li>Any stored Session objects that are ready for processing are processed 461 * </ul> 462 * @return {Boolean} Result 463 */ 464 Collector.prototype.reset = function() { 465 466 // Default to sending an event only if the code explicitly sets SEND_EVENT to true 467 this.SEND_EVENT=false; 468 469 if (this.CONFIG.params.Resolve_IP_and_Hostname) { 470 this.MAPS.ip2n.refresh(); 471 this.MAPS.n2ip.refresh(); 472 } 473 if (instance.CONFIG.params.Resolve_IP_To_User) { 474 this.MAPS.ip2u.refresh(); 475 } 476 477 // Before getting the next record, check if there are any waiting DB queries to send 478 for each ( var query in this.QUERIES ) { 479 if (query.needQuery && new Date().isAfter(query.queryScheduled)) { 480 conn.send(query); 481 query.needQuery = false; 482 } 483 } 484 485 // Before getting the next record, check if there are any waiting sessions to be processed 486 for each ( var key in this.STORE ) { 487 if ( key.retrieve().length >= key.MaxRecs || new Date().getTime() > key.WallTimer ) { 488 try { 489 key.Parser(); 490 } catch(err) { 491 log("Parsing failed in Session", 5, instance.LOG|instance.EVENT); 492 } 493 key.clear(); 494 } 495 } 496 497 return true; 498 }; 499