cef 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/VERSION +1 -1
- data/bin/cef_sender +25 -17
- data/cef.gemspec +73 -0
- data/lib/cef.rb +4 -268
- data/lib/cef/constants.rb +122 -0
- data/lib/cef/event.rb +116 -0
- data/lib/cef/file_logger.rb +8 -0
- data/lib/cef/sender.rb +38 -0
- data/spec/cef_spec.rb +3 -2
- data/spec/spec_helper.rb +4 -0
- metadata +10 -5
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.7.0
|
data/bin/cef_sender
CHANGED
@@ -3,19 +3,18 @@ require 'rubygems'
|
|
3
3
|
require 'cef'
|
4
4
|
require 'getoptlong'
|
5
5
|
|
6
|
-
sender=CEF::Sender.new
|
7
|
-
|
8
|
-
e=CEF::Event.new
|
9
6
|
@verbose=0
|
10
7
|
@file=nil
|
8
|
+
cef_event=CEF::Event.new
|
11
9
|
opts=GetoptLong.new(
|
12
|
-
["--verbose",
|
13
|
-
["--help",
|
14
|
-
["--schema",
|
10
|
+
["--verbose", GetoptLong::OPTIONAL_ARGUMENT],
|
11
|
+
["--help", GetoptLong::OPTIONAL_ARGUMENT],
|
12
|
+
["--schema", GetoptLong::OPTIONAL_ARGUMENT],
|
15
13
|
["--receiver", GetoptLong::OPTIONAL_ARGUMENT],
|
16
|
-
["--receiverPort",
|
17
|
-
["--append-file",
|
18
|
-
|
14
|
+
["--receiverPort", GetoptLong::OPTIONAL_ARGUMENT],
|
15
|
+
["--append-file", GetoptLong::OPTIONAL_ARGUMENT],
|
16
|
+
["--tcp", GetoptLong::OPTIONAL_ARGUMENT],
|
17
|
+
*cef_event.attrs.keys.collect {|o| ["--#{o}", GetoptLong::OPTIONAL_ARGUMENT]}
|
19
18
|
)
|
20
19
|
|
21
20
|
def print_usage
|
@@ -27,6 +26,8 @@ Usage: cef_sender --sourceAddress="192.168.1.1" [--eventAttribute="something"]
|
|
27
26
|
--schema will dump all of the callable event attribute names
|
28
27
|
--receiver= syslog receiver hostname/ip
|
29
28
|
--receiverPort= syslog port
|
29
|
+
--append-file= filename to append cef message to
|
30
|
+
--tcp will use TCP instead of the default (UDP) to send the message
|
30
31
|
|
31
32
|
cef_sender will send CEF-formatted syslog messages to a receiver of your choice.
|
32
33
|
only the cef fields defined in the cef reader flex connector are supported.
|
@@ -36,10 +37,11 @@ END_USAGE
|
|
36
37
|
|
37
38
|
end
|
38
39
|
|
39
|
-
def print_schema(
|
40
|
-
|
40
|
+
def print_schema(event)
|
41
|
+
event.attrs.keys.collect {|k| k.to_s}.sort.each {|a| puts a}
|
41
42
|
end
|
42
43
|
|
44
|
+
|
43
45
|
opts.each do |opt,arg|
|
44
46
|
# TODO: set up cases for startTime, receiptTime, endTime to parse
|
45
47
|
# text and convert to unix time * 1000
|
@@ -47,12 +49,12 @@ opts.each do |opt,arg|
|
|
47
49
|
when "--verbose"
|
48
50
|
@verbose+=1
|
49
51
|
when "--schema"
|
50
|
-
print_schema(
|
52
|
+
print_schema(cef_event)
|
51
53
|
exit(0)
|
52
54
|
when "--receiverPort"
|
53
|
-
|
55
|
+
@receiver_port=arg
|
54
56
|
when "--receiver"
|
55
|
-
|
57
|
+
@receiver_host=arg
|
56
58
|
when "--help"
|
57
59
|
print_usage
|
58
60
|
exit(0)
|
@@ -61,10 +63,16 @@ opts.each do |opt,arg|
|
|
61
63
|
else
|
62
64
|
fieldname = opt.gsub(/-/,'')
|
63
65
|
value=arg
|
64
|
-
|
66
|
+
cef_event.send("%s=" % fieldname, value)
|
65
67
|
end
|
66
68
|
end
|
67
|
-
|
69
|
+
|
70
|
+
cef_sender=CEF::UDPSender.new
|
71
|
+
cef_sender.receiver=@receiver_host
|
72
|
+
cef_sender.receiverPort=@receiver_port
|
73
|
+
|
74
|
+
|
75
|
+
msg=cef_event.format_cef
|
68
76
|
|
69
77
|
|
70
78
|
if @verbose>0
|
@@ -73,5 +81,5 @@ end
|
|
73
81
|
if !(@file.nil?) && File.exists?(@file)
|
74
82
|
@file.write "%s\n" % msg.gsub(/^<\d+>/,'')
|
75
83
|
else
|
76
|
-
|
84
|
+
cef_sender.emit(cef_event)
|
77
85
|
end
|
data/cef.gemspec
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{cef}
|
8
|
+
s.version = "0.7.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Ryan Breed"]
|
12
|
+
s.date = %q{2011-03-11}
|
13
|
+
s.default_executable = %q{cef_sender}
|
14
|
+
s.description = %q{ format/send CEF logs via API+syslog or client program }
|
15
|
+
s.email = %q{opensource@breed.org}
|
16
|
+
s.executables = ["cef_sender"]
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"LICENSE.txt",
|
19
|
+
"README.rdoc"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".document",
|
23
|
+
".rspec",
|
24
|
+
"Gemfile",
|
25
|
+
"LICENSE.txt",
|
26
|
+
"README.rdoc",
|
27
|
+
"Rakefile",
|
28
|
+
"VERSION",
|
29
|
+
"bin/cef_sender",
|
30
|
+
"cef.gemspec",
|
31
|
+
"lib/cef.rb",
|
32
|
+
"lib/cef/constants.rb",
|
33
|
+
"lib/cef/event.rb",
|
34
|
+
"lib/cef/file_logger.rb",
|
35
|
+
"lib/cef/sender.rb",
|
36
|
+
"spec/cef_spec.rb",
|
37
|
+
"spec/spec_helper.rb"
|
38
|
+
]
|
39
|
+
s.homepage = %q{http://github.com/ryanbreed/cef}
|
40
|
+
s.licenses = ["MIT"]
|
41
|
+
s.require_paths = ["lib"]
|
42
|
+
s.rubygems_version = %q{1.5.2}
|
43
|
+
s.summary = %q{CEF Generation Library and Client}
|
44
|
+
s.test_files = [
|
45
|
+
"spec/cef_spec.rb",
|
46
|
+
"spec/spec_helper.rb"
|
47
|
+
]
|
48
|
+
|
49
|
+
if s.respond_to? :specification_version then
|
50
|
+
s.specification_version = 3
|
51
|
+
|
52
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
53
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
|
54
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
55
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
|
56
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
57
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
|
58
|
+
else
|
59
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
60
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
61
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
62
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
63
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
64
|
+
end
|
65
|
+
else
|
66
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
67
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
68
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
69
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
70
|
+
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
data/lib/cef.rb
CHANGED
@@ -1,274 +1,10 @@
|
|
1
1
|
module CEF
|
2
2
|
require 'socket'
|
3
3
|
require 'parsedate'
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
# CEF Dictionary
|
9
|
-
# CEF Prefix attributes
|
10
|
-
PREFIX_ATTRIBUTES = {
|
11
|
-
:deviceVendor => "deviceVendor",
|
12
|
-
:deviceVersion => "deviceVersion",
|
13
|
-
:deviceProduct => "deviceProduct",
|
14
|
-
:name => "name",
|
15
|
-
:deviceSeverity => "deviceSeverity",
|
16
|
-
:deviceEventClassId => "deviceEventClassId"
|
17
|
-
}
|
18
|
-
|
19
|
-
# these are the basic extension attributes. implementing others is as
|
20
|
-
# simple as adding :symbolRepresentingMethodName => "cefkeyname", but
|
21
|
-
# i am supremely lazy to type in the whole dictionary right now. perhaps
|
22
|
-
# this should be a .yaml config file. Extension attributes are formatted
|
23
|
-
# differently than core attributes.
|
24
|
-
EXTENSION_ATTRIBUTES = {
|
25
|
-
:applicationProtocol => "app",
|
26
|
-
:baseEventCount => "cnt",
|
27
|
-
:bytesIn => "in",
|
28
|
-
:bytesOut => "out",
|
29
|
-
:deviceAction => "act",
|
30
|
-
:deviceHostNam => "dvc",
|
31
|
-
:deviceNtDomain => "deviceNtDomain",
|
32
|
-
:deviceDnsDomain => "deviceDnsDomain",
|
33
|
-
:deviceTranslatedAddress => "deviceTranslatedAddress",
|
34
|
-
:deviceMacAddress => "deviceMacAddress",
|
35
|
-
:deviceCustomNumber1 => "cn1",
|
36
|
-
:deviceCustomNumber2 => "cn2",
|
37
|
-
:deviceCustomNumber3 => "cn3",
|
38
|
-
:deviceCustomNumber1Label => "cn1Label",
|
39
|
-
:deviceCustomNumber2Label => "cn2Label",
|
40
|
-
:deviceCustomNumber3Label => "cn3Label",
|
41
|
-
:deviceCustomString1 => "cs1",
|
42
|
-
:deviceCustomString2 => "cs2",
|
43
|
-
:deviceCustomString3 => "cs3",
|
44
|
-
:deviceCustomString4 => "cs4",
|
45
|
-
:deviceCustomString5 => "cs5",
|
46
|
-
:deviceCustomString6 => "cs6",
|
47
|
-
:deviceCustomString1Label => "cs1Label",
|
48
|
-
:deviceCustomString2Label => "cs2Label",
|
49
|
-
:deviceCustomString3Label => "cs3Label",
|
50
|
-
:deviceCustomString4Label => "cs4Label",
|
51
|
-
:deviceCustomString5Label => "cs5Label",
|
52
|
-
:deviceCustomString6Label => "cs6Label",
|
53
|
-
:deviceCustomDate1 => "deviceCustomDate1",
|
54
|
-
:deviceCustomDate2 => "deviceCustomDate2",
|
55
|
-
:deviceCustomDate1Label => "deviceCustomDate1Label",
|
56
|
-
:deviceCustomDate2Label => "deviceCustomDate2Label",
|
57
|
-
:deviceEventCategory => "cat",
|
58
|
-
:destinationAddress => "dst",
|
59
|
-
:destinationDnsDomain => "destinationDnsDomain",
|
60
|
-
:destinationNtDomain => "dntdom",
|
61
|
-
:destinationHostName => "dhost",
|
62
|
-
:destinationMacAddress => "dmac",
|
63
|
-
:destinationPort => "dpt",
|
64
|
-
:destinationProcessName => "dproc",
|
65
|
-
:destinationServiceName => "destinationServiceName",
|
66
|
-
:destinationUserId => "duid",
|
67
|
-
:destinationUserPrivileges => "dpriv",
|
68
|
-
:destinationUserName => "duser",
|
69
|
-
:destinationTranslatedAddress => "destinationTranslatedAddress",
|
70
|
-
:destinationTranslatedPort => "destinationTranslatedPort",
|
71
|
-
:deviceDirection => "deviceDirection",
|
72
|
-
:deviceExternalId => "deviceExternalId",
|
73
|
-
:deviceFacility => "deviceFacility",
|
74
|
-
:deviceInboundInterface => "deviceInboundInterface",
|
75
|
-
:deviceOutboundInterface => "deviceOutboundInterface",
|
76
|
-
:deviceProcessName => "deviceProcessName",
|
77
|
-
:externalId => "externalId",
|
78
|
-
:fileHash => "fileHash",
|
79
|
-
:fileId => "fileId",
|
80
|
-
:fileName => "fname",
|
81
|
-
:filePath => "filePath",
|
82
|
-
:filePermission => "filePermission",
|
83
|
-
:fsize => "fsize",
|
84
|
-
:fileType => "fileType",
|
85
|
-
:message => "msg",
|
86
|
-
:oldfileHash => "oldfileHash",
|
87
|
-
:oldfileId => "oldfileId",
|
88
|
-
:oldFilename => "oldFilename",
|
89
|
-
:oldfilePath => "oldfilePath",
|
90
|
-
:oldfilePermission => "oldfilePermission",
|
91
|
-
:oldfsize => "oldfsize",
|
92
|
-
:oldfileType => "oldfileType",
|
93
|
-
:requestURL => "request",
|
94
|
-
:requestClientApplication => "requestClientApplication",
|
95
|
-
:requestCookies => "requestCookies",
|
96
|
-
:requestMethod => "requestMethod",
|
97
|
-
:sourceAddress => "src",
|
98
|
-
:sourceDnsDomain => "sourceDnsDomain",
|
99
|
-
:sourceHostName => "shost",
|
100
|
-
:sourceMacAddress => "smac",
|
101
|
-
:sourceNtDomain => "sntdom",
|
102
|
-
:sourcePort => "spt",
|
103
|
-
:sourceServiceName => "sourceServiceName",
|
104
|
-
:sourceTranslatedAddress => "sourceTranslatedAddress",
|
105
|
-
:sourceTranslatedPort => "sourceTranslatedPort",
|
106
|
-
:sourceUserPrivileges => "spriv",
|
107
|
-
:sourceUserId => "suid",
|
108
|
-
:sourceUserName => "suser",
|
109
|
-
:transportProtocol => "proto"
|
110
|
-
}
|
111
|
-
|
112
|
-
# these are tracked separately so they can be normalized during formatting
|
113
|
-
TIME_ATTRIBUTES={
|
114
|
-
:fileCreateTime => "fileCreateTime",
|
115
|
-
:fileModificationTime => "fileModificationTime",
|
116
|
-
:oldfileCreateTime => "oldfileCreateTime",
|
117
|
-
:oldfileModificationTime => "oldfileModificationTime",
|
118
|
-
:receiptTime => "rt",
|
119
|
-
:startTime => "start",
|
120
|
-
:endTime => "end"
|
121
|
-
}
|
122
|
-
|
123
|
-
ATTRIBUTES=PREFIX_ATTRIBUTES.merge EXTENSION_ATTRIBUTES.merge TIME_ATTRIBUTES
|
124
|
-
|
125
|
-
# this class will formats and sends the cef event objects. you can use senders to set
|
126
|
-
# default values for any event attribute, and you can send cef event objects to multiple
|
127
|
-
# senders if you wish.
|
128
|
-
class Sender
|
129
|
-
attr_accessor :receiver, :receiverPort, :eventDefaults
|
130
|
-
attr_reader :sock
|
131
|
-
|
132
|
-
# you can pass in a hash of options to be run to set parameters
|
133
|
-
def initialize(*params)
|
134
|
-
Hash[*params].each {|k,v| self.send("%s="%k,v) }
|
135
|
-
@sock=nil
|
136
|
-
end
|
137
|
-
|
138
|
-
def socksetup
|
139
|
-
@sock=UDPSocket.new
|
140
|
-
receiver= self.receiver || "127.0.0.1"
|
141
|
-
port= self.receiverPort || 514
|
142
|
-
@sock.connect(receiver,port)
|
143
|
-
end
|
144
|
-
|
145
|
-
# formats a CEFEvent
|
146
|
-
def format_event( event )
|
147
|
-
#HELL yeah it's hard-coded. What are you going to do about it?
|
148
|
-
#syslog_pri= Syslog::LOG_LOCAL0 | Syslog::LOG_NOTICE
|
149
|
-
syslog_pri=131
|
150
|
-
|
151
|
-
# process eventDefaults - we are expecting a hash here. These will
|
152
|
-
# override any values in the events passed to us. i know. brutal.
|
153
|
-
unless self.eventDefaults.nil?
|
154
|
-
self.eventDefaults.each do |k,v|
|
155
|
-
event.send("#{k}=",v)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
cef_message=PREFIX_FORMAT % [
|
160
|
-
syslog_pri.to_s,
|
161
|
-
Socket::gethostname,
|
162
|
-
Time.new.strftime("%b %d %Y %H:%M:%S"),
|
163
|
-
event.prefix,
|
164
|
-
event.extension
|
165
|
-
]
|
166
|
-
cef_message
|
167
|
-
end
|
168
|
-
|
169
|
-
#fire the message off
|
170
|
-
def emit(event)
|
171
|
-
self.socksetup if self.sock.nil?
|
172
|
-
self.sock.send self.format_event(event), 0
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
class Event
|
177
|
-
#%#
|
178
|
-
# set up accessors for all of the event attributes. ruby meta magic.
|
179
|
-
ATTRIBUTES.each do |k,v|
|
180
|
-
self.instance_eval do
|
181
|
-
attr_accessor k
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
def attrs
|
186
|
-
ATTRIBUTES
|
187
|
-
end
|
188
|
-
|
189
|
-
# so we can CEFEvent.new(:foo=>"bar")
|
190
|
-
def initialize( *params )
|
191
|
-
Hash[*params].each { |k,v| self.send("%s="%k,v) }
|
192
|
-
end
|
193
|
-
|
194
|
-
# escape only pipes and backslashes in the prefix. you bet your sweet
|
195
|
-
# ass there's a lot of backslashes in the substitution. you can thank
|
196
|
-
# the three levels of lexical analysis/substitution in the ruby interpreter
|
197
|
-
# for that.
|
198
|
-
def prefix_escape(val)
|
199
|
-
val.gsub(/(\||\\)/,'\\\\\&')
|
200
|
-
end
|
201
|
-
|
202
|
-
# only equals signs need to be escaped in the extension. i think.
|
203
|
-
# TODO: something in the spec about \n and some others.
|
204
|
-
def extension_escape(val)
|
205
|
-
val.gsub(/=/,'\=')
|
206
|
-
end
|
207
|
-
|
208
|
-
# make a guess as to how the time was set. parse strings and convert
|
209
|
-
# them to epoch milliseconds, or leave it alone if it looks like a number
|
210
|
-
# bigger than epoch milliseconds when i wrote this.
|
211
|
-
def time_convert(val)
|
212
|
-
converted=nil
|
213
|
-
#puts "converting time for #{val.class.to_s}/#{val}"
|
214
|
-
case val.class.to_s
|
215
|
-
when "String"
|
216
|
-
begin
|
217
|
-
converted=val.to_i
|
218
|
-
rescue
|
219
|
-
res=ParseDate.parsedate(val)
|
220
|
-
converted=Time.local(*res).to_i * 1000
|
221
|
-
end
|
222
|
-
when "Integer","Bignum"
|
223
|
-
if val < 1232589621000 #Wed Jan 21 20:00:21 -0600 2009
|
224
|
-
converted=val * 1000
|
225
|
-
else
|
226
|
-
converted=val
|
227
|
-
end
|
228
|
-
end
|
229
|
-
converted
|
230
|
-
end
|
231
|
-
|
232
|
-
# returns a pipe-delimeted list of prefix attributes
|
233
|
-
def prefix
|
234
|
-
vendor= self.deviceVendor || "Breed"
|
235
|
-
product= self.deviceProduct || "CEF Sender"
|
236
|
-
version= self.deviceVersion || CEF::VERSION
|
237
|
-
declid= self.deviceEventClassId || "generic:0"
|
238
|
-
name= self.name || "Generic Event"
|
239
|
-
sev= self.deviceSeverity || "1"
|
240
|
-
cef_prefix="%s|%s|%s|%s|%s|%s" % [
|
241
|
-
prefix_escape(vendor),
|
242
|
-
prefix_escape(product),
|
243
|
-
prefix_escape(version),
|
244
|
-
prefix_escape(declid),
|
245
|
-
prefix_escape(name),
|
246
|
-
prefix_escape(sev),
|
247
|
-
]
|
248
|
-
end
|
249
|
-
|
250
|
-
# returns a space-delimeted list of attribute=value pairs for all optionals
|
251
|
-
def extension
|
252
|
-
avpairs=[]
|
253
|
-
EXTENSION_ATTRIBUTES.each do |attribute,shortname|
|
254
|
-
unless self.send(attribute).nil?
|
255
|
-
avpairs.push(
|
256
|
-
"%s=%s" % [ shortname, extension_escape(self.send(attribute)) ]
|
257
|
-
)
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
|
-
# make sure time comes out as milliseconds since epoch
|
262
|
-
TIME_ATTRIBUTES.each do |attribute,shortname|
|
263
|
-
unless self.send(attribute).nil?
|
264
|
-
avpairs.push(
|
265
|
-
"%s=%s" % [ shortname, time_convert(self.send(attribute)) ]
|
266
|
-
)
|
267
|
-
end
|
268
|
-
end
|
269
|
-
avpairs.join(" ")
|
270
|
-
end
|
271
|
-
end
|
4
|
+
require 'cef/constants'
|
5
|
+
require 'cef/event'
|
6
|
+
require 'cef/sender'
|
7
|
+
require 'cef/file_logger'
|
272
8
|
end
|
273
9
|
|
274
10
|
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module CEF
|
2
|
+
PREFIX_FORMAT="<%d>%s %s CEF:0|%s|%s"
|
3
|
+
VERSION=File.read(File.join(File.expand_path(File.dirname(__FILE__)),'..','..','VERSION'))
|
4
|
+
|
5
|
+
|
6
|
+
# CEF Dictionary
|
7
|
+
# CEF Prefix attributes
|
8
|
+
PREFIX_ATTRIBUTES = {
|
9
|
+
:deviceVendor => "deviceVendor",
|
10
|
+
:deviceVersion => "deviceVersion",
|
11
|
+
:deviceProduct => "deviceProduct",
|
12
|
+
:name => "name",
|
13
|
+
:deviceSeverity => "deviceSeverity",
|
14
|
+
:deviceEventClassId => "deviceEventClassId"
|
15
|
+
}
|
16
|
+
|
17
|
+
# these are the basic extension attributes. implementing others is as
|
18
|
+
# simple as adding :symbolRepresentingMethodName => "cefkeyname", but
|
19
|
+
# i am supremely lazy to type in the whole dictionary right now. perhaps
|
20
|
+
# this should be a .yaml config file. Extension attributes are formatted
|
21
|
+
# differently than core attributes.
|
22
|
+
EXTENSION_ATTRIBUTES = {
|
23
|
+
:applicationProtocol => "app",
|
24
|
+
:baseEventCount => "cnt",
|
25
|
+
:bytesIn => "in",
|
26
|
+
:bytesOut => "out",
|
27
|
+
:deviceAction => "act",
|
28
|
+
:deviceHostNam => "dvc",
|
29
|
+
:deviceNtDomain => "deviceNtDomain",
|
30
|
+
:deviceDnsDomain => "deviceDnsDomain",
|
31
|
+
:deviceTranslatedAddress => "deviceTranslatedAddress",
|
32
|
+
:deviceMacAddress => "deviceMacAddress",
|
33
|
+
:deviceCustomNumber1 => "cn1",
|
34
|
+
:deviceCustomNumber2 => "cn2",
|
35
|
+
:deviceCustomNumber3 => "cn3",
|
36
|
+
:deviceCustomNumber1Label => "cn1Label",
|
37
|
+
:deviceCustomNumber2Label => "cn2Label",
|
38
|
+
:deviceCustomNumber3Label => "cn3Label",
|
39
|
+
:deviceCustomString1 => "cs1",
|
40
|
+
:deviceCustomString2 => "cs2",
|
41
|
+
:deviceCustomString3 => "cs3",
|
42
|
+
:deviceCustomString4 => "cs4",
|
43
|
+
:deviceCustomString5 => "cs5",
|
44
|
+
:deviceCustomString6 => "cs6",
|
45
|
+
:deviceCustomString1Label => "cs1Label",
|
46
|
+
:deviceCustomString2Label => "cs2Label",
|
47
|
+
:deviceCustomString3Label => "cs3Label",
|
48
|
+
:deviceCustomString4Label => "cs4Label",
|
49
|
+
:deviceCustomString5Label => "cs5Label",
|
50
|
+
:deviceCustomString6Label => "cs6Label",
|
51
|
+
:deviceCustomDate1 => "deviceCustomDate1",
|
52
|
+
:deviceCustomDate2 => "deviceCustomDate2",
|
53
|
+
:deviceCustomDate1Label => "deviceCustomDate1Label",
|
54
|
+
:deviceCustomDate2Label => "deviceCustomDate2Label",
|
55
|
+
:deviceEventCategory => "cat",
|
56
|
+
:destinationAddress => "dst",
|
57
|
+
:destinationDnsDomain => "destinationDnsDomain",
|
58
|
+
:destinationNtDomain => "dntdom",
|
59
|
+
:destinationHostName => "dhost",
|
60
|
+
:destinationMacAddress => "dmac",
|
61
|
+
:destinationPort => "dpt",
|
62
|
+
:destinationProcessName => "dproc",
|
63
|
+
:destinationServiceName => "destinationServiceName",
|
64
|
+
:destinationUserId => "duid",
|
65
|
+
:destinationUserPrivileges => "dpriv",
|
66
|
+
:destinationUserName => "duser",
|
67
|
+
:destinationTranslatedAddress => "destinationTranslatedAddress",
|
68
|
+
:destinationTranslatedPort => "destinationTranslatedPort",
|
69
|
+
:deviceDirection => "deviceDirection",
|
70
|
+
:deviceExternalId => "deviceExternalId",
|
71
|
+
:deviceFacility => "deviceFacility",
|
72
|
+
:deviceInboundInterface => "deviceInboundInterface",
|
73
|
+
:deviceOutboundInterface => "deviceOutboundInterface",
|
74
|
+
:deviceProcessName => "deviceProcessName",
|
75
|
+
:externalId => "externalId",
|
76
|
+
:fileHash => "fileHash",
|
77
|
+
:fileId => "fileId",
|
78
|
+
:fileName => "fname",
|
79
|
+
:filePath => "filePath",
|
80
|
+
:filePermission => "filePermission",
|
81
|
+
:fsize => "fsize",
|
82
|
+
:fileType => "fileType",
|
83
|
+
:message => "msg",
|
84
|
+
:oldfileHash => "oldfileHash",
|
85
|
+
:oldfileId => "oldfileId",
|
86
|
+
:oldFilename => "oldFilename",
|
87
|
+
:oldfilePath => "oldfilePath",
|
88
|
+
:oldfilePermission => "oldfilePermission",
|
89
|
+
:oldfsize => "oldfsize",
|
90
|
+
:oldfileType => "oldfileType",
|
91
|
+
:requestURL => "request",
|
92
|
+
:requestClientApplication => "requestClientApplication",
|
93
|
+
:requestCookies => "requestCookies",
|
94
|
+
:requestMethod => "requestMethod",
|
95
|
+
:sourceAddress => "src",
|
96
|
+
:sourceDnsDomain => "sourceDnsDomain",
|
97
|
+
:sourceHostName => "shost",
|
98
|
+
:sourceMacAddress => "smac",
|
99
|
+
:sourceNtDomain => "sntdom",
|
100
|
+
:sourcePort => "spt",
|
101
|
+
:sourceServiceName => "sourceServiceName",
|
102
|
+
:sourceTranslatedAddress => "sourceTranslatedAddress",
|
103
|
+
:sourceTranslatedPort => "sourceTranslatedPort",
|
104
|
+
:sourceUserPrivileges => "spriv",
|
105
|
+
:sourceUserId => "suid",
|
106
|
+
:sourceUserName => "suser",
|
107
|
+
:transportProtocol => "proto"
|
108
|
+
}
|
109
|
+
|
110
|
+
# these are tracked separately so they can be normalized during formatting
|
111
|
+
TIME_ATTRIBUTES={
|
112
|
+
:fileCreateTime => "fileCreateTime",
|
113
|
+
:fileModificationTime => "fileModificationTime",
|
114
|
+
:oldfileCreateTime => "oldfileCreateTime",
|
115
|
+
:oldfileModificationTime => "oldfileModificationTime",
|
116
|
+
:receiptTime => "rt",
|
117
|
+
:startTime => "start",
|
118
|
+
:endTime => "end"
|
119
|
+
}
|
120
|
+
|
121
|
+
ATTRIBUTES=PREFIX_ATTRIBUTES.merge EXTENSION_ATTRIBUTES.merge TIME_ATTRIBUTES
|
122
|
+
end
|
data/lib/cef/event.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
module CEF
|
2
|
+
class Event
|
3
|
+
attr_accessor :my_hostname, :syslog_pri
|
4
|
+
# set up accessors for all of the CEF event attributes. ruby meta magic.
|
5
|
+
CEF::ATTRIBUTES.each do |k,v|
|
6
|
+
self.instance_eval do
|
7
|
+
attr_accessor k
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def attrs
|
12
|
+
CEF::ATTRIBUTES
|
13
|
+
end
|
14
|
+
|
15
|
+
# so we can CEF::Event.new(:foo=>"bar")
|
16
|
+
def initialize( *params )
|
17
|
+
Hash[*params].each { |k,v| self.send("%s="%k,v) }
|
18
|
+
|
19
|
+
@my_hostname ||= Socket::gethostname
|
20
|
+
# used to avoid requiring syslog.h on windoze
|
21
|
+
#syslog_pri= Syslog::LOG_LOCAL0 | Syslog::LOG_NOTICE
|
22
|
+
@syslog_pri ||= 131
|
23
|
+
end
|
24
|
+
|
25
|
+
# returns a cef formatted string
|
26
|
+
def format_cef
|
27
|
+
cef_message=CEF::PREFIX_FORMAT % [
|
28
|
+
syslog_pri.to_s,
|
29
|
+
my_hostname,
|
30
|
+
Time.new.strftime("%b %d %Y %H:%M:%S"),
|
31
|
+
format_prefix,
|
32
|
+
format_extension
|
33
|
+
]
|
34
|
+
cef_message
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
# make a guess as to how the time was set. parse strings and convert
|
39
|
+
# them to epoch milliseconds, or leave it alone if it looks like a number
|
40
|
+
# bigger than epoch milliseconds when i wrote this.
|
41
|
+
def time_convert(val)
|
42
|
+
converted=nil
|
43
|
+
#puts "converting time for #{val.class.to_s}/#{val}"
|
44
|
+
case val.class.to_s
|
45
|
+
when "String"
|
46
|
+
begin
|
47
|
+
converted=val.to_i
|
48
|
+
rescue
|
49
|
+
res=ParseDate.parsedate(val)
|
50
|
+
converted=Time.local(*res).to_i * 1000
|
51
|
+
end
|
52
|
+
when "Integer","Bignum"
|
53
|
+
if val < 1232589621000 #Wed Jan 21 20:00:21 -0600 2009
|
54
|
+
converted=val * 1000
|
55
|
+
else
|
56
|
+
converted=val
|
57
|
+
end
|
58
|
+
end
|
59
|
+
converted
|
60
|
+
end
|
61
|
+
|
62
|
+
# escape only pipes and backslashes in the prefix. you bet your sweet
|
63
|
+
# ass there's a lot of backslashes in the substitution. you can thank
|
64
|
+
# the three levels of lexical analysis/substitution in the ruby interpreter
|
65
|
+
# for that.
|
66
|
+
def prefix_escape(val)
|
67
|
+
val.gsub(/(\||\\)/,'\\\\\&')
|
68
|
+
end
|
69
|
+
|
70
|
+
# only equals signs need to be escaped in the extension. i think.
|
71
|
+
# TODO: something in the spec about \n and some others.
|
72
|
+
def extension_escape(val)
|
73
|
+
val.gsub(/=/,'\=').gsub(/\n/,' ')
|
74
|
+
end
|
75
|
+
|
76
|
+
# returns a pipe-delimeted list of prefix attributes
|
77
|
+
def format_prefix
|
78
|
+
vendor= self.deviceVendor || "Breed"
|
79
|
+
product= self.deviceProduct || "CEF Sender"
|
80
|
+
version= self.deviceVersion || CEF::VERSION
|
81
|
+
declid= self.deviceEventClassId || "generic:0"
|
82
|
+
name= self.name || "Generic Event"
|
83
|
+
sev= self.deviceSeverity || "1"
|
84
|
+
cef_prefix="%s|%s|%s|%s|%s|%s" % [
|
85
|
+
prefix_escape(vendor),
|
86
|
+
prefix_escape(product),
|
87
|
+
prefix_escape(version),
|
88
|
+
prefix_escape(declid),
|
89
|
+
prefix_escape(name),
|
90
|
+
prefix_escape(sev),
|
91
|
+
]
|
92
|
+
end
|
93
|
+
|
94
|
+
# returns a space-delimeted list of attribute=value pairs for all optionals
|
95
|
+
def format_extension
|
96
|
+
avpairs=[]
|
97
|
+
CEF::EXTENSION_ATTRIBUTES.each do |attribute,shortname|
|
98
|
+
unless self.send(attribute).nil?
|
99
|
+
avpairs.push(
|
100
|
+
"%s=%s" % [ shortname, extension_escape(self.send(attribute)) ]
|
101
|
+
)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# make sure time comes out as milliseconds since epoch
|
106
|
+
CEF::TIME_ATTRIBUTES.each do |attribute,shortname|
|
107
|
+
unless self.send(attribute).nil?
|
108
|
+
avpairs.push(
|
109
|
+
"%s=%s" % [ shortname, time_convert(self.send(attribute)) ]
|
110
|
+
)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
avpairs.join(" ")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
data/lib/cef/sender.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module CEF
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
class Sender
|
5
|
+
attr_accessor :receiver, :receiverPort, :eventDefaults
|
6
|
+
attr_reader :sock
|
7
|
+
def initialize(*args)
|
8
|
+
Hash[*args].each { |argname, argval| self.send(("%s="%argname), argval) }
|
9
|
+
@sock=nil
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
#TODO: Implement relp/tcp senders
|
14
|
+
|
15
|
+
class UDPSender < Sender
|
16
|
+
|
17
|
+
#fire the message off
|
18
|
+
def emit(event)
|
19
|
+
self.socksetup if self.sock.nil?
|
20
|
+
# process eventDefaults - we are expecting a hash here. These will
|
21
|
+
# override any values in the events passed to us. i know. brutal.
|
22
|
+
unless self.eventDefaults.nil?
|
23
|
+
self.eventDefaults.each do |k,v|
|
24
|
+
event.send("%s=" % k,v)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
self.sock.send event.format_cef, 0
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def socksetup
|
32
|
+
@sock=UDPSocket.new
|
33
|
+
receiver= self.receiver || "127.0.0.1"
|
34
|
+
port= self.receiverPort || 514
|
35
|
+
@sock.connect(receiver,port)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/spec/cef_spec.rb
CHANGED
@@ -5,8 +5,9 @@ describe "CEF Event Format" do
|
|
5
5
|
prefix_vals=test_prefix_vals
|
6
6
|
e=CEF::Event.new
|
7
7
|
prefix_vals.each {|k,v| e.send("%s="%k,v) }
|
8
|
-
s=CEF::Sender.new
|
9
8
|
formatted=CEF::PREFIX_FORMAT % [ 131, *prefix_vals.values ]
|
10
|
-
|
9
|
+
|
10
|
+
e.format_cef==formatted
|
11
11
|
end
|
12
|
+
|
12
13
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cef
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 3
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 7
|
9
|
+
- 0
|
10
|
+
version: 0.7.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ryan Breed
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-03-11 00:00:00 -06:00
|
19
19
|
default_executable: cef_sender
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -114,7 +114,12 @@ files:
|
|
114
114
|
- Rakefile
|
115
115
|
- VERSION
|
116
116
|
- bin/cef_sender
|
117
|
+
- cef.gemspec
|
117
118
|
- lib/cef.rb
|
119
|
+
- lib/cef/constants.rb
|
120
|
+
- lib/cef/event.rb
|
121
|
+
- lib/cef/file_logger.rb
|
122
|
+
- lib/cef/sender.rb
|
118
123
|
- spec/cef_spec.rb
|
119
124
|
- spec/spec_helper.rb
|
120
125
|
has_rdoc: true
|