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 Event class, which represents the output event that will be
 19  * sent to Sentinel.
 20  * @name Event Class
 21  */
 22 
 23 /**
 24  * Constructs a new instance of an Event object, which can be sent to Sentinel.
 25  * The constructor can take an object or JSON string as input to set pre-defined
 26  * fields, e.g. constants for initialization of the new Event object.
 27  * @class
 28  * Represents a Sentinel event that can be sent up to the Collector Manager.
 29  * In general, Event objects are not manipulated directly as the field values
 30  * need to be protected, but are manipulated via methods. 
 31  * @param {Event} proto  The prototype or template event that should be cloned to create this new Event
 32  */
 33 function Event(proto) {
 34 	for (var src in proto) {
 35 		if ( proto.hasOwnProperty(src) ) {
 36 			this[src] = proto[src];
 37 		}
 38 	}
 39 };
 40 
 41 /**
 42  * Sets the ObserverEventTime and ObserverEventTimeString to the time according to the event source.
 43  * The Sentinel event schema includes two fields:
 44  * <dl>
 45  * <dt>ObserverEventTimeString</dt><dd>A string representation of the time, according to the event source, that the event occurred.</dd>
 46  * <dt>ObserverEventTime</dt><dd>A timestamp representation of the time, according to the event source, that the event occurred; if the event source clock is trusted, this will be copied to EventTime.</dd>
 47  * </dl>
 48  * Both fields will be set by this method.
 49  * <p>Note that you must pass a Date object to this method; you must therefore first convert whatever
 50  * textual representation of the date and time is embedded in the input record into a Date object.
 51  * We have included the <a href="http://www.datejs.com/">date.js library</a> to make this easier. You
 52  * should be able to pass it almost any datetime representation and have it correctly create a Date,
 53  * but note that in some cases you may need to add missing year and/or timezone information.
 54  * @example
 55  * Record.prototype.parse = function(e) {
 56  *  this.devtime = Date.parse(this.s_RXBufferString.substr(0,30));
 57  *  e.setObserverEventTime(this.devtime);
 58  *  
 59  * @param {Date} time  The date/time according to the event source. This MUST be a Date object.
 60  * @return {Boolean} A Boolean to indicate successful completion
 61  */
 62 Event.prototype.setObserverEventTime = function(time){
 63   if ( !(time instanceof Date) ) { return false; }	
 64 	this.ObserverEventTime = time;
 65 	return true;
 66 };
 67 
 68 /**
 69  * Sets the BeginTime field to the time the event(s) began according to the event source.
 70  * <p>Note that you must pass a Date object to this method; you must therefore first convert whatever
 71  * textual representation of the date and time is embedded in the input record into a Date object.
 72  * We have included the <a href="http://www.datejs.com/">date.js library</a> to make this easier. You
 73  * should be able to pass it almost any datetime representation and have it correctly create a Date,
 74  * but note that in some cases you may need to add missing year and/or timezone information.
 75  * @example
 76  * Record.prototype.parse = function(e) {
 77  *  this.begintime = Date.parse(this.s_RXBufferString.substr(0,30));
 78  *  e.setBeginTime(this.begintime);
 79  *  
 80  * @param {Date} time  The date/time the event(s) began according to the event source. This MUST be a Date object.
 81  * @return {Boolean} A Boolean to indicate successful completion
 82  */
 83 Event.prototype.setBeginTime = function(time){
 84   if ( !(time instanceof Date) ) { return false; }	
 85 		this.BeginTime = time;
 86 		return true;
 87 };
 88 
 89 /**
 90  * Sets the EndTime field to the time the event(s) ended according to the event source.
 91  * <p>Note that you must pass a Date object to this method; you must therefore first convert whatever
 92  * textual representation of the date and time is embedded in the input record into a Date object.
 93  * We have included the <a href="http://www.datejs.com/">date.js library</a> to make this easier. You
 94  * should be able to pass it almost any datetime representation and have it correctly create a Date,
 95  * but note that in some cases you may need to add missing year and/or timezone information.
 96  * @example
 97  * Record.prototype.parse = function(e) {
 98  *  this.endtime = Date.parse(this.s_RXBufferString.substr(0,30));
 99  *  e.setEndTime(this.endtime);
100  *  
101  * @param {Date} time  The date/time the event(s) ended according to the event source. This MUST be a Date object.
102  * @return {Boolean} A Boolean to indicate successful completion
103  */
104 Event.prototype.setEndTime = function(time){
105   if ( !(time instanceof Date) ) { return false; }	
106 		this.EndTime = time;
107 		return true;
108 };
109 
110 /**
111  * Adds the arguments to the Sentinel ExtendedInformation field.
112  * Sentinel's ExtendedInformation field has a pre-defined internal format as name-value pair.
113  * To enforce this format, use this method to add data to this field.
114  * <p>Example:
115  * <pre>
116  * curEvt.add2EI( "CustomData", rec.s_RXBufferString.substr(20,30);
117  * </pre>
118  * @param {String} name  The attribute name that will be set
119  * @param {String} val  The value for that attribute
120  * @return {Boolean} A Boolean to indicate successful completion
121  */
122 Event.prototype.add2EI = function(name, val){
123 	if (typeof name == "undefined" || typeof val == "undefined" || name === "" || val === "") { 
124 	 return false;
125 	}
126 	if (typeof this.EIObj == "undefined") {
127 		this.EIObj = {};
128 	}
129 	this.EIObj[name] = val;
130 	return true;
131 };
132 
133 /**
134  * Sets the taxonomy key to be used when constructing the output Event.
135  * The taxonomy key is used to determine the taxonomy classification for the event. It
136  * should include enough information to determine the type of action taken as well as the
137  * outcome of the event (status or result). The key needs to match one of the entries in
138  * the taxonomy {@link KeyMap}, which is loaded from the taxonomy.map file.
139  * <p>The taxonomy key is used to set the following Event fields:
140  * <ul>
141  * <li>Event.TaxonomyLevel1
142  * <li>Event.TaxonomyLevel2
143  * <li>Event.TaxonomyLevel3
144  * <li>Event.TaxonomyLevel4
145  * <li>Event.XDASEventName
146  * <li>Event.XDASEventOutcome
147  * <li>Event.XDASRegistry
148  * <li>Event.XDASProvider
149  * <li>Event.XDASClass
150  * <li>Event.XDASIdentifier
151  * <li>Event.XDASOutcome
152  * <li>Event.XDASDetail
153  * </ul>
154  * @param {String} key  The string to use as the taxonomy key
155  * @return {Boolean} Result
156  */
157 Event.prototype.setTaxKey = function(key){
158 	// Use key to look up taxonomy
159 	if (typeof key == "undefined") { return false; }
160 	var taxonomy = instance.MAPS.tax.lookup(key);
161 	if (typeof taxonomy == "undefined") { return false; }
162 	this.XDASTaxonomyName = taxonomy[0];
163 	this.XDASOutcomeName = taxonomy[1];
164 	var taxcode = instance.MAPS.taxcode.lookup(this.XDASTaxonomyName);
165 	var taxout = instance.MAPS.taxout.lookup(this.XDASOutcomeName);
166 	this.XDASRegistry = taxcode[0];
167 	this.XDASProvider = taxcode[1];
168 	this.XDASClass = taxcode[2];
169 	this.XDASIdentifier = taxcode[3];
170 	if (instance.CONFIG.params.addLegacyTaxonomy) {
171 		this.TaxonomyLevel1 = taxcode[4];
172 		this.TaxonomyLevel2 = taxcode[5];
173 		this.TaxonomyLevel3 = taxcode[6];
174 	}
175 	this.XDASOutcome = taxout[0];
176 	this.XDASDetail = taxout[1];
177 	if (instance.CONFIG.params.addLegacyTaxonomy) {
178 		switch (this.XDASOutcome) {
179 			case "1":
180 				this.TaxonomyLevel3 = this.TaxonomyLevel3 + " FAILED";
181 				break;
182 			case "2":
183 				this.TaxonomyLevel3 = this.TaxonomyLevel3 + " DENIED";
184 				break;
185 			case "0":
186 			case "3":
187 			default:
188 				break;
189 		}
190 	}
191 	return true;
192 };
193 
194 /**
195  * Sends an event to the Collector Manager.
196  * This method consumes the input record object to set fields in the output event,
197  * as well as performing some additional processing.
198  * @param {DataMap} datamap  The map to use (optional) when converting the 'rec' object into the Event. Will default to Rec2Evt.
199  * @return {Boolean} Result
200  */
201 Event.prototype.send = function(datamap) {
202 	
203 	if (typeof datamap == "undefined") {
204 		datamap = instance.MAPS.Rec2Evt;
205 	}
206 
207 	// Convert record to output event (if this is not an internal event)
208 	if (typeof rec != "undefined") {
209 		rec.convert(this, datamap);
210 	}
211 	else { // probably a debug event with no input record; need a blank one
212 		rec = new Record();
213 		var hashMap = new HashMap();
214 		rec.connectorData = new ConnectorData(hashMap);
215 	}
216 	
217 	// Create a JSON EI string
218 	if (typeof this.EIObj != "undefined") {
219 		this.ExtendedInformation = JSON.stringify(this.EIObj);
220 		delete this.EIObj;
221 	}
222 	
223 	// Produce debug output
224 	if (instance.CONFIG.params.ExecutionMode == "debug") {
225 		instance.CONFIG.debugFile.writeLine(JSON.stringify(this) + "\n");
226 	}
227 	
228 	// Mark replay events
229 	if (rec.CONNECTION_REPLAY == true) {
230 		this.ObserverType="T";
231 	}
232 	
233 	// resolve hostnames if asked
234 	if (instance.CONFIG.params.Resolve_IP_and_Hostname) {
235 		var assets = {"Source":null,"Target":null,"Observer":null,"Reporter":null};
236 		
237 		var unknownIPs = [];
238 		var unknownHosts = [];
239 		var fqdn;
240 		
241 		for (var asset in assets) {
242 			if (typeof this[asset + "HostName"] == 'undefined') { // Need the hostname
243 				if (typeof this[asset + "IP"] != 'undefined') {
244 					if (typeof instance.MAPS.ip2n[this[asset + "IP"]] != 'undefined') {
245 						fqdn = instance.MAPS.ip2n[this[asset + "IP"]][0];
246 						if (fqdn.search(/\./) != -1) {
247 							this[asset + "HostName"] = fqdn.substr(0, fqdn.indexOf("."));
248 							this[asset + "HostDomain"] = fqdn.substr(fqdn.indexOf(".") + 1);
249 						}	else {
250 							this[asset + "HostName"] = fqdn;
251 						}
252 					}	else {
253 						unknownIPs.push(this[asset + "IP"]);
254 					}
255 				}
256 			}	else { // Need the IP
257 				if (typeof this[asset + "IP"] == 'undefined') {
258 					if (typeof this[asset + "HostName"] != 'undefined' && typeof this[asset + "HostDomain"] != 'undefined') {
259 						fqdn = this[asset + "HostName"] + "." + this[asset + "HostDomain"];
260 					} else if (typeof this[asset + "HostName"] != 'undefined' && typeof this[asset + "HostDomain"] == 'undefined') {
261 						fqdn = this[asset + "HostName"];
262 					} 
263 					if (typeof fqdn != 'undefined') {
264 						if (typeof instance.MAPS.n2ip[fqdn] != 'undefined') {
265 							this[asset + "IP"] = instance.MAPS.n2ip[fqdn][0];
266 						}	else {
267 							unknownHosts.push(fqdn);
268 						}
269 					}
270 				}
271 			}
272 		}
273 	
274 		if (unknownIPs.length > 0) {
275 			var needIPsFile = new File(instance.CONFIG.commonDir + instance.UUID + ".needIPs.tmp", "w");
276 			for (var i = unknownIPs.length - 1; i > -1; i--) {
277 				needIPsFile.writeLine(unknownIPs[i]);
278 			}
279 			needIPsFile.close();
280 		}
281 		if (unknownHosts.length > 0) {
282 			var needHostsFile = new File(instance.CONFIG.commonDir + instance.UUID + ".needHosts.tmp", "w");
283 			for (i = unknownHosts.length - 1; i > -1; i--) {
284 				needHostsFile.writeLine(unknownHosts[i]);
285 			}
286 			needHostsFile.close();
287 		}
288 	} // end name resolution
289 	
290 	if (instance.CONFIG.params.Resolve_IP_To_User) {
291 		// Store a user if they log in
292 		if (this.XDASClass == "2" && this.XDASIdentifier == "0" && this.XDASOutcome == "0" &&
293 				(typeof this.TargetUserName != "undefined" || this.TargetUserName != "") &&
294 				(typeof this.TargetUserID != "undefined" || this.TargetUserID != "") && 
295 				typeof this.TargetIP != "undefined" && this.TargetIP != "") {
296 			var ip2uFile = new File(instance.CONFIG.commonDir + instance.UUID + ".ip2u.tmp", "w");
297 			ip2uFile.writeLine(this.TargetIP + "," + this.TargetUserName + 
298 													"," + this.TargetUserDomain + "," + this.TargetUserID);
299 			ip2uFile.close();		
300 		}
301 		// If InitiatorUserName is unset, try to find it
302 		if (typeof this.InitiatorUserName == "undefined" && typeof this.InitiatorUserID == "undefined" &&
303 				typeof this.SourceIP != undefined && this.SourceIP != "") {
304 			var userinfo = instance.MAPS.ip2u.lookup[this.SourceIP];
305 			if (typeof userinfo[0] != "undefined" && userinfo[0] != "") {
306 				this.InitiatorUserName = userinfo[0];
307 			}
308 			if (typeof userinfo[1] != "undefined" && userinfo[1] != "") {
309 				this.InitiatorUserDomain = userinfo[1];
310 			}
311 			if (typeof userinfo[2] != "undefined" && userinfo[2] != "") {
312 				this.InitiatorUserID = userinfo[2];
313 			}
314 		}
315 	} // end user resolution
316 	
317 	// Look up privilege level of users if provided
318 	if (instance.CONFIG.params.lookupPriv && typeof this.InitiatorUserName != "undefined" && this.InitiatorUserName != "") {
319 		var pl;
320 		pl = instance.MAPS.privLevel.lookup(this.InitiatorUserName);
321 		if (typeof pl != "undefined") { 
322 			this.InitiatorUserPrivilegeLevel = pl[0];
323 		} else {
324 			this.InitiatorUserPrivilegeLevel = 0;
325 		}
326 	}
327 	
328 	// Look up the sensitivity of the target data object
329 	if (instance.CONFIG.params.lookupSens) {
330 		var pl;
331 		if (typeof this.TargetDataNamespace != "undefined" && this.TargetDataNamespace != "") {
332 			pl = instance.MAPS.sensitivity.lookup(this.TargetDataNamespace + ":" + this.TargetDataContainer + "/" + this.TargetDataName);
333 			if (typeof pl != "undefined") { 
334 				this.TargetDataSensitivity = pl[0];
335 			}
336 		}
337 		if (typeof pl == "undefined" && typeof this.TargetDataContainer != "undefined" && this.TargetDataName != "") {
338 			pl = instance.MAPS.sensitivity.lookup(this.TargetDataContainer + "/" + this.TargetDataName);
339 			if (typeof pl != "undefined") { 
340 				this.TargetDataSensitivity = pl[0];
341 			}
342 		}
343 		if (typeof pl == "undefined") {
344 			pl = instance.MAPS.sensitivity.lookup(this.TargetDataContainer + "/");
345 			if (typeof pl != "undefined") { 
346 				this.TargetDataSensitivity = pl[0];
347 			} else {
348 				this.TargetDataSensitivity = 0;
349 			}
350 		}
351 	}
352 	
353 	// Set the ObserverTZ based on the record
354 	this.ObserverTZ = rec.s_TimeZone;
355 	
356 	// Make sure that we don't try to set new schema fields on old platforms
357 	if (instance.CONFIG.params.schemaVersion < 700) {
358 		for (var field in this) {
359 			switch (field) {
360 			
361 				case "InitiatorUserPrivilegeLevel":
362 				case "SourceMAC":
363 				case "SourceTranslatedIP":
364 				case "SourceTranslatedMAC":
365 				case "SourceTranslatedPort":
366 				case "VendorOutcomeCode":
367 				case "PolicyID":
368 				case "SessionID":
369 				case "ObserverTZ":
370 				case "TargetMAC":
371 				case "TargetTranslatedIP":
372 				case "TargetTranslatedPort":
373 				case "TargetTranslatedMAC":
374 				case "TargetDataNamespace":
375 				case "TargetDataSensitivity":
376 				case "TargetNewResourceName":
377 				case "TargetNewResourceType":
378 				case "TargetNewResourceContainer":
379 				case "TargetNewResourceNamespace":
380 				case "TargetAttributeName":
381 				case "ObserverMAC":
382 				case "ObserverServiceName":
383 				case "ReporterMAC":
384 					this.add2EI(field, this[field]);
385 					delete this[field];
386 					break;
387 				default:
388 					break;
389 			}
390 		}
391 	}
392 	
393 	// Construct the Java event from the JS event
394 	var JEvent = new EventData();
395 	for (var field in this) {
396 		if (this.hasOwnProperty(field)) {
397 				var value = this[field];
398 				if (value != "" && instance.MAPS.Evt2EvtData[field] && typeof value != "undefined" && value != "undefined" 
399 							&& field != "ObserverEventTime" && field != "BeginTime" && field != "EndTime" && field != "length") {
400 					 JEvent.setString(instance.MAPS.Evt2EvtData[field], String(value));
401 				}
402 	  }
403 	}
404 	
405 	// Set ObserverEventTime and ObserverEventTimeString 
406 		if (this.ObserverEventTime instanceof Date) {
407 			this.ObserverEventTime.adjustTimezone();
408 			JEvent.setDeviceEventTime(String(this.ObserverEventTime.getTime()));
409 			JEvent.setString( "et", this.ObserverEventTime.toString());
410 		}
411 	
412 		// Set BeginTime and EndTime
413 		if (this.BeginTime instanceof Date) {
414 			this.BeginTime.adjustTimezone();
415 			JEvent.setString("bgnt", String(this.BeginTime.getTime()));
416 		}
417 		if (this.EndTime instanceof Date) {
418 			this.EndTime.adjustTimezone();
419 			JEvent.setString("endt", String(this.EndTime.getTime()));
420 		}
421 		// Send event
422 	instance.CONFIG.scriptContext.fireEvent(JEvent, rec.connectorData);
423 	return true;
424 };
425