ruby_smb 1.1.0 → 2.0.4

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 (163) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.travis.yml +3 -5
  5. data/Gemfile +6 -2
  6. data/examples/anonymous_auth.rb +3 -3
  7. data/examples/append_file.rb +10 -8
  8. data/examples/authenticate.rb +9 -5
  9. data/examples/delete_file.rb +8 -6
  10. data/examples/enum_registry_key.rb +5 -4
  11. data/examples/enum_registry_values.rb +5 -4
  12. data/examples/list_directory.rb +8 -6
  13. data/examples/negotiate.rb +51 -8
  14. data/examples/negotiate_with_netbios_service.rb +9 -5
  15. data/examples/net_share_enum_all.rb +6 -4
  16. data/examples/pipes.rb +11 -12
  17. data/examples/query_service_status.rb +64 -0
  18. data/examples/read_file.rb +8 -6
  19. data/examples/read_file_encryption.rb +56 -0
  20. data/examples/read_registry_key_value.rb +6 -5
  21. data/examples/rename_file.rb +9 -7
  22. data/examples/tree_connect.rb +7 -5
  23. data/examples/write_file.rb +9 -7
  24. data/lib/ruby_smb.rb +4 -0
  25. data/lib/ruby_smb/client.rb +246 -26
  26. data/lib/ruby_smb/client/authentication.rb +32 -18
  27. data/lib/ruby_smb/client/echo.rb +2 -4
  28. data/lib/ruby_smb/client/encryption.rb +62 -0
  29. data/lib/ruby_smb/client/negotiation.rb +156 -16
  30. data/lib/ruby_smb/client/signing.rb +19 -0
  31. data/lib/ruby_smb/client/tree_connect.rb +6 -8
  32. data/lib/ruby_smb/client/utils.rb +24 -17
  33. data/lib/ruby_smb/client/winreg.rb +1 -1
  34. data/lib/ruby_smb/crypto.rb +30 -0
  35. data/lib/ruby_smb/dcerpc.rb +2 -0
  36. data/lib/ruby_smb/dcerpc/error.rb +3 -0
  37. data/lib/ruby_smb/dcerpc/ndr.rb +209 -44
  38. data/lib/ruby_smb/dcerpc/request.rb +13 -0
  39. data/lib/ruby_smb/dcerpc/rpc_security_attributes.rb +34 -0
  40. data/lib/ruby_smb/dcerpc/rrp_unicode_string.rb +9 -6
  41. data/lib/ruby_smb/dcerpc/svcctl.rb +479 -0
  42. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request.rb +48 -0
  43. data/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response.rb +26 -0
  44. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request.rb +25 -0
  45. data/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response.rb +26 -0
  46. data/lib/ruby_smb/dcerpc/svcctl/control_service_request.rb +26 -0
  47. data/lib/ruby_smb/dcerpc/svcctl/control_service_response.rb +26 -0
  48. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request.rb +35 -0
  49. data/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response.rb +23 -0
  50. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_request.rb +31 -0
  51. data/lib/ruby_smb/dcerpc/svcctl/open_service_w_response.rb +23 -0
  52. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request.rb +25 -0
  53. data/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response.rb +44 -0
  54. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_request.rb +23 -0
  55. data/lib/ruby_smb/dcerpc/svcctl/query_service_status_response.rb +27 -0
  56. data/lib/ruby_smb/dcerpc/svcctl/service_status.rb +25 -0
  57. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_request.rb +27 -0
  58. data/lib/ruby_smb/dcerpc/svcctl/start_service_w_response.rb +25 -0
  59. data/lib/ruby_smb/dcerpc/winreg.rb +98 -17
  60. data/lib/ruby_smb/dcerpc/winreg/create_key_request.rb +73 -0
  61. data/lib/ruby_smb/dcerpc/winreg/create_key_response.rb +36 -0
  62. data/lib/ruby_smb/dcerpc/winreg/enum_key_request.rb +1 -1
  63. data/lib/ruby_smb/dcerpc/winreg/enum_value_request.rb +1 -1
  64. data/lib/ruby_smb/dcerpc/winreg/enum_value_response.rb +1 -1
  65. data/lib/ruby_smb/dcerpc/winreg/open_root_key_request.rb +4 -4
  66. data/lib/ruby_smb/dcerpc/winreg/query_info_key_request.rb +1 -1
  67. data/lib/ruby_smb/dcerpc/winreg/query_value_request.rb +7 -6
  68. data/lib/ruby_smb/dcerpc/winreg/query_value_response.rb +10 -10
  69. data/lib/ruby_smb/dcerpc/winreg/save_key_request.rb +37 -0
  70. data/lib/ruby_smb/dcerpc/winreg/save_key_response.rb +23 -0
  71. data/lib/ruby_smb/dispatcher/base.rb +1 -1
  72. data/lib/ruby_smb/dispatcher/socket.rb +5 -4
  73. data/lib/ruby_smb/error.rb +49 -6
  74. data/lib/ruby_smb/field/stringz16.rb +17 -1
  75. data/lib/ruby_smb/generic_packet.rb +11 -1
  76. data/lib/ruby_smb/nbss/session_header.rb +4 -4
  77. data/lib/ruby_smb/smb1/commands.rb +1 -1
  78. data/lib/ruby_smb/smb1/file.rb +13 -28
  79. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +1 -1
  80. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -2
  81. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -1
  82. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -2
  83. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +1 -1
  84. data/lib/ruby_smb/smb1/pipe.rb +8 -8
  85. data/lib/ruby_smb/smb1/tree.rb +25 -12
  86. data/lib/ruby_smb/smb2/bit_field/session_flags.rb +2 -1
  87. data/lib/ruby_smb/smb2/bit_field/share_flags.rb +6 -4
  88. data/lib/ruby_smb/smb2/file.rb +59 -77
  89. data/lib/ruby_smb/smb2/negotiate_context.rb +108 -0
  90. data/lib/ruby_smb/smb2/packet.rb +2 -0
  91. data/lib/ruby_smb/smb2/packet/compression_transform_header.rb +41 -0
  92. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +51 -14
  93. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +50 -4
  94. data/lib/ruby_smb/smb2/packet/transform_header.rb +84 -0
  95. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +92 -6
  96. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -26
  97. data/lib/ruby_smb/smb2/pipe.rb +8 -20
  98. data/lib/ruby_smb/smb2/smb2_header.rb +1 -1
  99. data/lib/ruby_smb/smb2/tree.rb +44 -28
  100. data/lib/ruby_smb/version.rb +1 -1
  101. data/ruby_smb.gemspec +3 -1
  102. data/spec/lib/ruby_smb/client_spec.rb +1408 -70
  103. data/spec/lib/ruby_smb/crypto_spec.rb +25 -0
  104. data/spec/lib/ruby_smb/dcerpc/ndr_spec.rb +1396 -77
  105. data/spec/lib/ruby_smb/dcerpc/rpc_security_attributes_spec.rb +161 -0
  106. data/spec/lib/ruby_smb/dcerpc/rrp_unicode_string_spec.rb +49 -12
  107. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_request_spec.rb +191 -0
  108. data/spec/lib/ruby_smb/dcerpc/svcctl/change_service_config_w_response_spec.rb +38 -0
  109. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_request_spec.rb +30 -0
  110. data/spec/lib/ruby_smb/dcerpc/svcctl/close_service_handle_response_spec.rb +38 -0
  111. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_request_spec.rb +39 -0
  112. data/spec/lib/ruby_smb/dcerpc/svcctl/control_service_response_spec.rb +38 -0
  113. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_request_spec.rb +78 -0
  114. data/spec/lib/ruby_smb/dcerpc/svcctl/open_sc_manager_w_response_spec.rb +38 -0
  115. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_request_spec.rb +59 -0
  116. data/spec/lib/ruby_smb/dcerpc/svcctl/open_service_w_response_spec.rb +38 -0
  117. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_request_spec.rb +38 -0
  118. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_config_w_response_spec.rb +152 -0
  119. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_request_spec.rb +30 -0
  120. data/spec/lib/ruby_smb/dcerpc/svcctl/query_service_status_response_spec.rb +38 -0
  121. data/spec/lib/ruby_smb/dcerpc/svcctl/service_status_spec.rb +72 -0
  122. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_request_spec.rb +46 -0
  123. data/spec/lib/ruby_smb/dcerpc/svcctl/start_service_w_response_spec.rb +30 -0
  124. data/spec/lib/ruby_smb/dcerpc/svcctl_spec.rb +512 -0
  125. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_request_spec.rb +110 -0
  126. data/spec/lib/ruby_smb/dcerpc/winreg/create_key_response_spec.rb +44 -0
  127. data/spec/lib/ruby_smb/dcerpc/winreg/enum_key_request_spec.rb +0 -4
  128. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_request_spec.rb +2 -2
  129. data/spec/lib/ruby_smb/dcerpc/winreg/enum_value_response_spec.rb +2 -2
  130. data/spec/lib/ruby_smb/dcerpc/winreg/open_root_key_request_spec.rb +9 -4
  131. data/spec/lib/ruby_smb/dcerpc/winreg/query_info_key_request_spec.rb +0 -4
  132. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_request_spec.rb +17 -17
  133. data/spec/lib/ruby_smb/dcerpc/winreg/query_value_response_spec.rb +11 -23
  134. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_request_spec.rb +57 -0
  135. data/spec/lib/ruby_smb/dcerpc/winreg/save_key_response_spec.rb +22 -0
  136. data/spec/lib/ruby_smb/dcerpc/winreg_spec.rb +227 -41
  137. data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +12 -12
  138. data/spec/lib/ruby_smb/error_spec.rb +88 -0
  139. data/spec/lib/ruby_smb/field/stringz16_spec.rb +12 -0
  140. data/spec/lib/ruby_smb/generic_packet_spec.rb +7 -0
  141. data/spec/lib/ruby_smb/nbss/session_header_spec.rb +4 -11
  142. data/spec/lib/ruby_smb/smb1/file_spec.rb +1 -3
  143. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +2 -2
  144. data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +2 -2
  145. data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +2 -2
  146. data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +1 -1
  147. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +30 -5
  148. data/spec/lib/ruby_smb/smb1/tree_spec.rb +22 -0
  149. data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +9 -0
  150. data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +27 -0
  151. data/spec/lib/ruby_smb/smb2/file_spec.rb +147 -71
  152. data/spec/lib/ruby_smb/smb2/negotiate_context_spec.rb +332 -0
  153. data/spec/lib/ruby_smb/smb2/packet/compression_transform_header_spec.rb +108 -0
  154. data/spec/lib/ruby_smb/smb2/packet/negotiate_request_spec.rb +138 -3
  155. data/spec/lib/ruby_smb/smb2/packet/negotiate_response_spec.rb +120 -2
  156. data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +220 -0
  157. data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +339 -9
  158. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +3 -30
  159. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +9 -45
  160. data/spec/lib/ruby_smb/smb2/smb2_header_spec.rb +2 -2
  161. data/spec/lib/ruby_smb/smb2/tree_spec.rb +111 -9
  162. metadata +194 -75
  163. metadata.gz.sig +2 -1
