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
@@ -0,0 +1,108 @@
1
+ module RubySMB
2
+ module SMB2
3
+
4
+ # An SMB2 PREAUTH_INTEGRITY_CAPABILITIES context struct as defined in
5
+ # [2.2.3.1.1 SMB2_PREAUTH_INTEGRITY_CAPABILITIES](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5a07bd66-4734-4af8-abcf-5a44ff7ee0e5)
6
+ class PreauthIntegrityCapabilities < BinData::Record
7
+ SHA_512 = 0x0001
8
+ HASH_ALGORITM_MAP = {
9
+ SHA_512 => 'SHA512'
10
+ }
11
+
12
+ endian :little
13
+
14
+ uint16 :hash_algorithm_count, label: 'Hash Algorithm Count', initial_value: -> { hash_algorithms.size }
15
+ uint16 :salt_length, label: 'Salt Length', initial_value: -> { salt.num_bytes }
16
+ array :hash_algorithms, label: 'Hash Algorithms', type: :uint16, initial_length: -> { hash_algorithm_count }
17
+ string :salt, label: 'Salt', read_length: -> { salt_length }
18
+ end
19
+
20
+ # An SMB2 ENCRYPTION_CAPABILITIES context struct as defined in
21
+ # [2.2.3.1.2 SMB2_ENCRYPTION_CAPABILITIES](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/16693be7-2b27-4d3b-804b-f605bde5bcdd)
22
+ class EncryptionCapabilities < BinData::Record
23
+ AES_128_CCM = 0x0001
24
+ AES_128_GCM = 0x0002
25
+ ENCRYPTION_ALGORITHM_MAP = {
26
+ AES_128_CCM => 'AES-128-CCM',
27
+ AES_128_GCM => 'AES-128-GCM'
28
+ }
29
+
30
+ endian :little
31
+
32
+ uint16 :cipher_count, label: 'Cipher Count', initial_value: -> { ciphers.size }
33
+ array :ciphers, label: 'Ciphers', type: :uint16, initial_length: -> { cipher_count }
34
+ end
35
+
36
+ # An SMB2 COMPRESSION_CAPABILITIES context struct as defined in
37
+ # [2.2.3.1.3 SMB2_COMPRESSION_CAPABILITIES](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/78e0c942-ab41-472b-b117-4a95ebe88271)
38
+ class CompressionCapabilities < BinData::Record
39
+ # Flags
40
+ # Chained compression is not supported.
41
+ SMB2_COMPRESSION_CAPABILITIES_FLAG_NONE = 0x00000000
42
+ # Chained compression is supported on this connection.
43
+ SMB2_COMPRESSION_CAPABILITIES_FLAG_CHAINED = 0x00000001
44
+
45
+ # Compression Algorithms
46
+ NONE = 0x0000
47
+ LZNT1 = 0x0001
48
+ LZ77 = 0x0002
49
+ LZ77_Huffman = 0x0003
50
+ Pattern_V1 = 0x0004
51
+ COMPRESSION_ALGORITHM_MAP = {
52
+ NONE => 'NONE',
53
+ LZNT1 => 'LZNT1',
54
+ LZ77 => 'LZ77',
55
+ LZ77_Huffman => 'LZ77_Huffman',
56
+ Pattern_V1 => 'Pattern_V1'
57
+ }
58
+
59
+ endian :little
60
+
61
+ uint16 :compression_algorithm_count, label: 'Compression Algorithm Count', initial_value: -> { compression_algorithms.size }
62
+ uint16 :padding, label: 'Padding', initial_value: 0
63
+ uint32 :flags, label: 'Flags'
64
+ array :compression_algorithms, label: 'Compression Algorithms', type: :uint16, initial_length: -> { compression_algorithm_count }
65
+ end
66
+
67
+ # An SMB2 NETNAME_NEGOTIATE_CONTEXT_ID context struct as defined in
68
+ # [2.2.3.1.4 SMB2_NETNAME_NEGOTIATE_CONTEXT_ID](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/ca6726bd-b9cf-43d9-b0bc-d127d3c993b3)
69
+ class NetnameNegotiateContextId < BinData::Record
70
+ endian :little
71
+
72
+ stringz16 :net_name, label: 'Net Name'
73
+ end
74
+
75
+
76
+ # An SMB2 NEGOTIATE_CONTEXT struct as defined in
77
+ # [2.2.3.1 SMB2 NEGOTIATE_CONTEXT Request Values](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/15332256-522e-4a53-8cd7-0bd17678a2f7)
78
+ class NegotiateContext < BinData::Record
79
+ # The NegotiateContext Data field contains a list of preauthentication integrity hash functions as well as an optional salt value, as specified in section 2.2.3.1.1.
80
+ SMB2_PREAUTH_INTEGRITY_CAPABILITIES = 0x0001
81
+ # The NegotiateContext Data field contains a list of encryption algorithms, as specified in section 2.2.3.1.2.
82
+ SMB2_ENCRYPTION_CAPABILITIES = 0x0002
83
+ # The NegotiateContext Data field contains a list of compression algorithms, as specified in section 2.2.3.1.3.
84
+ SMB2_COMPRESSION_CAPABILITIES = 0x0003
85
+ # The NegotiateContext Data field contains the server name to which the client connects.
86
+ SMB2_NETNAME_NEGOTIATE_CONTEXT_ID = 0x0005
87
+
88
+ endian :little
89
+
90
+ string :pad, label: 'Padding', length: -> { pad_length }
91
+ uint16 :context_type, label: 'Context Type'
92
+ uint16 :data_length, label: 'Data Length', initial_value: -> { data.num_bytes }
93
+ uint32 :reserved, label: 'Reserved', initial_value: 0
94
+ choice :data, label: 'Data', selection: -> { context_type } do
95
+ preauth_integrity_capabilities SMB2_PREAUTH_INTEGRITY_CAPABILITIES, label: 'Preauthentication Integrity Capabilities'
96
+ encryption_capabilities SMB2_ENCRYPTION_CAPABILITIES, label: 'Encryption Capabilities'
97
+ compression_capabilities SMB2_COMPRESSION_CAPABILITIES, label: 'Compression Capabilities'
98
+ netname_negotiate_context_id SMB2_NETNAME_NEGOTIATE_CONTEXT_ID, label: 'Netname Negotiate Context ID'
99
+ end
100
+
101
+ def pad_length
102
+ offset = pad.abs_offset % 8
103
+ (8 - offset) % 8
104
+ end
105
+
106
+ end
107
+ end
108
+ end
@@ -30,6 +30,8 @@ module RubySMB
30
30
  require 'ruby_smb/smb2/packet/write_response'
