ruby_smb 0.0.18 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/README.md +247 -7
- data/examples/anonymous_auth.rb +6 -3
- data/examples/append_file.rb +40 -0
- data/examples/authenticate.rb +12 -7
- data/examples/delete_file.rb +40 -0
- data/examples/list_directory.rb +45 -0
- data/examples/negotiate.rb +0 -1
- data/examples/negotiate_with_netbios_service.rb +36 -0
- data/examples/net_share_enum_all.rb +30 -0
- data/examples/read_file.rb +39 -0
- data/examples/rename_file.rb +41 -0
- data/examples/tree_connect.rb +2 -4
- data/examples/write_file.rb +40 -0
- data/lib/ruby_smb.rb +5 -0
- data/lib/ruby_smb/client.rb +196 -43
- data/lib/ruby_smb/client/authentication.rb +89 -48
- data/lib/ruby_smb/client/echo.rb +1 -4
- data/lib/ruby_smb/client/negotiation.rb +46 -45
- data/lib/ruby_smb/client/signing.rb +9 -16
- data/lib/ruby_smb/client/tree_connect.rb +8 -13
- data/lib/ruby_smb/client/utils.rb +79 -0
- data/lib/ruby_smb/dcerpc.rb +30 -0
- data/lib/ruby_smb/dcerpc/bind.rb +60 -0
- data/lib/ruby_smb/dcerpc/handle.rb +60 -0
- data/lib/ruby_smb/dcerpc/ndr.rb +41 -0
- data/lib/ruby_smb/dcerpc/request.rb +43 -0
- data/lib/ruby_smb/dcerpc/response.rb +46 -0
- data/lib/ruby_smb/dcerpc/srvsvc.rb +17 -0
- data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +93 -0
- data/lib/ruby_smb/dcerpc/uuid.rb +28 -0
- data/lib/ruby_smb/dispatcher.rb +7 -3
- data/lib/ruby_smb/dispatcher/base.rb +23 -14
- data/lib/ruby_smb/dispatcher/socket.rb +71 -51
- data/lib/ruby_smb/dispositions.rb +32 -0
- data/lib/ruby_smb/error.rb +24 -18
- data/lib/ruby_smb/field.rb +5 -2
- data/lib/ruby_smb/field/extended_attribute_flag.rb +1 -1
- data/lib/ruby_smb/field/file_time.rb +3 -1
- data/lib/ruby_smb/field/security_descriptor.rb +6 -6
- data/lib/ruby_smb/field/smb2_fileid.rb +11 -0
- data/lib/ruby_smb/field/smb_fea.rb +3 -3
- data/lib/ruby_smb/field/smb_fea_list.rb +3 -3
- data/lib/ruby_smb/field/smb_gea.rb +12 -0
- data/lib/ruby_smb/field/smb_gea_list.rb +13 -0
- data/lib/ruby_smb/field/string16.rb +14 -0
- data/lib/ruby_smb/field/stringz16.rb +3 -7
- data/lib/ruby_smb/field/utime.rb +11 -10
- data/lib/ruby_smb/fscc.rb +12 -0
- data/lib/ruby_smb/fscc/control_codes.rb +26 -0
- data/lib/ruby_smb/{field → fscc}/ea_info_array.rb +12 -14
- data/lib/ruby_smb/fscc/file_attributes.rb +29 -0
- data/lib/ruby_smb/{field → fscc}/file_full_ea_info.rb +5 -5
- data/lib/ruby_smb/fscc/file_information.rb +60 -0
- data/lib/ruby_smb/fscc/file_information/file_both_directory_information.rb +29 -0
- data/lib/ruby_smb/fscc/file_information/file_directory_information.rb +25 -0
- data/lib/ruby_smb/fscc/file_information/file_disposition_information.rb +15 -0
- data/lib/ruby_smb/fscc/file_information/file_full_directory_information.rb +26 -0
- data/lib/ruby_smb/fscc/file_information/file_id_both_directory_information.rb +31 -0
- data/lib/ruby_smb/fscc/file_information/file_id_full_directory_information.rb +28 -0
- data/lib/ruby_smb/fscc/file_information/file_names_information.rb +18 -0
- data/lib/ruby_smb/fscc/file_information/file_rename_information.rb +44 -0
- data/lib/ruby_smb/generic_packet.rb +40 -18
- data/lib/ruby_smb/gss.rb +49 -56
- data/lib/ruby_smb/impersonation_levels.rb +7 -2
- data/lib/ruby_smb/nbss.rb +16 -0
- data/lib/ruby_smb/nbss/negative_session_response.rb +30 -0
- data/lib/ruby_smb/nbss/session_header.rb +13 -0
- data/lib/ruby_smb/nbss/session_request.rb +13 -0
- data/lib/ruby_smb/smb1.rb +20 -17
- data/lib/ruby_smb/smb1/bit_field.rb +3 -0
- data/lib/ruby_smb/smb1/bit_field/create_options.rb +5 -5
- data/lib/ruby_smb/smb1/bit_field/file_status_flags.rb +18 -0
- data/lib/ruby_smb/smb1/bit_field/open2_access_mode.rb +11 -11
- data/lib/ruby_smb/smb1/bit_field/open2_open_mode.rb +1 -1
- data/lib/ruby_smb/smb1/bit_field/optional_support.rb +0 -1
- data/lib/ruby_smb/smb1/bit_field/security_flags.rb +15 -0
- data/lib/ruby_smb/smb1/bit_field/share_access.rb +2 -3
- data/lib/ruby_smb/smb1/bit_field/smb_ext_file_attributes.rb +21 -24
- data/lib/ruby_smb/smb1/bit_field/smb_file_attributes.rb +1 -1
- data/lib/ruby_smb/smb1/commands.rb +5 -0
- data/lib/ruby_smb/smb1/create_actions.rb +3 -5
- data/lib/ruby_smb/smb1/file.rb +289 -0
- data/lib/ruby_smb/smb1/oplock_levels.rb +2 -4
- data/lib/ruby_smb/smb1/packet.rb +10 -0
- data/lib/ruby_smb/smb1/packet/close_request.rb +31 -0
- data/lib/ruby_smb/smb1/packet/close_response.rb +28 -0
- data/lib/ruby_smb/smb1/packet/echo_request.rb +5 -7
- data/lib/ruby_smb/smb1/packet/echo_response.rb +5 -7
- data/lib/ruby_smb/smb1/packet/empty_packet.rb +1 -2
- data/lib/ruby_smb/smb1/packet/logoff_request.rb +1 -4
- data/lib/ruby_smb/smb1/packet/logoff_response.rb +1 -4
- data/lib/ruby_smb/smb1/packet/negotiate_response.rb +22 -0
- data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +22 -0
- data/lib/ruby_smb/smb1/packet/nt_create_andx_request.rb +62 -0
- data/lib/ruby_smb/smb1/packet/nt_create_andx_response.rb +66 -0
- data/lib/ruby_smb/smb1/packet/nt_trans.rb +1 -2
- data/lib/ruby_smb/smb1/packet/nt_trans/create_request.rb +19 -13
- data/lib/ruby_smb/smb1/packet/nt_trans/create_response.rb +8 -10
- data/lib/ruby_smb/smb1/packet/nt_trans/request.rb +11 -11
- data/lib/ruby_smb/smb1/packet/nt_trans/response.rb +10 -10
- data/lib/ruby_smb/smb1/packet/nt_trans/subcommands.rb +8 -1
- data/lib/ruby_smb/smb1/packet/read_andx_request.rb +84 -0
- data/lib/ruby_smb/smb1/packet/read_andx_response.rb +47 -0
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +2 -6
- data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +1 -4
- data/lib/ruby_smb/smb1/packet/session_setup_request.rb +1 -6
- data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -4
- data/lib/ruby_smb/smb1/packet/trans2.rb +8 -2
- data/lib/ruby_smb/smb1/packet/trans2/data_block.rb +6 -7
- data/lib/ruby_smb/smb1/packet/trans2/find_first2_request.rb +77 -0
- data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +87 -0
- data/lib/ruby_smb/smb1/packet/trans2/find_information_level.rb +32 -0
- data/lib/ruby_smb/smb1/packet/trans2/find_information_level/find_file_full_directory_info.rb +45 -0
- data/lib/ruby_smb/smb1/packet/trans2/find_next2_request.rb +77 -0
- data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +86 -0
- data/lib/ruby_smb/smb1/packet/trans2/open2_request.rb +10 -10
- data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +10 -12
- data/lib/ruby_smb/smb1/packet/trans2/request.rb +15 -17
- data/lib/ruby_smb/smb1/packet/trans2/request_secondary.rb +8 -10
- data/lib/ruby_smb/smb1/packet/trans2/response.rb +11 -13
- data/lib/ruby_smb/smb1/packet/trans2/set_file_information_request.rb +66 -0
- data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +57 -0
- data/lib/ruby_smb/smb1/packet/trans2/subcommands.rb +5 -2
- data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +4 -6
- data/lib/ruby_smb/smb1/packet/tree_connect_response.rb +5 -7
- data/lib/ruby_smb/smb1/packet/tree_disconnect_request.rb +2 -4
- data/lib/ruby_smb/smb1/packet/tree_disconnect_response.rb +2 -4
- data/lib/ruby_smb/smb1/packet/write_andx_request.rb +68 -0
- data/lib/ruby_smb/smb1/packet/write_andx_response.rb +35 -0
- data/lib/ruby_smb/smb1/resource_type.rb +18 -0
- data/lib/ruby_smb/smb1/tree.rb +188 -5
- data/lib/ruby_smb/smb2.rb +16 -11
- data/lib/ruby_smb/smb2/bit_field.rb +1 -0
- data/lib/ruby_smb/smb2/bit_field/file_access_mask.rb +1 -1
- data/lib/ruby_smb/smb2/bit_field/session_flags.rb +1 -2
- data/lib/ruby_smb/smb2/bit_field/share_flags.rb +4 -6
- data/lib/ruby_smb/smb2/create_context.rb +29 -0
- data/lib/ruby_smb/smb2/file.rb +251 -0
- data/lib/ruby_smb/smb2/info_type.rb +21 -0
- data/lib/ruby_smb/smb2/packet.rb +16 -0
- data/lib/ruby_smb/smb2/packet/close_request.rb +22 -0
- data/lib/ruby_smb/smb2/packet/close_response.rb +29 -0
- data/lib/ruby_smb/smb2/packet/create_request.rb +54 -0
- data/lib/ruby_smb/smb2/packet/create_response.rb +35 -0
- data/lib/ruby_smb/smb2/packet/echo_request.rb +1 -3
- data/lib/ruby_smb/smb2/packet/echo_response.rb +1 -3
- data/lib/ruby_smb/smb2/packet/error_packet.rb +1 -3
- data/lib/ruby_smb/smb2/packet/ioctl_request.rb +54 -0
- data/lib/ruby_smb/smb2/packet/ioctl_response.rb +39 -0
- data/lib/ruby_smb/smb2/packet/logoff_request.rb +1 -4
- data/lib/ruby_smb/smb2/packet/logoff_response.rb +1 -4
- data/lib/ruby_smb/smb2/packet/negotiate_request.rb +1 -1
- data/lib/ruby_smb/smb2/packet/negotiate_response.rb +1 -1
- data/lib/ruby_smb/smb2/packet/query_directory_request.rb +35 -0
- data/lib/ruby_smb/smb2/packet/query_directory_response.rb +45 -0
- data/lib/ruby_smb/smb2/packet/read_request.rb +30 -0
- data/lib/ruby_smb/smb2/packet/read_response.rb +26 -0
- data/lib/ruby_smb/smb2/packet/session_setup_request.rb +2 -5
- data/lib/ruby_smb/smb2/packet/session_setup_response.rb +4 -7
- data/lib/ruby_smb/smb2/packet/set_info_request.rb +58 -0
- data/lib/ruby_smb/smb2/packet/set_info_response.rb +20 -0
- data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +3 -4
- data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +5 -9
- data/lib/ruby_smb/smb2/packet/tree_disconnect_request.rb +1 -4
- data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +1 -4
- data/lib/ruby_smb/smb2/packet/write_request.rb +29 -0
- data/lib/ruby_smb/smb2/packet/write_response.rb +26 -0
- data/lib/ruby_smb/smb2/tree.rb +163 -7
- data/lib/ruby_smb/version.rb +1 -1
- data/spec/lib/ruby_smb/client_spec.rb +459 -120
- data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +14 -0
- data/spec/lib/ruby_smb/dcerpc/handle_spec.rb +31 -0
- data/spec/lib/ruby_smb/dcerpc/request_spec.rb +21 -0
- data/spec/lib/ruby_smb/dcerpc/response_spec.rb +15 -0
- data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +13 -0
- data/spec/lib/ruby_smb/dcerpc/uuid_spec.rb +12 -0
- data/spec/lib/ruby_smb/dispatcher/base_spec.rb +26 -0
- data/spec/lib/ruby_smb/dispatcher/socket_spec.rb +118 -18
- data/spec/lib/ruby_smb/field/extended_attribute_flag_spec.rb +0 -3
- data/spec/lib/ruby_smb/field/file_time_spec.rb +4 -2
- data/spec/lib/ruby_smb/field/security_descriptor.rb +0 -1
- data/spec/lib/ruby_smb/field/smb2_fileid_spec.rb +10 -0
- data/spec/lib/ruby_smb/field/smb_fea_list_spec.rb +3 -5
- data/spec/lib/ruby_smb/field/smb_gea_list_spec.rb +37 -0
- data/spec/lib/ruby_smb/field/smb_gea_spec.rb +22 -0
- data/spec/lib/ruby_smb/field/stringz16_spec.rb +7 -9
- data/spec/lib/ruby_smb/field/utime_spec.rb +4 -2
- data/spec/lib/ruby_smb/{field → fscc}/ea_info_array_spec.rb +7 -9
- data/spec/lib/ruby_smb/{field → fscc}/file_full_ea_info_spec.rb +2 -3
- data/spec/lib/ruby_smb/fscc/file_information/file_both_directory_information_spec.rb +71 -0
- data/spec/lib/ruby_smb/fscc/file_information/file_directory_information_spec.rb +68 -0
- data/spec/lib/ruby_smb/fscc/file_information/file_disposition_information_spec.rb +26 -0
- data/spec/lib/ruby_smb/fscc/file_information/file_full_directory_information_spec.rb +69 -0
- data/spec/lib/ruby_smb/fscc/file_information/file_id_both_directory_information_spec.rb +72 -0
- data/spec/lib/ruby_smb/fscc/file_information/file_id_full_directory_information_spec.rb +70 -0
- data/spec/lib/ruby_smb/fscc/file_information/file_names_information_spec.rb +41 -0
- data/spec/lib/ruby_smb/fscc/file_information/file_rename_information_spec.rb +133 -0
- data/spec/lib/ruby_smb/fscc/fscc_file_attributes_spec.rb +143 -0
- data/spec/lib/ruby_smb/generic_packet_spec.rb +46 -21
- data/spec/lib/ruby_smb/nbss/negative_session_response_spec.rb +29 -0
- data/spec/lib/ruby_smb/nbss/session_header_spec.rb +30 -0
- data/spec/lib/ruby_smb/nbss/session_request_spec.rb +30 -0
- data/spec/lib/ruby_smb/smb1/bit_field/create_options_spec.rb +9 -1
- data/spec/lib/ruby_smb/smb1/bit_field/directory_access_mask_spec.rb +0 -2
- data/spec/lib/ruby_smb/smb1/bit_field/file_access_mask_spec.rb +0 -2
- data/spec/lib/ruby_smb/smb1/bit_field/file_status_flags_spec.rb +35 -0
- data/spec/lib/ruby_smb/smb1/bit_field/open2_access_mode_spec.rb +1 -3
- data/spec/lib/ruby_smb/smb1/bit_field/open2_flags_spec.rb +0 -2
- data/spec/lib/ruby_smb/smb1/bit_field/open2_open_mode_spec.rb +0 -1
- data/spec/lib/ruby_smb/smb1/bit_field/optional_support_spec.rb +0 -1
- data/spec/lib/ruby_smb/smb1/bit_field/security_flags_spec.rb +26 -0
- data/spec/lib/ruby_smb/smb1/bit_field/share_access_spec.rb +0 -3
- data/spec/lib/ruby_smb/smb1/bit_field/smb_ext_file_attributes_spec.rb +20 -39
- data/spec/lib/ruby_smb/smb1/bit_field/smb_file_attributes_spec.rb +0 -6
- data/spec/lib/ruby_smb/smb1/bit_field/smb_nmpipe_status_spec.rb +0 -2
- data/spec/lib/ruby_smb/smb1/bit_field/trans2_flags_spec.rb +0 -2
- data/spec/lib/ruby_smb/smb1/bit_field/tree_connect_flags_spec.rb +0 -2
- data/spec/lib/ruby_smb/smb1/file_spec.rb +469 -0
- data/spec/lib/ruby_smb/smb1/packet/close_request_spec.rb +54 -0
- data/spec/lib/ruby_smb/smb1/packet/close_response_spec.rb +45 -0
- data/spec/lib/ruby_smb/smb1/packet/echo_request_spec.rb +1 -4
- data/spec/lib/ruby_smb/smb1/packet/echo_response_spec.rb +1 -4
- data/spec/lib/ruby_smb/smb1/packet/error_packet_spec.rb +1 -3
- data/spec/lib/ruby_smb/smb1/packet/logoff_request_spec.rb +1 -4
- data/spec/lib/ruby_smb/smb1/packet/logoff_response_spec.rb +1 -4
- data/spec/lib/ruby_smb/smb1/packet/negotiate_request_spec.rb +1 -1
- data/spec/lib/ruby_smb/smb1/packet/negotiate_response_extended_spec.rb +37 -0
- data/spec/lib/ruby_smb/smb1/packet/negotiate_response_spec.rb +37 -0
- data/spec/lib/ruby_smb/smb1/packet/nt_create_andx_request_spec.rb +151 -0
- data/spec/lib/ruby_smb/smb1/packet/nt_create_andx_response_spec.rb +157 -0
- data/spec/lib/ruby_smb/smb1/packet/nt_trans/create_request_spec.rb +13 -6
- data/spec/lib/ruby_smb/smb1/packet/nt_trans/create_response_spec.rb +1 -7
- data/spec/lib/ruby_smb/smb1/packet/nt_trans/request_spec.rb +1 -6
- data/spec/lib/ruby_smb/smb1/packet/nt_trans/response_spec.rb +1 -5
- data/spec/lib/ruby_smb/smb1/packet/read_andx_request_spec.rb +149 -0
- data/spec/lib/ruby_smb/smb1/packet/read_andx_response_spec.rb +93 -0
- data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_request_spec.rb +1 -5
- data/spec/lib/ruby_smb/smb1/packet/session_setup_legacy_response_spec.rb +1 -5
- data/spec/lib/ruby_smb/smb1/packet/session_setup_request_spec.rb +3 -8
- data/spec/lib/ruby_smb/smb1/packet/session_setup_response_spec.rb +2 -4
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_request_spec.rb +180 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +104 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_information_level/find_file_full_directory_info_spec.rb +128 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_request_spec.rb +174 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +102 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/open2_request_spec.rb +1 -6
- data/spec/lib/ruby_smb/smb1/packet/trans2/open2_response_spec.rb +2 -7
- data/spec/lib/ruby_smb/smb1/packet/trans2/request_secondary_spec.rb +1 -5
- data/spec/lib/ruby_smb/smb1/packet/trans2/request_spec.rb +1 -5
- data/spec/lib/ruby_smb/smb1/packet/trans2/response_spec.rb +1 -4
- data/spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_request_spec.rb +98 -0
- data/spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_response_spec.rb +56 -0
- data/spec/lib/ruby_smb/smb1/packet/tree_connect_request_spec.rb +1 -5
- data/spec/lib/ruby_smb/smb1/packet/tree_connect_response_spec.rb +1 -6
- data/spec/lib/ruby_smb/smb1/packet/tree_disconnect_request_spec.rb +1 -4
- data/spec/lib/ruby_smb/smb1/packet/tree_disconnect_response_spec.rb +1 -4
- data/spec/lib/ruby_smb/smb1/packet/write_andx_request_spec.rb +148 -0
- data/spec/lib/ruby_smb/smb1/packet/write_andx_response_spec.rb +54 -0
- data/spec/lib/ruby_smb/smb1/tree_spec.rb +409 -7
- data/spec/lib/ruby_smb/smb2/bit_field/directory_access_mask_spec.rb +0 -2
- data/spec/lib/ruby_smb/smb2/bit_field/file_access_mask_spec.rb +0 -2
- data/spec/lib/ruby_smb/smb2/bit_field/session_flags_spec.rb +1 -1
- data/spec/lib/ruby_smb/smb2/bit_field/share_capabilities_spec.rb +0 -1
- data/spec/lib/ruby_smb/smb2/bit_field/share_flags_spec.rb +0 -2
- data/spec/lib/ruby_smb/smb2/create_context_spec.rb +42 -0
- data/spec/lib/ruby_smb/smb2/file_spec.rb +233 -0
- data/spec/lib/ruby_smb/smb2/packet/close_request_spec.rb +40 -0
- data/spec/lib/ruby_smb/smb2/packet/close_response_spec.rb +40 -0
- data/spec/lib/ruby_smb/smb2/packet/create_request_spec.rb +101 -0
- data/spec/lib/ruby_smb/smb2/packet/create_response_spec.rb +64 -0
- data/spec/lib/ruby_smb/smb2/packet/echo_request_spec.rb +1 -3
- data/spec/lib/ruby_smb/smb2/packet/echo_response_spec.rb +1 -3
- data/spec/lib/ruby_smb/smb2/packet/ioctl_request_spec.rb +48 -0
- data/spec/lib/ruby_smb/smb2/packet/logoff_request_spec.rb +1 -3
- data/spec/lib/ruby_smb/smb2/packet/logoff_response_spec.rb +1 -3
- data/spec/lib/ruby_smb/smb2/packet/query_directory_request_spec.rb +80 -0
- data/spec/lib/ruby_smb/smb2/packet/query_directory_response_spec.rb +64 -0
- data/spec/lib/ruby_smb/smb2/packet/read_request_spec.rb +43 -0
- data/spec/lib/ruby_smb/smb2/packet/read_response_spec.rb +50 -0
- data/spec/lib/ruby_smb/smb2/packet/session_setup_request_spec.rb +3 -4
- data/spec/lib/ruby_smb/smb2/packet/session_setup_response_spec.rb +2 -3
- data/spec/lib/ruby_smb/smb2/packet/set_info_request_spec.rb +205 -0
- data/spec/lib/ruby_smb/smb2/packet/set_info_response_spec.rb +32 -0
- data/spec/lib/ruby_smb/smb2/packet/tree_connect_request_spec.rb +3 -5
- data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +1 -2
- data/spec/lib/ruby_smb/smb2/packet/tree_disconnect_request_spec.rb +1 -3
- data/spec/lib/ruby_smb/smb2/packet/tree_disconnect_response_spec.rb +1 -3
- data/spec/lib/ruby_smb/smb2/packet/write_request_spec.rb +51 -0
- data/spec/lib/ruby_smb/smb2/packet/write_response_spec.rb +38 -0
- data/spec/lib/ruby_smb/smb2/tree_spec.rb +191 -5
- data/spec/spec_helper.rb +1 -1
- metadata +195 -12
- metadata.gz.sig +0 -0
- data/lib/ruby_smb/smb1/dispositions.rb +0 -36
- data/spec/lib/ruby_smb/dispatcher/dispatcher_base_spec.rb +0 -22
@@ -0,0 +1,20 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module SMB2
|
3
|
+
module Packet
|
4
|
+
# An SMB2 Read Response Packet as defined in
|
5
|
+
# [2.2.40 SMB2 SET_INFO Response](https://msdn.microsoft.com/en-us/library/cc246562.aspx)
|
6
|
+
class SetInfoResponse < RubySMB::GenericPacket
|
7
|
+
endian :little
|
8
|
+
|
9
|
+
smb2_header :smb2_header
|
10
|
+
uint16 :structure_size, label: 'Structure Size', initial_value: 2
|
11
|
+
|
12
|
+
def initialize_instance
|
13
|
+
super
|
14
|
+
smb2_header.command = RubySMB::SMB2::Commands::SET_INFO
|
15
|
+
smb2_header.flags.reply = 1
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module RubySMB
|
2
2
|
module SMB2
|
3
3
|
module Packet
|
4
|
-
|
5
4
|
# An SMB2 TreeConnectRequest Packet as defined in
|
6
5
|
# [2.2.9 SMB2 TREE_CONNECT Request](https://msdn.microsoft.com/en-us/library/cc246567.aspx)
|
7
6
|
class TreeConnectRequest < RubySMB::GenericPacket
|
@@ -10,7 +9,7 @@ module RubySMB
|
|
10
9
|
uint16 :structure_size, label: 'Structure Size', initial_value: 9
|
11
10
|
uint16 :flags, label: 'Flags', initial_value: 0x00
|
12
11
|
uint16 :path_offset, label: 'Path Offset', initial_value: 0x48
|
13
|
-
uint16 :path_length, label: 'Path Length', initial_value:
|
12
|
+
uint16 :path_length, label: 'Path Length', initial_value: -> { path.length }
|
14
13
|
string :path, label: 'Path Buffer'
|
15
14
|
|
16
15
|
def initialize_instance
|
@@ -19,9 +18,9 @@ module RubySMB
|
|
19
18
|
end
|
20
19
|
|
21
20
|
def encode_path(path)
|
22
|
-
self.path = path.encode(
|
21
|
+
self.path = path.encode('utf-16le')
|
23
22
|
end
|
24
23
|
end
|
25
24
|
end
|
26
25
|
end
|
27
|
-
end
|
26
|
+
end
|
@@ -1,11 +1,10 @@
|
|
1
1
|
module RubySMB
|
2
2
|
module SMB2
|
3
3
|
module Packet
|
4
|
-
|
5
4
|
# An SMB2 TreeConnectResponse Packet as defined in
|
6
5
|
# [2.2.10 SMB2 TREE_CONNECT Response](https://msdn.microsoft.com/en-us/library/cc246499.aspx)
|
7
6
|
class TreeConnectResponse < RubySMB::GenericPacket
|
8
|
-
endian
|
7
|
+
endian :little
|
9
8
|
smb2_header :smb2_header
|
10
9
|
uint16 :structure_size, label: 'Structure Size', initial_value: 16
|
11
10
|
uint8 :share_type, label: 'Share Type', initial_value: 0x01
|
@@ -28,9 +27,9 @@ module RubySMB
|
|
28
27
|
# @return [RubySMB::SMB2::BitField::FileAccessMask] if anything else was connected to
|
29
28
|
def access_rights
|
30
29
|
if is_directory?
|
31
|
-
|
30
|
+
maximal_access
|
32
31
|
else
|
33
|
-
mask =
|
32
|
+
mask = maximal_access.to_binary_s
|
34
33
|
RubySMB::SMB2::BitField::FileAccessMask.read(mask)
|
35
34
|
end
|
36
35
|
end
|
@@ -39,12 +38,9 @@ module RubySMB
|
|
39
38
|
#
|
40
39
|
# @return [Boolean]
|
41
40
|
def is_directory?
|
42
|
-
|
41
|
+
share_type == 0x01
|
43
42
|
end
|
44
|
-
|
45
|
-
|
46
|
-
|
47
43
|
end
|
48
44
|
end
|
49
45
|
end
|
50
|
-
end
|
46
|
+
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module RubySMB
|
2
2
|
module SMB2
|
3
3
|
module Packet
|
4
|
-
|
5
4
|
# An SMB2 TreeDisconnectRequest Packet as defined in
|
6
5
|
# [2.2.11 SMB2 TREE_DISCONNECT Request](https://msdn.microsoft.com/en-us/library/cc246500.aspx)
|
7
6
|
class TreeDisconnectRequest < RubySMB::GenericPacket
|
@@ -9,13 +8,11 @@ module RubySMB
|
|
9
8
|
smb2_header :smb2_header
|
10
9
|
uint16 :structure_size, label: 'Structure Size', initial_value: 4
|
11
10
|
|
12
|
-
|
13
11
|
def initialize_instance
|
14
12
|
super
|
15
13
|
smb2_header.command = RubySMB::SMB2::Commands::TREE_DISCONNECT
|
16
14
|
end
|
17
|
-
|
18
15
|
end
|
19
16
|
end
|
20
17
|
end
|
21
|
-
end
|
18
|
+
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module RubySMB
|
2
2
|
module SMB2
|
3
3
|
module Packet
|
4
|
-
|
5
4
|
# An SMB2 TreeDisconnectResponse Packet as defined in
|
6
5
|
# [2.2.12 SMB2 TREE_DISCONNECT Response](https://msdn.microsoft.com/en-us/library/cc246501.aspx)
|
7
6
|
class TreeDisconnectResponse < RubySMB::GenericPacket
|
@@ -9,14 +8,12 @@ module RubySMB
|
|
9
8
|
smb2_header :smb2_header
|
10
9
|
uint16 :structure_size, label: 'Structure Size', initial_value: 4
|
11
10
|
|
12
|
-
|
13
11
|
def initialize_instance
|
14
12
|
super
|
15
13
|
smb2_header.command = RubySMB::SMB2::Commands::TREE_DISCONNECT
|
16
14
|
smb2_header.flags.reply = 1
|
17
15
|
end
|
18
|
-
|
19
16
|
end
|
20
17
|
end
|
21
18
|
end
|
22
|
-
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module SMB2
|
3
|
+
module Packet
|
4
|
+
# An SMB2 Write Request Packet as defined in
|
5
|
+
# [2.2.21 SMB2 WRITE Request](https://msdn.microsoft.com/en-us/library/cc246532.aspx)
|
6
|
+
class WriteRequest < RubySMB::GenericPacket
|
7
|
+
endian :little
|
8
|
+
|
9
|
+
smb2_header :smb2_header
|
10
|
+
uint16 :structure_size, label: 'Structure Size', initial_value: 49
|
11
|
+
uint16 :data_offset, label: 'Data Offset' , initial_value: lambda { self.buffer.abs_offset }
|
12
|
+
uint32 :write_length, label: 'Write Length', initial_value: lambda { self.buffer.do_num_bytes }
|
13
|
+
uint64 :write_offset, label: 'File Write Offset'
|
14
|
+
smb2_fileid :file_id, label: 'File ID'
|
15
|
+
uint32 :channel, label: 'Channel'
|
16
|
+
uint32 :remaining_bytes, label: 'Remaining Bytes'
|
17
|
+
uint16 :channel_offset, label: 'Write Channel Info Offset'
|
18
|
+
uint16 :channel_length, label: 'Write Channel Info Length'
|
19
|
+
uint32 :flags, label: 'Flags'
|
20
|
+
string :buffer, label: 'Write Data Buffer'
|
21
|
+
|
22
|
+
def initialize_instance
|
23
|
+
super
|
24
|
+
smb2_header.command = RubySMB::SMB2::Commands::WRITE
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module RubySMB
|
2
|
+
module SMB2
|
3
|
+
module Packet
|
4
|
+
# An SMB2 Write Response Packet as defined in
|
5
|
+
# [2.2.22 SMB2 WRITE Response](https://msdn.microsoft.com/en-us/library/cc246533.aspx)
|
6
|
+
class WriteResponse < RubySMB::GenericPacket
|
7
|
+
endian :little
|
8
|
+
|
9
|
+
smb2_header :smb2_header
|
10
|
+
uint16 :structure_size, label: 'Structure Size', initial_value: 17
|
11
|
+
uint16 :reserved, label: 'Reserved Space'
|
12
|
+
uint32 :write_count, label: 'Bytes Written'
|
13
|
+
uint32 :remaining_bytes, label: 'Remaining Bytes'
|
14
|
+
uint16 :channel_offset, label: 'Write Channel Info Offset'
|
15
|
+
uint16 :channel_length, label: 'Write Channel Info Length'
|
16
|
+
|
17
|
+
|
18
|
+
def initialize_instance
|
19
|
+
super
|
20
|
+
smb2_header.command = RubySMB::SMB2::Commands::WRITE
|
21
|
+
smb2_header.flags.reply = 1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/ruby_smb/smb2/tree.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
module RubySMB
|
2
2
|
module SMB2
|
3
|
-
|
4
3
|
# An SMB2 connected remote Tree, as returned by a
|
5
4
|
# [RubySMB::SMB2::Packet::TreeConnectRequest]
|
6
5
|
class Tree
|
7
|
-
|
8
6
|
# The client this Tree is connected through
|
9
7
|
# @!attribute [rw] client
|
10
8
|
# @return [RubySMB::Client]
|
@@ -37,15 +35,173 @@ module RubySMB
|
|
37
35
|
# @return [WindowsError::ErrorCode] the NTStatus sent back by the server.
|
38
36
|
def disconnect!
|
39
37
|
request = RubySMB::SMB2::Packet::TreeDisconnectRequest.new
|
40
|
-
request
|
41
|
-
|
42
|
-
request.smb2_header.credits = 256
|
43
|
-
raw_response = self.client.send_recv(request)
|
38
|
+
request = set_header_fields(request)
|
39
|
+
raw_response = client.send_recv(request)
|
44
40
|
response = RubySMB::SMB2::Packet::TreeDisconnectResponse.read(raw_response)
|
45
41
|
response.status_code
|
46
42
|
end
|
47
43
|
|
44
|
+
def open_file(filename:, attributes: nil, options: nil, disposition: RubySMB::Dispositions::FILE_OPEN,
|
45
|
+
impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE, read: true, write: false, delete: false)
|
46
|
+
|
47
|
+
create_request = RubySMB::SMB2::Packet::CreateRequest.new
|
48
|
+
create_request = set_header_fields(create_request)
|
49
|
+
|
50
|
+
# If the user supplied file attributes, use those, otherwise set some
|
51
|
+
# sane defaults.
|
52
|
+
if attributes
|
53
|
+
create_request.file_attributes = attributes
|
54
|
+
else
|
55
|
+
create_request.file_attributes.directory = 0
|
56
|
+
create_request.file_attributes.normal = 1
|
57
|
+
end
|
58
|
+
|
59
|
+
# If the user supplied Create Options, use those, otherwise set some
|
60
|
+
# sane defaults.
|
61
|
+
if options
|
62
|
+
create_request.create_options = options
|
63
|
+
else
|
64
|
+
create_request.create_options.directory_file = 0
|
65
|
+
create_request.create_options.non_directory_file = 1
|
66
|
+
end
|
67
|
+
|
68
|
+
if read
|
69
|
+
create_request.share_access.read_access = 1
|
70
|
+
create_request.desired_access.read_data = 1
|
71
|
+
end
|
72
|
+
|
73
|
+
if write
|
74
|
+
create_request.share_access.write_access = 1
|
75
|
+
create_request.desired_access.write_data = 1
|
76
|
+
create_request.desired_access.append_data = 1
|
77
|
+
end
|
78
|
+
|
79
|
+
if delete
|
80
|
+
create_request.share_access.delete_access = 1
|
81
|
+
create_request.desired_access.delete_access = 1
|
82
|
+
end
|
83
|
+
|
84
|
+
create_request.requested_oplock = 0xff
|
85
|
+
create_request.impersonation_level = impersonation
|
86
|
+
create_request.create_disposition = disposition
|
87
|
+
create_request.name = filename
|
88
|
+
|
89
|
+
raw_response = client.send_recv(create_request)
|
90
|
+
response = RubySMB::SMB2::Packet::CreateResponse.read(raw_response)
|
91
|
+
|
92
|
+
RubySMB::SMB2::File.new(name: filename, tree: self, response: response)
|
93
|
+
end
|
94
|
+
|
95
|
+
# List `directory` on the remote share.
|
96
|
+
#
|
97
|
+
# @example
|
98
|
+
# tree = client.tree_connect("\\\\192.168.99.134\\Share")
|
99
|
+
# tree.list(directory: "path\\to\\directory")
|
100
|
+
#
|
101
|
+
# @param directory [String] path to the directory to be listed
|
102
|
+
# @param pattern [String] search pattern
|
103
|
+
# @param type [Class] file information class
|
104
|
+
# @return [Array] array of directory structures
|
105
|
+
def list(directory: nil, pattern: '*', type: RubySMB::Fscc::FileInformation::FileIdFullDirectoryInformation)
|
106
|
+
create_response = open_directory(directory: directory)
|
107
|
+
file_id = create_response.file_id
|
108
|
+
|
109
|
+
directory_request = RubySMB::SMB2::Packet::QueryDirectoryRequest.new
|
110
|
+
directory_request.file_information_class = type::CLASS_LEVEL
|
111
|
+
directory_request.file_id = file_id
|
112
|
+
directory_request.name = pattern
|
113
|
+
directory_request.output_length = 65_535
|
114
|
+
|
115
|
+
directory_request = set_header_fields(directory_request)
|
116
|
+
|
117
|
+
files = []
|
118
|
+
|
119
|
+
loop do
|
120
|
+
response = client.send_recv(directory_request)
|
121
|
+
directory_response = RubySMB::SMB2::Packet::QueryDirectoryResponse.read(response)
|
48
122
|
|
123
|
+
status_code = directory_response.smb2_header.nt_status.to_nt_status
|
124
|
+
|
125
|
+
break if status_code == WindowsError::NTStatus::STATUS_NO_MORE_FILES
|
126
|
+
|
127
|
+
unless status_code == WindowsError::NTStatus::STATUS_SUCCESS
|
128
|
+
|
129
|
+
raise RubySMB::Error::UnexpectedStatusCode, status_code.to_s
|
130
|
+
end
|
131
|
+
|
132
|
+
files += directory_response.results(type)
|
133
|
+
# Reset the message id so the client can update appropriately.
|
134
|
+
directory_request.smb2_header.message_id = 0
|
135
|
+
end
|
136
|
+
|
137
|
+
files
|
138
|
+
end
|
139
|
+
|
140
|
+
# 'Opens' a directory file on the remote end, using a CreateRequest. This
|
141
|
+
# can be used to open an existing directory, or create a new one, depending
|
142
|
+
# on the disposition set.
|
143
|
+
#
|
144
|
+
# @param directory [String] the name of the directory file
|
145
|
+
# @param disposition [Integer] the create disposition to use, should be one of {RubySMB::Dispositions}
|
146
|
+
# @param impersonation [Integer] the impersonation level to use, should be one of {RubySMB::ImpersonationLevels}
|
147
|
+
# @param read [Boolean] whether to request read access
|
148
|
+
# @param write [Boolean] whether to request write access
|
149
|
+
# @param delete [Boolean] whether to request delete access
|
150
|
+
# @return [RubySMB::SMB2::Packet::CreateResponse] the response packet returned from the server
|
151
|
+
def open_directory(directory: nil, disposition: RubySMB::Dispositions::FILE_OPEN,
|
152
|
+
impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE,
|
153
|
+
read: true, write: false, delete: false)
|
154
|
+
|
155
|
+
create_request = open_directory_packet(directory: directory, disposition: disposition,
|
156
|
+
impersonation: impersonation, read: read, write: write, delete: delete)
|
157
|
+
raw_response = client.send_recv(create_request)
|
158
|
+
RubySMB::SMB2::Packet::CreateResponse.read(raw_response)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Creates the Packet for the #open_directory method.
|
162
|
+
#
|
163
|
+
# @param directory [String] the name of the directory file
|
164
|
+
# @param disposition [Integer] the create disposition to use, should be one of {RubySMB::Dispositions}
|
165
|
+
# @param impersonation [Integer] the impersonation level to use, should be one of {RubySMB::ImpersonationLevels}
|
166
|
+
# @param read [Boolean] whether to request read access
|
167
|
+
# @param write [Boolean] whether to request write access
|
168
|
+
# @param delete [Boolean] whether to request delete access
|
169
|
+
# @return [RubySMB::SMB2::Packet::CreateRequest] the request packet to send to the server
|
170
|
+
def open_directory_packet(directory: nil, disposition: RubySMB::Dispositions::FILE_OPEN,
|
171
|
+
impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE,
|
172
|
+
read: true, write: false, delete: false)
|
173
|
+
create_request = RubySMB::SMB2::Packet::CreateRequest.new
|
174
|
+
create_request = set_header_fields(create_request)
|
175
|
+
|
176
|
+
create_request.impersonation_level = impersonation
|
177
|
+
create_request.create_options.directory_file = 1
|
178
|
+
create_request.file_attributes.directory = 1
|
179
|
+
create_request.desired_access.list = 1
|
180
|
+
create_request.share_access.read_access = 1 if read
|
181
|
+
create_request.share_access.write_access = 1 if write
|
182
|
+
create_request.share_access.delete_access = 1 if delete
|
183
|
+
create_request.create_disposition = disposition
|
184
|
+
|
185
|
+
if directory.nil? || directory.empty?
|
186
|
+
create_request.name = "\x00"
|
187
|
+
create_request.name_length = 0
|
188
|
+
else
|
189
|
+
create_request.name = directory
|
190
|
+
end
|
191
|
+
create_request
|
192
|
+
end
|
193
|
+
|
194
|
+
# Sets a few preset header fields that will always be set the same
|
195
|
+
# way for Tree operations. This is, the TreeID, Credits, and Credit Charge.
|
196
|
+
#
|
197
|
+
# @param [RubySMB::SMB2::Packet] the request packet to modify
|
198
|
+
# @return [RubySMB::SMB2::Packet] the modified packet.
|
199
|
+
def set_header_fields(request)
|
200
|
+
request.smb2_header.tree_id = id
|
201
|
+
request.smb2_header.credit_charge = 1
|
202
|
+
request.smb2_header.credits = 256
|
203
|
+
request
|
204
|
+
end
|
49
205
|
end
|
50
206
|
end
|
51
|
-
end
|
207
|
+
end
|
data/lib/ruby_smb/version.rb
CHANGED
@@ -1,19 +1,18 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.describe RubySMB::Client do
|
4
|
-
|
5
|
-
let(:sock) { double("Socket", peeraddr: '192.168.1.5' ) }
|
4
|
+
let(:sock) { double('Socket', peeraddr: '192.168.1.5') }
|
6
5
|
let(:dispatcher) { RubySMB::Dispatcher::Socket.new(sock) }
|
7
6
|
let(:username) { 'msfadmin' }
|
8
|
-
let(:password) { '
|
7
|
+
let(:password) { 'msfpasswd' }
|
9
8
|
subject(:client) { described_class.new(dispatcher, username: username, password: password) }
|
10
|
-
let(:smb1_client) { described_class.new(dispatcher, smb2:false, username: username, password: password) }
|
11
|
-
let(:smb2_client) { described_class.new(dispatcher, smb1:false, username: username, password: password) }
|
9
|
+
let(:smb1_client) { described_class.new(dispatcher, smb2: false, username: username, password: password) }
|
10
|
+
let(:smb2_client) { described_class.new(dispatcher, smb1: false, username: username, password: password) }
|
12
11
|
let(:empty_packet) { RubySMB::SMB1::Packet::EmptyPacket.new }
|
13
12
|
|
14
13
|
describe '#initialize' do
|
15
14
|
it 'should raise an ArgumentError without a valid dispatcher' do
|
16
|
-
expect{ described_class.new(nil) }.to raise_error(ArgumentError)
|
15
|
+
expect { described_class.new(nil) }.to raise_error(ArgumentError)
|
17
16
|
end
|
18
17
|
|
19
18
|
it 'defaults to true for SMB1 support' do
|
@@ -25,8 +24,7 @@ RSpec.describe RubySMB::Client do
|
|
25
24
|
end
|
26
25
|
|
27
26
|
it 'accepts an argument to disable smb1 support' do
|
28
|
-
|
29
|
-
expect(smb_client.smb1).to be false
|
27
|
+
expect(smb2_client.smb1).to be false
|
30
28
|
end
|
31
29
|
|
32
30
|
it 'accepts an argument to disable smb2 support' do
|
@@ -34,10 +32,10 @@ RSpec.describe RubySMB::Client do
|
|
34
32
|
end
|
35
33
|
|
36
34
|
it 'raises an exception if both SMB1 and SMB2 are disabled' do
|
37
|
-
expect{described_class.new(dispatcher, smb1:false, smb2:false, username: username, password: password)}.to raise_error(ArgumentError, 'You must enable at least one Protocol')
|
35
|
+
expect { described_class.new(dispatcher, smb1: false, smb2: false, username: username, password: password) }.to raise_error(ArgumentError, 'You must enable at least one Protocol')
|
38
36
|
end
|
39
37
|
|
40
|
-
it 'sets the username attribute'
|
38
|
+
it 'sets the username attribute' do
|
41
39
|
expect(client.username).to eq username
|
42
40
|
end
|
43
41
|
|
@@ -45,9 +43,36 @@ RSpec.describe RubySMB::Client do
|
|
45
43
|
expect(client.password).to eq password
|
46
44
|
end
|
47
45
|
|
48
|
-
it '
|
46
|
+
it 'creates an NTLM client' do
|
49
47
|
expect(client.ntlm_client).to be_a Net::NTLM::Client
|
50
48
|
end
|
49
|
+
|
50
|
+
it 'passes the expected arguments when creating the NTLM client' do
|
51
|
+
domain = 'SPEC_DOMAIN'
|
52
|
+
local_workstation = 'SPEC_WORKSTATION'
|
53
|
+
|
54
|
+
allow(Net::NTLM::Client).to receive(:new) do |username, passwd, opt|
|
55
|
+
expect(username).to eq(username)
|
56
|
+
expect(password).to eq(password)
|
57
|
+
expect(opt[:workstation]).to eq(local_workstation)
|
58
|
+
expect(opt[:domain]).to eq(domain)
|
59
|
+
flags = Net::NTLM::Client::DEFAULT_FLAGS |
|
60
|
+
Net::NTLM::FLAGS[:TARGET_INFO] | 0x02000000
|
61
|
+
expect(opt[:flags]).to eq(flags)
|
62
|
+
end
|
63
|
+
|
64
|
+
described_class.new(
|
65
|
+
dispatcher,
|
66
|
+
username: username,
|
67
|
+
password: password,
|
68
|
+
domain: domain,
|
69
|
+
local_workstation: local_workstation
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'sets the max_buffer_size to MAX_BUFFER_SIZE' do
|
74
|
+
expect(client.max_buffer_size).to eq RubySMB::Client::MAX_BUFFER_SIZE
|
75
|
+
end
|
51
76
|
end
|
52
77
|
|
53
78
|
describe '#send_recv' do
|
@@ -56,7 +81,7 @@ RSpec.describe RubySMB::Client do
|
|
56
81
|
|
57
82
|
before(:each) do
|
58
83
|
expect(dispatcher).to receive(:send_packet).and_return(nil)
|
59
|
-
expect(dispatcher).to receive(:recv_packet).and_return(
|
84
|
+
expect(dispatcher).to receive(:recv_packet).and_return('A')
|
60
85
|
end
|
61
86
|
|
62
87
|
it 'checks the packet version' do
|
@@ -64,7 +89,7 @@ RSpec.describe RubySMB::Client do
|
|
64
89
|
client.send_recv(smb1_request)
|
65
90
|
end
|
66
91
|
|
67
|
-
it 'calls #smb1_sign if it is an SMB1 packet'
|
92
|
+
it 'calls #smb1_sign if it is an SMB1 packet' do
|
68
93
|
expect(client).to receive(:smb1_sign).with(smb1_request).and_call_original
|
69
94
|
client.send_recv(smb1_request)
|
70
95
|
end
|
@@ -73,7 +98,6 @@ RSpec.describe RubySMB::Client do
|
|
73
98
|
expect(client).to receive(:smb2_sign).with(smb2_request).and_call_original
|
74
99
|
client.send_recv(smb2_request)
|
75
100
|
end
|
76
|
-
|
77
101
|
end
|
78
102
|
|
79
103
|
describe '#login' do
|
@@ -83,39 +107,39 @@ RSpec.describe RubySMB::Client do
|
|
83
107
|
end
|
84
108
|
|
85
109
|
it 'defaults username to what was in the initializer' do
|
86
|
-
expect{ client.login }.to_not change(client, :username)
|
110
|
+
expect { client.login }.to_not change(client, :username)
|
87
111
|
end
|
88
112
|
|
89
113
|
it 'overrides username if it is passed as a parameter' do
|
90
|
-
expect{ client.login(username:'test') }.to change(client, :username).to('test')
|
114
|
+
expect { client.login(username: 'test') }.to change(client, :username).to('test')
|
91
115
|
end
|
92
116
|
|
93
117
|
it 'defaults password to what was in the initializer' do
|
94
|
-
expect{ client.login }.to_not change(client, :password)
|
118
|
+
expect { client.login }.to_not change(client, :password)
|
95
119
|
end
|
96
120
|
|
97
121
|
it 'overrides password if it is passed as a parameter' do
|
98
|
-
expect{ client.login(password:'test') }.to change(client, :password).to('test')
|
122
|
+
expect { client.login(password: 'test') }.to change(client, :password).to('test')
|
99
123
|
end
|
100
124
|
|
101
125
|
it 'defaults domain to what was in the initializer' do
|
102
|
-
expect{ client.login }.to_not change(client, :domain)
|
126
|
+
expect { client.login }.to_not change(client, :domain)
|
103
127
|
end
|
104
128
|
|
105
129
|
it 'overrides domain if it is passed as a parameter' do
|
106
|
-
expect{ client.login(domain:'test') }.to change(client, :domain).to('test')
|
130
|
+
expect { client.login(domain: 'test') }.to change(client, :domain).to('test')
|
107
131
|
end
|
108
132
|
|
109
133
|
it 'defaults local_workstation to what was in the initializer' do
|
110
|
-
expect{ client.login }.to_not change(client, :local_workstation)
|
134
|
+
expect { client.login }.to_not change(client, :local_workstation)
|
111
135
|
end
|
112
136
|
|
113
137
|
it 'overrides local_workstation if it is passed as a parameter' do
|
114
|
-
expect{ client.login(local_workstation:'test') }.to change(client, :local_workstation).to('test')
|
138
|
+
expect { client.login(local_workstation: 'test') }.to change(client, :local_workstation).to('test')
|
115
139
|
end
|
116
140
|
|
117
141
|
it 'initialises a new NTLM Client' do
|
118
|
-
expect{ client.login }.to change(client, :ntlm_client)
|
142
|
+
expect { client.login }.to change(client, :ntlm_client)
|
119
143
|
end
|
120
144
|
|
121
145
|
it 'calls negotiate after the setup' do
|
@@ -127,37 +151,129 @@ RSpec.describe RubySMB::Client do
|
|
127
151
|
expect(client).to receive(:authenticate)
|
128
152
|
client.login
|
129
153
|
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context 'NetBIOS Session Service' do
|
157
|
+
describe '#session_request' do
|
158
|
+
let(:called_name) { '*SMBSERVER ' }
|
159
|
+
let(:calling_name) { " \x00" }
|
160
|
+
let(:session_header) { RubySMB::Nbss::SessionHeader.new }
|
161
|
+
let(:session_request) { RubySMB::Nbss::SessionRequest.new }
|
162
|
+
|
163
|
+
before :example do
|
164
|
+
allow(RubySMB::Nbss::SessionRequest).to receive(:new).and_return(session_request)
|
165
|
+
allow(dispatcher).to receive(:send_packet)
|
166
|
+
allow(dispatcher).to receive(:recv_packet).and_return(session_header.to_binary_s)
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'requests a session for *SMBSERVER by default' do
|
170
|
+
expect(client).to receive(:nb_name_encode).with(called_name).once
|
171
|
+
expect(client).to receive(:nb_name_encode).with(calling_name).once
|
172
|
+
client.session_request
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'requests a session for the provided name and the expected calling name' do
|
176
|
+
name = 'NBNAME'
|
177
|
+
called_name = "#{name} "
|
178
|
+
|
179
|
+
expect(client).to receive(:nb_name_encode).with(called_name).once
|
180
|
+
expect(client).to receive(:nb_name_encode).with(calling_name).once
|
181
|
+
client.session_request(name)
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'makes NetBIOS name uppercase' do
|
185
|
+
name = 'nbNamE'
|
186
|
+
called_name = "#{name.upcase} "
|
187
|
+
|
188
|
+
expect(client).to receive(:nb_name_encode).with(called_name).once
|
189
|
+
expect(client).to receive(:nb_name_encode).with(calling_name).once
|
190
|
+
client.session_request(name)
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'creates a SessionRequest packet' do
|
194
|
+
expect(RubySMB::Nbss::SessionRequest).to receive(:new).and_return(session_request)
|
195
|
+
client.session_request
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'sets the expected fields of the SessionRequest packet' do
|
199
|
+
encoded_called_name = "\x20#{client.nb_name_encode(called_name)}\x00"
|
200
|
+
encoded_calling_name = "\x20#{client.nb_name_encode(calling_name)}\x00"
|
201
|
+
allow(dispatcher).to receive(:send_packet) do |packet, _opts|
|
202
|
+
expect(packet).to be_a(RubySMB::Nbss::SessionRequest)
|
203
|
+
expect(packet.session_header.session_packet_type).to eq RubySMB::Nbss::SESSION_REQUEST
|
204
|
+
expect(packet.called_name).to eq encoded_called_name
|
205
|
+
expect(packet.calling_name).to eq encoded_calling_name
|
206
|
+
expect(packet.session_header.packet_length).to eq (encoded_called_name.size + encoded_calling_name.size)
|
207
|
+
end
|
208
|
+
client.session_request
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'sends the SessionRequest packet without adding additional NetBIOS Session Header' do
|
212
|
+
expect(dispatcher).to receive(:send_packet).with(session_request, nbss_header: false)
|
213
|
+
client.session_request
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'reads the full response packet, including the NetBIOS Session Header' do
|
217
|
+
expect(dispatcher).to receive(:recv_packet).with(full_response: true).and_return(session_header.to_binary_s)
|
218
|
+
client.session_request
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'parses the response with SessionHeader packet structure' do
|
222
|
+
expect(RubySMB::Nbss::SessionHeader).to receive(:read).with(session_header.to_binary_s).and_return(session_header)
|
223
|
+
client.session_request
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'returns true when it is a POSITIVE_SESSION_RESPONSE' do
|
227
|
+
session_header.session_packet_type = RubySMB::Nbss::POSITIVE_SESSION_RESPONSE
|
228
|
+
expect(client.session_request).to be true
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'raises an exception when it is a NEGATIVE_SESSION_RESPONSE' do
|
232
|
+
negative_session_response = RubySMB::Nbss::NegativeSessionResponse.new
|
233
|
+
negative_session_response.session_header.session_packet_type = RubySMB::Nbss::NEGATIVE_SESSION_RESPONSE
|
234
|
+
negative_session_response.error_code = 0x80
|
235
|
+
allow(dispatcher).to receive(:recv_packet).and_return(negative_session_response.to_binary_s)
|
236
|
+
expect { client.session_request }.to raise_error(RubySMB::Error::NetBiosSessionService)
|
237
|
+
end
|
238
|
+
end
|
130
239
|
|
240
|
+
describe '#nb_name_encode' do
|
241
|
+
it 'encodes as expected' do
|
242
|
+
input = 'TESTNB '
|
243
|
+
output = 'FEEFFDFEEOECCACACACACACACACACACA'
|
244
|
+
expect(client.nb_name_encode(input)).to eq output
|
245
|
+
end
|
246
|
+
end
|
131
247
|
end
|
132
248
|
|
133
249
|
context 'Protocol Negotiation' do
|
134
|
-
let(:random_junk) {
|
250
|
+
let(:random_junk) { 'fgrgrwgawrtw4t4tg4gahgn' }
|
135
251
|
let(:smb1_capabilities) {
|
136
|
-
{:
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
252
|
+
{ level_2_oplocks: 1,
|
253
|
+
nt_status: 1,
|
254
|
+
rpc_remote_apis: 1,
|
255
|
+
nt_smbs: 1,
|
256
|
+
large_files: 1,
|
257
|
+
unicode: 1,
|
258
|
+
mpx_mode: 0,
|
259
|
+
raw_mode: 0,
|
260
|
+
large_writex: 1,
|
261
|
+
large_readx: 1,
|
262
|
+
info_level_passthru: 1,
|
263
|
+
dfs: 0,
|
264
|
+
reserved1: 0,
|
265
|
+
bulk_transfer: 0,
|
266
|
+
nt_find: 1,
|
267
|
+
lock_and_read: 1,
|
268
|
+
unix: 0,
|
269
|
+
reserved2: 0,
|
270
|
+
lwio: 1,
|
271
|
+
extended_security: 1,
|
272
|
+
reserved3: 0,
|
273
|
+
dynamic_reauth: 0,
|
274
|
+
reserved4: 0,
|
275
|
+
compressed_data: 0,
|
276
|
+
reserved5: 0 }
|
161
277
|
}
|
162
278
|
let(:smb1_extended_response) {
|
163
279
|
packet = RubySMB::SMB1::Packet::NegotiateResponseExtended.new
|
@@ -176,19 +292,19 @@ RSpec.describe RubySMB::Client do
|
|
176
292
|
end
|
177
293
|
|
178
294
|
it 'sets the default SMB1 Dialect' do
|
179
|
-
expect(client.smb1_negotiate_request.dialects).to include(
|
295
|
+
expect(client.smb1_negotiate_request.dialects).to include(buffer_format: 2, dialect_string: RubySMB::Client::SMB1_DIALECT_SMB1_DEFAULT)
|
180
296
|
end
|
181
297
|
|
182
298
|
it 'sets the SMB2.02 dialect if SMB2 support is enabled' do
|
183
|
-
expect(client.smb1_negotiate_request.dialects).to include(
|
299
|
+
expect(client.smb1_negotiate_request.dialects).to include(buffer_format: 2, dialect_string: RubySMB::Client::SMB1_DIALECT_SMB2_DEFAULT)
|
184
300
|
end
|
185
301
|
|
186
302
|
it 'excludes the SMB2.02 Dialect if SMB2 support is disabled' do
|
187
|
-
expect(smb1_client.smb1_negotiate_request.dialects).to_not include(
|
303
|
+
expect(smb1_client.smb1_negotiate_request.dialects).to_not include(buffer_format: 2, dialect_string: RubySMB::Client::SMB1_DIALECT_SMB2_DEFAULT)
|
188
304
|
end
|
189
305
|
|
190
306
|
it 'excludes the default SMB1 Dialect if SMB1 support is disabled' do
|
191
|
-
expect(smb2_client.smb1_negotiate_request.dialects).to_not include(
|
307
|
+
expect(smb2_client.smb1_negotiate_request.dialects).to_not include(buffer_format: 2, dialect_string: RubySMB::Client::SMB1_DIALECT_SMB1_DEFAULT)
|
192
308
|
end
|
193
309
|
end
|
194
310
|
|
@@ -209,26 +325,18 @@ RSpec.describe RubySMB::Client do
|
|
209
325
|
describe '#negotiate_request' do
|
210
326
|
it 'calls #smb1_negotiate_request if SMB1 is enabled' do
|
211
327
|
expect(smb1_client).to receive(:smb1_negotiate_request)
|
212
|
-
expect(smb1_client).to receive(:send_recv)
|
213
328
|
smb1_client.negotiate_request
|
214
329
|
end
|
215
330
|
|
216
331
|
it 'calls #smb1_negotiate_request if both protocols are enabled' do
|
217
332
|
expect(client).to receive(:smb1_negotiate_request)
|
218
|
-
expect(client).to receive(:send_recv)
|
219
333
|
client.negotiate_request
|
220
334
|
end
|
221
335
|
|
222
336
|
it 'calls #smb2_negotiate_request if SMB2 is enabled' do
|
223
337
|
expect(smb2_client).to receive(:smb2_negotiate_request)
|
224
|
-
expect(smb2_client).to receive(:send_recv)
|
225
338
|
smb2_client.negotiate_request
|
226
339
|
end
|
227
|
-
|
228
|
-
it 'returns the raw response string from the server' do
|
229
|
-
expect(client).to receive(:send_recv).and_return('A')
|
230
|
-
expect(client.negotiate_request).to eq "A"
|
231
|
-
end
|
232
340
|
end
|
233
341
|
|
234
342
|
describe '#negotiate_response' do
|
@@ -238,39 +346,39 @@ RSpec.describe RubySMB::Client do
|
|
238
346
|
end
|
239
347
|
|
240
348
|
it 'raises an exception if the Response is invalid' do
|
241
|
-
expect{ smb1_client.negotiate_response(random_junk) }.to raise_error(RubySMB::Error::InvalidPacket)
|
349
|
+
expect { smb1_client.negotiate_response(random_junk) }.to raise_error(RubySMB::Error::InvalidPacket)
|
242
350
|
end
|
243
351
|
|
244
352
|
it 'considers the response invalid if it is not an actual Negotiate Response' do
|
245
353
|
bogus_response = smb1_extended_response
|
246
354
|
bogus_response.smb_header.command = 0xff
|
247
|
-
expect{ smb1_client.negotiate_response(bogus_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
355
|
+
expect { smb1_client.negotiate_response(bogus_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
248
356
|
end
|
249
357
|
|
250
358
|
it 'considers the response invalid if Extended Security is not enabled' do
|
251
359
|
bogus_response = smb1_extended_response
|
252
360
|
bogus_response.parameter_block.capabilities.extended_security = 0
|
253
|
-
expect{ smb1_client.negotiate_response(bogus_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
361
|
+
expect { smb1_client.negotiate_response(bogus_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
254
362
|
end
|
255
363
|
end
|
256
364
|
|
257
365
|
context 'with only SMB2' do
|
258
366
|
it 'returns a properly formed packet' do
|
259
|
-
expect(
|
367
|
+
expect(smb2_client.negotiate_response(smb2_response.to_binary_s)).to eq smb2_response
|
260
368
|
end
|
261
369
|
|
262
370
|
it 'raises an exception if the Response is invalid' do
|
263
|
-
expect{ smb2_client.negotiate_response(random_junk) }.to raise_error(RubySMB::Error::InvalidPacket)
|
371
|
+
expect { smb2_client.negotiate_response(random_junk) }.to raise_error(RubySMB::Error::InvalidPacket)
|
264
372
|
end
|
265
373
|
end
|
266
374
|
|
267
375
|
context 'with SMB1 and SMB2 enabled' do
|
268
376
|
it 'returns an SMB1 NegotiateResponse if it looks like SMB1' do
|
269
|
-
expect(
|
377
|
+
expect(client.negotiate_response(smb1_extended_response_raw)).to eq smb1_extended_response
|
270
378
|
end
|
271
379
|
|
272
380
|
it 'returns an SMB2 NegotiateResponse if it looks like SMB2' do
|
273
|
-
expect(
|
381
|
+
expect(client.negotiate_response(smb2_response.to_binary_s)).to eq smb2_response
|
274
382
|
end
|
275
383
|
end
|
276
384
|
end
|
@@ -279,7 +387,7 @@ RSpec.describe RubySMB::Client do
|
|
279
387
|
context 'when SMB1 was Negotiated' do
|
280
388
|
it 'turns off SMB2 support' do
|
281
389
|
client.parse_negotiate_response(smb1_extended_response)
|
282
|
-
expect(
|
390
|
+
expect(client.smb2).to be false
|
283
391
|
end
|
284
392
|
|
285
393
|
it 'sets whether or not signing is required' do
|
@@ -287,12 +395,27 @@ RSpec.describe RubySMB::Client do
|
|
287
395
|
client.parse_negotiate_response(smb1_extended_response)
|
288
396
|
expect(client.signing_required).to be true
|
289
397
|
end
|
398
|
+
|
399
|
+
it 'sets #dialect to the negotiated dialect' do
|
400
|
+
smb1_extended_response.dialects = [
|
401
|
+
RubySMB::SMB1::Dialect.new(dialect_string: 'A'),
|
402
|
+
RubySMB::SMB1::Dialect.new(dialect_string: 'B'),
|
403
|
+
RubySMB::SMB1::Dialect.new(dialect_string: 'C'),
|
404
|
+
]
|
405
|
+
smb1_extended_response.parameter_block.dialect_index = 1
|
406
|
+
client.parse_negotiate_response(smb1_extended_response)
|
407
|
+
expect(client.dialect).to eq 'B'
|
408
|
+
end
|
409
|
+
|
410
|
+
it 'returns the string \'SMB1\'' do
|
411
|
+
expect(client.parse_negotiate_response(smb1_extended_response)).to eq ('SMB1')
|
412
|
+
end
|
290
413
|
end
|
291
414
|
|
292
415
|
context 'when SMB2 was negotiated' do
|
293
416
|
it 'turns off SMB1 support' do
|
294
417
|
client.parse_negotiate_response(smb2_response)
|
295
|
-
expect(
|
418
|
+
expect(client.smb1).to be false
|
296
419
|
end
|
297
420
|
|
298
421
|
it 'sets whether or not signing is required' do
|
@@ -300,25 +423,50 @@ RSpec.describe RubySMB::Client do
|
|
300
423
|
client.parse_negotiate_response(smb2_response)
|
301
424
|
expect(client.signing_required).to be true
|
302
425
|
end
|
426
|
+
|
427
|
+
it 'sets #dialect to the negotiated dialect' do
|
428
|
+
smb2_response.dialect_revision = 2
|
429
|
+
client.parse_negotiate_response(smb2_response)
|
430
|
+
expect(client.dialect).to eq '0x0002'
|
431
|
+
end
|
432
|
+
|
433
|
+
it 'returns the string \'SMB2\'' do
|
434
|
+
expect(client.parse_negotiate_response(smb2_response)).to eq ('SMB2')
|
435
|
+
end
|
303
436
|
end
|
304
437
|
end
|
305
438
|
|
306
439
|
describe '#negotiate' do
|
307
440
|
it 'calls the backing methods' do
|
308
441
|
expect(client).to receive(:negotiate_request)
|
442
|
+
expect(client).to receive(:send_recv)
|
309
443
|
expect(client).to receive(:negotiate_response)
|
310
444
|
expect(client).to receive(:parse_negotiate_response)
|
311
445
|
client.negotiate
|
312
446
|
end
|
447
|
+
|
448
|
+
it 'sets the response-packet #dialects array with the dialects sent in the request' do
|
449
|
+
request_packet = client.smb1_negotiate_request
|
450
|
+
allow(client).to receive(:negotiate_request).and_return(request_packet)
|
451
|
+
allow(client).to receive(:send_recv)
|
452
|
+
allow(client).to receive(:negotiate_response).and_return(smb1_extended_response)
|
453
|
+
expect(smb1_extended_response).to receive(:dialects=).with(request_packet.dialects)
|
454
|
+
client.negotiate
|
455
|
+
end
|
456
|
+
|
457
|
+
it 'raise the expected exception if an error occurs' do
|
458
|
+
allow(client).to receive(:send_recv).and_raise(RubySMB::Error::InvalidPacket)
|
459
|
+
expect { client.negotiate }.to raise_error(RubySMB::Error::NegotiationFailure)
|
460
|
+
end
|
313
461
|
end
|
314
462
|
end
|
315
463
|
|
316
464
|
context 'Authentication' do
|
317
465
|
let(:type2_string) {
|
318
|
-
"TlRMTVNTUAACAAAAHgAeADgAAAA1goriwmZ8HEHtFHAAAAAAAAAAAJgAmABW\nAAAABgGxHQAAAA"
|
319
|
-
"9XAEkATgAtAFMATgBKAEQARwAwAFUAQQA5ADAARgACAB4A\nVwBJAE4ALQBTAE4ASgBEAEcAMABV"
|
320
|
-
"AEEAOQAwAEYAAQAeAFcASQBOAC0AUwBO\nAEoARABHADAAVQBBADkAMABGAAQAHgBXAEkATgAtAF"
|
321
|
-
"MATgBKAEQARwAwAFUA\nQQA5ADAARgADAB4AVwBJAE4ALQBTAE4ASgBEAEcAMABVAEEAOQAwAEYABw"
|
466
|
+
"TlRMTVNTUAACAAAAHgAeADgAAAA1goriwmZ8HEHtFHAAAAAAAAAAAJgAmABW\nAAAABgGxHQAAAA" \
|
467
|
+
"9XAEkATgAtAFMATgBKAEQARwAwAFUAQQA5ADAARgACAB4A\nVwBJAE4ALQBTAE4ASgBEAEcAMABV" \
|
468
|
+
"AEEAOQAwAEYAAQAeAFcASQBOAC0AUwBO\nAEoARABHADAAVQBBADkAMABGAAQAHgBXAEkATgAtAF" \
|
469
|
+
"MATgBKAEQARwAwAFUA\nQQA5ADAARgADAB4AVwBJAE4ALQBTAE4ASgBEAEcAMABVAEEAOQAwAEYABw" \
|
322
470
|
"AI\nADxThZ4nnNIBAAAAAA==\n"
|
323
471
|
}
|
324
472
|
|
@@ -342,28 +490,89 @@ RSpec.describe RubySMB::Client do
|
|
342
490
|
end
|
343
491
|
|
344
492
|
context 'for SMB1' do
|
345
|
-
let(:ntlm_client)
|
346
|
-
let(:type1_message)
|
493
|
+
let(:ntlm_client) { smb1_client.ntlm_client }
|
494
|
+
let(:type1_message) { ntlm_client.init_context }
|
347
495
|
let(:negotiate_packet) { RubySMB::SMB1::Packet::SessionSetupRequest.new }
|
496
|
+
let(:response_packet) { RubySMB::SMB1::Packet::SessionSetupResponse.new }
|
497
|
+
let(:final_response_packet) { RubySMB::SMB1::Packet::SessionSetupResponse.new }
|
348
498
|
let(:type3_message) { ntlm_client.init_context(type2_string) }
|
349
499
|
let(:user_id) { 2041 }
|
350
500
|
|
501
|
+
describe '#smb1_authenticate' do
|
502
|
+
before :example do
|
503
|
+
allow(smb1_client).to receive(:smb1_ntlmssp_negotiate)
|
504
|
+
allow(smb1_client).to receive(:smb1_ntlmssp_challenge_packet).and_return(response_packet)
|
505
|
+
allow(smb1_client).to receive(:smb1_type2_message).and_return(type2_string)
|
506
|
+
allow(smb1_client).to receive(:smb1_ntlmssp_authenticate)
|
507
|
+
allow(smb1_client).to receive(:smb1_ntlmssp_final_packet).and_return(final_response_packet)
|
508
|
+
end
|
509
|
+
|
510
|
+
it 'calls the backing methods' do
|
511
|
+
response_packet.smb_header.uid = user_id
|
512
|
+
expect(smb1_client).to receive(:smb1_ntlmssp_negotiate).and_return(negotiate_packet)
|
513
|
+
expect(smb1_client).to receive(:smb1_ntlmssp_challenge_packet).with(negotiate_packet).and_return(response_packet)
|
514
|
+
expect(smb1_client).to receive(:smb1_type2_message).with(response_packet).and_return(type2_string)
|
515
|
+
expect(smb1_client).to receive(:store_target_info).with(String)
|
516
|
+
expect(smb1_client).to receive(:extract_os_version).with(String)
|
517
|
+
expect(smb1_client).to receive(:smb1_ntlmssp_authenticate).with(Net::NTLM::Message::Type3, user_id)
|
518
|
+
expect(smb1_client).to receive(:smb1_ntlmssp_final_packet).and_return(final_response_packet)
|
519
|
+
smb1_client.smb1_authenticate
|
520
|
+
end
|
521
|
+
|
522
|
+
it 'stores the OS information from the challenge packet' do
|
523
|
+
native_os = 'Windows 7 Professional 7601 Service Pack 1'
|
524
|
+
native_lm = 'Windows 7 Professional 6.1'
|
525
|
+
response_packet.data_block.native_os = native_os
|
526
|
+
response_packet.data_block.native_lan_man = native_lm
|
527
|
+
smb1_client.smb1_authenticate
|
528
|
+
|
529
|
+
expect(smb1_client.peer_native_os).to eq native_os
|
530
|
+
expect(smb1_client.peer_native_lm).to eq native_lm
|
531
|
+
end
|
532
|
+
|
533
|
+
it 'stores the session key from the NTLM client' do
|
534
|
+
smb1_client.smb1_authenticate
|
535
|
+
expect(smb1_client.session_key).to eq ntlm_client.session_key
|
536
|
+
end
|
537
|
+
|
538
|
+
it 'stores the OS version number from the challenge message' do
|
539
|
+
smb1_client.smb1_authenticate
|
540
|
+
expect(smb1_client.os_version).to eq '6.1.7601'
|
541
|
+
end
|
542
|
+
|
543
|
+
it 'stores the user ID if the status code is \'STATUS_SUCCESS\'' do
|
544
|
+
response_packet.smb_header.uid = user_id
|
545
|
+
final_response_packet.smb_header.nt_status = WindowsError::NTStatus::STATUS_SUCCESS.value
|
546
|
+
smb1_client.smb1_authenticate
|
547
|
+
expect(smb1_client.user_id).to eq user_id
|
548
|
+
end
|
549
|
+
|
550
|
+
it 'does not store the user ID if the status code is not \'STATUS_SUCCESS\'' do
|
551
|
+
response_packet.smb_header.uid = user_id
|
552
|
+
final_response_packet.smb_header.nt_status = WindowsError::NTStatus::STATUS_PENDING.value
|
553
|
+
smb1_client.smb1_authenticate
|
554
|
+
expect(smb1_client.user_id).to eq nil
|
555
|
+
end
|
556
|
+
end
|
351
557
|
|
352
558
|
describe '#smb1_ntlmssp_auth_packet' do
|
353
559
|
it 'creates a new SessionSetupRequest packet' do
|
354
560
|
expect(RubySMB::SMB1::Packet::SessionSetupRequest).to receive(:new).and_return(negotiate_packet)
|
355
|
-
smb1_client.smb1_ntlmssp_auth_packet(
|
561
|
+
smb1_client.smb1_ntlmssp_auth_packet(type3_message, user_id)
|
356
562
|
end
|
357
563
|
|
358
|
-
it '
|
564
|
+
it 'sets the security blob with an NTLM Type 3 Message' do
|
359
565
|
expect(RubySMB::SMB1::Packet::SessionSetupRequest).to receive(:new).and_return(negotiate_packet)
|
360
|
-
expect(ntlm_client).to receive(:init_context).with(type2_string).and_return(type3_message)
|
361
566
|
expect(negotiate_packet).to receive(:set_type3_blob).with(type3_message.serialize)
|
362
|
-
smb1_client.smb1_ntlmssp_auth_packet(
|
567
|
+
smb1_client.smb1_ntlmssp_auth_packet(type3_message, user_id)
|
363
568
|
end
|
364
569
|
|
365
570
|
it 'enables extended security on the packet' do
|
366
|
-
expect(smb1_client.smb1_ntlmssp_auth_packet(
|
571
|
+
expect(smb1_client.smb1_ntlmssp_auth_packet(type3_message, user_id).smb_header.flags2.extended_security).to eq 1
|
572
|
+
end
|
573
|
+
|
574
|
+
it 'sets the max_buffer_size to the client\'s max_buffer_size' do
|
575
|
+
expect(smb1_client.smb1_ntlmssp_auth_packet(type3_message, user_id).parameter_block.max_buffer_size).to eq smb1_client.max_buffer_size
|
367
576
|
end
|
368
577
|
end
|
369
578
|
|
@@ -383,6 +592,10 @@ RSpec.describe RubySMB::Client do
|
|
383
592
|
it 'enables extended security on the packet' do
|
384
593
|
expect(smb1_client.smb1_ntlmssp_negotiate_packet.smb_header.flags2.extended_security).to eq 1
|
385
594
|
end
|
595
|
+
|
596
|
+
it 'sets the max_buffer_size to the client\'s max_buffer_size' do
|
597
|
+
expect(smb1_client.smb1_ntlmssp_negotiate_packet.parameter_block.max_buffer_size).to eq smb1_client.max_buffer_size
|
598
|
+
end
|
386
599
|
end
|
387
600
|
|
388
601
|
describe '#smb1_ntlmssp_authenticate' do
|
@@ -390,7 +603,7 @@ RSpec.describe RubySMB::Client do
|
|
390
603
|
expect(smb1_client).to receive(:smb1_ntlmssp_auth_packet).and_return(negotiate_packet)
|
391
604
|
expect(dispatcher).to receive(:send_packet).with(negotiate_packet)
|
392
605
|
expect(dispatcher).to receive(:recv_packet)
|
393
|
-
smb1_client.smb1_ntlmssp_authenticate(
|
606
|
+
smb1_client.smb1_ntlmssp_authenticate(type3_message, user_id)
|
394
607
|
end
|
395
608
|
end
|
396
609
|
|
@@ -421,11 +634,11 @@ RSpec.describe RubySMB::Client do
|
|
421
634
|
|
422
635
|
it 'raises an UnexpectedStatusCode if the status code is not correct' do
|
423
636
|
response.smb_header.nt_status = 0xc0000015
|
424
|
-
expect{ smb1_client.smb1_ntlmssp_challenge_packet(response.to_binary_s) }.to raise_error(
|
637
|
+
expect { smb1_client.smb1_ntlmssp_challenge_packet(response.to_binary_s) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
|
425
638
|
end
|
426
639
|
|
427
640
|
it 'raises an InvalidPacket if the Command field is wrong' do
|
428
|
-
expect{ smb1_client.smb1_ntlmssp_challenge_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
641
|
+
expect { smb1_client.smb1_ntlmssp_challenge_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
429
642
|
end
|
430
643
|
end
|
431
644
|
|
@@ -446,12 +659,12 @@ RSpec.describe RubySMB::Client do
|
|
446
659
|
end
|
447
660
|
|
448
661
|
it 'raises an InvalidPacket if the Command field is wrong' do
|
449
|
-
expect{ smb1_client.smb1_ntlmssp_final_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
662
|
+
expect { smb1_client.smb1_ntlmssp_final_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
450
663
|
end
|
451
664
|
end
|
452
665
|
|
453
666
|
describe '#smb1_type2_message' do
|
454
|
-
let(:fake_type2) {
|
667
|
+
let(:fake_type2) { 'NTLMSSP FOO' }
|
455
668
|
let(:response_packet) {
|
456
669
|
packet = RubySMB::SMB1::Packet::SessionSetupResponse.new
|
457
670
|
packet.set_type2_blob(fake_type2)
|
@@ -466,10 +679,50 @@ RSpec.describe RubySMB::Client do
|
|
466
679
|
let(:anonymous_request) { RubySMB::SMB1::Packet::SessionSetupLegacyRequest.new }
|
467
680
|
let(:anonymous_response) { RubySMB::SMB1::Packet::SessionSetupLegacyResponse.new }
|
468
681
|
|
682
|
+
describe '#smb1_anonymous_auth' do
|
683
|
+
it 'calls the backing methods' do
|
684
|
+
expect(client).to receive(:smb1_anonymous_auth_request).and_return(anonymous_request)
|
685
|
+
expect(client).to receive(:send_recv).with(anonymous_request)
|
686
|
+
expect(client).to receive(:smb1_anonymous_auth_response).and_return(anonymous_response)
|
687
|
+
client.smb1_anonymous_auth
|
688
|
+
end
|
689
|
+
|
690
|
+
it 'returns the status code' do
|
691
|
+
allow(client).to receive(:send_recv)
|
692
|
+
allow(client).to receive(:smb1_anonymous_auth_response).and_return(anonymous_response)
|
693
|
+
anonymous_response.smb_header.nt_status = WindowsError::NTStatus::STATUS_PENDING.value
|
694
|
+
expect(client.smb1_anonymous_auth).to eq WindowsError::NTStatus::STATUS_PENDING
|
695
|
+
end
|
696
|
+
|
697
|
+
it 'sets the expected Client\'s attribute from the response when the status code is STATUS_SUCCESS' do
|
698
|
+
native_os = 'Windows 7 Professional 7601 Service Pack 1'
|
699
|
+
native_lm = 'Windows 7 Professional 6.1'
|
700
|
+
primary_domain = 'SPEC_DOMAIN'
|
701
|
+
anonymous_response.smb_header.uid = user_id
|
702
|
+
anonymous_response.data_block.native_os = native_os
|
703
|
+
anonymous_response.data_block.native_lan_man = native_lm
|
704
|
+
anonymous_response.data_block.primary_domain = primary_domain
|
705
|
+
anonymous_response.smb_header.nt_status = WindowsError::NTStatus::STATUS_SUCCESS.value
|
706
|
+
|
707
|
+
allow(client).to receive(:send_recv)
|
708
|
+
allow(client).to receive(:smb1_anonymous_auth_response).and_return(anonymous_response)
|
709
|
+
|
710
|
+
client.smb1_anonymous_auth
|
711
|
+
expect(client.user_id).to eq user_id
|
712
|
+
expect(client.peer_native_os).to eq native_os
|
713
|
+
expect(client.peer_native_lm).to eq native_lm
|
714
|
+
expect(client.primary_domain).to eq primary_domain
|
715
|
+
end
|
716
|
+
end
|
717
|
+
|
469
718
|
describe '#smb1_anonymous_auth_request' do
|
470
719
|
it 'creates a SessionSetupLegacyRequest packet with a null byte for the oem password' do
|
471
720
|
expect(smb1_client.smb1_anonymous_auth_request.data_block.oem_password).to eq "\x00"
|
472
721
|
end
|
722
|
+
|
723
|
+
it 'creates a SessionSetupLegacyRequest packet with the max_buffer_size set to the client\'s max_buffer_size' do
|
724
|
+
expect(smb1_client.smb1_anonymous_auth_request.parameter_block.max_buffer_size).to eq smb1_client.max_buffer_size
|
725
|
+
end
|
473
726
|
end
|
474
727
|
|
475
728
|
describe '#smb1_anonymous_auth_response' do
|
@@ -484,20 +737,59 @@ RSpec.describe RubySMB::Client do
|
|
484
737
|
|
485
738
|
it 'raises an InvalidPacket error if the command is wrong' do
|
486
739
|
anonymous_response.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_NEGOTIATE
|
487
|
-
expect{ smb1_client.smb1_anonymous_auth_response(anonymous_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
740
|
+
expect { smb1_client.smb1_anonymous_auth_response(anonymous_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
488
741
|
end
|
489
742
|
end
|
490
|
-
|
491
743
|
end
|
492
744
|
end
|
493
745
|
|
494
746
|
context 'for SMB2' do
|
495
747
|
let(:ntlm_client) { smb2_client.ntlm_client }
|
496
|
-
let(:type1_message)
|
748
|
+
let(:type1_message) { ntlm_client.init_context }
|
497
749
|
let(:negotiate_packet) { RubySMB::SMB2::Packet::SessionSetupRequest.new }
|
750
|
+
let(:response_packet) { RubySMB::SMB2::Packet::SessionSetupResponse.new }
|
751
|
+
let(:final_response_packet) { RubySMB::SMB2::Packet::SessionSetupResponse.new }
|
498
752
|
let(:type3_message) { ntlm_client.init_context(type2_string) }
|
499
753
|
let(:session_id) { 0x0000040000000005 }
|
500
754
|
|
755
|
+
describe '#smb2_authenticate' do
|
756
|
+
before :example do
|
757
|
+
allow(smb2_client).to receive(:smb2_ntlmssp_negotiate)
|
758
|
+
allow(smb2_client).to receive(:smb2_ntlmssp_challenge_packet).and_return(response_packet)
|
759
|
+
allow(smb2_client).to receive(:smb2_type2_message).and_return(type2_string)
|
760
|
+
allow(smb2_client).to receive(:smb2_ntlmssp_authenticate)
|
761
|
+
allow(smb2_client).to receive(:smb2_ntlmssp_final_packet).and_return(final_response_packet)
|
762
|
+
end
|
763
|
+
|
764
|
+
it 'calls the backing methods' do
|
765
|
+
response_packet.smb2_header.session_id = session_id
|
766
|
+
expect(smb2_client).to receive(:smb2_ntlmssp_negotiate).and_return(negotiate_packet)
|
767
|
+
expect(smb2_client).to receive(:smb2_ntlmssp_challenge_packet).with(negotiate_packet).and_return(response_packet)
|
768
|
+
expect(smb2_client).to receive(:smb2_type2_message).with(response_packet).and_return(type2_string)
|
769
|
+
expect(smb2_client).to receive(:store_target_info).with(String)
|
770
|
+
expect(smb2_client).to receive(:extract_os_version).with(String)
|
771
|
+
expect(smb2_client).to receive(:smb2_ntlmssp_authenticate).with(Net::NTLM::Message::Type3, session_id)
|
772
|
+
expect(smb2_client).to receive(:smb2_ntlmssp_final_packet).and_return(final_response_packet)
|
773
|
+
smb2_client.smb2_authenticate
|
774
|
+
end
|
775
|
+
|
776
|
+
it 'stores the session ID from the challenge message' do
|
777
|
+
response_packet.smb2_header.session_id = session_id
|
778
|
+
smb2_client.smb2_authenticate
|
779
|
+
expect(smb2_client.session_id).to eq session_id
|
780
|
+
end
|
781
|
+
|
782
|
+
it 'stores the session key from the NTLM client' do
|
783
|
+
smb2_client.smb2_authenticate
|
784
|
+
expect(smb2_client.session_key).to eq ntlm_client.session_key
|
785
|
+
end
|
786
|
+
|
787
|
+
it 'stores the OS version number from the challenge message' do
|
788
|
+
smb2_client.smb2_authenticate
|
789
|
+
expect(smb2_client.os_version).to eq '6.1.7601'
|
790
|
+
end
|
791
|
+
end
|
792
|
+
|
501
793
|
describe '#smb2_ntlmssp_negotiate_packet' do
|
502
794
|
it 'creates a new SessionSetupRequest packet' do
|
503
795
|
expect(RubySMB::SMB2::Packet::SessionSetupRequest).to receive(:new).and_return(negotiate_packet)
|
@@ -516,7 +808,7 @@ RSpec.describe RubySMB::Client do
|
|
516
808
|
end
|
517
809
|
|
518
810
|
it 'increments client#smb2_message_id' do
|
519
|
-
expect{ smb2_client.smb2_ntlmssp_negotiate_packet }.to change(smb2_client, :smb2_message_id).to(2)
|
811
|
+
expect { smb2_client.smb2_ntlmssp_negotiate_packet }.to change(smb2_client, :smb2_message_id).to(2)
|
520
812
|
end
|
521
813
|
end
|
522
814
|
|
@@ -547,16 +839,16 @@ RSpec.describe RubySMB::Client do
|
|
547
839
|
|
548
840
|
it 'raises an UnexpectedStatusCode if the status code is not correct' do
|
549
841
|
response.smb2_header.nt_status = 0xc0000015
|
550
|
-
expect{ smb2_client.smb2_ntlmssp_challenge_packet(response.to_binary_s) }.to raise_error(
|
842
|
+
expect { smb2_client.smb2_ntlmssp_challenge_packet(response.to_binary_s) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
|
551
843
|
end
|
552
844
|
|
553
845
|
it 'raises an InvalidPacket if the Command field is wrong' do
|
554
|
-
expect{ smb2_client.smb2_ntlmssp_challenge_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
846
|
+
expect { smb2_client.smb2_ntlmssp_challenge_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
555
847
|
end
|
556
848
|
end
|
557
849
|
|
558
850
|
describe '#smb2_type2_message' do
|
559
|
-
let(:fake_type2) {
|
851
|
+
let(:fake_type2) { 'NTLMSSP FOO' }
|
560
852
|
let(:response_packet) {
|
561
853
|
packet = RubySMB::SMB2::Packet::SessionSetupResponse.new
|
562
854
|
packet.set_type2_blob(fake_type2)
|
@@ -567,23 +859,21 @@ RSpec.describe RubySMB::Client do
|
|
567
859
|
end
|
568
860
|
end
|
569
861
|
|
570
|
-
describe '#
|
862
|
+
describe '#smb2_ntlmssp_auth_packet' do
|
571
863
|
it 'creates a new SessionSetupRequest packet' do
|
572
864
|
expect(RubySMB::SMB2::Packet::SessionSetupRequest).to receive(:new).and_return(negotiate_packet)
|
573
|
-
smb2_client.smb2_ntlmssp_auth_packet(
|
865
|
+
smb2_client.smb2_ntlmssp_auth_packet(type3_message, session_id)
|
574
866
|
end
|
575
867
|
|
576
|
-
it '
|
868
|
+
it 'sets the security blob with an NTLM Type 3 Message' do
|
577
869
|
expect(RubySMB::SMB2::Packet::SessionSetupRequest).to receive(:new).and_return(negotiate_packet)
|
578
|
-
expect(ntlm_client).to receive(:init_context).with(type2_string).and_return(type3_message)
|
579
870
|
expect(negotiate_packet).to receive(:set_type3_blob).with(type3_message.serialize)
|
580
|
-
smb2_client.smb2_ntlmssp_auth_packet(
|
871
|
+
smb2_client.smb2_ntlmssp_auth_packet(type3_message, session_id)
|
581
872
|
end
|
582
873
|
|
583
874
|
it 'sets the session ID on the request packet' do
|
584
|
-
expect(smb2_client.smb2_ntlmssp_auth_packet(
|
875
|
+
expect(smb2_client.smb2_ntlmssp_auth_packet(type3_message, session_id).smb2_header.session_id).to eq session_id
|
585
876
|
end
|
586
|
-
|
587
877
|
end
|
588
878
|
|
589
879
|
describe '#smb2_ntlmssp_authenticate' do
|
@@ -591,7 +881,7 @@ RSpec.describe RubySMB::Client do
|
|
591
881
|
expect(smb2_client).to receive(:smb2_ntlmssp_auth_packet).and_return(negotiate_packet)
|
592
882
|
expect(dispatcher).to receive(:send_packet).with(negotiate_packet)
|
593
883
|
expect(dispatcher).to receive(:recv_packet)
|
594
|
-
smb2_client.smb2_ntlmssp_authenticate(
|
884
|
+
smb2_client.smb2_ntlmssp_authenticate(type3_message, session_id)
|
595
885
|
end
|
596
886
|
end
|
597
887
|
|
@@ -612,10 +902,50 @@ RSpec.describe RubySMB::Client do
|
|
612
902
|
end
|
613
903
|
|
614
904
|
it 'raises an InvalidPacket if the Command field is wrong' do
|
615
|
-
expect{ smb2_client.smb2_ntlmssp_final_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
905
|
+
expect { smb2_client.smb2_ntlmssp_final_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
|
616
906
|
end
|
617
907
|
end
|
618
908
|
end
|
909
|
+
|
910
|
+
describe '#store_target_info' do
|
911
|
+
let(:target_info_str) { "\x02\x00\x14\x00T\x00E\x00S\x00T\x00D\x00O\x00M"\
|
912
|
+
"\x00A\x00I\x00N\x00\x01\x00\x10\x00T\x00E\x00S\x00T\x00N\x00A\x00M"\
|
913
|
+
"\x00E\x00\x04\x00 \x00t\x00e\x00s\x00t\x00d\x00o\x00m\x00a\x00i\x00"\
|
914
|
+
"n\x00.\x00l\x00o\x00c\x00a\x00l\x00\x03\x002\x00t\x00e\x00s\x00t\x00"\
|
915
|
+
"n\x00a\x00m\x00e\x00.\x00t\x00e\x00s\x00t\x00d\x00o\x00m\x00a\x00i"\
|
916
|
+
"\x00n\x00.\x00l\x00o\x00c\x00a\x00l\x00\x05\x00 \x00t\x00e\x00s\x00t"\
|
917
|
+
"\x00f\x00o\x00r\x00e\x00s\x00t\x00.\x00l\x00o\x00c\x00a\x00l\x00\a"\
|
918
|
+
"\x00\b\x00Q7w\x01Fh\xD3\x01\x00\x00\x00\x00" }
|
919
|
+
|
920
|
+
it 'creates a Net::NTLM::TargetInfo object from the target_info string' do
|
921
|
+
expect(Net::NTLM::TargetInfo).to receive(:new).with(target_info_str).and_call_original
|
922
|
+
client.store_target_info(target_info_str)
|
923
|
+
end
|
924
|
+
|
925
|
+
it 'sets the expected Client\'s attribute' do
|
926
|
+
client.store_target_info(target_info_str)
|
927
|
+
expect(client.default_name).to eq 'TESTNAME'
|
928
|
+
expect(client.default_domain).to eq 'TESTDOMAIN'
|
929
|
+
expect(client.dns_host_name).to eq 'testname.testdomain.local'
|
930
|
+
expect(client.dns_domain_name).to eq 'testdomain.local'
|
931
|
+
expect(client.dns_tree_name).to eq 'testforest.local'
|
932
|
+
end
|
933
|
+
|
934
|
+
it 'stores the strings with UTF-8 encoding' do
|
935
|
+
client.store_target_info(target_info_str)
|
936
|
+
expect(client.default_name.encoding.name).to eq 'UTF-8'
|
937
|
+
expect(client.default_domain.encoding.name).to eq 'UTF-8'
|
938
|
+
expect(client.dns_host_name.encoding.name).to eq 'UTF-8'
|
939
|
+
expect(client.dns_domain_name.encoding.name).to eq 'UTF-8'
|
940
|
+
expect(client.dns_tree_name.encoding.name).to eq 'UTF-8'
|
941
|
+
end
|
942
|
+
end
|
943
|
+
|
944
|
+
describe '#extract_os_version' do
|
945
|
+
it 'returns the expected version number' do
|
946
|
+
expect(client.extract_os_version("\x06\x00q\x17\x00\x00\x00\x0F")).to eq '6.0.6001'
|
947
|
+
end
|
948
|
+
end
|
619
949
|
end
|
620
950
|
|
621
951
|
context 'Signing' do
|
@@ -626,13 +956,13 @@ RSpec.describe RubySMB::Client do
|
|
626
956
|
packet.smb2_header.signature = "\x00" * 16
|
627
957
|
packet
|
628
958
|
}
|
629
|
-
let(:fake_hmac) { "\x31\x07\x78\x3e\x35\xd7\x0e\x89\x08\x43\x8a\x18\xcd\x78\x52\x39".force_encoding(
|
959
|
+
let(:fake_hmac) { "\x31\x07\x78\x3e\x35\xd7\x0e\x89\x08\x43\x8a\x18\xcd\x78\x52\x39".force_encoding('ASCII-8BIT') }
|
630
960
|
|
631
961
|
context 'if signing is required and we have a session key' do
|
632
962
|
it 'generates the HMAC based on the packet and the NTLM session key and signs the packet with it' do
|
633
963
|
smb2_client.session_key = 'foo'
|
634
964
|
smb2_client.signing_required = true
|
635
|
-
expect(OpenSSL::HMAC).to receive(:digest).with(instance_of(OpenSSL::Digest::SHA256),smb2_client.session_key,request1.to_binary_s).and_return(fake_hmac)
|
965
|
+
expect(OpenSSL::HMAC).to receive(:digest).with(instance_of(OpenSSL::Digest::SHA256), smb2_client.session_key, request1.to_binary_s).and_return(fake_hmac)
|
636
966
|
expect(smb2_client.smb2_sign(request1).smb2_header.signature).to eq fake_hmac
|
637
967
|
end
|
638
968
|
end
|
@@ -652,12 +982,11 @@ RSpec.describe RubySMB::Client do
|
|
652
982
|
expect(smb2_client.smb2_sign(request1)).to eq request1
|
653
983
|
end
|
654
984
|
end
|
655
|
-
|
656
985
|
end
|
657
986
|
|
658
987
|
describe '#smb1_sign' do
|
659
988
|
let(:request1) { RubySMB::SMB1::Packet::SessionSetupRequest.new }
|
660
|
-
let(:fake_sig) { "\x9f\x62\xcf\x08\xd9\xc2\x83\x21".force_encoding(
|
989
|
+
let(:fake_sig) { "\x9f\x62\xcf\x08\xd9\xc2\x83\x21".force_encoding('ASCII-8BIT') }
|
661
990
|
|
662
991
|
context 'if signing is required and we have a session key' do
|
663
992
|
it 'generates the signature based on the packet, the sequence counter and the NTLM session key and signs the packet with it' do
|
@@ -685,7 +1014,7 @@ RSpec.describe RubySMB::Client do
|
|
685
1014
|
smb1_client.signing_required = true
|
686
1015
|
expect(smb1_client.smb1_sign(request1)).to eq request1
|
687
1016
|
end
|
688
|
-
|
1017
|
+
end
|
689
1018
|
end
|
690
1019
|
end
|
691
1020
|
|
@@ -699,7 +1028,7 @@ RSpec.describe RubySMB::Client do
|
|
699
1028
|
|
700
1029
|
it 'increments the client message id' do
|
701
1030
|
client.smb2_message_id = 1
|
702
|
-
expect{ client.increment_smb_message_id(request_packet) }.to change{ client.smb2_message_id}.by(1)
|
1031
|
+
expect { client.increment_smb_message_id(request_packet) }.to change { client.smb2_message_id }.by(1)
|
703
1032
|
end
|
704
1033
|
end
|
705
1034
|
|
@@ -735,23 +1064,23 @@ RSpec.describe RubySMB::Client do
|
|
735
1064
|
describe '#smb1_tree_from_response' do
|
736
1065
|
it 'raises an InvalidPacket exception if the command is not TREE_CONNECT' do
|
737
1066
|
response.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_NEGOTIATE
|
738
|
-
expect{ smb1_client.smb1_tree_from_response(path,response) }.to raise_error(RubySMB::Error::InvalidPacket)
|
1067
|
+
expect { smb1_client.smb1_tree_from_response(path, response) }.to raise_error(RubySMB::Error::InvalidPacket)
|
739
1068
|
end
|
740
1069
|
|
741
1070
|
it 'raises an UnexpectedStatusCode exception if we do not get STATUS_SUCCESS' do
|
742
1071
|
response.smb_header.nt_status = 0xc0000015
|
743
|
-
expect{ smb1_client.smb1_tree_from_response(path,response) }.to raise_error(RubySMB::Error::UnexpectedStatusCode, 'STATUS_NONEXISTENT_SECTOR')
|
1072
|
+
expect { smb1_client.smb1_tree_from_response(path, response) }.to raise_error(RubySMB::Error::UnexpectedStatusCode, 'STATUS_NONEXISTENT_SECTOR')
|
744
1073
|
end
|
745
1074
|
|
746
1075
|
it 'creates a new Tree from itself, the share path, and the response packet' do
|
747
|
-
expect(RubySMB::SMB1::Tree).to receive(:new).with(client:smb1_client, share:path, response:response)
|
748
|
-
smb1_client.smb1_tree_from_response(path,response)
|
1076
|
+
expect(RubySMB::SMB1::Tree).to receive(:new).with(client: smb1_client, share: path, response: response)
|
1077
|
+
smb1_client.smb1_tree_from_response(path, response)
|
749
1078
|
end
|
750
1079
|
end
|
751
1080
|
end
|
752
1081
|
|
753
1082
|
context 'with SMB2' do
|
754
|
-
let(:request) { RubySMB::SMB2::Packet::TreeConnectRequest.new
|
1083
|
+
let(:request) { RubySMB::SMB2::Packet::TreeConnectRequest.new }
|
755
1084
|
let(:response) {
|
756
1085
|
packet = RubySMB::SMB2::Packet::TreeConnectResponse.new
|
757
1086
|
packet.smb2_header.tree_id = tree_id
|
@@ -779,20 +1108,28 @@ RSpec.describe RubySMB::Client do
|
|
779
1108
|
describe '#smb2_tree_from_response' do
|
780
1109
|
it 'raises an InvalidPacket exception if the command is not TREE_CONNECT' do
|
781
1110
|
response.smb2_header.command = RubySMB::SMB2::Commands::NEGOTIATE
|
782
|
-
expect{ smb2_client.smb2_tree_from_response(path,response) }.to raise_error(RubySMB::Error::InvalidPacket)
|
1111
|
+
expect { smb2_client.smb2_tree_from_response(path, response) }.to raise_error(RubySMB::Error::InvalidPacket)
|
783
1112
|
end
|
784
1113
|
|
785
1114
|
it 'raises an UnexpectedStatusCode exception if we do not get STATUS_SUCCESS' do
|
786
1115
|
response.smb2_header.nt_status = 0xc0000015
|
787
|
-
expect{ smb2_client.smb2_tree_from_response(path,response) }.to raise_error(RubySMB::Error::UnexpectedStatusCode, 'STATUS_NONEXISTENT_SECTOR')
|
1116
|
+
expect { smb2_client.smb2_tree_from_response(path, response) }.to raise_error(RubySMB::Error::UnexpectedStatusCode, 'STATUS_NONEXISTENT_SECTOR')
|
788
1117
|
end
|
789
1118
|
|
790
1119
|
it 'creates a new Tree from itself, the share path, and the response packet' do
|
791
|
-
expect(RubySMB::SMB2::Tree).to receive(:new).with(client:smb2_client, share:path, response:response)
|
792
|
-
smb2_client.smb2_tree_from_response(path,response)
|
1120
|
+
expect(RubySMB::SMB2::Tree).to receive(:new).with(client: smb2_client, share: path, response: response)
|
1121
|
+
smb2_client.smb2_tree_from_response(path, response)
|
793
1122
|
end
|
794
1123
|
end
|
795
1124
|
|
1125
|
+
describe '#net_share_enum_all' do
|
1126
|
+
let(:shares){{}}
|
1127
|
+
|
1128
|
+
it 'it calls the smb2 method' do
|
1129
|
+
expect(smb2_client).to receive(:smb2_net_share_enum_all).with(sock.peeraddr).and_return(shares)
|
1130
|
+
smb2_client.net_share_enum_all(sock.peeraddr)
|
1131
|
+
end
|
1132
|
+
end
|
796
1133
|
end
|
797
1134
|
end
|
798
1135
|
|
@@ -819,9 +1156,9 @@ RSpec.describe RubySMB::Client do
|
|
819
1156
|
|
820
1157
|
it 'sets the data on the request packet' do
|
821
1158
|
modified_request = echo_request
|
822
|
-
modified_request.data_block.data =
|
1159
|
+
modified_request.data_block.data = 'DEADBEEF'
|
823
1160
|
expect(smb1_client).to receive(:send_recv).with(modified_request).and_return(echo_response.to_binary_s)
|
824
|
-
smb1_client.smb1_echo(data:
|
1161
|
+
smb1_client.smb1_echo(data: 'DEADBEEF')
|
825
1162
|
end
|
826
1163
|
|
827
1164
|
it 'returns the NT status code' do
|
@@ -841,4 +1178,6 @@ RSpec.describe RubySMB::Client do
|
|
841
1178
|
end
|
842
1179
|
end
|
843
1180
|
end
|
844
|
-
|
1181
|
+
|
1182
|
+
end
|
1183
|
+
|