@@ -40,6 +40,25 @@ module RubySMB
40
40
  end
41
41
  packet
42
42
  end
43
+
44
+ def smb3_sign(packet)
45
+ if !session_key.empty? && (signing_required || packet.is_a?(RubySMB::SMB2::Packet::TreeConnectRequest))
46
+ case @dialect
47
+ when '0x0300', '0x0302'
48
+ signing_key = RubySMB::Crypto::KDF.counter_mode(@session_key, "SMB2AESCMAC\x00", "SmbSign\x00")
49
+ when '0x0311'
50
+ signing_key = RubySMB::Crypto::KDF.counter_mode(@session_key, "SMBSigningKey\x00", @preauth_integrity_hash_value)
51
+ else
52
+ raise RubySMB::Error::SigningError.new('Dialect is incompatible with SMBv3 signing')
53
+ end
54
+
55
+ packet.smb2_header.flags.signed = 1
56
+ packet.smb2_header.signature = "\x00" * 16
57
+ hmac = OpenSSL::CMAC.digest('AES', signing_key, packet.to_binary_s)
58
+ packet.smb2_header.signature = hmac[0, 16]
59
+ end
60
+ packet
61
+ end
43
62
  end
44
63
  end
45
64
  end
@@ -33,12 +33,11 @@ module RubySMB
33
33
  raise RubySMB::Error::InvalidPacket.new(
34
34
  expected_proto: RubySMB::SMB1::SMB_PROTOCOL_ID,
35
35
  expected_cmd: RubySMB::SMB1::Packet::TreeConnectResponse::COMMAND,
36
- received_proto: response.smb_header.protocol,
37
- received_cmd: response.smb_header.command
36
+ packet: response
38
37
  )