31
31
  require 'ruby_smb/smb2/packet/ioctl_request'
32
32
  require 'ruby_smb/smb2/packet/ioctl_response'
33
+ require 'ruby_smb/smb2/packet/transform_header'
34
+ require 'ruby_smb/smb2/packet/compression_transform_header'
33
35
  end
34
36
  end
35
37
  end
@@ -0,0 +1,41 @@
1
+ module RubySMB
2
+ module SMB2
3
+ module Packet
4
+ # An SMB2 COMPRESSION_TRANSFORM_HEADER Packet as defined in
5
+ # [2.2.42 SMB2 COMPRESSION_TRANSFORM_HEADER](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/1d435f21-9a21-4f4c-828e-624a176cf2a0)
6
+ class CompressionTransformHeader < BinData::Record
7
+ endian :little
8
+
9
+ bit32 :protocol, label: 'Protocol ID Field', initial_value: 0xFC534D42
10
+ uint32 :original_compressed_segment_size, label: 'Original Compressed Segment Size'
11
+ uint16 :compression_algorithm, label: 'Compression Algorithm'
12
+ uint16 :flags, label: 'Flags'
13
+ uint32 :offset, label: 'Offset / Length'
14
+ end
15
+
16
+ # An SMB2 SMB2_COMPRESSION_TRANSFORM_HEADER_PAYLOAD Packet as defined in
17
+ # [2.2.42.1 SMB2_COMPRESSION_TRANSFORM_HEADER_PAYLOAD](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/8898e8e7-f1b2-47f5-a525-2ce5bad6db64)
18
+ class Smb2CompressionPayloadHeader < BinData::Record
19
+ endian :little
20
+ hide :reserved
21
+
22
+ uint16 :algorithm_id, label: 'Algorithm ID'
23
+ uint16 :reserved
24
+ uint32 :payload_length, label: 'Compressed Payload Length'
25
+ end
26
+
27
+ # An SMB2 SMB2_COMPRESSION_PATTERN_PAYLOAD_V1 Packet as defined in
28
+ # [2.2.42.2 SMB2_COMPRESSION_PATTERN_PAYLOAD_V1](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/f6859837-395a-4d0a-8971-1fc3919e2d09)
29
+ class Smb2CompressionPatternPayloadV1 < BinData::Record
30
+ endian :little
31
+ hide :reserved1, :reserved2
32
+
33
+ uint8 :pattern, label: 'Pattern'
34
+ uint8 :reserved1
35
+ uint16 :reserved2
36
+ uint32 :repetitions, label: 'Repetitions'
37
+ end
38
+ end
39
+ end
40
+ end
41
+
@@ -1,3 +1,5 @@
1
+ require 'ruby_smb/smb2/negotiate_context'
2
+
1
3
  module RubySMB
