packetgen-plugin-smb 0.3.0 → 0.6.2

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.
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'