packetgen-plugin-smb 0.3.0 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/specs.yml +28 -0
  3. data/.rubocop.yml +8 -1
  4. data/Gemfile +15 -3
  5. data/README.md +59 -3
  6. data/Rakefile +10 -4
  7. data/examples/llmnr-responder +110 -0
  8. data/examples/smb-responder +233 -0
  9. data/lib/packetgen-plugin-smb.rb +5 -2
  10. data/lib/packetgen/plugin/gssapi.rb +11 -6
  11. data/lib/packetgen/plugin/llmnr.rb +58 -0
  12. data/lib/packetgen/plugin/netbios.rb +19 -0
  13. data/lib/packetgen/plugin/netbios/datagram.rb +108 -0
  14. data/lib/packetgen/plugin/netbios/name.rb +64 -0
  15. data/lib/packetgen/plugin/netbios/session.rb +72 -0
  16. data/lib/packetgen/plugin/ntlm.rb +211 -0
  17. data/lib/packetgen/plugin/ntlm/authenticate.rb +197 -0
  18. data/lib/packetgen/plugin/ntlm/av_pair.rb +115 -0
  19. data/lib/packetgen/plugin/ntlm/challenge.rb +140 -0
  20. data/lib/packetgen/plugin/ntlm/negotiate.rb +127 -0
  21. data/lib/packetgen/plugin/ntlm/ntlmv2_response.rb +59 -0
  22. data/lib/packetgen/plugin/smb.rb +27 -15
  23. data/lib/packetgen/plugin/smb/blocks.rb +2 -4
  24. data/lib/packetgen/plugin/smb/browser.rb +8 -8
  25. data/lib/packetgen/plugin/smb/browser/domain_announcement.rb +2 -7
  26. data/lib/packetgen/plugin/smb/browser/host_announcement.rb +10 -7
  27. data/lib/packetgen/plugin/smb/browser/local_master_announcement.rb +2 -7
  28. data/lib/packetgen/plugin/smb/close.rb +2 -2
  29. data/lib/packetgen/plugin/smb/close/request.rb +3 -3
  30. data/lib/packetgen/plugin/smb/close/response.rb +3 -3
  31. data/lib/packetgen/plugin/smb/filetime.rb +30 -3
  32. data/lib/packetgen/plugin/smb/negotiate.rb +20 -0
  33. data/lib/packetgen/plugin/smb/negotiate/dialect.rb +39 -0
  34. data/lib/packetgen/plugin/smb/negotiate/request.rb +35 -0
  35. data/lib/packetgen/plugin/smb/negotiate/response.rb +29 -0
  36. data/lib/packetgen/plugin/smb/nt_create_and_x.rb +2 -2
  37. data/lib/packetgen/plugin/smb/ntcreateandx/request.rb +5 -5
  38. data/lib/packetgen/plugin/smb/ntcreateandx/response.rb +3 -3
  39. data/lib/packetgen/plugin/smb/string.rb +60 -23
  40. data/lib/packetgen/plugin/smb/trans.rb +2 -2
  41. data/lib/packetgen/plugin/smb/trans/request.rb +4 -4
  42. data/lib/packetgen/plugin/smb/trans/response.rb +3 -3
  43. data/lib/packetgen/plugin/smb2.rb +20 -9
  44. data/lib/packetgen/plugin/smb2/base.rb +5 -7
  45. data/lib/packetgen/plugin/smb2/error.rb +3 -4
  46. data/lib/packetgen/plugin/smb2/guid.rb +6 -4
  47. data/lib/packetgen/plugin/smb2/negotiate.rb +2 -2
  48. data/lib/packetgen/plugin/smb2/negotiate/context.rb +28 -27
  49. data/lib/packetgen/plugin/smb2/negotiate/request.rb +16 -12
  50. data/lib/packetgen/plugin/smb2/negotiate/response.rb +25 -14
  51. data/lib/packetgen/plugin/smb2/session_setup.rb +2 -2
  52. data/lib/packetgen/plugin/smb2/session_setup/request.rb +12 -7
  53. data/lib/packetgen/plugin/smb2/session_setup/response.rb +13 -8
  54. data/lib/packetgen/plugin/smb_version.rb +3 -1
  55. data/packetgen-plugin-smb.gemspec +10 -15
  56. metadata +28 -81
  57. data/.travis.yml +0 -12