2
4
  module SMB2
3
5
  module Packet
@@ -8,23 +10,30 @@ module RubySMB
8
10
 
9
11
  endian :little
10
12
  smb2_header :smb2_header
11
- uint16 :structure_size, label: 'Structure Size', initial_value: 36
12
- uint16 :dialect_count, label: 'Dialect Count'
13
- smb2_security_mode :security_mode
14
- uint16 :reserved1, label: 'Reserved', initial_value: 0
15
- smb2_capabilities :capabilities
16
- string :client_guid, label: 'Client GUID', length: 16
17
- file_time :client_start_time, label: 'Client Start Time', initial_value: 0
18
- array :dialects, label: 'Dialects', type: :uint16, read_until: :eof
19
-
20
- # Adds a dialect to the Dialects array and increments the dialect count
13
+ uint16 :structure_size, label: 'Structure Size', initial_value: 36
14
+ uint16 :dialect_count, label: 'Dialect Count', initial_value: -> { dialects.size }
15
+ smb2_security_mode :security_mode, label: 'Security Mode'
16
+ uint16 :reserved1, label: 'Reserved', initial_value: 0
17
+ smb2_capabilities :capabilities, label: 'Capabilities'
18
+ string :client_guid, label: 'Client GUID', length: 16
19
+ struct :negotiate_context_info, label: 'Negotiate Context Info', onlyif: -> { has_negotiate_context? } do
20
+ uint32 :negotiate_context_offset, label: 'Negotiate Context Offset', initial_value: -> { negotiate_context_list.abs_offset }
21
+ uint16 :negotiate_context_count, label: 'Negotiate Context Count', initial_value: -> { negotiate_context_list.size }
22
+ uint16 :reserved2, label: 'Reserved', initial_value: 0
23
+ end
24
+ file_time :client_start_time, label: 'Client Start Time', initial_value: 0, onlyif: -> { !has_negotiate_context? }
25
+ array :dialects, label: 'Dialects', type: :uint16, initial_length: -> { dialect_count }
26
+ string :pad, label: 'Padding', length: -> { pad_length(self.dialects) }, onlyif: -> { has_negotiate_context? }
27
+ array :negotiate_context_list, label: 'Negotiate Context List', type: :negotiate_context, onlyif: -> { has_negotiate_context? }, read_until: :eof
28
+
29
+ # Adds a dialect to the Dialects array
21
30
  #
22
31
  # @param [Fixnum] the numeric code for the dialect you wish to add
23
32
  # @return [Array<Fixnum>] the array of all currently selected dialects
33
+ # @raise [ArgumentError] if the dialect is not an Integer
24
34
  def add_dialect(dialect)
25
- return ArgumentError, 'Must be a number' unless dialect.is_a? Integer
26
- self.dialect_count += 1
27
- dialects << dialect
35
+ raise ArgumentError, 'Must be a number' unless dialect.is_a? Integer
36
+ self.dialects << dialect
28
37
  end
29
38
 
30
39
  # Takes an array of dialects and sets it on the packet. Also updates
@@ -35,12 +44,40 @@ module RubySMB
35
44
  # @return [Array<Fixnum>] the current value of the dialects array
36
45
  def set_dialects(add_dialects = [])
37
46
  self.dialects = []
38
- self.dialect_count = 0
39
47
  add_dialects.each do |dialect|
