ruby_smb 0.0.18 → 0.0.19
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|