@@ -1,12 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  require 'packetgen'
9
9
  require_relative 'packetgen/plugin/smb_version'
10
10
  require_relative 'packetgen/plugin/gssapi'
11
+ require_relative 'packetgen/plugin/netbios'
11
12
  require_relative 'packetgen/plugin/smb'
12
13
  require_relative 'packetgen/plugin/smb2'
14
+ require_relative 'packetgen/plugin/llmnr'
15
+ require_relative 'packetgen/plugin/ntlm'
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of packetgen-plugin-smb.
2
4
  # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
3
5
  # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
4
6
  # This program is published under MIT license.
5
7
 
6
- # frozen_string_literal: true
7
-
8
8
  require 'rasn1'
9
9
 
10
10
  module PacketGen::Plugin
@@ -75,7 +75,7 @@ module PacketGen::Plugin
75
75
  content: [sequence_of(:mech_types, RASN1::Types::ObjectId, explicit: 0, class: :context),
76
76
  bit_string(:req_flags, explicit: 1, class: :context, constructed: true, optional: true),
77
77
  octet_string(:mech_token, explicit: 2, class: :context, constructed: true, optional: true),
78
- octet_string(:mech_list_mic, explicit: 3, class: :context, constructed: true, optional: true)]
78
+ any(:mech_list_mic, explicit: 3, class: :context, constructed: true, optional: true)]
79
79
  end
80
80
 
81
81
  # GSS API Negotiation Token Response
@@ -86,7 +86,7 @@ module PacketGen::Plugin
86
86
  'accept-incomplete' => 1,
87
87
  'reject' => 2,
88
88
  'request-mic' => 3
89
- }
89
+ }.freeze
90
90
  sequence :token, explicit: 1, class: :context, constructed: true,
91
91
  content: [enumerated(:negstate, enum: NEG_STATES, explicit: 0, class: :context, constructed: true, optional: true),
92
92
  objectid(:supported_mech, explicit: 1, class: :context, constructed: true, optional: true),
@@ -101,17 +101,20 @@ module PacketGen::Plugin
101
101
  model(:token_resp, NegTokenResp)]
102
102
 
103
103
  # @param [Hash] args
104
- # @param [Symbol] :type +:init+ or +:response+ to force selection of
104
+ # @option args [Symbol] :token +:init+ or +:response+ to force selection of
105
105
  # token CHOICE.
106
106
  def initialize(args={})
107
107
  token = args.delete(:token)
108
108
  super
109
- self[:gssapi].chosen = (token == :init) ? 0 : 1
109
+ self[:gssapi].chosen = token == :init ? 0 : 1
110
110
  end
111
+
111
112
  # Populate Object from +str+
112
113
  # @param [String] str
113
114
  # @return [self]
114
115
  def read(str)
116
+ return self if str.nil?
117
+
115
118
  parse!(str, ber: true)
116
119
  self
117
120
  end
@@ -121,5 +124,7 @@ module PacketGen::Plugin
121
124
  def sz
122
125
  to_der.size
123
126
  end
127
+
128
+ alias to_s to_der
124
129
  end