40
48
  add_dialect(dialect)
41
49
  end
42
50
  dialects
43
51
  end
52
+
53
+ # Adds a Negotiate Context to the #negotiate_context_list
54
+ #
55
+ # @param [NegotiateContext] the Negotiate Context structure you wish to add
56
+ # @return [Array<Fixnum>] the array of all currently added Negotiate Contexts
57
+ # @raise [ArgumentError] if the dialect is not a NegotiateContext structure
58
+ def add_negotiate_context(nc)
59
+ raise ArgumentError, 'Must be a NegotiateContext' unless nc.is_a? NegotiateContext
60
+ previous_element = negotiate_context_list.last || negotiate_context_list
61
+ pad_length = pad_length(previous_element)
62
+ self.negotiate_context_list << nc
63
+ self.negotiate_context_list.last.pad = "\x00" * pad_length
64
+ self.negotiate_context_list
65
+ end
66
+
67
+
68
+ private
69
+
70
+ # Determines the correct length for the padding, so that the next
71
+ # field is 8-byte aligned.
72
+ def pad_length(prev_element)
73
+ offset = (prev_element.abs_offset + prev_element.to_binary_s.length) % 8
74
+ (8 - offset) % 8
75
+ end
76
+
77
+ # Return true if the dialect version requires Negotiate Contexts
78
+ def has_negotiate_context?
79
+ dialects.any? { |dialect| dialect.to_i == 0x0311 }
80
+ end
44
81
  end
45
82
  end
46
83
  end
@@ -1,3 +1,5 @@
1
+ require 'ruby_smb/smb2/negotiate_context'
2
+
1
3
  module RubySMB
2
4
  module SMB2
3
5
  module Packet
@@ -8,11 +10,12 @@ module RubySMB
8
10
 
9
11
  endian :little
10
12
  smb2_header :smb2_header
11
- uint16 :structure_size, label: 'Structure Size', initial_value: 65
13
+ uint16 :structure_size, label: 'Structure Size', initial_value: 65
12
14
  smb2_security_mode :security_mode
13
15
  uint16 :dialect_revision, label: 'Dialect Revision'
14
- uint16 :negotiate_context_count, label: 'Negotiate Context Count', initial_value: 0
15
- string :server_guid, label: 'Server GUID', length: 16
16
+ uint16 :negotiate_context_count, label: 'Negotiate Context Count', initial_value: -> { negotiate_context_list.size }, onlyif: -> { has_negotiate_context? }
17
+ uint16 :reserved1, label: 'Reserved', initial_value: 0, onlyif: -> { !has_negotiate_context? }
18
+ string :server_guid, label: 'Server GUID', length: 16
16
19
  smb2_capabilities :capabilities
17
20
  uint32 :max_transact_size, label: 'Max Transaction Size'
18
21
  uint32 :max_read_size, label: 'Max Read Size'
@@ -21,13 +24,56 @@ module RubySMB
21
24
  file_time :server_start_time, label: 'Server Start Time'
22
25
  uint16 :security_buffer_offset, label: 'Offset to Security Buffer'
23
26
  uint16 :security_buffer_length, label: 'Security Buffer Length', initial_value: -> { security_buffer.length }
24
- uint32 :negotiate_context_offset, label: 'Offset to Negotiate Context'
27
+ uint32 :negotiate_context_offset, label: 'Offset to Negotiate Context', onlyif: -> { has_negotiate_context? }
28
+ uint32 :reserved2, label: 'Reserved', initial_value: 0, onlyif: -> { !has_negotiate_context? }
25
29
  string :security_buffer, label: 'Security Buffer', read_length: :security_buffer_length
30
+ string :pad, label: 'Padding', length: -> { pad_length(self.security_buffer) }, onlyif: -> { has_negotiate_context? }
31
+ array :negotiate_context_list, label: 'Negotiate Context List', initial_length: -> { negotiate_context_count }, type: :negotiate_context, onlyif: -> { has_negotiate_context? }
26
32
 
27
33
  def initialize_instance
28
34
  super
29
35
  smb2_header.flags.reply = 1
30
36
  end