39
38
  end
40
39
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
41
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
40
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
42
41
  end
43
42
  RubySMB::SMB1::Tree.new(client: self, share: share, response: response)
44
43
  end
@@ -55,7 +54,7 @@ module RubySMB
55
54
  def smb2_tree_connect(share)
56
55
  request = RubySMB::SMB2::Packet::TreeConnectRequest.new
57
56
  request.smb2_header.tree_id = 65_535
58
- request.encode_path(share)
57
+ request.path = share
59
58
  raw_response = send_recv(request)
60
59
  response = RubySMB::SMB2::Packet::TreeConnectResponse.read(raw_response)
61
60
  smb2_tree_from_response(share, response)
@@ -73,14 +72,13 @@ module RubySMB
73
72
  raise RubySMB::Error::InvalidPacket.new(
74
73
  expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
75
74
  expected_cmd: RubySMB::SMB2::Packet::TreeConnectResponse::COMMAND,
76
- received_proto: response.smb2_header.protocol,
77
- received_cmd: response.smb2_header.command
75
+ packet: response
78
76
  )
79
77
  end
80
78
  unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
81
- raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
79
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code
82
80
  end
83
- RubySMB::SMB2::Tree.new(client: self, share: share, response: response)
81
+ RubySMB::SMB2::Tree.new(client: self, share: share, response: response, encrypt: response.share_flags.encrypt == 1)
84
82
  end