125
130
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of packetgen-plugin-smb.
4
+ # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
5
+ # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
6
+ # This program is published under MIT license.
7
+
8
+ module PacketGen::Plugin
9
+ # Link-Local Multicast Name Resolution (LLMNR) header ({https://tools.ietf.org/html/rfc4795 RFC 4795}).
10
+ # @author Sylvain Daubert
11
+ class LLMNR < PacketGen::Header::DNS
12
+ # UDP port number
13
+ UDP_PORT = 5355
14
+ # MAC address used with IPv4 multicast addresses
15
+ MAC_IPV4_MCAST = '01:00:5e:00:00:fc'
16
+
17
+ # @api private
18
+ # @note This method is used internally by PacketGen and should not be
19
+ # directly called
20
+ def added_to_packet(packet)
21
+ packet.instance_eval <<-END_OF_DEFINITION
22
+ def llmnrize(**kwargs)
23
+ llmnr = headers.find { |hdr| hdr.is_a? PacketGen::Plugin::LLMNR }
24
+ llmnr.llmnrize(**kwargs)
25
+ end
26
+ END_OF_DEFINITION
27
+ end
28
+
29
+ # Fixup IP header according to RFC 4795:
30
+ # * optionally set destination address,
31
+ # * set TTL to 1 if destination is a mcast address,
32
+ # * set MAC destination address to {MAC_IPV4_MCAST} if destination address is a mcast one.
33
+ # This method may be called as:
34
+ # # first way
35
+ # pkt.llmnr.llmnrize
36
+ # # second way
37
+ # pkt.llmnrize
38
+ # @param [String,nil] dst destination address. May be a dotted IP
39
+ # address (by example '224.0.0.252').
40
+ # @return [void]
41
+ def llmnrize(dst: nil)
42
+ ip = ip_header(self)
43
+ ip.dst = dst unless dst.nil?
44
+ ip.ttl = 1 if ip[:dst].mcast?
45
+
46
+ # rubocop:disable Lint/SuppressedException
47
+ begin
48
+ llh = ll_header(self)
49
+ llh.dst = MAC_IPV4_MCAST if ip[:dst].mcast?
50
+ rescue PacketGen::FormatError
51
+ end
52
+ # rubocop:enable Lint/SuppressedException
53
+ end
54
+ end
55
+ PacketGen::Header.add_class LLMNR
56
+ PacketGen::Header::UDP.bind LLMNR, sport: LLMNR::UDP_PORT
57
+ PacketGen::Header::UDP.bind LLMNR, dport: LLMNR::UDP_PORT
58
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of PacketGen
4
+ # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
5
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
6
+ # This program is published under MIT license.
7
+
8
+ # frozen_string_literal: true
9
+
10
+ module PacketGen::Plugin
11
+ # Module to group all NetBIOS headers
12
+ # @author Sylvain Daubert
13
+ module NetBIOS
14
+ end
15
+ end
16
+
17
+ require_relative 'netbios/name'
18
+ require_relative 'netbios/session'
19
+ require_relative 'netbios/datagram'
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of PacketGen
4
+ # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
5
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
6
+ # This program is published under MIT license.
7
+
8
+ module PacketGen::Plugin
9
+ # Module to group all NetBIOS headers
10
+ # @author Sylvain Daubert
11
+ module NetBIOS
12
+ # NetBIOS Datagram Service messages.
13
+ # @author Sylvain Daubert
14
+ class Datagram < PacketGen::Header::Base
15
+ # Give protocol name
16
+ # @return [String]
17
+ def self.protocol_name
18
+ 'NetBIOS::Datagram'
19
+ end
20
+
21
+ # Port number for NetBIOS Session Service over TCP
22
+ UDP_PORT = 138
23
+
24
+ # Datagram packet types
25
+ TYPES = {
26
+ 'direct_unique' => 0x10,
27
+ 'direct_group' => 0x11,
28
+ 'broadcast' => 0x12,
29
+ 'error' => 0x13,
30
+ 'query_request' => 0x14,
31
+ 'positive_query_resp' => 0x15,
32
+ 'negative_query_resp' => 0x16,
33
+ }.freeze
34
+
35
+ # @!attribute type
36
+ # 8-bit session packet type
37
+ # @return [Integer]
38
+ define_field :type, PacketGen::Types::Int8Enum, enum: TYPES
39
+ # @!attribute flags
40
+ # 8-bit flags
41
+ # @return [Integer]
42
+ define_field :flags, PacketGen::Types::Int8
43
+ # @!attribute dgm_id
44
+ # 16-bit next transaction ID for datagrams
45
+ # @return [Integer]
46
+ define_field :dgm_id, PacketGen::Types::Int16
47
+ # @!attribute src_ip
48
+ # Source IP address
49
+ # @return [IP::Addr]
50
+ define_field :src_ip, PacketGen::Header::IP::Addr
51
+ # @!attribute src_port
52
+ # Source port
53
+ # @return [IP::Addr]
54
+ define_field :src_port, PacketGen::Types::Int16
55
+ # @!attribute dgm_length
56
+ # Length of data + second level of encoded names. Not present in error datagram.
57
+ # @return [Integer]
58
+ define_field :dgm_length, PacketGen::Types::Int16, optional: ->(h) { h.type != 0x13 }
59
+ # @!attribute packet_offset
60
+ # Not present in error datagram.
61
+ # @return [Integer]
62
+ define_field :packet_offset, PacketGen::Types::Int16, optional: ->(h) { h.type != 0x13 }
63
+ # @!attribute error_code
64
+ # Error code. Only present in error datagrams.
65
+ # @return [Integer]
66
+ define_field :error_code, PacketGen::Types::Int16, optional: ->(h) { h.type == 0x13 }
67
+ # @!attribute src_name
68
+ # NetBIOS source name. Only present in direct_unique, direct_group and broadcast datagrams.
69
+ # @return []
70
+ define_field :src_name, Name, default: '', optional: ->(h) { (h.type >= 0x10) && (h.type <= 0x12) }
71
+ # @!attribute dst_name
72
+ # NetBIOS destination name. Present in all but error datagrams.
73
+ # @return []
74
+ define_field :dst_name, Name, default: '', optional: ->(h) { h.type != 0x13 }
75
+ # @!attribute body
76
+ # User data. Ony present in direct_unique, direct_group and broadcast datagrams.
77
+ # @return [String]
78
+ define_field :body, PacketGen::Types::String, optional: ->(h) { (h.type >= 0x10) && (h.type <= 0x12) }
79
+
80
+ # @!attribute :rsv
81
+ # 4-bit rsv field. 4 upper bits of {#flags}
82
+ # @return [Integer]
83
+ # @!attribute :snt
84
+ # 2-bit SNT (Source end-Node Type) field from {#flags}.
85
+ # @return [Integer]
86
+ # @!attribute f
87
+ # First packet flag. If set then this is first
88
+ # (and possibly only) fragment of NetBIOS datagram.
89
+ # @return [Boolean]
90
+ # @!attribute m
91
+ # More flag. If set then more NetBIOS datagram
92
+ # fragments follow.
93
+ # @return [Boolean]
94
+ define_bit_fields_on :flags, :rsv, 4, :snt, 2, :f, :m
95
+
96
+ # Compute and set {#dgm_length} field
97
+ # @return [Integer] calculated length
98
+ def calc_length
99
+ length = self[:body].sz
100
+ length += self[:src_name].sz if present?(:src_name)
101
+ length += self[:dst_name].sz if present?(:dst_name)
102
+ self.dgm_length = length
103
+ end
104
+ end
105
+ PacketGen::Header.add_class Datagram
106
+ PacketGen::Header::UDP.bind Datagram, dport: Datagram::UDP_PORT, sport: Datagram::UDP_PORT
107
+ end
108
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of PacketGen
4
+ # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
5
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
6
+ # This program is published under MIT license.
7
+
8
+ module PacketGen::Plugin
9
+ # Module to group all NetBIOS headers
10
+ # @author Sylvain Daubert
11
+ module NetBIOS
12
+ # NetBIOS Name.
13
+ # @author Sylvain Daubert
14
+ class Name < PacketGen::Header::DNS::Name
15
+ # Size, in bytes, of an encoded NetBIOS name
16
+ ENCODED_NAME_SIZE = 32
17
+
18
+ # Read a NetBIOS name from a string
19
+ # @param [String] str
20
+ # @return [Name] self
21
+ def from_human(str)
22
+ clear
23
+ return self if str.nil?
24
+
25
+ encoded_name = encode_name(str)
26
+ super(encoded_name)
27
+ end
28
+
29
+ # Get a human readable string
30
+ # @return [String]
31
+ def to_human
32
+ encoded_name = super
33
+ decode_name(encoded_name)
34
+ end
35
+
36
+ private
37
+
38
+ def encode_name(name)
39
+ basename, *scope_id = name.split('.')
40
+ basename = '' if basename.nil?
41
+ scope_id = scope_id.join('.')
42
+ encoded_name = +''
43
+ basename.each_byte do |byte|
44
+ a = (byte >> 4) + 0x41
45
+ b = (byte & 0xf) + 0x41
46
+ encoded_name << [a, b].pack('C2')
47
+ end
48
+ encoded_name << 'CA' * ((ENCODED_NAME_SIZE - encoded_name.size) / 2) if encoded_name.size < ENCODED_NAME_SIZE
49
+ encoded_name << ".#{scope_id}" if scope_id
50
+ encoded_name
51
+ end
52
+
53
+ def decode_name(encoded_name)
54
+ name = +''
55
+ encoded_name.partition('.').first.scan(/../).map do |duo|
56
+ a = (duo[0].ord - 0x41) & 0xf
57
+ b = (duo[1].ord - 0x41) & 0xf
58
+ name << (a << 4 | b).chr
59
+ end
60
+ name.strip
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of PacketGen
4
+ # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
5
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
6
+ # This program is published under MIT license.
7
+
8
+ module PacketGen::Plugin
9
+ # Module to group all NetBIOS headers
10
+ # @author Sylvain Daubert
11
+ module NetBIOS
12
+ # NetBIOS Session Service messages.
13
+ # @author Sylvain Daubert
14
+ class Session < PacketGen::Header::Base
15
+ # Give protocol name
16
+ # @return [String]
17
+ def self.protocol_name
18
+ 'NetBIOS::Session'
19
+ end
20
+
21
+ # Port number for NetBIOS Session Service over TCP
22
+ TCP_PORT = 139
23
+ # Port number for NetBIOS Session Service over TCP (mainly used yb {SMB2})
24
+ TCP_PORT2 = 445
25
+
26
+ # Session packet types
27
+ TYPES = {
28
+ 'message' => 0,
29
+ 'request' => 0x81,
30
+ 'positive_response' => 0x82,
31
+ 'negative_response' => 0x83,
32
+ 'retarget_response' => 0x84,
33
+ 'keep_alive' => 0x85,
34
+ }.freeze
35
+
36
+ # @!attribute type
37
+ # 8-bit session packet type
38
+ # @return [Integer]
39
+ define_field :type, PacketGen::Types::Int8Enum, enum: TYPES
40
+ # @!attribute length
41
+ # 17-bit session packet length
42
+ # @return [Integer]
43
+ define_field :length, PacketGen::Types::Int24
44
+ # @!attribute body
45
+ # @return [String]
46
+ define_field :body, PacketGen::Types::String
47
+
48
+ # Compute and set {#length} field
49
+ # @return [Integer] calculated length
50
+ def calc_length
51
+ PacketGen::Header::Base.calculate_and_set_length(self, header_in_size: false)
52
+ end
53
+
54
+ # @api private
55
+ # @note This method is used internally by PacketGen and should not be
56
+ # directly called
57
+ # @since 2.7.0 Set TCP sport according to bindings, only if sport is 0.
58
+ # Needed by new bind API.
59
+ def added_to_packet(packet)
60
+ return unless packet.is? 'TCP'
61
+ return unless packet.tcp.sport.zero?
62
+
63
+ packet.tcp.sport = TCP_PORT
64
+ end
65
+ end
66
+ PacketGen::Header.add_class Session
67
+ PacketGen::Header::TCP.bind Session, dport: Session::TCP_PORT
68
+ PacketGen::Header::TCP.bind Session, sport: Session::TCP_PORT
69
+ PacketGen::Header::TCP.bind Session, dport: Session::TCP_PORT2
70
+ PacketGen::Header::TCP.bind Session, sport: Session::TCP_PORT2
71
+ end
72
+ end
@@ -0,0 +1,211 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of packetgen-plugin-smb.
4
+ # See https://github.com/sdaubert/packetgen-plugin-smb for more informations
5
+ # Copyright (C) 2018 Sylvain Daubert <sylvain.daubert@laposte.net>
6
+ # This program is published under MIT license.
7
+
8
+ module PacketGen::Plugin
9
+ # Base class for NTLM authentication protocol.
10
+ # @author Sylvain Daubert
11
+ class NTLM < PacketGen::Types::Fields
12
+ # NTLM message types
13
+ TYPES = {
14
+ 'negotiate' => 1,
15
+ 'challenge' => 2,
16
+ 'authenticate' => 3
17
+ }.freeze
18
+
19
+ # NTLM signature
20
+ SIGNATURE = "NTLMSSP\0"
21
+
22
+ # void version
23
+ VOID_VERSION = [0].pack('q').freeze
24
+ VOID_CHALLENGE = VOID_VERSION
25
+
26
+ # @!attribute signature
27
+ # 8-byte NTLM signature
28
+ # @return [String]
29
+ define_field :signature, PacketGen::Types::String, static_length: 8, default: SIGNATURE
30
+ # @!attribute type
31
+ # 4-byte message type
32
+ # @return [Integer]
33
+ define_field :type, PacketGen::Types::Int32leEnum, enum: TYPES
34
+ # @!attribute payload
35
+ # @return [String]
36
+ define_field :payload, PacketGen::Types::String
37
+
38
+ class <<self
39
+ # @api private
40
+ # Return fields defined in payload one.
41
+ # @return [Hash]
42
+ attr_accessor :payload_fields
43
+
44
+ # Create a NTLM object from a binary string
45
+ # @param [String] str
46
+ # @return [NTLM]
47
+ def read(str)
48
+ ntlm = self.new.read(str)
49
+ type = TYPES.key(ntlm.type)
50
+ return ntlm if type.nil?
51
+
52
+ klass = NTLM.const_get(type.capitalize)
53
+ klass.new.read(str)
54
+ end
55
+
56
+ # Define a flags field.
57
+ # @return [void]
58
+ def define_negotiate_flags
59
+ define_field_before :payload, :flags, PacketGen::Types::Int32le
60
+ define_bit_fields_on :flags, :flags_w, :flags_v, :flags_u, :flags_r13, 3,
61
+ :flags_t, :flags_r4, :flags_s, :flags_r,
62
+ :flags_r5, :flags_q, :flags_p, :flags_r6,
63
+ :flags_o, :flags_n, :flags_m, :flags_r7,
64
+ :flags_l, :flags_k, :flags_j, :flags_r8,
65
+ :flags_h, :flags_r9, :flags_g, :flags_f,
66
+ :flags_e, :flags_d, :flags_r10, :flags_c,
67
+ :flags_b, :flags_a
68
+ alias_method :nego56?, :flags_w?
69
+ alias_method :key_exch?, :flags_v?
70
+ alias_method :nego128?, :flags_u?
71
+ alias_method :version?, :flags_t?
72
+ alias_method :target_info?, :flags_s?
73
+ alias_method :non_nt_session_key?, :flags_r?
74
+ alias_method :identify?, :flags_q?
75
+ alias_method :ext_session_security?, :flags_p?
76
+ alias_method :target_type_server?, :flags_o?
77
+ alias_method :target_type_domain?, :flags_n?
78
+ alias_method :always_sign?, :flags_m?
79
+ alias_method :oem_workstation_supplied?, :flags_l?
80
+ alias_method :oem_domain_supplied?, :flags_k?
81
+ alias_method :anonymous?, :flags_j?
82
+ alias_method :ntlm?, :flags_h?
83
+ alias_method :lm_key?, :flags_g?
84
+ alias_method :datagram?, :flags_f?
85
+ alias_method :seal?, :flags_e?
86
+ alias_method :sign?, :flags_d?
87
+ alias_method :request_target?, :flags_c?
88
+ alias_method :oem?, :flags_b?
89
+ alias_method :unicode?, :flags_a?
90
+ alias_method :old_flags_a=, :flags_a=
91
+ alias_method :old_flags=, :flags=
92
+
93
+ class_eval do
94
+ def flags_a=(value)
95
+ self.old_flags_a = value
96
+ self.class.payload_fields.each do |name, _|
97
+ attr = send(name)
98
+ attr.unicode = value if attr.respond_to?(:unicode=)
99
+ end
100
+
101
+ value
102
+ end
103
+
104
+ def flags=(value)
105
+ self.old_flags = value
106
+ self.flags_a = value & 1
107
+ end
108
+ end
109
+ end
110
+
111
+ # Define a field in payload. Also add +name_len+, +name_maxlen+ and
112
+ # +name_offset+ fields.
113
+ # @param [Symbol] name name of field.
114
+ # @param [Class,nil] type type of +name+ field.
115
+ # @param [Hash] options type's options needed at build time
116
+ # @return [void]
117
+ def define_in_payload(name, type=SMB::String, options={})
118
+ @payload_fields ||= {}
119
+ @payload_fields[name] = [type, options]
120
+
121
+ define_field_before :payload, :"#{name}_len", PacketGen::Types::Int16le
122
+ define_field_before :payload, :"#{name}_maxlen", PacketGen::Types::Int16le
123
+ define_field_before :payload, :"#{name}_offset", PacketGen::Types::Int32le
124
+
125
+ attr_accessor name
126
+ end
127
+ end
128
+
129
+ # @abstract This method is meaningful for {NTLM} subclasses only.
130
+ def initialize(options={})
131
+ super
132
+ return if self.class.payload_fields.nil?
133
+
134
+ self.class.payload_fields.each do |name, type_and_opt|
135
+ type, options = type_and_opt
136
+ content = if type.new.respond_to?(:unicode?)
137
+ type.new(options.merge(unicode: unicode?))
138
+ else
139
+ type.new(options)
140
+ end
141
+ send(:"#{name}=", content)
142
+ end
143
+ end
144
+
145
+ # @abstract This class is meaningful for {NTLM} subclasses only.
146
+ # Populate object from a binary string
147
+ # @param [String] str
148
+ # @return [self]
149
+ def read(str)
150
+ super
151
+ return self if self.class.payload_fields.nil?
152
+
153
+ self.class.payload_fields.each do |name, type_and_opt|
154
+ type, options = type_and_opt
155
+ offset_in_payload = send(:"#{name}_offset") - offset_of(:payload)
156
+ length = send(:"#{name}_len")
157
+ content = if type.new.respond_to?(:unicode?)
158
+ type.new(options.merge(unicode: unicode?))
159
+ else
160
+ type.new(options)
161
+ end
162
+ content.read(payload[offset_in_payload, length]) if length.positive?
163
+ send(:"#{name}=", content)
164
+ end
165
+
166
+ self
167
+ end
168
+
169
+ # @abstract This class is meaningful for {NTLM} subclasses only.
170
+ # Calculate and set +len+, +maxlen+ and +offset+ fields defined for
171
+ # fields in {#payload}.
172
+ # @return [void]
173
+ def calc_length
174
+ return self if self.class.payload_fields.nil?
175
+
176
+ previous_len = 0
177
+ self.class.payload_fields.each do |name, _type_and_opt|
178
+ send(:"#{name}_len=", 0)
179
+ send(:"#{name}_offset=", offset_of(:payload) + previous_len)
180
+
181
+ field = send(name)
182
+ next unless field && !field.empty?
183
+
184
+ length = field.respond_to?(:sz) ? field.sz : field.size
185
+ send(:"#{name}_len=", length)
186
+ send(:"#{name}_maxlen=", length)
187
+ previous_len = length
188
+ end
189
+ end
190
+
191
+ # @abstract This class is meaningful for {NTLM} subclasses only.
192
+ # @return [String]
193
+ def to_s
194
+ s = super
195
+ return s if self.class.payload_fields.nil?
196
+
197
+ self.class.payload_fields.each do |name, _type_and_opt|
198
+ attr = send(name)
199
+ attr.unicode = unicode? if attr.respond_to?(:unicode=)
200
+ s << attr.to_s unless attr.nil? || send("#{name}_len").zero?
201
+ end
202
+ s
203
+ end
204
+ end
205
+ end
206
+
207
+ require_relative 'ntlm/av_pair'
208
+ require_relative 'ntlm/ntlmv2_response'
209
+ require_relative 'ntlm/negotiate'
210
+ require_relative 'ntlm/challenge'
211
+ require_relative 'ntlm/authenticate'