37
+
38
+ # Find the first Negotiate Context structure that matches the given
39
+ # context type
40
+ #
41
+ # @param [Integer] the Negotiate Context structure you wish to add
42
+ # @return [NegotiateContext] the Negotiate Context structure or nil if
43
+ # not found
44
+ def find_negotiate_context(type)
45
+ negotiate_context_list.find { |nc| nc.context_type == type }
46
+ end
47
+
48
+ # Adds a Negotiate Context to the #negotiate_context_list
49
+ #
50
+ # @param [NegotiateContext] the Negotiate Context structure you wish to add
51
+ # @return [Array<Fixnum>] the array of all currently added Negotiate Contexts
52
+ # @raise [ArgumentError] if the dialect is not a NegotiateContext structure
53
+ def add_negotiate_context(nc)
54
+ raise ArgumentError, 'Must be a NegotiateContext' unless nc.is_a? NegotiateContext
55
+ previous_element = negotiate_context_list.last || negotiate_context_list
56
+ pad_length = pad_length(previous_element)
57
+ self.negotiate_context_list << nc
58
+ self.negotiate_context_list.last.pad = "\x00" * pad_length
59
+ self.negotiate_context_list
60
+ end
61
+
62
+
63
+ private
64
+
65
+ # Determines the correct length for the padding, so that the next
66
+ # field is 8-byte aligned.
67
+ def pad_length(prev_element)
68
+ offset = (prev_element.abs_offset + prev_element.to_binary_s.length) % 8
69
+ (8 - offset) % 8
70
+ end
71
+
72
+ # Return true if the dialect version requires Negotiate Contexts
73
+ def has_negotiate_context?
74
+ dialect_revision == 0x0311
75
+ end
76
+
31
77
  end
32
78
  end
33
79
  end
@@ -0,0 +1,84 @@
1
+ module RubySMB
2
+ module SMB2
3
+ module Packet
4
+ # An SMB2 TRANSFORM_HEADER Packet as defined in
5
+ # [2.2.41 SMB2 TRANSFORM_HEADER](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/d6ce2327-a4c9-4793-be66-7b5bad2175fa)
6
+ class TransformHeader < BinData::Record
7
+ endian :little
8
+ hide :reserved0
9
+
10
+ endian :little
11
+ bit32 :protocol, label: 'Protocol ID Field', initial_value: 0xFD534D42
12
+ string :signature, label: 'Signature', length: 16
13
+ string :nonce, label: 'Nonce', length: 16
14
+ uint32 :original_message_size, label: 'Original Message Size'
15
+ uint16 :reserved0
16
+ uint16 :flags, label: 'Flags / Encryption Algorithm'
17
+ uint64 :session_id, label: 'Session ID'
18
+ array :encrypted_data, label: 'Encrypted Data', type: :uint8, read_until: :eof
19
+
20
+ def decrypt(key, algorithm: 'AES-128-GCM')
21
+ auth_data = self.to_binary_s[20...52]
22
+ encrypted_data = self.encrypted_data.to_ary.pack('C*')
23
+
24
+ case algorithm
25
+ when 'AES-128-CCM'
26
+ cipher = OpenSSL::CCM.new('AES', key, 16)
27
+ unencrypted_data = cipher.decrypt(encrypted_data + self.signature, self.nonce[0...11], auth_data)
28
+ unless unencrypted_data.length > 0
29
+ raise OpenSSL::Cipher::CipherError # raised for consistency with GCM mode
30
+ end
31
+ when 'AES-128-GCM'
32
+ cipher = OpenSSL::Cipher.new(algorithm).decrypt
33
+ cipher.key = key
34
+ cipher.iv = self.nonce[0...12]
35
+ cipher.auth_data = auth_data
36
+ cipher.auth_tag = self.signature
37
+ unencrypted_data = cipher.update(encrypted_data)
38
+ cipher.final # raises OpenSSL::Cipher::CipherError on signature failure
39
+ else
40
+ raise ArgumentError.new('Invalid algorithm, must be either AES-128-CCM or AES-128-GCM')
41
+ end
42
+
43
+ unencrypted_data[0...self.original_message_size]
44
+ rescue Exception => e
45
+ raise RubySMB::Error::EncryptionError, "Error while decrypting with '#{algorithm}' (#{e.class}: #{e})"
46
+ end
47
+
48
+ def encrypt(unencrypted_data, key, algorithm: 'AES-128-GCM')
49
+ if unencrypted_data.is_a? BinData::Record
50
+ unencrypted_data = unencrypted_data.to_binary_s
51
+ end
52
+
53
+ self.original_message_size.assign(unencrypted_data.length)
54
+
55
+ case algorithm
56
+ when 'AES-128-CCM'
57
+ cipher = OpenSSL::CCM.new('AES', key, 16)
58
+ random_iv = OpenSSL::Random.random_bytes(11)
59
+ self.nonce.assign(random_iv)
60
+ result = cipher.encrypt(unencrypted_data, random_iv, self.to_binary_s[20...52])
61
+ encrypted_data = result[0...-16]
62
+ auth_tag = result[-16..-1]
63
+ when 'AES-128-GCM'
64
+ cipher = OpenSSL::Cipher.new(algorithm).encrypt
65
+ cipher.iv_len = 12
66
+ cipher.key = key
67
+ self.nonce.assign(cipher.random_iv)
68
+ cipher.auth_data = self.to_binary_s[20...52]
69
+ encrypted_data = cipher.update(unencrypted_data) + cipher.final
70
+ auth_tag = cipher.auth_tag
71
+ else
72
+ raise ArgumentError.new('Invalid algorithm, must be either AES-128-CCM or AES-128-GCM')
73
+ end
74
+
75
+ self.encrypted_data.assign(encrypted_data.bytes)
76
+ self.signature.assign(auth_tag)
77
+ nil
78
+ rescue Exception => e
79
+ raise RubySMB::Error::EncryptionError, "Error while encrypting with '#{algorithm}' (#{e.class}: #{e})"
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -1,22 +1,108 @@
1
1
  module RubySMB