85
83
  end
86
84
  end
@@ -24,43 +24,50 @@ module RubySMB
24
24
  last_tree.id
25
25
  end
26
26
 
27
- def open(path, disposition=RubySMB::Dispositions::FILE_OPEN, write: false, read: true)
28
- file = last_tree.open_file(filename: path.sub(/^\\/, ''), write: write, read: read, disposition: disposition)
29
- @last_file_id = if file.respond_to?(:guid)
30
- file.guid.to_binary_s
31
- elsif file.respond_to?(:fid)
32
- file.fid.to_binary_s
33
- end
34
- @open_files[@last_file_id] = file
35
- @last_file_id
27
+ def open(path, disposition=RubySMB::Dispositions::FILE_OPEN, write: false, read: true, pipe: false)
28
+ if pipe
29
+ file = last_tree.open_pipe(filename: path, write: write, read: read, disposition: disposition)
30
+ else
31
+ file = last_tree.open_file(filename: path, write: write, read: read, disposition: disposition)
32
+ end
33
+ @last_file_id = if file.respond_to?(:guid)
34
+ # SMB2 uses guid
35
+ file.guid.to_binary_s
36
+ elsif file.respond_to?(:fid)
37
+ # SMB1 uses fid
38
+ file.fid.to_binary_s
39
+ end
40
+ @open_files[@last_file_id] = file
41
+ @last_file_id
36
42
  end
37
43
 
38
44
  def create_pipe(path, disposition=RubySMB::Dispositions::FILE_OPEN_IF)
39
- open(path.gsub(/\\/, ''), disposition, write: true, read: true)
45
+ open(path, disposition, write: true, read: true, pipe: true)
40
46
  end
41
47
 
42
48
  #Writes data to an open file handle
43
- def write(file_id, offset = 0, data = '', do_recv = true)
49
+ def write(file_id = last_file_id, offset = 0, data = '', do_recv = true)
44
50
  @open_files[file_id].send_recv_write(data: data, offset: offset)
