patronus_fati 0.8.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.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.yardopts +1 -0
- data/Gemfile +4 -0
- data/LICENSE +165 -0
- data/README.md +29 -0
- data/Rakefile +21 -0
- data/bin/patronus_fati +54 -0
- data/kismet_configs/pat_fat_startup.sh +3 -0
- data/kismet_configs/patronus_fati_kismet.conf +88 -0
- data/lib/patronus_fati/cap_struct.rb +97 -0
- data/lib/patronus_fati/connection.rb +85 -0
- data/lib/patronus_fati/consts.rb +85 -0
- data/lib/patronus_fati/data_mapper/crypt_flags.rb +83 -0
- data/lib/patronus_fati/data_mapper/null_table_prefix.rb +7 -0
- data/lib/patronus_fati/data_models/access_point.rb +74 -0
- data/lib/patronus_fati/data_models/alert.rb +24 -0
- data/lib/patronus_fati/data_models/ap_frequency.rb +14 -0
- data/lib/patronus_fati/data_models/ap_signal.rb +12 -0
- data/lib/patronus_fati/data_models/client.rb +69 -0
- data/lib/patronus_fati/data_models/client_frequency.rb +14 -0
- data/lib/patronus_fati/data_models/client_signal.rb +12 -0
- data/lib/patronus_fati/data_models/common.rb +49 -0
- data/lib/patronus_fati/data_models/connection.rb +48 -0
- data/lib/patronus_fati/data_models/mac.rb +48 -0
- data/lib/patronus_fati/data_models/probe.rb +13 -0
- data/lib/patronus_fati/data_models/ssid.rb +35 -0
- data/lib/patronus_fati/data_observers/access_point_observer.rb +53 -0
- data/lib/patronus_fati/data_observers/alert_observer.rb +12 -0
- data/lib/patronus_fati/data_observers/client_observer.rb +52 -0
- data/lib/patronus_fati/data_observers/connection_observer.rb +66 -0
- data/lib/patronus_fati/data_observers/probe_observer.rb +11 -0
- data/lib/patronus_fati/data_observers/ssid_observer.rb +53 -0
- data/lib/patronus_fati/event_handler.rb +27 -0
- data/lib/patronus_fati/factory_base.rb +56 -0
- data/lib/patronus_fati/message_models/ack.rb +5 -0
- data/lib/patronus_fati/message_models/alert.rb +10 -0
- data/lib/patronus_fati/message_models/battery.rb +6 -0
- data/lib/patronus_fati/message_models/bssid.rb +43 -0
- data/lib/patronus_fati/message_models/bssidsrc.rb +15 -0
- data/lib/patronus_fati/message_models/btscandev.rb +11 -0
- data/lib/patronus_fati/message_models/capability.rb +5 -0
- data/lib/patronus_fati/message_models/channel.rb +13 -0
- data/lib/patronus_fati/message_models/client.rb +45 -0
- data/lib/patronus_fati/message_models/clisrc.rb +17 -0
- data/lib/patronus_fati/message_models/clitag.rb +6 -0
- data/lib/patronus_fati/message_models/common.rb +15 -0
- data/lib/patronus_fati/message_models/critfail.rb +5 -0
- data/lib/patronus_fati/message_models/error.rb +6 -0
- data/lib/patronus_fati/message_models/gps.rb +6 -0
- data/lib/patronus_fati/message_models/info.rb +11 -0
- data/lib/patronus_fati/message_models/kismet.rb +8 -0
- data/lib/patronus_fati/message_models/nettag.rb +6 -0
- data/lib/patronus_fati/message_models/packet.rb +10 -0
- data/lib/patronus_fati/message_models/plugin.rb +8 -0
- data/lib/patronus_fati/message_models/protocols.rb +5 -0
- data/lib/patronus_fati/message_models/remove.rb +6 -0
- data/lib/patronus_fati/message_models/source.rb +10 -0
- data/lib/patronus_fati/message_models/spectrum.rb +8 -0
- data/lib/patronus_fati/message_models/ssid.rb +25 -0
- data/lib/patronus_fati/message_models/status.rb +5 -0
- data/lib/patronus_fati/message_models/string.rb +6 -0
- data/lib/patronus_fati/message_models/terminate.rb +5 -0
- data/lib/patronus_fati/message_models/time.rb +6 -0
- data/lib/patronus_fati/message_models/trackinfo.rb +8 -0
- data/lib/patronus_fati/message_models/wepkey.rb +6 -0
- data/lib/patronus_fati/message_models.rb +39 -0
- data/lib/patronus_fati/message_parser.rb +44 -0
- data/lib/patronus_fati/message_processor/alert.rb +15 -0
- data/lib/patronus_fati/message_processor/bssid.rb +47 -0
- data/lib/patronus_fati/message_processor/capability.rb +24 -0
- data/lib/patronus_fati/message_processor/client.rb +55 -0
- data/lib/patronus_fati/message_processor/critfail.rb +8 -0
- data/lib/patronus_fati/message_processor/error.rb +7 -0
- data/lib/patronus_fati/message_processor/protocols.rb +7 -0
- data/lib/patronus_fati/message_processor/ssid.rb +48 -0
- data/lib/patronus_fati/message_processor.rb +52 -0
- data/lib/patronus_fati/version.rb +3 -0
- data/lib/patronus_fati.rb +68 -0
- data/patronus_fati.gemspec +41 -0
- data/spec/data_models/access_point_spec.rb +26 -0
- data/spec/data_models/alert_spec.rb +12 -0
- data/spec/data_models/client_spec.rb +25 -0
- data/spec/data_models/connection_spec.rb +86 -0
- data/spec/data_models/mac_spec.rb +26 -0
- data/spec/patronus_fati_spec.rb +13 -0
- data/spec/spec_helper.rb +71 -0
- data/wrapper.rb +19 -0
- metadata +393 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
require 'dm-core'
|
|
2
|
+
|
|
3
|
+
module DataMapper
|
|
4
|
+
class Property
|
|
5
|
+
class CryptFlags < DataMapper::Property::Integer
|
|
6
|
+
def self.flags
|
|
7
|
+
PatronusFati::SSID_CRYPT_MAP.values.map(&:to_sym)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
attr_reader :flag_map
|
|
11
|
+
|
|
12
|
+
def custom?
|
|
13
|
+
true
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
#def dump(value)
|
|
17
|
+
# unless value.nil?
|
|
18
|
+
# flags = Array(value).map(&:to_s)
|
|
19
|
+
# flags.uniq!
|
|
20
|
+
|
|
21
|
+
# valid_values = flags & PatronusFati::SSID_CRYPT_MAP.values
|
|
22
|
+
# PatronusFati::SSID_CRYPT_MAP.map { |k, v| valid_values.include?(v) ? k : 0 }.inject(&:+)
|
|
23
|
+
# end
|
|
24
|
+
#end
|
|
25
|
+
|
|
26
|
+
def dump(value)
|
|
27
|
+
unless value.nil?
|
|
28
|
+
flags = Array(value).map { |flag| flag.to_sym }
|
|
29
|
+
flags.uniq!
|
|
30
|
+
|
|
31
|
+
flag = 0
|
|
32
|
+
|
|
33
|
+
flag_map.invert.values_at(*flags).each do |i|
|
|
34
|
+
next if i.nil?
|
|
35
|
+
flag += (1 << i)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
flag
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def initialize(model, name, options = {})
|
|
43
|
+
super
|
|
44
|
+
|
|
45
|
+
@flag_map = {}
|
|
46
|
+
|
|
47
|
+
flags = options.fetch(:flags, self.class.flags)
|
|
48
|
+
flags.each_with_index do |flag, i|
|
|
49
|
+
flag_map[i] = flag
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
#def load(value)
|
|
54
|
+
# return [] if value.nil?
|
|
55
|
+
# PatronusFati::SSID_CRYPT_MAP.select { |k, v| (k & value) == k }.map { |k, v| v }
|
|
56
|
+
#end
|
|
57
|
+
|
|
58
|
+
def load(value)
|
|
59
|
+
return [] if value.nil? || value <= 0
|
|
60
|
+
|
|
61
|
+
begin
|
|
62
|
+
matches = []
|
|
63
|
+
|
|
64
|
+
0.upto(flag_map.size - 1) do |i|
|
|
65
|
+
matches << flag_map[i] if value[i] == 1
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
matches.compact
|
|
69
|
+
rescue TypeError, Errno::EDOM
|
|
70
|
+
[]
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def typecast(value)
|
|
75
|
+
case value
|
|
76
|
+
when nil then nil
|
|
77
|
+
when ::Array then value.map { |v| v.to_sym }
|
|
78
|
+
else [value.to_sym]
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module PatronusFati::DataModels
|
|
2
|
+
class AccessPoint
|
|
3
|
+
include DataMapper::Resource
|
|
4
|
+
|
|
5
|
+
include PatronusFati::DataModels::ExpirationAttributes
|
|
6
|
+
include PatronusFati::DataModels::ReportedAttributes
|
|
7
|
+
|
|
8
|
+
property :id, Serial
|
|
9
|
+
|
|
10
|
+
property :bssid, String, :length => 17,
|
|
11
|
+
:required => true,
|
|
12
|
+
:unique => true
|
|
13
|
+
|
|
14
|
+
property :channel, Integer
|
|
15
|
+
property :max_seen_rate, Integer
|
|
16
|
+
property :type, String, :required => true
|
|
17
|
+
|
|
18
|
+
property :duplicate_iv_pkts, Integer, :default => 0
|
|
19
|
+
property :crypt_packets, Integer, :default => 0
|
|
20
|
+
property :data_packets, Integer, :default => 0
|
|
21
|
+
property :data_size, Integer, :default => 0
|
|
22
|
+
|
|
23
|
+
property :fragments, Integer, :default => 0
|
|
24
|
+
property :retries, Integer, :default => 0
|
|
25
|
+
|
|
26
|
+
property :range_ip, String
|
|
27
|
+
property :netmask, String
|
|
28
|
+
property :gateway_ip, String
|
|
29
|
+
|
|
30
|
+
has n, :clients, :through => :connections
|
|
31
|
+
has n, :connections, :constraint => :destroy,
|
|
32
|
+
:child_key => :access_point_id
|
|
33
|
+
has n, :ssids, :constraint => :destroy
|
|
34
|
+
has n, :ap_frequencies, :constraint => :destroy
|
|
35
|
+
has n, :ap_signals, :constraint => :destroy
|
|
36
|
+
|
|
37
|
+
belongs_to :mac, :required => false
|
|
38
|
+
before :save do
|
|
39
|
+
self.mac = Mac.first_or_create(mac: bssid)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.current_expiration_threshold
|
|
43
|
+
Time.now.to_i - PatronusFati::AP_EXPIRATION
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def connected_clients
|
|
47
|
+
connections.active.clients
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def current_ssids
|
|
51
|
+
ssids.active
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def disconnect_clients!
|
|
55
|
+
connections.connected.map(&:disconnect!)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def full_state
|
|
59
|
+
blacklisted_keys = %w(id last_seen_at reported_online).map(&:to_sym)
|
|
60
|
+
attributes.reject { |k, v| blacklisted_keys.include?(k) || v.nil? }.merge(vendor: mac.vendor)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def record_signal(dbm)
|
|
64
|
+
PatronusFati::DataModels::ApSignal.create(access_point: self, dbm: dbm)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def update_frequencies(freq_hsh)
|
|
68
|
+
freq_hsh.each do |freq, packet_count|
|
|
69
|
+
f = ap_frequencies.first_or_create({mhz: freq}, {packet_count: packet_count})
|
|
70
|
+
f.update({packet_count: packet_count})
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module PatronusFati::DataModels
|
|
2
|
+
class Alert
|
|
3
|
+
include DataMapper::Resource
|
|
4
|
+
|
|
5
|
+
property :id, Serial
|
|
6
|
+
property :created_at, Integer, :default => Proc.new { Time.now.to_i }
|
|
7
|
+
property :message, String, :length => 255
|
|
8
|
+
|
|
9
|
+
belongs_to :src_mac, :model => 'Mac', :required => false
|
|
10
|
+
belongs_to :dst_mac, :model => 'Mac', :required => false
|
|
11
|
+
belongs_to :other_mac, :model => 'Mac', :required => false
|
|
12
|
+
|
|
13
|
+
def full_state
|
|
14
|
+
{
|
|
15
|
+
created_at: created_at,
|
|
16
|
+
message: message,
|
|
17
|
+
|
|
18
|
+
source: src_mac.mac,
|
|
19
|
+
destination: dst_mac.mac,
|
|
20
|
+
other: other_mac.mac
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module PatronusFati::DataModels
|
|
2
|
+
class ApFrequency
|
|
3
|
+
include DataMapper::Resource
|
|
4
|
+
|
|
5
|
+
property :mhz, Integer, :key => true,
|
|
6
|
+
:required => true
|
|
7
|
+
property :access_point_id, Integer, :key => true,
|
|
8
|
+
:required => true
|
|
9
|
+
property :packet_count, Integer, :default => 0,
|
|
10
|
+
:required => true
|
|
11
|
+
|
|
12
|
+
belongs_to :access_point
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module PatronusFati::DataModels
|
|
2
|
+
class ApSignal
|
|
3
|
+
include DataMapper::Resource
|
|
4
|
+
|
|
5
|
+
property :id, Serial
|
|
6
|
+
property :timestamp, Integer, :default => Proc.new { Time.now.to_i },
|
|
7
|
+
:required => true
|
|
8
|
+
property :dbm, Integer, :required => true
|
|
9
|
+
|
|
10
|
+
belongs_to :access_point
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module PatronusFati::DataModels
|
|
2
|
+
class Client
|
|
3
|
+
include DataMapper::Resource
|
|
4
|
+
|
|
5
|
+
include PatronusFati::DataModels::ExpirationAttributes
|
|
6
|
+
include PatronusFati::DataModels::ReportedAttributes
|
|
7
|
+
|
|
8
|
+
property :id, Serial
|
|
9
|
+
property :bssid, String, :length => 17, :unique => true
|
|
10
|
+
property :channel, Integer
|
|
11
|
+
|
|
12
|
+
property :crypt_packets, Integer, :default => 0
|
|
13
|
+
property :data_packets, Integer, :default => 0
|
|
14
|
+
property :data_size, Integer, :default => 0
|
|
15
|
+
property :fragments, Integer, :default => 0
|
|
16
|
+
property :retries, Integer, :default => 0
|
|
17
|
+
|
|
18
|
+
property :max_seen_rate, Integer
|
|
19
|
+
|
|
20
|
+
property :ip, String
|
|
21
|
+
property :gateway_ip, String
|
|
22
|
+
property :dhcp_host, String, :length => 64
|
|
23
|
+
|
|
24
|
+
has n, :connections, :constraint => :destroy
|
|
25
|
+
has n, :access_points, :through => :connections
|
|
26
|
+
|
|
27
|
+
has n, :client_frequencies, :constraint => :destroy
|
|
28
|
+
has n, :client_signals, :constraint => :destroy
|
|
29
|
+
has n, :probes, :constraint => :destroy
|
|
30
|
+
|
|
31
|
+
belongs_to :mac, :required => false
|
|
32
|
+
before :save do
|
|
33
|
+
self.mac = Mac.first_or_create(mac: bssid)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.current_expiration_threshold
|
|
37
|
+
Time.now.to_i - PatronusFati::CLIENT_EXPIRATION
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def connected_access_points
|
|
41
|
+
connections.active.access_points
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def disconnect!
|
|
45
|
+
connections.connected.map(&:disconnect!)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def full_state
|
|
49
|
+
blacklisted_keys = %w(id last_seen_at reported_online).map(&:to_sym)
|
|
50
|
+
base_attrs = attributes.reject { |k, v| blacklisted_keys.include?(k) || v.nil? }
|
|
51
|
+
base_attrs.merge(
|
|
52
|
+
connected_access_points: connected_access_points.map(&:bssid),
|
|
53
|
+
probes: probes.map(&:essid),
|
|
54
|
+
vendor: mac.vendor
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def record_signal(dbm)
|
|
59
|
+
PatronusFati::DataModels::ClientSignal.create(client: self, dbm: dbm)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def update_frequencies(freq_hsh)
|
|
63
|
+
freq_hsh.each do |freq, packet_count|
|
|
64
|
+
f = client_frequencies.first_or_create({mhz: freq}, {packet_count: packet_count})
|
|
65
|
+
f.update({packet_count: packet_count})
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module PatronusFati::DataModels
|
|
2
|
+
class ClientFrequency
|
|
3
|
+
include DataMapper::Resource
|
|
4
|
+
|
|
5
|
+
property :mhz, Integer, :key => true,
|
|
6
|
+
:required => true
|
|
7
|
+
property :client_id, Integer, :key => true,
|
|
8
|
+
:required => true
|
|
9
|
+
property :packet_count, Integer, :default => 0,
|
|
10
|
+
:required => true
|
|
11
|
+
|
|
12
|
+
belongs_to :client
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module PatronusFati::DataModels
|
|
2
|
+
class ClientSignal
|
|
3
|
+
include DataMapper::Resource
|
|
4
|
+
|
|
5
|
+
property :id, Serial
|
|
6
|
+
property :timestamp, Integer, :default => Proc.new { Time.now.to_i },
|
|
7
|
+
:required => true
|
|
8
|
+
property :dbm, Integer, :required => true
|
|
9
|
+
|
|
10
|
+
belongs_to :client
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module PatronusFati
|
|
2
|
+
module DataModels
|
|
3
|
+
module ReportedAttributes
|
|
4
|
+
def self.included(klass)
|
|
5
|
+
klass.extend RAClassMethods
|
|
6
|
+
klass.property :reported_online, DataMapper::Property::Boolean, :default => false
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module RAClassMethods
|
|
10
|
+
def reported_offline
|
|
11
|
+
all(:reported_online => false)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def reported_online
|
|
15
|
+
all(:reported_online => true)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
module ExpirationAttributes
|
|
21
|
+
def self.included(klass)
|
|
22
|
+
klass.extend EAClassMethods
|
|
23
|
+
klass.property :last_seen_at, DataMapper::Property::Integer, :default => Proc.new { Time.now.to_i }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def active?
|
|
27
|
+
last_seen_at < self.class.current_expiration_threshold
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def seen!
|
|
31
|
+
update(last_seen_at: Time.now.to_i)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def uptime
|
|
35
|
+
Time.now.to_i - last_seen_at
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module EAClassMethods
|
|
39
|
+
def active
|
|
40
|
+
all(:last_seen_at.gte => current_expiration_threshold)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def inactive
|
|
44
|
+
all(:last_seen_at.lt => current_expiration_threshold)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module PatronusFati::DataModels
|
|
2
|
+
class Connection
|
|
3
|
+
include DataMapper::Resource
|
|
4
|
+
|
|
5
|
+
include PatronusFati::DataModels::ExpirationAttributes
|
|
6
|
+
|
|
7
|
+
property :id, Serial
|
|
8
|
+
|
|
9
|
+
property :connected_at, Integer, :default => Proc.new { Time.now.to_i }
|
|
10
|
+
property :disconnected_at, Integer, :default => Proc.new { Time.now.to_i }
|
|
11
|
+
property :duration, Integer
|
|
12
|
+
|
|
13
|
+
belongs_to :access_point
|
|
14
|
+
belongs_to :client
|
|
15
|
+
|
|
16
|
+
def self.connected
|
|
17
|
+
all(:disconnected_at => nil)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.disconnected
|
|
21
|
+
all(:disconnected_at.not => nil)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.current_expiration_threshold
|
|
25
|
+
Time.now.to_i - PatronusFati::CONNECTION_EXPIRATION
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def connected?
|
|
29
|
+
disconnected_at.nil?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def disconnect!
|
|
33
|
+
update(disconnected_at: Time.now.to_i, duration: duration) if connected?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def duration
|
|
37
|
+
self[:duration] || (Time.now.to_i - connected_at)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def full_state
|
|
41
|
+
{
|
|
42
|
+
access_point: access_point.bssid,
|
|
43
|
+
client: client.bssid,
|
|
44
|
+
connected: connected?
|
|
45
|
+
}
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module PatronusFati::DataModels
|
|
2
|
+
class Mac
|
|
3
|
+
include DataMapper::Resource
|
|
4
|
+
|
|
5
|
+
property :id, Serial
|
|
6
|
+
|
|
7
|
+
property :mac, String, :length => 17, :unique => true
|
|
8
|
+
property :vendor, String, :length => 255
|
|
9
|
+
|
|
10
|
+
property :alert_count, Integer, :default => 0
|
|
11
|
+
property :clients_connected, Integer, :default => 0
|
|
12
|
+
property :active_ssids, Integer, :default => 0
|
|
13
|
+
property :is_client, Boolean, :default => false
|
|
14
|
+
property :connections_to_ap, Integer, :default => 0
|
|
15
|
+
|
|
16
|
+
has n, :access_points
|
|
17
|
+
has n, :clients
|
|
18
|
+
|
|
19
|
+
has n, :dst_alerts, :model => 'Alert', :child_key => :dst_mac_id
|
|
20
|
+
has n, :other_alerts, :model => 'Alert', :child_key => :other_mac_id
|
|
21
|
+
has n, :src_alerts, :model => 'Alert', :child_key => :src_mac_id
|
|
22
|
+
|
|
23
|
+
before :save do
|
|
24
|
+
next if self.vendor
|
|
25
|
+
|
|
26
|
+
result = Louis.lookup(mac)
|
|
27
|
+
self.vendor = result['long_vendor'] || result['short_vendor']
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def is_ap?
|
|
31
|
+
access_points.active.any?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def is_client?
|
|
35
|
+
clients.active.any?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def update_cached_counts!
|
|
39
|
+
update(
|
|
40
|
+
alert_count: (dst_alerts | other_alerts | src_alerts).count,
|
|
41
|
+
active_ssids: access_points.ssids.active.count,
|
|
42
|
+
clients_connected: access_points.connections.connected.count,
|
|
43
|
+
connections_to_ap: clients.connections.connected.count,
|
|
44
|
+
is_client: is_client?
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module PatronusFati::DataModels
|
|
2
|
+
class Probe
|
|
3
|
+
include DataMapper::Resource
|
|
4
|
+
|
|
5
|
+
property :client_id, Integer, :key => true
|
|
6
|
+
property :essid, String, :key => true, :length => 64
|
|
7
|
+
|
|
8
|
+
property :first_seen_at, Integer, :default => Proc.new { Time.now.to_i }
|
|
9
|
+
property :last_seen_at, Integer, :default => Proc.new { Time.now.to_i }
|
|
10
|
+
|
|
11
|
+
belongs_to :client
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module PatronusFati::DataModels
|
|
2
|
+
class Ssid
|
|
3
|
+
include DataMapper::Resource
|
|
4
|
+
|
|
5
|
+
include PatronusFati::DataModels::ExpirationAttributes
|
|
6
|
+
include PatronusFati::DataModels::ReportedAttributes
|
|
7
|
+
|
|
8
|
+
property :id, Serial
|
|
9
|
+
|
|
10
|
+
property :beacon_rate, Integer
|
|
11
|
+
property :beacon_info, String
|
|
12
|
+
|
|
13
|
+
property :cloaked, Boolean, :default => false
|
|
14
|
+
property :essid, String, :length => 64
|
|
15
|
+
property :crypt_set, CryptFlags
|
|
16
|
+
property :max_rate, Integer
|
|
17
|
+
|
|
18
|
+
belongs_to :access_point
|
|
19
|
+
|
|
20
|
+
def self.current_expiration_threshold
|
|
21
|
+
Time.now.to_i - PatronusFati::SSID_EXPIRATION
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def full_state
|
|
25
|
+
{
|
|
26
|
+
beacon_info: beacon_info,
|
|
27
|
+
beacon_rate: beacon_rate,
|
|
28
|
+
cloaked: cloaked,
|
|
29
|
+
crypt_set: crypt_set,
|
|
30
|
+
essid: essid,
|
|
31
|
+
max_rate: max_rate
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module PatronusFati::DataObservers
|
|
2
|
+
class AccessPointObserver
|
|
3
|
+
include DataMapper::Observer
|
|
4
|
+
|
|
5
|
+
observe PatronusFati::DataModels::AccessPoint
|
|
6
|
+
|
|
7
|
+
before :save do
|
|
8
|
+
next unless self.valid?
|
|
9
|
+
|
|
10
|
+
self.reported_online = active?
|
|
11
|
+
|
|
12
|
+
@change_type = self.new? ? :new : :changed
|
|
13
|
+
|
|
14
|
+
if @change_type == :changed
|
|
15
|
+
dirty = self.dirty_attributes.map { |a| a.first.name }.map(&:to_s)
|
|
16
|
+
dirty.select! { |k, _| full_state.keys.include?(k) || k == 'reported_online' }
|
|
17
|
+
|
|
18
|
+
# If there weren't any meaningful changes, don't print out anything
|
|
19
|
+
# after we save.
|
|
20
|
+
if dirty.empty?
|
|
21
|
+
@change_type = nil
|
|
22
|
+
next
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
changes = dirty.map do |attr|
|
|
26
|
+
clean = original_attributes[PatronusFati::DataModels::AccessPoint.properties[attr]]
|
|
27
|
+
dirty = dirty_attributes[PatronusFati::DataModels::AccessPoint.properties[attr]]
|
|
28
|
+
|
|
29
|
+
[attr, [clean, dirty]]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
@change_list = Hash[changes]
|
|
33
|
+
@change_list.delete('reported_online')
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
after :save do
|
|
38
|
+
next unless @change_type
|
|
39
|
+
|
|
40
|
+
mac.update_cached_counts!
|
|
41
|
+
|
|
42
|
+
PatronusFati.event_handler.event(
|
|
43
|
+
:access_point,
|
|
44
|
+
@change_type,
|
|
45
|
+
self.full_state,
|
|
46
|
+
@change_list || {}
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
@change_type = nil
|
|
50
|
+
@change_list = nil
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module PatronusFati::DataObservers
|
|
2
|
+
class AlertObserver
|
|
3
|
+
include DataMapper::Observer
|
|
4
|
+
|
|
5
|
+
observe PatronusFati::DataModels::Alert
|
|
6
|
+
|
|
7
|
+
after :save do
|
|
8
|
+
[src_mac, dst_mac, other_mac].uniq.map(&:update_cached_counts!)
|
|
9
|
+
PatronusFati.event_handler.event(:alert, :new, self.full_state)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module PatronusFati::DataObservers
|
|
2
|
+
class ClientObserver
|
|
3
|
+
include DataMapper::Observer
|
|
4
|
+
|
|
5
|
+
observe PatronusFati::DataModels::Client
|
|
6
|
+
|
|
7
|
+
before :save do
|
|
8
|
+
break unless self.valid?
|
|
9
|
+
|
|
10
|
+
self.reported_online = active?
|
|
11
|
+
|
|
12
|
+
@change_type = self.new? ? :new : :changed
|
|
13
|
+
if @change_type == :changed
|
|
14
|
+
dirty = self.dirty_attributes.map { |a| a.first.name }.map(&:to_s)
|
|
15
|
+
dirty.select! { |k, _| full_state.keys.include?(k) || k == 'reported_online' }
|
|
16
|
+
|
|
17
|
+
# If there weren't any meaningful changes, don't print out anything
|
|
18
|
+
# after we save.
|
|
19
|
+
if dirty.empty?
|
|
20
|
+
@change_type = nil
|
|
21
|
+
next
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
changes = dirty.map do |attr|
|
|
25
|
+
clean = original_attributes[PatronusFati::DataModels::Client.properties[attr]]
|
|
26
|
+
dirty = dirty_attributes[PatronusFati::DataModels::Client.properties[attr]]
|
|
27
|
+
|
|
28
|
+
[attr, [clean, dirty]]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
@change_list = Hash[changes]
|
|
32
|
+
@change_list.delete('reported_online')
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
after :save do
|
|
37
|
+
next unless @change_type
|
|
38
|
+
|
|
39
|
+
mac.update_cached_counts!
|
|
40
|
+
|
|
41
|
+
PatronusFati.event_handler.event(
|
|
42
|
+
:client,
|
|
43
|
+
@change_type,
|
|
44
|
+
self.full_state,
|
|
45
|
+
@change_list || {}
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
@change_type = nil
|
|
49
|
+
@change_list = nil
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|