2
2
  module SMB2
3
3
  module Packet
4
+
5
+
6
+ # An SMB2 RemotedIdentityTreeConnectContext Packet as defined in
7
+ # [2.2.9.2.1 SMB2_REMOTED_IDENTITY_TREE_CONNECT Context](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/ee7ff411-93e0-484f-9f73-31916fee4cb8)
8
+ # TODO: implement helper methods to add each Remote Identity element
9
+ class RemotedIdentityTreeConnectContext < BinData::Record
10
+ endian :little
11
+ uint16 :ticket_type, label: 'Ticket Type', initial_value: 0x0001
12
+ uint16 :ticket_size, label: 'Ticket Size', initial_value: -> { num_bytes }
13
+ uint16 :user, label: 'User'
14
+ uint16 :user_name, label: 'User Name'
15
+ uint16 :domain, label: 'Domain'
16
+ uint16 :groups, label: 'Groups'
17
+ uint16 :restricted_groups, label: 'Restricted Groups'
18
+ uint16 :privileges, label: 'Privileges'
19
+ uint16 :primary_group, label: 'Primary Group'
20
+ uint16 :owner, label: 'Owner'
21
+ uint16 :default_dacl, label: 'Default DACL'
22
+ uint16 :device_groups, label: 'Device Groups'
23
+ uint16 :user_claims, label: 'User Claims'
24
+ uint16 :device_claims, label: 'Device Claims'
25
+ string :ticket_info, label: 'Ticket Info', read_length: -> { ticket_size - ticket_info.rel_offset}
26
+ end
27
+
28
+ # An SMB2 TreeConnectContext Packet as defined in
29
+ # [2.2.9.2 SMB2 TREE_CONNECT_CONTEXT Request Values](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/06eaaabc-caca-4776-9daf-82439e90dacd)
30
+ class TreeConnectContext < BinData::Record
31
+
32
+ # Context Types
33
+
34
+ # This value is reserved.
35
+ SMB2_RESERVED_TREE_CONNECT_CONTEXT_ID = 0x0000
36
+ # The Data field contains remoted identity tree connect context data as
37
+ # specified in section [2.2.9.2.1](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/ee7ff411-93e0-484f-9f73-31916fee4cb8)
38
+ SMB2_REMOTED_IDENTITY_TREE_CONNECT_CONTEXT_ID = 0x0001
39
+
40
+ endian :little
41
+ uint16 :context_type, label: 'Context Type'
42
+ uint16 :data_length, label: 'Data Length', initial_value: -> { data.to_binary_s.size }
43
+ uint32 :reserved, label: 'Reserved'
44
+ choice :data, label: 'Data', selection: -> { context_type } do
45
+ remoted_identity_tree_connect_context SMB2_REMOTED_IDENTITY_TREE_CONNECT_CONTEXT_ID, label: 'Remoted Identity Tree Connect Context'
46
+ end
47
+
48
+ end
49
+
50
+ # An SMB2 TreeConnectRequestExtension Packet as defined in
51
+ # [2.2.9.1 SMB2 TREE_CONNECT Request Extension](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/9ca7328b-b6ca-41a7-9773-0fa237261b76)
52
+ class TreeConnectRequestExtension < BinData::Record
53
+ endian :little
54
+ uint32 :tree_connect_context_offset, label: 'Tree Connect Context Offset', initial_value: -> { tree_connect_contexts.rel_offset }
55
+ uint16 :tree_connect_context_count, label: 'Tree Connect Context Count', initial_value: -> { tree_connect_contexts.size }
56
+ string :reserved, label: 'Reserved', length: 10
57
+ string16 :path, label: 'Path Buffer'
58
+ array :tree_connect_contexts, label: 'Tree Connect Contexts', type: :tree_connect_context, initial_length: -> { tree_connect_context_count }
59
+ end
60
+
4
61
  # An SMB2 TreeConnectRequest Packet as defined in