45
51
  end
46
52
 
47
- def read(file_id, offset = 0, length = last_file.size)
53
+ def read(file_id = last_file_id, offset = 0, length = last_file.size, do_recv = true)
48
54
  data = @open_files[file_id].send_recv_read(read_length: length, offset: offset)
49
55
  data.bytes
50
56
  end
51
57
 
52
- def delete(path)
53
- file = last_tree.open_file(filename: path.sub(/^\\/, ''), delete: true)
58
+ def delete(path, tree_id = last_tree_id, do_recv = true)
59
+ tree = @tree_connects.detect{ |tree| tree.id == tree_id }
60
+ file = tree.open_file(filename: path.sub(/^\\/, ''), delete: true)
54
61
  file.delete
55
62
  file.close
56
63
  end
57
64
 
58
- def close(file_id, tree_id)
65
+ def close(file_id = last_file_id, tree_id = last_tree_id, do_recv = true)
59
66
  @open_files[file_id].close
60
67
  end
61
68
 
62
- def tree_disconnect(share)
63
- @tree_connects.detect{|tree| tree.id == share }.disconnect!
69
+ def tree_disconnect(tree_id = last_tree_id, do_recv = true)
70
+ @tree_connects.detect{|tree| tree.id == tree_id }.disconnect!
64
71
  end
65
72
 
66
73
  def native_os
@@ -6,7 +6,7 @@ module RubySMB
6
6
  share = "\\\\#{host}\\IPC$"
7
7
  tree = @tree_connects.find {|tree| tree.share == share}
8
8
  tree = tree_connect(share) unless tree
9
- named_pipe = tree.open_file(filename: "winreg", write: true, read: true)
9
+ named_pipe = tree.open_pipe(filename: "winreg", write: true, read: true)
10
10
  if block_given?
11
11
  res = yield named_pipe
12
12
  named_pipe.close
@@ -0,0 +1,30 @@
1
+ module RubySMB
2
+ module Crypto
3
+ module KDF
4
+ def self.counter_mode(ki, label, context, length: 128)
5
+ digest = OpenSSL::Digest.new('SHA256')
6
+ r = 32
7
+
8
+ n = length / 256
9
+ n = 1 if n == 0
10
+
11
+ raise ArgumentError if n > 2**r - 1
12
+ result = ""
13
+
14
+ n.times do |i|
15
+ input = [i + 1].pack('L>')
16
+ input << label
17
+ input << "\x00"
18
+ input << context
19
+ input << [length].pack('L>')
20
+ k = OpenSSL::HMAC.digest(digest, ki, input)
21
+ result << k
22
+ end
23
+
24
+ return result[0...(length / 8)]
25
+ rescue OpenSSL::OpenSSLError => e
26
+ raise RubySMB::Error::EncryptionError, "Crypto::KDF.counter_mode OpenSSL error: #{e.message}"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -10,9 +10,11 @@ module RubySMB
10
10
  require 'ruby_smb/dcerpc/ptypes'
11
11
  require 'ruby_smb/dcerpc/p_syntax_id_t'
12
12
  require 'ruby_smb/dcerpc/rrp_unicode_string'
13
+ require 'ruby_smb/dcerpc/rpc_security_attributes'
13
14
  require 'ruby_smb/dcerpc/pdu_header'
14
15
  require 'ruby_smb/dcerpc/srvsvc'
15
16
  require 'ruby_smb/dcerpc/winreg'
17
+ require 'ruby_smb/dcerpc/svcctl'
16
18
  require 'ruby_smb/dcerpc/request'
17
19
  require 'ruby_smb/dcerpc/response'
18
20
  require 'ruby_smb/dcerpc/bind'
@@ -13,6 +13,9 @@ module RubySMB
13
13
 
14
14
  # Raised when an error is returned during a Winreg operation
15
15
  class WinregError < DcerpcError; end
16
+
17
+ # Raised when an error is returned during a Svcctl operation
18
+ class SvcctlError < DcerpcError; end
16
19
  end
17
20
  end
18
21
  end
@@ -7,64 +7,88 @@ module RubySMB
7
7
  VER_MAJOR = 2
8
8
  VER_MINOR = 0
9
9
 
10
- # An NDR Top-level Full Pointers representation as defined in
11
- # [Transfer Syntax NDR - Top-level Full Pointers](http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_11_01)
12
- # This class must be inherited and the subclass must have a #referent protperty
13
- class NdrTopLevelFullPointer < BinData::Primitive
10
+
11
+ # An NDR Conformant and Varying String representation as defined in
12
+ # [Transfer Syntax NDR - Conformant and Varying Strings](http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_04_02)
13
+ # The string elements are Stringz16 (unicode)
14
+ class NdrString < BinData::Primitive
14
15
  endian :little
15
16
 
16
- uint32 :referent_identifier, initial_value: 0x00020000
17
+ uint32 :max_count
18
+ uint32 :offset, initial_value: 0
19
+ uint32 :actual_count
20
+ stringz16 :str, max_length: -> { actual_count * 2 }, onlyif: -> { actual_count > 0 }
17
21
 
18
22
  def get
19
- is_a_null_pointer? ? 0 : self.referent
23
+ self.actual_count == 0 ? 0 : self.str
20
24
  end
21
25
 
22
26
  def set(v)
23
- if v.is_a?(Integer) && v == 0
24
- self.referent_identifier = 0
27
+ if v == 0
28
+ self.str.clear
29
+ self.actual_count = 0
25
30
  else
26
- self.referent = v
31
+ v = v.str if v.is_a?(self.class)
32
+ unless self.str.equal?(v)
33
+ if v.empty?
34
+ self.actual_count = 0
35
+ else
36
+ self.actual_count = v.to_s.size + 1
37
+ self.max_count = self.actual_count
38
+ end
39
+ end
40
+ self.str = v.to_s
27
41
  end
28
42
  end
29
43
 
30
- def is_a_null_pointer?
31
- self.referent_identifier == 0
44
+ def clear
45
+ # Make sure #max_count and #offset are not cleared out
46
+ self.str.clear
47
+ self.actual_count.clear
48
+ end
49
+
50
+ def to_s
51
+ self.str.to_s
32
52
  end
33
53
  end
34
54
 
35
- # An NDR Conformant and Varying String representation as defined in
36
- # [Transfer Syntax NDR - Conformant and Varying Strings](http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_04_02)
37
- # The string elements are Stringz16 (unicode)
38
- class NdrString < BinData::Primitive
55
+ # An NDR Uni-dimensional Conformant Array of Bytes representation as defined in
56
+ # [Transfer Syntax NDR - Uni-dimensional Conformant Arrays](https://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_03_02)
57
+ class NdrLpByte < BinData::Primitive
39
58
  endian :little
40
59
 
41
- uint32 :max_count
42
- uint32 :offset, initial_value: 0
43
- uint32 :actual_count
44
- stringz16 :str, read_length: -> { actual_count }, onlyif: -> { actual_count > 0 }
60
+ uint32 :max_count, initial_value: -> { self.elements.size }
61
+ array :elements, type: :uint8, read_until: -> { index == self.max_count - 1 }, onlyif: -> { self.max_count > 0 }
45
62
 
46
63
  def get
47
- self.actual_count == 0 ? 0 : self.str
64
+ self.elements
48
65
  end
49
66
 
50
67
  def set(v)
51
- if v.is_a?(Integer) && v == 0
52
- self.actual_count = 0
53
- else
54
- self.str = v
55
- self.max_count = self.actual_count = str.to_binary_s.size / 2
56
- end
68
+ v = v.elements if v.is_a?(self.class)
69
+ self.elements = v.to_ary
70
+ self.max_count = self.elements.size unless self.elements.equal?(v)
57
71
  end
58
72
  end
59
73
 
60
- # A pointer to a NdrString structure
61
- class NdrLpStr < NdrTopLevelFullPointer
74
+ # An NDR Uni-dimensional Conformant-varying Arrays of bytes representation as defined in:
75
+ # [Transfer Syntax NDR - NDR Constructed Types](http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_03_04)
76
+ class NdrByteArray < BinData::Primitive
62
77
  endian :little