5
62
  # [2.2.9 SMB2 TREE_CONNECT Request](https://msdn.microsoft.com/en-us/library/cc246567.aspx)
6
63
  class TreeConnectRequest < RubySMB::GenericPacket
7
64
  COMMAND = RubySMB::SMB2::Commands::TREE_CONNECT
8
65
 
66
+ # Flags (SMB 3.1.1 only)
67
+
68
+ # The client has previously connected to the specified cluster share
69
+ # using the SMB dialect of the connection on which the request is received.
70
+ SMB2_TREE_CONNECT_FLAG_CLUSTER_RECONNECT = 0x0001
71
+ # The client can handle synchronous share redirects via a Share Redirect
72
+ # error context response as specified in section [2.2.2.2.2](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/f3073a8b-9f0f-47c0-91e5-ec3be9a49f37).
73
+ SMB2_TREE_CONNECT_FLAG_REDIRECT_TO_OWNER = 0x0002
74
+ # A tree connect request extension, as specified in section
75
+ # [2.2.9.1](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/9ca7328b-b6ca-41a7-9773-0fa237261b76),
76
+ # is present, starting at the Buffer field of this tree connect request.
77
+ SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT = 0x0003
78
+
9
79
  endian :little
10
80
  smb2_header :smb2_header
11
81
  uint16 :structure_size, label: 'Structure Size', initial_value: 9
82
+ # The flags field is only used by SMB 3.1.1, it must be 0 for other versions
12
83
  uint16 :flags, label: 'Flags', initial_value: 0x00
13
- uint16 :path_offset, label: 'Path Offset', initial_value: 0x48
14
- uint16 :path_length, label: 'Path Length', initial_value: -> { path.length }
15
- string :path, label: 'Path Buffer'
16
-
17
- def encode_path(path)
18
- self.path = path.encode('utf-16le')
84
+ # if SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT flag is set, #path_offset
85
+ # will have to be updated with the correct offset of the path name,
86
+ # which is located in the TreeConnect Context.
87
+ uint16 :path_offset, label: 'Path Offset', initial_value: -> do
88
+ if flags == SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT
89
+ tree_connect_request_extension.path.abs_offset
90
+ else
91
+ path.abs_offset
92
+ end
93
+ end
94
+ # if SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT flag is set, #path_length
95
+ # will have to be updated with the correct full share path name,
96
+ # which is located in the TreeConnect Context.
97
+ uint16 :path_length, label: 'Path Length', initial_value: -> do
98
+ if flags == SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT
99
+ tree_connect_request_extension.path.to_binary_s.length
100
+ else
101
+ path.to_binary_s.length
102
+ end
19
103
  end
104
+ string16 :path, label: 'Path Buffer', onlyif: -> { flags != SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT }
105
+ tree_connect_request_extension :tree_connect_request_extension, label: 'Tree Connect Request Extension', onlyif: -> { flags == SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT }
20
106
  end
21
107
  end
22
108
  end