63
78
 
64
- ndr_string :referent, onlyif: -> { !is_a_null_pointer? }
79
+ uint32 :max_count, initial_value: -> { self.actual_count }
80
+ uint32 :offset, initial_value: 0
81
+ uint32 :actual_count, initial_value: -> { self.bytes.size }
82
+ array :bytes, :type => :uint8, initial_length: -> { self.actual_count }
65
83
 
66
- def to_s
67
- is_a_null_pointer? ? "\0" : self.referent
84
+ def get
85
+ self.bytes
86
+ end
87
+
88
+ def set(v)
89
+ v = v.bytes if v.is_a?(self.class)
90
+ self.bytes = v.to_ary
91
+ self.max_count = self.bytes.size unless self.bytes.equal?(v)
68
92
  end
69
93
  end
70
94
 
@@ -72,6 +96,7 @@ module RubySMB
72
96
  # [IDL Data Type Declarations - Basic Type Declarations](http://pubs.opengroup.org/onlinepubs/9629399/apdxn.htm#tagcjh_34_01)
73
97
  class NdrContextHandle < BinData::Primitive
74
98
  endian :little
99
+
75
100
  uint32 :context_handle_attributes
76
101
  uuid :context_handle_uuid
77
102
 
@@ -91,30 +116,170 @@ module RubySMB
91
116
  end
92
117
  end
93
118
 
94
- # A pointer to a DWORD
95
- class NdrLpDword < NdrTopLevelFullPointer
119
+ # An NDR Top-level Full Pointers representation as defined in
120
+ # [Transfer Syntax NDR - Top-level Full Pointers](http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_11_01)
121
+ # This class must be inherited and the subclass must have a #referent property
122
+ class NdrPointer < BinData::Primitive
96
123
  endian :little
97
124
 
98
- uint32 :referent, onlyif: -> { !is_a_null_pointer? }
125
+ uint32 :referent_id, initial_value: 0
126
+
127
+ def do_read(io)
128
+ self.referent_id.do_read(io)
129
+ if process_referent?
130
+ self.referent.do_read(io) unless self.referent_id == 0
131
+ end
132
+ end
133
+
134
+ def do_write(io)
135
+ self.referent_id.do_write(io)
136
+ if process_referent?
137
+ self.referent.do_write(io) unless self.referent_id == 0
138
+ end
139
+ end
140
+
141
+ def set(v)
142
+ if v == :null
143
+ self.referent.clear
144
+ self.referent_id = 0
145
+ else
146
+ if self.referent.respond_to?(:set)
147
+ self.referent.set(v)
148
+ else
149
+ self.referent = v
150
+ end
151
+ self.referent_id = rand(0xFFFFFFFF) if self.referent_id == 0
152
+ end
153
+ end
154
+
155
+ def get
156
+ if self.referent_id == 0
157
+ :null
158
+ else
159
+ self.referent
160
+ end
161
+ end
162
+
163
+ def process_referent?
164
+ current_parent = parent
165
+ loop do
166
+ return true unless current_parent
167
+ return false if current_parent.is_a?(NdrStruct)
168
+ current_parent = current_parent.parent
169
+ end
170
+ end
99
171
  end
100
172
 
101
- # An NDR Uni-dimensional Conformant-varying Arrays representation as defined in:
102
- # [Transfer Syntax NDR - NDR Constructed Types](http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagcjh_19_03_03_04)
103
- class NdrLpByte < BinData::Record
173
+ # A pointer to a NdrString structure
174
+ class NdrLpStr < NdrPointer
175
+ endian :little
176
+
177
+ ndr_string :referent, onlyif: -> { self.referent_id != 0 }
178
+ end
179
+
180
+ class NdrLpDword < NdrPointer
181
+ endian :little
182
+
183
+ uint32 :referent, onlyif: -> { self.referent_id != 0 }
184
+ end
185
+
186
+ # A pointer to an NDR Uni-dimensional Conformant-varying Arrays of bytes
187
+ class NdrLpByteArray < NdrPointer
104
188
  endian :little
105
189
 
106
- uint32 :referent_identifier, initial_value: 0x00020000
107
- uint32 :max_count, initial_value: -> { actual_count }, onlyif: -> { referent_identifier != 0 }
108
- uint32 :offset, initial_value: 0, onlyif: -> { referent_identifier != 0 }
109
- uint32 :actual_count, initial_value: -> { bytes.size }, onlyif: -> { referent_identifier != 0 }
110
- array :bytes, :type => :uint8, initial_length: -> { actual_count }, onlyif: -> { referent_identifier != 0 }
190
+ ndr_byte_array :referent, onlyif: -> { self.referent_id != 0 }
191
+
192
+ def set(v)
193
+ if v != :null && v.is_a?(NdrLpByteArray)
194
+ super(v.referent)
195
+ else
196
+ super(v)
197
+ end
198
+ end
111
199
  end
112
200
 
113
201
  # A pointer to a Windows FILETIME structure
114
- class NdrLpFileTime < NdrTopLevelFullPointer
202
+ class NdrLpFileTime < NdrPointer
115
203
  endian :little
116
204
 
117
- file_time :referent, onlyif: -> { !is_a_null_pointer? }
205
+ file_time :referent, onlyif: -> { self.referent_id != 0 }
206
+ end
207
+
208
+ # A generic NDR structure that implements logic to #read and #write
209
+ # (#to_binary_s) in case the structure contains BinData::Array or
210
+ # NdrPointer fields. This class must be inherited.
211
+ class NdrStruct < BinData::Record
212
+
213
+ def do_read(io)
214
+ super(io)
215
+ each_pair do |_name, field|
216
+ case field
217
+ when BinData::Array
218
+ field.each do |element|
219
+ next unless element.is_a?(NdrPointer)
220
+ next if element.referent_id == 0
221
+ pad = (4 - io.offset % 4) % 4
222
+ io.seekbytes(pad) if pad > 0
223
+ element.referent.do_read(io)
224
+ end
225
+ when NdrPointer
226
+ next if field.referent_id == 0
227
+ pad = (4 - io.offset % 4) % 4
228
+ io.seekbytes(pad) if pad > 0
229
+ field.referent.do_read(io)
230
+ end
231
+ end
232
+ end
233
+
234
+ def do_write(io)
235
+ super(io)
236
+ each_pair do |_name, field|
237
+ case field
238
+ when BinData::Array
239
+ field.each do |element|
240
+ next unless element.is_a?(NdrPointer)
241
+ next if element.referent_id == 0
242
+ pad = (4 - io.offset % 4) % 4
243
+ io.writebytes("\x00" * pad + element.referent.to_binary_s)
244
+ end
245
+ when NdrPointer
246
+ next if field.referent_id == 0
247
+ pad = (4 - io.offset % 4) % 4
248
+ io.writebytes("\x00" * pad + field.referent.to_binary_s)
249
+ end
250
+ end
251
+ end
252
+ end
253
+
254
+ class NdrStringPtrsw < NdrStruct
255
+ endian :little
256
+
257
+ uint32 :max_count, initial_value: -> { self.elements.size }
258
+ array :elements, type: :ndr_lp_str, read_until: -> { index == self.max_count - 1 }, onlyif: -> { self.max_count > 0 }
259
+
260
+ def get
261
+ self.elements
262
+ end
263
+
264
+ def set(v)
265
+ v = v.elements if v.is_a?(self.class)
266
+ self.elements = v.to_ary
267
+ self.max_count = self.elements.size unless self.elements.equal?(v)
268
+ end
269
+
270
+ def do_num_bytes
271
+ to_binary_s.size
272
+ end
273
+ end
274
+
275
+ class NdrLpStringPtrsw < NdrPointer
276
+ endian :little
277
+
278
+ ndr_string_ptrsw :referent, onlyif: -> { self.referent_id != 0 }
279
+
280
+ def set(v)
281
+ super(v.respond_to?(:to_ary) ? v.to_ary : v)
282
+ end
118
283
  end
119
284
  end
120
285
  end