pax_global_header00006660000000000000000000000064131537657200014523gustar00rootroot0000000000000052 comment=4790748f9fad1aafda500d68747bc21a465f262c net-ssh-4.2.0/000077500000000000000000000000001315376572000131075ustar00rootroot00000000000000net-ssh-4.2.0/.gitignore000066400000000000000000000000711315376572000150750ustar00rootroot00000000000000/Gemfile.lock doc ri pkg *.swp test/integration/.vagrant net-ssh-4.2.0/.rubocop.yml000066400000000000000000000001111315376572000153520ustar00rootroot00000000000000inherit_from: .rubocop_todo.yml AllCops: Exclude: - 'tryout/**/*' net-ssh-4.2.0/.rubocop_todo.yml000066400000000000000000001007111315376572000164060ustar00rootroot00000000000000# This configuration was generated by # `rubocop --auto-gen-config` # on 2016-12-11 13:27:14 +0100 using RuboCop version 0.46.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # Offense count: 4 # Configuration parameters: AllowSafeAssignment. Lint/AssignmentInCondition: Exclude: - 'lib/net/ssh/connection/channel.rb' - 'lib/net/ssh/connection/session.rb' - 'lib/net/ssh/proxy/command.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: AlignWith, SupportedStyles. # SupportedStyles: either, start_of_block, start_of_line Lint/BlockAlignment: Exclude: - 'test/integration/test_proxy.rb' # Offense count: 1 # Cop supports --auto-correct. Lint/DeprecatedClassMethods: Exclude: - 'Rakefile' # Offense count: 1 Lint/EmptyWhen: Exclude: - 'lib/net/ssh/config.rb' # Offense count: 223 # Cop supports --auto-correct. # Configuration parameters: AlignWith, SupportedStyles, AutoCorrect. # SupportedStyles: keyword, variable, start_of_line Lint/EndAlignment: Enabled: false # Offense count: 8 Lint/HandleExceptions: Exclude: - 'lib/net/ssh/authentication/session.rb' - 'lib/net/ssh/known_hosts.rb' - 'lib/net/ssh/transport/openssl.rb' - 'test/integration/common.rb' - 'test/integration/test_forward.rb' - 'test/start/test_connection.rb' - 'test/start/test_transport.rb' # Offense count: 72 Lint/ImplicitStringConcatenation: Exclude: - 'lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb' - 'lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb' # Offense count: 1 Lint/LiteralInCondition: Exclude: - 'lib/net/ssh/authentication/pageant.rb' # Offense count: 2 Lint/Loop: Exclude: - 'lib/net/ssh/authentication/methods/password.rb' - 'lib/net/ssh/key_factory.rb' # Offense count: 1 Lint/NextWithoutAccumulator: Exclude: - 'lib/net/ssh/connection/session.rb' # Offense count: 1 Lint/NonLocalExitFromIterator: Exclude: - 'lib/net/ssh/known_hosts.rb' # Offense count: 4 Lint/RescueException: Exclude: - 'lib/net/ssh/authentication/key_manager.rb' - 'lib/net/ssh/service/forward.rb' - 'lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb' # Offense count: 2 Lint/ShadowedException: Exclude: - 'lib/net/ssh/authentication/key_manager.rb' # Offense count: 1 Lint/UnderscorePrefixedVariableName: Exclude: - 'lib/net/ssh/test/local_packet.rb' # Offense count: 52 # Cop supports --auto-correct. # Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. Lint/UnusedBlockArgument: Exclude: - 'lib/net/ssh/connection/keepalive.rb' - 'lib/net/ssh/connection/session.rb' - 'lib/net/ssh/service/forward.rb' - 'test/authentication/methods/test_password.rb' - 'test/authentication/test_agent.rb' - 'test/common.rb' - 'test/connection/test_channel.rb' - 'test/connection/test_session.rb' - 'test/transport/test_algorithms.rb' - 'test/transport/test_hmac.rb' # Offense count: 50 # Cop supports --auto-correct. # Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods. Lint/UnusedMethodArgument: Enabled: false # Offense count: 3 # Configuration parameters: ContextCreatingMethods. Lint/UselessAccessModifier: Exclude: - 'lib/net/ssh/buffered_io.rb' - 'lib/net/ssh/connection/channel.rb' - 'lib/net/ssh/transport/session.rb' # Offense count: 10 Lint/UselessAssignment: Exclude: - 'lib/net/ssh/proxy/socks4.rb' - 'lib/net/ssh/proxy/socks5.rb' - 'test/integration/common.rb' - 'test/integration/test_forward.rb' # Offense count: 196 Metrics/AbcSize: Max: 76 # Offense count: 14 # Configuration parameters: CountComments. Metrics/BlockLength: Max: 88 # Offense count: 1 Metrics/BlockNesting: Max: 4 # Offense count: 27 # Configuration parameters: CountComments. Metrics/ClassLength: Max: 548 # Offense count: 34 Metrics/CyclomaticComplexity: Max: 39 # Offense count: 1341 # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. # URISchemes: http, https Metrics/LineLength: Max: 934 # Offense count: 164 # Configuration parameters: CountComments. Metrics/MethodLength: Max: 93 # Offense count: 1 # Configuration parameters: CountComments. Metrics/ModuleLength: Max: 142 # Offense count: 1 # Configuration parameters: CountKeywordArgs. Metrics/ParameterLists: Max: 6 # Offense count: 27 Metrics/PerceivedComplexity: Max: 23 # Offense count: 3 # Cop supports --auto-correct. Performance/RangeInclude: Exclude: - 'lib/net/ssh/transport/algorithms.rb' # Offense count: 3 # Cop supports --auto-correct. Performance/RedundantBlockCall: Exclude: - 'lib/net/ssh/connection/session.rb' - 'test/connection/test_channel.rb' # Offense count: 6 # Cop supports --auto-correct. Performance/RedundantMatch: Exclude: - 'lib/net/ssh/key_factory.rb' - 'lib/net/ssh/transport/server_version.rb' # Offense count: 13 Style/AccessorMethodName: Exclude: - 'lib/net/ssh/authentication/methods/password.rb' - 'lib/net/ssh/authentication/pageant.rb' - 'lib/net/ssh/connection/session.rb' - 'lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb' - 'lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb' - 'lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb' - 'lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: prefer_alias, prefer_alias_method Style/Alias: Exclude: - 'lib/net/ssh/connection/session.rb' - 'lib/net/ssh/service/forward.rb' # Offense count: 1 # Cop supports --auto-correct. Style/AlignArray: Exclude: - 'lib/net/ssh/proxy/socks5.rb' # Offense count: 25 # Cop supports --auto-correct. # Configuration parameters: EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle, SupportedLastArgumentHashStyles. # SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit Style/AlignHash: Exclude: - 'lib/net/ssh/transport/algorithms.rb' - 'test/connection/test_channel.rb' - 'test/integration/test_id_rsa_keys.rb' - 'test/transport/kex/test_diffie_hellman_group1_sha1.rb' - 'test/transport/kex/test_ecdh_sha2_nistp256.rb' - 'test/transport/test_algorithms.rb' - 'test/transport/test_cipher_factory.rb' - 'test/transport/test_state.rb' # Offense count: 72 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. # SupportedStyles: with_first_parameter, with_fixed_indentation Style/AlignParameters: Enabled: false # Offense count: 33 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: always, conditionals Style/AndOr: Exclude: - 'lib/net/ssh/authentication/key_manager.rb' - 'lib/net/ssh/buffer.rb' - 'lib/net/ssh/buffered_io.rb' - 'lib/net/ssh/config.rb' - 'lib/net/ssh/connection/channel.rb' - 'lib/net/ssh/connection/session.rb' - 'lib/net/ssh/key_factory.rb' - 'lib/net/ssh/service/forward.rb' - 'lib/net/ssh/test/channel.rb' - 'lib/net/ssh/test/script.rb' - 'lib/net/ssh/transport/cipher_factory.rb' - 'lib/net/ssh/transport/hmac.rb' - 'lib/net/ssh/transport/key_expander.rb' - 'lib/net/ssh/transport/packet_stream.rb' - 'test/common.rb' # Offense count: 2 Style/AsciiComments: Exclude: - 'lib/net/ssh/authentication/pageant.rb' - 'lib/net/ssh/buffered_io.rb' # Offense count: 11 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, ProceduralMethods, FunctionalMethods, IgnoredMethods. # SupportedStyles: line_count_based, semantic, braces_for_chaining # ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object # FunctionalMethods: let, let!, subject, watch # IgnoredMethods: lambda, proc, it Style/BlockDelimiters: Exclude: - 'Rakefile' - 'lib/net/ssh/authentication/key_manager.rb' - 'lib/net/ssh/config.rb' - 'lib/net/ssh/connection/keepalive.rb' - 'lib/net/ssh/proxy/command.rb' - 'lib/net/ssh/transport/ctr.rb' - 'test/test_buffer.rb' - 'test/verifiers/test_secure.rb' # Offense count: 8 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: braces, no_braces, context_dependent Style/BracesAroundHashParameters: Exclude: - 'lib/net/ssh/config.rb' - 'test/integration/test_ed25519_pkeys.rb' - 'test/integration/test_id_rsa_keys.rb' - 'test/transport/test_hmac.rb' - 'test/transport/test_session.rb' - 'test_kerberos_client2.rb' # Offense count: 2 Style/CaseEquality: Exclude: - 'lib/net/ssh/buffer.rb' - 'lib/net/ssh/connection/session.rb' # Offense count: 42 # Cop supports --auto-correct. # Configuration parameters: IndentWhenRelativeTo, SupportedStyles, IndentOneStep, IndentationWidth. # SupportedStyles: case, end Style/CaseIndentation: Exclude: - 'lib/net/ssh.rb' - 'lib/net/ssh/authentication/methods/hostbased.rb' - 'lib/net/ssh/authentication/methods/none.rb' - 'lib/net/ssh/authentication/methods/password.rb' - 'lib/net/ssh/authentication/methods/publickey.rb' - 'lib/net/ssh/buffer.rb' - 'lib/net/ssh/config.rb' - 'lib/net/ssh/test/local_packet.rb' - 'lib/net/ssh/test/packet.rb' # Offense count: 1 # Cop supports --auto-correct. Style/CharacterLiteral: Exclude: - 'test/test_buffer.rb' # Offense count: 12 Style/ClassAndModuleCamelCase: Exclude: - 'lib/net/ssh/transport/hmac/md5_96.rb' - 'lib/net/ssh/transport/hmac/sha1_96.rb' - 'lib/net/ssh/transport/hmac/sha2_256.rb' - 'lib/net/ssh/transport/hmac/sha2_256_96.rb' - 'lib/net/ssh/transport/hmac/sha2_512.rb' - 'lib/net/ssh/transport/hmac/sha2_512_96.rb' - 'test/transport/hmac/test_md5_96.rb' - 'test/transport/hmac/test_sha1_96.rb' - 'test/transport/hmac/test_sha2_256.rb' - 'test/transport/hmac/test_sha2_256_96.rb' - 'test/transport/hmac/test_sha2_512.rb' - 'test/transport/hmac/test_sha2_512_96.rb' # Offense count: 16 # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: nested, compact Style/ClassAndModuleChildren: Enabled: false # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: is_a?, kind_of? Style/ClassCheck: Exclude: - 'test/authentication/test_key_manager.rb' # Offense count: 7 Style/ClassVars: Exclude: - 'lib/net/ssh/config.rb' - 'lib/net/ssh/packet.rb' - 'test/authentication/methods/test_hostbased.rb' - 'test/authentication/methods/test_publickey.rb' # Offense count: 1 # Cop supports --auto-correct. Style/ColonMethodCall: Exclude: - 'lib/net/ssh/authentication/ed25519.rb' # Offense count: 4 # Cop supports --auto-correct. # Configuration parameters: Keywords. # Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW Style/CommentAnnotation: Exclude: - 'lib/net/ssh/authentication/ed25519.rb' - 'lib/net/ssh/authentication/session.rb' - 'lib/net/ssh/buffer.rb' - 'lib/net/ssh/config.rb' # Offense count: 2 # Cop supports --auto-correct. Style/CommentIndentation: Exclude: - 'test/integration/test_forward.rb' - 'test/transport/test_server_version.rb' # Offense count: 4 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, SingleLineConditionsOnly. # SupportedStyles: assign_to_condition, assign_inside_condition Style/ConditionalAssignment: Exclude: - 'lib/net/ssh/transport/ctr.rb' - 'lib/net/ssh/transport/state.rb' - 'test/test_key_factory.rb' - 'lib/net/ssh/config.rb' - 'lib/net/ssh/known_hosts.rb' - 'lib/net/ssh/proxy/socks5.rb' - 'lib/net/ssh/test/script.rb' # Offense count: 5 Style/ConstantName: Exclude: - 'lib/net/ssh/authentication/pageant.rb' - 'lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb' - 'lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb' - 'lib/net/ssh/transport/openssl.rb' # Offense count: 1 # Cop supports --auto-correct. Style/DefWithParentheses: Exclude: - 'test/integration/test_forward.rb' # Offense count: 16 Style/Documentation: Exclude: - 'spec/**/*' - 'test/**/*' - 'lib/net/ssh/authentication/ed25519.rb' - 'lib/net/ssh/connection/keepalive.rb' - 'lib/net/ssh/connection/session.rb' - 'lib/net/ssh/ruby_compat.rb' - 'lib/net/ssh/test/extensions.rb' - 'lib/net/ssh/transport/hmac/sha2_256_96.rb' - 'lib/net/ssh/transport/hmac/sha2_512.rb' - 'lib/net/ssh/transport/hmac/sha2_512_96.rb' - 'lib/net/ssh/transport/kex.rb' - 'lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb' - 'lib/net/ssh/transport/key_expander.rb' - 'lib/net/ssh/transport/openssl.rb' # Offense count: 6 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: leading, trailing Style/DotPosition: Exclude: - 'test/transport/test_algorithms.rb' # Offense count: 3 # Cop supports --auto-correct. Style/EachWithObject: Exclude: - 'lib/net/ssh/config.rb' - 'lib/net/ssh/connection/session.rb' - 'lib/net/ssh/service/forward.rb' # Offense count: 1 # Cop supports --auto-correct. Style/ElseAlignment: Exclude: - 'lib/net/ssh/packet.rb' # Offense count: 12 # Cop supports --auto-correct. # Configuration parameters: AllowAdjacentOneLineDefs. Style/EmptyLineBetweenDefs: Exclude: - 'lib/net/ssh/buffered_io.rb' - 'lib/net/ssh/ruby_compat.rb' - 'lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb' - 'lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb' - 'test/test_buffer.rb' - 'test/test_key_factory.rb' # Offense count: 16 # Cop supports --auto-correct. Style/EmptyLines: Exclude: - 'Rakefile' - 'lib/net/ssh/buffered_io.rb' - 'lib/net/ssh/connection/session.rb' - 'lib/net/ssh/known_hosts.rb' - 'lib/net/ssh/transport/cipher_factory.rb' - 'lib/net/ssh/transport/packet_stream.rb' - 'lib/net/ssh/transport/session.rb' - 'test/authentication/methods/test_keyboard_interactive.rb' - 'test/authentication/methods/test_password.rb' - 'test/integration/test_ed25519_pkeys.rb' - 'test/integration/test_id_rsa_keys.rb' - 'test/test_config.rb' - 'test/test_known_hosts.rb' - 'test/transport/test_cipher_factory.rb' # Offense count: 39 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines Style/EmptyLinesAroundClassBody: Enabled: false # Offense count: 4 # Cop supports --auto-correct. Style/EmptyLinesAroundMethodBody: Exclude: - 'lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb' - 'lib/net/ssh/transport/openssl.rb' - 'test/transport/hmac/test_sha2_256.rb' - 'test/transport/hmac/test_sha2_512.rb' # Offense count: 209 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines Style/EmptyLinesAroundModuleBody: Enabled: false # Offense count: 2 # Cop supports --auto-correct. Style/EmptyLiteral: Exclude: - 'lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb' - 'lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb' # Offense count: 1 # Cop supports --auto-correct. Style/EvenOdd: Exclude: - 'lib/net/ssh/buffer.rb' # Offense count: 17 # Cop supports --auto-correct. # Configuration parameters: AllowForAlignment, ForceEqualSignAlignment. Style/ExtraSpacing: Exclude: - 'Rakefile' - 'lib/net/ssh/authentication/methods/password.rb' - 'lib/net/ssh/authentication/pageant.rb' - 'lib/net/ssh/proxy/socks5.rb' - 'lib/net/ssh/transport/hmac/sha2_256_96.rb' - 'lib/net/ssh/transport/hmac/sha2_512_96.rb' - 'test/authentication/test_key_manager.rb' - 'test/integration/test_forward.rb' - 'test/test_config.rb' - 'test/test_key_factory.rb' - 'test/transport/test_packet_stream.rb' # Offense count: 2 # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: format, sprintf, percent Style/FormatString: Exclude: - 'lib/net/ssh/authentication/pageant.rb' - 'lib/net/ssh/loggable.rb' # Offense count: 35 # Configuration parameters: MinBodyLength. Style/GuardClause: Enabled: false # Offense count: 1 Style/IfInsideElse: Exclude: - 'lib/net/ssh/connection/session.rb' # Offense count: 9 # Cop supports --auto-correct. # Configuration parameters: MaxLineLength. Style/IfUnlessModifier: Exclude: - 'lib/net/ssh.rb' - 'lib/net/ssh/authentication/pageant.rb' - 'lib/net/ssh/proxy/command.rb' - 'lib/net/ssh/service/forward.rb' - 'lib/net/ssh/transport/ctr.rb' - 'lib/net/ssh/transport/key_expander.rb' - 'test/integration/test_proxy.rb' - 'test/test_key_factory.rb' # Offense count: 4 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. # SupportedStyles: special_inside_parentheses, consistent, align_brackets Style/IndentArray: Exclude: - 'lib/net/ssh/transport/openssl.rb' # Offense count: 4 # Cop supports --auto-correct. # Configuration parameters: IndentationWidth. Style/IndentAssignment: Exclude: - 'test/transport/kex/test_diffie_hellman_group1_sha1.rb' - 'test/transport/kex/test_ecdh_sha2_nistp256.rb' - 'test/transport/kex/test_ecdh_sha2_nistp384.rb' - 'test/transport/kex/test_ecdh_sha2_nistp521.rb' # Offense count: 227 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: normal, rails Style/IndentationConsistency: Enabled: false # Offense count: 96 # Cop supports --auto-correct. # Configuration parameters: Width. Style/IndentationWidth: Enabled: false # Offense count: 1 # Cop supports --auto-correct. Style/InfiniteLoop: Exclude: - 'lib/net/ssh/authentication/pageant.rb' # Offense count: 17 # Cop supports --auto-correct. Style/LeadingCommentSpace: Exclude: - 'test/integration/test_ed25519_pkeys.rb' - 'test/integration/test_forward.rb' - 'test/integration/test_id_rsa_keys.rb' - 'test/integration/test_proxy.rb' # Offense count: 27 # Cop supports --auto-correct. Style/LineEndConcatenation: Exclude: - 'lib/net/ssh/authentication/pageant.rb' - 'lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb' - 'lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb' - 'lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb' - 'lib/net/ssh/verifiers/secure.rb' # Offense count: 12 # Cop supports --auto-correct. Style/MethodCallWithoutArgsParentheses: Exclude: - 'test/authentication/test_key_manager.rb' - 'test/connection/test_session.rb' - 'test/integration/test_forward.rb' - 'test/start/test_user_nil.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline Style/MethodDefParentheses: Exclude: - 'test/common.rb' - 'test/integration/common.rb' # Offense count: 1 Style/MethodMissing: Exclude: - 'lib/net/ssh/connection/session.rb' # Offense count: 24 # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: snake_case, camelCase Style/MethodName: Exclude: - 'lib/net/ssh/authentication/ed25519_loader.rb' - 'test/authentication/test_agent.rb' - 'test/authentication/test_session.rb' - 'test/common.rb' - 'test/connection/test_channel.rb' - 'test/test_config.rb' - 'test/test_key_factory.rb' # Offense count: 3 # Cop supports --auto-correct. Style/MultilineIfThen: Exclude: - 'lib/net/ssh/buffered_io.rb' - 'lib/net/ssh/service/forward.rb' # Offense count: 23 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. # SupportedStyles: aligned, indented Style/MultilineOperationIndentation: Exclude: - 'lib/net/ssh/authentication/pageant.rb' - 'lib/net/ssh/known_hosts.rb' - 'lib/net/ssh/proxy/https.rb' - 'lib/net/ssh/transport/algorithms.rb' - 'lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb' - 'lib/net/ssh/transport/state.rb' - 'lib/net/ssh/verifiers/secure.rb' - 'test/authentication/methods/test_hostbased.rb' - 'test/authentication/methods/test_publickey.rb' # Offense count: 40 # Cop supports --auto-correct. Style/MutableConstant: Enabled: false # Offense count: 13 # Cop supports --auto-correct. Style/NegatedIf: Exclude: - 'lib/net/ssh.rb' - 'lib/net/ssh/authentication/key_manager.rb' - 'lib/net/ssh/service/forward.rb' - 'lib/net/ssh/transport/algorithms.rb' - 'lib/net/ssh/transport/hmac/abstract.rb' - 'lib/net/ssh/transport/session.rb' - 'lib/net/ssh/transport/state.rb' - 'test/test_key_factory.rb' - 'test/transport/test_state.rb' # Offense count: 1 # Cop supports --auto-correct. Style/NegatedWhile: Exclude: - 'lib/net/ssh/config.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles. # SupportedStyles: skip_modifier_ifs, always Style/Next: Exclude: - 'lib/net/ssh/authentication/key_manager.rb' - 'lib/net/ssh/transport/algorithms.rb' # Offense count: 2 # Cop supports --auto-correct. Style/NilComparison: Exclude: - 'lib/net/ssh/proxy/command.rb' - 'lib/net/ssh/transport/openssl.rb' # Offense count: 3 # Cop supports --auto-correct. Style/Not: Exclude: - 'lib/net/ssh/connection/channel.rb' # Offense count: 7 # Cop supports --auto-correct. Style/NumericLiterals: MinDigits: 310 # Offense count: 11 # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles. # SupportedStyles: predicate, comparison Style/NumericPredicate: Exclude: - 'spec/**/*' - 'lib/net/ssh/authentication/methods/password.rb' - 'lib/net/ssh/authentication/pageant.rb' - 'lib/net/ssh/buffer.rb' - 'lib/net/ssh/service/forward.rb' - 'lib/net/ssh/test/extensions.rb' - 'lib/net/ssh/transport/key_expander.rb' - 'test/transport/test_packet_stream.rb' - 'test_connection_close_shall_close_cannels.rb' # Offense count: 2 Style/OpMethod: Exclude: - 'lib/net/ssh/buffer.rb' - 'lib/net/ssh/version.rb' # Offense count: 15 # Cop supports --auto-correct. Style/ParallelAssignment: Exclude: - 'lib/net/ssh/config.rb' - 'lib/net/ssh/connection/channel.rb' - 'lib/net/ssh/connection/session.rb' - 'lib/net/ssh/errors.rb' - 'lib/net/ssh/test/socket.rb' - 'lib/net/ssh/version.rb' - 'test/authentication/test_agent.rb' - 'test/common.rb' - 'test/connection/test_channel.rb' # Offense count: 7 # Cop supports --auto-correct. # Configuration parameters: AllowSafeAssignment. Style/ParenthesesAroundCondition: Exclude: - 'lib/net/ssh/authentication/ed25519.rb' - 'lib/net/ssh/service/forward.rb' - 'lib/net/ssh/transport/cipher_factory.rb' - 'lib/net/ssh/transport/ctr.rb' - 'test/integration/test_proxy.rb' # Offense count: 5 # Cop supports --auto-correct. # Configuration parameters: PreferredDelimiters. Style/PercentLiteralDelimiters: Exclude: - 'Rakefile' - 'net-ssh.gemspec' - 'test/test_config.rb' # Offense count: 16 # Cop supports --auto-correct. Style/PerlBackrefs: Exclude: - 'lib/net/ssh/buffer.rb' - 'lib/net/ssh/config.rb' - 'lib/net/ssh/key_factory.rb' - 'lib/net/ssh/proxy/command.rb' - 'lib/net/ssh/proxy/socks5.rb' - 'lib/net/ssh/transport/openssl.rb' - 'test/integration/common.rb' # Offense count: 14 # Cop supports --auto-correct. Style/Proc: Exclude: - 'lib/net/ssh/connection/session.rb' - 'lib/net/ssh/test/channel.rb' - 'lib/net/ssh/transport/algorithms.rb' - 'lib/net/ssh/verifiers/secure.rb' - 'test/authentication/methods/test_hostbased.rb' - 'test/authentication/methods/test_publickey.rb' - 'test/connection/test_channel.rb' - 'test/connection/test_session.rb' # Offense count: 7 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: compact, exploded Style/RaiseArgs: Exclude: - 'lib/net/ssh/authentication/ed25519.rb' # Offense count: 4 # Cop supports --auto-correct. Style/RedundantBegin: Exclude: - 'lib/net/ssh/buffered_io.rb' - 'lib/net/ssh/verifiers/strict.rb' - 'test/manual/test_pageant.rb' # Offense count: 1 # Cop supports --auto-correct. Style/RedundantParentheses: Exclude: - 'support/arcfour_check.rb' # Offense count: 57 # Cop supports --auto-correct. # Configuration parameters: AllowMultipleReturnValues. Style/RedundantReturn: Enabled: false # Offense count: 11 # Cop supports --auto-correct. Style/RedundantSelf: Exclude: - 'lib/net/ssh/authentication/ed25519.rb' - 'lib/net/ssh/connection/channel.rb' - 'lib/net/ssh/test/extensions.rb' - 'lib/net/ssh/transport/openssl.rb' - 'test/authentication/test_ed25519.rb' # Offense count: 4 # Cop supports --auto-correct. Style/RescueModifier: Exclude: - 'lib/net/ssh/service/forward.rb' - 'lib/net/ssh/transport/algorithms.rb' # Offense count: 1 # Cop supports --auto-correct. Style/SelfAssignment: Exclude: - 'lib/net/ssh/config.rb' # Offense count: 8 # Cop supports --auto-correct. # Configuration parameters: AllowAsExpressionSeparator. Style/Semicolon: Exclude: - 'lib/net/ssh/buffer.rb' - 'test/connection/test_channel.rb' - 'test/connection/test_session.rb' - 'test/transport/kex/test_ecdh_sha2_nistp256.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: only_raise, only_fail, semantic Style/SignalException: Exclude: - 'lib/net/ssh/config.rb' - 'lib/net/ssh/connection/channel.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: AllowIfMethodIsEmpty. Style/SingleLineMethods: Exclude: - 'lib/net/ssh/buffered_io.rb' # Offense count: 16 # Cop supports --auto-correct. Style/SpaceAfterColon: Exclude: - 'lib/net/ssh/authentication/ed25519.rb' - 'test/integration/test_ed25519_pkeys.rb' - 'test/verifiers/test_secure.rb' # Offense count: 254 # Cop supports --auto-correct. Style/SpaceAfterComma: Enabled: false # Offense count: 132 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: space, no_space Style/SpaceAroundEqualsInParameterDefault: Enabled: false # Offense count: 55 # Cop supports --auto-correct. # Configuration parameters: AllowForAlignment. Style/SpaceAroundOperators: Enabled: false # Offense count: 6 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: space, no_space Style/SpaceBeforeBlockBraces: Exclude: - 'lib/net/ssh/authentication/session.rb' - 'support/ssh_tunnel_bug.rb' - 'test/authentication/test_agent.rb' - 'test/connection/test_session.rb' # Offense count: 4 # Cop supports --auto-correct. Style/SpaceBeforeComma: Exclude: - 'test/integration/test_forward.rb' - 'test/integration/test_proxy.rb' # Offense count: 3 # Cop supports --auto-correct. # Configuration parameters: AllowForAlignment. Style/SpaceBeforeFirstArg: Exclude: - 'lib/net/ssh/transport/hmac/sha2_256_96.rb' - 'lib/net/ssh/transport/hmac/sha2_512_96.rb' - 'test/authentication/test_key_manager.rb' # Offense count: 10 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters. # SupportedStyles: space, no_space Style/SpaceInsideBlockBraces: Exclude: - 'lib/net/ssh/authentication/session.rb' - 'lib/net/ssh/transport/ctr.rb' - 'support/ssh_tunnel_bug.rb' - 'test/authentication/test_agent.rb' - 'test/authentication/test_key_manager.rb' - 'test/start/test_user_nil.rb' # Offense count: 22 # Cop supports --auto-correct. Style/SpaceInsideBrackets: Exclude: - 'Rakefile' - 'lib/net/ssh/transport/algorithms.rb' - 'lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb' - 'test/integration/test_forward.rb' - 'test/start/test_options.rb' # Offense count: 69 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SupportedStyles. # SupportedStyles: space, no_space, compact Style/SpaceInsideHashLiteralBraces: Enabled: false # Offense count: 13 # Cop supports --auto-correct. Style/SpaceInsideParens: Exclude: - 'lib/net/ssh/transport/algorithms.rb' - 'lib/net/ssh/transport/openssl.rb' # Offense count: 3 # Cop supports --auto-correct. Style/SpaceInsideRangeLiteral: Exclude: - 'lib/net/ssh/authentication/ed25519.rb' - 'lib/net/ssh/transport/algorithms.rb' # Offense count: 22 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: use_perl_names, use_english_names Style/SpecialGlobalVars: Exclude: - 'lib/net/ssh/authentication/agent.rb' - 'lib/net/ssh/connection/session.rb' - 'support/ssh_tunnel_bug.rb' - 'test/integration/common.rb' - 'test/integration/test_forward.rb' - 'test/manual/test_pageant.rb' - 'test/test_all.rb' - 'test_connection_close_shall_close_cannels.rb' - 'test_kerberos_client2.rb' # Offense count: 1846 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes Style/StringLiterals: Enabled: false # Offense count: 11 # Cop supports --auto-correct. # Configuration parameters: IgnoredMethods. # IgnoredMethods: respond_to, define_method Style/SymbolProc: Exclude: - 'lib/net/ssh/authentication/session.rb' - 'lib/net/ssh/buffer.rb' - 'lib/net/ssh/connection/session.rb' - 'lib/net/ssh/test/extensions.rb' - 'lib/net/ssh/transport/algorithms.rb' - 'test/integration/test_forward.rb' - 'test/test/test_test.rb' # Offense count: 1 # Cop supports --auto-correct. Style/Tab: Exclude: - 'lib/net/ssh/config.rb' # Offense count: 39 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: final_newline, final_blank_line Style/TrailingBlankLines: Enabled: false # Offense count: 255 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyleForMultiline, SupportedStyles. # SupportedStyles: comma, consistent_comma, no_comma Style/TrailingCommaInLiteral: Exclude: - 'lib/net/ssh/key_factory.rb' - 'lib/net/ssh/transport/cipher_factory.rb' - 'lib/net/ssh/transport/kex.rb' - 'lib/net/ssh/transport/openssl.rb' - 'test/transport/test_packet_stream.rb' # Offense count: 66 # Cop supports --auto-correct. Style/TrailingWhitespace: Enabled: false # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, Whitelist. # Whitelist: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym Style/TrivialAccessors: Exclude: - 'test/transport/kex/test_diffie_hellman_group1_sha1.rb' # Offense count: 6 # Cop supports --auto-correct. Style/UnlessElse: Exclude: - 'lib/net/ssh/buffer.rb' - 'lib/net/ssh/transport/ctr.rb' - 'test/transport/kex/test_ecdh_sha2_nistp256.rb' - 'test/transport/kex/test_ecdh_sha2_nistp384.rb' - 'test/transport/kex/test_ecdh_sha2_nistp521.rb' - 'test/transport/test_server_version.rb' # Offense count: 4 # Cop supports --auto-correct. Style/UnneededInterpolation: Exclude: - 'lib/net/ssh/proxy/socks5.rb' - 'lib/net/ssh/transport/session.rb' - 'test/integration/test_forward.rb' # Offense count: 4 # Cop supports --auto-correct. Style/UnneededPercentQ: Exclude: - 'net-ssh.gemspec' - 'test/test_config.rb' # Offense count: 2 # Cop supports --auto-correct. Style/WhileUntilDo: Exclude: - 'lib/net/ssh/config.rb' - 'test/integration/common.rb' # Offense count: 3 # Cop supports --auto-correct. # Configuration parameters: SupportedStyles, WordRegex. # SupportedStyles: percent, brackets Style/WordArray: EnforcedStyle: percent MinSize: 3 # Offense count: 7 # Cop supports --auto-correct. Style/ZeroLengthPredicate: Exclude: - 'lib/net/ssh/buffered_io.rb' - 'lib/net/ssh/connection/channel.rb' - 'test/integration/test_forward.rb' - 'test_connection_close_shall_close_cannels.rb' net-ssh-4.2.0/.travis.yml000066400000000000000000000026221315376572000152220ustar00rootroot00000000000000language: ruby sudo: true dist: trusty addon: hosts: gateway.netssh rvm: - 2.0 - 2.1 - 2.2 - 2.3.0 - 2.4.0 - jruby-9.1.6.0 - rbx-3.69 - ruby-head env: NET_SSH_RUN_INTEGRATION_TESTS=1 matrix: exclude: - rvm: rbx-3.69 - rvm: jruby-9.1.6.0 include: - rvm: rbx-3.69 env: NET_SSH_RUN_INTEGRATION_TESTS= - rvm: jruby-9.1.6.0 env: JRUBY_OPTS='--client -J-XX:+TieredCompilation -J-XX:TieredStopAtLevel=1 -Xcext.enabled=false -J-Xss2m -Xcompile.invokedynamic=false' NET_SSH_RUN_INTEGRATION_TESTS= fast_finish: true allow_failures: - rvm: rbx-3.69 - rvm: jruby-9.1.6.0 - rvm: ruby-head install: - export JRUBY_OPTS='--client -J-XX:+TieredCompilation -J-XX:TieredStopAtLevel=1 -Xcext.enabled=false -J-Xss2m -Xcompile.invokedynamic=false' - sudo pip install ansible - gem install bundler -v "= 1.13.7" - bundle _1.13.7_ install - BUNDLE_GEMFILE=./Gemfile.norbnacl bundle _1.13.7_ install - sudo ansible-galaxy install rvm_io.ruby - sudo chown -R travis:travis /home/travis/.ansible - ansible-playbook ./test/integration/playbook.yml -i "localhost," --become -c local -e 'no_rvm=true' -e 'myuser=travis' -e 'mygroup=travis' -e 'homedir=/home/travis' script: - ssh -V - bundle _1.13.7_ exec rake test - BUNDLE_GEMFILE=./Gemfile.norbnacl bundle _1.13.7_ exec rake test - bundle _1.13.7_ exec rake test_test - bundle _1.13.7_ exec rubocop net-ssh-4.2.0/CHANGES.txt000066400000000000000000000477641315376572000147420ustar00rootroot00000000000000=== 4.2.0.rc2 * Fix double close bug on auth failure (or ruby 2.2 or earlier) [#538] === 4.2.0.rc1 * Improved logging with proxy command [Dmitriy Ivliev, #530] * Close transport on proxy error [adamruzicka, #526] * Support multiple identity files [Kimura Masayuki, #528] * Move `none` cipher to end of cipher list [Brian Cain, #525] * Deprecate `:paranoid` in favor of `:verify_host_key` [Jared Beck, #524] * Support Multile Include ssh config files [Kasumi Hanazuki, #516] * Support Relative path in ssh confif files [Akinori MUSHA, #510] * add direct-streamlocal@openssh.com support in Forward class [Harald Sitter, #502] === 4.1.0 === 4.1.0.rc1 * ProxyJump support [Ryan McGeary, #500] * Fix agent detection on Windows [Christian Koehler, #495] === 4.1.0.beta1 * Fix nil error when libsodium is not there [chapmajs ,#488] * SSH certificate support for client auth [David Bartley, #485] === 4.0.1 === 4.0.1.rc2 * ENV["HOME"] might be empty so filter non expandable paths [Matt Casper, #351] === 4.0.1.rc1 * support of rbnacl 4.0 and better error message [#479] * support include in config files [Kimura Masayuki, #475] * fixed issue with ruby 2.2 or older on windows [#472] === 4.0.0 === 4.0.0.rc3 * parse `+` character in config files [Christoph Lupprich, #470, #314] === 4.0.0.rc2 * Fixed OpenSSL 2.0/Ruby 2.4.0 warnings [Miklós Fazekas, #468] * Added ssh-ed25519 to KnownHosts:SUPPORTED_TYPE [detatka-kuzlatka-otevrete, Miklós Fazekas, #459] * Allow nil for :passhrase and passing in nil option is now a depreaction warning [Miklós Fazekas, #465] === 4.0.0.rc1 * Allow :password to be nil for capistrano v2 compatibility [Will Bryant, #357] * In next_packet if prefer consuming buffer before filling it again if we have enough data [Miklós Fazekas, #454] === 4.0.0.beta4 * Added exitstatus method to exec's return [Miklós Fazekas, #452] * Don't raise from exec if server closes transport just after channel close [Miklós Fazekas, #450] * Removed java_pageant, as jruby should be using regular pagent impl [Miklós Fazekas, ] * Use SSH_AUTH_SOCK if possible on windows (cygwin) [Miklós Fazekas, Martin Dürst, #365, #361] * HTTPS proxy support [Marcus Ilgner, #432] * Supports ruby 2.4.0.dev new exception type from OpenSSL::PKey.read === 4.0.0.beta3 * Fix Net::SSH::Disconnect exceptions when channels are closed cleanly [Miklos Fazekas, #421, #422] === 4.0.0.beta2 * Fix raiseUnlessLoaded undefined ERROR issue [Miklos Fazekas, #418] === 4.0.0.beta1 * Fix pageant [elconas, #235] * Relaxed rbnacl,rbnacl-selenium contstraints ang give better errors about them [Miklos Fazekas, #398] * Fix UTF-8 encoding issues [Ethan J. Brown, #407] === 4.0.0.alpha4 * Experimental event loop abstraction [Miklos Fazekas] * RbNacl dependency is optional [Miklos Fazekas] * agent_socket_factory option [Alon Goldboim] * client sends KEXINIT, it doesn't have to wait for server [Miklos Fazekas] * better error message when option is nil [Kane Morgan] * prompting can be customized [Miklos Fazekas] === 4.0.0.alpha3 * added max_select_wait_time [Eugene Kenny] === 4.0.0.alpha2 * when transport closes we're cleaning up channels [Miklos Fazekas] === 4.0.0.alpha1 * ed25519 key support [Miklos Fazekas] * removed camellia [Miklos Fazekas] === 3.1.0 === 3.1.0.rc1 * fix Secure#verify [Jean Boussier] * use the smallest of don't spend longer time than keepalive if it's configured [Eugene Kenny] === 3.1.0.beta3 * forward/on_open_failed should stop listning closed socket otherwise it locks #269 [Miklos Fazekas,Scott McGillivray] * fix incorrect pattern handling in config files #310 [Miklos Fazekas] === 3.1.0.beta2 * trying to execute something on a not yet opend channel throws nicer messag [Miklos Fazekas] * calling close on a not opened channel marks the channel for close [Miklos Fazekas] * read keepalive configuration from ssh config files [Miklos Fazekas] * send client version on hadshake before waiting for server to reduce handshake time [Miklos Fazekas] * allow custom Net::SSH::KnownHosts implementations [Jean Boussier] * memoize known host so we only search it once per session [Jean Boussier, Miklos Fazekas] === 3.0.2 === 3.0.2.rc1 * fixed rare WaitWritable error with proxy commands [Miklos Fazkas, Andre Meij]] * if Net::SSH.start user is nil and config has no entry we default to Etc.getlogin * Bugfix: CHANNEL_CLOSE was sent before draining ouput buffer #280 [Christopher F. Auston] === 3.0.1 === 3.0.1.rc1 * Breaking change from 2.* series: exec! without block now returns empty string instread of nil if command has no output [https://github.com/net-ssh/net-ssh/pull/273] * Support remote_user as %r in proxy commands [Dominic Scheirlinck] * Raise Net::SSH::ConnectionTimeout from connection timeout [Carl Hoerberg] === 3.0.0.rc1 * SemVer: Major version change because of dropping of ruby 1.9 === 2.10.1.rc2 * Win: Use fiddle on ruby 2.1 too [Charlie Savage] === 2.10.1.rc1 * Added ruby 2.0 requirement to gemspec [Alex Schultz] === 2.10.0 === 2.10.0-beta2 * Fix :passphrase option with :non_interactive [Jeremy Stanley] * Use Socket.tcp with connect_timeout instead of Timeout::timeout [Carl Hörberg] * Support for hostname hashes [Jef Mathiot] * Ruby 1.9.3 is no longer supported but should moslty work expect for stuff like connect_timeout === 2.10.0-beta1 * Fix could not parse PKey error. [Andrey Voronkov] * Workaround for threading issue in MRI + singleton method declaration [Matt Brictson] * Configuration change: we no longer append all supported algorithms, this is so you can exclude insecure algorithms. If you want to use the old behaviour specify append_all_supported_algorithms => true [voidus, mfazekas] * New configuration option: :non_interactive => true in case you prefer an authmethod to fail rather than prompt. [mfazekas] * Configuration change: password will now ask for password up to the :number_of_password_prompts times. If you want the 2.9.1 behaviour of never asking password please set number_of_password_prompts to 0. === 2.9.4-beta1 * Use sysread and syswrite on Windows instead of read_nonblock and write [marc-etienne] * Windows/peagant: use fiddle on ruby 2.2+/windows [Charlie Savage] * Check if ssh key is a file [kiela] === 2.9.3 === 2.9.2-rc3 * Remove advertised algorithms that were not working (curve25519-sha256@libssh.org) [mfazekas] === 2.9.2-rc2 * number_of_password_prompts is now accepted as ssh option, by setting it 0 net-ssh will not ask for password for password auth as with previous versions [mfazekas] === 2.9.2-rc1 * Documentation fixes and refactoring to keepalive [detiber, mfazekas] === 2.9.2-beta * Remove advertised algorithms that were not working (ssh-rsa-cert-* *ed25519 acm*-gcm@openssh.com) [mfazekas] * Unkown algorithms now ignored instead of failed [mfazekas] * Configuration change: Asks for password with password auth (up to number_of_password_prompts) [mfazekas] * Removed warnings [amatsuda] === 2.9.1 / 13 May 2014 * Fix for unknown response from agent on Windows with 64-bit PuTTY [chrahunt] * Support negative patterns in host lookup from the SSH config file [nirvdrum] === 2.9.0 / 30 Apr 2014 * New ciphers [chr4] * Added host keys: ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com ssh-ed25519-cert-v01@openssh.com ssh-ed25519 * Added HMACs: hmac-sha2-512-etm@openssh.com hmac-sha2-256-etm@openssh.com umac-128-etm@openssh.com * Added Kex: aes256-gcm@openssh.com aes128-gcm@openssh.com curve25519-sha256@libssh.org * Added private key support for id_ed25519 * IdentiesOnly will not disable ssh_agent - fixes #148 and new fix for #137 [mfazekas] * Ignore errors during ssh agent negotiation [simonswine, jasiek] * Added an optional "options" argument to test socket open method [jefmathiot] * Added gem signing (again) with new cert [delano] === 2.8.1 / 19 Feb 2014 * Correct location of global known_hosts files [mfischer-zd] * Fix for password authentication [blackpond, zachlipton, delano] === 2.8.0 / 01 Feb 2014 * Handle ssh-rsa and ssh-dss certificate files [bobveznat] * Correctly interpret /etc/ssh_config Authentication settings based on openssh /etc/ssh_config system defaults [therealjessesanford, liggitt] * Fixed pageant support for Windows [jarredholman] * Support %r in ProxyCommand configuration in ssh_config files as defined in OpenSSH [yugui] * Don't use ssh-agent if :keys_only is true [SFEley] * Fix the bug in keys with comments [bobtfish] * Add a failing tests for options in pub keys [bobtfish] * Assert that the return value from ssh block is returned [carlhoerberg] * Don't close the connection it's already closed [carlhoerberg] * Ensure the connection closes even on exception [carlhoerberg] * Make the authentication error message more useful [deric] * Fix "ConnectionError" typo in lib/net/ssh/proxy/socks5.rb [mirakui] * Allow KeyManager to recover from incompatible agents [ecki, delano] * Fix for "Authentication Method determination can pick up a class from the root namespace" [dave.sieh] === 2.7.0 / 11 Sep 2013 * Fix for 'Could not parse PKey: no start line' error on private keys with passphrases (issue #101) [metametaclass] * Automatically forward environment variables defined in OpenSSH config files [fnordfish] * Guard against socket.gets being nil in Net::SSH::Proxy::HTTP [krishicks] * Implemented experimental keepalive feature [noric] === 2.6.8 / 6 Jul 2013 * Added support for host wildcard substitution [GabKlein] * Added a wait to the loop in close to help fix possible blocks [Josh Kalderimis] * Fixed test file encoding issues with Ruby 2.0 (#87) [voxik] === 2.6.7 / 11 Apr 2013 * Decreased default packet size to 32768 as described in RFC 4253 [Olipro] * Added max_pkt_size and max_win_size options to Net::SSH.start [Olipro] === 2.6.6 / 03 Mar 2013 * Fix for ruby 2.0 in windows [jansegre] === 2.6.5 / 06 Feb 2013 * Fixed path in gemspec [thanks priteau] === 2.6.4 / 06 Feb 2013 * Added license info to gemspec [jordimassaguerpla] * Added public cert. All gem releases are now signed. === 2.6.3 / 10 Jan 2013 * Small doc fix and correct error class for PKey::EC key type [Andreas Wolff] * Improve test dependencies [Kenichi Kamiya] === 2.6.2 / 22 Nov 2012 * Net::SSH.start now returns result of block [mhuffnagle] * Add stderr handling to Net::SSH::Test [ohrite] * Fix Invalid key size in JRuby [ohrite] === 2.6.1 / 18 Oct 2012 * Remove platform specific jruby dependency from gemspec * Changed encoding of file to prevent warnings when generating docs [iltempo] === 2.6.0 / 19 Sep 2012 * Use OpenSSL::PKey.read to read arbitrary private key. [nagachika] * Check availability of UNIXSocket and UNIXServer for Windows [Nobuhiro IMAI] * Bump version to 2.5.3 and depend on newer jruby-pageant version for Java 1.5 compat. [arturaz] * Implementation of the "none"-authentication method [dubspeed] * Add class for stricter host key verification [Andy Brody] === 2.5.2 / 25 May 2012 * Fix for Net::SSH::KnownHosts::SUPPORTED_TYPE [Marco Sandrini] === 2.5.1 / 24 May 2012 * Added missing file to manifest [Marco Sandrini] === 2.5.0 / 24 May 2012 * Implement many algorithms [Ryosuke Yamazaki] * Key Exchange * diffie-hellman-group14-sha1 * ecdh-sha2-nistp{256,384,521} * Host Key * ecdsa-sha2-nistp{256,384,521} * Authentication * ecdsa-sha2-nistp{256,384,521} * HMAC * hmac-ripemd160 * Cipher: * aes{128,192,256}-ctr * camellia{128,192,256}-ctr * blowfish-ctr * cast128-ctr * 3des-ctr * arcfour (has problems with weak keys, and should be used with caution) * camellia{128,192,256}-cbc === 2.4.0 / 17 May 2012 * Support for JRuby + Pageant + Windows [arturaz] === 2.3.0 / 11 Jan 2012 * Support for hmac-sha2 and diffie-hellman-group-exchange-sha256 [Ryosuke Yamazaki] === 2.2.2 / 04 Jan 2012 * Fixed: Connection hangs on ServerVersion.new(socket, logger) [muffl0n] * Avoid dying when unsupported auth mechanisms are defined [pcn] === 2.2.1 / 24 Aug 2011 * Do not prompt any passphrases before trying all identities from agent. [musybite] (see: http://net-ssh.lighthouseapp.com/projects/36253-net-ssh/tickets/30) === 2.2.0 / 16 Aug 2011 * Add support for forward a local UNIX domain socket to a remote TCP socket. [Mark Imbriaco] === 2.1.4 / 3 Apr 2011 * Add ConnectionTimeout exception class. [Joel Watson] See: https://github.com/net-ssh/net-ssh-multi/pull/1 === 2.1.3 / 2 Mar 2011 * Call to transport.closed should be transport.close [Woon Jung] === 2.1.2 / 1 Mar 2011 * Fix for Net::SSH Continues to attempt authentication when notified it is not allowed [Eric Hodel] (see: http://net-ssh.lighthouseapp.com/projects/36253-net-ssh/tickets/26) * Fix for transport won't be closed if authentication fails [Patrick Marchi] === 2.1 / 19 Jan 2011 * Support "IdentitiesOnly" directive (LH-24) [Musy Bite, Edmund Haselwanter] * Speeding up the Loggable module (LH-23) [robbebob] === 2.0.24 / 14 Jan 2011 * Fix for process code to correctly wait until remote_id is set before sending any output, including eof. [Daniel Pittman, Markus Roberts] * Fix circular require warning in Ruby 1.9.2 [Gavin Brock] === 2.0.23 / 03 Jun 2010 * delay CHANNEL_EOF packet until output buffer is empty [Rich Lane] Previously, calling #eof! after #send_data would result in the CHANNEL_EOF packet being sent immediately, ahead of the data in the output buffer. Now buffer becomes empty. === 2.0.22 / 20 Apr 2010 * Fix for: "Parsing the config errors out because it coerces the "1" into an integer and then tries to split it on spaces for multiple host checking." (http://net-ssh.lighthouseapp.com/projects/36253/tickets/10) [Lee Marlow] === 2.0.21 / 20 Mar 2010 * Fix for "IdentifyFile" in ~/.ssh/config does not work if no "Host" statement is given (http://net-ssh.lighthouseapp.com/projects/36253/tickets/9-identifyfile-in-sshconfig-does-not-work-if-no-host-statement-is-given#ticket-9-5) [xbaldauf, Delano Mandelbaum] * Fix for client closes a forwarded connection, but the server is reading, net-ssh terminates with IOError socket closed (http://net-ssh.lighthouseapp.com/projects/36253/tickets/7) [Miklós Fazekas] * Fix for client force closes (RST) a forwarded connection, but server is reading, net-ssh terminates with exception [Miklós Fazekas] * Fix for server closes the sending side, the on_eof is not handled. [Miklós Fazekas] * Removed Hanna dependency in Rakefile [Delano Mandelbaum] === 2.0.20 / 10 Feb 2010 * Support "ProxyCommand none" directive [Andy Lo-A-Foe] === 2.0.19 / 16 Jan 2010 * Support plus sign in sshconfig hostname [Jason Weathered] === 2.0.18 / 15 Jan 2010 * Fix related to #recv(1) to #readpartial change in 2.0.16 [Hans de Graaff, Delano Mandelbaum] === 2.0.17 / 14 Dec 2009 * Don't load net/ssh/authentication/pageant on Windows with Ruby 1.9 [Travis Reeder, Delano Mandelbaum] === 2.0.16 / 28 Nov 2009 * Fix for "multiple hosts are separated by whitespace" [Akinori MUSHA] * Add support for the ProxyCommand directive [Akinori MUSHA] * Switched from #recv(1) to #readpartial in lib/net/ssh/transport/server_version.rb, so that closed sockets are recognized [Alex Peuchert] === 2.0.15 / 03 Sep 2009 * Scale back IO#select patch so it mutexes only zero-timeout calls [Daniel Azuma, Will Bryant] === 2.0.14 / 28 Aug 2009 * Fix for IO#select threading bug in Ruby 1.8 (LH-1) [Daniel Azuma] * Fix for "uninitialized constant OpenSSL::Digest::MD5" exception in Net::SFTP [DL Redden] === 2.0.13 / 17 Aug 2009 * Added fix for hanging in ServerVersion#negotiate! when using SOCKS5 proxy (GH-9) [Gerald Talton] * Added support for specifying a list of hosts in .ssh/config, with tests (GH-6) [ckoehler, Delano Mandelbaum] * Added tests for arcfour128/256/512 lengths, encryption, and decryption [Delano Mandelbaum] * Skip packet stream tests for arcfour128/256/512 [Delano Mandelbaum] * Fix for OpenSSL cipher key length because it always returns 16, even when 32 byte keys are required, e.g. for arcfour256 and arcfour512 ciphers [Karl Varga] === 2.0.12 / 08 Jun 2009 * Applied patch for arcfour128 and arcfour256 support [Denis Bernard] * Use unbuffered reads when negotiating the protocol version [Steven Hazel] === 2.0.11 / 24 Feb 2009 * Add :key_data option for specifying raw private keys in PEM format [Alex Holems, Andrew Babkin] === 2.0.10 / 4 Feb 2009 * Added Net::SSH.configuration_for to make it easier to query the SSH configuration file(s) [Jamis Buck] === 2.0.9 / 1 Feb 2009 * Specifying non-nil user argument overrides user in .ssh/config [Jamis Buck] * Ignore requests for non-existent channels (workaround ssh server bug) [Jamis Buck] * Add terminate! method for hard shutdown scenarios [Jamis Buck] * Revert to pre-2.0.7 key-loading behavior by default, but load private-key if public-key doesn't exist [Jamis Buck] * Make sure :passphrase option gets passed to key manager [Bob Cotton] === 2.0.8 / 29 December 2008 * Fix private key change from 2.0.7 so that keys are loaded just-in-time, avoiding unecessary prompts from encrypted keys. [Jamis Buck] === 2.0.7 / 29 December 2008 * Make key manager use private keys instead of requiring public key to exist [arilerner@mac.com] * Fix failing tests [arilerner@mac.com] * Don't include pageant when running under JRuby [Angel N. Sciortino] === 2.0.6 / 6 December 2008 * Update the Manifest file so that the gem includes all necessary files [Jamis Buck] === 2.0.5 / 6 December 2008 * Make the Pageant interface comply with more of the Socket interface to avoid related errors [Jamis Buck] * Don't busy-wait on session close for remaining channels to close [Will Bryant] * Ruby 1.9 compatibility [Jamis Buck] * Fix Cipher#final to correctly flag a need for a cipher reset [Jamis Buck] === 2.0.4 / 27 Aug 2008 * Added Connection::Session#closed? and Transport::Session#closed? [Jamis Buck] * Numeric host names in .ssh/config are now parsed correct [Yanko Ivanov] * Make sure the error raised when a public key file is malformed is more informative than a MethodMissing error [Jamis Buck] * Cipher#reset is now called after Cipher#final, with the last n bytes used as the next initialization vector [Jamis Buck] === 2.0.3 / 27 Jun 2008 * Make Net::SSH::Version comparable [Brian Candler] * Fix errors in port forwarding when a channel could not be opened due to a typo in the exception name [Matthew Todd] * Use #chomp instead of #strip when cleaning the version string reported by the remote host, so that trailing whitespace is preserved (this is to play nice with servers like Mocana SSH) [Timo Gatsonides] * Correctly parse ssh_config entries with eq-sign delimiters [Jamis Buck] * Ignore malformed ssh_config entries [Jamis Buck] === 2.0.2 / 29 May 2008 * Make sure the agent client understands both RSA "identities answers" [Jamis Buck] * Fixed key truncation bug that caused hmacs other than SHA1 to fail with "corrupt hmac" errors [Jamis Buck] * Fix detection and loading of public keys when the keys don't actually exist [David Dollar] === 2.0.1 / 5 May 2008 * Teach Net::SSH about a handful of default key names [Jamis Buck] === 2.0.0 / 1 May 2008 * Allow the :verbose argument to accept symbols (:debug, etc.) as well as Logger level constants (Logger::DEBUG, etc.) [Jamis Buck] === 2.0 Preview Release 4 (1.99.3) / 19 Apr 2008 * Make sure HOME is set to something sane, even on OS's that don't set it by default [Jamis Buck] * Add a :passphrase option to specify the passphrase to use with private keys [Francis Sullivan] * Open a new auth agent connection for every auth-agent channel request [Jamis Buck] === 2.0 Preview Release 3 (1.99.2) / 10 Apr 2008 * Session properties [Jamis Buck] * Make channel open failure work with a callback so that failures can be handled similarly to successes [Jamis Buck] === 2.0 Preview Release 2 (1.99.1) / 22 Mar 2008 * Partial support for ~/.ssh/config (and related) SSH configuration files [Daniel J. Berger, Jamis Buck] * Added Net::SSH::Test to facilitate testing complex SSH state machines [Jamis Buck] * Reworked Net::SSH::Prompt to use conditionally-selected modules [Jamis Buck, suggested by James Rosen] * Added Channel#eof? and Channel#eof! [Jamis Buck] * Fixed bug in strict host key verifier on cache miss [Mike Timm] === 2.0 Preview Release 1 (1.99.0) / 21 Aug 2007 * First preview release of Net::SSH v2 net-ssh-4.2.0/Gemfile000066400000000000000000000006161315376572000144050ustar00rootroot00000000000000source 'https://rubygems.org' # Specify your gem's dependencies in mygem.gemspec gemspec if !Gem.win_platform? && RUBY_ENGINE == "ruby" gem 'byebug', group: [:development, :test] end if (Gem::Version.new(RUBY_VERSION) <=> Gem::Version.new("2.2.6")) < 0 gem 'rbnacl', '< 4.0' end if ENV["CI"] gem 'codecov', require: false, group: :test gem 'simplecov', require: false, group: :test end net-ssh-4.2.0/Gemfile.norbnacl000066400000000000000000000004021315376572000161730ustar00rootroot00000000000000source 'https://rubygems.org' ENV['NET_SSH_NO_RBNACL'] = 'true' # Specify your gem's dependencies in mygem.gemspec gemspec if ENV["CI"] && !Gem.win_platform? gem 'simplecov', require: false, group: :test gem 'codecov', require: false, group: :test end net-ssh-4.2.0/Gemfile.norbnacl.lock000066400000000000000000000013331315376572000171260ustar00rootroot00000000000000PATH remote: . specs: net-ssh (4.0.0) GEM remote: https://rubygems.org/ specs: ast (2.3.0) metaclass (0.0.4) minitest (5.10.1) mocha (1.2.1) metaclass (~> 0.0.1) parser (2.3.3.1) ast (~> 2.2) powerpack (0.1.1) rainbow (2.1.0) rake (12.0.0) rubocop (0.46.0) parser (>= 2.3.1.1, < 3.0) powerpack (~> 0.1) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) ruby-progressbar (1.8.1) unicode-display_width (1.1.2) PLATFORMS ruby x86-mingw32 DEPENDENCIES bundler (~> 1.11) minitest (~> 5.10) mocha (>= 1.2.1) net-ssh! rake (~> 12.0) rubocop (~> 0.46.0) BUNDLED WITH 1.13.6 net-ssh-4.2.0/ISSUE_TEMPLATE.md000066400000000000000000000011711315376572000156140ustar00rootroot00000000000000### Expected behavior Tell us what should happen ### Actual behavior Tell us what happens instead. ### System configuration - net-ssh version - Ruby version ### Example App Please provide an example script that reproduces the problem. This will save maintainers time so they can spend it fixing your issues instead of trying to build a reproduction case from sparse instructions. You can use this as stating point: ```ruby gem 'net-ssh', '= 4.0.0.beta3' require 'net/ssh' puts Net::SSH::Version::CURRENT @host = 'localhost' @user = ENV['USER'] Net::SSH.start(@host, @user) do |ssh| puts ssh.exec!('echo "hello"') end ``` net-ssh-4.2.0/LICENSE.txt000066400000000000000000000020451315376572000147330ustar00rootroot00000000000000Copyright © 2008 Jamis Buck Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. net-ssh-4.2.0/Manifest000066400000000000000000000106311315376572000146010ustar00rootroot00000000000000CHANGELOG.rdoc Manifest README.rdoc Rakefile Rudyfile THANKS.rdoc lib/net/ssh.rb lib/net/ssh/authentication/agent.rb lib/net/ssh/authentication/constants.rb lib/net/ssh/authentication/key_manager.rb lib/net/ssh/authentication/methods/abstract.rb lib/net/ssh/authentication/methods/hostbased.rb lib/net/ssh/authentication/methods/keyboard_interactive.rb lib/net/ssh/authentication/methods/password.rb lib/net/ssh/authentication/methods/publickey.rb lib/net/ssh/authentication/pageant.rb lib/net/ssh/authentication/session.rb lib/net/ssh/buffer.rb lib/net/ssh/buffered_io.rb lib/net/ssh/config.rb lib/net/ssh/connection/channel.rb lib/net/ssh/connection/constants.rb lib/net/ssh/connection/session.rb lib/net/ssh/connection/term.rb lib/net/ssh/errors.rb lib/net/ssh/key_factory.rb lib/net/ssh/known_hosts.rb lib/net/ssh/loggable.rb lib/net/ssh/packet.rb lib/net/ssh/prompt.rb lib/net/ssh/proxy/command.rb lib/net/ssh/proxy/errors.rb lib/net/ssh/proxy/http.rb lib/net/ssh/proxy/socks4.rb lib/net/ssh/proxy/socks5.rb lib/net/ssh/ruby_compat.rb lib/net/ssh/service/forward.rb lib/net/ssh/test.rb lib/net/ssh/test/channel.rb lib/net/ssh/test/extensions.rb lib/net/ssh/test/kex.rb lib/net/ssh/test/local_packet.rb lib/net/ssh/test/packet.rb lib/net/ssh/test/remote_packet.rb lib/net/ssh/test/script.rb lib/net/ssh/test/socket.rb lib/net/ssh/transport/algorithms.rb lib/net/ssh/transport/cipher_factory.rb lib/net/ssh/transport/constants.rb lib/net/ssh/transport/ctr.rb lib/net/ssh/transport/hmac.rb lib/net/ssh/transport/hmac/abstract.rb lib/net/ssh/transport/hmac/md5.rb lib/net/ssh/transport/hmac/md5_96.rb lib/net/ssh/transport/hmac/none.rb lib/net/ssh/transport/hmac/ripemd160.rb lib/net/ssh/transport/hmac/sha1.rb lib/net/ssh/transport/hmac/sha1_96.rb lib/net/ssh/transport/hmac/sha2_256.rb lib/net/ssh/transport/hmac/sha2_256_96.rb lib/net/ssh/transport/hmac/sha2_512.rb lib/net/ssh/transport/hmac/sha2_512_96.rb lib/net/ssh/transport/identity_cipher.rb lib/net/ssh/transport/key_expander.rb lib/net/ssh/transport/kex.rb lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb lib/net/ssh/transport/openssl.rb lib/net/ssh/transport/packet_stream.rb lib/net/ssh/transport/server_version.rb lib/net/ssh/transport/session.rb lib/net/ssh/transport/state.rb lib/net/ssh/verifiers/lenient.rb lib/net/ssh/verifiers/null.rb lib/net/ssh/verifiers/secure.rb lib/net/ssh/verifiers/strict.rb lib/net/ssh/version.rb net-ssh.gemspec setup.rb support/arcfour_check.rb support/ssh_tunnel_bug.rb test/authentication/methods/common.rb test/authentication/methods/test_abstract.rb test/authentication/methods/test_hostbased.rb test/authentication/methods/test_keyboard_interactive.rb test/authentication/methods/test_password.rb test/authentication/methods/test_publickey.rb test/authentication/test_agent.rb test/authentication/test_key_manager.rb test/authentication/test_session.rb test/common.rb test/configs/eqsign test/configs/exact_match test/configs/host_plus test/configs/multihost test/configs/wild_cards test/connection/test_channel.rb test/connection/test_session.rb test/test_all.rb test/test_buffer.rb test/test_buffered_io.rb test/test_config.rb test/test_key_factory.rb test/transport/hmac/test_md5.rb test/transport/hmac/test_md5_96.rb test/transport/hmac/test_none.rb test/transport/hmac/test_ripemd160.rb test/transport/hmac/test_sha1.rb test/transport/hmac/test_sha1_96.rb test/transport/hmac/test_sha2_256.rb test/transport/hmac/test_sha2_256_96.rb test/transport/hmac/test_sha2_512.rb test/transport/hmac/test_sha2_512_96.rb test/transport/kex/test_diffie_hellman_group1_sha1.rb test/transport/kex/test_diffie_hellman_group14_sha1.rb test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb test/transport/kex/test_ecdh_sha2_nistp256.rb test/transport/kex/test_ecdh_sha2_nistp384.rb test/transport/kex/test_ecdh_sha2_nistp521.rb test/transport/test_algorithms.rb test/transport/test_cipher_factory.rb test/transport/test_hmac.rb test/transport/test_identity_cipher.rb test/transport/test_packet_stream.rb test/transport/test_server_version.rb test/transport/test_session.rb test/transport/test_state.rb net-ssh-4.2.0/README.rdoc000066400000000000000000000152431315376572000147220ustar00rootroot00000000000000{Gem Version}[https://badge.fury.io/rb/net-ssh] {Join the chat at https://gitter.im/net-ssh/net-ssh}[https://gitter.im/net-ssh/net-ssh?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge] {Build Status}[https://travis-ci.org/net-ssh/net-ssh] {Coverage status}[https://codecov.io/gh/net-ssh/net-ssh] = Net::SSH 4.x * Docs: http://net-ssh.github.com/net-ssh * Issues: https://github.com/net-ssh/net-ssh/issues * Codes: https://github.com/net-ssh/net-ssh * Email: net-ssh@solutious.com As of v2.6.4, all gem releases are signed. See INSTALL. == DESCRIPTION: Net::SSH is a pure-Ruby implementation of the SSH2 client protocol. It allows you to write programs that invoke and interact with processes on remote servers, via SSH2. == FEATURES: * Execute processes on remote servers and capture their output * Run multiple processes in parallel over a single SSH connection * Support for SSH subsystems * Forward local and remote ports via an SSH connection == SYNOPSIS: In a nutshell: require 'net/ssh' Net::SSH.start('host', 'user', :password => "password") do |ssh| # capture all stderr and stdout output from a remote process output = ssh.exec!("hostname") puts output # capture only stdout matching a particular pattern stdout = "" ssh.exec!("ls -l /home/jamis") do |channel, stream, data| stdout << data if stream == :stdout end puts stdout # run multiple processes in parallel to completion ssh.exec "sed ..." ssh.exec "awk ..." ssh.exec "rm -rf ..." ssh.loop # open a new channel and configure a minimal set of callbacks, then run # the event loop until the channel finishes (closes) channel = ssh.open_channel do |ch| ch.exec "/usr/local/bin/ruby /path/to/file.rb" do |ch, success| raise "could not execute command" unless success # "on_data" is called when the process writes something to stdout ch.on_data do |c, data| $stdout.print data end # "on_extended_data" is called when the process writes something to stderr ch.on_extended_data do |c, type, data| $stderr.print data end ch.on_close { puts "done!" } end end channel.wait # forward connections on local port 1234 to port 80 of www.capify.org ssh.forward.local(1234, "www.capify.org", 80) ssh.loop { true } end See Net::SSH for more documentation, and links to further information. == REQUIREMENTS: The only requirement you might be missing is the OpenSSL bindings for Ruby. These are built by default on most platforms, but you can verify that they're built and installed on your system by running the following command line: ruby -ropenssl -e 'puts OpenSSL::OPENSSL_VERSION' If that spits out something like "OpenSSL 0.9.8g 19 Oct 2007", then you're set. If you get an error, then you'll need to see about rebuilding ruby with OpenSSL support, or (if your platform supports it) installing the OpenSSL bindings separately. Lastly, if you want to run the tests or use any of the Rake tasks, you'll need Mocha and other dependencies listed in Gemfile == INSTALL: * gem install net-ssh (might need sudo privileges) NOTE: If you are running on jruby on windows you need to install jruby-pageant manually (gemspec doesn't allow for platform specific dependencies). However, in order to be sure the code you're installing hasn't been tampered with, it's recommended that you verify the signature[http://docs.rubygems.org/read/chapter/21]. To do this, you need to add my public key as a trusted certificate (you only need to do this once): # Add the public key as a trusted certificate # (You only need to do this once) $ curl -O https://raw.githubusercontent.com/net-ssh/net-ssh/master/net-ssh-public_cert.pem $ gem cert --add net-ssh-public_cert.pem Then, when install the gem, do so with high security: $ gem install net-ssh -P HighSecurity If you don't add the public key, you'll see an error like "Couldn't verify data signature". If you're still having trouble let me know and I'll give you a hand. For ed25519 public key auth support your bundle file should contain ```rbnacl-libsodium```, ```rbnacl```, ```bcrypt_pbkdf``` dependencies. == RUBY SUPPORT * Ruby 1.8.x is supported up until the net-ssh 2.5.1 release. * Ruby 1.9.x is supported up until the net-ssh 2.9.x release. * Current net-ssh releases require Ruby 2.0 or later. == RUNNING TESTS Run the test suite from the net-ssh directory with the following command: bundle exec rake test Run a single test file like this: ruby -Ilib -Itest test/transport/test_server_version.rb To run integration tests see test/integration/README.txt === BUILDING GEM rake build === GEM SIGNING (for maintainers) If you have the net-ssh private signing key, you will be able to create signed release builds. Make sure the private key path matches the `signing_key` path set in `net-ssh.gemspec` and tell rake to sign the gem by setting the `NET_SSH_BUILDGEM_SIGNED` flag: NET_SSH_BUILDGEM_SIGNED=true rake build For time to time, the public certificate associated to the private key needs to be renewed. You can do this with the following command: gem cert --build netssh@solutious.com --private-key path/2/net-ssh-private_key.pem mv gem-public_cert.pem net-ssh-public_cert.pem gem cert --add net-ssh-public_cert.pem == LICENSE: (The MIT License) Copyright (c) 2008 Jamis Buck Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. net-ssh-4.2.0/Rakefile000066400000000000000000000053561315376572000145650ustar00rootroot00000000000000# coding: UTF-8 # # Also in your terminal environment run: # $ export LANG=en_US.UTF-8 # $ export LANGUAGE=en_US.UTF-8 # $ export LC_ALL=en_US.UTF-8 require "rubygems" require "rake" require "rake/clean" require "bundler/gem_tasks" require "rdoc/task" desc "When releasing make sure NET_SSH_BUILDGEM_SIGNED is set" task :check_NET_SSH_BUILDGEM_SIGNED do raise "NET_SSH_BUILDGEM_SIGNED should be set to release" unless ENV['NET_SSH_BUILDGEM_SIGNED'] end Rake::Task[:release].enhance [:check_NET_SSH_BUILDGEM_SIGNED] Rake::Task[:release].prerequisites.unshift(:check_NET_SSH_BUILDGEM_SIGNED) task default: ["build"] CLEAN.include [ 'pkg', 'rdoc' ] name = "net-ssh" require_relative "lib/net/ssh/version" version = Net::SSH::Version::CURRENT extra_files = %w[LICENSE.txt THANKS.txt CHANGES.txt] RDoc::Task.new do |rdoc| rdoc.rdoc_dir = "rdoc" rdoc.title = "#{name} #{version}" rdoc.generator = 'hanna' # gem install hanna-nouveau rdoc.main = 'README.rdoc' rdoc.rdoc_files.include("README*") rdoc.rdoc_files.include("bin/*.rb") rdoc.rdoc_files.include("lib/**/*.rb") extra_files.each { |file| rdoc.rdoc_files.include(file) if File.exists?(file) } end namespace :rdoc do desc "Update gh-pages branch" task :publish do # copy/checkout rm_rf "/tmp/net-ssh-rdoc" rm_rf "/tmp/net-ssh-gh-pages" cp_r "./rdoc", "/tmp/net-ssh-rdoc" mkdir "/tmp/net-ssh-gh-pages" Dir.chdir "/tmp/net-ssh-gh-pages" do sh "git clone --branch gh-pages --single-branch https://github.com/net-ssh/net-ssh" rm_rf "/tmp/net-ssh-gh-pages/net-ssh/*" end # update sh "cp -rf ./rdoc/* /tmp/net-ssh-gh-pages/net-ssh/" Dir.chdir "/tmp/net-ssh-gh-pages/net-ssh" do sh "git add -A ." sh "git commit -m \"Update docs\"" end # publish Dir.chdir "/tmp/net-ssh-gh-pages/net-ssh" do sh "git push origin gh-pages" end end end require 'rake/testtask' Rake::TestTask.new do |t| t.libs = ["lib", "test"] t.libs << "test/integration" if ENV['NET_SSH_RUN_INTEGRATION_TESTS'] t.libs << "test/win_integration" if ENV['NET_SSH_RUN_WIN_INTEGRATION_TESTS'] test_files = FileList['test/**/test_*.rb'] test_files -= FileList['test/integration/**/test_*.rb'] unless ENV['NET_SSH_RUN_INTEGRATION_TESTS'] test_files -= FileList['test/win_integration/**/test_*.rb'] unless ENV['NET_SSH_RUN_WIN_INTEGRATION_TESTS'] test_files -= FileList['test/manual/test_*.rb'] test_files -= FileList['test/test_pageant.rb'] test_files -= FileList['test/test/**/test_*.rb'] t.test_files = test_files end desc "Run tests of Net::SSH:Test" Rake::TestTask.new do |t| t.name = "test_test" # we need to run test/test separatedly as it hacks io + other modules t.libs = ["lib", "test"] test_files = FileList['test/test/**/test_*.rb'] t.test_files = test_files end net-ssh-4.2.0/THANKS.txt000066400000000000000000000034361315376572000146460ustar00rootroot00000000000000Net::SSH was originally written by Jamis Buck . It is currently maintained by Delano Mandelbaum . In addition, the following individuals are gratefully acknowledged for their contributions: GOTOU Yuuzou * help and code related to OpenSSL Guillaume Marçais * support for communicating with the the PuTTY "pageant" process Daniel Berger * help getting unit tests in earlier Net::SSH versions to pass in Windows * initial version of Net::SSH::Config provided inspiration and encouragement Chris Andrews and Lee Jensen * support for ssh agent forwarding Hiroshi Nakamura * fixed errors with JRuby tests bobveznat therealjessesanford liggitt jarredholman yugui SFEley bobtfish carlhoerberg deric mirakui ecki Dave Sieh metametaclass fnordfish krishicks noric GabKlein Josh Kalderimis voxik Olipro jansegre priteau jordimassaguerpla Kenichi Kamiya Andreas Wolff mhuffnagle ohrite iltempo nagachika Nobuhiro IMAI arturaz dubspeed Andy Brody Marco Sandrini Ryosuke Yamazaki muffl0n pcn musybite Mark Imbriaco Joel Watson Woon Jung Edmund Haselwanter robbebob Daniel Pittman Markus Roberts Gavin Brock Rich Lane Lee Marlow xbaldauf Delano Mandelbaum Miklós Fazekas Andy Lo-A-Foe Jason Weathered Hans de Graaff Travis Reeder Akinori MUSHA Alex Peuchert Daniel Azuma Will Bryant Gerald Talton ckoehler Karl Varga Denis Bernard Steven Hazel Alex Holems Andrew Babkin Bob Cotton Yanko Ivanov Angel N. Sciortino arilerner@mac.com David Dollar Timo Gatsonides Matthew Todd Brian Candler Francis Sullivan James Rosen Mike Timm guns devrandom kachick Pablo Merino thedarkone czarneckid jbarnette watsonian Grant Hutchins Michael Schubert mtrudel Aurélien Derouineau net-ssh-4.2.0/appveyor.yml000066400000000000000000000035141315376572000155020ustar00rootroot00000000000000version: '{build}' skip_tags: true environment: matrix: - ruby_version: "jruby-9.1.2.0" - ruby_version: "23" - ruby_version: "23-x64" - ruby_version: "22-x64" matrix: allow_failures: - ruby_version: "jruby-9.1.2.0" #init: # - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) #on_finish: # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) platform: - x86 install: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - if "%ruby_version%" == "jruby-9.1.2.0" ( cinst javaruntime -i ) - if "%ruby_version%" == "jruby-9.1.2.0" ( cinst jruby --version 9.1.2.0 -i --allow-empty-checksums ) - if "%ruby_version%" == "jruby-9.1.2.0" ( SET "PATH=C:\jruby-9.1.2.0\bin\;%PATH%" ) - ruby --version - gem install bundler --no-document -v 1.13.5 - SET BUNDLE_GEMFILE=Gemfile.norbnacl - bundle _1.13.5_ install --retry=3 - cinst freesshd - cinst putty --allow-empty-checksums - ps: | if ($env:Processor_Architecture -eq "x86") { dir 'C:\Program Files\' dir 'C:\Program Files\freeSSHd' cp 'test\win_integration\FreeSSHDService.ini' 'C:\Program Files\freeSSHd\FreeSSHDService.ini' & 'C:\Program Files\freeSSHd\FreeSSHDService.exe' } else { dir 'C:\Program Files (x86)\' dir 'C:\Program Files (x86)\freeSSHd' cp 'test\win_integration\FreeSSHDService32.ini' 'C:\Program Files (x86)\freeSSHd\FreeSSHDService.ini' & 'C:\Program Files (x86)\freeSSHd\FreeSSHDService.exe' } test_script: - SET BUNDLE_GEMFILE=Gemfile.norbnacl - SET NET_SSH_RUN_WIN_INTEGRATION_TESTS=YES - bundle _1.13.5_ exec rake test build: off net-ssh-4.2.0/lib/000077500000000000000000000000001315376572000136555ustar00rootroot00000000000000net-ssh-4.2.0/lib/net/000077500000000000000000000000001315376572000144435ustar00rootroot00000000000000net-ssh-4.2.0/lib/net/ssh.rb000066400000000000000000000354501315376572000155740ustar00rootroot00000000000000# Make sure HOME is set, regardless of OS, so that File.expand_path works # as expected with tilde characters. ENV['HOME'] ||= ENV['HOMEPATH'] ? "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}" : Dir.pwd require 'logger' require 'etc' require 'net/ssh/config' require 'net/ssh/errors' require 'net/ssh/loggable' require 'net/ssh/transport/session' require 'net/ssh/authentication/session' require 'net/ssh/connection/session' require 'net/ssh/prompt' module Net # Net::SSH is a library for interacting, programmatically, with remote # processes via the SSH2 protocol. Sessions are always initiated via # Net::SSH.start. From there, a program interacts with the new SSH session # via the convenience methods on Net::SSH::Connection::Session, by opening # and interacting with new channels (Net::SSH::Connection:Session#open_channel # and Net::SSH::Connection::Channel), or by forwarding local and/or # remote ports through the connection (Net::SSH::Service::Forward). # # The SSH protocol is very event-oriented. Requests are sent from the client # to the server, and are answered asynchronously. This gives great flexibility # (since clients can have multiple requests pending at a time), but it also # adds complexity. Net::SSH tries to manage this complexity by providing # some simpler methods of synchronous communication (see Net::SSH::Connection::Session#exec!). # # In general, though, and if you want to do anything more complicated than # simply executing commands and capturing their output, you'll need to use # channels (Net::SSH::Connection::Channel) to build state machines that are # executed while the event loop runs (Net::SSH::Connection::Session#loop). # # Net::SSH::Connection::Session and Net::SSH::Connection::Channel have more # information about this technique. # # = "Um, all I want to do is X, just show me how!" # # == X == "execute a command and capture the output" # # Net::SSH.start("host", "user", password: "password") do |ssh| # result = ssh.exec!("ls -l") # puts result # end # # == X == "forward connections on a local port to a remote host" # # Net::SSH.start("host", "user", password: "password") do |ssh| # ssh.forward.local(1234, "www.google.com", 80) # ssh.loop { true } # end # # == X == "forward connections on a remote port to the local host" # # Net::SSH.start("host", "user", password: "password") do |ssh| # ssh.forward.remote(80, "www.google.com", 1234) # ssh.loop { true } # end module SSH # This is the set of options that Net::SSH.start recognizes. See # Net::SSH.start for a description of each option. VALID_OPTIONS = [ :auth_methods, :bind_address, :compression, :compression_level, :config, :encryption, :forward_agent, :hmac, :host_key, :remote_user, :keepalive, :keepalive_interval, :keepalive_maxcount, :kex, :keys, :key_data, :languages, :logger, :paranoid, :password, :port, :proxy, :rekey_blocks_limit,:rekey_limit, :rekey_packet_limit, :timeout, :verbose, :known_hosts, :global_known_hosts_file, :user_known_hosts_file, :host_key_alias, :host_name, :user, :properties, :passphrase, :keys_only, :max_pkt_size, :max_win_size, :send_env, :use_agent, :number_of_password_prompts, :append_all_supported_algorithms, :non_interactive, :password_prompt, :agent_socket_factory, :minimum_dh_bits, :verify_host_key ] # The standard means of starting a new SSH connection. When used with a # block, the connection will be closed when the block terminates, otherwise # the connection will just be returned. The yielded (or returned) value # will be an instance of Net::SSH::Connection::Session (q.v.). (See also # Net::SSH::Connection::Channel and Net::SSH::Service::Forward.) # # Net::SSH.start("host", "user") do |ssh| # ssh.exec! "cp /some/file /another/location" # hostname = ssh.exec!("hostname") # # ssh.open_channel do |ch| # ch.exec "sudo -p 'sudo password: ' ls" do |ch, success| # abort "could not execute sudo ls" unless success # # ch.on_data do |ch, data| # print data # if data =~ /sudo password: / # ch.send_data("password\n") # end # end # end # end # # ssh.loop # end # # This method accepts the following options (all are optional): # # * :auth_methods => an array of authentication methods to try # * :bind_address => the IP address on the connecting machine to use in # establishing connection. (:bind_address is discarded if :proxy # is set.) # * :compression => the compression algorithm to use, or +true+ to use # whatever is supported. # * :compression_level => the compression level to use when sending data # * :config => set to +true+ to load the default OpenSSH config files # (~/.ssh/config, /etc/ssh_config), or to +false+ to not load them, or to # a file-name (or array of file-names) to load those specific configuration # files. Defaults to +true+. # * :encryption => the encryption cipher (or ciphers) to use # * :forward_agent => set to true if you want the SSH agent connection to # be forwarded # * :known_hosts => a custom object holding known hosts records. # It must implement #search_for and add in a similiar manner as KnownHosts. # * :global_known_hosts_file => the location of the global known hosts # file. Set to an array if you want to specify multiple global known # hosts files. Defaults to %w(/etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2). # * :hmac => the hmac algorithm (or algorithms) to use # * :host_key => the host key algorithm (or algorithms) to use # * :host_key_alias => the host name to use when looking up or adding a # host to a known_hosts dictionary file # * :host_name => the real host name or IP to log into. This is used # instead of the +host+ parameter, and is primarily only useful when # specified in an SSH configuration file. It lets you specify an # "alias", similarly to adding an entry in /etc/hosts but without needing # to modify /etc/hosts. # * :keepalive => set to +true+ to send a keepalive packet to the SSH server # when there's no traffic between the SSH server and Net::SSH client for # the keepalive_interval seconds. Defaults to +false+. # * :keepalive_interval => the interval seconds for keepalive. # Defaults to +300+ seconds. # * :keepalive_maxcount => the maximun number of keepalive packet miss allowed. # Defaults to 3 # * :kex => the key exchange algorithm (or algorithms) to use # * :keys => an array of file names of private keys to use for publickey # and hostbased authentication # * :key_data => an array of strings, with each element of the array being # a raw private key in PEM format. # * :keys_only => set to +true+ to use only private keys from +keys+ and # +key_data+ parameters, even if ssh-agent offers more identities. This # option is intended for situations where ssh-agent offers many different # identites. # * :logger => the logger instance to use when logging # * :max_pkt_size => maximum size we tell the other side that is supported per # packet. Default is 0x8000 (32768 bytes). Increase to 0x10000 (65536 bytes) # for better performance if your SSH server supports it (most do). # * :max_win_size => maximum size we tell the other side that is supported for # the window. # * :non_interactive => set to true if your app is non interactive and prefers # authentication failure vs password prompt. Non-interactive applications # should set it to true to prefer failing a password/etc auth methods vs. # asking for password. # * :paranoid => deprecated alias for :verify_host_key # * :passphrase => the passphrase to use when loading a private key (default # is +nil+, for no passphrase) # * :password => the password to use to login # * :port => the port to use when connecting to the remote host # * :properties => a hash of key/value pairs to add to the new connection's # properties (see Net::SSH::Connection::Session#properties) # * :proxy => a proxy instance (see Proxy) to use when connecting # * :rekey_blocks_limit => the max number of blocks to process before rekeying # * :rekey_limit => the max number of bytes to process before rekeying # * :rekey_packet_limit => the max number of packets to process before rekeying # * :send_env => an array of local environment variable names to export to the # remote environment. Names may be given as String or Regexp. # * :timeout => how long to wait for the initial connection to be made # * :user => the user name to log in as; this overrides the +user+ # parameter, and is primarily only useful when provided via an SSH # configuration file. # * :remote_user => used for substitution into the '%r' part of a ProxyCommand # * :user_known_hosts_file => the location of the user known hosts file. # Set to an array to specify multiple user known hosts files. # Defaults to %w(~/.ssh/known_hosts ~/.ssh/known_hosts2). # * :use_agent => Set false to disable the use of ssh-agent. Defaults to # true # * :verbose => how verbose to be (Logger verbosity constants, Logger::DEBUG # is very verbose, Logger::FATAL is all but silent). Logger::FATAL is the # default. The symbols :debug, :info, :warn, :error, and :fatal are also # supported and are translated to the corresponding Logger constant. # * :append_all_supported_algorithms => set to +true+ to append all supported # algorithms by net-ssh. Was the default behaviour until 2.10 # * :number_of_password_prompts => Number of prompts for the password # authentication method defaults to 3 set to 0 to disable prompt for # password auth method # * :password_prompt => a custom prompt object with ask method. See Net::SSH::Prompt # # * :agent_socket_factory => enables the user to pass a lambda/block that will serve as the socket factory # Net::SSH::start(user,host,agent_socket_factory: ->{ UNIXSocket.open('/foo/bar') }) # example: ->{ UNIXSocket.open('/foo/bar')} # * :verify_host_key => either false, true, :very, or :secure specifying how # strict host-key verification should be (in increasing order here). # You can also provide an own Object which responds to +verify+. The argument # given to +verify+ is a hash consisting of the +:key+, the +:key_blob+, # the +:fingerprint+ and the +:session+. Returning true accepts the host key, # returning false declines it and closes the connection. # # If +user+ parameter is nil it defaults to USER from ssh_config, or # local username def self.start(host, user=nil, options={}, &block) invalid_options = options.keys - VALID_OPTIONS if invalid_options.any? raise ArgumentError, "invalid option(s): #{invalid_options.join(', ')}" end assign_defaults(options) _sanitize_options(options) options[:user] = user if user options = configuration_for(host, options.fetch(:config, true)).merge(options) host = options.fetch(:host_name, host) if options[:non_interactive] options[:number_of_password_prompts] = 0 end _support_deprecated_option_paranoid(options) if options[:verbose] options[:logger].level = case options[:verbose] when Integer then options[:verbose] when :debug then Logger::DEBUG when :info then Logger::INFO when :warn then Logger::WARN when :error then Logger::ERROR when :fatal then Logger::FATAL else raise ArgumentError, "can't convert #{options[:verbose].inspect} to any of the Logger level constants" end end transport = Transport::Session.new(host, options) auth = Authentication::Session.new(transport, options) user = options.fetch(:user, user) || Etc.getlogin if auth.authenticate("ssh-connection", user, options[:password]) connection = Connection::Session.new(transport, options) if block_given? begin yield connection ensure connection.close unless connection.closed? end else return connection end else transport.close raise AuthenticationFailed, "Authentication failed for user #{user}@#{host}" end end # Returns a hash of the configuration options for the given host, as read # from the SSH configuration file(s). If +use_ssh_config+ is true (the # default), this will load configuration from both ~/.ssh/config and # /etc/ssh_config. If +use_ssh_config+ is nil or false, nothing will be # loaded (and an empty hash returned). Otherwise, +use_ssh_config+ may # be a file name (or array of file names) of SSH configuration file(s) # to read. # # See Net::SSH::Config for the full description of all supported options. def self.configuration_for(host, use_ssh_config) files = case use_ssh_config when true then Net::SSH::Config.expandable_default_files when false, nil then return {} else Array(use_ssh_config) end Net::SSH::Config.for(host, files) end def self.assign_defaults(options) if !options[:logger] options[:logger] = Logger.new(STDERR) options[:logger].level = Logger::FATAL end options[:password_prompt] ||= Prompt.default(options) [:password, :passphrase].each do |key| options.delete(key) if options.key?(key) && options[key].nil? end end def self._sanitize_options(options) invalid_option_values = [nil,[nil]] unless (options.values & invalid_option_values).empty? nil_options = options.select { |_k,v| invalid_option_values.include?(v) }.map(&:first) Kernel.warn "#{caller_locations(2, 1)[0]}: Passing nil, or [nil] to Net::SSH.start is deprecated for keys: #{nil_options.join(', ')}" end end private_class_method :_sanitize_options def self._support_deprecated_option_paranoid(options) if options.key?(:paranoid) Kernel.warn( ":paranoid is deprecated, please use :verify_host_key. Supported " \ "values are exactly the same, only the name of the option has changed." ) if options.key?(:verify_host_key) Kernel.warn( "Both :paranoid and :verify_host_key were specified. " \ ":verify_host_key takes precedence, :paranoid will be ignored." ) else options[:verify_host_key] = options.delete(:paranoid) end end end private_class_method :_support_deprecated_option_paranoid end end net-ssh-4.2.0/lib/net/ssh/000077500000000000000000000000001315376572000152405ustar00rootroot00000000000000net-ssh-4.2.0/lib/net/ssh/authentication/000077500000000000000000000000001315376572000202575ustar00rootroot00000000000000net-ssh-4.2.0/lib/net/ssh/authentication/agent.rb000066400000000000000000000245031315376572000217060ustar00rootroot00000000000000require 'net/ssh/buffer' require 'net/ssh/errors' require 'net/ssh/loggable' require 'net/ssh/transport/server_version' require 'socket' require 'rubygems' require 'net/ssh/authentication/pageant' if Gem.win_platform? && RUBY_PLATFORM != "java" module Net; module SSH; module Authentication # Class for representing agent-specific errors. class AgentError < Net::SSH::Exception; end # An exception for indicating that the SSH agent is not available. class AgentNotAvailable < AgentError; end # This class implements a simple client for the ssh-agent protocol. It # does not implement any specific protocol, but instead copies the # behavior of the ssh-agent functions in the OpenSSH library (3.8). # # This means that although it behaves like a SSH1 client, it also has # some SSH2 functionality (like signing data). class Agent include Loggable # A simple module for extending keys, to allow comments to be specified # for them. module Comment attr_accessor :comment end SSH2_AGENT_REQUEST_VERSION = 1 SSH2_AGENT_REQUEST_IDENTITIES = 11 SSH2_AGENT_IDENTITIES_ANSWER = 12 SSH2_AGENT_SIGN_REQUEST = 13 SSH2_AGENT_SIGN_RESPONSE = 14 SSH2_AGENT_ADD_IDENTITY = 17 SSH2_AGENT_REMOVE_IDENTITY = 18 SSH2_AGENT_REMOVE_ALL_IDENTITIES = 19 SSH2_AGENT_ADD_ID_CONSTRAINED = 25 SSH2_AGENT_FAILURE = 30 SSH2_AGENT_VERSION_RESPONSE = 103 SSH_COM_AGENT2_FAILURE = 102 SSH_AGENT_REQUEST_RSA_IDENTITIES = 1 SSH_AGENT_RSA_IDENTITIES_ANSWER1 = 2 SSH_AGENT_RSA_IDENTITIES_ANSWER2 = 5 SSH_AGENT_FAILURE = 5 SSH_AGENT_SUCCESS = 6 SSH_AGENT_CONSTRAIN_LIFETIME = 1 SSH_AGENT_CONSTRAIN_CONFIRM = 2 SSH_AGENT_RSA_SHA2_256 = 0x02 SSH_AGENT_RSA_SHA2_512 = 0x04 # The underlying socket being used to communicate with the SSH agent. attr_reader :socket # Instantiates a new agent object, connects to a running SSH agent, # negotiates the agent protocol version, and returns the agent object. def self.connect(logger=nil, agent_socket_factory = nil) agent = new(logger) agent.connect!(agent_socket_factory) agent.negotiate! agent end # Creates a new Agent object, using the optional logger instance to # report status. def initialize(logger=nil) self.logger = logger end # Connect to the agent process using the socket factory and socket name # given by the attribute writers. If the agent on the other end of the # socket reports that it is an SSH2-compatible agent, this will fail # (it only supports the ssh-agent distributed by OpenSSH). def connect!(agent_socket_factory = nil) debug { "connecting to ssh-agent" } @socket = if agent_socket_factory agent_socket_factory.call elsif ENV['SSH_AUTH_SOCK'] && unix_socket_class unix_socket_class.open(ENV['SSH_AUTH_SOCK']) elsif Gem.win_platform? && RUBY_ENGINE != "jruby" Pageant::Socket.open else raise AgentNotAvailable, "Agent not configured" end rescue StandardError => e error { "could not connect to ssh-agent: #{e.message}" } raise AgentNotAvailable, $!.message end # Attempts to negotiate the SSH agent protocol version. Raises an error # if the version could not be negotiated successfully. def negotiate! # determine what type of agent we're communicating with type, body = send_and_wait(SSH2_AGENT_REQUEST_VERSION, :string, Transport::ServerVersion::PROTO_VERSION) raise AgentNotAvailable, "SSH2 agents are not yet supported" if type == SSH2_AGENT_VERSION_RESPONSE if type == SSH2_AGENT_FAILURE debug { "Unexpected response type==#{type}, this will be ignored" } elsif type != SSH_AGENT_RSA_IDENTITIES_ANSWER1 && type != SSH_AGENT_RSA_IDENTITIES_ANSWER2 raise AgentNotAvailable, "unknown response from agent: #{type}, #{body.to_s.inspect}" end end # Return an array of all identities (public keys) known to the agent. # Each key returned is augmented with a +comment+ property which is set # to the comment returned by the agent for that key. def identities type, body = send_and_wait(SSH2_AGENT_REQUEST_IDENTITIES) raise AgentError, "could not get identity count" if agent_failed(type) raise AgentError, "bad authentication reply: #{type}" if type != SSH2_AGENT_IDENTITIES_ANSWER identities = [] body.read_long.times do key_str = body.read_string comment_str = body.read_string begin key = Buffer.new(key_str).read_key key.extend(Comment) key.comment = comment_str identities.push key rescue NotImplementedError => e error { "ignoring unimplemented key:#{e.message} #{comment_str}" } end end return identities end # Closes this socket. This agent reference is no longer able to # query the agent. def close @socket.close end # Using the agent and the given public key, sign the given data. The # signature is returned in SSH2 format. def sign(key, data, flags = 0) type, reply = send_and_wait(SSH2_AGENT_SIGN_REQUEST, :string, Buffer.from(:key, key), :string, data, :long, flags) raise AgentError, "agent could not sign data with requested identity" if agent_failed(type) raise AgentError, "bad authentication response #{type}" if type != SSH2_AGENT_SIGN_RESPONSE return reply.read_string end # Adds the private key with comment to the agent. # If lifetime is given, the key will automatically be removed after lifetime # seconds. # If confirm is true, confirmation will be required for each agent signing # operation. def add_identity(priv_key, comment, lifetime: nil, confirm: false) constraints = Buffer.new if lifetime constraints.write_byte(SSH_AGENT_CONSTRAIN_LIFETIME) constraints.write_long(lifetime) end constraints.write_byte(SSH_AGENT_CONSTRAIN_CONFIRM) if confirm req_type = constraints.empty? ? SSH2_AGENT_ADD_IDENTITY : SSH2_AGENT_ADD_ID_CONSTRAINED type, = send_and_wait(req_type, :string, priv_key.ssh_type, :raw, blob_for_add(priv_key), :string, comment, :raw, constraints) raise AgentError, "could not add identity to agent" if type != SSH_AGENT_SUCCESS end # Removes key from the agent. def remove_identity(key) type, = send_and_wait(SSH2_AGENT_REMOVE_IDENTITY, :string, key.to_blob) raise AgentError, "could not remove identity from agent" if type != SSH_AGENT_SUCCESS end # Removes all identities from the agent. def remove_all_identities type, = send_and_wait(SSH2_AGENT_REMOVE_ALL_IDENTITIES) raise AgentError, "could not remove all identity from agent" if type != SSH_AGENT_SUCCESS end private def unix_socket_class defined?(UNIXSocket) && UNIXSocket end # Send a new packet of the given type, with the associated data. def send_packet(type, *args) buffer = Buffer.from(*args) data = [buffer.length + 1, type.to_i, buffer.to_s].pack("NCA*") debug { "sending agent request #{type} len #{buffer.length}" } @socket.send data, 0 end # Read the next packet from the agent. This will return a two-part # tuple consisting of the packet type, and the packet's body (which # is returned as a Net::SSH::Buffer). def read_packet buffer = Net::SSH::Buffer.new(@socket.read(4)) buffer.append(@socket.read(buffer.read_long)) type = buffer.read_byte debug { "received agent packet #{type} len #{buffer.length-4}" } return type, buffer end # Send the given packet and return the subsequent reply from the agent. # (See #send_packet and #read_packet). def send_and_wait(type, *args) send_packet(type, *args) read_packet end # Returns +true+ if the parameter indicates a "failure" response from # the agent, and +false+ otherwise. def agent_failed(type) type == SSH_AGENT_FAILURE || type == SSH2_AGENT_FAILURE || type == SSH_COM_AGENT2_FAILURE end def blob_for_add(priv_key) # Ideally we'd have something like `to_private_blob` on the various key types, but the # nuances with encoding (e.g. `n` and `e` are reversed for RSA keys) make this impractical. case priv_key.ssh_type when /^ssh-dss$/ Net::SSH::Buffer.from(:bignum, priv_key.p, :bignum, priv_key.q, :bignum, priv_key.g, :bignum, priv_key.pub_key, :bignum, priv_key.priv_key).to_s when /^ssh-dss-cert-v01@openssh\.com$/ Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.priv_key).to_s when /^ecdsa\-sha2\-(\w*)$/ curve_name = OpenSSL::PKey::EC::CurveNameAliasInv[priv_key.group.curve_name] Net::SSH::Buffer.from(:string, curve_name, :mstring, priv_key.public_key.to_bn.to_s(2), :bignum, priv_key.private_key).to_s when /^ecdsa\-sha2\-(\w*)-cert-v01@openssh\.com$/ Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.private_key).to_s when /^ssh-ed25519$/ Net::SSH::Buffer.from(:string, priv_key.public_key.verify_key.to_bytes, :string, priv_key.sign_key.keypair_bytes).to_s when /^ssh-ed25519-cert-v01@openssh\.com$/ # Unlike the other certificate types, the public key is included after the certifiate. Net::SSH::Buffer.from(:string, priv_key.to_blob, :string, priv_key.key.public_key.verify_key.to_bytes, :string, priv_key.key.sign_key.keypair_bytes).to_s when /^ssh-rsa$/ # `n` and `e` are reversed compared to the ordering in `OpenSSL::PKey::RSA#to_blob`. Net::SSH::Buffer.from(:bignum, priv_key.n, :bignum, priv_key.e, :bignum, priv_key.d, :bignum, priv_key.iqmp, :bignum, priv_key.p, :bignum, priv_key.q).to_s when /^ssh-rsa-cert-v01@openssh\.com$/ Net::SSH::Buffer.from(:string, priv_key.to_blob, :bignum, priv_key.key.d, :bignum, priv_key.key.iqmp, :bignum, priv_key.key.p, :bignum, priv_key.key.q).to_s end end end end; end; end net-ssh-4.2.0/lib/net/ssh/authentication/certificate.rb000066400000000000000000000113031315376572000230640ustar00rootroot00000000000000require 'securerandom' module Net; module SSH; module Authentication # Class for representing an SSH certificate. # # http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/usr.bin/ssh/PROTOCOL.certkeys?rev=1.10&content-type=text/plain class Certificate attr_accessor :nonce attr_accessor :key attr_accessor :serial attr_accessor :type attr_accessor :key_id attr_accessor :valid_principals attr_accessor :valid_after attr_accessor :valid_before attr_accessor :critical_options attr_accessor :extensions attr_accessor :reserved attr_accessor :signature_key attr_accessor :signature # Read a certificate blob associated with a key of the given type. def self.read_certblob(buffer, type) cert = Certificate.new cert.nonce = buffer.read_string cert.key = buffer.read_keyblob(type) cert.serial = buffer.read_int64 cert.type = type_symbol(buffer.read_long) cert.key_id = buffer.read_string cert.valid_principals = buffer.read_buffer.read_all(&:read_string) cert.valid_after = Time.at(buffer.read_int64) cert.valid_before = Time.at(buffer.read_int64) cert.critical_options = read_options(buffer) cert.extensions = read_options(buffer) cert.reserved = buffer.read_string cert.signature_key = buffer.read_buffer.read_key cert.signature = buffer.read_string cert end def ssh_type key.ssh_type + "-cert-v01@openssh.com" end def ssh_signature_type key.ssh_type end # Serializes the certificate (and key). def to_blob Buffer.from( :raw, to_blob_without_signature, :string, signature ).to_s end def ssh_do_sign(data) key.ssh_do_sign(data) end def ssh_do_verify(sig, data) key.ssh_do_verify(sig, data) end def to_pem key.to_pem end def fingerprint key.fingerprint end # Signs the certificate with key. def sign!(key, sign_nonce=nil) # ssh-keygen uses 32 bytes of nonce. self.nonce = sign_nonce || SecureRandom.random_bytes(32) self.signature_key = key self.signature = Net::SSH::Buffer.from( :string, key.ssh_signature_type, :mstring, key.ssh_do_sign(to_blob_without_signature) ).to_s self end def sign(key, sign_nonce=nil) cert = clone cert.sign!(key, sign_nonce) end # Checks whether the certificate's signature was signed by signature key. def signature_valid? buffer = Buffer.new(signature) buffer.read_string # skip signature format signature_key.ssh_do_verify(buffer.read_string, to_blob_without_signature) end def self.read_options(buffer) names = [] options = buffer.read_buffer.read_all do |b| name = b.read_string names << name data = b.read_string data = Buffer.new(data).read_string unless data.empty? [name, data] end if names.sort != names raise ArgumentError, "option/extension names must be in sorted order" end Hash[options] end private_class_method :read_options def self.type_symbol(type) types = {1 => :user, 2 => :host} raise ArgumentError("unsupported type: #{type}") unless types.include?(type) types.fetch(type) end private_class_method :type_symbol private def type_value(type) types = {user: 1, host: 2} raise ArgumentError("unsupported type: #{type}") unless types.include?(type) types.fetch(type) end def ssh_time(t) # Times in certificates are represented as a uint64. [[t.to_i, 0].max, 2<<64 - 1].min end def to_blob_without_signature Buffer.from( :string, ssh_type, :string, nonce, :raw, key_without_type, :int64, serial, :long, type_value(type), :string, key_id, :string, valid_principals.inject(Buffer.new) { |acc, elem| acc.write_string(elem) }.to_s, :int64, ssh_time(valid_after), :int64, ssh_time(valid_before), :string, options_to_blob(critical_options), :string, options_to_blob(extensions), :string, reserved, :string, signature_key.to_blob ).to_s end def key_without_type # key.to_blob gives us e.g. "ssh-rsa," but we just want "". tmp = Buffer.new(key.to_blob) tmp.read_string # skip the underlying key type tmp.read end def options_to_blob(options) options.keys.sort.inject(Buffer.new) do |b, name| b.write_string(name) data = options.fetch(name) data = Buffer.from(:string, data).to_s unless data.empty? b.write_string(data) end.to_s end end end; end; end net-ssh-4.2.0/lib/net/ssh/authentication/constants.rb000066400000000000000000000010671315376572000226240ustar00rootroot00000000000000module Net; module SSH; module Authentication # Describes the constants used by the Net::SSH::Authentication components # of the Net::SSH library. Individual authentication method implemenations # may define yet more constants that are specific to their implementation. module Constants USERAUTH_REQUEST = 50 USERAUTH_FAILURE = 51 USERAUTH_SUCCESS = 52 USERAUTH_BANNER = 53 USERAUTH_PASSWD_CHANGEREQ = 60 USERAUTH_PK_OK = 60 USERAUTH_METHOD_RANGE = 60..79 end end; end; endnet-ssh-4.2.0/lib/net/ssh/authentication/ed25519.rb000066400000000000000000000105101315376572000215770ustar00rootroot00000000000000gem 'rbnacl', '>= 3.2.0', '< 5.0' gem 'bcrypt_pbkdf', '~> 1.0' unless RUBY_PLATFORM == "java" begin require 'rbnacl/libsodium' rescue LoadError # rubocop:disable Lint/HandleExceptions end require 'rbnacl' require 'rbnacl/signatures/ed25519/verify_key' require 'rbnacl/signatures/ed25519/signing_key' require 'rbnacl/hash' require 'base64' require 'net/ssh/transport/cipher_factory' require 'bcrypt_pbkdf' unless RUBY_PLATFORM == "java" module Net; module SSH; module Authentication module ED25519 class SigningKeyFromFile < RbNaCl::Signatures::Ed25519::SigningKey def initialize(pk,sk) @signing_key = sk @verify_key = RbNaCl::Signatures::Ed25519::VerifyKey.new(pk) end end class PubKey attr_reader :verify_key def initialize(data) @verify_key = RbNaCl::Signatures::Ed25519::VerifyKey.new(data) end def self.read_keyblob(buffer) PubKey.new(buffer.read_string) end def to_blob Net::SSH::Buffer.from(:mstring,"ssh-ed25519",:string,@verify_key.to_bytes).to_s end def ssh_type "ssh-ed25519" end def ssh_signature_type ssh_type end def ssh_do_verify(sig,data) @verify_key.verify(sig,data) end def to_pem # TODO this is not pem ssh_type + Base64.encode64(@verify_key.to_bytes) end def fingerprint @fingerprint ||= OpenSSL::Digest::MD5.hexdigest(to_blob).scan(/../).join(":") end end class PrivKey CipherFactory = Net::SSH::Transport::CipherFactory MBEGIN = "-----BEGIN OPENSSH PRIVATE KEY-----\n" MEND = "-----END OPENSSH PRIVATE KEY-----\n" MAGIC = "openssh-key-v1" attr_reader :sign_key def initialize(datafull,password) raise ArgumentError.new("Expected #{MBEGIN} at start of private key") unless datafull.start_with?(MBEGIN) raise ArgumentError.new("Expected #{MEND} at end of private key") unless datafull.end_with?(MEND) datab64 = datafull[MBEGIN.size ... -MEND.size] data = Base64.decode64(datab64) raise ArgumentError.new("Expected #{MAGIC} at start of decoded private key") unless data.start_with?(MAGIC) buffer = Net::SSH::Buffer.new(data[MAGIC.size+1 .. -1]) ciphername = buffer.read_string raise ArgumentError.new("#{ciphername} in private key is not supported") unless CipherFactory.supported?(ciphername) kdfname = buffer.read_string raise ArgumentError.new("Expected #{kdfname} to be or none or bcrypt") unless %w(none bcrypt).include?(kdfname) kdfopts = Net::SSH::Buffer.new(buffer.read_string) num_keys = buffer.read_long raise ArgumentError.new("Only 1 key is supported in ssh keys #{num_keys} was in private key") unless num_keys == 1 _pubkey = buffer.read_string len = buffer.read_long keylen, blocksize, ivlen = CipherFactory.get_lengths(ciphername, iv_len: true) raise ArgumentError.new("Private key len:#{len} is not a multiple of #{blocksize}") if ((len < blocksize) || ((blocksize > 0) && (len % blocksize) != 0)) if kdfname == 'bcrypt' salt = kdfopts.read_string rounds = kdfopts.read_long raise "BCryptPbkdf is not implemented for jruby" if RUBY_PLATFORM == "java" key = BCryptPbkdf::key(password, salt, keylen + ivlen, rounds) else key = '\x00' * (keylen + ivlen) end cipher = CipherFactory.get(ciphername, key: key[0...keylen], iv:key[keylen...keylen+ivlen], decrypt: true) decoded = cipher.update(buffer.remainder_as_buffer.to_s) decoded << cipher.final decoded = Net::SSH::Buffer.new(decoded) check1 = decoded.read_long check2 = decoded.read_long raise ArgumentError, "Decrypt failed on private key" if (check1 != check2) _type_name = decoded.read_string pk = decoded.read_string sk = decoded.read_string _comment = decoded.read_string @pk = pk @sign_key = SigningKeyFromFile.new(pk,sk) end def to_blob public_key.to_blob end def ssh_type "ssh-ed25519" end def ssh_signature_type ssh_type end def public_key PubKey.new(@pk) end def ssh_do_sign(data) @sign_key.sign(data) end def self.read(data,password) self.new(data,password) end def self.read_keyblob(buffer) ED25519::PubKey.read_keyblob(buffer) end end end end; end; end net-ssh-4.2.0/lib/net/ssh/authentication/ed25519_loader.rb000066400000000000000000000017221315376572000231320ustar00rootroot00000000000000module Net; module SSH; module Authentication # Loads ED25519 support which requires optinal dependecies like # rbnacl, bcrypt_pbkdf module ED25519Loader begin require 'net/ssh/authentication/ed25519' LOADED = true ERROR = nil rescue LoadError => e ERROR = e LOADED = false end def self.raiseUnlessLoaded(message) description = ERROR.is_a?(LoadError) ? dependenciesRequiredForED25519 : '' description << "#{ERROR.class} : \"#{ERROR.message}\"\n" if ERROR raise NotImplementedError, "#{message}\n#{description}" unless LOADED end def self.dependenciesRequiredForED25519 result = "net-ssh requires the following gems for ed25519 support:\n" result << " * rbnacl (>= 3.2, < 5.0)\n" result << " * rbnacl-libsodium, if your system doesn't have libsodium installed.\n" result << " * bcrypt_pbkdf (>= 1.0, < 2.0)\n" unless RUBY_PLATFORM == "java" result << "See https://github.com/net-ssh/net-ssh/issues/478 for more information\n" end end end; end; end net-ssh-4.2.0/lib/net/ssh/authentication/key_manager.rb000066400000000000000000000244731315376572000231000ustar00rootroot00000000000000require 'net/ssh/errors' require 'net/ssh/key_factory' require 'net/ssh/loggable' require 'net/ssh/authentication/agent' module Net module SSH module Authentication # A trivial exception class used to report errors in the key manager. class KeyManagerError < Net::SSH::Exception; end # This class encapsulates all operations done by clients on a user's # private keys. In practice, the client should never need a reference # to a private key; instead, they grab a list of "identities" (public # keys) that are available from the KeyManager, and then use # the KeyManager to do various private key operations using those # identities. # # The KeyManager also uses the Agent class to encapsulate the # ssh-agent. Thus, from a client's perspective it is completely # hidden whether an identity comes from the ssh-agent or from a file # on disk. class KeyManager include Loggable # The list of user key files that will be examined attr_reader :key_files # The list of user key data that will be examined attr_reader :key_data # The map of loaded identities attr_reader :known_identities # The map of options that were passed to the key-manager attr_reader :options # Create a new KeyManager. By default, the manager will # use the ssh-agent if it is running and the `:use_agent` option # is not false. def initialize(logger, options={}) self.logger = logger @key_files = [] @key_data = [] @use_agent = !(options[:use_agent] == false) @known_identities = {} @agent = nil @options = options end # Clear all knowledge of any loaded user keys. This also clears the list # of default identity files that are to be loaded, thus making it # appropriate to use if a client wishes to NOT use the default identity # files. def clear! key_files.clear key_data.clear known_identities.clear self end # Add the given key_file to the list of key files that will be used. def add(key_file) key_files.push(File.expand_path(key_file)).uniq! self end # Add the given key_file to the list of keys that will be used. def add_key_data(key_data_) key_data.push(key_data_).uniq! self end # This is used as a hint to the KeyManager indicating that the agent # connection is no longer needed. Any other open resources may be closed # at this time. # # Calling this does NOT indicate that the KeyManager will no longer # be used. Identities may still be requested and operations done on # loaded identities, in which case, the agent will be automatically # reconnected. This method simply allows the client connection to be # closed when it will not be used in the immediate future. def finish @agent.close if @agent @agent = nil end # Iterates over all available identities (public keys) known to this # manager. As it finds one, it will then yield it to the caller. # The origin of the identities may be from files on disk or from an # ssh-agent. Note that identities from an ssh-agent are always listed # first in the array, with other identities coming after. # # If key manager was created with :keys_only option, any identity # from ssh-agent will be ignored unless it present in key_files or # key_data. def each_identity prepared_identities = prepare_identities_from_files + prepare_identities_from_data user_identities = load_identities(prepared_identities, false, true) if agent agent.identities.each do |key| corresponding_user_identity = user_identities.detect { |identity| identity[:public_key] && identity[:public_key].to_pem == key.to_pem } user_identities.delete(corresponding_user_identity) if corresponding_user_identity if !options[:keys_only] || corresponding_user_identity known_identities[key] = { from: :agent } yield key end end end user_identities = load_identities(user_identities, !options[:non_interactive], false) user_identities.each do |identity| key = identity.delete(:public_key) known_identities[key] = identity yield key end self end # Sign the given data, using the corresponding private key of the given # identity. If the identity was originally obtained from an ssh-agent, # then the ssh-agent will be used to sign the data, otherwise the # private key for the identity will be loaded from disk (if it hasn't # been loaded already) and will then be used to sign the data. # # Regardless of the identity's origin or who does the signing, this # will always return the signature in an SSH2-specified "signature # blob" format. def sign(identity, data) info = known_identities[identity] or raise KeyManagerError, "the given identity is unknown to the key manager" if info[:key].nil? && info[:from] == :file begin info[:key] = KeyFactory.load_private_key(info[:file], options[:passphrase], !options[:non_interactive]) rescue OpenSSL::OpenSSLError, Exception => e raise KeyManagerError, "the given identity is known, but the private key could not be loaded: #{e.class} (#{e.message})" end end if info[:key] return Net::SSH::Buffer.from(:string, identity.ssh_signature_type, :mstring, info[:key].ssh_do_sign(data.to_s)).to_s end if info[:from] == :agent raise KeyManagerError, "the agent is no longer available" unless agent return agent.sign(identity, data.to_s) end raise KeyManagerError, "[BUG] can't determine identity origin (#{info.inspect})" end # Identifies whether the ssh-agent will be used or not. def use_agent? @use_agent end # Toggles whether the ssh-agent will be used or not. If true, an # attempt will be made to use the ssh-agent. If false, any existing # connection to an agent is closed and the agent will not be used. def use_agent=(use_agent) finish if !use_agent @use_agent = use_agent end # Returns an Agent instance to use for communicating with an SSH # agent process. Returns nil if use of an SSH agent has been disabled, # or if the agent is otherwise not available. def agent return unless use_agent? @agent ||= Agent.connect(logger, options[:agent_socket_factory]) rescue AgentNotAvailable @use_agent = false nil end private # Prepares identities from user key_files for loading, preserving their order and sources. def prepare_identities_from_files key_files.map do |file| if readable_file?(file) identity = {} cert_file = file + "-cert.pub" public_key_file = file + ".pub" if readable_file?(cert_file) identity[:load_from] = :pubkey_file identity[:pubkey_file] = cert_file elsif readable_file?(public_key_file) identity[:load_from] = :pubkey_file identity[:pubkey_file] = public_key_file else identity[:load_from] = :privkey_file end identity.merge(privkey_file: file) end end.compact end def readable_file?(path) File.file?(path) && File.readable?(path) end # Prepared identities from user key_data, preserving their order and sources. def prepare_identities_from_data key_data.map do |data| { load_from: :data, data: data } end end # Load prepared identities. Private key decryption errors ignored if ignore_decryption_errors def load_identities(identities, ask_passphrase, ignore_decryption_errors) identities.map do |identity| begin case identity[:load_from] when :pubkey_file key = KeyFactory.load_public_key(identity[:pubkey_file]) { public_key: key, from: :file, file: identity[:privkey_file] } when :privkey_file private_key = KeyFactory.load_private_key(identity[:privkey_file], options[:passphrase], ask_passphrase, options[:password_prompt]) key = private_key.send(:public_key) { public_key: key, from: :file, file: identity[:privkey_file], key: private_key } when :data private_key = KeyFactory.load_data_private_key(identity[:data], options[:passphrase], ask_passphrase, "", options[:password_prompt]) key = private_key.send(:public_key) { public_key: key, from: :key_data, data: identity[:data], key: private_key } else identity end rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError, OpenSSL::PKey::ECError, OpenSSL::PKey::PKeyError, ArgumentError => e if ignore_decryption_errors identity else process_identity_loading_error(identity, e) nil end rescue Exception => e process_identity_loading_error(identity, e) nil end end.compact end def process_identity_loading_error(identity, e) case identity[:load_from] when :pubkey_file error { "could not load public key file `#{identity[:pubkey_file]}': #{e.class} (#{e.message})" } when :privkey_file error { "could not load private key file `#{identity[:privkey_file]}': #{e.class} (#{e.message})" } else raise e end end end end end end net-ssh-4.2.0/lib/net/ssh/authentication/methods/000077500000000000000000000000001315376572000217225ustar00rootroot00000000000000net-ssh-4.2.0/lib/net/ssh/authentication/methods/abstract.rb000066400000000000000000000036261315376572000240610ustar00rootroot00000000000000require 'net/ssh/buffer' require 'net/ssh/errors' require 'net/ssh/loggable' require 'net/ssh/authentication/constants' module Net; module SSH; module Authentication; module Methods # The base class of all user authentication methods. It provides a few # bits of common functionality. class Abstract include Constants, Loggable # The authentication session object attr_reader :session # The key manager object. Not all authentication methods will require # this. attr_reader :key_manager # Instantiates a new authentication method. def initialize(session, options={}) @session = session @key_manager = options[:key_manager] @options = options @prompt = options[:password_prompt] self.logger = session.logger end # Returns the session-id, as generated during the first key exchange of # an SSH connection. def session_id session.transport.algorithms.session_id end # Sends a message via the underlying transport layer abstraction. This # will block until the message is completely sent. def send_message(msg) session.transport.send_message(msg) end # Creates a new USERAUTH_REQUEST packet. The extra arguments on the end # must be either boolean values or strings, and are tacked onto the end # of the packet. The new packet is returned, ready for sending. def userauth_request(username, next_service, auth_method, *others) buffer = Net::SSH::Buffer.from(:byte, USERAUTH_REQUEST, :string, username, :string, next_service, :string, auth_method) others.each do |value| case value when true, false then buffer.write_bool(value) when String then buffer.write_string(value) else raise ArgumentError, "don't know how to write #{value.inspect}" end end buffer end private attr_reader :prompt end end; end; end; endnet-ssh-4.2.0/lib/net/ssh/authentication/methods/hostbased.rb000066400000000000000000000050651315376572000242310ustar00rootroot00000000000000require 'net/ssh/authentication/methods/abstract' module Net module SSH module Authentication module Methods # Implements the host-based SSH authentication method. class Hostbased < Abstract include Constants # Attempts to perform host-based authorization of the user by trying # all known keys. def authenticate(next_service, username, password=nil) return false unless key_manager key_manager.each_identity do |identity| return true if authenticate_with(identity, next_service, username, key_manager) end return false end private # Returns the hostname as reported by the underlying socket. def hostname session.transport.socket.client_name end # Attempts to perform host-based authentication of the user, using # the given host identity (key). def authenticate_with(identity, next_service, username, key_manager) debug { "trying hostbased (#{identity.fingerprint})" } client_username = ENV['USER'] || username req = build_request(identity, next_service, username, "#{hostname}.", client_username) sig_data = Buffer.from(:string, session_id, :raw, req) sig = key_manager.sign(identity, sig_data.to_s) message = Buffer.from(:raw, req, :string, sig) send_message(message) message = session.next_message case message.type when USERAUTH_SUCCESS info { "hostbased succeeded (#{identity.fingerprint})" } return true when USERAUTH_FAILURE info { "hostbased failed (#{identity.fingerprint})" } raise Net::SSH::Authentication::DisallowedMethod unless message[:authentications].split(/,/).include? 'hostbased' return false else raise Net::SSH::Exception, "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})" end end # Build the "core" hostbased request string. def build_request(identity, next_service, username, hostname, client_username) userauth_request(username, next_service, "hostbased", identity.ssh_type, Buffer.from(:key, identity).to_s, hostname, client_username).to_s end end end end end end net-ssh-4.2.0/lib/net/ssh/authentication/methods/keyboard_interactive.rb000066400000000000000000000054501315376572000264500ustar00rootroot00000000000000require 'net/ssh/prompt' require 'net/ssh/authentication/methods/abstract' module Net module SSH module Authentication module Methods # Implements the "keyboard-interactive" SSH authentication method. class KeyboardInteractive < Abstract USERAUTH_INFO_REQUEST = 60 USERAUTH_INFO_RESPONSE = 61 # Attempt to authenticate the given user for the given service. def authenticate(next_service, username, password=nil) debug { "trying keyboard-interactive" } send_message(userauth_request(username, next_service, "keyboard-interactive", "", "")) prompter = nil loop do message = session.next_message case message.type when USERAUTH_SUCCESS debug { "keyboard-interactive succeeded" } prompter.success if prompter return true when USERAUTH_FAILURE debug { "keyboard-interactive failed" } raise Net::SSH::Authentication::DisallowedMethod unless message[:authentications].split(/,/).include? 'keyboard-interactive' return false unless interactive? password = nil debug { "retrying keyboard-interactive" } send_message(userauth_request(username, next_service, "keyboard-interactive", "", "")) when USERAUTH_INFO_REQUEST name = message.read_string instruction = message.read_string debug { "keyboard-interactive info request" } if password.nil? && interactive? && prompter.nil? prompter = prompt.start(type: 'keyboard-interactive', name: name, instruction: instruction) end _ = message.read_string # lang_tag responses =[] message.read_long.times do text = message.read_string echo = message.read_bool password_to_send = password || (prompter && prompter.ask(text, echo)) responses << password_to_send end # if the password failed the first time around, don't try # and use it on subsequent requests. password = nil msg = Buffer.from(:byte, USERAUTH_INFO_RESPONSE, :long, responses.length, :string, responses) send_message(msg) else raise Net::SSH::Exception, "unexpected reply in keyboard interactive: #{message.type} (#{message.inspect})" end end end def interactive? options = session.transport.options || {} !options[:non_interactive] end end end end end end net-ssh-4.2.0/lib/net/ssh/authentication/methods/none.rb000066400000000000000000000021331315376572000232050ustar00rootroot00000000000000require 'net/ssh/errors' require 'net/ssh/authentication/methods/abstract' module Net module SSH module Authentication module Methods # Implements the "none" SSH authentication method. class None < Abstract # Attempt to authenticate as "none" def authenticate(next_service, user="", password="") send_message(userauth_request(user, next_service, "none")) message = session.next_message case message.type when USERAUTH_SUCCESS debug { "none succeeded" } return true when USERAUTH_FAILURE debug { "none failed" } raise Net::SSH::Authentication::DisallowedMethod unless message[:authentications].split(/,/).include? 'none' return false else raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})" end end end end end end end net-ssh-4.2.0/lib/net/ssh/authentication/methods/password.rb000066400000000000000000000052121315376572000241110ustar00rootroot00000000000000require 'net/ssh/errors' require 'net/ssh/prompt' require 'net/ssh/authentication/methods/abstract' module Net module SSH module Authentication module Methods # Implements the "password" SSH authentication method. class Password < Abstract # Attempt to authenticate the given user for the given service. If # the password parameter is nil, this will ask for password def authenticate(next_service, username, password=nil) clear_prompter! retries = 0 max_retries = get_max_retries return false if !password && max_retries == 0 begin password_to_send = password || ask_password(username) send_message(userauth_request(username, next_service, "password", false, password_to_send)) message = session.next_message retries += 1 if message.type == USERAUTH_FAILURE debug { "password failed" } raise Net::SSH::Authentication::DisallowedMethod unless message[:authentications].split(/,/).include? 'password' password = nil end end until (message.type != USERAUTH_FAILURE || retries >= max_retries) case message.type when USERAUTH_SUCCESS debug { "password succeeded" } @prompter.success if @prompter return true when USERAUTH_FAILURE return false when USERAUTH_PASSWD_CHANGEREQ debug { "password change request received, failing" } return false else raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})" end end private NUMBER_OF_PASSWORD_PROMPTS = 3 def clear_prompter! @prompt_info = nil @prompter = nil end def ask_password(username) host = session.transport.host prompt_info = {type: 'password', user: username, host: host} if @prompt_info != prompt_info @prompt_info = prompt_info @prompter = prompt.start(prompt_info) end echo = false @prompter.ask("#{username}@#{host}'s password:", echo) end def get_max_retries options = session.transport.options || {} result = options[:number_of_password_prompts] || NUMBER_OF_PASSWORD_PROMPTS options[:non_interactive] ? 0 : result end end end end end end net-ssh-4.2.0/lib/net/ssh/authentication/methods/publickey.rb000066400000000000000000000070261315376572000242430ustar00rootroot00000000000000require 'net/ssh/buffer' require 'net/ssh/errors' require 'net/ssh/authentication/methods/abstract' module Net module SSH module Authentication module Methods # Implements the "publickey" SSH authentication method. class Publickey < Abstract # Attempts to perform public-key authentication for the given # username, trying each identity known to the key manager. If any of # them succeed, returns +true+, otherwise returns +false+. This # requires the presence of a key manager. def authenticate(next_service, username, password=nil) return false unless key_manager key_manager.each_identity do |identity| return true if authenticate_with(identity, next_service, username) end return false end private # Builds a packet that contains the request formatted for sending # a public-key request to the server. def build_request(pub_key, username, next_service, has_sig) blob = Net::SSH::Buffer.new blob.write_key pub_key userauth_request(username, next_service, "publickey", has_sig, pub_key.ssh_type, blob.to_s) end # Builds and sends a request formatted for a public-key # authentication request. def send_request(pub_key, username, next_service, signature=nil) msg = build_request(pub_key, username, next_service, !signature.nil?) msg.write_string(signature) if signature send_message(msg) end # Attempts to perform public-key authentication for the given # username, with the given identity (public key). Returns +true+ if # successful, or +false+ otherwise. def authenticate_with(identity, next_service, username) debug { "trying publickey (#{identity.fingerprint})" } send_request(identity, username, next_service) message = session.next_message case message.type when USERAUTH_PK_OK buffer = build_request(identity, username, next_service, true) sig_data = Net::SSH::Buffer.new sig_data.write_string(session_id) sig_data.append(buffer.to_s) sig_blob = key_manager.sign(identity, sig_data) send_request(identity, username, next_service, sig_blob.to_s) message = session.next_message case message.type when USERAUTH_SUCCESS debug { "publickey succeeded (#{identity.fingerprint})" } return true when USERAUTH_FAILURE debug { "publickey failed (#{identity.fingerprint})" } raise Net::SSH::Authentication::DisallowedMethod unless message[:authentications].split(/,/).include? 'publickey' return false else raise Net::SSH::Exception, "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})" end when USERAUTH_FAILURE return false else raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})" end end end end end end end net-ssh-4.2.0/lib/net/ssh/authentication/pageant.rb000066400000000000000000000366611315376572000222370ustar00rootroot00000000000000if RUBY_VERSION < "1.9" require 'dl/import' require 'dl/struct' elsif RUBY_VERSION < "2.1" require 'dl/import' require 'dl/types' require 'dl' else require 'fiddle' require 'fiddle/types' require 'fiddle/import' # For now map DL to Fiddler versus updating all the code below module DL CPtr ||= Fiddle::Pointer if RUBY_PLATFORM != "java" RUBY_FREE ||= Fiddle::RUBY_FREE end end end require 'net/ssh/errors' module Net; module SSH; module Authentication # This module encapsulates the implementation of a socket factory that # uses the PuTTY "pageant" utility to obtain information about SSH # identities. # # This code is a slightly modified version of the original implementation # by Guillaume Marçais (guillaume.marcais@free.fr). It is used and # relicensed by permission. module Pageant # From Putty pageant.c AGENT_MAX_MSGLEN = 8192 AGENT_COPYDATA_ID = 0x804e50ba # The definition of the Windows methods and data structures used in # communicating with the pageant process. module Win # rubocop:disable Metrics/ModuleLength # Compatibility on initialization if RUBY_VERSION < "1.9" extend DL::Importable dlload 'user32' dlload 'kernel32' dlload 'advapi32' SIZEOF_DWORD = DL.sizeof('L') elsif RUBY_VERSION < "2.1" extend DL::Importer dlload 'user32','kernel32', 'advapi32' include DL::Win32Types SIZEOF_DWORD = DL::SIZEOF_LONG else extend Fiddle::Importer dlload 'user32','kernel32', 'advapi32' include Fiddle::Win32Types SIZEOF_DWORD = Fiddle::SIZEOF_LONG end if RUBY_ENGINE=="jruby" typealias("HANDLE", "void *") # From winnt.h typealias("PHANDLE", "void *") # From winnt.h typealias("ULONG_PTR", "unsigned long*") end typealias("LPCTSTR", "char *") # From winnt.h typealias("LPVOID", "void *") # From winnt.h typealias("LPCVOID", "const void *") # From windef.h typealias("LRESULT", "long") # From windef.h typealias("WPARAM", "unsigned int *") # From windef.h typealias("LPARAM", "long *") # From windef.h typealias("PDWORD_PTR", "long *") # From basetsd.h typealias("USHORT", "unsigned short") # From windef.h # From winbase.h, winnt.h INVALID_HANDLE_VALUE = -1 NULL = nil PAGE_READWRITE = 0x0004 FILE_MAP_WRITE = 2 WM_COPYDATA = 74 SMTO_NORMAL = 0 # From winuser.h SUFFIX = if RUBY_ENGINE == "jruby" "A" else "" end # args: lpClassName, lpWindowName extern "HWND FindWindow#{SUFFIX}(LPCTSTR, LPCTSTR)" # args: none extern 'DWORD GetCurrentThreadId()' # args: hFile, (ignored), flProtect, dwMaximumSizeHigh, # dwMaximumSizeLow, lpName extern "HANDLE CreateFileMapping#{SUFFIX}(HANDLE, void *, DWORD, " + "DWORD, DWORD, LPCTSTR)" # args: hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh, # dwfileOffsetLow, dwNumberOfBytesToMap extern 'LPVOID MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, DWORD)' # args: lpBaseAddress extern 'BOOL UnmapViewOfFile(LPCVOID)' # args: hObject extern 'BOOL CloseHandle(HANDLE)' # args: hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult extern "LRESULT SendMessageTimeout#{SUFFIX}(HWND, UINT, WPARAM, LPARAM, " + "UINT, UINT, PDWORD_PTR)" # args: none extern 'DWORD GetLastError()' # args: none extern 'HANDLE GetCurrentProcess()' # args: hProcessHandle, dwDesiredAccess, (out) phNewTokenHandle extern 'BOOL OpenProcessToken(HANDLE, DWORD, PHANDLE)' # args: hTokenHandle, uTokenInformationClass, # (out) lpTokenInformation, dwTokenInformationLength # (out) pdwInfoReturnLength extern 'BOOL GetTokenInformation(HANDLE, UINT, LPVOID, DWORD, ' + 'PDWORD)' # args: (out) lpSecurityDescriptor, dwRevisionLevel extern 'BOOL InitializeSecurityDescriptor(LPVOID, DWORD)' # args: (out) lpSecurityDescriptor, lpOwnerSid, bOwnerDefaulted extern 'BOOL SetSecurityDescriptorOwner(LPVOID, LPVOID, BOOL)' # args: pSecurityDescriptor extern 'BOOL IsValidSecurityDescriptor(LPVOID)' # Constants needed for security attribute retrieval. # Specifies the access mask corresponding to the desired access # rights. TOKEN_QUERY = 0x8 # The value of TOKEN_USER from the TOKEN_INFORMATION_CLASS enum. TOKEN_USER_INFORMATION_CLASS = 1 # The initial revision level assigned to the security descriptor. REVISION = 1 # Structs for security attribute functions. # Holds the retrieved user access token. TOKEN_USER = struct ['void * SID', 'DWORD ATTRIBUTES'] # Contains the security descriptor, this gets passed to the # function that constructs the shared memory map. SECURITY_ATTRIBUTES = struct ['DWORD nLength', 'LPVOID lpSecurityDescriptor', 'BOOL bInheritHandle'] # The security descriptor holds security information. SECURITY_DESCRIPTOR = struct ['UCHAR Revision', 'UCHAR Sbz1', 'USHORT Control', 'LPVOID Owner', 'LPVOID Group', 'LPVOID Sacl', 'LPVOID Dacl'] # The COPYDATASTRUCT is used to send WM_COPYDATA messages COPYDATASTRUCT = if RUBY_ENGINE == "jruby" struct ['ULONG_PTR dwData', 'DWORD cbData', 'LPVOID lpData'] else struct ['uintptr_t dwData', 'DWORD cbData', 'LPVOID lpData'] end # Compatibility for security attribute retrieval. if RUBY_VERSION < "1.9" # Alias functions to > 1.9 capitalization %w(findWindow getCurrentProcess initializeSecurityDescriptor setSecurityDescriptorOwner isValidSecurityDescriptor openProcessToken getTokenInformation getLastError getCurrentThreadId createFileMapping mapViewOfFile sendMessageTimeout unmapViewOfFile closeHandle).each do |name| new_name = name[0].chr.upcase + name[1..name.length] alias_method new_name, name module_function new_name end def self.malloc_ptr(size) return DL.malloc(size) end def self.get_ptr(data) return data.to_ptr end def self.set_ptr_data(ptr, data) ptr[0] = data end elsif RUBY_ENGINE == "jruby" %w(FindWindow CreateFileMapping SendMessageTimeout).each do |name| alias_method name, name+"A" module_function name end # :nodoc: module LibC extend FFI::Library ffi_lib FFI::Library::LIBC attach_function :malloc, [:size_t], :pointer attach_function :free, [:pointer], :void end def self.malloc_ptr(size) Fiddle::Pointer.new(LibC.malloc(size), size, LibC.method(:free)) end def self.get_ptr(ptr) return data.address end def self.set_ptr_data(ptr, data) ptr.write_string_length(data, data.size) end else def self.malloc_ptr(size) return DL::CPtr.malloc(size, DL::RUBY_FREE) end def self.get_ptr(data) return DL::CPtr.to_ptr data end def self.set_ptr_data(ptr, data) DL::CPtr.new(ptr)[0,data.size] = data end end def self.get_security_attributes_for_user user = get_current_user psd_information = malloc_ptr(Win::SECURITY_DESCRIPTOR.size) raise_error_if_zero( Win.InitializeSecurityDescriptor(psd_information, Win::REVISION) ) raise_error_if_zero( Win.SetSecurityDescriptorOwner(psd_information, get_sid_ptr(user), 0) ) raise_error_if_zero( Win.IsValidSecurityDescriptor(psd_information) ) sa = Win::SECURITY_ATTRIBUTES.new(to_struct_ptr(malloc_ptr(Win::SECURITY_ATTRIBUTES.size))) sa.nLength = Win::SECURITY_ATTRIBUTES.size sa.lpSecurityDescriptor = psd_information.to_i sa.bInheritHandle = 1 return sa end if RUBY_ENGINE == "jruby" def self.ptr_to_s(ptr, size) ret = ptr.to_s(size) ret << "\x00" while ret.size < size ret end def self.ptr_to_handle(phandle) phandle.ptr end def self.ptr_to_dword(ptr) first = ptr.ptr.to_i second = ptr_to_s(ptr,Win::SIZEOF_DWORD).unpack('L')[0] raise "Error" unless first == second first end def self.to_token_user(ptoken_information) TOKEN_USER.new(ptoken_information.to_ptr) end def self.to_struct_ptr(ptr) ptr.to_ptr end def self.get_sid(user) ptr_to_s(user.to_ptr.ptr,Win::SIZEOF_DWORD).unpack('L')[0] end def self.get_sid_ptr(user) user.to_ptr.ptr end else def self.get_sid(user) user.SID end def self.ptr_to_handle(phandle) phandle.ptr.to_i end def self.to_struct_ptr(ptr) ptr end def self.ptr_to_dword(ptr) ptr.to_s(Win::SIZEOF_DWORD).unpack('L')[0] end def self.to_token_user(ptoken_information) TOKEN_USER.new(ptoken_information) end def self.get_sid_ptr(user) user.SID end end def self.get_current_user token_handle = open_process_token(Win.GetCurrentProcess, Win::TOKEN_QUERY) token_user = get_token_information(token_handle, Win::TOKEN_USER_INFORMATION_CLASS) return token_user end def self.open_process_token(process_handle, desired_access) ptoken_handle = malloc_ptr(Win::SIZEOF_DWORD) raise_error_if_zero( Win.OpenProcessToken(process_handle, desired_access, ptoken_handle) ) token_handle = ptr_to_handle(ptoken_handle) return token_handle end def self.get_token_information(token_handle, token_information_class) # Hold the size of the information to be returned preturn_length = malloc_ptr(Win::SIZEOF_DWORD) # Going to throw an INSUFFICIENT_BUFFER_ERROR, but that is ok # here. This is retrieving the size of the information to be # returned. Win.GetTokenInformation(token_handle, token_information_class, Win::NULL, 0, preturn_length) ptoken_information = malloc_ptr(ptr_to_dword(preturn_length)) # This call is going to write the requested information to # the memory location referenced by token_information. raise_error_if_zero( Win.GetTokenInformation(token_handle, token_information_class, ptoken_information, ptoken_information.size, preturn_length) ) return to_token_user(ptoken_information) end def self.raise_error_if_zero(result) if result == 0 raise "Windows error: #{Win.GetLastError}" end end # Get a null-terminated string given a string. def self.get_cstr(str) return str + "\000" end end # This is the pseudo-socket implementation that mimics the interface of # a socket, translating each request into a Windows messaging call to # the pageant daemon. This allows pageant support to be implemented # simply by replacing the socket factory used by the Agent class. class Socket private_class_method :new # The factory method for creating a new Socket instance. def self.open new end # Create a new instance that communicates with the running pageant # instance. If no such instance is running, this will cause an error. def initialize @win = Win.FindWindow("Pageant", "Pageant") if @win.to_i == 0 raise Net::SSH::Exception, "pageant process not running" end @input_buffer = Net::SSH::Buffer.new @output_buffer = Net::SSH::Buffer.new end # Forwards the data to #send_query, ignoring any arguments after # the first. def send(data, *args) @input_buffer.append(data) ret = data.length while true return ret if @input_buffer.length < 4 msg_length = @input_buffer.read_long + 4 @input_buffer.reset! return ret if @input_buffer.length < msg_length msg = @input_buffer.read!(msg_length) @output_buffer.append(send_query(msg)) end end # Reads +n+ bytes from the cached result of the last query. If +n+ # is +nil+, returns all remaining data from the last query. def read(n = nil) @output_buffer.read(n) end def close; end # Packages the given query string and sends it to the pageant # process via the Windows messaging subsystem. The result is # cached, to be returned piece-wise when #read is called. def send_query(query) res = nil filemap = 0 ptr = nil id = Win.malloc_ptr(Win::SIZEOF_DWORD) mapname = "PageantRequest%08x" % Win.GetCurrentThreadId() security_attributes = Win.get_ptr Win.get_security_attributes_for_user filemap = Win.CreateFileMapping(Win::INVALID_HANDLE_VALUE, security_attributes, Win::PAGE_READWRITE, 0, AGENT_MAX_MSGLEN, mapname) if filemap == 0 || filemap == Win::INVALID_HANDLE_VALUE raise Net::SSH::Exception, "Creation of file mapping failed with error: #{Win.GetLastError}" end ptr = Win.MapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0, 0) if ptr.nil? || ptr.null? raise Net::SSH::Exception, "Mapping of file failed" end Win.set_ptr_data(ptr, query) # using struct to achieve proper alignment and field size on 64-bit platform cds = Win::COPYDATASTRUCT.new(Win.malloc_ptr(Win::COPYDATASTRUCT.size)) cds.dwData = AGENT_COPYDATA_ID cds.cbData = mapname.size + 1 cds.lpData = Win.get_cstr(mapname) succ = Win.SendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL, cds.to_ptr, Win::SMTO_NORMAL, 5000, id) if succ > 0 retlen = 4 + ptr.to_s(4).unpack("N")[0] res = ptr.to_s(retlen) else raise Net::SSH::Exception, "Message failed with error: #{Win.GetLastError}" end return res ensure Win.UnmapViewOfFile(ptr) unless ptr.nil? || ptr.null? Win.CloseHandle(filemap) if filemap != 0 end end end end; end; end net-ssh-4.2.0/lib/net/ssh/authentication/session.rb000066400000000000000000000124161315376572000222730ustar00rootroot00000000000000require 'net/ssh/loggable' require 'net/ssh/transport/constants' require 'net/ssh/authentication/constants' require 'net/ssh/authentication/key_manager' require 'net/ssh/authentication/methods/none' require 'net/ssh/authentication/methods/publickey' require 'net/ssh/authentication/methods/hostbased' require 'net/ssh/authentication/methods/password' require 'net/ssh/authentication/methods/keyboard_interactive' module Net; module SSH; module Authentication # Raised if the current authentication method is not allowed class DisallowedMethod < Net::SSH::Exception end # Represents an authentication session. It manages the authentication of # a user over an established connection (the "transport" object, see # Net::SSH::Transport::Session). # # The use of an authentication session to manage user authentication is # internal to Net::SSH (specifically Net::SSH.start). Consumers of the # Net::SSH library will never need to access this class directly. class Session include Transport::Constants, Constants, Loggable # transport layer abstraction attr_reader :transport # the list of authentication methods to try attr_reader :auth_methods # the list of authentication methods that are allowed attr_reader :allowed_auth_methods # a hash of options, given at construction time attr_reader :options # Instantiates a new Authentication::Session object over the given # transport layer abstraction. def initialize(transport, options={}) self.logger = transport.logger @transport = transport @auth_methods = options[:auth_methods] || Net::SSH::Config.default_auth_methods @options = options @allowed_auth_methods = @auth_methods end # Attempts to authenticate the given user, in preparation for the next # service request. Returns true if an authentication method succeeds in # authenticating the user, and false otherwise. def authenticate(next_service, username, password=nil) debug { "beginning authentication of `#{username}'" } transport.send_message(transport.service_request("ssh-userauth")) expect_message(SERVICE_ACCEPT) key_manager = KeyManager.new(logger, options) keys.each { |key| key_manager.add(key) } unless keys.empty? key_data.each { |key2| key_manager.add_key_data(key2) } unless key_data.empty? attempted = [] @auth_methods.each do |name| begin next unless @allowed_auth_methods.include?(name) attempted << name debug { "trying #{name}" } begin auth_class = Methods.const_get(name.split(/\W+/).map { |p| p.capitalize }.join) method = auth_class.new(self, key_manager: key_manager, password_prompt: options[:password_prompt]) rescue NameError debug{"Mechanism #{name} was requested, but isn't a known type. Ignoring it."} next end return true if method.authenticate(next_service, username, password) rescue Net::SSH::Authentication::DisallowedMethod end end error { "all authorization methods failed (tried #{attempted.join(', ')})" } return false ensure key_manager.finish if key_manager end # Blocks until a packet is received. It silently handles USERAUTH_BANNER # packets, and will raise an error if any packet is received that is not # valid during user authentication. def next_message loop do packet = transport.next_message case packet.type when USERAUTH_BANNER info { packet[:message] } # TODO add a hook for people to retrieve the banner when it is sent when USERAUTH_FAILURE @allowed_auth_methods = packet[:authentications].split(/,/) debug { "allowed methods: #{packet[:authentications]}" } return packet when USERAUTH_METHOD_RANGE, SERVICE_ACCEPT return packet when USERAUTH_SUCCESS transport.hint :authenticated return packet else raise Net::SSH::Exception, "unexpected message #{packet.type} (#{packet})" end end end # Blocks until a packet is received, and returns it if it is of the given # type. If it is not, an exception is raised. def expect_message(type) message = next_message unless message.type == type raise Net::SSH::Exception, "expected #{type}, got #{message.type} (#{message})" end message end private # Returns an array of paths to the key files usually defined # by system default. def default_keys if defined?(OpenSSL::PKey::EC) %w(~/.ssh/id_ed25519 ~/.ssh/id_rsa ~/.ssh/id_dsa ~/.ssh/id_ecdsa ~/.ssh2/id_ed25519 ~/.ssh2/id_rsa ~/.ssh2/id_dsa ~/.ssh2/id_ecdsa) else %w(~/.ssh/id_dsa ~/.ssh/id_rsa ~/.ssh2/id_dsa ~/.ssh2/id_rsa) end end # Returns an array of paths to the key files that should be used when # attempting any key-based authentication mechanism. def keys Array(options[:keys] || default_keys) end # Returns an array of the key data that should be used when # attempting any key-based authentication mechanism. def key_data Array(options[:key_data]) end end end; end; end net-ssh-4.2.0/lib/net/ssh/buffer.rb000066400000000000000000000317451315376572000170500ustar00rootroot00000000000000require 'net/ssh/ruby_compat' require 'net/ssh/transport/openssl' require 'net/ssh/authentication/certificate' require 'net/ssh/authentication/ed25519_loader' module Net; module SSH # Net::SSH::Buffer is a flexible class for building and parsing binary # data packets. It provides a stream-like interface for sequentially # reading data items from the buffer, as well as a useful helper method # for building binary packets given a signature. # # Writing to a buffer always appends to the end, regardless of where the # read cursor is. Reading, on the other hand, always begins at the first # byte of the buffer and increments the read cursor, with subsequent reads # taking up where the last left off. # # As a consumer of the Net::SSH library, you will rarely come into contact # with these buffer objects directly, but it could happen. Also, if you # are ever implementing a protocol on top of SSH (e.g. SFTP), this buffer # class can be quite handy. class Buffer # This is a convenience method for creating and populating a new buffer # from a single command. The arguments must be even in length, with the # first of each pair of arguments being a symbol naming the type of the # data that follows. If the type is :raw, the value is written directly # to the hash. # # b = Buffer.from(:byte, 1, :string, "hello", :raw, "\1\2\3\4") # #-> "\1\0\0\0\5hello\1\2\3\4" # # The supported data types are: # # * :raw => write the next value verbatim (#write) # * :int64 => write an 8-byte integer (#write_int64) # * :long => write a 4-byte integer (#write_long) # * :byte => write a single byte (#write_byte) # * :string => write a 4-byte length followed by character data (#write_string) # * :mstring => same as string, but caller cannot resuse the string, avoids potential duplication (#write_moved) # * :bool => write a single byte, interpreted as a boolean (#write_bool) # * :bignum => write an SSH-encoded bignum (#write_bignum) # * :key => write an SSH-encoded key value (#write_key) # # Any of these, except for :raw, accepts an Array argument, to make it # easier to write multiple values of the same type in a briefer manner. def self.from(*args) raise ArgumentError, "odd number of arguments given" unless args.length % 2 == 0 buffer = new 0.step(args.length-1, 2) do |index| type = args[index] value = args[index+1] if type == :raw buffer.append(value.to_s) elsif Array === value buffer.send("write_#{type}", *value) else buffer.send("write_#{type}", value) end end buffer end # exposes the raw content of the buffer attr_reader :content # the current position of the pointer in the buffer attr_accessor :position # Creates a new buffer, initialized to the given content. The position # is initialized to the beginning of the buffer. def initialize(content="") @content = content.to_s @position = 0 end # Returns the length of the buffer's content. def length @content.length end # Returns the number of bytes available to be read (e.g., how many bytes # remain between the current position and the end of the buffer). def available length - position end # Returns a copy of the buffer's content. def to_s (@content || "").dup end # Compares the contents of the two buffers, returning +true+ only if they # are identical in size and content. def ==(buffer) to_s == buffer.to_s end # Returns +true+ if the buffer contains no data (e.g., it is of zero length). def empty? @content.empty? end # Resets the pointer to the start of the buffer. Subsequent reads will # begin at position 0. def reset! @position = 0 end # Returns true if the pointer is at the end of the buffer. Subsequent # reads will return nil, in this case. def eof? @position >= length end # Resets the buffer, making it empty. Also, resets the read position to # 0. def clear! @content = "" @position = 0 end # Consumes n bytes from the buffer, where n is the current position # unless otherwise specified. This is useful for removing data from the # buffer that has previously been read, when you are expecting more data # to be appended. It helps to keep the size of buffers down when they # would otherwise tend to grow without bound. # # Returns the buffer object itself. def consume!(n=position) if n >= length # optimize for a fairly common case clear! elsif n > 0 @content = @content[n..-1] || "" @position -= n @position = 0 if @position < 0 end self end # Appends the given text to the end of the buffer. Does not alter the # read position. Returns the buffer object itself. def append(text) @content << text self end # Returns all text from the current pointer to the end of the buffer as # a new Net::SSH::Buffer object. def remainder_as_buffer Buffer.new(@content[@position..-1]) end # Reads all data up to and including the given pattern, which may be a # String, Fixnum, or Regexp and is interpreted exactly as String#index # does. Returns nil if nothing matches. Increments the position to point # immediately after the pattern, if it does match. Returns all data up to # and including the text that matched the pattern. def read_to(pattern) index = @content.index(pattern, @position) or return nil length = case pattern when String then pattern.length when Integer then 1 when Regexp then $&.length end index && read(index+length) end # Reads and returns the next +count+ bytes from the buffer, starting from # the read position. If +count+ is +nil+, this will return all remaining # text in the buffer. This method will increment the pointer. def read(count=nil) count ||= length count = length - @position if @position + count > length @position += count @content[@position-count, count] end # Reads (as #read) and returns the given number of bytes from the buffer, # and then consumes (as #consume!) all data up to the new read position. def read!(count=nil) data = read(count) consume! data end # Calls block(self) until the buffer is empty, and returns all results. def read_all(&block) Enumerator.new { |e| e << yield(self) until eof? }.to_a end # Return the next 8 bytes as a 64-bit integer (in network byte order). # Returns nil if there are less than 8 bytes remaining to be read in the # buffer. def read_int64 hi = read_long or return nil lo = read_long or return nil return (hi << 32) + lo end # Return the next four bytes as a long integer (in network byte order). # Returns nil if there are less than 4 bytes remaining to be read in the # buffer. def read_long b = read(4) or return nil b.unpack("N").first end # Read and return the next byte in the buffer. Returns nil if called at # the end of the buffer. def read_byte b = read(1) or return nil b.getbyte(0) end # Read and return an SSH2-encoded string. The string starts with a long # integer that describes the number of bytes remaining in the string. # Returns nil if there are not enough bytes to satisfy the request. def read_string length = read_long or return nil read(length) end # Read a single byte and convert it into a boolean, using 'C' rules # (i.e., zero is false, non-zero is true). def read_bool b = read_byte or return nil b != 0 end # Read a bignum (OpenSSL::BN) from the buffer, in SSH2 format. It is # essentially just a string, which is reinterpreted to be a bignum in # binary format. def read_bignum data = read_string return unless data OpenSSL::BN.new(data, 2) end # Read a key from the buffer. The key will start with a string # describing its type. The remainder of the key is defined by the # type that was read. def read_key type = read_string return (type ? read_keyblob(type) : nil) end # Read a keyblob of the given type from the buffer, and return it as # a key. Only RSA, DSA, and ECDSA keys are supported. def read_keyblob(type) case type when /^(.*)-cert-v01@openssh\.com$/ key = Net::SSH::Authentication::Certificate.read_certblob(self, $1) when /^ssh-dss$/ key = OpenSSL::PKey::DSA.new if key.respond_to?(:set_pqg) key.set_pqg(read_bignum, read_bignum, read_bignum) else key.p = read_bignum key.q = read_bignum key.g = read_bignum end if key.respond_to?(:set_key) key.set_key(read_bignum, nil) else key.pub_key = read_bignum end when /^ssh-rsa$/ key = OpenSSL::PKey::RSA.new if key.respond_to?(:set_key) e = read_bignum n = read_bignum key.set_key(n, e, nil) else key.e = read_bignum key.n = read_bignum end when /^ssh-ed25519$/ Net::SSH::Authentication::ED25519Loader.raiseUnlessLoaded("unsupported key type `#{type}'") key = Net::SSH::Authentication::ED25519::PubKey.read_keyblob(self) when /^ecdsa\-sha2\-(\w*)$/ unless defined?(OpenSSL::PKey::EC) raise NotImplementedError, "unsupported key type `#{type}'" else begin key = OpenSSL::PKey::EC.read_keyblob($1, self) rescue OpenSSL::PKey::ECError raise NotImplementedError, "unsupported key type `#{type}'" end end else raise NotImplementedError, "unsupported key type `#{type}'" end return key end # Reads the next string from the buffer, and returns a new Buffer # object that wraps it. def read_buffer Buffer.new(read_string) end # Writes the given data literally into the string. Does not alter the # read position. Returns the buffer object. def write(*data) data.each { |datum| @content << datum.dup.force_encoding('BINARY') } self end # Optimized version of write where the caller gives up ownership of string # to the method. This way we can mutate the string. def write_moved(string) @content << string.force_encoding('BINARY') self end # Writes each argument to the buffer as a network-byte-order-encoded # 64-bit integer (8 bytes). Does not alter the read position. Returns the # buffer object. def write_int64(*n) n.each do |i| hi = (i >> 32) & 0xFFFFFFFF lo = i & 0xFFFFFFFF @content << [hi, lo].pack("N2") end self end # Writes each argument to the buffer as a network-byte-order-encoded # long (4-byte) integer. Does not alter the read position. Returns the # buffer object. def write_long(*n) @content << n.pack("N*") self end # Writes each argument to the buffer as a byte. Does not alter the read # position. Returns the buffer object. def write_byte(*n) n.each { |b| @content << b.chr } self end # Writes each argument to the buffer as an SSH2-encoded string. Each # string is prefixed by its length, encoded as a 4-byte long integer. # Does not alter the read position. Returns the buffer object. def write_string(*text) text.each do |string| s = string.to_s write_long(s.bytesize) write(s) end self end # Writes each argument to the buffer as an SSH2-encoded string. Each # string is prefixed by its length, encoded as a 4-byte long integer. # Does not alter the read position. Returns the buffer object. # Might alter arguments see write_moved def write_mstring(*text) text.each do |string| s = string.to_s write_long(s.bytesize) write_moved(s) end self end # Writes each argument to the buffer as a (C-style) boolean, with 1 # meaning true, and 0 meaning false. Does not alter the read position. # Returns the buffer object. def write_bool(*b) b.each { |v| @content << (v ? "\1" : "\0") } self end # Writes each argument to the buffer as a bignum (SSH2-style). No # checking is done to ensure that the arguments are, in fact, bignums. # Does not alter the read position. Returns the buffer object. def write_bignum(*n) @content << n.map { |b| b.to_ssh }.join self end # Writes the given arguments to the buffer as SSH2-encoded keys. Does not # alter the read position. Returns the buffer object. def write_key(*key) key.each { |k| append(k.to_blob) } self end end end; end; net-ssh-4.2.0/lib/net/ssh/buffered_io.rb000066400000000000000000000140331315376572000200370ustar00rootroot00000000000000require 'net/ssh/buffer' require 'net/ssh/loggable' require 'net/ssh/ruby_compat' module Net; module SSH # This module is used to extend sockets and other IO objects, to allow # them to be buffered for both read and write. This abstraction makes it # quite easy to write a select-based event loop # (see Net::SSH::Connection::Session#listen_to). # # The general idea is that instead of calling #read directly on an IO that # has been extended with this module, you call #fill (to add pending input # to the internal read buffer), and then #read_available (to read from that # buffer). Likewise, you don't call #write directly, you call #enqueue to # add data to the write buffer, and then #send_pending or #wait_for_pending_sends # to actually send the data across the wire. # # In this way you can easily use the object as an argument to IO.select, # calling #fill when it is available for read, or #send_pending when it is # available for write, and then call #enqueue and #read_available during # the idle times. # # socket = TCPSocket.new(address, port) # socket.extend(Net::SSH::BufferedIo) # # ssh.listen_to(socket) # # ssh.loop do # if socket.available > 0 # puts socket.read_available # socket.enqueue("response\n") # end # end # # Note that this module must be used to extend an instance, and should not # be included in a class. If you do want to use it via an include, then you # must make sure to invoke the private #initialize_buffered_io method in # your class' #initialize method: # # class Foo < IO # include Net::SSH::BufferedIo # # def initialize # initialize_buffered_io # # ... # end # end module BufferedIo include Loggable # Called when the #extend is called on an object, with this module as the # argument. It ensures that the modules instance variables are all properly # initialized. def self.extended(object) #:nodoc: # need to use __send__ because #send is overridden in Socket object.__send__(:initialize_buffered_io) end # Tries to read up to +n+ bytes of data from the remote end, and appends # the data to the input buffer. It returns the number of bytes read, or 0 # if no data was available to be read. def fill(n=8192) input.consume! data = recv(n) debug { "read #{data.length} bytes" } input.append(data) return data.length rescue EOFError => e @input_errors << e return 0 end # Read up to +length+ bytes from the input buffer. If +length+ is nil, # all available data is read from the buffer. (See #available.) def read_available(length=nil) input.read(length || available) end # Returns the number of bytes available to be read from the input buffer. # (See #read_available.) def available input.available end # Enqueues data in the output buffer, to be written when #send_pending # is called. Note that the data is _not_ sent immediately by this method! def enqueue(data) output.append(data) end # Returns +true+ if there is data waiting in the output buffer, and # +false+ otherwise. def pending_write? output.length > 0 end # Sends as much of the pending output as possible. Returns +true+ if any # data was sent, and +false+ otherwise. def send_pending if output.length > 0 sent = send(output.to_s, 0) debug { "sent #{sent} bytes" } output.consume!(sent) return sent > 0 else return false end end # Calls #send_pending repeatedly, if necessary, blocking until the output # buffer is empty. def wait_for_pending_sends send_pending while output.length > 0 result = Net::SSH::Compat.io_select(nil, [self]) or next next unless result[1].any? send_pending end end public # these methods are primarily for use in tests def write_buffer #:nodoc: output.to_s end def read_buffer #:nodoc: input.to_s end private #-- # Can't use attr_reader here (after +private+) without incurring the # wrath of "ruby -w". We hates it. #++ def input; @input; end def output; @output; end # Initializes the intput and output buffers for this object. This method # is called automatically when the module is mixed into an object via # Object#extend (see Net::SSH::BufferedIo.extended), but must be called # explicitly in the +initialize+ method of any class that uses # Module#include to add this module. def initialize_buffered_io @input = Net::SSH::Buffer.new @input_errors = [] @output = Net::SSH::Buffer.new @output_errors = [] end end # Fixes for two issues by Miklós Fazekas: # # * if client closes a forwarded connection, but the server is # reading, net-ssh terminates with IOError socket closed. # * if client force closes (RST) a forwarded connection, but # server is reading, net-ssh terminates with [an exception] # # See: # # http://net-ssh.lighthouseapp.com/projects/36253/tickets/7 # http://github.com/net-ssh/net-ssh/tree/portfwfix # module ForwardedBufferedIo def fill(n=8192) begin super(n) rescue Errno::ECONNRESET => e debug { "connection was reset => shallowing exception:#{e}" } return 0 rescue IOError => e if e.message =~ /closed/ then debug { "connection was reset => shallowing exception:#{e}" } return 0 else raise end end end def send_pending begin super rescue Errno::ECONNRESET => e debug { "connection was reset => shallowing exception:#{e}" } return 0 rescue IOError => e if e.message =~ /closed/ then debug { "connection was reset => shallowing exception:#{e}" } return 0 else raise end end end end end; end net-ssh-4.2.0/lib/net/ssh/config.rb000066400000000000000000000300321315376572000170300ustar00rootroot00000000000000module Net; module SSH # The Net::SSH::Config class is used to parse OpenSSH configuration files, # and translates that syntax into the configuration syntax that Net::SSH # understands. This lets Net::SSH scripts read their configuration (to # some extent) from OpenSSH configuration files (~/.ssh/config, /etc/ssh_config, # and so forth). # # Only a subset of OpenSSH configuration options are understood: # # * ChallengeResponseAuthentication => maps to the :auth_methods option challenge-response (then coleasced into keyboard-interactive) # * KbdInteractiveAuthentication => maps to the :auth_methods keyboard-interactive # * Ciphers => maps to the :encryption option # * Compression => :compression # * CompressionLevel => :compression_level # * ConnectTimeout => maps to the :timeout option # * ForwardAgent => :forward_agent # * GlobalKnownHostsFile => :global_known_hosts_file # * HostBasedAuthentication => maps to the :auth_methods option # * HostKeyAlgorithms => maps to :host_key option # * HostKeyAlias => :host_key_alias # * HostName => :host_name # * IdentityFile => maps to the :keys option # * IdentitiesOnly => :keys_only # * Macs => maps to the :hmac option # * PasswordAuthentication => maps to the :auth_methods option password # * Port => :port # * PreferredAuthentications => maps to the :auth_methods option # * ProxyCommand => maps to the :proxy option # * ProxyJump => maps to the :proxy option # * PubKeyAuthentication => maps to the :auth_methods option # * RekeyLimit => :rekey_limit # * User => :user # * UserKnownHostsFile => :user_known_hosts_file # * NumberOfPasswordPrompts => :number_of_password_prompts # # Note that you will never need to use this class directly--you can control # whether the OpenSSH configuration files are read by passing the :config # option to Net::SSH.start. (They are, by default.) class Config class << self @@default_files = %w(~/.ssh/config /etc/ssh_config /etc/ssh/ssh_config) # The following defaults follow the openssh client ssh_config defaults. # http://lwn.net/Articles/544640/ # "hostbased" is off and "none" is not supported but we allow it since # it's used by some clients to query the server for allowed auth methods @@default_auth_methods = %w(none publickey password keyboard-interactive) # Returns an array of locations of OpenSSH configuration files # to parse by default. def default_files @@default_files end def default_auth_methods @@default_auth_methods end # Loads the configuration data for the given +host+ from all of the # given +files+ (defaulting to the list of files returned by # #default_files), translates the resulting hash into the options # recognized by Net::SSH, and returns them. def for(host, files=expandable_default_files) translate(files.inject({}) { |settings, file| load(file, host, settings) }) end # Load the OpenSSH configuration settings in the given +file+ for the # given +host+. If +settings+ is given, the options are merged into # that hash, with existing values taking precedence over newly parsed # ones. Returns a hash containing the OpenSSH options. (See # #translate for how to convert the OpenSSH options into Net::SSH # options.) def load(path, host, settings={}, base_dir = nil) file = File.expand_path(path) base_dir ||= File.dirname(file) return settings unless File.readable?(file) globals = {} host_matched = false seen_host = false IO.foreach(file) do |line| next if line =~ /^\s*(?:#.*)?$/ if line =~ /^\s*(\S+)\s*=(.*)$/ key, value = $1, $2 else key, value = line.strip.split(/\s+/, 2) end # silently ignore malformed entries next if value.nil? key.downcase! value = $1 if value =~ /^"(.*)"$/ value = case value.strip when /^\d+$/ then value.to_i when /^no$/i then false when /^yes$/i then true else value end if key == 'host' # Support "Host host1 host2 hostN". # See http://github.com/net-ssh/net-ssh/issues#issue/6 negative_hosts, positive_hosts = value.to_s.split(/\s+/).partition { |h| h.start_with?('!') } # Check for negative patterns first. If the host matches, that overrules any other positive match. # The host substring code is used to strip out the starting "!" so the regexp will be correct. negative_matched = negative_hosts.any? { |h| host =~ pattern2regex(h[1..-1]) } if negative_matched host_matched = false else host_matched = positive_hosts.any? { |h| host =~ pattern2regex(h) } end seen_host = true settings[key] = host elsif !seen_host case key when 'identityfile' (globals[key] ||= []) << value when 'include' included_file_paths(base_dir, value).each do |file_path| globals = load(file_path, host, globals, base_dir) end else globals[key] = value unless settings.key?(key) end elsif host_matched case key when 'identityfile' (settings[key] ||= []) << value when 'include' included_file_paths(base_dir, value).each do |file_path| settings = load(file_path, host, settings, base_dir) end else settings[key] = value unless settings.key?(key) end end end if globals settings = globals.merge(settings) do |key, oldval, newval| case key when 'identityfile' oldval + newval else newval end end end return settings end # Given a hash of OpenSSH configuration options, converts them into # a hash of Net::SSH options. Unrecognized options are ignored. The # +settings+ hash must have Strings for keys, all downcased, and # the returned hash will have Symbols for keys. def translate(settings) auth_methods = default_auth_methods.clone (auth_methods << 'challenge-response').uniq! ret = settings.inject({auth_methods: auth_methods}) do |hash, (key, value)| translate_config_key(hash, key.to_sym, value, settings) hash end merge_challenge_response_with_keyboard_interactive(ret) end # Filters default_files down to the files that are expandable. def expandable_default_files default_files.keep_if do |path| begin File.expand_path(path) true rescue ArgumentError false end end end private def translate_config_key(hash, key, value, settings) rename = { bindaddress: :bind_address, compression: :compression, compressionlevel: :compression_level, connecttimeout: :timeout, forwardagent: :forward_agent, identitiesonly: :keys_only, globalknownhostsfile: :global_known_hosts_file, hostkeyalias: :host_key_alias, identityfile: :keys, port: :port, user: :user, userknownhostsfile: :user_known_hosts_file } case key when :ciphers hash[:encryption] = value.split(/,/) when :hostbasedauthentication if value (hash[:auth_methods] << "hostbased").uniq! else hash[:auth_methods].delete("hostbased") end when :hostkeyalgorithms hash[:host_key] = value.split(/,/) when :hostname hash[:host_name] = value.gsub(/%h/, settings['host']) when :macs hash[:hmac] = value.split(/,/) when :serveralivecountmax hash[:keepalive_maxcount] = value.to_i if value when :serveraliveinterval if value && value.to_i > 0 hash[:keepalive] = true hash[:keepalive_interval] = value.to_i else hash[:keepalive] = false end when :passwordauthentication if value (hash[:auth_methods] << 'password').uniq! else hash[:auth_methods].delete('password') end when :challengeresponseauthentication if value (hash[:auth_methods] << 'challenge-response').uniq! else hash[:auth_methods].delete('challenge-response') end when :kbdinteractiveauthentication if value (hash[:auth_methods] << 'keyboard-interactive').uniq! else hash[:auth_methods].delete('keyboard-interactive') end when :preferredauthentications hash[:auth_methods] = value.split(/,/) # TODO we should place to preferred_auth_methods rather than auth_methods when :proxycommand if value and !(value =~ /^none$/) require 'net/ssh/proxy/command' hash[:proxy] = Net::SSH::Proxy::Command.new(value) end when :proxyjump if value require 'net/ssh/proxy/jump' hash[:proxy] = Net::SSH::Proxy::Jump.new(value) end when :pubkeyauthentication if value (hash[:auth_methods] << 'publickey').uniq! else hash[:auth_methods].delete('publickey') end when :rekeylimit hash[:rekey_limit] = interpret_size(value) when :sendenv multi_send_env = value.to_s.split(/\s+/) hash[:send_env] = multi_send_env.map { |e| Regexp.new pattern2regex(e).source, false } when :numberofpasswordprompts hash[:number_of_password_prompts] = value.to_i when *rename.keys hash[rename[key]] = value end end # Converts an ssh_config pattern into a regex for matching against # host names. def pattern2regex(pattern) tail = pattern prefix = "" while !tail.empty? do head,sep,tail = tail.partition(/[\*\?]/) prefix = prefix + Regexp.quote(head) case sep when '*' prefix += '.*' when '?' prefix += '.' when '' else fail "Unpexpcted sep:#{sep}" end end Regexp.new("^" + prefix + "$", true) end # Converts the given size into an integer number of bytes. def interpret_size(size) case size when /k$/i then size.to_i * 1024 when /m$/i then size.to_i * 1024 * 1024 when /g$/i then size.to_i * 1024 * 1024 * 1024 else size.to_i end end def merge_challenge_response_with_keyboard_interactive(hash) if hash[:auth_methods].include?('challenge-response') hash[:auth_methods].delete('challenge-response') (hash[:auth_methods] << 'keyboard-interactive').uniq! end hash end def included_file_paths(base_dir, config_paths) tokenize_config_value(config_paths).flat_map do |path| Dir.glob(File.expand_path(path, base_dir)).select { |f| File.file?(f) } end end # Tokenize string into tokens. # A token is a word or a quoted sequence of words, separated by whitespaces. def tokenize_config_value(str) str.scan(/([^"\s]+)?(?:"([^"]+)")?\s*/).map(&:join) end end end end; end net-ssh-4.2.0/lib/net/ssh/connection/000077500000000000000000000000001315376572000173775ustar00rootroot00000000000000net-ssh-4.2.0/lib/net/ssh/connection/channel.rb000066400000000000000000000647021315376572000213450ustar00rootroot00000000000000require 'net/ssh/loggable' require 'net/ssh/connection/constants' require 'net/ssh/connection/term' module Net; module SSH; module Connection # The channel abstraction. Multiple "channels" can be multiplexed onto a # single SSH channel, each operating independently and seemingly in parallel. # This class represents a single such channel. Most operations performed # with the Net::SSH library will involve using one or more channels. # # Channels are intended to be used asynchronously. You request that one be # opened (via Connection::Session#open_channel), and when it is opened, your # callback is invoked. Then, you set various other callbacks on the newly # opened channel, which are called in response to the corresponding events. # Programming with Net::SSH works best if you think of your programs as # state machines. Complex programs are best implemented as objects that # wrap a channel. See Net::SCP and Net::SFTP for examples of how complex # state machines can be built on top of the SSH protocol. # # ssh.open_channel do |channel| # channel.exec("/invoke/some/command") do |ch, success| # abort "could not execute command" unless success # # channel.on_data do |ch, data| # puts "got stdout: #{data}" # channel.send_data "something for stdin\n" # end # # channel.on_extended_data do |ch, type, data| # puts "got stderr: #{data}" # end # # channel.on_close do |ch| # puts "channel is closing!" # end # end # end # # ssh.loop # # Channels also have a basic hash-like interface, that allows programs to # store arbitrary state information on a channel object. This helps simplify # the writing of state machines, especially when you may be juggling # multiple open channels at the same time. # # Note that data sent across SSH channels are governed by maximum packet # sizes and maximum window sizes. These details are managed internally # by Net::SSH::Connection::Channel, so you may remain blissfully ignorant # if you so desire, but you can always inspect the current maximums, as # well as the remaining window size, using the reader attributes for those # values. class Channel include Constants, Loggable # The local id for this channel, assigned by the Net::SSH::Connection::Session instance. attr_reader :local_id # The remote id for this channel, assigned by the remote host. attr_reader :remote_id # The type of this channel, usually "session". attr_reader :type # The underlying Net::SSH::Connection::Session instance that supports this channel. attr_reader :connection # The maximum packet size that the local host can receive. attr_reader :local_maximum_packet_size # The maximum amount of data that the local end of this channel can # receive. This is a total, not per-packet. attr_reader :local_maximum_window_size # The maximum packet size that the remote host can receive. attr_reader :remote_maximum_packet_size # The maximum amount of data that the remote end of this channel can # receive. This is a total, not per-packet. attr_reader :remote_maximum_window_size # This is the remaining window size on the local end of this channel. When # this reaches zero, no more data can be received. attr_reader :local_window_size # This is the remaining window size on the remote end of this channel. When # this reaches zero, no more data can be sent. attr_reader :remote_window_size # A hash of properties for this channel. These can be used to store state # information about this channel. See also #[] and #[]=. attr_reader :properties # The output buffer for this channel. Data written to the channel is # enqueued here, to be written as CHANNEL_DATA packets during each pass of # the event loop. See Connection::Session#process and #enqueue_pending_output. attr_reader :output #:nodoc: # The list of pending requests. Each time a request is sent which requires # a reply, the corresponding callback is pushed onto this queue. As responses # arrive, they are shifted off the front and handled. attr_reader :pending_requests #:nodoc: # Instantiates a new channel on the given connection, of the given type, # and with the given id. If a block is given, it will be remembered until # the channel is confirmed open by the server, and will be invoked at # that time (see #do_open_confirmation). # # This also sets the default maximum packet size and maximum window size. def initialize(connection, type, local_id, max_pkt_size = 0x8000, max_win_size = 0x20000, &on_confirm_open) self.logger = connection.logger @connection = connection @type = type @local_id = local_id @local_maximum_packet_size = max_pkt_size @local_window_size = @local_maximum_window_size = max_win_size @on_confirm_open = on_confirm_open @output = Buffer.new @properties = {} @pending_requests = [] @on_open_failed = @on_data = @on_extended_data = @on_process = @on_close = @on_eof = nil @on_request = {} @closing = @eof = @sent_eof = @local_closed = @remote_closed = false end # A shortcut for accessing properties of the channel (see #properties). def [](name) @properties[name] end # A shortcut for setting properties of the channel (see #properties). def []=(name, value) @properties[name] = value end # Syntactic sugar for executing a command. Sends a channel request asking # that the given command be invoked. If the block is given, it will be # called when the server responds. The first parameter will be the # channel, and the second will be true or false, indicating whether the # request succeeded or not. In this case, success means that the command # is being executed, not that it has completed, and failure means that the # command altogether failed to be executed. # # channel.exec "ls -l /home" do |ch, success| # if success # puts "command has begun executing..." # # this is a good place to hang callbacks like #on_data... # else # puts "alas! the command could not be invoked!" # end # end def exec(command, &block) send_channel_request("exec", :string, command, &block) end # Syntactic sugar for requesting that a subsystem be started. Subsystems # are a way for other protocols (like SFTP) to be run, using SSH as # the transport. Generally, you'll never need to call this directly unless # you are the implementor of something that consumes an SSH subsystem, like # SFTP. # # channel.subsystem("sftp") do |ch, success| # if success # puts "subsystem successfully started" # else # puts "subsystem could not be started" # end # end def subsystem(subsystem, &block) send_channel_request("subsystem", :string, subsystem, &block) end # Syntactic sugar for setting an environment variable in the remote # process' environment. Note that for security reasons, the server may # refuse to set certain environment variables, or all, at the server's # discretion. If you are connecting to an OpenSSH server, you will # need to update the AcceptEnv setting in the sshd_config to include the # environment variables you want to send. # # channel.env "PATH", "/usr/local/bin" def env(variable_name, variable_value, &block) send_channel_request("env", :string, variable_name, :string, variable_value, &block) end # A hash of the valid PTY options (see #request_pty). VALID_PTY_OPTIONS = { term: "xterm", chars_wide: 80, chars_high: 24, pixels_wide: 640, pixels_high: 480, modes: {} } # Requests that a pseudo-tty (or "pty") be made available for this channel. # This is useful when you want to invoke and interact with some kind of # screen-based program (e.g., vim, or some menuing system). # # Note, that without a pty some programs (e.g. sudo, or subversion) on # some systems, will not be able to run interactively, and will error # instead of prompt if they ever need some user interaction. # # Note, too, that when a pty is requested, user's shell configuration # scripts (.bashrc and such) are not run by default, whereas they are # run when a pty is not present. # # channel.request_pty do |ch, success| # if success # puts "pty successfully obtained" # else # puts "could not obtain pty" # end # end def request_pty(opts={}, &block) extra = opts.keys - VALID_PTY_OPTIONS.keys raise ArgumentError, "invalid option(s) to request_pty: #{extra.inspect}" if extra.any? opts = VALID_PTY_OPTIONS.merge(opts) modes = opts[:modes].inject(Buffer.new) do |memo, (mode, data)| memo.write_byte(mode).write_long(data) end # mark the end of the mode opcode list with a 0 byte modes.write_byte(0) send_channel_request("pty-req", :string, opts[:term], :long, opts[:chars_wide], :long, opts[:chars_high], :long, opts[:pixels_wide], :long, opts[:pixels_high], :string, modes.to_s, &block) end # Sends data to the channel's remote endpoint. This usually has the # effect of sending the given string to the remote process' stdin stream. # Note that it does not immediately send the data across the channel, # but instead merely appends the given data to the channel's output buffer, # preparatory to being packaged up and sent out the next time the connection # is accepting data. (A connection might not be accepting data if, for # instance, it has filled its data window and has not yet been resized by # the remote end-point.) # # This will raise an exception if the channel has previously declared # that no more data will be sent (see #eof!). # # channel.send_data("the password\n") def send_data(data) raise EOFError, "cannot send data if channel has declared eof" if eof? output.append(data.to_s) end # Returns true if the channel exists in the channel list of the session, # and false otherwise. This can be used to determine whether a channel has # been closed or not. # # ssh.loop { channel.active? } def active? connection.channels.key?(local_id) end # Runs the SSH event loop until the channel is no longer active. This is # handy for blocking while you wait for some channel to finish. # # channel.exec("grep ...") { ... } # channel.wait def wait connection.loop { active? } end # True if close() has been called; NOTE: if the channel has data waiting to # be sent then the channel will close after all the data is sent. See # closed?() to determine if we have actually sent CHANNEL_CLOSE to server. # This may be true for awhile before closed? returns true if we are still # sending buffered output to server. def closing? @closing end # True if we have sent CHANNEL_CLOSE to the remote server. def local_closed? @local_closed end def remote_closed? @remote_closed end def remote_closed! @remote_closed = true end # Requests that the channel be closed. It only marks the channel to be closed # the CHANNEL_CLOSE message will be sent from event loop def close return if @closing @closing = true end # Returns true if the local end of the channel has declared that no more # data is forthcoming (see #eof!). Trying to send data via #send_data when # this is true will result in an exception being raised. def eof? @eof end # Tells the remote end of the channel that no more data is forthcoming # from this end of the channel. The remote end may still send data. # The CHANNEL_EOF packet will be sent once the output buffer is empty. def eof! return if eof? @eof = true end # If an #on_process handler has been set up, this will cause it to be # invoked (passing the channel itself as an argument). It also causes all # pending output to be enqueued as CHANNEL_DATA packets (see #enqueue_pending_output). def process @on_process.call(self) if @on_process enqueue_pending_output if @eof and not @sent_eof and output.empty? and remote_id and not @local_closed connection.send_message(Buffer.from(:byte, CHANNEL_EOF, :long, remote_id)) @sent_eof = true end if @closing and not @local_closed and output.empty? and remote_id connection.send_message(Buffer.from(:byte, CHANNEL_CLOSE, :long, remote_id)) @local_closed = true connection.cleanup_channel(self) end end # Registers a callback to be invoked when data packets are received by the # channel. The callback is called with the channel as the first argument, # and the data as the second. # # channel.on_data do |ch, data| # puts "got data: #{data.inspect}" # end # # Data received this way is typically the data written by the remote # process to its +stdout+ stream. def on_data(&block) old, @on_data = @on_data, block old end # Registers a callback to be invoked when extended data packets are received # by the channel. The callback is called with the channel as the first # argument, the data type (as an integer) as the second, and the data as # the third. Extended data is almost exclusively used to send +stderr+ data # (+type+ == 1). Other extended data types are not defined by the SSH # protocol. # # channel.on_extended_data do |ch, type, data| # puts "got stderr: #{data.inspect}" # end def on_extended_data(&block) old, @on_extended_data = @on_extended_data, block old end # Registers a callback to be invoked for each pass of the event loop for # this channel. There are no guarantees on timeliness in the event loop, # but it will be called roughly once for each packet received by the # connection (not the channel). This callback is invoked with the channel # as the sole argument. # # Here's an example that accumulates the channel data into a variable on # the channel itself, and displays individual lines in the input one # at a time when the channel is processed: # # channel[:data] = "" # # channel.on_data do |ch, data| # channel[:data] << data # end # # channel.on_process do |ch| # if channel[:data] =~ /^.*?\n/ # puts $& # channel[:data] = $' # end # end def on_process(&block) old, @on_process = @on_process, block old end # Registers a callback to be invoked when the server acknowledges that a # channel is closed. This is invoked with the channel as the sole argument. # # channel.on_close do |ch| # puts "remote end is closing!" # end def on_close(&block) old, @on_close = @on_close, block old end # Registers a callback to be invoked when the server indicates that no more # data will be sent to the channel (although the channel can still send # data to the server). The channel is the sole argument to the callback. # # channel.on_eof do |ch| # puts "remote end is done sending data" # end def on_eof(&block) old, @on_eof = @on_eof, block old end # Registers a callback to be invoked when the server was unable to open # the requested channel. The channel itself will be passed to the block, # along with the integer "reason code" for the failure, and a textual # description of the failure from the server. # # channel = session.open_channel do |ch| # # .. # end # # channel.on_open_failed { |ch, code, desc| ... } def on_open_failed(&block) old, @on_open_failed = @on_open_failed, block old end # Registers a callback to be invoked when a channel request of the given # type is received. The callback will receive the channel as the first # argument, and the associated (unparsed) data as the second. The data # will be a Net::SSH::Buffer that you will need to parse, yourself, # according to the kind of request you are watching. # # By default, if the request wants a reply, Net::SSH will send a # CHANNEL_SUCCESS response for any request that was handled by a registered # callback, and CHANNEL_FAILURE for any that wasn't, but if you want your # registered callback to result in a CHANNEL_FAILURE response, just raise # Net::SSH::ChannelRequestFailed. # # Some common channel requests that your programs might want to listen # for are: # # * "exit-status" : the exit status of the remote process will be reported # as a long integer in the data buffer, which you can grab via # data.read_long. # * "exit-signal" : if the remote process died as a result of a signal # being sent to it, the signal will be reported as a string in the # data, via data.read_string. (Not all SSH servers support this channel # request type.) # # channel.on_request "exit-status" do |ch, data| # puts "process terminated with exit status: #{data.read_long}" # end def on_request(type, &block) old, @on_request[type] = @on_request[type], block old end # Sends a new channel request with the given name. The extra +data+ # parameter must either be empty, or consist of an even number of # arguments. See Net::SSH::Buffer.from for a description of their format. # If a block is given, it is registered as a callback for a pending # request, and the packet will be flagged so that the server knows a # reply is required. If no block is given, the server will send no # response to this request. Responses, where required, will cause the # callback to be invoked with the channel as the first argument, and # either true or false as the second, depending on whether the request # succeeded or not. The meaning of "success" and "failure" in this context # is dependent on the specific request that was sent. # # channel.send_channel_request "shell" do |ch, success| # if success # puts "user shell started successfully" # else # puts "could not start user shell" # end # end # # Most channel requests you'll want to send are already wrapped in more # convenient helper methods (see #exec and #subsystem). def send_channel_request(request_name, *data, &callback) info { "sending channel request #{request_name.inspect}" } fail "Channel open not yet confirmed, please call send_channel_request(or exec) from block of open_channel" unless remote_id msg = Buffer.from(:byte, CHANNEL_REQUEST, :long, remote_id, :string, request_name, :bool, !callback.nil?, *data) connection.send_message(msg) pending_requests << callback if callback end public # these methods are public, but for Net::SSH internal use only # Enqueues pending output at the connection as CHANNEL_DATA packets. This # does nothing if the channel has not yet been confirmed open (see # #do_open_confirmation). This is called automatically by #process, which # is called from the event loop (Connection::Session#process). You will # generally not need to invoke it directly. def enqueue_pending_output #:nodoc: return unless remote_id while output.length > 0 length = output.length length = remote_window_size if length > remote_window_size length = remote_maximum_packet_size if length > remote_maximum_packet_size if length > 0 connection.send_message(Buffer.from(:byte, CHANNEL_DATA, :long, remote_id, :string, output.read(length))) output.consume! @remote_window_size -= length else break end end end # Invoked when the server confirms that a channel has been opened. # The remote_id is the id of the channel as assigned by the remote host, # and max_window and max_packet are the maximum window and maximum # packet sizes, respectively. If an open-confirmation callback was # given when the channel was created, it is invoked at this time with # the channel itself as the sole argument. def do_open_confirmation(remote_id, max_window, max_packet) #:nodoc: @remote_id = remote_id @remote_window_size = @remote_maximum_window_size = max_window @remote_maximum_packet_size = max_packet connection.forward.agent(self) if connection.options[:forward_agent] && type == "session" forward_local_env(connection.options[:send_env]) if connection.options[:send_env] @on_confirm_open.call(self) if @on_confirm_open end # Invoked when the server failed to open the channel. If an #on_open_failed # callback was specified, it will be invoked with the channel, reason code, # and description as arguments. Otherwise, a ChannelOpenFailed exception # will be raised. def do_open_failed(reason_code, description) if @on_open_failed @on_open_failed.call(self, reason_code, description) else raise ChannelOpenFailed.new(reason_code, description) end end # Invoked when the server sends a CHANNEL_WINDOW_ADJUST packet, and # causes the remote window size to be adjusted upwards by the given # number of bytes. This has the effect of allowing more data to be sent # from the local end to the remote end of the channel. def do_window_adjust(bytes) #:nodoc: @remote_maximum_window_size += bytes @remote_window_size += bytes end # Invoked when the server sends a channel request. If any #on_request # callback has been registered for the specific type of this request, # it is invoked. If +want_reply+ is true, a packet will be sent of # either CHANNEL_SUCCESS or CHANNEL_FAILURE type. If there was no callback # to handle the request, CHANNEL_FAILURE will be sent. Otherwise, # CHANNEL_SUCCESS, unless the callback raised ChannelRequestFailed. The # callback should accept the channel as the first argument, and the # request-specific data as the second. def do_request(request, want_reply, data) #:nodoc: result = true begin callback = @on_request[request] or raise ChannelRequestFailed callback.call(self, data) rescue ChannelRequestFailed result = false end if want_reply msg = Buffer.from(:byte, result ? CHANNEL_SUCCESS : CHANNEL_FAILURE, :long, remote_id) connection.send_message(msg) end end # Invokes the #on_data callback when the server sends data to the # channel. This will reduce the available window size on the local end, # but does not actually throttle requests that come in illegally when # the window size is too small. The callback is invoked with the channel # as the first argument, and the data as the second. def do_data(data) #:nodoc: update_local_window_size(data.length) @on_data.call(self, data) if @on_data end # Invokes the #on_extended_data callback when the server sends # extended data to the channel. This will reduce the available window # size on the local end. The callback is invoked with the channel, # type, and data. def do_extended_data(type, data) update_local_window_size(data.length) @on_extended_data.call(self, type, data) if @on_extended_data end # Invokes the #on_eof callback when the server indicates that no # further data is forthcoming. The callback is invoked with the channel # as the argument. def do_eof @on_eof.call(self) if @on_eof end # Invokes the #on_close callback when the server closes a channel. # The channel is the only argument. def do_close @on_close.call(self) if @on_close end # Invokes the next pending request callback with +false+ as the second # argument. def do_failure if callback = pending_requests.shift callback.call(self, false) else error { "channel failure received with no pending request to handle it (bug?)" } end end # Invokes the next pending request callback with +true+ as the second # argument. def do_success if callback = pending_requests.shift callback.call(self, true) else error { "channel success received with no pending request to handle it (bug?)" } end end private # Runs the SSH event loop until the remote confirmed channel open # experimental api def wait_until_open_confirmed connection.loop { !remote_id } end # Updates the local window size by the given amount. If the window # size drops to less than half of the local maximum (an arbitrary # threshold), a CHANNEL_WINDOW_ADJUST message will be sent to the # server telling it that the window size has grown. def update_local_window_size(size) @local_window_size -= size if local_window_size < local_maximum_window_size/2 connection.send_message(Buffer.from(:byte, CHANNEL_WINDOW_ADJUST, :long, remote_id, :long, 0x20000)) @local_window_size += 0x20000 @local_maximum_window_size += 0x20000 end end # Gets an +Array+ of local environment variables in the remote process' # environment. # A variable name can either be described by a +Regexp+ or +String+. # # channel.forward_local_env [/^GIT_.*$/, "LANG"] def forward_local_env(env_variable_patterns) Array(env_variable_patterns).each do |env_variable_pattern| matched_variables = ENV.find_all do |env_name, _| case env_variable_pattern when Regexp then env_name =~ env_variable_pattern when String then env_name == env_variable_pattern end end matched_variables.each do |env_name, env_value| self.env(env_name, env_value) end end end end end; end; end net-ssh-4.2.0/lib/net/ssh/connection/constants.rb000066400000000000000000000014131315376572000217370ustar00rootroot00000000000000module Net; module SSH; module Connection # Definitions of constants that are specific to the connection layer of the # SSH protocol. module Constants #-- # Connection protocol generic messages #++ GLOBAL_REQUEST = 80 REQUEST_SUCCESS = 81 REQUEST_FAILURE = 82 #-- # Channel related messages #++ CHANNEL_OPEN = 90 CHANNEL_OPEN_CONFIRMATION = 91 CHANNEL_OPEN_FAILURE = 92 CHANNEL_WINDOW_ADJUST = 93 CHANNEL_DATA = 94 CHANNEL_EXTENDED_DATA = 95 CHANNEL_EOF = 96 CHANNEL_CLOSE = 97 CHANNEL_REQUEST = 98 CHANNEL_SUCCESS = 99 CHANNEL_FAILURE = 100 end end; end endnet-ssh-4.2.0/lib/net/ssh/connection/event_loop.rb000066400000000000000000000065161315376572000221060ustar00rootroot00000000000000require 'net/ssh/loggable' require 'net/ssh/ruby_compat' module Net; module SSH; module Connection # EventLoop can be shared across multiple sessions # # one issue is with blocks passed to loop, etc. # they should get current session as parameter, but in # case you're using multiple sessions in an event loop it doesnt makes sense # and we don't pass session. class EventLoop include Loggable def initialize(logger=nil) self.logger = logger @sessions = [] end def register(session) @sessions << session end # process until timeout # if a block is given a session will be removed from loop # if block returns false for that session def process(wait = nil, &block) return false unless ev_preprocess(&block) ev_select_and_postprocess(wait) end # process the event loop but only for the sepcified session def process_only(session, wait = nil) orig_sessions = @sessions begin @sessions = [session] return false unless ev_preprocess ev_select_and_postprocess(wait) ensure @sessions = orig_sessions end end # Call preprocess on each session. If block given and that # block retuns false then we exit the processing def ev_preprocess(&block) return false if block_given? && !yield(self) @sessions.each(&:ev_preprocess) return false if block_given? && !yield(self) return true end def ev_select_and_postprocess(wait) owners = {} r = [] w = [] minwait = nil @sessions.each do |session| sr,sw,actwait = session.ev_do_calculate_rw_wait(wait) minwait = actwait if actwait && (minwait.nil? || actwait < minwait) r.push(*sr) w.push(*sw) sr.each { |ri| owners[ri] = session } sw.each { |wi| owners[wi] = session } end readers, writers, = Net::SSH::Compat.io_select(r, w, nil, minwait) fired_sessions = {} if readers readers.each do |reader| session = owners[reader] (fired_sessions[session] ||= {r: [],w: []})[:r] << reader end end if writers writers.each do |writer| session = owners[writer] (fired_sessions[session] ||= {r: [],w: []})[:w] << writer end end fired_sessions.each do |s,rw| s.ev_do_handle_events(rw[:r],rw[:w]) end @sessions.each { |s| s.ev_do_postprocess(fired_sessions.key?(s)) } true end end # optimized version for a single session class SingleSessionEventLoop < EventLoop # Compatibility for original single session event loops: # we call block with session as argument def ev_preprocess(&block) return false if block_given? && !yield(@sessions.first) @sessions.each(&:ev_preprocess) return false if block_given? && !yield(@sessions.first) return true end def ev_select_and_postprocess(wait) raise "Only one session expected" unless @sessions.count == 1 session = @sessions.first sr,sw,actwait = session.ev_do_calculate_rw_wait(wait) readers, writers, = Net::SSH::Compat.io_select(sr, sw, nil, actwait) session.ev_do_handle_events(readers,writers) session.ev_do_postprocess(!((readers.nil? || readers.empty?) && (writers.nil? || writers.empty?))) end end end; end; end net-ssh-4.2.0/lib/net/ssh/connection/keepalive.rb000066400000000000000000000027261315376572000217000ustar00rootroot00000000000000require 'net/ssh/loggable' module Net; module SSH; module Connection class Keepalive include Loggable def initialize(session) @last_keepalive_sent_at = nil @unresponded_keepalive_count = 0 @session = session self.logger = session.logger end def options @session.options end def enabled? options[:keepalive] end def interval options[:keepalive_interval] || Session::DEFAULT_IO_SELECT_TIMEOUT end def should_send? return false unless enabled? return true unless @last_keepalive_sent_at Time.now - @last_keepalive_sent_at >= interval end def keepalive_maxcount (options[:keepalive_maxcount] || 3).to_i end def send_as_needed(was_events) return if was_events return unless should_send? info { "sending keepalive #{@unresponded_keepalive_count}" } @unresponded_keepalive_count += 1 @session.send_global_request("keepalive@openssh.com") { |success, response| debug { "keepalive response successful. Missed #{@unresponded_keepalive_count-1} keepalives" } @unresponded_keepalive_count = 0 } @last_keepalive_sent_at = Time.now if keepalive_maxcount > 0 && @unresponded_keepalive_count > keepalive_maxcount error { "Timeout, server #{@session.host} not responding. Missed #{@unresponded_keepalive_count-1} timeouts." } @unresponded_keepalive_count = 0 raise Net::SSH::Timeout, "Timeout, server #{@session.host} not responding." end end end end; end; end net-ssh-4.2.0/lib/net/ssh/connection/session.rb000066400000000000000000000634031315376572000214150ustar00rootroot00000000000000require 'net/ssh/loggable' require 'net/ssh/ruby_compat' require 'net/ssh/connection/channel' require 'net/ssh/connection/constants' require 'net/ssh/service/forward' require 'net/ssh/connection/keepalive' require 'net/ssh/connection/event_loop' module Net; module SSH; module Connection # A session class representing the connection service running on top of # the SSH transport layer. It manages the creation of channels (see # #open_channel), and the dispatching of messages to the various channels. # It also encapsulates the SSH event loop (via #loop and #process), # and serves as a central point-of-reference for all SSH-related services (e.g. # port forwarding, SFTP, SCP, etc.). # # You will rarely (if ever) need to instantiate this class directly; rather, # you'll almost always use Net::SSH.start to initialize a new network # connection, authenticate a user, and return a new connection session, # all in one call. # # Net::SSH.start("localhost", "user") do |ssh| # # 'ssh' is an instance of Net::SSH::Connection::Session # ssh.exec! "/etc/init.d/some_process start" # end class Session include Constants, Loggable # Default IO.select timeout threshold DEFAULT_IO_SELECT_TIMEOUT = 300 # The underlying transport layer abstraction (see Net::SSH::Transport::Session). attr_reader :transport # The map of options that were used to initialize this instance. attr_reader :options # The collection of custom properties for this instance. (See #[] and #[]=). attr_reader :properties # The map of channels, each key being the local-id for the channel. attr_reader :channels #:nodoc: # The map of listeners that the event loop knows about. See #listen_to. attr_reader :listeners #:nodoc: # The map of specialized handlers for opening specific channel types. See # #on_open_channel. attr_reader :channel_open_handlers #:nodoc: # The list of callbacks for pending requests. See #send_global_request. attr_reader :pending_requests #:nodoc: class NilChannel def initialize(session) @session = session end def method_missing(sym, *args) @session.lwarn { "ignoring request #{sym.inspect} for non-existent (closed?) channel; probably ssh server bug" } end end # Create a new connection service instance atop the given transport # layer. Initializes the listeners to be only the underlying socket object. def initialize(transport, options={}) self.logger = transport.logger @transport = transport @options = options @channel_id_counter = -1 @channels = Hash.new(NilChannel.new(self)) @listeners = { transport.socket => nil } @pending_requests = [] @channel_open_handlers = {} @on_global_request = {} @properties = (options[:properties] || {}).dup @max_pkt_size = (options.key?(:max_pkt_size) ? options[:max_pkt_size] : 0x8000) @max_win_size = (options.key?(:max_win_size) ? options[:max_win_size] : 0x20000) @keepalive = Keepalive.new(self) @event_loop = options[:event_loop] || SingleSessionEventLoop.new @event_loop.register(self) end # Retrieves a custom property from this instance. This can be used to # store additional state in applications that must manage multiple # SSH connections. def [](key) @properties[key] end # Sets a custom property for this instance. def []=(key, value) @properties[key] = value end # Returns the name of the host that was given to the transport layer to # connect to. def host transport.host end # Returns true if the underlying transport has been closed. Note that # this can be a little misleading, since if the remote server has # closed the connection, the local end will still think it is open # until the next operation on the socket. Nevertheless, this method can # be useful if you just want to know if _you_ have closed the connection. def closed? transport.closed? end # Closes the session gracefully, blocking until all channels have # successfully closed, and then closes the underlying transport layer # connection. def close info { "closing remaining channels (#{channels.length} open)" } channels.each { |id, channel| channel.close } begin loop(0.1) { channels.any? } rescue Net::SSH::Disconnect raise unless channels.empty? end transport.close end # Performs a "hard" shutdown of the connection. In general, this should # never be done, but it might be necessary (in a rescue clause, for instance, # when the connection needs to close but you don't know the status of the # underlying protocol's state). def shutdown! transport.shutdown! end # preserve a reference to Kernel#loop alias :loop_forever :loop # Returns +true+ if there are any channels currently active on this # session. By default, this will not include "invisible" channels # (such as those created by forwarding ports and such), but if you pass # a +true+ value for +include_invisible+, then those will be counted. # # This can be useful for determining whether the event loop should continue # to be run. # # ssh.loop { ssh.busy? } def busy?(include_invisible=false) if include_invisible channels.any? else channels.any? { |id, ch| !ch[:invisible] } end end # The main event loop. Calls #process until #process returns false. If a # block is given, it is passed to #process, otherwise a default proc is # used that just returns true if there are any channels active (see #busy?). # The # +wait+ parameter is also passed through to #process (where it is # interpreted as the maximum number of seconds to wait for IO.select to return). # # # loop for as long as there are any channels active # ssh.loop # # # loop for as long as there are any channels active, but make sure # # the event loop runs at least once per 0.1 second # ssh.loop(0.1) # # # loop until ctrl-C is pressed # int_pressed = false # trap("INT") { int_pressed = true } # ssh.loop(0.1) { not int_pressed } def loop(wait=nil, &block) running = block || Proc.new { busy? } loop_forever { break unless process(wait, &running) } begin process(0) rescue IOError => e if e.message =~ /closed/ debug { "stream was closed after loop => shallowing exception so it will be re-raised in next loop" } else raise end end end # The core of the event loop. It processes a single iteration of the event # loop. If a block is given, it should return false when the processing # should abort, which causes #process to return false. Otherwise, # #process returns true. The session itself is yielded to the block as its # only argument. # # If +wait+ is nil (the default), this method will block until any of the # monitored IO objects are ready to be read from or written to. If you want # it to not block, you can pass 0, or you can pass any other numeric value # to indicate that it should block for no more than that many seconds. # Passing 0 is a good way to poll the connection, but if you do it too # frequently it can make your CPU quite busy! # # This will also cause all active channels to be processed once each (see # Net::SSH::Connection::Channel#on_process). # # TODO revise example # # # process multiple Net::SSH connections in parallel # connections = [ # Net::SSH.start("host1", ...), # Net::SSH.start("host2", ...) # ] # # connections.each do |ssh| # ssh.exec "grep something /in/some/files" # end # # condition = Proc.new { |s| s.busy? } # # loop do # connections.delete_if { |ssh| !ssh.process(0.1, &condition) } # break if connections.empty? # end def process(wait=nil, &block) @event_loop.process(wait, &block) rescue force_channel_cleanup_on_close if closed? raise end # This is called internally as part of #process. It dispatches any # available incoming packets, and then runs Net::SSH::Connection::Channel#process # for any active channels. If a block is given, it is invoked at the # start of the method and again at the end, and if the block ever returns # false, this method returns false. Otherwise, it returns true. def preprocess(&block) return false if block_given? && !yield(self) ev_preprocess(&block) return false if block_given? && !yield(self) return true end # Called by event loop to process available data before going to # event multiplexing def ev_preprocess(&block) dispatch_incoming_packets(raise_disconnect_errors: false) each_channel { |id, channel| channel.process unless channel.local_closed? } end # Returns the file descriptors the event loop should wait for read/write events, # we also return the max wait def ev_do_calculate_rw_wait(wait) r = listeners.keys w = r.select { |w2| w2.respond_to?(:pending_write?) && w2.pending_write? } [r,w,io_select_wait(wait)] end # This is called internally as part of #process. def postprocess(readers, writers) ev_do_handle_events(readers, writers) end # It loops over the given arrays of reader IO's and writer IO's, # processing them as needed, and # then calls Net::SSH::Transport::Session#rekey_as_needed to allow the # transport layer to rekey. Then returns true. def ev_do_handle_events(readers, writers) Array(readers).each do |reader| if listeners[reader] listeners[reader].call(reader) else if reader.fill.zero? reader.close stop_listening_to(reader) end end end Array(writers).each do |writer| writer.send_pending end end # calls Net::SSH::Transport::Session#rekey_as_needed to allow the # transport layer to rekey def ev_do_postprocess(was_events) @keepalive.send_as_needed(was_events) transport.rekey_as_needed true end # Send a global request of the given type. The +extra+ parameters must # be even in number, and conform to the same format as described for # Net::SSH::Buffer.from. If a callback is not specified, the request will # not require a response from the server, otherwise the server is required # to respond and indicate whether the request was successful or not. This # success or failure is indicated by the callback being invoked, with the # first parameter being true or false (success, or failure), and the second # being the packet itself. # # Generally, Net::SSH will manage global requests that need to be sent # (e.g. port forward requests and such are handled in the Net::SSH::Service::Forward # class, for instance). However, there may be times when you need to # send a global request that isn't explicitly handled by Net::SSH, and so # this method is available to you. # # ssh.send_global_request("keep-alive@openssh.com") def send_global_request(type, *extra, &callback) info { "sending global request #{type}" } msg = Buffer.from(:byte, GLOBAL_REQUEST, :string, type.to_s, :bool, !callback.nil?, *extra) send_message(msg) pending_requests << callback if callback self end # Requests that a new channel be opened. By default, the channel will be # of type "session", but if you know what you're doing you can select any # of the channel types supported by the SSH protocol. The +extra+ parameters # must be even in number and conform to the same format as described for # Net::SSH::Buffer.from. If a callback is given, it will be invoked when # the server confirms that the channel opened successfully. The sole parameter # for the callback is the channel object itself. # # In general, you'll use #open_channel without any arguments; the only # time you'd want to set the channel type or pass additional initialization # data is if you were implementing an SSH extension. # # channel = ssh.open_channel do |ch| # ch.exec "grep something /some/files" do |ch, success| # ... # end # end # # channel.wait def open_channel(type="session", *extra, &on_confirm) local_id = get_next_channel_id channel = Channel.new(self, type, local_id, @max_pkt_size, @max_win_size, &on_confirm) msg = Buffer.from(:byte, CHANNEL_OPEN, :string, type, :long, local_id, :long, channel.local_maximum_window_size, :long, channel.local_maximum_packet_size, *extra) send_message(msg) channels[local_id] = channel end class StringWithExitstatus < String def initialize(str, exitstatus) super(str) @exitstatus = exitstatus end attr_reader :exitstatus end # A convenience method for executing a command and interacting with it. If # no block is given, all output is printed via $stdout and $stderr. Otherwise, # the block is called for each data and extended data packet, with three # arguments: the channel object, a symbol indicating the data type # (:stdout or :stderr), and the data (as a string). # # Note that this method returns immediately, and requires an event loop # (see Session#loop) in order for the command to actually execute. # # This is effectively identical to calling #open_channel, and then # Net::SSH::Connection::Channel#exec, and then setting up the channel # callbacks. However, for most uses, this will be sufficient. # # ssh.exec "grep something /some/files" do |ch, stream, data| # if stream == :stderr # puts "ERROR: #{data}" # else # puts data # end # end def exec(command, status: nil, &block) open_channel do |channel| channel.exec(command) do |ch, success| raise "could not execute command: #{command.inspect}" unless success if status channel.on_request("exit-status") do |ch2,data| status[:exit_code] = data.read_long end channel.on_request("exit-signal") do |ch2, data| status[:exit_signal] = data.read_long end end channel.on_data do |ch2, data| if block block.call(ch2, :stdout, data) else $stdout.print(data) end end channel.on_extended_data do |ch2, type, data| if block block.call(ch2, :stderr, data) else $stderr.print(data) end end end end end # Same as #exec, except this will block until the command finishes. Also, # if no block is given, this will return all output (stdout and stderr) # as a single string. # # matches = ssh.exec!("grep something /some/files") # # the returned string has an exitstatus method to query it's exit satus def exec!(command, status: nil, &block) block_or_concat = block || Proc.new do |ch, type, data| ch[:result] ||= "" ch[:result] << data end status ||= {} channel = exec(command, status: status, &block_or_concat) channel.wait channel[:result] ||= "" unless block channel[:result] &&= channel[:result].force_encoding("UTF-8") unless block StringWithExitstatus.new(channel[:result], status[:exit_code]) if channel[:result] end # Enqueues a message to be sent to the server as soon as the socket is # available for writing. Most programs will never need to call this, but # if you are implementing an extension to the SSH protocol, or if you # need to send a packet that Net::SSH does not directly support, you can # use this to send it. # # ssh.send_message(Buffer.from(:byte, REQUEST_SUCCESS).to_s) def send_message(message) transport.enqueue_message(message) end # Adds an IO object for the event loop to listen to. If a callback # is given, it will be invoked when the io is ready to be read, otherwise, # the io will merely have its #fill method invoked. # # Any +io+ value passed to this method _must_ have mixed into it the # Net::SSH::BufferedIo functionality, typically by calling #extend on the # object. # # The following example executes a process on the remote server, opens # a socket to somewhere, and then pipes data from that socket to the # remote process' stdin stream: # # channel = ssh.open_channel do |ch| # ch.exec "/some/process/that/wants/input" do |ch, success| # abort "can't execute!" unless success # # io = TCPSocket.new(somewhere, port) # io.extend(Net::SSH::BufferedIo) # ssh.listen_to(io) # # ch.on_process do # if io.available > 0 # ch.send_data(io.read_available) # end # end # # ch.on_close do # ssh.stop_listening_to(io) # io.close # end # end # end # # channel.wait def listen_to(io, &callback) listeners[io] = callback end # Removes the given io object from the listeners collection, so that the # event loop will no longer monitor it. def stop_listening_to(io) listeners.delete(io) end # Returns a reference to the Net::SSH::Service::Forward service, which can # be used for forwarding ports over SSH. def forward @forward ||= Service::Forward.new(self) end # Registers a handler to be invoked when the server wants to open a # channel on the client. The callback receives the connection object, # the new channel object, and the packet itself as arguments, and should # raise ChannelOpenFailed if it is unable to open the channel for some # reason. Otherwise, the channel will be opened and a confirmation message # sent to the server. # # This is used by the Net::SSH::Service::Forward service to open a channel # when a remote forwarded port receives a connection. However, you are # welcome to register handlers for other channel types, as needed. def on_open_channel(type, &block) channel_open_handlers[type] = block end # Registers a handler to be invoked when the server sends a global request # of the given type. The callback receives the request data as the first # parameter, and true/false as the second (indicating whether a response # is required). If the callback sends the response, it should return # :sent. Otherwise, if it returns true, REQUEST_SUCCESS will be sent, and # if it returns false, REQUEST_FAILURE will be sent. def on_global_request(type, &block) old, @on_global_request[type] = @on_global_request[type], block old end def cleanup_channel(channel) if channel.local_closed? and channel.remote_closed? info { "#{host} delete channel #{channel.local_id} which closed locally and remotely" } channels.delete(channel.local_id) end end # If the #preprocess and #postprocess callbacks for this session need to run # periodically, this method returns the maximum number of seconds which may # pass between callbacks. def max_select_wait_time @keepalive.interval if @keepalive.enabled? end private # iterate channels with the posibility of callbacks opening new channels during the iteration def each_channel(&block) channels.dup.each(&block) end # Read all pending packets from the connection and dispatch them as # appropriate. Returns as soon as there are no more pending packets. def dispatch_incoming_packets(raise_disconnect_errors: true) while packet = transport.poll_message unless MAP.key?(packet.type) raise Net::SSH::Exception, "unexpected response #{packet.type} (#{packet.inspect})" end send(MAP[packet.type], packet) end rescue force_channel_cleanup_on_close if closed? raise if raise_disconnect_errors || !$!.is_a?(Net::SSH::Disconnect) end # Returns the next available channel id to be assigned, and increments # the counter. def get_next_channel_id @channel_id_counter += 1 end def force_channel_cleanup_on_close channels.each do |id, channel| channel_closed(channel) end end def channel_closed(channel) channel.remote_closed! channel.close cleanup_channel(channel) channel.do_close end # Invoked when a global request is received. The registered global # request callback will be invoked, if one exists, and the necessary # reply returned. def global_request(packet) info { "global request received: #{packet[:request_type]} #{packet[:want_reply]}" } callback = @on_global_request[packet[:request_type]] result = callback ? callback.call(packet[:request_data], packet[:want_reply]) : false if result != :sent && result != true && result != false raise "expected global request handler for `#{packet[:request_type]}' to return true, false, or :sent, but got #{result.inspect}" end if packet[:want_reply] && result != :sent msg = Buffer.from(:byte, result ? REQUEST_SUCCESS : REQUEST_FAILURE) send_message(msg) end end # Invokes the next pending request callback with +true+. def request_success(packet) info { "global request success" } callback = pending_requests.shift callback.call(true, packet) if callback end # Invokes the next pending request callback with +false+. def request_failure(packet) info { "global request failure" } callback = pending_requests.shift callback.call(false, packet) if callback end # Called when the server wants to open a channel. If no registered # channel handler exists for the given channel type, CHANNEL_OPEN_FAILURE # is returned, otherwise the callback is invoked and everything proceeds # accordingly. def channel_open(packet) info { "channel open #{packet[:channel_type]}" } local_id = get_next_channel_id channel = Channel.new(self, packet[:channel_type], local_id, @max_pkt_size, @max_win_size) channel.do_open_confirmation(packet[:remote_id], packet[:window_size], packet[:packet_size]) callback = channel_open_handlers[packet[:channel_type]] if callback begin callback[self, channel, packet] rescue ChannelOpenFailed => err failure = [err.code, err.reason] else channels[local_id] = channel msg = Buffer.from(:byte, CHANNEL_OPEN_CONFIRMATION, :long, channel.remote_id, :long, channel.local_id, :long, channel.local_maximum_window_size, :long, channel.local_maximum_packet_size) end else failure = [3, "unknown channel type #{channel.type}"] end if failure error { failure.inspect } msg = Buffer.from(:byte, CHANNEL_OPEN_FAILURE, :long, channel.remote_id, :long, failure[0], :string, failure[1], :string, "") end send_message(msg) end def channel_open_confirmation(packet) info { "channel_open_confirmation: #{packet[:local_id]} #{packet[:remote_id]} #{packet[:window_size]} #{packet[:packet_size]}" } channel = channels[packet[:local_id]] channel.do_open_confirmation(packet[:remote_id], packet[:window_size], packet[:packet_size]) end def channel_open_failure(packet) error { "channel_open_failed: #{packet[:local_id]} #{packet[:reason_code]} #{packet[:description]}" } channel = channels.delete(packet[:local_id]) channel.do_open_failed(packet[:reason_code], packet[:description]) end def channel_window_adjust(packet) info { "channel_window_adjust: #{packet[:local_id]} +#{packet[:extra_bytes]}" } channels[packet[:local_id]].do_window_adjust(packet[:extra_bytes]) end def channel_request(packet) info { "channel_request: #{packet[:local_id]} #{packet[:request]} #{packet[:want_reply]}" } channels[packet[:local_id]].do_request(packet[:request], packet[:want_reply], packet[:request_data]) end def channel_data(packet) info { "channel_data: #{packet[:local_id]} #{packet[:data].length}b" } channels[packet[:local_id]].do_data(packet[:data]) end def channel_extended_data(packet) info { "channel_extended_data: #{packet[:local_id]} #{packet[:data_type]} #{packet[:data].length}b" } channels[packet[:local_id]].do_extended_data(packet[:data_type], packet[:data]) end def channel_eof(packet) info { "channel_eof: #{packet[:local_id]}" } channels[packet[:local_id]].do_eof end def channel_close(packet) info { "channel_close: #{packet[:local_id]}" } channel = channels[packet[:local_id]] channel_closed(channel) end def channel_success(packet) info { "channel_success: #{packet[:local_id]}" } channels[packet[:local_id]].do_success end def channel_failure(packet) info { "channel_failure: #{packet[:local_id]}" } channels[packet[:local_id]].do_failure end def io_select_wait(wait) [wait, max_select_wait_time].compact.min end MAP = Constants.constants.inject({}) do |memo, name| value = const_get(name) next unless Integer === value memo[value] = name.downcase.to_sym memo end end end; end; end net-ssh-4.2.0/lib/net/ssh/connection/term.rb000066400000000000000000000071771315376572000207070ustar00rootroot00000000000000module Net; module SSH; module Connection # These constants are used when requesting a pseudo-terminal (via # Net::SSH::Connection::Channel#request_pty). The descriptions for each are # taken directly from RFC 4254 ("The Secure Shell (SSH) Connection Protocol"), # http://tools.ietf.org/html/rfc4254. module Term # Interrupt character; 255 if none. Similarly for the other characters. # Not all of these characters are supported on all systems. VINTR = 1 # The quit character (sends SIGQUIT signal on POSIX systems). VQUIT = 2 # Erase the character to left of the cursor. VERASE = 3 # Kill the current input line. VKILL = 4 # End-of-file character (sends EOF from the terminal). VEOF = 5 # End-of-line character in addition to carriage return and/or linefeed. VEOL = 6 # Additional end-of-line character. VEOL2 = 7 # Continues paused output (normally control-Q). VSTART = 8 # Pauses output (normally control-S). VSTOP = 9 # Suspends the current program. VSUSP = 10 # Another suspend character. VDSUSP = 11 # Reprints the current input line. VREPRINT = 12 # Erases a word left of cursor. VWERASE = 13 # Enter the next character typed literally, even if it is a special # character. VLNEXT = 14 # Character to flush output. VFLUSH = 15 # Switch to a different shell layer. VSWITCH = 16 # Prints system status line (load, command, pid, etc). VSTATUS = 17 # Toggles the flushing of terminal output. VDISCARD = 18 # The ignore parity flag. The parameter SHOULD be 0 if this flag is FALSE, # and 1 if it is TRUE. IGNPAR = 30 # Mark parity and framing errors. PARMRK = 31 # Enable checking of parity errors. INPCK = 32 # Strip 8th bit off characters. ISTRIP = 33 # Map NL into CR on input. INCLR = 34 # Ignore CR on input. IGNCR = 35 # Map CR to NL on input. ICRNL = 36 # Translate uppercase characters to lowercase. IUCLC = 37 # Enable output flow control. IXON = 38 # Any char will restart after stop. IXANY = 39 # Enable input flow control. IXOFF = 40 # Ring bell on input queue full. IMAXBEL = 41 # Enable signals INTR, QUIT, [D]SUSP. ISIG = 50 # Canonicalize input lines. ICANON = 51 # Enable input and output of uppercase characters by preceding their # lowercase equivalents with "\". XCASE = 52 # Enable echoing. ECHO = 53 # Visually erase chars. ECHOE = 54 # Kill character discards current line. ECHOK = 55 # Echo NL even if ECHO is off. ECHONL = 56 # Don't flush after interrupt. NOFLSH = 57 # Stop background jobs from output. TOSTOP= 58 # Enable extensions. IEXTEN = 59 # Echo control characters as ^(Char). ECHOCTL = 60 # Visual erase for line kill. ECHOKE = 61 # Retype pending input. PENDIN = 62 # Enable output processing. OPOST = 70 # Convert lowercase to uppercase. OLCUC = 71 # Map NL to CR-NL. ONLCR = 72 # Translate carriage return to newline (output). OCRNL = 73 # Translate newline to carriage return-newline (output). ONOCR = 74 # Newline performs a carriage return (output). ONLRET = 75 # 7 bit mode. CS7 = 90 # 8 bit mode. CS8 = 91 # Parity enable. PARENB = 92 # Odd parity, else even. PARODD = 93 # Specifies the input baud rate in bits per second. TTY_OP_ISPEED = 128 # Specifies the output baud rate in bits per second. TTY_OP_OSPEED = 129 end end; end; end net-ssh-4.2.0/lib/net/ssh/errors.rb000066400000000000000000000076351315376572000171140ustar00rootroot00000000000000module Net; module SSH # A general exception class, to act as the ancestor of all other Net::SSH # exception classes. class Exception < ::RuntimeError; end # This exception is raised when authentication fails (whether it be # public key authentication, password authentication, or whatever). class AuthenticationFailed < Net::SSH::Exception; end # This exception is raised when a connection attempt times out. class ConnectionTimeout < Net::SSH::Exception; end # This exception is raised when the remote host has disconnected # unexpectedly. class Disconnect < Net::SSH::Exception; end # This exception is raised when the remote host has disconnected/ # timeouted unexpectedly. class Timeout < Disconnect; end # This exception is primarily used internally, but if you have a channel # request handler (see Net::SSH::Connection::Channel#on_request) that you # want to fail in such a way that the server knows it failed, you can # raise this exception in the handler and Net::SSH will translate that into # a "channel failure" message. class ChannelRequestFailed < Net::SSH::Exception; end # This is exception is primarily used internally, but if you have a channel # open handler (see Net::SSH::Connection::Session#on_open_channel) and you # want to fail in such a way that the server knows it failed, you can # raise this exception in the handler and Net::SSH will translate that into # a "channel open failed" message. class ChannelOpenFailed < Net::SSH::Exception attr_reader :code, :reason def initialize(code, reason) @code, @reason = code, reason super "#{reason} (#{code})" end end # Base class for host key exceptions. When rescuing this exception, you can # inspect the key fingerprint and, if you want to proceed anyway, simply call # the remember_host! method on the exception, and then retry. class HostKeyError < Net::SSH::Exception # the callback to use when #remember_host! is called attr_writer :callback #:nodoc: # situation-specific data describing the host (see #host, #port, etc.) attr_writer :data #:nodoc: # An accessor for getting at the data that was used to look up the host # (see also #fingerprint, #host, #port, #ip, and #key). def [](key) @data && @data[key] end # Returns the fingerprint of the key for the host, which either was not # found or did not match. def fingerprint @data && @data[:fingerprint] end # Returns the host name for the remote host, as reported by the socket. def host @data && @data[:peer] && @data[:peer][:host] end # Returns the port number for the remote host, as reported by the socket. def port @data && @data[:peer] && @data[:peer][:port] end # Returns the IP address of the remote host, as reported by the socket. def ip @data && @data[:peer] && @data[:peer][:ip] end # Returns the key itself, as reported by the remote host. def key @data && @data[:key] end # Tell Net::SSH to record this host and key in the known hosts file, so # that subsequent connections will remember them. def remember_host! @callback.call end end # Raised when the cached key for a particular host does not match the # key given by the host, which can be indicative of a man-in-the-middle # attack. When rescuing this exception, you can inspect the key fingerprint # and, if you want to proceed anyway, simply call the remember_host! # method on the exception, and then retry. class HostKeyMismatch < HostKeyError; end # Raised when there is no cached key for a particular host, which probably # means that the host has simply not been seen before. # When rescuing this exception, you can inspect the key fingerprint and, if # you want to proceed anyway, simply call the remember_host! method on the # exception, and then retry. class HostKeyUnknown < HostKeyError; end end; end net-ssh-4.2.0/lib/net/ssh/key_factory.rb000066400000000000000000000127371315376572000201160ustar00rootroot00000000000000require 'net/ssh/transport/openssl' require 'net/ssh/prompt' require 'net/ssh/authentication/ed25519_loader' module Net; module SSH # A factory class for returning new Key classes. It is used for obtaining # OpenSSL key instances via their SSH names, and for loading both public and # private keys. It used used primarily by Net::SSH itself, internally, and # will rarely (if ever) be directly used by consumers of the library. # # klass = Net::SSH::KeyFactory.get("rsa") # assert klass.is_a?(OpenSSL::PKey::RSA) # # key = Net::SSH::KeyFactory.load_public_key("~/.ssh/id_dsa.pub") class KeyFactory # Specifies the mapping of SSH names to OpenSSL key classes. MAP = { "dh" => OpenSSL::PKey::DH, "rsa" => OpenSSL::PKey::RSA, "dsa" => OpenSSL::PKey::DSA, } if defined?(OpenSSL::PKey::EC) MAP["ecdsa"] = OpenSSL::PKey::EC MAP["ed25519"] = Net::SSH::Authentication::ED25519::PrivKey if defined? Net::SSH::Authentication::ED25519 end class <(key_data, passphrase) { Net::SSH::Authentication::ED25519::PrivKey.read(key_data, passphrase) }, [ArgumentError] elsif OpenSSL::PKey.respond_to?(:read) return ->(key_data, passphrase) { OpenSSL::PKey.read(key_data, passphrase) }, [ArgumentError, OpenSSL::PKey::PKeyError] elsif data.match(/-----BEGIN DSA PRIVATE KEY-----/) return ->(key_data, passphrase) { OpenSSL::PKey::DSA.new(key_data, passphrase) }, [OpenSSL::PKey::DSAError] elsif data.match(/-----BEGIN RSA PRIVATE KEY-----/) return ->(key_data, passphrase) { OpenSSL::PKey::RSA.new(key_data, passphrase) }, [OpenSSL::PKey::RSAError] elsif data.match(/-----BEGIN EC PRIVATE KEY-----/) && defined?(OpenSSL::PKey::EC) return ->(key_data, passphrase) { OpenSSL::PKey::EC.new(key_data, passphrase) }, [OpenSSL::PKey::ECError] elsif data.match(/-----BEGIN (.+) PRIVATE KEY-----/) raise OpenSSL::PKey::PKeyError, "not a supported key type '#{$1}'" else raise OpenSSL::PKey::PKeyError, "not a private key (#{filename})" end end end end end; end net-ssh-4.2.0/lib/net/ssh/known_hosts.rb000066400000000000000000000136361315376572000201520ustar00rootroot00000000000000require 'strscan' require 'openssl' require 'base64' require 'net/ssh/buffer' module Net; module SSH # Represents the result of a search in known hosts # see search_for class HostKeys include Enumerable attr_reader :host def initialize(host_keys, host, known_hosts, options = {}) @host_keys = host_keys @host = host @known_hosts = known_hosts @options = options end def add_host_key(key) @known_hosts.add(@host, key, @options) @host_keys.push(key) end def each(&block) @host_keys.each(&block) end def empty? @host_keys.empty? end end # Searches an OpenSSH-style known-host file for a given host, and returns all # matching keys. This is used to implement host-key verification, as well as # to determine what key a user prefers to use for a given host. # # This is used internally by Net::SSH, and will never need to be used directly # by consumers of the library. class KnownHosts if defined?(OpenSSL::PKey::EC) SUPPORTED_TYPE = %w(ssh-rsa ssh-dss ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521) else SUPPORTED_TYPE = %w(ssh-rsa ssh-dss) end class < 98 (CHANNEL_REQUEST) # p packet[:request] # p packet[:want_reply] # # This is used exclusively internally by Net::SSH, and unless you're doing # protocol-level manipulation or are extending Net::SSH in some way, you'll # never need to use this class directly. class Packet < Buffer @@types = {} # Register a new packet type that should be recognized and auto-parsed by # Net::SSH::Packet. Note that any packet type that is not preregistered # will not be autoparsed. # # The +pairs+ parameter must be either empty, or an array of two-element # tuples, where the first element of each tuple is the name of the field, # and the second is the type. # # register DISCONNECT, [:reason_code, :long], [:description, :string], [:language, :string] def self.register(type, *pairs) @@types[type] = pairs end include Transport::Constants, Authentication::Constants, Connection::Constants #-- # These are the recognized packet types. All other packet types will be # accepted, but not auto-parsed, requiring the client to parse the # fields using the methods provided by Net::SSH::Buffer. #++ register DISCONNECT, [:reason_code, :long], [:description, :string], [:language, :string] register IGNORE, [:data, :string] register UNIMPLEMENTED, [:number, :long] register DEBUG, [:always_display, :bool], [:message, :string], [:language, :string] register SERVICE_ACCEPT, [:service_name, :string] register USERAUTH_BANNER, [:message, :string], [:language, :string] register USERAUTH_FAILURE, [:authentications, :string], [:partial_success, :bool] register GLOBAL_REQUEST, [:request_type, :string], [:want_reply, :bool], [:request_data, :buffer] register CHANNEL_OPEN, [:channel_type, :string], [:remote_id, :long], [:window_size, :long], [:packet_size, :long] register CHANNEL_OPEN_CONFIRMATION, [:local_id, :long], [:remote_id, :long], [:window_size, :long], [:packet_size, :long] register CHANNEL_OPEN_FAILURE, [:local_id, :long], [:reason_code, :long], [:description, :string], [:language, :string] register CHANNEL_WINDOW_ADJUST, [:local_id, :long], [:extra_bytes, :long] register CHANNEL_DATA, [:local_id, :long], [:data, :string] register CHANNEL_EXTENDED_DATA, [:local_id, :long], [:data_type, :long], [:data, :string] register CHANNEL_EOF, [:local_id, :long] register CHANNEL_CLOSE, [:local_id, :long] register CHANNEL_REQUEST, [:local_id, :long], [:request, :string], [:want_reply, :bool], [:request_data, :buffer] register CHANNEL_SUCCESS, [:local_id, :long] register CHANNEL_FAILURE, [:local_id, :long] # The (integer) type of this packet. attr_reader :type # Create a new packet from the given payload. This will automatically # parse the packet if it is one that has been previously registered with # Packet.register; otherwise, the packet will need to be manually parsed # using the methods provided in the Net::SSH::Buffer superclass. def initialize(payload) @named_elements = {} super @type = read_byte instantiate! end # Access one of the auto-parsed fields by name. Raises an error if no # element by the given name exists. def [](name) name = name.to_sym raise ArgumentError, "no such element #{name}" unless @named_elements.key?(name) @named_elements[name] end private # Parse the packet's contents and assign the named elements, as described # by the registered format for the packet. def instantiate! (@@types[type] || []).each do |name, datatype| @named_elements[name.to_sym] = if datatype == :buffer remainder_as_buffer else send("read_#{datatype}") end end end end end; endnet-ssh-4.2.0/lib/net/ssh/prompt.rb000066400000000000000000000037141315376572000171130ustar00rootroot00000000000000require 'io/console' module Net; module SSH # Default prompt implementation, called for asking password from user. # It will never be instantiated directly, but will instead be created for # you automatically. # # A custom prompt objects can implement caching, or different UI. The prompt # object should implemnted a start method, which should return something implementing # ask and success. Net::SSH uses it like: # # prompter = options[:password_prompt].start({type:'password'}) # while !ok && max_retries < 3 # user = prompter.ask("user: ", {}, true) # password = prompter.ask("password: ", {}, false) # ok = send(user, password) # prompter.sucess if ok # end # class Prompt # factory def self.default(options = {}) @default ||= new(options) end def initialize(options = {}); end # default prompt object implementation. More sophisticated implemenetations # might implement caching. class Prompter def initialize(info) if info[:type] == 'keyboard-interactive' $stdout.puts(info[:name]) unless info[:name].empty? $stdout.puts(info[:instruction]) unless info[:instruction].empty? end end # ask input from user, a prompter might ask for multiple inputs # (like user and password) in a single session. def ask(prompt, echo=true) $stdout.print(prompt) $stdout.flush ret = $stdin.noecho(&:gets).chomp $stdout.print("\n") ret end # success method will be called when the password was accepted # It's a good time to save password asked to a cache. def success; end end # start password session. Multiple questions might be asked multiple times # on the returned object. Info hash tries to uniquely identify the password # session, so caching implementations can save passwords properly. def start(info) Prompter.new(info) end end end; end net-ssh-4.2.0/lib/net/ssh/proxy/000077500000000000000000000000001315376572000164215ustar00rootroot00000000000000net-ssh-4.2.0/lib/net/ssh/proxy/command.rb000066400000000000000000000060321315376572000203650ustar00rootroot00000000000000require 'socket' require 'rubygems' require 'net/ssh/proxy/errors' require 'net/ssh/ruby_compat' module Net; module SSH; module Proxy # An implementation of a command proxy. To use it, instantiate it, # then pass the instantiated object via the :proxy key to # Net::SSH.start: # # require 'net/ssh/proxy/command' # # proxy = Net::SSH::Proxy::Command.new('ssh relay nc %h %p') # Net::SSH.start('host', 'user', :proxy => proxy) do |ssh| # ... # end class Command # The command line template attr_reader :command_line_template # The command line for the session attr_reader :command_line # Create a new socket factory that tunnels via a command executed # with the user's shell, which is composed from the given command # template. In the command template, `%h' will be substituted by # the host name to connect and `%p' by the port. def initialize(command_line_template) @command_line_template = command_line_template @command_line = nil end # Return a new socket connected to the given host and port via the # proxy that was requested when the socket factory was instantiated. def open(host, port, connection_options = nil) command_line = @command_line_template.gsub(/%(.)/) { case $1 when 'h' host when 'p' port.to_s when 'r' remote_user = connection_options && connection_options[:remote_user] if remote_user remote_user else raise ArgumentError, "remote user name not available" end when '%' '%' else raise ArgumentError, "unknown key: #{$1}" end } begin io = IO.popen(command_line, "r+") if result = Net::SSH::Compat.io_select([io], nil, [io], 60) if result.last.any? || io.eof? io.close raise "command failed" end else raise "command timed out" end rescue => e raise ConnectError, "#{e}: #{command_line}" end @command_line = command_line if Gem.win_platform? # read_nonblock and write_nonblock are not available on Windows # pipe. Use sysread and syswrite as a replacement works. def io.send(data, flag) syswrite(data) end def io.recv(size) sysread(size) end else def io.send(data, flag) begin result = write_nonblock(data) rescue IO::WaitWritable, Errno::EINTR IO.select(nil, [self]) retry end result end def io.recv(size) begin result = read_nonblock(size) rescue IO::WaitReadable, Errno::EINTR timeout_in_seconds = 20 if IO.select([self], nil, [self], timeout_in_seconds) == nil raise "Unexpected spurious read wakeup" end retry end result end end io end end end; end; end net-ssh-4.2.0/lib/net/ssh/proxy/errors.rb000066400000000000000000000005521315376572000202640ustar00rootroot00000000000000require 'net/ssh/errors' module Net; module SSH; module Proxy # A general exception class for all Proxy errors. class Error < Net::SSH::Exception; end # Used for reporting proxy connection errors. class ConnectError < Error; end # Used when the server doesn't recognize the user's credentials. class UnauthorizedError < Error; end end; end; end net-ssh-4.2.0/lib/net/ssh/proxy/http.rb000066400000000000000000000057131315376572000177330ustar00rootroot00000000000000require 'socket' require 'net/ssh/proxy/errors' module Net; module SSH; module Proxy # An implementation of an HTTP proxy. To use it, instantiate it, then # pass the instantiated object via the :proxy key to Net::SSH.start: # # require 'net/ssh/proxy/http' # # proxy = Net::SSH::Proxy::HTTP.new('proxy_host', proxy_port) # Net::SSH.start('host', 'user', :proxy => proxy) do |ssh| # ... # end # # If the proxy requires authentication, you can pass :user and :password # to the proxy's constructor: # # proxy = Net::SSH::Proxy::HTTP.new('proxy_host', proxy_port, # :user => "user", :password => "password") # # Note that HTTP digest authentication is not supported; Basic only at # this point. class HTTP # The hostname or IP address of the HTTP proxy. attr_reader :proxy_host # The port number of the proxy. attr_reader :proxy_port # The map of additional options that were given to the object at # initialization. attr_reader :options # Create a new socket factory that tunnels via the given host and # port. The +options+ parameter is a hash of additional settings that # can be used to tweak this proxy connection. Specifically, the following # options are supported: # # * :user => the user name to use when authenticating to the proxy # * :password => the password to use when authenticating def initialize(proxy_host, proxy_port=80, options={}) @proxy_host = proxy_host @proxy_port = proxy_port @options = options end # Return a new socket connected to the given host and port via the # proxy that was requested when the socket factory was instantiated. def open(host, port, connection_options) socket = establish_connection(connection_options[:timeout]) socket.write "CONNECT #{host}:#{port} HTTP/1.0\r\n" if options[:user] credentials = ["#{options[:user]}:#{options[:password]}"].pack("m*").gsub(/\s/, "") socket.write "Proxy-Authorization: Basic #{credentials}\r\n" end socket.write "\r\n" resp = parse_response(socket) return socket if resp[:code] == 200 socket.close raise ConnectError, resp.inspect end protected def establish_connection(connect_timeout) Socket.tcp(proxy_host, proxy_port, nil, nil, connect_timeout: connect_timeout) end def parse_response(socket) version, code, reason = socket.gets.chomp.split(/ /, 3) headers = {} while (line = socket.gets) && (line.chomp! != "") name, value = line.split(/:/, 2) headers[name.strip] = value.strip end if headers["Content-Length"] body = socket.read(headers["Content-Length"].to_i) end return { version: version, code: code.to_i, reason: reason, headers: headers, body: body } end end end; end; end net-ssh-4.2.0/lib/net/ssh/proxy/https.rb000066400000000000000000000031341315376572000201110ustar00rootroot00000000000000require 'socket' require 'openssl' require 'net/ssh/proxy/errors' require 'net/ssh/proxy/http' module Net; module SSH; module Proxy # A specialization of the HTTP proxy which encrypts the whole connection # using OpenSSL. This has the advantage that proxy authentication # information is not sent in plaintext. class HTTPS < HTTP # Create a new socket factory that tunnels via the given host and # port. The +options+ parameter is a hash of additional settings that # can be used to tweak this proxy connection. In addition to the options # taken by Net::SSH::Proxy::HTTP it supports: # # * :ssl_context => the SSL configuration to use for the connection def initialize(proxy_host, proxy_port=80, options={}) @ssl_context = options.delete(:ssl_context) || OpenSSL::SSL::SSLContext.new super(proxy_host, proxy_port, options) end protected # Shim to make OpenSSL::SSL::SSLSocket behave like a regular TCPSocket # for all intents and purposes of Net::SSH::BufferedIo module SSLSocketCompatibility def self.extended(object) #:nodoc: object.define_singleton_method(:recv, object.method(:sysread)) object.sync_close = true end def send(data, _opts) syswrite(data) end end def establish_connection(connect_timeout) plain_socket = super(connect_timeout) OpenSSL::SSL::SSLSocket.new(plain_socket, @ssl_context).tap do |socket| socket.extend(SSLSocketCompatibility) socket.connect end end end end; end; end net-ssh-4.2.0/lib/net/ssh/proxy/jump.rb000066400000000000000000000032201315376572000177160ustar00rootroot00000000000000require 'uri' require 'net/ssh/proxy/command' module Net; module SSH; module Proxy # An implementation of a jump proxy. To use it, instantiate it, # then pass the instantiated object via the :proxy key to # Net::SSH.start: # # require 'net/ssh/proxy/jump' # # proxy = Net::SSH::Proxy::Jump.new('user@proxy') # Net::SSH.start('host', 'user', :proxy => proxy) do |ssh| # ... # end class Jump < Command # The jump proxies attr_reader :jump_proxies # Create a new socket factory that tunnels via multiple jump proxes as # [user@]host[:port]. def initialize(jump_proxies) @jump_proxies = jump_proxies end # Return a new socket connected to the given host and port via the jump # proxy that was requested when the socket factory was instantiated. def open(host, port, connection_options = nil) build_proxy_command_equivalent(connection_options) super end # We cannot build the ProxyCommand template until we know if the :config # option was specified during `Net::SSH.start`. def build_proxy_command_equivalent(connection_options = nil) first_jump, extra_jumps = jump_proxies.split(",", 2) config = connection_options && connection_options[:config] uri = URI.parse("ssh://#{first_jump}") template = "ssh" template << " -l #{uri.user}" if uri.user template << " -p #{uri.port}" if uri.port template << " -J #{extra_jumps}" if extra_jumps template << " -F #{config}" if config != true && config template << " -W %h:%p " template << uri.host @command_line_template = template end end end; end; end net-ssh-4.2.0/lib/net/ssh/proxy/socks4.rb000066400000000000000000000042551315376572000201620ustar00rootroot00000000000000require 'socket' require 'resolv' require 'ipaddr' require 'net/ssh/proxy/errors' module Net module SSH module Proxy # An implementation of a SOCKS4 proxy. To use it, instantiate it, then # pass the instantiated object via the :proxy key to Net::SSH.start: # # require 'net/ssh/proxy/socks4' # # proxy = Net::SSH::Proxy::SOCKS4.new('proxy.host', proxy_port, :user => 'user') # Net::SSH.start('host', 'user', :proxy => proxy) do |ssh| # ... # end class SOCKS4 # The SOCKS protocol version used by this class VERSION = 4 # The packet type for connection requests CONNECT = 1 # The status code for a successful connection GRANTED = 90 # The proxy's host name or IP address, as given to the constructor. attr_reader :proxy_host # The proxy's port number. attr_reader :proxy_port # The additional options that were given to the proxy's constructor. attr_reader :options # Create a new proxy connection to the given proxy host and port. # Optionally, a :user key may be given to identify the username # with which to authenticate. def initialize(proxy_host, proxy_port=1080, options={}) @proxy_host = proxy_host @proxy_port = proxy_port @options = options end # Return a new socket connected to the given host and port via the # proxy that was requested when the socket factory was instantiated. def open(host, port, connection_options) socket = Socket.tcp(proxy_host, proxy_port, nil, nil, connect_timeout: connection_options[:timeout]) ip_addr = IPAddr.new(Resolv.getaddress(host)) packet = [VERSION, CONNECT, port.to_i, ip_addr.to_i, options[:user]].pack("CCnNZ*") socket.send packet, 0 version, status, port, ip = socket.recv(8).unpack("CCnN") if status != GRANTED socket.close raise ConnectError, "error connecting to proxy (#{status})" end return socket end end end end end net-ssh-4.2.0/lib/net/ssh/proxy/socks5.rb000066400000000000000000000107131315376572000201570ustar00rootroot00000000000000require 'socket' require 'net/ssh/ruby_compat' require 'net/ssh/proxy/errors' module Net module SSH module Proxy # An implementation of a SOCKS5 proxy. To use it, instantiate it, then # pass the instantiated object via the :proxy key to Net::SSH.start: # # require 'net/ssh/proxy/socks5' # # proxy = Net::SSH::Proxy::SOCKS5.new('proxy.host', proxy_port, # :user => 'user', :password => "password") # Net::SSH.start('host', 'user', :proxy => proxy) do |ssh| # ... # end class SOCKS5 # The SOCKS protocol version used by this class VERSION = 5 # The SOCKS authentication type for requests without authentication METHOD_NO_AUTH = 0 # The SOCKS authentication type for requests via username/password METHOD_PASSWD = 2 # The SOCKS authentication type for when there are no supported # authentication methods. METHOD_NONE = 0xFF # The SOCKS packet type for requesting a proxy connection. CMD_CONNECT = 1 # The SOCKS address type for connections via IP address. ATYP_IPV4 = 1 # The SOCKS address type for connections via domain name. ATYP_DOMAIN = 3 # The SOCKS response code for a successful operation. SUCCESS = 0 # The proxy's host name or IP address attr_reader :proxy_host # The proxy's port number attr_reader :proxy_port # The map of options given at initialization attr_reader :options # Create a new proxy connection to the given proxy host and port. # Optionally, :user and :password options may be given to # identify the username and password with which to authenticate. def initialize(proxy_host, proxy_port=1080, options={}) @proxy_host = proxy_host @proxy_port = proxy_port @options = options end # Return a new socket connected to the given host and port via the # proxy that was requested when the socket factory was instantiated. def open(host, port, connection_options) socket = Socket.tcp(proxy_host, proxy_port, nil, nil, connect_timeout: connection_options[:timeout]) methods = [METHOD_NO_AUTH] methods << METHOD_PASSWD if options[:user] packet = [VERSION, methods.size, *methods].pack("C*") socket.send packet, 0 version, method = socket.recv(2).unpack("CC") if version != VERSION socket.close raise Net::SSH::Proxy::Error, "invalid SOCKS version (#{version})" end if method == METHOD_NONE socket.close raise Net::SSH::Proxy::Error, "no supported authorization methods" end negotiate_password(socket) if method == METHOD_PASSWD packet = [VERSION, CMD_CONNECT, 0].pack("C*") if host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/ packet << [ATYP_IPV4, $1.to_i, $2.to_i, $3.to_i, $4.to_i].pack("C*") else packet << [ATYP_DOMAIN, host.length, host].pack("CCA*") end packet << [port].pack("n") socket.send packet, 0 version, reply, = socket.recv(2).unpack("C*") socket.recv(1) address_type = socket.recv(1).getbyte(0) case address_type when 1 socket.recv(4) # get four bytes for IPv4 address when 3 len = socket.recv(1).getbyte(0) hostname = socket.recv(len) when 4 ipv6addr hostname = socket.recv(16) else socket.close raise ConnectError, "Illegal response type" end portnum = socket.recv(2) unless reply == SUCCESS socket.close raise ConnectError, "#{reply}" end return socket end private # Simple username/password negotiation with the SOCKS5 server. def negotiate_password(socket) packet = [0x01, options[:user].length, options[:user], options[:password].length, options[:password]].pack("CCA*CA*") socket.send packet, 0 version, status = socket.recv(2).unpack("CC") if status != SUCCESS socket.close raise UnauthorizedError, "could not authorize user" end end end end end end net-ssh-4.2.0/lib/net/ssh/ruby_compat.rb000066400000000000000000000006171315376572000201150ustar00rootroot00000000000000require 'thread' class String if RUBY_VERSION < "1.9" def getbyte(index) self[index] end def setbyte(index, c) self[index] = c end end end module Net; module SSH # This class contains miscellaneous patches and workarounds # for different ruby implementations. class Compat def self.io_select(*params) IO.select(*params) end end end; end net-ssh-4.2.0/lib/net/ssh/service/000077500000000000000000000000001315376572000167005ustar00rootroot00000000000000net-ssh-4.2.0/lib/net/ssh/service/forward.rb000066400000000000000000000410221315376572000206700ustar00rootroot00000000000000# -*- coding: utf-8 -*- require 'net/ssh/loggable' module Net; module SSH; module Service # This class implements various port forwarding services for use by # Net::SSH clients. The Forward class should never need to be instantiated # directly; instead, it should be accessed via the singleton instance # returned by Connection::Session#forward: # # ssh.forward.local(1234, "www.capify.org", 80) class Forward include Loggable # The underlying connection service instance that the port-forwarding # services employ. attr_reader :session # A simple class for representing a requested remote forwarded port. Remote = Struct.new(:host, :port) #:nodoc: # Instantiates a new Forward service instance atop the given connection # service session. This will register new channel open handlers to handle # the specialized channels that the SSH port forwarding protocols employ. def initialize(session) @session = session self.logger = session.logger @remote_forwarded_ports = {} @local_forwarded_ports = {} @agent_forwarded = false @local_forwarded_sockets = {} session.on_open_channel('forwarded-tcpip', &method(:forwarded_tcpip)) session.on_open_channel('auth-agent', &method(:auth_agent_channel)) session.on_open_channel('auth-agent@openssh.com', &method(:auth_agent_channel)) end # Starts listening for connections on the local host, and forwards them # to the specified remote host/port via the SSH connection. This method # accepts either three or four arguments. When four arguments are given, # they are: # # * the local address to bind to # * the local port to listen on # * the remote host to forward connections to # * the port on the remote host to connect to # # If three arguments are given, it is as if the local bind address is # "127.0.0.1", and the rest are applied as above. # # To request an ephemeral port on the remote server, provide 0 (zero) for # the port number. In all cases, this method will return the port that # has been assigned. # # ssh.forward.local(1234, "www.capify.org", 80) # assigned_port = ssh.forward.local("0.0.0.0", 0, "www.capify.org", 80) def local(*args) if args.length < 3 || args.length > 4 raise ArgumentError, "expected 3 or 4 parameters, got #{args.length}" end local_port_type = :long socket = begin if defined?(UNIXServer) and args.first.class == UNIXServer local_port_type = :string args.shift else bind_address = "127.0.0.1" bind_address = args.shift if args.first.is_a?(String) && args.first =~ /\D/ local_port = args.shift.to_i local_port_type = :long TCPServer.new(bind_address, local_port) end end local_port = socket.addr[1] if local_port == 0 # ephemeral port was requested remote_host = args.shift remote_port = args.shift.to_i @local_forwarded_ports[[local_port, bind_address]] = socket session.listen_to(socket) do |server| client = server.accept debug { "received connection on #{socket}" } channel = session.open_channel("direct-tcpip", :string, remote_host, :long, remote_port, :string, bind_address, local_port_type, local_port) do |achannel| achannel.info { "direct channel established" } end prepare_client(client, channel, :local) channel.on_open_failed do |ch, code, description| channel.error { "could not establish direct channel: #{description} (#{code})" } session.stop_listening_to(channel[:socket]) channel[:socket].close end end local_port end # Terminates an active local forwarded port. # # ssh.forward.cancel_local(1234) # ssh.forward.cancel_local(1234, "0.0.0.0") def cancel_local(port, bind_address="127.0.0.1") socket = @local_forwarded_ports.delete([port, bind_address]) socket.shutdown rescue nil socket.close rescue nil session.stop_listening_to(socket) end # Returns a list of all active locally forwarded ports. The returned value # is an array of arrays, where each element is a two-element tuple # consisting of the local port and bind address corresponding to the # forwarding port. def active_locals @local_forwarded_ports.keys end # Starts listening for connections on the local host, and forwards them # to the specified remote socket via the SSH connection. This will # (re)create the local socket file. The remote server needs to have the # socket file already available. # # ssh.forward.local_socket('/tmp/local.sock', '/tmp/remote.sock') def local_socket(local_socket_path, remote_socket_path) File.delete(local_socket_path) if File.exist?(local_socket_path) socket = Socket.unix_server_socket(local_socket_path) @local_forwarded_sockets[local_socket_path] = socket session.listen_to(socket) do |server| client = server.accept[0] debug { "received connection on #{socket}" } channel = session.open_channel("direct-streamlocal@openssh.com", :string, remote_socket_path, :string, nil, :long, 0) do |achannel| achannel.info { "direct channel established" } end prepare_client(client, channel, :local) channel.on_open_failed do |ch, code, description| channel.error { "could not establish direct channel: #{description} (#{code})" } session.stop_listening_to(channel[:socket]) channel[:socket].close end end local_socket_path end # Terminates an active local forwarded socket. # # ssh.forward.cancel_local_socket('/tmp/foo.sock') def cancel_local_socket(local_socket_path) socket = @local_forwarded_sockets.delete(local_socket_path) socket.shutdown rescue nil socket.close rescue nil session.stop_listening_to(socket) end # Returns a list of all active locally forwarded sockets. The returned value # is an array of Unix domain socket file paths. def active_local_sockets @local_forwarded_sockets.keys end # Requests that all connections on the given remote-port be forwarded via # the local host to the given port/host. The last argument describes the # bind address on the remote host, and defaults to 127.0.0.1. # # This method will return immediately, but the port will not actually be # forwarded immediately. If the remote server is not able to begin the # listener for this request, an exception will be raised asynchronously. # # To request an ephemeral port on the remote server, provide 0 (zero) for # the port number. The assigned port will show up in the # #active_remotes # list. # # remote_host is interpreted by the server per RFC 4254, which has these # special values: # # - "" means that connections are to be accepted on all protocol # families supported by the SSH implementation. # - "0.0.0.0" means to listen on all IPv4 addresses. # - "::" means to listen on all IPv6 addresses. # - "localhost" means to listen on all protocol families supported by # the SSH implementation on loopback addresses only ([RFC3330] and # [RFC3513]). # - "127.0.0.1" and "::1" indicate listening on the loopback # interfaces for IPv4 and IPv6, respectively. # # You may pass a block that will be called when the the port forward # request receives a response. This block will be passed the remote_port # that was actually bound to, or nil if the binding failed. If the block # returns :no_exception, the "failed binding" exception will not be thrown. # # If you want to block until the port is active, you could do something # like this: # # got_remote_port = nil # remote(port, host, remote_port, remote_host) do |actual_remote_port| # got_remote_port = actual_remote_port || :error # :no_exception # will yield the exception on my own thread # end # session.loop { !got_remote_port } # if got_remote_port == :error # raise Net::SSH::Exception, "remote forwarding request failed" # end # def remote(port, host, remote_port, remote_host="127.0.0.1") session.send_global_request("tcpip-forward", :string, remote_host, :long, remote_port) do |success, response| if success remote_port = response.read_long if remote_port == 0 debug { "remote forward from remote #{remote_host}:#{remote_port} to #{host}:#{port} established" } @remote_forwarded_ports[[remote_port, remote_host]] = Remote.new(host, port) yield remote_port, remote_host if block_given? else instruction = if block_given? yield :error end unless instruction == :no_exception error { "remote forwarding request failed" } raise Net::SSH::Exception, "remote forwarding request failed" end end end end # an alias, for token backwards compatibility with the 1.x API alias :remote_to :remote # Requests that a remote forwarded port be cancelled. The remote forwarded # port on the remote host, bound to the given address on the remote host, # will be terminated, but not immediately. This method returns immediately # after queueing the request to be sent to the server. If for some reason # the port cannot be cancelled, an exception will be raised (asynchronously). # # If you want to know when the connection has been cancelled, it will no # longer be present in the #active_remotes list. If you want to block until # the port is no longer active, you could do something like this: # # ssh.forward.cancel_remote(1234, "0.0.0.0") # ssh.loop { ssh.forward.active_remotes.include?([1234, "0.0.0.0"]) } def cancel_remote(port, host="127.0.0.1") session.send_global_request("cancel-tcpip-forward", :string, host, :long, port) do |success, response| if success @remote_forwarded_ports.delete([port, host]) else raise Net::SSH::Exception, "could not cancel remote forward request on #{host}:#{port}" end end end # Returns all active forwarded remote ports. The returned value is an # array of two-element tuples, where the first element is the port on the # remote host and the second is the bind address. def active_remotes @remote_forwarded_ports.keys end # Returns all active remote forwarded ports and where they forward to. The # returned value is a hash from [, ] # to [, ]. def active_remote_destinations @remote_forwarded_ports.inject({}) do |result, (remote, local)| result[[local.port, local.host]] = remote result end end # Enables SSH agent forwarding on the given channel. The forwarded agent # will remain active even after the channel closes--the channel is only # used as the transport for enabling the forwarded connection. You should # never need to call this directly--it is called automatically the first # time a session channel is opened, when the connection was created with # :forward_agent set to true: # # Net::SSH.start("remote.host", "me", :forward_agent => true) do |ssh| # ssh.open_channel do |ch| # # agent will be automatically forwarded by this point # end # ssh.loop # end def agent(channel) return if @agent_forwarded @agent_forwarded = true channel.send_channel_request("auth-agent-req@openssh.com") do |achannel, success| if success debug { "authentication agent forwarding is active" } else achannel.send_channel_request("auth-agent-req") do |a2channel, success2| if success2 debug { "authentication agent forwarding is active" } else error { "could not establish forwarding of authentication agent" } end end end end end private # Perform setup operations that are common to all forwarded channels. # +client+ is a socket, +channel+ is the channel that was just created, # and +type+ is an arbitrary string describing the type of the channel. def prepare_client(client, channel, type) client.extend(Net::SSH::BufferedIo) client.extend(Net::SSH::ForwardedBufferedIo) client.logger = logger session.listen_to(client) channel[:socket] = client channel.on_data do |ch, data| debug { "data:#{data.length} on #{type} forwarded channel" } ch[:socket].enqueue(data) end channel.on_eof do |ch| debug { "eof #{type} on #{type} forwarded channel" } begin ch[:socket].send_pending ch[:socket].shutdown Socket::SHUT_WR rescue IOError => e if e.message =~ /closed/ then debug { "epipe in on_eof => shallowing exception:#{e}" } else raise end rescue Errno::EPIPE => e debug { "epipe in on_eof => shallowing exception:#{e}" } rescue Errno::ENOTCONN => e debug { "enotconn in on_eof => shallowing exception:#{e}" } end end channel.on_close do |ch| debug { "closing #{type} forwarded channel" } ch[:socket].close if !client.closed? session.stop_listening_to(ch[:socket]) end channel.on_process do |ch| if ch[:socket].closed? ch.info { "#{type} forwarded connection closed" } ch.close elsif ch[:socket].available > 0 data = ch[:socket].read_available(8192) ch.debug { "read #{data.length} bytes from client, sending over #{type} forwarded connection" } ch.send_data(data) end end end # not a real socket, so use a simpler behaviour def prepare_simple_client(client, channel, type) channel[:socket] = client channel.on_data do |ch, data| ch.debug { "data:#{data.length} on #{type} forwarded channel" } ch[:socket].send(data) end channel.on_process do |ch| data = ch[:socket].read(8192) if data ch.debug { "read #{data.length} bytes from client, sending over #{type} forwarded connection" } ch.send_data(data) end end end # The callback used when a new "forwarded-tcpip" channel is requested # by the server. This will open a new socket to the host/port specified # when the forwarded connection was first requested. def forwarded_tcpip(session, channel, packet) connected_address = packet.read_string connected_port = packet.read_long originator_address = packet.read_string originator_port = packet.read_long remote = @remote_forwarded_ports[[connected_port, connected_address]] if remote.nil? raise Net::SSH::ChannelOpenFailed.new(1, "unknown request from remote forwarded connection on #{connected_address}:#{connected_port}") end client = TCPSocket.new(remote.host, remote.port) info { "connected #{connected_address}:#{connected_port} originator #{originator_address}:#{originator_port}" } prepare_client(client, channel, :remote) rescue SocketError => err raise Net::SSH::ChannelOpenFailed.new(2, "could not connect to remote host (#{remote.host}:#{remote.port}): #{err.message}") end # The callback used when an auth-agent channel is requested by the server. def auth_agent_channel(session, channel, packet) info { "opening auth-agent channel" } channel[:invisible] = true begin agent = Authentication::Agent.connect(logger, session.options[:agent_socket_factory]) if (agent.socket.is_a? ::IO) prepare_client(agent.socket, channel, :agent) else prepare_simple_client(agent.socket, channel, :agent) end rescue Exception => e error { "attempted to connect to agent but failed: #{e.class.name} (#{e.message})" } raise Net::SSH::ChannelOpenFailed.new(2, "could not connect to authentication agent") end end end end; end; end net-ssh-4.2.0/lib/net/ssh/test.rb000066400000000000000000000065441315376572000165550ustar00rootroot00000000000000require 'net/ssh/transport/session' require 'net/ssh/connection/session' require 'net/ssh/test/kex' require 'net/ssh/test/socket' module Net; module SSH # This module may be used in unit tests, for when you want to test that your # SSH state machines are really doing what you expect they are doing. You will # typically include this module in your unit test class, and then build a # "story" of expected sends and receives: # # require 'minitest/autorun' # require 'net/ssh/test' # # class MyTest < Minitest::Test # include Net::SSH::Test # # def test_exec_via_channel_works # story do |session| # channel = session.opens_channel # channel.sends_exec "ls" # channel.gets_data "result of ls" # channel.gets_close # channel.sends_close # end # # assert_scripted do # result = nil # # connection.open_channel do |ch| # ch.exec("ls") do |success| # ch.on_data { |c, data| result = data } # ch.on_close { |c| c.close } # end # end # # connection.loop # assert_equal "result of ls", result # end # end # end # # See Net::SSH::Test::Channel and Net::SSH::Test::Script for more options. # # Note that the Net::SSH::Test system is rather finicky yet, and can be kind # of frustrating to get working. Any suggestions for improvement will be # welcome! module Test # If a block is given, yields the script for the test socket (#socket). # Otherwise, simply returns the socket's script. See Net::SSH::Test::Script. def story Net::SSH::Test::Extensions::IO.with_test_extension { yield socket.script if block_given? } return socket.script end # Returns the test socket instance to use for these tests (see # Net::SSH::Test::Socket). def socket(options={}) @socket ||= Net::SSH::Test::Socket.new end # Returns the connection session (Net::SSH::Connection::Session) for use # in these tests. It is a fully functional SSH session, operating over # a mock socket (#socket). def connection(options={}) @connection ||= Net::SSH::Connection::Session.new(transport(options), options) end # Returns the transport session (Net::SSH::Transport::Session) for use # in these tests. It is a fully functional SSH transport session, operating # over a mock socket (#socket). def transport(options={}) @transport ||= Net::SSH::Transport::Session.new( options[:host] || "localhost", options.merge(kex: "test", host_key: "ssh-rsa", verify_host_key: false, proxy: socket(options)) ) end # First asserts that a story has been described (see #story). Then yields, # and then asserts that all items described in the script have been # processed. Typically, this is called immediately after a story has # been built, and the SSH commands being tested are then executed within # the block passed to this assertion. def assert_scripted raise "there is no script to be processed" if socket.script.events.empty? Net::SSH::Test::Extensions::IO.with_test_extension { yield } assert socket.script.events.empty?, "there should not be any remaining scripted events, but there are still #{socket.script.events.length} pending" end end end; end net-ssh-4.2.0/lib/net/ssh/test/000077500000000000000000000000001315376572000162175ustar00rootroot00000000000000net-ssh-4.2.0/lib/net/ssh/test/channel.rb000066400000000000000000000114541315376572000201610ustar00rootroot00000000000000module Net; module SSH; module Test # A mock channel, used for scripting actions in tests. It wraps a # Net::SSH::Test::Script instance, and delegates to it for the most part. # This class has little real functionality on its own, but rather acts as # a convenience for scripting channel-related activity for later comparison # in a unit test. # # story do |session| # channel = session.opens_channel # channel.sends_exec "ls" # channel.gets_data "result of ls" # channel.gets_extended_data "some error coming from ls" # channel.gets_close # channel.sends_close # end class Channel # The Net::SSH::Test::Script instance employed by this mock channel. attr_reader :script # Sets the local-id of this channel object (the id assigned by the client). attr_writer :local_id # Sets the remote-id of this channel object (the id assigned by the mock-server). attr_writer :remote_id # Creates a new Test::Channel instance on top of the given +script+ (which # must be a Net::SSH::Test::Script instance). def initialize(script) @script = script @local_id = @remote_id = nil end # Returns the local (client-assigned) id for this channel, or a Proc object # that will return the local-id later if the local id has not yet been set. # (See Net::SSH::Test::Packet#instantiate!.) def local_id @local_id || Proc.new { @local_id or raise "local-id has not been set yet!" } end # Returns the remote (server-assigned) id for this channel, or a Proc object # that will return the remote-id later if the remote id has not yet been set. # (See Net::SSH::Test::Packet#instantiate!.) def remote_id @remote_id || Proc.new { @remote_id or raise "remote-id has not been set yet!" } end # Because adjacent calls to #gets_data will sometimes cause the data packets # to be concatenated (causing expectations in tests to fail), you may # need to separate those calls with calls to #inject_remote_delay! (which # essentially just mimics receiving an empty data packet): # # channel.gets_data "abcdefg" # channel.inject_remote_delay! # channel.gets_data "hijklmn" def inject_remote_delay! gets_data("") end # Scripts the sending of an "exec" channel request packet to the mock # server. If +reply+ is true, then the server is expected to reply to the # request, otherwise no response to this request will be sent. If +success+ # is +true+, then the request will be successful, otherwise a failure will # be scripted. # # channel.sends_exec "ls -l" def sends_exec(command, reply=true, success=true) script.sends_channel_request(self, "exec", reply, command, success) end # Scripts the sending of a "subsystem" channel request packet to the mock # server. See #sends_exec for a discussion of the meaning of the +reply+ # and +success+ arguments. # # channel.sends_subsystem "sftp" def sends_subsystem(subsystem, reply=true, success=true) script.sends_channel_request(self, "subsystem", reply, subsystem, success) end # Scripts the sending of a data packet across the channel. # # channel.sends_data "foo" def sends_data(data) script.sends_channel_data(self, data) end # Scripts the sending of an EOF packet across the channel. # # channel.sends_eof def sends_eof script.sends_channel_eof(self) end # Scripts the sending of a "channel close" packet across the channel. # # channel.sends_close def sends_close script.sends_channel_close(self) end # Scripts the sending of a "request pty" request packet across the channel. # # channel.sends_request_pty def sends_request_pty script.sends_channel_request_pty(self) end # Scripts the reception of a channel data packet from the remote end. # # channel.gets_data "bar" def gets_data(data) script.gets_channel_data(self, data) end # Scripts the reception of a channel extended data packet from the remote # end. # # channel.gets_extended_data "whoops" def gets_extended_data(data) script.gets_channel_extended_data(self, data) end # Scripts the reception of an "exit-status" channel request packet. # # channel.gets_exit_status(127) def gets_exit_status(status=0) script.gets_channel_request(self, "exit-status", false, status) end # Scripts the reception of an EOF packet from the remote end. # # channel.gets_eof def gets_eof script.gets_channel_eof(self) end # Scripts the reception of a "channel close" packet from the remote end. # # channel.gets_close def gets_close script.gets_channel_close(self) end end end; end; endnet-ssh-4.2.0/lib/net/ssh/test/extensions.rb000066400000000000000000000141011315376572000207400ustar00rootroot00000000000000require 'net/ssh/buffer' require 'net/ssh/packet' require 'net/ssh/buffered_io' require 'net/ssh/connection/channel' require 'net/ssh/connection/constants' require 'net/ssh/transport/constants' require 'net/ssh/transport/packet_stream' module Net; module SSH; module Test # A collection of modules used to extend/override the default behavior of # Net::SSH internals for ease of testing. As a consumer of Net::SSH, you'll # never need to use this directly--they're all used under the covers by # the Net::SSH::Test system. module Extensions # An extension to Net::SSH::BufferedIo (assumes that the underlying IO # is actually a StringIO). Facilitates unit testing. module BufferedIo # Returns +true+ if the position in the stream is less than the total # length of the stream. def select_for_read? pos < size end # Set this to +true+ if you want the IO to pretend to be available for writing attr_accessor :select_for_write # Set this to +true+ if you want the IO to pretend to be in an error state attr_accessor :select_for_error alias select_for_write? select_for_write alias select_for_error? select_for_error end # An extension to Net::SSH::Transport::PacketStream (assumes that the # underlying IO is actually a StringIO). Facilitates unit testing. module PacketStream include BufferedIo # make sure we get the extensions here, too def self.included(base) #:nodoc: base.send :alias_method, :real_available_for_read?, :available_for_read? base.send :alias_method, :available_for_read?, :test_available_for_read? base.send :alias_method, :real_enqueue_packet, :enqueue_packet base.send :alias_method, :enqueue_packet, :test_enqueue_packet base.send :alias_method, :real_poll_next_packet, :poll_next_packet base.send :alias_method, :poll_next_packet, :test_poll_next_packet end # Called when another packet should be inspected from the current # script. If the next packet is a remote packet, it pops it off the # script and shoves it onto this IO object, making it available to # be read. def idle! return false unless script.next(:first) if script.next(:first).remote? self.string << script.next.to_s self.pos = pos end return true end # The testing version of Net::SSH::Transport::PacketStream#available_for_read?. # Returns true if there is data pending to be read. Otherwise calls #idle!. def test_available_for_read? return true if select_for_read? idle! false end # The testing version of Net::SSH::Transport::PacketStream#enqueued_packet. # Simply calls Net::SSH::Test::Script#process on the packet. def test_enqueue_packet(payload) packet = Net::SSH::Buffer.new(payload.to_s) script.process(packet) end # The testing version of Net::SSH::Transport::PacketStream#poll_next_packet. # Reads the next available packet from the IO object and returns it. def test_poll_next_packet return nil if available <= 0 packet = Net::SSH::Buffer.new(read_available(4)) length = packet.read_long Net::SSH::Packet.new(read_available(length)) end end # An extension to Net::SSH::Connection::Channel. Facilitates unit testing. module Channel def self.included(base) #:nodoc: base.send :alias_method, :send_data_for_real, :send_data base.send :alias_method, :send_data, :send_data_for_test end # The testing version of Net::SSH::Connection::Channel#send_data. Calls # the original implementation, and then immediately enqueues the data for # output so that scripted sends are properly interpreted as discrete # (rather than concatenated) data packets. def send_data_for_test(data) send_data_for_real(data) enqueue_pending_output end end # An extension to the built-in ::IO class. Simply redefines IO.select # so that it can be scripted in Net::SSH unit tests. module IO def self.included(base) #:nodoc: base.extend(ClassMethods) end @extension_enabled = false def self.with_test_extension(&block) orig_value = @extension_enabled @extension_enabled = true begin yield ensure @extension_enabled = orig_value end end def self.extension_enabled? @extension_enabled end module ClassMethods def self.extended(obj) #:nodoc: class < "des-ede3-cbc", "blowfish-cbc" => "bf-cbc", "aes256-cbc" => "aes-256-cbc", "aes192-cbc" => "aes-192-cbc", "aes128-cbc" => "aes-128-cbc", "idea-cbc" => "idea-cbc", "cast128-cbc" => "cast-cbc", "rijndael-cbc@lysator.liu.se" => "aes-256-cbc", "arcfour128" => "rc4", "arcfour256" => "rc4", "arcfour512" => "rc4", "arcfour" => "rc4", "3des-ctr" => "des-ede3", "blowfish-ctr" => "bf-ecb", "aes256-ctr" => "aes-256-ecb", "aes192-ctr" => "aes-192-ecb", "aes128-ctr" => "aes-128-ecb", "cast128-ctr" => "cast5-ecb", "none" => "none", } # Ruby's OpenSSL bindings always return a key length of 16 for RC4 ciphers # resulting in the error: OpenSSL::CipherError: key length too short. # The following ciphers will override this key length. KEY_LEN_OVERRIDE = { "arcfour256" => 32, "arcfour512" => 64 } # Returns true if the underlying OpenSSL library supports the given cipher, # and false otherwise. def self.supported?(name) ossl_name = SSH_TO_OSSL[name] or raise NotImplementedError, "unimplemented cipher `#{name}'" return true if ossl_name == "none" return OpenSSL::Cipher.ciphers.include?(ossl_name) end # Retrieves a new instance of the named algorithm. The new instance # will be initialized using an iv and key generated from the given # iv, key, shared, hash and digester values. Additionally, the # cipher will be put into encryption or decryption mode, based on the # value of the +encrypt+ parameter. def self.get(name, options={}) ossl_name = SSH_TO_OSSL[name] or raise NotImplementedError, "unimplemented cipher `#{name}'" return IdentityCipher if ossl_name == "none" cipher = OpenSSL::Cipher.new(ossl_name) cipher.send(options[:encrypt] ? :encrypt : :decrypt) cipher.padding = 0 cipher.extend(Net::SSH::Transport::CTR) if (name =~ /-ctr(@openssh.org)?$/) cipher.iv = Net::SSH::Transport::KeyExpander.expand_key(cipher.iv_len, options[:iv], options) if ossl_name != "rc4" key_len = KEY_LEN_OVERRIDE[name] || cipher.key_len cipher.key_len = key_len cipher.key = Net::SSH::Transport::KeyExpander.expand_key(key_len, options[:key], options) cipher.update(" " * 1536) if (ossl_name == "rc4" && name != "arcfour") return cipher end # Returns a two-element array containing the [ key-length, # block-size ] for the named cipher algorithm. If the cipher # algorithm is unknown, or is "none", 0 is returned for both elements # of the tuple. # if :iv_len option is supplied the third return value will be ivlen def self.get_lengths(name, options = {}) ossl_name = SSH_TO_OSSL[name] if ossl_name.nil? || ossl_name == "none" result = [0, 0] result << 0 if options[:iv_len] else cipher = OpenSSL::Cipher.new(ossl_name) key_len = KEY_LEN_OVERRIDE[name] || cipher.key_len cipher.key_len = key_len result = [key_len, ossl_name=="rc4" ? 8 : cipher.block_size] result << cipher.iv_len if options[:iv_len] end result end end end; end; end net-ssh-4.2.0/lib/net/ssh/transport/constants.rb000066400000000000000000000012351315376572000216360ustar00rootroot00000000000000module Net; module SSH; module Transport module Constants #-- # Transport layer generic messages #++ DISCONNECT = 1 IGNORE = 2 UNIMPLEMENTED = 3 DEBUG = 4 SERVICE_REQUEST = 5 SERVICE_ACCEPT = 6 #-- # Algorithm negotiation messages #++ KEXINIT = 20 NEWKEYS = 21 #-- # Key exchange method specific messages #++ KEXDH_INIT = 30 KEXDH_REPLY = 31 KEXECDH_INIT = 30 KEXECDH_REPLY = 31 end end; end; end net-ssh-4.2.0/lib/net/ssh/transport/ctr.rb000066400000000000000000000037751315376572000204250ustar00rootroot00000000000000require 'openssl' module Net::SSH::Transport # Pure-Ruby implementation of Stateful Decryption Counter(SDCTR) Mode # for Block Ciphers. See RFC4344 for detail. module CTR def self.extended(orig) orig.instance_eval { @remaining = "" @counter = nil @counter_len = orig.block_size orig.encrypt orig.padding = 0 singleton_class.send(:alias_method, :_update, :update) singleton_class.send(:private, :_update) singleton_class.send(:undef_method, :update) def iv @counter end def iv_len block_size end def iv=(iv_s) @counter = iv_s if @counter.nil? end def encrypt # DO NOTHING (always set to "encrypt") end def decrypt # DO NOTHING (always set to "encrypt") end def padding=(pad) # DO NOTHING (always 0) end def reset @remaining = "" end def update(data) @remaining += data encrypted = "" while @remaining.bytesize >= block_size encrypted += xor!(@remaining.slice!(0, block_size), _update(@counter)) increment_counter! end encrypted end def final unless @remaining.empty? s = xor!(@remaining, _update(@counter)) else s = "" end @remaining = "" s end def xor!(s1, s2) s = [] s1.unpack('Q*').zip(s2.unpack('Q*')) {|a,b| s.push(a^b) } s.pack('Q*') end singleton_class.send(:private, :xor!) def increment_counter! c = @counter_len while ((c -= 1) > 0) if @counter.setbyte(c, (@counter.getbyte(c) + 1) & 0xff) != 0 break end end end singleton_class.send(:private, :increment_counter!) } end end end net-ssh-4.2.0/lib/net/ssh/transport/hmac.rb000066400000000000000000000036271315376572000205410ustar00rootroot00000000000000require 'net/ssh/transport/key_expander' require 'net/ssh/transport/hmac/md5' require 'net/ssh/transport/hmac/md5_96' require 'net/ssh/transport/hmac/sha1' require 'net/ssh/transport/hmac/sha1_96' require 'net/ssh/transport/hmac/sha2_256' require 'net/ssh/transport/hmac/sha2_256_96' require 'net/ssh/transport/hmac/sha2_512' require 'net/ssh/transport/hmac/sha2_512_96' require 'net/ssh/transport/hmac/ripemd160' require 'net/ssh/transport/hmac/none' # Implements a simple factory interface for fetching hmac implementations, or # for finding the key lengths for hmac implementations.s module Net::SSH::Transport::HMAC # The mapping of SSH hmac algorithms to their implementations MAP = { 'hmac-md5' => MD5, 'hmac-md5-96' => MD5_96, 'hmac-sha1' => SHA1, 'hmac-sha1-96' => SHA1_96, 'hmac-ripemd160' => RIPEMD160, 'hmac-ripemd160@openssh.com' => RIPEMD160, 'none' => None } # add mapping to sha2 hmac algorithms if they're available MAP['hmac-sha2-256'] = SHA2_256 if defined?(::Net::SSH::Transport::HMAC::SHA2_256) MAP['hmac-sha2-256-96'] = SHA2_256_96 if defined?(::Net::SSH::Transport::HMAC::SHA2_256_96) MAP['hmac-sha2-512'] = SHA2_512 if defined?(::Net::SSH::Transport::HMAC::SHA2_512) MAP['hmac-sha2-512-96'] = SHA2_512_96 if defined?(::Net::SSH::Transport::HMAC::SHA2_512_96) # Retrieves a new hmac instance of the given SSH type (+name+). If +key+ is # given, the new instance will be initialized with that key. def self.get(name, key="", parameters = {}) impl = MAP[name] or raise ArgumentError, "hmac not found: #{name.inspect}" impl.new(Net::SSH::Transport::KeyExpander.expand_key(impl.key_length, key, parameters)) end # Retrieves the key length for the hmac of the given SSH type (+name+). def self.key_length(name) impl = MAP[name] or raise ArgumentError, "hmac not found: #{name.inspect}" impl.key_length end end net-ssh-4.2.0/lib/net/ssh/transport/hmac/000077500000000000000000000000001315376572000202045ustar00rootroot00000000000000net-ssh-4.2.0/lib/net/ssh/transport/hmac/abstract.rb000066400000000000000000000040541315376572000223370ustar00rootroot00000000000000require 'openssl' require 'openssl/digest' module Net; module SSH; module Transport; module HMAC # The base class of all OpenSSL-based HMAC algorithm wrappers. class Abstract class < DiffieHellmanGroupExchangeSHA1, 'diffie-hellman-group1-sha1' => DiffieHellmanGroup1SHA1, 'diffie-hellman-group14-sha1' => DiffieHellmanGroup14SHA1, } if defined?(DiffieHellmanGroupExchangeSHA256) MAP['diffie-hellman-group-exchange-sha256'] = DiffieHellmanGroupExchangeSHA256 end if defined?(OpenSSL::PKey::EC) require 'net/ssh/transport/kex/ecdh_sha2_nistp256' require 'net/ssh/transport/kex/ecdh_sha2_nistp384' require 'net/ssh/transport/kex/ecdh_sha2_nistp521' MAP['ecdh-sha2-nistp256'] = EcdhSHA2NistP256 MAP['ecdh-sha2-nistp384'] = EcdhSHA2NistP384 MAP['ecdh-sha2-nistp521'] = EcdhSHA2NistP521 end end end net-ssh-4.2.0/lib/net/ssh/transport/kex/000077500000000000000000000000001315376572000200635ustar00rootroot00000000000000net-ssh-4.2.0/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb000066400000000000000000000027411315376572000256370ustar00rootroot00000000000000require 'net/ssh/transport/kex/diffie_hellman_group1_sha1' module Net; module SSH; module Transport; module Kex # A key-exchange service implementing the "diffie-hellman-group14-sha1" # key-exchange algorithm. (defined in RFC 4253) class DiffieHellmanGroup14SHA1 < DiffieHellmanGroup1SHA1 include Constants, Loggable # The value of 'P', as a string, in hexadecimal P_s = "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" + "C4C6628B" "80DC1CD1" "29024E08" "8A67CC74" + "020BBEA6" "3B139B22" "514A0879" "8E3404DD" + "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" + "4FE1356D" "6D51C245" "E485B576" "625E7EC6" + "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" + "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" + "49286651" "ECE45B3D" "C2007CB8" "A163BF05" + "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F" + "83655D23" "DCA3AD96" "1C62F356" "208552BB" + "9ED52907" "7096966D" "670C354E" "4ABC9804" + "F1746C08" "CA18217C" "32905E46" "2E36CE3B" + "E39E772C" "180E8603" "9B2783A2" "EC07A28F" + "B5C55DF0" "6F4C52C9" "DE2BCBF6" "95581718" + "3995497C" "EA956AE5" "15D22618" "98FA0510" + "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF" # The radix in which P_s represents the value of P P_r = 16 # The group constant G = 2 private def get_p OpenSSL::BN.new(P_s, P_r) end def get_g G end end end; end; end; end net-ssh-4.2.0/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb000066400000000000000000000163021315376572000255510ustar00rootroot00000000000000require 'net/ssh/buffer' require 'net/ssh/errors' require 'net/ssh/loggable' require 'net/ssh/transport/openssl' require 'net/ssh/transport/constants' module Net; module SSH; module Transport; module Kex # A key-exchange service implementing the "diffie-hellman-group1-sha1" # key-exchange algorithm. class DiffieHellmanGroup1SHA1 include Constants, Loggable # The value of 'P', as a string, in hexadecimal P_s = "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" + "C4C6628B" "80DC1CD1" "29024E08" "8A67CC74" + "020BBEA6" "3B139B22" "514A0879" "8E3404DD" + "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" + "4FE1356D" "6D51C245" "E485B576" "625E7EC6" + "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" + "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" + "49286651" "ECE65381" "FFFFFFFF" "FFFFFFFF" # The radix in which P_s represents the value of P P_r = 16 # The group constant G = 2 attr_reader :p attr_reader :g attr_reader :digester attr_reader :algorithms attr_reader :connection attr_reader :data attr_reader :dh # Create a new instance of the DiffieHellmanGroup1SHA1 algorithm. # The data is a Hash of symbols representing information # required by this algorithm, which was acquired during earlier # processing. def initialize(algorithms, connection, data) @p = get_p @g = get_g @digester = OpenSSL::Digest::SHA1 @algorithms = algorithms @connection = connection @data = data.dup @dh = generate_key @logger = @data.delete(:logger) end # Perform the key-exchange for the given session, with the given # data. This method will return a hash consisting of the # following keys: # # * :session_id # * :server_key # * :shared_secret # * :hashing_algorithm # # The caller is expected to be able to understand how to use these # deliverables. def exchange_keys result = send_kexinit verify_server_key(result[:server_key]) session_id = verify_signature(result) confirm_newkeys return { session_id: session_id, server_key: result[:server_key], shared_secret: result[:shared_secret], hashing_algorithm: digester } end private def get_p OpenSSL::BN.new(P_s, P_r) end def get_g G end # Returns the DH key parameters for the current connection. def get_parameters [p, g] end # Returns the INIT/REPLY constants used by this algorithm. def get_message_types [KEXDH_INIT, KEXDH_REPLY] end # Build the signature buffer to use when verifying a signature from # the server. def build_signature_buffer(result) response = Net::SSH::Buffer.new response.write_string data[:client_version_string], data[:server_version_string], data[:client_algorithm_packet], data[:server_algorithm_packet], result[:key_blob] response.write_bignum dh.pub_key, result[:server_dh_pubkey], result[:shared_secret] response end # Generate a DH key with a private key consisting of the given # number of bytes. def generate_key #:nodoc: dh = OpenSSL::PKey::DH.new if dh.respond_to?(:set_pqg) p, g = get_parameters dh.set_pqg(p, nil, g) else dh.p, dh.g = get_parameters end dh.generate_key! until dh.valid? && dh.priv_key.num_bytes == data[:need_bytes] if dh.respond_to?(:set_key) dh.set_key(nil, OpenSSL::BN.rand(data[:need_bytes] * 8)) else dh.priv_key = OpenSSL::BN.rand(data[:need_bytes] * 8) end dh.generate_key! end dh end # Send the KEXDH_INIT message, and expect the KEXDH_REPLY. Return the # resulting buffer. # # Parse the buffer from a KEXDH_REPLY message, returning a hash of # the extracted values. def send_kexinit #:nodoc: init, reply = get_message_types # send the KEXDH_INIT message buffer = Net::SSH::Buffer.from(:byte, init, :bignum, dh.pub_key) connection.send_message(buffer) # expect the KEXDH_REPLY message buffer = connection.next_message raise Net::SSH::Exception, "expected REPLY" unless buffer.type == reply result = Hash.new result[:key_blob] = buffer.read_string result[:server_key] = Net::SSH::Buffer.new(result[:key_blob]).read_key result[:server_dh_pubkey] = buffer.read_bignum result[:shared_secret] = OpenSSL::BN.new(dh.compute_key(result[:server_dh_pubkey]), 2) sig_buffer = Net::SSH::Buffer.new(buffer.read_string) sig_type = sig_buffer.read_string if sig_type != algorithms.host_key raise Net::SSH::Exception, "host key algorithm mismatch for signature " + "'#{sig_type}' != '#{algorithms.host_key}'" end result[:server_sig] = sig_buffer.read_string return result end # Verify that the given key is of the expected type, and that it # really is the key for the session's host. Raise Net::SSH::Exception # if it is not. def verify_server_key(key) #:nodoc: if key.ssh_type != algorithms.host_key raise Net::SSH::Exception, "host key algorithm mismatch " + "'#{key.ssh_type}' != '#{algorithms.host_key}'" end blob, fingerprint = generate_key_fingerprint(key) unless connection.host_key_verifier.verify(key: key, key_blob: blob, fingerprint: fingerprint, session: connection) raise Net::SSH::Exception, "host key verification failed" end end def generate_key_fingerprint(key) blob = Net::SSH::Buffer.from(:key, key).to_s fingerprint = OpenSSL::Digest::MD5.hexdigest(blob).scan(/../).join(":") [blob, fingerprint] rescue ::Exception => e [nil, "(could not generate fingerprint: #{e.message})"] end # Verify the signature that was received. Raise Net::SSH::Exception # if the signature could not be verified. Otherwise, return the new # session-id. def verify_signature(result) #:nodoc: response = build_signature_buffer(result) hash = @digester.digest(response.to_s) unless result[:server_key].ssh_do_verify(result[:server_sig], hash) raise Net::SSH::Exception, "could not verify server signature" end return hash end # Send the NEWKEYS message, and expect the NEWKEYS message in # reply. def confirm_newkeys #:nodoc: # send own NEWKEYS message first (the wodSSHServer won't send first) response = Net::SSH::Buffer.new response.write_byte(NEWKEYS) connection.send_message(response) # wait for the server's NEWKEYS message buffer = connection.next_message raise Net::SSH::Exception, "expected NEWKEYS" unless buffer.type == NEWKEYS end end end; end; end; end net-ssh-4.2.0/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb000066400000000000000000000051451315376572000273350ustar00rootroot00000000000000require 'net/ssh/errors' require 'net/ssh/transport/constants' require 'net/ssh/transport/kex/diffie_hellman_group1_sha1' module Net::SSH::Transport::Kex # A key-exchange service implementing the # "diffie-hellman-group-exchange-sha1" key-exchange algorithm. class DiffieHellmanGroupExchangeSHA1 < DiffieHellmanGroup1SHA1 MINIMUM_BITS = 1024 MAXIMUM_BITS = 8192 KEXDH_GEX_GROUP = 31 KEXDH_GEX_INIT = 32 KEXDH_GEX_REPLY = 33 KEXDH_GEX_REQUEST = 34 private # Compute the number of bits needed for the given number of bytes. def compute_need_bits # for Compatibility: OpenSSH requires (need_bits * 2 + 1) length of parameter need_bits = data[:need_bytes] * 8 * 2 + 1 data[:minimum_dh_bits] ||= MINIMUM_BITS if need_bits < data[:minimum_dh_bits] need_bits = data[:minimum_dh_bits] elsif need_bits > MAXIMUM_BITS need_bits = MAXIMUM_BITS end data[:need_bits ] = need_bits data[:need_bytes] = need_bits / 8 end # Returns the DH key parameters for the given session. def get_parameters compute_need_bits # request the DH key parameters for the given number of bits. buffer = Net::SSH::Buffer.from(:byte, KEXDH_GEX_REQUEST, :long, data[:minimum_dh_bits], :long, data[:need_bits], :long, MAXIMUM_BITS) connection.send_message(buffer) buffer = connection.next_message unless buffer.type == KEXDH_GEX_GROUP raise Net::SSH::Exception, "expected KEXDH_GEX_GROUP, got #{buffer.type}" end p = buffer.read_bignum g = buffer.read_bignum [p, g] end # Returns the INIT/REPLY constants used by this algorithm. def get_message_types [KEXDH_GEX_INIT, KEXDH_GEX_REPLY] end # Build the signature buffer to use when verifying a signature from # the server. def build_signature_buffer(result) response = Net::SSH::Buffer.new response.write_string data[:client_version_string], data[:server_version_string], data[:client_algorithm_packet], data[:server_algorithm_packet], result[:key_blob] response.write_long MINIMUM_BITS, data[:need_bits], MAXIMUM_BITS response.write_bignum dh.p, dh.g, dh.pub_key, result[:server_dh_pubkey], result[:shared_secret] response end end end net-ssh-4.2.0/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb000066400000000000000000000007041315376572000275050ustar00rootroot00000000000000require 'net/ssh/transport/kex/diffie_hellman_group_exchange_sha1' module Net::SSH::Transport::Kex if defined?(OpenSSL::Digest::SHA256) # A key-exchange service implementing the # "diffie-hellman-group-exchange-sha256" key-exchange algorithm. class DiffieHellmanGroupExchangeSHA256 < DiffieHellmanGroupExchangeSHA1 def initialize(*args) super(*args) @digester = OpenSSL::Digest::SHA256 end end end end net-ssh-4.2.0/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb000066400000000000000000000060601315376572000237040ustar00rootroot00000000000000require 'net/ssh/transport/constants' require 'net/ssh/transport/kex/diffie_hellman_group1_sha1' module Net; module SSH; module Transport; module Kex # A key-exchange service implementing the "ecdh-sha2-nistp256" # key-exchange algorithm. (defined in RFC 5656) class EcdhSHA2NistP256 < DiffieHellmanGroup1SHA1 include Constants, Loggable attr_reader :ecdh def digester OpenSSL::Digest::SHA256 end def curve_name OpenSSL::PKey::EC::CurveNameAlias['nistp256'] end def initialize(algorithms, connection, data) @algorithms = algorithms @connection = connection @digester = digester @data = data.dup @ecdh = generate_key @logger = @data.delete(:logger) end private def get_message_types [KEXECDH_INIT, KEXECDH_REPLY] end def build_signature_buffer(result) response = Net::SSH::Buffer.new response.write_string data[:client_version_string], data[:server_version_string], data[:client_algorithm_packet], data[:server_algorithm_packet], result[:key_blob], ecdh.public_key.to_bn.to_s(2), result[:server_ecdh_pubkey] response.write_bignum result[:shared_secret] response end def generate_key #:nodoc: OpenSSL::PKey::EC.new(curve_name).generate_key end def send_kexinit #:nodoc: init, reply = get_message_types # send the KEXECDH_INIT message ## byte SSH_MSG_KEX_ECDH_INIT ## string Q_C, client's ephemeral public key octet string buffer = Net::SSH::Buffer.from(:byte, init, :mstring, ecdh.public_key.to_bn.to_s(2)) connection.send_message(buffer) # expect the following KEXECDH_REPLY message ## byte SSH_MSG_KEX_ECDH_REPLY ## string K_S, server's public host key ## string Q_S, server's ephemeral public key octet string ## string the signature on the exchange hash buffer = connection.next_message raise Net::SSH::Exception, "expected REPLY" unless buffer.type == reply result = Hash.new result[:key_blob] = buffer.read_string result[:server_key] = Net::SSH::Buffer.new(result[:key_blob]).read_key result[:server_ecdh_pubkey] = buffer.read_string # compute shared secret from server's public key and client's private key pk = OpenSSL::PKey::EC::Point.new(OpenSSL::PKey::EC.new(curve_name).group, OpenSSL::BN.new(result[:server_ecdh_pubkey], 2)) result[:shared_secret] = OpenSSL::BN.new(ecdh.dh_compute_key(pk), 2) sig_buffer = Net::SSH::Buffer.new(buffer.read_string) sig_type = sig_buffer.read_string if sig_type != algorithms.host_key raise Net::SSH::Exception, "host key algorithm mismatch for signature " + "'#{sig_type}' != '#{algorithms.host_key}'" end result[:server_sig] = sig_buffer.read_string return result end end end; end; end; end net-ssh-4.2.0/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb000066400000000000000000000005641315376572000237110ustar00rootroot00000000000000module Net; module SSH; module Transport; module Kex # A key-exchange service implementing the "ecdh-sha2-nistp256" # key-exchange algorithm. (defined in RFC 5656) class EcdhSHA2NistP384 < EcdhSHA2NistP256 def digester OpenSSL::Digest::SHA384 end def curve_name OpenSSL::PKey::EC::CurveNameAlias['nistp384'] end end end; end; end; end net-ssh-4.2.0/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb000066400000000000000000000005641315376572000237020ustar00rootroot00000000000000module Net; module SSH; module Transport; module Kex # A key-exchange service implementing the "ecdh-sha2-nistp521" # key-exchange algorithm. (defined in RFC 5656) class EcdhSHA2NistP521 < EcdhSHA2NistP256 def digester OpenSSL::Digest::SHA512 end def curve_name OpenSSL::PKey::EC::CurveNameAlias['nistp521'] end end end; end; end; end net-ssh-4.2.0/lib/net/ssh/transport/key_expander.rb000066400000000000000000000013131315376572000222750ustar00rootroot00000000000000module Net; module SSH; module Transport module KeyExpander # Generate a key value in accordance with the SSH2 specification. # (RFC4253 7.2. "Output from Key Exchange") def self.expand_key(bytes, start, options={}) if bytes == 0 return "" end k = start[0, bytes] return k if k.length >= bytes digester = options[:digester] or raise 'No digester supplied' shared = options[:shared] or raise 'No shared secret supplied' hash = options[:hash] or raise 'No hash supplied' while k.length < bytes step = digester.digest(shared + hash + k) bytes_needed = bytes - k.length k << step[0, bytes_needed] end return k end end end; end; end net-ssh-4.2.0/lib/net/ssh/transport/openssl.rb000066400000000000000000000167321315376572000213150ustar00rootroot00000000000000# -*- coding: utf-8 -*- require 'openssl' module OpenSSL # This class is originally defined in the OpenSSL module. As needed, methods # have been added to it by the Net::SSH module for convenience in dealing with # SSH functionality. class BN # Converts a BN object to a string. The format used is that which is # required by the SSH2 protocol. def to_ssh if zero? return [0].pack("N") else buf = to_s(2) if buf.getbyte(0)[7] == 1 return [buf.length+1, 0, buf].pack("NCA*") else return [buf.length, buf].pack("NA*") end end end end module PKey class PKey def fingerprint @fingerprint ||= OpenSSL::Digest::MD5.hexdigest(to_blob).scan(/../).join(":") end end # This class is originally defined in the OpenSSL module. As needed, methods # have been added to it by the Net::SSH module for convenience in dealing # with SSH functionality. class DH # Determines whether the pub_key for this key is valid. (This algorithm # lifted more-or-less directly from OpenSSH, dh.c, dh_pub_is_valid.) def valid? return false if pub_key.nil? || pub_key < 0 bits_set = 0 pub_key.num_bits.times { |i| bits_set += 1 if pub_key.bit_set?(i) } return ( bits_set > 1 && pub_key < p ) end end # This class is originally defined in the OpenSSL module. As needed, methods # have been added to it by the Net::SSH module for convenience in dealing # with SSH functionality. class RSA # Returns "ssh-rsa", which is the description of this key type used by the # SSH2 protocol. def ssh_type "ssh-rsa" end def ssh_signature_type ssh_type end # Converts the key to a blob, according to the SSH2 protocol. def to_blob @blob ||= Net::SSH::Buffer.from(:string, ssh_type, :bignum, e, :bignum, n).to_s end # Verifies the given signature matches the given data. def ssh_do_verify(sig, data) verify(OpenSSL::Digest::SHA1.new, sig, data) end # Returns the signature for the given data. def ssh_do_sign(data) sign(OpenSSL::Digest::SHA1.new, data) end end # This class is originally defined in the OpenSSL module. As needed, methods # have been added to it by the Net::SSH module for convenience in dealing # with SSH functionality. class DSA # Returns "ssh-dss", which is the description of this key type used by the # SSH2 protocol. def ssh_type "ssh-dss" end def ssh_signature_type ssh_type end # Converts the key to a blob, according to the SSH2 protocol. def to_blob @blob ||= Net::SSH::Buffer.from(:string, ssh_type, :bignum, p, :bignum, q, :bignum, g, :bignum, pub_key).to_s end # Verifies the given signature matches the given data. def ssh_do_verify(sig, data) sig_r = sig[0,20].unpack("H*")[0].to_i(16) sig_s = sig[20,20].unpack("H*")[0].to_i(16) a1sig = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Integer(sig_r), OpenSSL::ASN1::Integer(sig_s) ]) return verify(OpenSSL::Digest::DSS1.new, a1sig.to_der, data) end # Signs the given data. def ssh_do_sign(data) sig = sign( OpenSSL::Digest::DSS1.new, data) a1sig = OpenSSL::ASN1.decode( sig ) sig_r = a1sig.value[0].value.to_s(2) sig_s = a1sig.value[1].value.to_s(2) if sig_r.length > 20 || sig_s.length > 20 raise OpenSSL::PKey::DSAError, "bad sig size" end sig_r = "\0" * ( 20 - sig_r.length ) + sig_r if sig_r.length < 20 sig_s = "\0" * ( 20 - sig_s.length ) + sig_s if sig_s.length < 20 return sig_r + sig_s end end if defined?(OpenSSL::PKey::EC) # This class is originally defined in the OpenSSL module. As needed, methods # have been added to it by the Net::SSH module for convenience in dealing # with SSH functionality. class EC CurveNameAlias = { "nistp256" => "prime256v1", "nistp384" => "secp384r1", "nistp521" => "secp521r1", } CurveNameAliasInv = { "prime256v1" => "nistp256", "secp384r1" => "nistp384", "secp521r1" => "nistp521", } def self.read_keyblob(curve_name_in_type, buffer) curve_name_in_key = buffer.read_string unless curve_name_in_type == curve_name_in_key raise Net::SSH::Exception, "curve name mismatched (`#{curve_name_in_key}' with `#{curve_name_in_type}')" end public_key_oct = buffer.read_string begin key = OpenSSL::PKey::EC.new(OpenSSL::PKey::EC::CurveNameAlias[curve_name_in_key]) group = key.group point = OpenSSL::PKey::EC::Point.new(group, OpenSSL::BN.new(public_key_oct, 2)) key.public_key = point return key rescue OpenSSL::PKey::ECError raise NotImplementedError, "unsupported key type `#{type}'" end end # Returns the description of this key type used by the # SSH2 protocol, like "ecdsa-sha2-nistp256" def ssh_type "ecdsa-sha2-#{CurveNameAliasInv[self.group.curve_name]}" end def ssh_signature_type ssh_type end def digester if self.group.curve_name =~ /^[a-z]+(\d+)\w*\z/ curve_size = $1.to_i if curve_size <= 256 OpenSSL::Digest::SHA256.new elsif curve_size <= 384 OpenSSL::Digest::SHA384.new else OpenSSL::Digest::SHA512.new end else OpenSSL::Digest::SHA256.new end end private :digester # Converts the key to a blob, according to the SSH2 protocol. def to_blob @blob ||= Net::SSH::Buffer.from(:string, ssh_type, :string, CurveNameAliasInv[self.group.curve_name], :mstring, self.public_key.to_bn.to_s(2)).to_s @blob end # Verifies the given signature matches the given data. def ssh_do_verify(sig, data) digest = digester.digest(data) a1sig = nil begin sig_r_len = sig[0,4].unpack("H*")[0].to_i(16) sig_l_len = sig[4+sig_r_len,4].unpack("H*")[0].to_i(16) sig_r = sig[4,sig_r_len].unpack("H*")[0] sig_s = sig[4+sig_r_len+4,sig_l_len].unpack("H*")[0] a1sig = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Integer(sig_r.to_i(16)), OpenSSL::ASN1::Integer(sig_s.to_i(16)), ]) rescue end if a1sig == nil return false else dsa_verify_asn1(digest, a1sig.to_der) end end # Returns the signature for the given data. def ssh_do_sign(data) digest = digester.digest(data) sig = dsa_sign_asn1(digest) a1sig = OpenSSL::ASN1.decode( sig ) sig_r = a1sig.value[0].value sig_s = a1sig.value[1].value return Net::SSH::Buffer.from(:bignum, sig_r, :bignum, sig_s).to_s end end else class OpenSSL::PKey::ECError < RuntimeError # for compatibility with interpreters # without EC support (i.e. JRuby) end end end end net-ssh-4.2.0/lib/net/ssh/transport/packet_stream.rb000066400000000000000000000202231315376572000224420ustar00rootroot00000000000000require 'net/ssh/buffered_io' require 'net/ssh/errors' require 'net/ssh/packet' require 'net/ssh/ruby_compat' require 'net/ssh/transport/cipher_factory' require 'net/ssh/transport/hmac' require 'net/ssh/transport/state' module Net; module SSH; module Transport # A module that builds additional functionality onto the Net::SSH::BufferedIo # module. It adds SSH encryption, compression, and packet validation, as # per the SSH2 protocol. It also adds an abstraction for polling packets, # to allow for both blocking and non-blocking reads. module PacketStream PROXY_COMMAND_HOST_IP = ''.freeze include BufferedIo def self.extended(object) object.__send__(:initialize_ssh) end # The map of "hints" that can be used to modify the behavior of the packet # stream. For instance, when authentication succeeds, an "authenticated" # hint is set, which is used to determine whether or not to compress the # data when using the "delayed" compression algorithm. attr_reader :hints # The server state object, which encapsulates the algorithms used to interpret # packets coming from the server. attr_reader :server # The client state object, which encapsulates the algorithms used to build # packets to send to the server. attr_reader :client # The name of the client (local) end of the socket, as reported by the # socket. def client_name @client_name ||= begin sockaddr = getsockname begin Socket.getnameinfo(sockaddr, Socket::NI_NAMEREQD).first rescue begin Socket.getnameinfo(sockaddr).first rescue begin Socket.gethostbyname(Socket.gethostname).first rescue lwarn { "the client ipaddr/name could not be determined" } "unknown" end end end end end # The IP address of the peer (remote) end of the socket, as reported by # the socket. def peer_ip @peer_ip ||= if respond_to?(:getpeername) addr = getpeername Socket.getnameinfo(addr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV).first else PROXY_COMMAND_HOST_IP end end # Returns true if the IO is available for reading, and false otherwise. def available_for_read? result = Net::SSH::Compat.io_select([self], nil, nil, 0) result && result.first.any? end # Returns the next full packet. If the mode parameter is :nonblock (the # default), then this will return immediately, whether a packet is # available or not, and will return nil if there is no packet ready to be # returned. If the mode parameter is :block, then this method will block # until a packet is available. def next_packet(mode=:nonblock) case mode when :nonblock then packet = poll_next_packet return packet if packet if available_for_read? if fill <= 0 result = poll_next_packet if result.nil? raise Net::SSH::Disconnect, "connection closed by remote host" else return result end end end poll_next_packet when :block then loop do packet = poll_next_packet return packet if packet loop do result = Net::SSH::Compat.io_select([self]) or next break if result.first.any? end if fill <= 0 raise Net::SSH::Disconnect, "connection closed by remote host" end end else raise ArgumentError, "expected :block or :nonblock, got #{mode.inspect}" end end # Enqueues a packet to be sent, and blocks until the entire packet is # sent. def send_packet(payload) enqueue_packet(payload) wait_for_pending_sends end # Enqueues a packet to be sent, but does not immediately send the packet. # The given payload is pre-processed according to the algorithms specified # in the client state (compression, cipher, and hmac). def enqueue_packet(payload) # try to compress the packet payload = client.compress(payload) # the length of the packet, minus the padding actual_length = 4 + payload.bytesize + 1 # compute the padding length padding_length = client.block_size - (actual_length % client.block_size) padding_length += client.block_size if padding_length < 4 # compute the packet length (sans the length field itself) packet_length = payload.bytesize + padding_length + 1 if packet_length < 16 padding_length += client.block_size packet_length = payload.bytesize + padding_length + 1 end padding = Array.new(padding_length) { rand(256) }.pack("C*") unencrypted_data = [packet_length, padding_length, payload, padding].pack("NCA*A*") mac = client.hmac.digest([client.sequence_number, unencrypted_data].pack("NA*")) encrypted_data = client.update_cipher(unencrypted_data) << client.final_cipher message = encrypted_data + mac debug { "queueing packet nr #{client.sequence_number} type #{payload.getbyte(0)} len #{packet_length}" } enqueue(message) client.increment(packet_length) self end # Performs any pending cleanup necessary on the IO and its associated # state objects. (See State#cleanup). def cleanup client.cleanup server.cleanup end # If the IO object requires a rekey operation (as indicated by either its # client or server state objects, see State#needs_rekey?), this will # yield. Otherwise, this does nothing. def if_needs_rekey? if client.needs_rekey? || server.needs_rekey? yield client.reset! if client.needs_rekey? server.reset! if server.needs_rekey? end end protected # Called when this module is used to extend an object. It initializes # the states and generally prepares the object for use as a packet stream. def initialize_ssh @hints = {} @server = State.new(self, :server) @client = State.new(self, :client) @packet = nil initialize_buffered_io end # Tries to read the next packet. If there is insufficient data to read # an entire packet, this returns immediately, otherwise the packet is # read, post-processed according to the cipher, hmac, and compression # algorithms specified in the server state object, and returned as a # new Packet object. def poll_next_packet if @packet.nil? minimum = server.block_size < 4 ? 4 : server.block_size return nil if available < minimum data = read_available(minimum) # decipher it @packet = Net::SSH::Buffer.new(server.update_cipher(data)) @packet_length = @packet.read_long end need = @packet_length + 4 - server.block_size raise Net::SSH::Exception, "padding error, need #{need} block #{server.block_size}" if need % server.block_size != 0 return nil if available < need + server.hmac.mac_length if need > 0 # read the remainder of the packet and decrypt it. data = read_available(need) @packet.append(server.update_cipher(data)) end # get the hmac from the tail of the packet (if one exists), and # then validate it. real_hmac = read_available(server.hmac.mac_length) || "" @packet.append(server.final_cipher) padding_length = @packet.read_byte payload = @packet.read(@packet_length - padding_length - 1) my_computed_hmac = server.hmac.digest([server.sequence_number, @packet.content].pack("NA*")) raise Net::SSH::Exception, "corrupted mac detected" if real_hmac != my_computed_hmac # try to decompress the payload, in case compression is active payload = server.decompress(payload) debug { "received packet nr #{server.sequence_number} type #{payload.getbyte(0)} len #{@packet_length}" } server.increment(@packet_length) @packet = nil return Packet.new(payload) end end end; end; end net-ssh-4.2.0/lib/net/ssh/transport/server_version.rb000066400000000000000000000050721315376572000227000ustar00rootroot00000000000000require 'net/ssh/errors' require 'net/ssh/loggable' require 'net/ssh/version' module Net; module SSH; module Transport # Negotiates the SSH protocol version and trades information about server # and client. This is never used directly--it is always called by the # transport layer as part of the initialization process of the transport # layer. # # Note that this class also encapsulates the negotiated version, and acts as # the authoritative reference for any queries regarding the version in effect. class ServerVersion include Loggable # The SSH version string as reported by Net::SSH PROTO_VERSION = "SSH-2.0-Ruby/Net::SSH_#{Net::SSH::Version::CURRENT} #{RUBY_PLATFORM}" # Any header text sent by the server prior to sending the version. attr_reader :header # The version string reported by the server. attr_reader :version # Instantiates a new ServerVersion and immediately (and synchronously) # negotiates the SSH protocol in effect, using the given socket. def initialize(socket, logger, timeout = nil) @header = "" @version = nil @logger = logger negotiate!(socket, timeout) end private # Negotiates the SSH protocol to use, via the given socket. If the server # reports an incompatible SSH version (e.g., SSH1), this will raise an # exception. def negotiate!(socket, timeout) info { "negotiating protocol version" } debug { "local is `#{PROTO_VERSION}'" } socket.write "#{PROTO_VERSION}\r\n" socket.flush if timeout && !IO.select([socket], nil, nil, timeout) raise Net::SSH::ConnectionTimeout, "timeout during server version negotiating" end loop do @version = "" loop do begin b = socket.readpartial(1) raise Net::SSH::Disconnect, "connection closed by remote host" if b.nil? rescue EOFError raise Net::SSH::Disconnect, "connection closed by remote host" end @version << b break if b == "\n" end break if @version.match(/^SSH-/) @header << @version end @version.chomp! debug { "remote is `#{@version}'" } unless @version.match(/^SSH-(1\.99|2\.0)-/) raise Net::SSH::Exception, "incompatible SSH version `#{@version}'" end if timeout && !IO.select(nil, [socket], nil, timeout) raise Net::SSH::ConnectionTimeout, "timeout during client version negotiating" end end end end; end; end net-ssh-4.2.0/lib/net/ssh/transport/session.rb000066400000000000000000000242011315376572000213030ustar00rootroot00000000000000require 'socket' require 'net/ssh/errors' require 'net/ssh/loggable' require 'net/ssh/version' require 'net/ssh/transport/algorithms' require 'net/ssh/transport/constants' require 'net/ssh/transport/packet_stream' require 'net/ssh/transport/server_version' require 'net/ssh/verifiers/null' require 'net/ssh/verifiers/secure' require 'net/ssh/verifiers/strict' require 'net/ssh/verifiers/lenient' module Net; module SSH; module Transport # The transport layer represents the lowest level of the SSH protocol, and # implements basic message exchanging and protocol initialization. It will # never be instantiated directly (unless you really know what you're about), # but will instead be created for you automatically when you create a new # SSH session via Net::SSH.start. class Session include Constants, Loggable # The standard port for the SSH protocol. DEFAULT_PORT = 22 # The host to connect to, as given to the constructor. attr_reader :host # The port number to connect to, as given in the options to the constructor. # If no port number was given, this will default to DEFAULT_PORT. attr_reader :port # The underlying socket object being used to communicate with the remote # host. attr_reader :socket # The ServerVersion instance that encapsulates the negotiated protocol # version. attr_reader :server_version # The Algorithms instance used to perform key exchanges. attr_reader :algorithms # The host-key verifier object used to verify host keys, to ensure that # the connection is not being spoofed. attr_reader :host_key_verifier # The hash of options that were given to the object at initialization. attr_reader :options # Instantiates a new transport layer abstraction. This will block until # the initial key exchange completes, leaving you with a ready-to-use # transport session. def initialize(host, options={}) self.logger = options[:logger] @host = host @port = options[:port] || DEFAULT_PORT @bind_address = options[:bind_address] || nil @options = options @socket = if (factory = options[:proxy]) debug { "establishing connection to #{@host}:#{@port} through proxy" } factory.open(@host, @port, options) else debug { "establishing connection to #{@host}:#{@port}" } Socket.tcp(@host, @port, @bind_address, nil, connect_timeout: options[:timeout]) end @socket.extend(PacketStream) @socket.logger = @logger debug { "connection established" } @queue = [] @host_key_verifier = select_host_key_verifier(options[:verify_host_key]) @server_version = ServerVersion.new(socket, logger, options[:timeout]) @algorithms = Algorithms.new(self, options) @algorithms.start wait { algorithms.initialized? } rescue Errno::ETIMEDOUT raise Net::SSH::ConnectionTimeout end def host_keys @host_keys ||= begin known_hosts = options.fetch(:known_hosts, KnownHosts) known_hosts.search_for(options[:host_key_alias] || host_as_string, options) end end # Returns the host (and possibly IP address) in a format compatible with # SSH known-host files. def host_as_string @host_as_string ||= begin string = "#{host}" string = "[#{string}]:#{port}" if port != DEFAULT_PORT peer_ip = socket.peer_ip if peer_ip != Net::SSH::Transport::PacketStream::PROXY_COMMAND_HOST_IP && peer_ip != host string2 = peer_ip string2 = "[#{string2}]:#{port}" if port != DEFAULT_PORT string << "," << string2 end string end end # Returns true if the underlying socket has been closed. def closed? socket.closed? end # Cleans up (see PacketStream#cleanup) and closes the underlying socket. def close socket.cleanup socket.close end # Performs a "hard" shutdown of the connection. In general, this should # never be done, but it might be necessary (in a rescue clause, for instance, # when the connection needs to close but you don't know the status of the # underlying protocol's state). def shutdown! error { "forcing connection closed" } socket.close end # Returns a new service_request packet for the given service name, ready # for sending to the server. def service_request(service) Net::SSH::Buffer.from(:byte, SERVICE_REQUEST, :string, service) end # Requests a rekey operation, and blocks until the operation completes. # If a rekey is already pending, this returns immediately, having no # effect. def rekey! if !algorithms.pending? algorithms.rekey! wait { algorithms.initialized? } end end # Returns immediately if a rekey is already in process. Otherwise, if a # rekey is needed (as indicated by the socket, see PacketStream#if_needs_rekey?) # one is performed, causing this method to block until it completes. def rekey_as_needed return if algorithms.pending? socket.if_needs_rekey? { rekey! } end # Returns a hash of information about the peer (remote) side of the socket, # including :ip, :port, :host, and :canonized (see #host_as_string). def peer @peer ||= { ip: socket.peer_ip, port: @port.to_i, host: @host, canonized: host_as_string } end # Blocks until a new packet is available to be read, and returns that # packet. See #poll_message. def next_message poll_message(:block) end # Tries to read the next packet from the socket. If mode is :nonblock (the # default), this will not block and will return nil if there are no packets # waiting to be read. Otherwise, this will block until a packet is # available. Note that some packet types (DISCONNECT, IGNORE, UNIMPLEMENTED, # DEBUG, and KEXINIT) are handled silently by this method, and will never # be returned. # # If a key-exchange is in process and a disallowed packet type is # received, it will be enqueued and otherwise ignored. When a key-exchange # is not in process, and consume_queue is true, packets will be first # read from the queue before the socket is queried. def poll_message(mode=:nonblock, consume_queue=true) loop do if consume_queue && @queue.any? && algorithms.allow?(@queue.first) return @queue.shift end packet = socket.next_packet(mode) return nil if packet.nil? case packet.type when DISCONNECT raise Net::SSH::Disconnect, "disconnected: #{packet[:description]} (#{packet[:reason_code]})" when IGNORE debug { "IGNORE packet received: #{packet[:data].inspect}" } when UNIMPLEMENTED lwarn { "UNIMPLEMENTED: #{packet[:number]}" } when DEBUG send(packet[:always_display] ? :fatal : :debug) { packet[:message] } when KEXINIT algorithms.accept_kexinit(packet) else return packet if algorithms.allow?(packet) push(packet) end end end # Waits (blocks) until the given block returns true. If no block is given, # this just waits long enough to see if there are any pending packets. Any # packets read are enqueued (see #push). def wait loop do break if block_given? && yield message = poll_message(:nonblock, false) push(message) if message break if !block_given? end end # Adds the given packet to the packet queue. If the queue is non-empty, # #poll_message will return packets from the queue in the order they # were received. def push(packet) @queue.push(packet) end # Sends the given message via the packet stream, blocking until the # entire message has been sent. def send_message(message) socket.send_packet(message) end # Enqueues the given message, such that it will be sent at the earliest # opportunity. This does not block, but returns immediately. def enqueue_message(message) socket.enqueue_packet(message) end # Configure's the packet stream's client state with the given set of # options. This is typically used to define the cipher, compression, and # hmac algorithms to use when sending packets to the server. def configure_client(options={}) socket.client.set(options) end # Configure's the packet stream's server state with the given set of # options. This is typically used to define the cipher, compression, and # hmac algorithms to use when reading packets from the server. def configure_server(options={}) socket.server.set(options) end # Sets a new hint for the packet stream, which the packet stream may use # to change its behavior. (See PacketStream#hints). def hint(which, value=true) socket.hints[which] = value end public # this method is primarily for use in tests attr_reader :queue #:nodoc: private # Instantiates a new host-key verification class, based on the value of # the parameter. When true or nil, the default Lenient verifier is # returned. If it is false, the Null verifier is returned, and if it is # :very, the Strict verifier is returned. If it is :secure, the even more # strict Secure verifier is returned. If the argument happens to respond # to :verify, it is returned directly. Otherwise, an exception # is raised. def select_host_key_verifier(verify_host_key) case verify_host_key when true, nil then Net::SSH::Verifiers::Lenient.new when false then Net::SSH::Verifiers::Null.new when :very then Net::SSH::Verifiers::Strict.new when :secure then Net::SSH::Verifiers::Secure.new else if verify_host_key.respond_to?(:verify) verify_host_key else raise( ArgumentError, "Invalid argument to :verify_host_key (or deprecated " \ ":paranoid): #{verify_host_key.inspect}" ) end end end end end; end; end net-ssh-4.2.0/lib/net/ssh/transport/state.rb000066400000000000000000000144301315376572000207430ustar00rootroot00000000000000require 'zlib' require 'net/ssh/transport/cipher_factory' require 'net/ssh/transport/hmac' module Net; module SSH; module Transport # Encapsulates state information about one end of an SSH connection. Such # state includes the packet sequence number, the algorithms in use, how # many packets and blocks have been processed since the last reset, and so # forth. This class will never be instantiated directly, but is used as # part of the internal state of the PacketStream module. class State # The socket object that owns this state object. attr_reader :socket # The next packet sequence number for this socket endpoint. attr_reader :sequence_number # The hmac algorithm in use for this endpoint. attr_reader :hmac # The compression algorithm in use for this endpoint. attr_reader :compression # The compression level to use when compressing data (or nil, for the default). attr_reader :compression_level # The number of packets processed since the last call to #reset! attr_reader :packets # The number of data blocks processed since the last call to #reset! attr_reader :blocks # The cipher algorithm in use for this socket endpoint. attr_reader :cipher # The block size for the cipher attr_reader :block_size # The role that this state plays (either :client or :server) attr_reader :role # The maximum number of packets that this endpoint wants to process before # needing a rekey. attr_accessor :max_packets # The maximum number of blocks that this endpoint wants to process before # needing a rekey. attr_accessor :max_blocks # The user-specified maximum number of bytes that this endpoint ought to # process before needing a rekey. attr_accessor :rekey_limit # Creates a new state object, belonging to the given socket. Initializes # the algorithms to "none". def initialize(socket, role) @socket = socket @role = role @sequence_number = @packets = @blocks = 0 @cipher = CipherFactory.get("none") @block_size = 8 @hmac = HMAC.get("none") @compression = nil @compressor = @decompressor = nil @next_iv = "" end # A convenience method for quickly setting multiple values in a single # command. def set(values) values.each do |key, value| instance_variable_set("@#{key}", value) end reset! end def update_cipher(data) result = cipher.update(data) update_next_iv(role == :client ? result : data) return result end def final_cipher result = cipher.final update_next_iv(role == :client ? result : "", true) return result end # Increments the counters. The sequence number is incremented (and remapped # so it always fits in a 32-bit integer). The number of packets and blocks # are also incremented. def increment(packet_length) @sequence_number = (@sequence_number + 1) & 0xFFFFFFFF @packets += 1 @blocks += (packet_length + 4) / @block_size end # The compressor object to use when compressing data. This takes into account # the desired compression level. def compressor @compressor ||= Zlib::Deflate.new(compression_level || Zlib::DEFAULT_COMPRESSION) end # The decompressor object to use when decompressing data. def decompressor @decompressor ||= Zlib::Inflate.new(nil) end # Returns true if data compression/decompression is enabled. This will # return true if :standard compression is selected, or if :delayed # compression is selected and the :authenticated hint has been received # by the socket. def compression? compression == :standard || (compression == :delayed && socket.hints[:authenticated]) end # Compresses the data. If no compression is in effect, this will just return # the data unmodified, otherwise it uses #compressor to compress the data. def compress(data) data = data.to_s return data unless compression? compressor.deflate(data, Zlib::SYNC_FLUSH) end # Deompresses the data. If no compression is in effect, this will just return # the data unmodified, otherwise it uses #decompressor to decompress the data. def decompress(data) data = data.to_s return data unless compression? decompressor.inflate(data) end # Resets the counters on the state object, but leaves the sequence_number # unchanged. It also sets defaults for and recomputes the max_packets and # max_blocks values. def reset! @packets = @blocks = 0 @max_packets ||= 1 << 31 @block_size = cipher.name == "RC4" ? 8 : cipher.block_size if max_blocks.nil? # cargo-culted from openssh. the idea is that "the 2^(blocksize*2) # limit is too expensive for 3DES, blowfish, etc., so enforce a 1GB # limit for small blocksizes." if @block_size >= 16 @max_blocks = 1 << (@block_size * 2) else @max_blocks = (1 << 30) / @block_size end # if a limit on the # of bytes has been given, convert that into a # minimum number of blocks processed. if rekey_limit @max_blocks = [@max_blocks, rekey_limit / @block_size].min end end cleanup end # Closes any the compressor and/or decompressor objects that have been # instantiated. def cleanup if @compressor @compressor.finish if !@compressor.finished? @compressor.close end if @decompressor # we call reset here so that we don't get warnings when we try to # close the decompressor @decompressor.reset @decompressor.close end @compressor = @decompressor = nil end # Returns true if the number of packets processed exceeds the maximum # number of packets, or if the number of blocks processed exceeds the # maximum number of blocks. def needs_rekey? max_packets && packets > max_packets || max_blocks && blocks > max_blocks end private def update_next_iv(data, reset=false) @next_iv << data @next_iv = @next_iv[@next_iv.size-cipher.iv_len..-1] if reset cipher.reset cipher.iv = @next_iv end return data end end end; end; end net-ssh-4.2.0/lib/net/ssh/verifiers/000077500000000000000000000000001315376572000172365ustar00rootroot00000000000000net-ssh-4.2.0/lib/net/ssh/verifiers/lenient.rb000066400000000000000000000020441315376572000212210ustar00rootroot00000000000000require 'net/ssh/verifiers/strict' module Net; module SSH; module Verifiers # Basically the same as the Strict verifier, but does not try to actually # verify a connection if the server is the localhost and the port is a # nonstandard port number. Those two conditions will typically mean the # connection is being tunnelled through a forwarded port, so the known-hosts # file will not be helpful (in general). class Lenient < Strict # Tries to determine if the connection is being tunnelled, and if so, # returns true. Otherwise, performs the standard strict verification. def verify(arguments) return true if tunnelled?(arguments) super end private # A connection is potentially being tunnelled if the port is not 22, # and the ip refers to the localhost. def tunnelled?(args) return false if args[:session].port == Net::SSH::Transport::Session::DEFAULT_PORT ip = args[:session].peer[:ip] return ip == "127.0.0.1" || ip == "::1" end end end; end; endnet-ssh-4.2.0/lib/net/ssh/verifiers/null.rb000066400000000000000000000004351315376572000205370ustar00rootroot00000000000000module Net; module SSH; module Verifiers # The Null host key verifier simply allows every key it sees, without # bothering to verify. This is simple, but is not particularly secure. class Null # Returns true. def verify(arguments) true end end end; end; endnet-ssh-4.2.0/lib/net/ssh/verifiers/secure.rb000066400000000000000000000034161315376572000210550ustar00rootroot00000000000000require 'net/ssh/errors' require 'net/ssh/known_hosts' module Net; module SSH; module Verifiers # Does a strict host verification, looking the server up in the known # host files to see if a key has already been seen for this server. If this # server does not appear in any host file, an exception will be raised # (HostKeyUnknown). This is in contrast to the "Strict" class, which will # silently add the key to your known_hosts file. If the server does appear at # least once, but the key given does not match any known for the server, an # exception will be raised (HostKeyMismatch). # Otherwise, this returns true. class Secure def verify(arguments) host_keys = arguments[:session].host_keys # We've never seen this host before, so raise an exception. if host_keys.empty? process_cache_miss(host_keys, arguments, HostKeyUnknown, "is unknown") end # If we found any matches, check to see that the key type and # blob also match. found = host_keys.any? do |key| key.ssh_type == arguments[:key].ssh_type && key.to_blob == arguments[:key].to_blob end # If a match was found, return true. Otherwise, raise an exception # indicating that the key was not recognized. unless found process_cache_miss(host_keys, arguments, HostKeyMismatch, "does not match") end found end private def process_cache_miss(host_keys, args, exc_class, message) exception = exc_class.new("fingerprint #{args[:fingerprint]} " + "#{message} for #{host_keys.host.inspect}") exception.data = args exception.callback = Proc.new do host_keys.add_host_key(args[:key]) end raise exception end end end; end; end net-ssh-4.2.0/lib/net/ssh/verifiers/strict.rb000066400000000000000000000013551315376572000210770ustar00rootroot00000000000000require 'net/ssh/errors' require 'net/ssh/known_hosts' require 'net/ssh/verifiers/secure' module Net; module SSH; module Verifiers # Does a strict host verification, looking the server up in the known # host files to see if a key has already been seen for this server. If this # server does not appear in any host file, this will silently add the # server. If the server does appear at least once, but the key given does # not match any known for the server, an exception will be raised (HostKeyMismatch). # Otherwise, this returns true. class Strict < Secure def verify(arguments) begin super rescue HostKeyUnknown => err err.remember_host! return true end end end end; end; end net-ssh-4.2.0/lib/net/ssh/version.rb000066400000000000000000000040331315376572000172520ustar00rootroot00000000000000module Net; module SSH # A class for describing the current version of a library. The version # consists of three parts: the +major+ number, the +minor+ number, and the # +tiny+ (or +patch+) number. # # Two Version instances may be compared, so that you can test that a version # of a library is what you require: # # require 'net/ssh/version' # # if Net::SSH::Version::CURRENT < Net::SSH::Version[2,1,0] # abort "your software is too old!" # end class Version include Comparable # A convenience method for instantiating a new Version instance with the # given +major+, +minor+, and +tiny+ components. def self.[](major, minor, tiny, pre = nil) new(major, minor, tiny, pre) end attr_reader :major, :minor, :tiny # Create a new Version object with the given components. def initialize(major, minor, tiny, pre = nil) @major, @minor, @tiny, @pre = major, minor, tiny, pre end # Compare this version to the given +version+ object. def <=>(version) to_i <=> version.to_i end # Converts this version object to a string, where each of the three # version components are joined by the '.' character. E.g., 2.0.0. def to_s @to_s ||= [@major, @minor, @tiny, @pre].compact.join(".") end # Converts this version to a canonical integer that may be compared # against other version objects. def to_i @to_i ||= @major * 1_000_000 + @minor * 1_000 + @tiny end # The major component of this version of the Net::SSH library MAJOR = 4 # The minor component of this version of the Net::SSH library MINOR = 2 # The tiny component of this version of the Net::SSH library TINY = 0 # The prerelease component of this version of the Net::SSH library # nil allowed PRE = nil # The current version of the Net::SSH library as a Version instance CURRENT = new(*[MAJOR, MINOR, TINY, PRE].compact) # The current version of the Net::SSH library as a String STRING = CURRENT.to_s end end; end net-ssh-4.2.0/net-ssh-public_cert.pem000066400000000000000000000023611315376572000174660ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDeDCCAmCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBBMQ8wDQYDVQQDDAZuZXRz c2gxGTAXBgoJkiaJk/IsZAEZFglzb2x1dGlvdXMxEzARBgoJkiaJk/IsZAEZFgNj b20wHhcNMTYxMjE1MTgwNTIyWhcNMTcxMjE1MTgwNTIyWjBBMQ8wDQYDVQQDDAZu ZXRzc2gxGTAXBgoJkiaJk/IsZAEZFglzb2x1dGlvdXMxEzARBgoJkiaJk/IsZAEZ FgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGJ4TbZ9H+qZ08 pQfJhPJTHaDCyQvCsKTFrL5O9z3tllQ7B/zksMMM+qFBpNYu9HCcg4yBATacE/PB qVVyUrpr6lbH/XwoN5ljXm+bdCfmnjZvTCL2FTE6o+bcnaF0IsJyC0Q2B1fbWdXN 6Off1ZWoUk6We2BIM1bn6QJLxBpGyYhvOPXsYoqSuzDf2SJDDsWFZ8kV5ON13Ohm JbBzn0oD8HF8FuYOewwsC0C1q4w7E5GtvHcQ5juweS7+RKsyDcVcVrLuNzoGRttS KP4yMn+TzaXijyjRg7gECfJr3TGASaA4bQsILFGG5dAWcwO4OMrZedR7SHj/o0Kf 3gL7P0axAgMBAAGjezB5MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW BBQF8qLA7Z4zg0SJGtUbv3eoQ8tjIzAfBgNVHREEGDAWgRRuZXRzc2hAc29sdXRp b3VzLmNvbTAfBgNVHRIEGDAWgRRuZXRzc2hAc29sdXRpb3VzLmNvbTANBgkqhkiG 9w0BAQUFAAOCAQEATd8If+Ytmhf5lELy24j76ahGv64m518WTCdV2nIViGXB2BnV uLQylGRb1rcgUS3Eh9TE28hqrfhotKS6a96qF9kN0mY2H6UwPWswJ+tj3gA1vLW8 wlZNlYGJ91Ig9zULPSbATyOOprUZyggy5p1260BaaI3LQYDeGJOSqpHCVu+TuMcy k00ofiLT1crDSUl2WE/OIFK8AXpmd798AMsef8okHeoo+Dj7zCXn0VSimN+MO1mE L4d54WIy4HkZCqQXoTSiK5HZMIdXkPk3F1bZdJ8Dy1sMRru0rUkkM5mW7TQ75mfW Zp0QrZyNZhtitrXFbZneGRrIA/8G2Krft5Ly/A== -----END CERTIFICATE----- net-ssh-4.2.0/net-ssh.gemspec000066400000000000000000000033321315376572000160360ustar00rootroot00000000000000# coding: utf-8 require_relative 'lib/net/ssh/version' Gem::Specification.new do |spec| spec.name = "net-ssh" spec.version = Net::SSH::Version::STRING spec.authors = ["Jamis Buck", "Delano Mandelbaum", "Mikl\u{f3}s Fazekas"] spec.email = ["net-ssh@solutious.com"] if ENV['NET_SSH_BUILDGEM_SIGNED'] spec.cert_chain = ["net-ssh-public_cert.pem"] spec.signing_key = "/mnt/gem/net-ssh-private_key.pem" end spec.summary = %q{Net::SSH: a pure-Ruby implementation of the SSH2 client protocol.} spec.description = %q{Net::SSH: a pure-Ruby implementation of the SSH2 client protocol. It allows you to write programs that invoke and interact with processes on remote servers, via SSH2.} spec.homepage = "https://github.com/net-ssh/net-ssh" spec.license = "MIT" spec.required_ruby_version = Gem::Requirement.new(">= 2.0") spec.extra_rdoc_files = [ "LICENSE.txt", "README.rdoc" ] spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] unless ENV['NET_SSH_NO_RBNACL'] spec.add_development_dependency("rbnacl-libsodium", "~> 1.0.11") spec.add_development_dependency("rbnacl", ['>= 3.2.0','< 5.0']) spec.add_development_dependency("bcrypt_pbkdf", "~> 1.0") unless RUBY_PLATFORM == "java" end spec.add_development_dependency "bundler", "~> 1.11" spec.add_development_dependency "rake", "~> 12.0" spec.add_development_dependency "minitest", "~> 5.10" spec.add_development_dependency "rubocop", "~> 0.47.0" spec.add_development_dependency "mocha", ">= 1.2.1" end net-ssh-4.2.0/support/000077500000000000000000000000001315376572000146235ustar00rootroot00000000000000net-ssh-4.2.0/support/arcfour_check.rb000066400000000000000000000010501315376572000177420ustar00rootroot00000000000000 require 'net/ssh' # ARCFOUR CHECK # # Usage: # $ ruby support/arcfour_check.rb # # Expected Output: # arcfour128: [16, 8] OpenSSL::Cipher::Cipher # arcfour256: [32, 8] OpenSSL::Cipher::Cipher # arcfour512: [64, 8] OpenSSL::Cipher::Cipher [['arcfour128', 16], ['arcfour256', 32], ['arcfour512', 64]].each do |cipher| print "#{cipher[0]}: " a = Net::SSH::Transport::CipherFactory.get_lengths(cipher[0]) b = Net::SSH::Transport::CipherFactory.get(cipher[0], key: ([].fill('x', 0, cipher[1]).join)) puts "#{a} #{b.class}" end net-ssh-4.2.0/support/ssh_tunnel_bug.rb000077500000000000000000000046501315376572000201770ustar00rootroot00000000000000#!/usr/bin/ruby # SSH TUNNEL CONNECTION BUG # from: http://net-ssh.lighthouseapp.com/projects/36253/tickets/7-an-existing-connection-was-forcibly-closed-by-the-remote-host#ticket-7-3 # # Steps to reproduce: # # * Start HTTP Proxy # * If running debian in EC2: # * apt-get install squid # * Add the following to /etc/squid/squid.conf: # acl localnet src 1.2.3.0/255.255.255.0 # http_access allow localnet # icp_access allow localnet # visible_hostname netsshtest # * Start squid squid -N -d 1 -D # * Run this script # * Configure browser proxy to use localhost with LOCAL_PORT. # * Load any page, wait for it to load fully. If the page loads # correctly, move on. If not, something needs to be corrected. # * Refresh the page several times. This should cause this # script to failed with the error: "closed stream". You may # need to try a few times. # require 'highline/import' require 'net/ssh' LOCAL_PORT = 8080 PROXY_PORT = 3128 host, user = *ARGV abort "Usage: #{$0} host user" unless ARGV.size == 2 puts "Connecting to #{user}@#{host}..." pass = ask("Password: ") { |q| q.echo = "*" } puts "Configure your browser proxy to localhost:#{LOCAL_PORT}" begin session = Net::SSH.start(host, user, password: pass) session.forward.local(LOCAL_PORT, host, PROXY_PORT) session.loop{true} rescue => e puts e.message puts e.backtrace end __END__ $ ruby support/ssh_tunnel.rb host user Connecting to user@host... Password: ****** Configure your browser proxy to localhost:8080 closed stream /usr/local/lib/ruby/gems/1.9.1/gems/net-ssh-2.0.15/lib/net/ssh/buffered_io.rb:99:in `send' /usr/local/lib/ruby/gems/1.9.1/gems/net-ssh-2.0.15/lib/net/ssh/buffered_io.rb:99:in `send_pending' /usr/local/lib/ruby/gems/1.9.1/gems/net-ssh-2.0.15/lib/net/ssh/connection/session.rb:236:in `block in postprocess' /usr/local/lib/ruby/gems/1.9.1/gems/net-ssh-2.0.15/lib/net/ssh/connection/session.rb:235:in `each' /usr/local/lib/ruby/gems/1.9.1/gems/net-ssh-2.0.15/lib/net/ssh/connection/session.rb:235:in `postprocess' /usr/local/lib/ruby/gems/1.9.1/gems/net-ssh-2.0.15/lib/net/ssh/connection/session.rb:203:in `process' /usr/local/lib/ruby/gems/1.9.1/gems/net-ssh-2.0.15/lib/net/ssh/connection/session.rb:161:in `block in loop' /usr/local/lib/ruby/gems/1.9.1/gems/net-ssh-2.0.15/lib/net/ssh/connection/session.rb:161:in `loop' /usr/local/lib/ruby/gems/1.9.1/gems/net-ssh-2.0.15/lib/net/ssh/connection/session.rb:161:in `loop' net-ssh-4.2.0/test/000077500000000000000000000000001315376572000140665ustar00rootroot00000000000000net-ssh-4.2.0/test/README.txt000066400000000000000000000006761315376572000155750ustar00rootroot00000000000000RUNNING TESTS Run the test suite from the net-ssh directory with the following command: ruby -Ilib -Itest test/test_all.rb Run a single test file like this: ruby -Ilib -Itest test/transport/test_server_version.rb EXPECTED RESULTS https://travis-ci.org/net-ssh/net-ssh/ INTEGRATION TESTS brew install ansible ; ansible-galaxy install rvm_io.rvm1-ruby ; vagrant up ; vagrant ssh cd /net-ssh ; rake integration-test net-ssh-4.2.0/test/authentication/000077500000000000000000000000001315376572000171055ustar00rootroot00000000000000net-ssh-4.2.0/test/authentication/methods/000077500000000000000000000000001315376572000205505ustar00rootroot00000000000000net-ssh-4.2.0/test/authentication/methods/common.rb000066400000000000000000000013321315376572000223640ustar00rootroot00000000000000module Authentication; module Methods module Common include Net::SSH::Authentication::Constants private def socket(options={}) @socket ||= stub("socket", client_name: "me.ssh.test") end def transport(options={}) @transport ||= MockTransport.new(options.merge(socket: socket)) end def session(options={}) @session ||= begin sess = stub("auth-session", logger: nil, transport: transport(options)) def sess.next_message transport.next_message end sess end end def reset_session(options = {}) @transport = nil @session = nil session(options) end end end; endnet-ssh-4.2.0/test/authentication/methods/test_abstract.rb000066400000000000000000000033601315376572000237410ustar00rootroot00000000000000require 'common' require 'authentication/methods/common' require 'net/ssh/authentication/methods/abstract' module Authentication; module Methods class TestAbstract < NetSSHTest include Common def test_constructor_should_set_defaults assert_nil subject.key_manager end def test_constructor_should_honor_options assert_equal :manager, subject(key_manager: :manager).key_manager end def test_session_id_should_query_session_id_from_key_exchange transport.stubs(:algorithms).returns(stub("algorithms", session_id: "abcxyz123")) assert_equal "abcxyz123", subject.session_id end def test_send_message_should_delegate_to_transport transport.expects(:send_message).with("abcxyz123") subject.send_message("abcxyz123") end def test_userauth_request_should_build_well_formed_userauth_packet packet = subject.userauth_request("jamis", "ssh-connection", "password") assert_equal "\062\0\0\0\005jamis\0\0\0\016ssh-connection\0\0\0\010password", packet.to_s end def test_userauth_request_should_translate_extra_booleans_onto_end packet = subject.userauth_request("jamis", "ssh-connection", "password", true, false) assert_equal "\062\0\0\0\005jamis\0\0\0\016ssh-connection\0\0\0\010password\1\0", packet.to_s end def test_userauth_request_should_translate_extra_strings_onto_end packet = subject.userauth_request("jamis", "ssh-connection", "password", "foo", "bar") assert_equal "\062\0\0\0\005jamis\0\0\0\016ssh-connection\0\0\0\010password\0\0\0\3foo\0\0\0\3bar", packet.to_s end private def subject(options={}) @subject ||= Net::SSH::Authentication::Methods::Abstract.new(session(options), options) end end end; endnet-ssh-4.2.0/test/authentication/methods/test_hostbased.rb000066400000000000000000000077021315376572000241160ustar00rootroot00000000000000require 'common' require 'net/ssh/authentication/methods/hostbased' require 'authentication/methods/common' module Authentication; module Methods class TestHostbased < NetSSHTest include Common def test_authenticate_should_return_false_when_no_key_manager_has_been_set assert_equal false, subject(key_manager: nil).authenticate("ssh-connection", "jamis") end def test_authenticate_should_return_false_when_key_manager_has_no_keys assert_equal false, subject(keys: []).authenticate("ssh-connection", "jamis") end def test_authenticate_should_return_false_if_no_keys_can_authenticate ENV.stubs(:[]).with('USER').returns(nil) key_manager.expects(:sign).with(&signature_parameters(keys.first)).returns("sig-one") key_manager.expects(:sign).with(&signature_parameters(keys.last)).returns("sig-two") transport.expect do |t, packet| assert_equal USERAUTH_REQUEST, packet.type assert verify_userauth_request_packet(packet, keys.first) assert_equal "sig-one", packet.read_string t.return(USERAUTH_FAILURE, :string, "hostbased,password") t.expect do |t2, packet2| assert_equal USERAUTH_REQUEST, packet2.type assert verify_userauth_request_packet(packet2, keys.last) assert_equal "sig-two", packet2.read_string t2.return(USERAUTH_FAILURE, :string, "hostbased,password") end end assert_equal false, subject.authenticate("ssh-connection", "jamis") end def test_authenticate_should_return_true_if_any_key_can_authenticate ENV.stubs(:[]).with('USER').returns(nil) key_manager.expects(:sign).with(&signature_parameters(keys.first)).returns("sig-one") transport.expect do |t, packet| assert_equal USERAUTH_REQUEST, packet.type assert verify_userauth_request_packet(packet, keys.first) assert_equal "sig-one", packet.read_string t.return(USERAUTH_SUCCESS) end assert subject.authenticate("ssh-connection", "jamis") end private def signature_parameters(key) Proc.new do |given_key, data| next false unless given_key.to_blob == key.to_blob buffer = Net::SSH::Buffer.new(data) buffer.read_string == "abcxyz123" && # session-id buffer.read_byte == USERAUTH_REQUEST && # type verify_userauth_request_packet(buffer, key) end end def verify_userauth_request_packet(packet, key) packet.read_string == "jamis" && # user-name packet.read_string == "ssh-connection" && # next service packet.read_string == "hostbased" && # auth-method packet.read_string == key.ssh_type && # key type packet.read_buffer.read_key.to_blob == key.to_blob && # key packet.read_string == "me.ssh.test." && # client hostname packet.read_string == "jamis" # client username end @@keys = nil def keys @@keys ||= [OpenSSL::PKey::RSA.new(512), OpenSSL::PKey::DSA.new(512)] end def key_manager(options={}) @key_manager ||= begin manager = stub("key_manager") manager.stubs(:each_identity).multiple_yields(*(options[:keys] || keys)) manager end end def subject(options={}) options[:key_manager] = key_manager(options) unless options.key?(:key_manager) @subject ||= Net::SSH::Authentication::Methods::Hostbased.new(session(options), options) end def socket(options={}) @socket ||= stub("socket", client_name: "me.ssh.test") end def transport(options={}) @transport ||= MockTransport.new(options.merge(socket: socket)) end def session(options={}) @session ||= begin sess = stub("auth-session", logger: nil, transport: transport(options)) def sess.next_message transport.next_message end sess end end end end; end net-ssh-4.2.0/test/authentication/methods/test_keyboard_interactive.rb000066400000000000000000000115731315376572000263400ustar00rootroot00000000000000require_relative '../../common' require 'net/ssh/authentication/methods/keyboard_interactive' require_relative 'common' module Authentication; module Methods class TestKeyboardInteractive < NetSSHTest include Common USERAUTH_INFO_REQUEST = 60 USERAUTH_INFO_RESPONSE = 61 def setup reset_subject({}) if defined? @subject && !@subject.options.empty? end def test_authenticate_should_raise_if_keyboard_interactive_disallowed transport.expect do |t,packet| assert_equal USERAUTH_REQUEST, packet.type assert_equal "jamis", packet.read_string assert_equal "ssh-connection", packet.read_string assert_equal "keyboard-interactive", packet.read_string assert_equal "", packet.read_string # language tags assert_equal "", packet.read_string # submethods t.return(USERAUTH_FAILURE, :string, "password") end assert_raises Net::SSH::Authentication::DisallowedMethod do subject.authenticate("ssh-connection", "jamis") end end def test_authenticate_should_be_false_if_given_password_is_not_accepted reset_subject(non_interactive: true) transport.expect do |t,packet| assert_equal USERAUTH_REQUEST, packet.type t.return(USERAUTH_INFO_REQUEST, :string, "", :string, "", :string, "", :long, 1, :string, "Password:", :bool, false) t.expect do |t2,packet2| assert_equal USERAUTH_INFO_RESPONSE, packet2.type assert_equal 1, packet2.read_long assert_equal "the-password", packet2.read_string t2.return(USERAUTH_FAILURE, :string, "keyboard-interactive") end end assert_equal false, subject.authenticate("ssh-connection", "jamis", "the-password") end def test_authenticate_should_be_true_if_given_password_is_accepted transport.expect do |t,packet| assert_equal USERAUTH_REQUEST, packet.type t.return(USERAUTH_INFO_REQUEST, :string, "", :string, "", :string, "", :long, 1, :string, "Password:", :bool, false) t.expect do |t2,packet2| assert_equal USERAUTH_INFO_RESPONSE, packet2.type t2.return(USERAUTH_SUCCESS) end end assert subject.authenticate("ssh-connection", "jamis", "the-password") end def test_authenticate_should_duplicate_password_as_needed_to_fill_request transport.expect do |t,packet| assert_equal USERAUTH_REQUEST, packet.type t.return(USERAUTH_INFO_REQUEST, :string, "", :string, "", :string, "", :long, 2, :string, "Password:", :bool, false, :string, "Again:", :bool, false) t.expect do |t2,packet2| assert_equal USERAUTH_INFO_RESPONSE, packet2.type assert_equal 2, packet2.read_long assert_equal "the-password", packet2.read_string assert_equal "the-password", packet2.read_string t2.return(USERAUTH_SUCCESS) end end assert subject.authenticate("ssh-connection", "jamis", "the-password") end def test_authenticate_should_not_prompt_for_input_when_in_non_interactive_mode reset_subject(non_interactive: true) transport.expect do |t,packet| assert_equal USERAUTH_REQUEST, packet.type t.return(USERAUTH_INFO_REQUEST, :string, "", :string, "", :string, "", :long, 2, :string, "Name:", :bool, true, :string, "Password:", :bool, false) t.expect do |t2,packet2| assert_equal USERAUTH_INFO_RESPONSE, packet2.type assert_equal 2, packet2.read_long assert_equal "", packet2.read_string assert_equal "", packet2.read_string t2.return(USERAUTH_SUCCESS) end end assert subject.authenticate("ssh-connection", "jamis", nil) end def test_authenticate_should_prompt_for_input_when_password_is_not_given prompt = MockPrompt.new prompt.expects(:_ask).with("Name:", anything, true).returns("name") prompt.expects(:_ask).with("Password:", anything, false).returns("password") reset_subject(password_prompt: prompt) transport.expect do |t,packet| assert_equal USERAUTH_REQUEST, packet.type t.return(USERAUTH_INFO_REQUEST, :string, "", :string, "", :string, "", :long, 2, :string, "Name:", :bool, true, :string, "Password:", :bool, false) t.expect do |t2,packet2| assert_equal USERAUTH_INFO_RESPONSE, packet2.type assert_equal 2, packet2.read_long assert_equal "name", packet2.read_string assert_equal "password", packet2.read_string t2.return(USERAUTH_SUCCESS) end end assert subject.authenticate("ssh-connection", "jamis", nil) end private def subject(options={}) @subject ||= Net::SSH::Authentication::Methods::KeyboardInteractive.new(session(options), options) end def reset_subject(options) @subject = nil reset_session(options) subject(options) end end end; end net-ssh-4.2.0/test/authentication/methods/test_none.rb000066400000000000000000000021511315376572000230720ustar00rootroot00000000000000require 'common' require 'net/ssh/authentication/methods/none' require 'authentication/methods/common' module Authentication; module Methods class TestNone < NetSSHTest include Common def test_authenticate_should_raise_if_none_disallowed transport.expect do |t,packet| assert_equal USERAUTH_REQUEST, packet.type assert_equal "jamis", packet.read_string assert_equal "ssh-connection", packet.read_string assert_equal "none", packet.read_string t.return(USERAUTH_FAILURE, :string, "publickey") end assert_raises Net::SSH::Authentication::DisallowedMethod do subject.authenticate("ssh-connection", "jamis", "pass") end end def test_authenticate_should_return_true transport.expect do |t,packet| assert_equal USERAUTH_REQUEST, packet.type t.return(USERAUTH_SUCCESS) end assert subject.authenticate("ssh-connection", "", "") end private def subject(options={}) @subject ||= Net::SSH::Authentication::Methods::None.new(session(options), options) end end end; end net-ssh-4.2.0/test/authentication/methods/test_password.rb000066400000000000000000000071161315376572000240030ustar00rootroot00000000000000require 'common' require 'net/ssh/authentication/methods/password' require 'net/ssh/authentication/session' require 'authentication/methods/common' module Authentication; module Methods class TestPassword < NetSSHTest include Common def test_authenticate_should_raise_if_password_disallowed transport.expect do |t,packet| assert_equal USERAUTH_REQUEST, packet.type assert_equal "jamis", packet.read_string assert_equal "ssh-connection", packet.read_string assert_equal "password", packet.read_string assert_equal false, packet.read_bool assert_equal "the-password", packet.read_string t.return(USERAUTH_FAILURE, :string, "publickey") end assert_raises Net::SSH::Authentication::DisallowedMethod do subject.authenticate("ssh-connection", "jamis", "the-password") end end def test_authenticate_ask_for_password_for_second_time_when_password_is_incorrect transport.expect do |t,packet| assert_equal USERAUTH_REQUEST, packet.type assert_equal "jamis", packet.read_string assert_equal "ssh-connection", packet.read_string assert_equal "password", packet.read_string assert_equal false, packet.read_bool assert_equal "the-password", packet.read_string t.return(USERAUTH_FAILURE, :string, "publickey,password") t.expect do |t2, packet2| assert_equal USERAUTH_REQUEST, packet2.type assert_equal "jamis", packet2.read_string assert_equal "ssh-connection", packet2.read_string assert_equal "password", packet2.read_string assert_equal false, packet2.read_bool assert_equal "the-password-2", packet2.read_string t.return(USERAUTH_SUCCESS) end end prompt = MockPrompt.new prompt.expects(:_ask).with("jamis@'s password:", {type: 'password', user: 'jamis', host: nil}, false).returns("the-password-2") subject(password_prompt: prompt).authenticate("ssh-connection", "jamis", "the-password") end def test_authenticate_ask_for_password_if_not_given transport.expect do |t,packet| assert_equal USERAUTH_REQUEST, packet.type assert_equal "bill", packet.read_string assert_equal "ssh-connection", packet.read_string assert_equal "password", packet.read_string assert_equal false, packet.read_bool assert_equal "good-password", packet.read_string t.return(USERAUTH_SUCCESS) end transport.instance_eval { @host='testhost' } prompt = MockPrompt.new prompt.expects(:_ask).with("bill@testhost's password:", {type: 'password', user: 'bill', host: 'testhost'}, false).returns("good-password") subject(password_prompt: prompt).authenticate("ssh-connection", "bill", nil) end def test_authenticate_when_password_is_acceptible_should_return_true transport.expect do |t,packet| assert_equal USERAUTH_REQUEST, packet.type t.return(USERAUTH_SUCCESS) end assert subject.authenticate("ssh-connection", "jamis", "the-password") end def test_authenticate_should_return_false_if_password_change_request_is_received transport.expect do |t,packet| assert_equal USERAUTH_REQUEST, packet.type t.return(USERAUTH_PASSWD_CHANGEREQ, :string, "Change your password:", :string, "") end assert !subject.authenticate("ssh-connection", "jamis", "the-password") end private def subject(options={}) @subject ||= Net::SSH::Authentication::Methods::Password.new(session(options), options) end end end; end net-ssh-4.2.0/test/authentication/methods/test_publickey.rb000066400000000000000000000133351315376572000241300ustar00rootroot00000000000000require 'common' require 'net/ssh/authentication/methods/publickey' require 'authentication/methods/common' module Authentication; module Methods class TestPublickey < NetSSHTest include Common def test_authenticate_should_return_false_when_no_key_manager_has_been_set assert_equal false, subject(key_manager: nil).authenticate("ssh-connection", "jamis") end def test_authenticate_should_return_false_when_key_manager_has_no_keys assert_equal false, subject(keys: []).authenticate("ssh-connection", "jamis") end def test_authenticate_should_return_false_if_no_keys_can_authenticate transport.expect do |t, packet| assert_equal USERAUTH_REQUEST, packet.type assert verify_userauth_request_packet(packet, keys.first, false) t.return(USERAUTH_FAILURE, :string, "hostbased,password") t.expect do |t2, packet2| assert_equal USERAUTH_REQUEST, packet2.type assert verify_userauth_request_packet(packet2, keys.last, false) t2.return(USERAUTH_FAILURE, :string, "hostbased,password") end end assert_equal false, subject.authenticate("ssh-connection", "jamis") end def test_authenticate_should_raise_if_publickey_disallowed key_manager.expects(:sign).with(&signature_parameters(keys.first)).returns("sig-one") transport.expect do |t, packet| assert_equal USERAUTH_REQUEST, packet.type assert verify_userauth_request_packet(packet, keys.first, false) t.return(USERAUTH_PK_OK, :string, keys.first.ssh_type, :string, Net::SSH::Buffer.from(:key, keys.first)) t.expect do |t2,packet2| assert_equal USERAUTH_REQUEST, packet2.type assert verify_userauth_request_packet(packet2, keys.first, true) assert_equal "sig-one", packet2.read_string t2.return(USERAUTH_FAILURE, :string, "hostbased,password") end end assert_raises Net::SSH::Authentication::DisallowedMethod do subject.authenticate("ssh-connection", "jamis") end end def test_authenticate_should_return_false_if_signature_exchange_fails key_manager.expects(:sign).with(&signature_parameters(keys.first)).returns("sig-one") key_manager.expects(:sign).with(&signature_parameters(keys.last)).returns("sig-two") transport.expect do |t, packet| assert_equal USERAUTH_REQUEST, packet.type assert verify_userauth_request_packet(packet, keys.first, false) t.return(USERAUTH_PK_OK, :string, keys.first.ssh_type, :string, Net::SSH::Buffer.from(:key, keys.first)) t.expect do |t2,packet2| assert_equal USERAUTH_REQUEST, packet2.type assert verify_userauth_request_packet(packet2, keys.first, true) assert_equal "sig-one", packet2.read_string t2.return(USERAUTH_FAILURE, :string, "publickey") t2.expect do |t3, packet3| assert_equal USERAUTH_REQUEST, packet3.type assert verify_userauth_request_packet(packet3, keys.last, false) t3.return(USERAUTH_PK_OK, :string, keys.last.ssh_type, :string, Net::SSH::Buffer.from(:key, keys.last)) t3.expect do |t4,packet4| assert_equal USERAUTH_REQUEST, packet4.type assert verify_userauth_request_packet(packet4, keys.last, true) assert_equal "sig-two", packet4.read_string t4.return(USERAUTH_FAILURE, :string, "publickey") end end end end assert !subject.authenticate("ssh-connection", "jamis") end def test_authenticate_should_return_true_if_any_key_can_authenticate key_manager.expects(:sign).with(&signature_parameters(keys.first)).returns("sig-one") transport.expect do |t, packet| assert_equal USERAUTH_REQUEST, packet.type assert verify_userauth_request_packet(packet, keys.first, false) t.return(USERAUTH_PK_OK, :string, keys.first.ssh_type, :string, Net::SSH::Buffer.from(:key, keys.first)) t.expect do |t2,packet2| assert_equal USERAUTH_REQUEST, packet2.type assert verify_userauth_request_packet(packet2, keys.first, true) assert_equal "sig-one", packet2.read_string t2.return(USERAUTH_SUCCESS) end end assert subject.authenticate("ssh-connection", "jamis") end private def signature_parameters(key) Proc.new do |given_key, data| next false unless given_key.to_blob == key.to_blob buffer = Net::SSH::Buffer.new(data) buffer.read_string == "abcxyz123" && # session-id buffer.read_byte == USERAUTH_REQUEST && # type verify_userauth_request_packet(buffer, key, true) end end def verify_userauth_request_packet(packet, key, has_sig) packet.read_string == "jamis" && # user-name packet.read_string == "ssh-connection" && # next service packet.read_string == "publickey" && # auth-method packet.read_bool == has_sig && # whether a signature is appended packet.read_string == key.ssh_type && # ssh key type packet.read_buffer.read_key.to_blob == key.to_blob # key end @@keys = nil def keys @@keys ||= [OpenSSL::PKey::RSA.new(512), OpenSSL::PKey::DSA.new(512)] end def key_manager(options={}) @key_manager ||= begin manager = stub("key_manager") manager.stubs(:each_identity).multiple_yields(*(options[:keys] || keys)) manager end end def subject(options={}) options[:key_manager] = key_manager(options) unless options.key?(:key_manager) @subject ||= Net::SSH::Authentication::Methods::Publickey.new(session(options), options) end end end; end net-ssh-4.2.0/test/authentication/test_agent.rb000066400000000000000000000445661315376572000216060ustar00rootroot00000000000000require 'common' require 'net/ssh/authentication/agent' module Authentication class TestAgent < NetSSHTest SSH2_AGENT_REQUEST_VERSION = 1 SSH2_AGENT_REQUEST_IDENTITIES = 11 SSH2_AGENT_IDENTITIES_ANSWER = 12 SSH2_AGENT_SIGN_REQUEST = 13 SSH2_AGENT_SIGN_RESPONSE = 14 SSH2_AGENT_ADD_IDENTITY = 17 SSH2_AGENT_REMOVE_IDENTITY = 18 SSH2_AGENT_REMOVE_ALL_IDENTITIES = 19 SSH2_AGENT_ADD_ID_CONSTRAINED = 25 SSH2_AGENT_FAILURE = 30 SSH2_AGENT_VERSION_RESPONSE = 103 SSH_COM_AGENT2_FAILURE = 102 SSH_AGENT_REQUEST_RSA_IDENTITIES = 1 SSH_AGENT_RSA_IDENTITIES_ANSWER = 2 SSH_AGENT_FAILURE = 5 SSH_AGENT_SUCCESS = 6 SSH_AGENT_CONSTRAIN_LIFETIME = 1 SSH_AGENT_CONSTRAIN_CONFIRM = 2 ED25519 = <<-EOF -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW QyNTUxOQAAACDuVIPDUXcVkXOyNAaFsotbySHLNG/Gw6gc3j2k2zcRVAAAAKD6bG5++mxu fgAAAAtzc2gtZWQyNTUxOQAAACDuVIPDUXcVkXOyNAaFsotbySHLNG/Gw6gc3j2k2zcRVA AAAEAydU4FtZ9+5o5Y/m1aPNHFda37Fm0Us5FlUKx50tWw+e5Ug8NRdxWRc7I0BoWyi1vJ Ics0b8bDqBzePaTbNxFUAAAAGmJhcnRsZUBCYXJ0bGVzLU1hY0Jvb2stUHJvAQID -----END OPENSSH PRIVATE KEY----- EOF # rubocop:disable LineLength CERT = "\x00\x00\x00\x1Cssh-rsa-cert-v01@openssh.com\x00\x00\x00 Ir\xB9\xC9\x94l\x0ER\xA1h\xF5\xFDx\xB2J\xC6g\eHS\xDD\x162\x86\xF1\x90%\\$rf\xAF\x00\x00\x00\x03\x01\x00\x01\x00\x00\x01\x01\x00\xB3R\xBC\xF8\xEA\xA30\x90\x87\x85\xF6m\x80\xFB\x7F\x96%\xC0h\x85$\x05\x05J\x9BE\xD9\xDE\x81\xC0\xC9\xC2\xC0\x0F'\xD1TR\xCBb\xCD\xD0o\xA0\x15Q\x8B\xF26t\xC9!8\x85\xD2\f'\xC6\x14u\x1De\x90qyXl\a\x06\xA7\xD0\xB8 \xE1\xB3IP\xDE\xB5\xBE\x19\x0E\x97-M\xFDJT\x81\xE2\x8E>\xCD\x18\x9CJz\x1C\xB5}LsO\xF3\xAC\xAA\r\xAB\xF9\xD4\x83\x8DQ\x82\xE7F\xA4\x9F\x1C\x9A\xC5\xC3Y\x84k\x86\ef\xD7\x84\xE3\v\rlG\x15ya\xB0=\xDF\x11\x8D\x0FtZ/p\xBB\xB7g\xF5\xEBF8\xF5\x05}}\xDB\xFA\xA34dw\xE5\x80\xBC!=\x0E\x96\x18\bF\x10\a{\xFF\x9D2\xCA\xAAnu\x82\x82\xBA-F\x8C\x12\xBB\x04+nh\xE9N\xAF\fe\x16\x00Q\x9C\x1C\xCB\x94\x02\x8CQ\xFB,H[\x96\xF1Z4\nY]@\xE0\bs\x9Bh\x0E\xAA~\x105\x99\\\x8C\xA7q\x1A=\xA9\x9D\xBAbx\xF5`[\x8Aw\x80\b\xE0vy\x00\x00\x00\x00\x00\x00\x00c\x00\x00\x00\x01\x00\x00\x00\x06foobar\x00\x00\x00\b\x00\x00\x00\x04root\x00\x00\x00\x00Xk\\\x1C\x00\x00\x00\x00ZK>g\x00\x00\x00#\x00\x00\x00\rforce-command\x00\x00\x00\x0E\x00\x00\x00\n/bin/false\x00\x00\x00c\x00\x00\x00\x15permit-X11-forwarding\x00\x00\x00\x00\x00\x00\x00\x16permit-port-forwarding\x00\x00\x00\x00\x00\x00\x00\npermit-pty\x00\x00\x00\x00\x00\x00\x00\x0Epermit-user-rc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x17\x00\x00\x00\assh-rsa\x00\x00\x00\x03\x01\x00\x01\x00\x00\x01\x01\x00\x9DRU\x0E\x83\x8Eb}\x81vOn\xCA\xBA\x01%\xFE\x87\x80b\xB5\x98R%\xA9(\xC1\xAE\xEFq|\x82L\xADQ?\x1D\xC6o\xB8\xD8pI\e\xFC\xF8\xFE^\xAD*\xA4u;\x99S\fc\x11\xBE\xFD\x047B\x1C\xF2h\xBA\xB1\xB0\n\x12F\e\x16\xF7Z\x8D\xD3\xF2f\xC0\x1C\xD8\xBE\xCC\x82\x85Qka$\xB6\xBD\x1C)\x85B\xAAf\xC8\xF3V*\xC3\x1C\xAA\xDC\xC3I\xDDe\xEFu\x02M\x12\x1A\xE2};he\x9D\xB5\xA47\xE4\x12\x8F\xE0\xF1\xA5\x91/\xFB\xEA\t\x0F \x1E\xB4B@+6\x1F\xBD\xA7\xA9u\x80\x19\xAA\xAC\xFFK\\F\x8C\xD9u\f?\xB9#[M\xDF\xB0\xFC\xE8\xF6J\x98\xA4\x99\x8F\xF9]\x88\x1D|A%\xAB\e\x0EN\xAA\xD3 \xCF\xA7}c\xDE\xF5\xBA4\xC8\xD2\x81(\x13\xB3\x94@fC\xDC\xDF\xFD\xA1\e$?\x13\xA9m\xEB*\xCA'\xB3\x19\x19\xF0\xD2\xB3P\x00\x96ou\xE90\xC4-\x1F\xCF\x1Aw\x034\xC6\xDF\xA7\x8C\xCA^Ix\x15\xFA\x9A+\x00\x00\x01\x0F\x00\x00\x00\assh-rsa\x00\x00\x01\x00I\b%\x01\xB2\xCC\x87\xD7\e\xC5\x88\x93|\x9D\xEC}\xA4\x86\xD7\xBB\xB6\xD3\x93\xFD\\\xC73\xC2*\aV\xA2\x81\x05J\x91\x9AEKV\n\xB4\xEB\xF3\xBC\xBAr\x16\xE5\x9A\xB9\xDC(0\xB4\x1C\x9F\"\x9E\xF9\x91\xD0\x1F\x9Cp\r*\xE3\x8A\xD3\xB9W$[OI\xD2\x8F8\x9B\xA4\x9E\xFFuGg\x00\xA5\xCD\r\xDB\x95\xEE)_\xC3\xBCi\xA2\xCC\r\x86\xFD\xE9\xE6\x188\x92\xFD\xCC\n\x98t\x8C\x16\xF4O\xF6\xD5\xD4\xB7\\\xB95\x19\xA3\xBBW\xF3\xF7r<\xE6\x8C\xFC\xE5\x9F\xBF\xE0\xBF\x06\xE7v\xF2\x8Ek\xA4\x02\xB6fMd\xA5e\x87\xE1\x93\xF5\x81\xCF\xDF\x88\xDC\a\xA2\e\xD5\xCA\x14\xB2>\xF4\x8F|\xE5-w\xF5\x85\xD0\xF1F((\xD1\xEEE&\x1D\xA2+\xEC\x93\xE7\xC7\xAE\xE38\xE4\xAE\xF7 \xED\xC6\r\xD6\x1A\xE1#<\xA2)j\xB3TA\\\xFF;\xC5\xA6Tu\xAAap\xDE\xF4\xF7 p\xCA\xD2\xBA\xDC\xCDv\x17\xC2\xBCQ\xDF\xAB7^\xA1G\x18\xB9\xB2F\x81\x9Fq\x92\xD3".force_encoding('BINARY') def setup @original, ENV['SSH_AUTH_SOCK'] = ENV['SSH_AUTH_SOCK'], "/path/to/ssh.agent.sock" end def teardown ENV['SSH_AUTH_SOCK'] = @original end def test_connect_should_use_agent_factory_to_determine_connection_type factory.expects(:open).with("/path/to/ssh.agent.sock").returns(socket) agent(false).connect! end def test_connect_should_use_agent_socket_factory_instead_of_factory assert_equal agent.connect!, socket assert_equal agent.connect!(agent_socket_factory), "/foo/bar.sock" end def test_connect_should_raise_error_if_connection_could_not_be_established factory.expects(:open).raises(SocketError) assert_raises(Net::SSH::Authentication::AgentNotAvailable) { agent(false).connect! } end def test_negotiate_should_raise_error_if_ssh2_agent_response_received socket.expect do |s, type, buffer| assert_equal SSH2_AGENT_REQUEST_VERSION, type assert_equal Net::SSH::Transport::ServerVersion::PROTO_VERSION, buffer.read_string s.return(SSH2_AGENT_VERSION_RESPONSE) end assert_raises(Net::SSH::Authentication::AgentNotAvailable) { agent.negotiate! } end def test_negotiate_should_raise_error_if_response_was_unexpected socket.expect do |s, type, buffer| assert_equal SSH2_AGENT_REQUEST_VERSION, type s.return(255) end assert_raises(Net::SSH::Authentication::AgentNotAvailable) { agent.negotiate! } end def test_negotiate_should_be_successful_with_expected_response socket.expect do |s, type, buffer| assert_equal SSH2_AGENT_REQUEST_VERSION, type s.return(SSH_AGENT_RSA_IDENTITIES_ANSWER) end assert_nothing_raised { agent(:connect).negotiate! } end def test_identities_should_fail_if_SSH_AGENT_FAILURE_received socket.expect do |s, type, buffer| assert_equal SSH2_AGENT_REQUEST_IDENTITIES, type s.return(SSH_AGENT_FAILURE) end assert_raises(Net::SSH::Authentication::AgentError) { agent.identities } end def test_identities_should_fail_if_SSH2_AGENT_FAILURE_received socket.expect do |s, type, buffer| assert_equal SSH2_AGENT_REQUEST_IDENTITIES, type s.return(SSH2_AGENT_FAILURE) end assert_raises(Net::SSH::Authentication::AgentError) { agent.identities } end def test_identities_should_fail_if_SSH_COM_AGENT2_FAILURE_received socket.expect do |s, type, buffer| assert_equal SSH2_AGENT_REQUEST_IDENTITIES, type s.return(SSH_COM_AGENT2_FAILURE) end assert_raises(Net::SSH::Authentication::AgentError) { agent.identities } end def test_identities_should_fail_if_response_is_not_SSH2_AGENT_IDENTITIES_ANSWER socket.expect do |s, type, buffer| assert_equal SSH2_AGENT_REQUEST_IDENTITIES, type s.return(255) end assert_raises(Net::SSH::Authentication::AgentError) { agent.identities } end def test_identities_should_augment_identities_with_comment_field key1 = key key2 = OpenSSL::PKey::DSA.new(512) socket.expect do |s, type, buffer| assert_equal SSH2_AGENT_REQUEST_IDENTITIES, type s.return(SSH2_AGENT_IDENTITIES_ANSWER, :long, 2, :string, Net::SSH::Buffer.from(:key, key1), :string, "My favorite key", :string, Net::SSH::Buffer.from(:key, key2), :string, "Okay, but not the best") end result = agent.identities assert_equal key1.to_blob, result.first.to_blob assert_equal key2.to_blob, result.last.to_blob assert_equal "My favorite key", result.first.comment assert_equal "Okay, but not the best", result.last.comment end def test_identities_should_ignore_unimplemented_ones key1 = key key2 = OpenSSL::PKey::DSA.new(512) key2.to_blob[0..5]='badkey' key3 = OpenSSL::PKey::DSA.new(512) socket.expect do |s, type, buffer| assert_equal SSH2_AGENT_REQUEST_IDENTITIES, type s.return(SSH2_AGENT_IDENTITIES_ANSWER, :long, 3, :string, Net::SSH::Buffer.from(:key, key1), :string, "My favorite key", :string, Net::SSH::Buffer.from(:key, key2), :string, "bad", :string, Net::SSH::Buffer.from(:key, key3), :string, "Okay, but not the best") end result = agent.identities assert_equal 2,result.size assert_equal key1.to_blob, result.first.to_blob assert_equal key3.to_blob, result.last.to_blob assert_equal "My favorite key", result.first.comment assert_equal "Okay, but not the best", result.last.comment end def test_close_should_close_socket socket.expects(:close) agent.close end def test_sign_should_fail_if_response_is_SSH_AGENT_FAILURE socket.expect { |s,| s.return(SSH_AGENT_FAILURE) } assert_raises(Net::SSH::Authentication::AgentError) { agent.sign(key, "hello world") } end def test_sign_should_fail_if_response_is_SSH2_AGENT_FAILURE socket.expect { |s,| s.return(SSH2_AGENT_FAILURE) } assert_raises(Net::SSH::Authentication::AgentError) { agent.sign(key, "hello world") } end def test_sign_should_fail_if_response_is_SSH_COM_AGENT2_FAILURE socket.expect { |s,| s.return(SSH_COM_AGENT2_FAILURE) } assert_raises(Net::SSH::Authentication::AgentError) { agent.sign(key, "hello world") } end def test_sign_should_fail_if_response_is_not_SSH2_AGENT_SIGN_RESPONSE socket.expect { |s,| s.return(255) } assert_raises(Net::SSH::Authentication::AgentError) { agent.sign(key, "hello world") } end def test_sign_should_return_signed_data_from_agent socket.expect do |s,type,buffer| assert_equal SSH2_AGENT_SIGN_REQUEST, type assert_equal key.to_blob, Net::SSH::Buffer.new(buffer.read_string).read_key.to_blob assert_equal "hello world", buffer.read_string assert_equal 0, buffer.read_long s.return(SSH2_AGENT_SIGN_RESPONSE, :string, "abcxyz123") end assert_equal "abcxyz123", agent.sign(key, "hello world") end def test_add_rsa_identity_with_constraints rsa = OpenSSL::PKey::RSA.new(512) socket.expect do |s,type,buffer| assert_equal SSH2_AGENT_ADD_ID_CONSTRAINED, type assert_equal buffer.read_string, "ssh-rsa" assert_equal buffer.read_bignum.to_s, rsa.n.to_s assert_equal buffer.read_bignum.to_s, rsa.e.to_s assert_equal buffer.read_bignum.to_s, rsa.d.to_s assert_equal buffer.read_bignum.to_s, rsa.iqmp.to_s assert_equal buffer.read_bignum.to_s, rsa.p.to_s assert_equal buffer.read_bignum.to_s, rsa.q.to_s assert_equal 'foobar', buffer.read_string assert_equal SSH_AGENT_CONSTRAIN_LIFETIME, buffer.read_byte assert_equal 42, buffer.read_long assert_equal SSH_AGENT_CONSTRAIN_CONFIRM, buffer.read_byte assert buffer.eof? s.return(SSH_AGENT_SUCCESS) end agent.add_identity(rsa, "foobar", lifetime: 42, confirm: true) end def test_add_rsa_cert_identity cert = make_cert(OpenSSL::PKey::RSA.new(512)) socket.expect do |s,type,buffer| assert_equal SSH2_AGENT_ADD_IDENTITY, type assert_equal buffer.read_string, "ssh-rsa-cert-v01@openssh.com" assert_equal buffer.read_string, cert.to_blob assert_equal buffer.read_bignum.to_s, cert.key.d.to_s assert_equal buffer.read_bignum.to_s, cert.key.iqmp.to_s assert_equal buffer.read_bignum.to_s, cert.key.p.to_s assert_equal buffer.read_bignum.to_s, cert.key.q.to_s assert_equal 'foobar', buffer.read_string assert buffer.eof? s.return(SSH_AGENT_SUCCESS) end agent.add_identity(cert, "foobar") end def test_add_dsa_identity dsa = OpenSSL::PKey::DSA.new(512) socket.expect do |s,type,buffer| assert_equal SSH2_AGENT_ADD_IDENTITY, type assert_equal buffer.read_string, "ssh-dss" assert_equal buffer.read_bignum.to_s, dsa.p.to_s assert_equal buffer.read_bignum.to_s, dsa.q.to_s assert_equal buffer.read_bignum.to_s, dsa.g.to_s assert_equal buffer.read_bignum.to_s, dsa.pub_key.to_s assert_equal buffer.read_bignum.to_s, dsa.priv_key.to_s assert_equal 'foobar', buffer.read_string assert buffer.eof? s.return(SSH_AGENT_SUCCESS) end agent.add_identity(dsa, "foobar") end def test_add_dsa_cert_identity cert = make_cert(OpenSSL::PKey::DSA.new(512)) socket.expect do |s,type,buffer| assert_equal SSH2_AGENT_ADD_IDENTITY, type assert_equal buffer.read_string, "ssh-dss-cert-v01@openssh.com" assert_equal buffer.read_string, cert.to_blob assert_equal buffer.read_bignum.to_s, cert.key.priv_key.to_s assert_equal 'foobar', buffer.read_string assert buffer.eof? s.return(SSH_AGENT_SUCCESS) end agent.add_identity(cert, "foobar") end def test_add_ecdsa_identity return unless defined?(OpenSSL::PKey::EC) ecdsa = OpenSSL::PKey::EC.new("prime256v1").generate_key socket.expect do |s,type,buffer| assert_equal SSH2_AGENT_ADD_IDENTITY, type assert_equal buffer.read_string, "ecdsa-sha2-nistp256" assert_equal buffer.read_string, "nistp256" assert_equal buffer.read_string, ecdsa.public_key.to_bn.to_s(2) assert_equal buffer.read_bignum, ecdsa.private_key assert_equal 'foobar', buffer.read_string assert buffer.eof? s.return(SSH_AGENT_SUCCESS) end agent.add_identity(ecdsa, "foobar") end def test_add_ecdsa_cert_identity return unless defined?(OpenSSL::PKey::EC) cert = make_cert(OpenSSL::PKey::EC.new("prime256v1").generate_key) socket.expect do |s,type,buffer| assert_equal SSH2_AGENT_ADD_IDENTITY, type assert_equal buffer.read_string, "ecdsa-sha2-nistp256-cert-v01@openssh.com" assert_equal buffer.read_string, cert.to_blob assert_equal buffer.read_bignum, cert.key.private_key assert_equal 'foobar', buffer.read_string assert buffer.eof? s.return(SSH_AGENT_SUCCESS) end agent.add_identity(cert, "foobar") end def test_add_ed25519_identity return unless Net::SSH::Authentication::ED25519Loader::LOADED ed25519 = Net::SSH::Authentication::ED25519::PrivKey.new(ED25519, nil) socket.expect do |s,type,buffer| assert_equal SSH2_AGENT_ADD_IDENTITY, type assert_equal buffer.read_string, "ssh-ed25519" assert_equal buffer.read_string, ed25519.public_key.verify_key.to_bytes assert_equal buffer.read_string, ed25519.sign_key.keypair_bytes assert_equal 'foobar', buffer.read_string assert buffer.eof? s.return(SSH_AGENT_SUCCESS) end agent.add_identity(ed25519, "foobar") end def test_add_ed25519_cert_identity return unless Net::SSH::Authentication::ED25519Loader::LOADED cert = make_cert(Net::SSH::Authentication::ED25519::PrivKey.new(ED25519, nil)) socket.expect do |s,type,buffer| assert_equal SSH2_AGENT_ADD_IDENTITY, type assert_equal buffer.read_string, "ssh-ed25519-cert-v01@openssh.com" assert_equal buffer.read_string, cert.to_blob assert_equal buffer.read_string, cert.key.public_key.verify_key.to_bytes assert_equal buffer.read_string, cert.key.sign_key.keypair_bytes assert_equal 'foobar', buffer.read_string assert buffer.eof? s.return(SSH_AGENT_SUCCESS) end agent.add_identity(cert, "foobar") end def test_add_identity_should_raise_error_on_failure socket.expect do |s,type,buffer| s.return(SSH_AGENT_FAILURE) end assert_raises(Net::SSH::Authentication::AgentError) do agent.add_identity(key, "foobar") end end def test_remove_identity socket.expect do |s,type,buffer| assert_equal SSH2_AGENT_REMOVE_IDENTITY, type assert_equal buffer.read_string, key.to_blob assert buffer.eof? s.return(SSH_AGENT_SUCCESS) end agent.remove_identity(key) end def test_remove_identity_should_raise_error_on_failure socket.expect do |s,type,buffer| s.return(SSH_AGENT_FAILURE) end assert_raises(Net::SSH::Authentication::AgentError) do agent.remove_identity(key) end end def test_remove_all_identities socket.expect do |s,type,buffer| assert_equal SSH2_AGENT_REMOVE_ALL_IDENTITIES, type assert buffer.eof? s.return(SSH_AGENT_SUCCESS) end agent.remove_all_identities end def test_remove_all_identities_should_raise_error_on_failure socket.expect do |s,type,buffer| s.return(SSH_AGENT_FAILURE) end assert_raises(Net::SSH::Authentication::AgentError) do agent.remove_all_identities end end private def make_cert(key) cert = Net::SSH::Buffer.new(CERT).read_key cert.key = key cert.sign!(key) end class MockSocket def initialize @expectation = nil @buffer = Net::SSH::Buffer.new end def expect(&block) @expectation = block end def return(type, *args) data = Net::SSH::Buffer.from(*args) @buffer.append([data.length+1, type, data.to_s].pack("NCA*")) end def send(data, flags) raise "got #{data.inspect} but no packet was expected" unless @expectation buffer = Net::SSH::Buffer.new(data) buffer.read_long # skip the length type = buffer.read_byte @expectation.call(self, type, buffer) @expectation = nil end def read(length) @buffer.read(length) end end def key @key ||= OpenSSL::PKey::RSA.new(512) end def socket @socket ||= MockSocket.new end def factory @factory ||= stub("socket factory", open: socket) end def agent(auto=:connect) @agent ||= begin agent = Net::SSH::Authentication::Agent.new agent.stubs(:unix_socket_class).returns(factory) agent.connect! if auto == :connect agent end end def agent_socket_factory @agent_socket_factory ||= ->{"/foo/bar.sock"} end end end net-ssh-4.2.0/test/authentication/test_certificate.rb000066400000000000000000000230611315376572000227550ustar00rootroot00000000000000require_relative '../common' require 'net/ssh/authentication/certificate' CA_KEY = <<-EOF -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAnVJVDoOOYn2Bdk9uyroBJf6HgGK1mFIlqSjBru9xfIJMrVE/ HcZvuNhwSRv8+P5erSqkdTuZUwxjEb79BDdCHPJourGwChJGGxb3Wo3T8mbAHNi+ zIKFUWthJLa9HCmFQqpmyPNWKsMcqtzDSd1l73UCTRIa4n07aGWdtaQ35BKP4PGl kS/76gkPIB60QkArNh+9p6l1gBmqrP9LXEaM2XUMP7kjW03fsPzo9kqYpJmP+V2I HXxBJasbDk6q0yDPp31j3vW6NMjSgSgTs5RAZkPc3/2hGyQ/E6lt6yrKJ7MZGfDS s1AAlm916TDELR/PGncDNMbfp4zKXkl4FfqaKwIDAQABAoIBAAZVIBNbfEm+n41x mRYT8qPi4PVsA79D3zw15cXy4XCPliKL2KyMJkccfziSJdan9oul4cTOR1eucfZu 56RZzRF5OHn7WQiuv5+rhv1gJB3nwOfoWZXF0zP5zIk7ydTuXuzWCxkfomJKREck Z7/7Z3UCErujdO2U+OU04epD1/QYMwWZC+S1HT45zGbQtTA0M0EnZ+3kNuWP3DKk GdZ4kGQU3n8gvD6ygyYgP33tMlVFZrkSPFgZn/s0Tq5f/7dsiBg/wuDkDU63JXm0 YXsltGAJ059ptOtEQkWPorCJQ/SRDdIo99VCVmwvkJh5BX4kpTcgSBqR8fSosCkb bt2QBdECgYEAzMauVPgByL8zkoWnrv8BVoICVDyO/YjcMJeA/DzyOc6KdAzJ37a9 17aUOoA+hOAI74RusOnnjoHhNwTa8n4Fxo8oLhGxcQhimwIcQ9GA2bB3LyU2te91 m6dGl2UIZPDbaPLRE4KDzAKSHsJdU+bZE035ZhvHpuc4+g+RfvDwfD8CgYEAxKzK pHezJ2RgZEhz6jI5pKIAMZbz9ogmNbeGzWrKPsuFnQ0pjRADVVtRCj8/fCQUzNtR TJdYIowVA2mnJOH8/7QBD7KWK8q60egPlCZ5Jhq0c2IlBLIR3ICUf/HbT+GKZ3Ja XJKZxhd02JqJ4hcZkt5lO8cWdgYi0wto+5I5VxUCgYEAtmw8e6dgd4SVne8BPa0g dP9mscItJAGbHpKpLovgwcyUnOOTp381Sgj1rdP3XgnzC+Tvcx080kAz8P6bSjEo VgXMJpZOe8KbjTlpEqV9YvCIjHBbd+J15A81nMM9oibLX4gI55d6b/DOWSaPW6Io OcFZ7zPKPY54vJPH6s0bf6MCgYAf5DD72FkZoyIqQMFjEX/dXVOQtvyaVltzzG20 c4OWCSSCYfcB473WoncSpUzjEWq6CTo2pDfrajGiGwi6Z1bCE+s0I25MbZQ7o1ib Wl28uwnVx+1exI025zatRIeefWEXAyj559+9imItGWoQWlSQRzW9KrxOqRIOjMQa PwzDPQKBgBT0PXS9xtWOJrSjM14OfgUwv6N0jRLknknKLHma7tyXbFKw1py107Dy XptT2M+GhtBGbjCw4sz6GLkgLQLBmKmm0Ktr0BulAIl33j/gYL7UWLeU3703e2Xx CAHisz08DFRX3OkU2bsRDhyhJVjydWHhQSlokF9WPR69Lho6Y1HU -----END RSA PRIVATE KEY----- EOF KEY = <<-EOF -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAs1K8+OqjMJCHhfZtgPt/liXAaIUkBQVKm0XZ3oHAycLADyfR VFLLYs3Qb6AVUYvyNnTJITiF0gwnxhR1HWWQcXlYbAcGp9C4IOGzSVDetb4ZDpct Tf1KVIHijj7NGJxKehy1fUxzT/Osqg2r+dSDjVGC50aknxyaxcNZhGuGG2bXhOML DWxHFXlhsD3fEY0PdFovcLu3Z/XrRjj1BX192/qjNGR35YC8IT0OlhgIRhAHe/+d MsqqbnWCgrotRowSuwQrbmjpTq8MZRYAUZwcy5QCjFH7LEhblvFaNApZXUDgCHOb aA6qfhA1mVyMp3EaPamdumJ49WBbineACOB2eQIDAQABAoIBAGM9XcFvsQJWafn0 R+PCy3gfylzNmgKBTCmkPY+LRVMjSUDZ61n8O/yhJEIyWLn5dgE3HnwZGM4G1hgk CDBNneN+oTWfqcpDkzL3VU40yBvSaXGOro7jpzgfbW8FSGHfVMRBkRsXrRVJKHwv 9sXbGzahLo2ppb88iFb75lWHX/9XO2Hm9ozY+wPwPrOxqLumcVefBTUbc8nUIC2j Qx81wUhPf4xXAnxdtYURhsJOjnznzINBfA0X5p7Ecamy6i1o4n2AgMWdXaPrJiIB xNBuFRDdKcDjP4qKbcwXFkARtsy45WPK3gz+Mm61sdUE3XZIGa+oRLSkzMDM81Oh e4pmaVECgYEA7ixbkPW67oXX1+DzoTBrKs77s0vM/RaBmDOQrVHqmJN19A5bFEuj bqc5z+6cwQmHQz/J8x2WyicrCzROeTDSvQf+AjL4noEjMdeX/YU14Gaz74ntWJ5T tvOtFl5iF1ffpoHDFckXTJ1fveF7YudiQ8E7rkw53D7TWXCJbRbQp/UCgYEAwL7A zUM9OP1SwyxsMk7+ovtEUh4qfBUiWTq9HGxbEUjhPAjnIQRL1mdLJko3k2oHzNT3 hTSwu82cd5Zws5+vuNMG2dFQqW62J3d2uIzPrWm8pNlZQsuME/rZBIi212a5UcYZ oRYIO7OkVJrbj/m99pimLH4tdV2RazXeTB6CNfUCgYEAgjhOeBticTdMpAOiMOdA MM+qXoV7NoUvpf/LgnffRDybqSyQL7CLUtyrhzx3CDQleGdQC1SKNUzlA+M9ZJWF I0VTY/Bqbn88tuuhdkN3CZIdn0JSOrmWG9lvMWO5TfoFlgwslaS00Hba+f5mb9UC rPjhoJKcsAbJl4UoHjTzMGECgYACQvq+LdjND2PmOGI4oOaqAOrHT+VNuW3CwEax y6+x3zoNW0ljAMrnBCVEmMBYMXlP9PvGi1y7h2kbmh9ObERCle9RpPweUNdAVU2G Utio/0GgaZB7kSneniXnwLbshh8Mj5eDZV/JW41FFOAYq2SIPThN81kTNHrdWC94 ky8R9QKBgQDXzGNrXay1Qn4TewdlevK7PNiB9xzkdRGf7pYCh5PWMFWDDJUwo17S gTn5Kf77XhIZskNDA12mVZgR+EnDmybHqWybRG3pKc4hmKogKmDTHjnG4M/cVbNE zlh8zP2fVxwrdNFkJxn9a+9/qIhkh/if9JhCecajaE9mD3xvOJ1/iA== -----END RSA PRIVATE KEY----- EOF # Generated via `ssh-keygen -s ca -I foobar -V +52w -O no-agent-forwarding -O force-command=/bin/false -z 99 key`. # rubocop:disable LineLength SIGNED_CERT = "\x00\x00\x00\x1Cssh-rsa-cert-v01@openssh.com\x00\x00\x00 Ir\xB9\xC9\x94l\x0ER\xA1h\xF5\xFDx\xB2J\xC6g\eHS\xDD\x162\x86\xF1\x90%\\$rf\xAF\x00\x00\x00\x03\x01\x00\x01\x00\x00\x01\x01\x00\xB3R\xBC\xF8\xEA\xA30\x90\x87\x85\xF6m\x80\xFB\x7F\x96%\xC0h\x85$\x05\x05J\x9BE\xD9\xDE\x81\xC0\xC9\xC2\xC0\x0F'\xD1TR\xCBb\xCD\xD0o\xA0\x15Q\x8B\xF26t\xC9!8\x85\xD2\f'\xC6\x14u\x1De\x90qyXl\a\x06\xA7\xD0\xB8 \xE1\xB3IP\xDE\xB5\xBE\x19\x0E\x97-M\xFDJT\x81\xE2\x8E>\xCD\x18\x9CJz\x1C\xB5}LsO\xF3\xAC\xAA\r\xAB\xF9\xD4\x83\x8DQ\x82\xE7F\xA4\x9F\x1C\x9A\xC5\xC3Y\x84k\x86\ef\xD7\x84\xE3\v\rlG\x15ya\xB0=\xDF\x11\x8D\x0FtZ/p\xBB\xB7g\xF5\xEBF8\xF5\x05}}\xDB\xFA\xA34dw\xE5\x80\xBC!=\x0E\x96\x18\bF\x10\a{\xFF\x9D2\xCA\xAAnu\x82\x82\xBA-F\x8C\x12\xBB\x04+nh\xE9N\xAF\fe\x16\x00Q\x9C\x1C\xCB\x94\x02\x8CQ\xFB,H[\x96\xF1Z4\nY]@\xE0\bs\x9Bh\x0E\xAA~\x105\x99\\\x8C\xA7q\x1A=\xA9\x9D\xBAbx\xF5`[\x8Aw\x80\b\xE0vy\x00\x00\x00\x00\x00\x00\x00c\x00\x00\x00\x01\x00\x00\x00\x06foobar\x00\x00\x00\b\x00\x00\x00\x04root\x00\x00\x00\x00Xk\\\x1C\x00\x00\x00\x00ZK>g\x00\x00\x00#\x00\x00\x00\rforce-command\x00\x00\x00\x0E\x00\x00\x00\n/bin/false\x00\x00\x00c\x00\x00\x00\x15permit-X11-forwarding\x00\x00\x00\x00\x00\x00\x00\x16permit-port-forwarding\x00\x00\x00\x00\x00\x00\x00\npermit-pty\x00\x00\x00\x00\x00\x00\x00\x0Epermit-user-rc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x17\x00\x00\x00\assh-rsa\x00\x00\x00\x03\x01\x00\x01\x00\x00\x01\x01\x00\x9DRU\x0E\x83\x8Eb}\x81vOn\xCA\xBA\x01%\xFE\x87\x80b\xB5\x98R%\xA9(\xC1\xAE\xEFq|\x82L\xADQ?\x1D\xC6o\xB8\xD8pI\e\xFC\xF8\xFE^\xAD*\xA4u;\x99S\fc\x11\xBE\xFD\x047B\x1C\xF2h\xBA\xB1\xB0\n\x12F\e\x16\xF7Z\x8D\xD3\xF2f\xC0\x1C\xD8\xBE\xCC\x82\x85Qka$\xB6\xBD\x1C)\x85B\xAAf\xC8\xF3V*\xC3\x1C\xAA\xDC\xC3I\xDDe\xEFu\x02M\x12\x1A\xE2};he\x9D\xB5\xA47\xE4\x12\x8F\xE0\xF1\xA5\x91/\xFB\xEA\t\x0F \x1E\xB4B@+6\x1F\xBD\xA7\xA9u\x80\x19\xAA\xAC\xFFK\\F\x8C\xD9u\f?\xB9#[M\xDF\xB0\xFC\xE8\xF6J\x98\xA4\x99\x8F\xF9]\x88\x1D|A%\xAB\e\x0EN\xAA\xD3 \xCF\xA7}c\xDE\xF5\xBA4\xC8\xD2\x81(\x13\xB3\x94@fC\xDC\xDF\xFD\xA1\e$?\x13\xA9m\xEB*\xCA'\xB3\x19\x19\xF0\xD2\xB3P\x00\x96ou\xE90\xC4-\x1F\xCF\x1Aw\x034\xC6\xDF\xA7\x8C\xCA^Ix\x15\xFA\x9A+\x00\x00\x01\x0F\x00\x00\x00\assh-rsa\x00\x00\x01\x00I\b%\x01\xB2\xCC\x87\xD7\e\xC5\x88\x93|\x9D\xEC}\xA4\x86\xD7\xBB\xB6\xD3\x93\xFD\\\xC73\xC2*\aV\xA2\x81\x05J\x91\x9AEKV\n\xB4\xEB\xF3\xBC\xBAr\x16\xE5\x9A\xB9\xDC(0\xB4\x1C\x9F\"\x9E\xF9\x91\xD0\x1F\x9Cp\r*\xE3\x8A\xD3\xB9W$[OI\xD2\x8F8\x9B\xA4\x9E\xFFuGg\x00\xA5\xCD\r\xDB\x95\xEE)_\xC3\xBCi\xA2\xCC\r\x86\xFD\xE9\xE6\x188\x92\xFD\xCC\n\x98t\x8C\x16\xF4O\xF6\xD5\xD4\xB7\\\xB95\x19\xA3\xBBW\xF3\xF7r<\xE6\x8C\xFC\xE5\x9F\xBF\xE0\xBF\x06\xE7v\xF2\x8Ek\xA4\x02\xB6fMd\xA5e\x87\xE1\x93\xF5\x81\xCF\xDF\x88\xDC\a\xA2\e\xD5\xCA\x14\xB2>\xF4\x8F|\xE5-w\xF5\x85\xD0\xF1F((\xD1\xEEE&\x1D\xA2+\xEC\x93\xE7\xC7\xAE\xE38\xE4\xAE\xF7 \xED\xC6\r\xD6\x1A\xE1#<\xA2)j\xB3TA\\\xFF;\xC5\xA6Tu\xAAap\xDE\xF4\xF7 p\xCA\xD2\xBA\xDC\xCDv\x17\xC2\xBCQ\xDF\xAB7^\xA1G\x18\xB9\xB2F\x81\x9Fq\x92\xD3".force_encoding('BINARY') module Authentication class TestCertificate < NetSSHTest def test_certificate cert = Net::SSH::Buffer.new(SIGNED_CERT).read_key assert_equal "Ir\xB9\xC9\x94l\x0ER\xA1h\xF5\xFDx\xB2J\xC6g\eHS\xDD\x162\x86\xF1\x90%\\$rf\xAF".force_encoding('BINARY'), cert.nonce assert_equal 99, cert.serial assert_equal :user, cert.type assert_equal "foobar", cert.key_id assert_equal ["root"], cert.valid_principals assert_equal Time.at(1483430940), cert.valid_after assert_equal Time.at(1514880615), cert.valid_before assert_equal({"force-command" => "/bin/false"}, cert.critical_options) assert_equal({"permit-X11-forwarding" => "", "permit-port-forwarding" => "", "permit-pty" => "", "permit-user-rc" => ""}, cert.extensions) assert_equal "", cert.reserved assert_equal "\x00\x00\x00\assh-rsa\x00\x00\x00\x03\x01\x00\x01\x00\x00\x01\x01\x00\x9DRU\x0E\x83\x8Eb}\x81vOn\xCA\xBA\x01%\xFE\x87\x80b\xB5\x98R%\xA9(\xC1\xAE\xEFq|\x82L\xADQ?\x1D\xC6o\xB8\xD8pI\e\xFC\xF8\xFE^\xAD*\xA4u;\x99S\fc\x11\xBE\xFD\x047B\x1C\xF2h\xBA\xB1\xB0\n\x12F\e\x16\xF7Z\x8D\xD3\xF2f\xC0\x1C\xD8\xBE\xCC\x82\x85Qka$\xB6\xBD\x1C)\x85B\xAAf\xC8\xF3V*\xC3\x1C\xAA\xDC\xC3I\xDDe\xEFu\x02M\x12\x1A\xE2};he\x9D\xB5\xA47\xE4\x12\x8F\xE0\xF1\xA5\x91/\xFB\xEA\t\x0F \x1E\xB4B@+6\x1F\xBD\xA7\xA9u\x80\x19\xAA\xAC\xFFK\\F\x8C\xD9u\f?\xB9#[M\xDF\xB0\xFC\xE8\xF6J\x98\xA4\x99\x8F\xF9]\x88\x1D|A%\xAB\e\x0EN\xAA\xD3 \xCF\xA7}c\xDE\xF5\xBA4\xC8\xD2\x81(\x13\xB3\x94@fC\xDC\xDF\xFD\xA1\e$?\x13\xA9m\xEB*\xCA'\xB3\x19\x19\xF0\xD2\xB3P\x00\x96ou\xE90\xC4-\x1F\xCF\x1Aw\x034\xC6\xDF\xA7\x8C\xCA^Ix\x15\xFA\x9A+".force_encoding('BINARY'), cert.signature_key.to_blob expected_signature = "\x00\x00\x00\assh-rsa\x00\x00\x01\x00I\b%\x01\xB2\xCC\x87\xD7\e\xC5\x88\x93|\x9D\xEC}\xA4\x86\xD7\xBB\xB6\xD3\x93\xFD\\\xC73\xC2*\aV\xA2\x81\x05J\x91\x9AEKV\n\xB4\xEB\xF3\xBC\xBAr\x16\xE5\x9A\xB9\xDC(0\xB4\x1C\x9F\"\x9E\xF9\x91\xD0\x1F\x9Cp\r*\xE3\x8A\xD3\xB9W$[OI\xD2\x8F8\x9B\xA4\x9E\xFFuGg\x00\xA5\xCD\r\xDB\x95\xEE)_\xC3\xBCi\xA2\xCC\r\x86\xFD\xE9\xE6\x188\x92\xFD\xCC\n\x98t\x8C\x16\xF4O\xF6\xD5\xD4\xB7\\\xB95\x19\xA3\xBBW\xF3\xF7r<\xE6\x8C\xFC\xE5\x9F\xBF\xE0\xBF\x06\xE7v\xF2\x8Ek\xA4\x02\xB6fMd\xA5e\x87\xE1\x93\xF5\x81\xCF\xDF\x88\xDC\a\xA2\e\xD5\xCA\x14\xB2>\xF4\x8F|\xE5-w\xF5\x85\xD0\xF1F((\xD1\xEEE&\x1D\xA2+\xEC\x93\xE7\xC7\xAE\xE38\xE4\xAE\xF7 \xED\xC6\r\xD6\x1A\xE1#<\xA2)j\xB3TA\\\xFF;\xC5\xA6Tu\xAAap\xDE\xF4\xF7 p\xCA\xD2\xBA\xDC\xCDv\x17\xC2\xBCQ\xDF\xAB7^\xA1G\x18\xB9\xB2F\x81\x9Fq\x92\xD3".force_encoding('BINARY') assert_equal expected_signature, cert.signature assert cert.signature_valid? assert_equal SIGNED_CERT, cert.to_blob end def test_signature_not_valid_for_corrupted_cert cert = Net::SSH::Buffer.new(SIGNED_CERT).read_key cert.nonce = 'x' * 32 assert !cert.signature_valid? end def test_sign cert = Net::SSH::Buffer.new(SIGNED_CERT).read_key cert.signature = nil cert.signature_key = nil cert.sign!(OpenSSL::PKey::RSA.new(CA_KEY), cert.nonce) assert_equal SIGNED_CERT, cert.to_blob end end end net-ssh-4.2.0/test/authentication/test_ed25519.rb000066400000000000000000000056661315376572000215040ustar00rootroot00000000000000unless ENV['NET_SSH_NO_RBNACL'] require 'common' require 'net/ssh/authentication/ed25519_loader' require 'base64' module Authentication class TestED25519 < NetSSHTest def setup raise "No ED25519 set NET_SSH_NO_RBNACL to ignore this test" unless Net::SSH::Authentication::ED25519Loader::LOADED end def test_no_pwd_key pub = Net::SSH::Buffer.new(Base64.decode64(public_key_no_pwd.split(' ')[1])) _type = pub.read_string pub_data = pub.read_string priv = private_key_no_pwd pub_key = Net::SSH::Authentication::ED25519::PubKey.new(pub_data) priv_key = Net::SSH::Authentication::ED25519::PrivKey.new(priv,nil) shared_secret = "Hello" signed = priv_key.ssh_do_sign(shared_secret) self.assert_equal(true,pub_key.ssh_do_verify(signed,shared_secret)) self.assert_equal(priv_key.public_key.fingerprint, pub_key.fingerprint) end def test_pwd_key if defined?(JRUBY_VERSION) puts "Skipping password protected ED25519 for JRuby" return end pub = Net::SSH::Buffer.new(Base64.decode64(public_key_pwd.split(' ')[1])) _type = pub.read_string pub_data = pub.read_string priv = private_key_pwd pub_key = Net::SSH::Authentication::ED25519::PubKey.new(pub_data) priv_key = Net::SSH::Authentication::ED25519::PrivKey.new(priv,'pwd') shared_secret = "Hello" signed = priv_key.ssh_do_sign(shared_secret) self.assert_equal(true,pub_key.ssh_do_verify(signed,shared_secret)) self.assert_equal(priv_key.public_key.fingerprint, pub_key.fingerprint) end def private_key_pwd @pwd_key = <<-EOF -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABBxwCvr3V /8pWhC/xvTnGJhAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAICaHkFaGXqYhUVFc aZ10TPUbkIvmaFXwYRoOS5qE8MciAAAAsNUAhbNQKwNcOr0eNq3nhtjoyeVyH8hRrpWsiY 46vPiECi6R6OdYGSd7W3fdzUDeyOYCY9ZVIjAzENG+9FsygYzMi6XCuw00OuDFLUp4fL4K i/coUIVqouB4TPQAmsCVXiIRVTWQtRG0kWfFaV3qRt/bc22ZCvCT6ZZ1UmtulqqfUhSlKM oPcTikV1iWH5Xc+GxRFRRGTN/6HvBf0AKDB1kMXlDhGnBnHGeNH1pk44xG -----END OPENSSH PRIVATE KEY----- EOF end def public_key_pwd 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICaHkFaGXqYhUVFcaZ10TPUbkIvmaFXwYRoOS5qE8Mci vagrant@vagrant-ubuntu-trusty-64' end def private_key_no_pwd @anonymous_key = <<-EOF -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW QyNTUxOQAAACAwdjQYeBiTz1DdZFzzLvG+t913L+eVqCgtzpAYxQG8yQAAAKjlHzLo5R8y 6AAAAAtzc2gtZWQyNTUxOQAAACAwdjQYeBiTz1DdZFzzLvG+t913L+eVqCgtzpAYxQG8yQ AAAEBPrD+n4901Y+NYJ2sry+EWRdltGFhMISvp91TywJ//mTB2NBh4GJPPUN1kXPMu8b63 3Xcv55WoKC3OkBjFAbzJAAAAIHZhZ3JhbnRAdmFncmFudC11YnVudHUtdHJ1c3R5LTY0AQ IDBAU= -----END OPENSSH PRIVATE KEY----- EOF end def public_key_no_pwd 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDB2NBh4GJPPUN1kXPMu8b633Xcv55WoKC3OkBjFAbzJ vagrant@vagrant-ubuntu-trusty-64' end end end end net-ssh-4.2.0/test/authentication/test_key_manager.rb000066400000000000000000000237561315376572000227700ustar00rootroot00000000000000require_relative '../common' require 'net/ssh/authentication/key_manager' module Authentication class TestKeyManager < NetSSHTest def test_key_files_and_known_identities_are_empty_by_default assert manager.key_files.empty? assert manager.known_identities.empty? end def test_assume_agent_is_available_by_default assert manager.use_agent? end def test_add_ensures_list_is_unique manager.add "/first" manager.add "/second" manager.add "/third" manager.add "/second" assert_equal 3, manager.key_files.length final_files = manager.key_files.map {|item| item.split('/').last} assert_equal %w(first second third), final_files end def test_use_agent_should_be_set_to_false_if_agent_could_not_be_found Net::SSH::Authentication::Agent.expects(:connect).raises(Net::SSH::Authentication::AgentNotAvailable) assert manager.use_agent? assert_nil manager.agent assert !manager.use_agent? end def test_agent_should_be_used_by_default assert manager().use_agent? end def test_agent_should_not_be_used_with_no_agent assert !manager(use_agent: false).use_agent? end def test_each_identity_should_load_from_key_files manager.stubs(:agent).returns(nil) first = File.expand_path("/first") second = File.expand_path("/second") stub_file_private_key first, rsa stub_file_private_key second, dsa identities = [] manager.each_identity { |identity| identities << identity } assert_equal 2, identities.length assert_equal rsa.to_blob, identities.first.to_blob assert_equal dsa.to_blob, identities.last.to_blob assert_equal({from: :file, file: first, key: rsa}, manager.known_identities[rsa]) assert_equal({from: :file, file: second, key: dsa}, manager.known_identities[dsa]) end def test_each_identity_should_load_form_cert_file manager.stubs(:agent).returns(nil) first = File.expand_path("/first") stub_file_cert first, rsa identities = [] manager.each_identity { |identity| identities << identity } assert_equal 1, identities.length assert_equal rsa.to_blob, identities.first.to_blob assert_equal({from: :file, file: first}, manager.known_identities[rsa]) end def test_each_identity_should_not_prompt_for_passphrase_in_non_interactive_mode manager(non_interactive: true).stubs(:agent).returns(nil) first = File.expand_path("/first") stub_file_private_key first, rsa, passphrase: :should_not_be_asked identities = [] manager.each_identity { |identity| identities << identity } assert_equal(identities, []) end def test_identities_should_load_from_agent manager.stubs(:agent).returns(agent) identities = [] manager.each_identity { |identity| identities << identity } assert_equal 2, identities.length assert_equal rsa.to_blob, identities.first.to_blob assert_equal dsa.to_blob, identities.last.to_blob assert_equal({from: :agent}, manager.known_identities[rsa]) assert_equal({from: :agent}, manager.known_identities[dsa]) end if defined?(OpenSSL::PKey::EC) def test_identities_with_ecdsa_should_load_from_agent manager.stubs(:agent).returns(agent_with_ecdsa_keys) identities = [] manager.each_identity { |identity| identities << identity } assert_equal 5, identities.length assert_equal rsa.to_blob, identities[0].to_blob assert_equal dsa.to_blob, identities[1].to_blob assert_equal ecdsa_sha2_nistp256.to_blob, identities[2].to_blob assert_equal ecdsa_sha2_nistp384.to_blob, identities[3].to_blob assert_equal ecdsa_sha2_nistp521.to_blob, identities[4].to_blob assert_equal({from: :agent}, manager.known_identities[rsa]) assert_equal({from: :agent}, manager.known_identities[dsa]) assert_equal({from: :agent}, manager.known_identities[ecdsa_sha2_nistp256]) assert_equal({from: :agent}, manager.known_identities[ecdsa_sha2_nistp384]) assert_equal({from: :agent}, manager.known_identities[ecdsa_sha2_nistp521]) end end def test_only_identities_with_key_files_should_load_from_agent_of_keys_only_set manager(keys_only: true).stubs(:agent).returns(agent) first = File.expand_path("/first") stub_file_private_key first, rsa identities = [] manager.each_identity { |identity| identities << identity } assert_equal 1, identities.length assert_equal rsa.to_blob, identities.first.to_blob assert_equal({from: :agent}, manager.known_identities[rsa]) assert manager.use_agent? end def test_identities_without_public_key_files_should_not_be_touched_if_identity_loaded_from_agent manager.stubs(:agent).returns(agent) first = File.expand_path("/first") stub_file_public_key first, rsa second = File.expand_path("/second") stub_file_private_key second, dsa, passphrase: :should_not_be_asked identities = [] manager.each_identity do |identity| identities << identity break if manager.known_identities[identity][:from] == :agent end assert_equal 1, identities.length assert_equal rsa.to_blob, identities.first.to_blob end def test_sign_with_agent_originated_key_should_request_signature_from_agent manager.stubs(:agent).returns(agent) manager.each_identity { |identity| } # preload the known_identities agent.expects(:sign).with(rsa, "hello, world").returns("abcxyz123") assert_equal "abcxyz123", manager.sign(rsa, "hello, world") end def test_sign_with_file_originated_key_should_load_private_key_and_sign_with_it manager.stubs(:agent).returns(nil) first = File.expand_path("/first") stub_file_private_key first, rsa(512) rsa.expects(:ssh_do_sign).with("hello, world").returns("abcxyz123") manager.each_identity { |identity| } # preload the known_identities assert_equal "\0\0\0\assh-rsa\0\0\0\011abcxyz123", manager.sign(rsa, "hello, world") end def test_sign_with_file_originated_key_should_raise_key_manager_error_if_unloadable manager.known_identities[rsa] = { from: :file, file: "/first" } Net::SSH::KeyFactory.expects(:load_private_key).raises(OpenSSL::PKey::RSAError) assert_raises Net::SSH::Authentication::KeyManagerError do manager.sign(rsa, "hello, world") end end private def stub_file_private_key(name, key, options = {}) manager.add(name) File.stubs(:file?).with(name).returns(true) File.stubs(:readable?).with(name).returns(true) File.stubs(:file?).with(name + ".pub").returns(true) File.stubs(:readable?).with(name + ".pub").returns(false) File.stubs(:file?).with(name + "-cert.pub").returns(false) case options.fetch(:passphrase, :indifferently) when :should_be_asked Net::SSH::KeyFactory.expects(:load_private_key).with(name, nil, false, prompt).raises(OpenSSL::PKey::RSAError).at_least_once Net::SSH::KeyFactory.expects(:load_private_key).with(name, nil, true, prompt).returns(key).at_least_once when :should_not_be_asked Net::SSH::KeyFactory.expects(:load_private_key).with(name, nil, false, prompt).raises(OpenSSL::PKey::RSAError).at_least_once Net::SSH::KeyFactory.expects(:load_private_key).with(name, nil, true, prompt).never else # :indifferently Net::SSH::KeyFactory.expects(:load_private_key).with(name, nil, any_of(true, false), prompt).returns(key).at_least_once end # do not override OpenSSL::PKey::EC#public_key # (it will be called in transport/openssl.rb.) unless defined?(OpenSSL::PKey::EC) && key.public_key.kind_of?(OpenSSL::PKey::EC::Point) key.stubs(:public_key).returns(key) end end def stub_file_public_key(name, key) manager.add(name) File.stubs(:file?).with(name).returns(true) File.stubs(:readable?).with(name).returns(true) File.stubs(:file?).with(name + ".pub").returns(true) File.stubs(:readable?).with(name + ".pub").returns(true) File.stubs(:file?).with(name + "-cert.pub").returns(false) Net::SSH::KeyFactory.expects(:load_public_key).with(name + ".pub").returns(key).at_least_once end def stub_file_cert(name, key) manager.add(name) File.stubs(:file?).with(name).returns(true) File.stubs(:readable?).with(name).returns(true) File.stubs(:file?).with(name + ".pub").returns(true) File.stubs(:readable?).with(name + ".pub").returns(true) File.stubs(:file?).with(name + "-cert.pub").returns(true) File.stubs(:readable?).with(name + "-cert.pub").returns(true) Net::SSH::KeyFactory.expects(:load_public_key).with(name + "-cert.pub").returns(key).at_least_once end def rsa(size=512) @rsa ||= OpenSSL::PKey::RSA.new(size) end def dsa @dsa ||= OpenSSL::PKey::DSA.new(512) end if defined?(OpenSSL::PKey::EC) def ecdsa_sha2_nistp256 @ecdsa_sha2_nistp256 ||= OpenSSL::PKey::EC.new("prime256v1").generate_key end def ecdsa_sha2_nistp384 @ecdsa_sha2_nistp384 ||= OpenSSL::PKey::EC.new("secp384r1").generate_key end def ecdsa_sha2_nistp521 @ecdsa_sha2_nistp521 ||= OpenSSL::PKey::EC.new("secp521r1").generate_key end end def agent @agent ||= stub("agent", identities: [rsa, dsa]) end def agent_with_ecdsa_keys @agent ||= stub("agent", identities: [rsa, dsa, ecdsa_sha2_nistp256, ecdsa_sha2_nistp384, ecdsa_sha2_nistp521]) end def prompt @promp ||= MockPrompt.new end def manager(options = {}) @manager ||= Net::SSH::Authentication::KeyManager.new(nil, {password_prompt: prompt}.merge(options)) end end end net-ssh-4.2.0/test/authentication/test_session.rb000066400000000000000000000104631315376572000221600ustar00rootroot00000000000000require 'common' require 'net/ssh/authentication/session' module Authentication class TestSession < NetSSHTest include Net::SSH::Transport::Constants include Net::SSH::Authentication::Constants def test_constructor_should_set_defaults assert_equal %w(none publickey password keyboard-interactive), session.auth_methods assert_equal session.auth_methods, session.allowed_auth_methods end def test_authenticate_should_continue_if_method_disallowed transport.expect do |t, packet| assert_equal SERVICE_REQUEST, packet.type assert_equal "ssh-userauth", packet.read_string t.return(SERVICE_ACCEPT) end Net::SSH::Authentication::Methods::Publickey.any_instance.expects(:authenticate).with("next service", "username", "password").raises(Net::SSH::Authentication::DisallowedMethod) Net::SSH::Authentication::Methods::Password.any_instance.expects(:authenticate).with("next service", "username", "password").returns(true) Net::SSH::Authentication::Methods::None.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false) assert session.authenticate("next service", "username", "password") end def test_authenticate_should_raise_error_if_service_request_fails transport.expect do |t, packet| assert_equal SERVICE_REQUEST, packet.type assert_equal "ssh-userauth", packet.read_string t.return(255) end assert_raises(Net::SSH::Exception) { session.authenticate("next service", "username", "password") } end def test_authenticate_should_return_false_if_all_auth_methods_fail transport.expect do |t, packet| assert_equal SERVICE_REQUEST, packet.type assert_equal "ssh-userauth", packet.read_string t.return(SERVICE_ACCEPT) end Net::SSH::Authentication::Methods::Publickey.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false) Net::SSH::Authentication::Methods::Password.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false) Net::SSH::Authentication::Methods::KeyboardInteractive.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false) Net::SSH::Authentication::Methods::None.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false) assert_equal false, session.authenticate("next service", "username", "password") end def test_next_message_should_silently_handle_USERAUTH_BANNER_packets transport.return(USERAUTH_BANNER, :string, "Howdy, folks!") transport.return(SERVICE_ACCEPT) assert_equal SERVICE_ACCEPT, session.next_message.type end def test_next_message_should_understand_USERAUTH_FAILURE transport.return(USERAUTH_FAILURE, :string, "a,b,c", :bool, false) packet = session.next_message assert_equal USERAUTH_FAILURE, packet.type assert_equal %w(a b c), session.allowed_auth_methods end (60..79).each do |type| define_method("test_next_message_should_return_packets_of_type_#{type}") do transport.return(type) assert_equal type, session.next_message.type end end def test_next_message_should_understand_USERAUTH_SUCCESS transport.return(USERAUTH_SUCCESS) assert !transport.hints[:authenticated] assert_equal USERAUTH_SUCCESS, session.next_message.type assert transport.hints[:authenticated] end def test_next_message_should_raise_error_on_unrecognized_packet_types transport.return(1) assert_raises(Net::SSH::Exception) { session.next_message } end def test_expect_message_should_raise_exception_if_next_packet_is_not_expected_type transport.return(SERVICE_ACCEPT) assert_raises(Net::SSH::Exception) { session.expect_message(USERAUTH_BANNER) } end def test_expect_message_should_return_packet_if_next_packet_is_expected_type transport.return(SERVICE_ACCEPT) assert_equal SERVICE_ACCEPT, session.expect_message(SERVICE_ACCEPT).type end private def session(options={}) @session ||= Net::SSH::Authentication::Session.new(transport(options), options) end def transport(options={}) @transport ||= MockTransport.new(options) end end end net-ssh-4.2.0/test/common.rb000066400000000000000000000063631315376572000157130ustar00rootroot00000000000000$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" if ENV["CI"] unless Gem.win_platform? require 'simplecov' SimpleCov.start require 'codecov' SimpleCov.formatter = SimpleCov::Formatter::Codecov end end require 'minitest' require 'mocha/setup' require 'net/ssh/buffer' require 'net/ssh/config' require 'net/ssh/loggable' require 'net/ssh/packet' require 'net/ssh/transport/session' require 'ostruct' require 'minitest/autorun' # clear the default files out so that tests don't get confused by existing # SSH config files. $_original_config_default_files = Net::SSH::Config.default_files.dup # rubocop:disable Style/GlobalVars Net::SSH::Config.default_files.clear def with_restored_default_files(&block) act_default_files = Net::SSH::Config.default_files.dup begin Net::SSH::Config.default_files.clear Net::SSH::Config.default_files.concat($_original_config_default_files) # rubocop:disable Style/GlobalVars yield ensure Net::SSH::Config.default_files.clear Net::SSH::Config.default_files.concat(act_default_files) end end def P(*args) Net::SSH::Packet.new(Net::SSH::Buffer.from(*args)) end class NetSSHTest < Minitest::Test def assert_nothing_raised(&block) yield end def assert_not_nil obj, msg = nil refute_nil obj, msg end end class MockPrompt def start(info) @info = info self end def ask(message, echo) _ask(message, @info, echo) end def success; end end class MockTransport < Net::SSH::Transport::Session class BlockVerifier def initialize(block) @block = block end def verify(data) @block.call(data) end end attr_reader :host_key_verifier attr_accessor :host_as_string attr_accessor :server_version attr_reader :client_options attr_reader :server_options attr_reader :hints, :queue attr_accessor :mock_enqueue def initialize(options={}) @options = options self.logger = options[:logger] self.host_as_string = "net.ssh.test,127.0.0.1" self.server_version = OpenStruct.new(version: "SSH-2.0-Ruby/Net::SSH::Test") @expectation = nil @queue = [] @hints = {} @socket = options[:socket] @algorithms = OpenStruct.new(session_id: "abcxyz123") verifier { |data| true } end def send_message(message) buffer = Net::SSH::Buffer.new(message.to_s) if @expectation.nil? raise "got #{message.to_s.inspect} but was not expecting anything" else block, @expectation = @expectation, nil block.call(self, Net::SSH::Packet.new(buffer)) end end def enqueue_message(message) if mock_enqueue send_message(message) else super end end def closed? false end def poll_message @queue.shift end def next_message @queue.shift or raise "expected a message from the server but nothing was ready to send" end def return(type, *args) @queue << P(:byte, type, *args) end def expect(&block) @expectation = block end def expect! expect {} end def verifier(&block) @host_key_verifier = BlockVerifier.new(block) end def configure_client(options) @client_options = options end def configure_server(options) @server_options = options end def hint(name, value=true) @hints[name] = value end end net-ssh-4.2.0/test/configs/000077500000000000000000000000001315376572000155165ustar00rootroot00000000000000net-ssh-4.2.0/test/configs/auth_off000066400000000000000000000002201315376572000172260ustar00rootroot00000000000000HostBasedAuthentication no PasswordAuthentication no PubKeyAuthentication no ChallengeResponseAuthentication no KbdInteractiveAuthentication no net-ssh-4.2.0/test/configs/auth_on000066400000000000000000000001631315376572000170760ustar00rootroot00000000000000HostBasedAuthentication yes PasswordAuthentication yes PubKeyAuthentication yes ChallengeResponseAuthentication yesnet-ssh-4.2.0/test/configs/conf.d/000077500000000000000000000000001315376572000166655ustar00rootroot00000000000000net-ssh-4.2.0/test/configs/conf.d/subdir/000077500000000000000000000000001315376572000201555ustar00rootroot00000000000000net-ssh-4.2.0/test/configs/conf.d/subdir/subset4000066400000000000000000000000341315376572000214660ustar00rootroot00000000000000User foo IdentitiesOnly yes net-ssh-4.2.0/test/configs/conf.d/subset2000066400000000000000000000000301315376572000201700ustar00rootroot00000000000000Include conf.d/subdir/* net-ssh-4.2.0/test/configs/conf.d/subset3000066400000000000000000000000611315376572000201750ustar00rootroot00000000000000HostName example.com IdentityFile ~/.ssh/id3.pem net-ssh-4.2.0/test/configs/empty000066400000000000000000000000001315376572000165650ustar00rootroot00000000000000net-ssh-4.2.0/test/configs/eqsign000066400000000000000000000000561315376572000167300ustar00rootroot00000000000000Host=test.test Port =1234 Compression yes net-ssh-4.2.0/test/configs/exact_match000066400000000000000000000001551315376572000177220ustar00rootroot00000000000000Host other.host Compression no Port 1231 Host test.host Compression yes ForwardAgent yes Port 1234net-ssh-4.2.0/test/configs/host_plus000066400000000000000000000005331315376572000174620ustar00rootroot00000000000000# Jump through hosts with one SSH call # via Host *+* ProxyCommand ssh $(echo %h | sed 's/+[^+]*$//;s/\([^+%%]*\)%%\([^+]*\)$/\2 -l \1/;s/:/ -p /') PATH=.:\$PATH nc -w1 $(echo %h | sed 's/^.*+//;/:/!s/$/ %p/;s/:/ /') Host office-offsite HostName work-gateway+office-workstation Host test.host Compression yesnet-ssh-4.2.0/test/configs/include000066400000000000000000000001251315376572000170620ustar00rootroot00000000000000Include subset1 "subset ws" IdentityFile ~/.ssh/id2.pem Host xyz Include conf.d/* net-ssh-4.2.0/test/configs/multihost000066400000000000000000000001101315376572000174610ustar00rootroot00000000000000Host other.host test.host Compression yes Port 1980 RekeyLimit 2G net-ssh-4.2.0/test/configs/negative_match000066400000000000000000000001171315376572000204160ustar00rootroot00000000000000Host test.* !test.host Port 1234 Compression no Host test.host Port 9876net-ssh-4.2.0/test/configs/nohost000066400000000000000000000002301315376572000167460ustar00rootroot00000000000000 IdentityFile ~/.ssh/id_dsa IdentityFile ~/.ssh/id_rsa Port 1980 Host test.host Port 1985 net-ssh-4.2.0/test/configs/numeric_host000066400000000000000000000000701315376572000201350ustar00rootroot00000000000000Host 1234 Compression yes Port 1980 RekeyLimit 2G net-ssh-4.2.0/test/configs/proxy_jump000066400000000000000000000001671315376572000176610ustar00rootroot00000000000000Host behind-proxy ProxyJump user@proxy Host behind-three-proxies ProxyJump user1@proxy1,user2@proxy2,user3@proxy3 net-ssh-4.2.0/test/configs/proxy_remote_user000066400000000000000000000000671315376572000212360ustar00rootroot00000000000000Host behind-proxy ProxyCommand ssh %r@proxy -W %h:%p net-ssh-4.2.0/test/configs/send_env000066400000000000000000000000431315376572000172370ustar00rootroot00000000000000Host 1234 SendEnv GIT_* LANG LC_* net-ssh-4.2.0/test/configs/subset ws000066400000000000000000000000201315376572000173500ustar00rootroot00000000000000Compression yes net-ssh-4.2.0/test/configs/subset1000066400000000000000000000000451315376572000170260ustar00rootroot00000000000000IdentityFile ~/.ssh/id.pem Port 2345 net-ssh-4.2.0/test/configs/substitutes000066400000000000000000000001311315376572000200320ustar00rootroot00000000000000Host test HostName %h.sufix Host 1234 HostName prefix.%h.sufix Host * HostName %hnet-ssh-4.2.0/test/configs/wild_cards000066400000000000000000000002611315376572000175530ustar00rootroot00000000000000Host test.* Port 1234 Compression no Host tes?.host Port 4321 ForwardAgent yes Host *.hos? IdentityFile ~/.ssh/id_dsa Compression yes Host k*.host RekeyLimit 1Gnet-ssh-4.2.0/test/connection/000077500000000000000000000000001315376572000162255ustar00rootroot00000000000000net-ssh-4.2.0/test/connection/test_channel.rb000066400000000000000000000414551315376572000212320ustar00rootroot00000000000000require 'common' require 'net/ssh/connection/channel' module Connection class TestChannel < NetSSHTest include Net::SSH::Connection::Constants def teardown connection.test! end def test_constructor_should_set_defaults assert_equal 0x8000, channel.local_maximum_packet_size assert_equal 0x20000, channel.local_maximum_window_size assert channel.pending_requests.empty? end def test_channel_properties channel[:hello] = "some value" assert_equal "some value", channel[:hello] end def test_exec_should_be_syntactic_sugar_for_a_channel_request channel.expects(:send_channel_request).with("exec", :string, "ls").yields found_block = false channel.exec("ls") { found_block = true } assert found_block, "expected block to be passed to send_channel_request" end def test_subsystem_should_be_syntactic_sugar_for_a_channel_request channel.expects(:send_channel_request).with("subsystem", :string, "sftp").yields found_block = false channel.subsystem("sftp") { found_block = true } assert found_block, "expected block to be passed to send_channel_request" end def test_request_pty_with_invalid_option_should_raise_error assert_raises(ArgumentError) do channel.request_pty(bogus: "thing") end end def test_request_pty_without_options_should_use_defaults channel.expects(:send_channel_request).with("pty-req", :string, "xterm", :long, 80, :long, 24, :long, 640, :long, 480, :string, "\0").yields found_block = false channel.request_pty { found_block = true } assert found_block, "expected block to be passed to send_channel_request" end def test_request_pty_with_options_should_honor_options channel.expects(:send_channel_request).with("pty-req", :string, "vanilla", :long, 60, :long, 15, :long, 400, :long, 200, :string, "\5\0\0\0\1\0") channel.request_pty term: "vanilla", chars_wide: 60, chars_high: 15, pixels_wide: 400, pixels_high: 200, modes: { 5 => 1 } end def test_send_data_should_append_to_channels_output_buffer channel.send_data("hello") assert_equal "hello", channel.output.to_s channel.send_data("world") assert_equal "helloworld", channel.output.to_s end def test_close_before_channel_has_been_confirmed_should_set_closing assert !channel.closing? channel.close assert channel.closing? end def test_close_should_set_closing_and_send_message channel.do_open_confirmation(0, 100, 100) assert !channel.closing? connection.expect { |t,packet| assert_equal CHANNEL_CLOSE, packet.type } connection.expects(:cleanup_channel).with(channel) channel.close channel.process assert channel.closing? end def test_close_while_closing_should_do_nothing test_close_should_set_closing_and_send_message assert_nothing_raised { channel.close } end def test_process_when_process_callback_is_not_set_should_just_enqueue_data channel.expects(:enqueue_pending_output) channel.process end def test_process_when_process_callback_is_set_should_yield_self_before_enqueuing_data channel.expects(:enqueue_pending_output).never channel.on_process { |ch| ch.expects(:enqueue_pending_output).once } channel.process end def test_enqueue_pending_output_should_have_no_effect_if_channel_has_not_been_confirmed channel.send_data("hello") assert_nothing_raised { channel.enqueue_pending_output } end def test_enqueue_pending_output_should_have_no_effect_if_there_is_no_output channel.do_open_confirmation(0, 100, 100) assert_nothing_raised { channel.enqueue_pending_output } end def test_enqueue_pending_output_should_not_enqueue_more_than_output_length channel.do_open_confirmation(0, 100, 100) channel.send_data("hello world") connection.expect do |t,packet| assert_equal CHANNEL_DATA, packet.type assert_equal 0, packet[:local_id] assert_equal 11, packet[:data].length end channel.enqueue_pending_output end def test_enqueue_pending_output_should_not_enqueue_more_than_max_packet_length_at_once channel.do_open_confirmation(0, 100, 8) channel.send_data("hello world") connection.expect do |t,packet| assert_equal CHANNEL_DATA, packet.type assert_equal 0, packet[:local_id] assert_equal "hello wo", packet[:data] t.expect do |t2,packet2| assert_equal CHANNEL_DATA, packet2.type assert_equal 0, packet2[:local_id] assert_equal "rld", packet2[:data] end end channel.enqueue_pending_output end def test_enqueue_pending_output_should_not_enqueue_more_than_max_window_size channel.do_open_confirmation(0, 8, 100) channel.send_data("hello world") connection.expect do |t,packet| assert_equal CHANNEL_DATA, packet.type assert_equal 0, packet[:local_id] assert_equal "hello wo", packet[:data] end channel.enqueue_pending_output end def test_on_data_with_block_should_set_callback flag = false channel.on_data { flag = !flag } channel.do_data("") assert(flag, "callback should have been invoked") channel.on_data channel.do_data("") assert(flag, "callback should have been removed") end def test_on_extended_data_with_block_should_set_callback flag = false channel.on_extended_data { flag = !flag } channel.do_extended_data(0, "") assert(flag, "callback should have been invoked") channel.on_extended_data channel.do_extended_data(0, "") assert(flag, "callback should have been removed") end def test_on_process_with_block_should_set_callback flag = false channel.on_process { flag = !flag } channel.process assert(flag, "callback should have been invoked") channel.on_process channel.process assert(flag, "callback should have been removed") end def test_on_close_with_block_should_set_callback flag = false channel.on_close { flag = !flag } channel.do_close assert(flag, "callback should have been invoked") channel.on_close channel.do_close assert(flag, "callback should have been removed") end def test_on_eof_with_block_should_set_callback flag = false channel.on_eof { flag = !flag } channel.do_eof assert(flag, "callback should have been invoked") channel.on_eof channel.do_eof assert(flag, "callback should have been removed") end def test_do_request_for_unhandled_request_should_do_nothing_if_not_wants_reply channel.do_open_confirmation(0, 100, 100) assert_nothing_raised { channel.do_request "exit-status", false, nil } end def test_do_request_for_unhandled_request_should_send_CHANNEL_FAILURE_if_wants_reply channel.do_open_confirmation(0, 100, 100) connection.expect { |t,packet| assert_equal CHANNEL_FAILURE, packet.type } channel.do_request "keepalive@openssh.com", true, nil end def test_do_request_for_handled_request_should_invoke_callback_and_do_nothing_if_returns_true_and_not_wants_reply channel.do_open_confirmation(0, 100, 100) flag = false channel.on_request("exit-status") { flag = true; true } assert_nothing_raised { channel.do_request "exit-status", false, nil } assert flag, "callback should have been invoked" end def test_do_request_for_handled_request_should_invoke_callback_and_do_nothing_if_fails_and_not_wants_reply channel.do_open_confirmation(0, 100, 100) flag = false channel.on_request("exit-status") { flag = true; raise Net::SSH::ChannelRequestFailed } assert_nothing_raised { channel.do_request "exit-status", false, nil } assert flag, "callback should have been invoked" end def test_do_request_for_handled_request_should_invoke_callback_and_send_CHANNEL_SUCCESS_if_returns_true_and_wants_reply channel.do_open_confirmation(0, 100, 100) flag = false channel.on_request("exit-status") { flag = true; true } connection.expect { |t,p| assert_equal CHANNEL_SUCCESS, p.type } assert_nothing_raised { channel.do_request "exit-status", true, nil } assert flag, "callback should have been invoked" end def test_do_request_for_handled_request_should_invoke_callback_and_send_CHANNEL_FAILURE_if_returns_false_and_wants_reply channel.do_open_confirmation(0, 100, 100) flag = false channel.on_request("exit-status") { flag = true; raise Net::SSH::ChannelRequestFailed } connection.expect { |t,p| assert_equal CHANNEL_FAILURE, p.type } assert_nothing_raised { channel.do_request "exit-status", true, nil } assert flag, "callback should have been invoked" end def test_send_channel_request_without_callback_should_not_want_reply channel.do_open_confirmation(0, 100, 100) connection.expect do |t,p| assert_equal CHANNEL_REQUEST, p.type assert_equal 0, p[:local_id] assert_equal "exec", p[:request] assert_equal false, p[:want_reply] assert_equal "ls", p[:request_data].read_string end channel.send_channel_request("exec", :string, "ls") assert channel.pending_requests.empty? end def test_send_channel_request_should_wait_for_remote_id channel.expects(:remote_id).times(1).returns(nil) msg = nil begin channel.send_channel_request("exec", :string, "ls") rescue RuntimeError => e msg = e.message end assert_equal "Channel open not yet confirmed, please call send_channel_request(or exec) from block of open_channel", msg assert channel.pending_requests.empty? end def test_send_channel_request_with_callback_should_want_reply channel.do_open_confirmation(0, 100, 100) connection.expect do |t,p| assert_equal CHANNEL_REQUEST, p.type assert_equal 0, p[:local_id] assert_equal "exec", p[:request] assert_equal true, p[:want_reply] assert_equal "ls", p[:request_data].read_string end callback = Proc.new {} channel.send_channel_request("exec", :string, "ls", &callback) assert_equal [callback], channel.pending_requests end def test_do_open_confirmation_should_set_remote_parameters channel.do_open_confirmation(1, 2, 3) assert_equal 1, channel.remote_id assert_equal 2, channel.remote_window_size assert_equal 2, channel.remote_maximum_window_size assert_equal 3, channel.remote_maximum_packet_size end def test_do_open_confirmation_should_call_open_confirmation_callback flag = false channel { flag = true } assert !flag, "callback should not have been invoked yet" channel.do_open_confirmation(1,2,3) assert flag, "callback should have been invoked" end def test_do_open_confirmation_with_session_channel_should_invoke_agent_forwarding_if_agent_forwarding_requested connection forward_agent: true forward = mock("forward") forward.expects(:agent).with(channel) connection.expects(:forward).returns(forward) channel.do_open_confirmation(1,2,3) end def test_do_open_confirmation_with_non_session_channel_should_not_invoke_agent_forwarding_even_if_agent_forwarding_requested connection forward_agent: true channel type: "direct-tcpip" connection.expects(:forward).never channel.do_open_confirmation(1,2,3) end def test_do_window_adjust_should_adjust_remote_window_size_by_the_given_amount channel.do_open_confirmation(0, 1000, 1000) assert_equal 1000, channel.remote_window_size assert_equal 1000, channel.remote_maximum_window_size channel.do_window_adjust(500) assert_equal 1500, channel.remote_window_size assert_equal 1500, channel.remote_maximum_window_size end def test_do_data_should_update_local_window_size assert_equal 0x20000, channel.local_maximum_window_size assert_equal 0x20000, channel.local_window_size channel.do_data("here is some data") assert_equal 0x20000, channel.local_maximum_window_size assert_equal 0x1FFEF, channel.local_window_size end def test_do_extended_data_should_update_local_window_size assert_equal 0x20000, channel.local_maximum_window_size assert_equal 0x20000, channel.local_window_size channel.do_extended_data(1, "here is some data") assert_equal 0x20000, channel.local_maximum_window_size assert_equal 0x1FFEF, channel.local_window_size end def test_do_data_when_local_window_size_drops_below_threshold_should_trigger_WINDOW_ADJUST_message channel.do_open_confirmation(0, 1000, 1000) assert_equal 0x20000, channel.local_maximum_window_size assert_equal 0x20000, channel.local_window_size connection.expect do |t,p| assert_equal CHANNEL_WINDOW_ADJUST, p.type assert_equal 0, p[:local_id] assert_equal 0x20000, p[:extra_bytes] end channel.do_data("." * 0x10001) assert_equal 0x40000, channel.local_maximum_window_size assert_equal 0x2FFFF, channel.local_window_size end def test_do_failure_should_grab_next_pending_request_and_call_it result = nil channel.pending_requests << Proc.new { |*args| result = args } channel.do_failure assert_equal [channel, false], result assert channel.pending_requests.empty? end def test_do_success_should_grab_next_pending_request_and_call_it result = nil channel.pending_requests << Proc.new { |*args| result = args } channel.do_success assert_equal [channel, true], result assert channel.pending_requests.empty? end def test_active_should_be_true_when_channel_appears_in_channel_list connection.channels[channel.local_id] = channel assert channel.active? end def test_active_should_be_false_when_channel_is_not_in_channel_list assert !channel.active? end def test_wait_should_block_while_channel_is_active? channel.expects(:active?).times(3).returns(true,true,false) channel.wait end def test_wait_until_open_confirmed_should_block_while_remote_id_nil channel.expects(:remote_id).times(3).returns(nil,nil,3) channel.send(:wait_until_open_confirmed) end def test_eof_bang_should_send_eof_to_server channel.do_open_confirmation(0, 1000, 1000) connection.expect { |t,p| assert_equal CHANNEL_EOF, p.type } channel.eof! channel.process end def test_eof_bang_should_not_send_eof_if_eof_was_already_declared channel.do_open_confirmation(0, 1000, 1000) connection.expect { |t,p| assert_equal CHANNEL_EOF, p.type } channel.eof! assert_nothing_raised { channel.eof! } channel.process end def test_eof_q_should_return_true_if_eof_declared channel.do_open_confirmation(0, 1000, 1000) connection.expect { |t,p| assert_equal CHANNEL_EOF, p.type } assert !channel.eof? channel.eof! assert channel.eof? channel.process end def test_send_data_should_raise_exception_if_eof_declared channel.do_open_confirmation(0, 1000, 1000) connection.expect { |t,p| assert_equal CHANNEL_EOF, p.type } channel.eof! channel.process assert_raises(EOFError) { channel.send_data("die! die! die!") } end def test_data_should_precede_eof channel.do_open_confirmation(0, 1000, 1000) connection.expect do |_t,p| assert_equal CHANNEL_DATA, p.type connection.expect { |_t,p2| assert_equal CHANNEL_EOF, p2.type } end channel.send_data "foo" channel.eof! channel.process end private class MockConnection attr_reader :logger attr_reader :options attr_reader :channels def initialize(options={}) @expectation = nil @options = options @channels = {} end def expect(&block) @expectation = block end def send_message(msg) raise "#{msg.to_s.inspect} received but no message was expected" unless @expectation packet = Net::SSH::Packet.new(msg.to_s) callback, @expectation = @expectation, nil callback.call(self, packet) end alias loop_forever loop def loop(&block) loop_forever { break unless block.call } end def test! raise "expected a packet but none were sent" if @expectation end end def connection(options={}) @connection ||= MockConnection.new(options) end def channel(options={}, &block) @channel ||= Net::SSH::Connection::Channel.new(connection(options), options[:type] || "session", options[:local_id] || 0, &block) end end end net-ssh-4.2.0/test/connection/test_session.rb000066400000000000000000000554101315376572000213010ustar00rootroot00000000000000require_relative '../common' require 'net/ssh/connection/session' module Connection class TestSession < NetSSHTest include Net::SSH::Connection::Constants def test_constructor_should_set_defaults assert session.channels.empty? assert session.pending_requests.empty? assert_equal({ socket => nil }, session.listeners) end def test_on_open_channel_should_register_block_with_given_channel_type flag = false session.on_open_channel("testing") { flag = true } assert_not_nil session.channel_open_handlers["testing"] session.channel_open_handlers["testing"].call assert flag, "callback should have been invoked" end def test_forward_should_create_and_cache_instance_of_forward_service assert_instance_of Net::SSH::Service::Forward, session.forward assert_equal session.forward.object_id, session.forward.object_id end def test_listen_to_without_callback_should_add_argument_as_listener io = stub("io") session.listen_to(io) assert session.listeners.key?(io) assert_nil session.listeners[io] end def test_listen_to_should_add_argument_to_listeners_list_if_block_is_given io = stub("io", pending_write?: true) flag = false session.listen_to(io) { flag = true } assert !flag, "callback should not be invoked immediately" assert session.listeners.key?(io) session.listeners[io].call assert flag, "callback should have been invoked" end def test_stop_listening_to_should_remove_argument_from_listeners io = stub("io", pending_write?: true) session.listen_to(io) assert session.listeners.key?(io) session.stop_listening_to(io) assert !session.listeners.key?(io) end def test_send_message_should_enqueue_message_at_transport_layer packet = P(:byte, REQUEST_SUCCESS) session.send_message(packet) assert_equal packet.to_s, socket.write_buffer end def test_open_channel_defaults_should_use_session_channel flag = false channel = session.open_channel { flag = true } assert !flag, "callback should not be invoked immediately" channel.do_open_confirmation(1,2,3) assert flag, "callback should have been invoked" assert_equal "session", channel.type assert_equal 0, channel.local_id assert_equal channel, session.channels[channel.local_id] packet = P(:byte, CHANNEL_OPEN, :string, "session", :long, channel.local_id, :long, channel.local_maximum_window_size, :long, channel.local_maximum_packet_size) assert_equal packet.to_s, socket.write_buffer end def test_open_channel_with_type_should_use_type channel = session.open_channel("direct-tcpip") assert_equal "direct-tcpip", channel.type packet = P(:byte, CHANNEL_OPEN, :string, "direct-tcpip", :long, channel.local_id, :long, channel.local_maximum_window_size, :long, channel.local_maximum_packet_size) assert_equal packet.to_s, socket.write_buffer end def test_open_channel_with_extras_should_append_extras_to_packet channel = session.open_channel("direct-tcpip", :string, "other.host", :long, 1234) packet = P(:byte, CHANNEL_OPEN, :string, "direct-tcpip", :long, channel.local_id, :long, channel.local_maximum_window_size, :long, channel.local_maximum_packet_size, :string, "other.host", :long, 1234) assert_equal packet.to_s, socket.write_buffer end def test_send_global_request_without_callback_should_not_expect_reply packet = P(:byte, GLOBAL_REQUEST, :string, "testing", :bool, false) session.send_global_request("testing") assert_equal packet.to_s, socket.write_buffer assert session.pending_requests.empty? end def test_send_global_request_with_callback_should_expect_reply packet = P(:byte, GLOBAL_REQUEST, :string, "testing", :bool, true) proc = Proc.new {} session.send_global_request("testing", &proc) assert_equal packet.to_s, socket.write_buffer assert_equal [proc], session.pending_requests end def test_send_global_request_with_extras_should_append_extras_to_packet packet = P(:byte, GLOBAL_REQUEST, :string, "testing", :bool, false, :string, "other.host", :long, 1234) session.send_global_request("testing", :string, "other.host", :long, 1234) assert_equal packet.to_s, socket.write_buffer end def test_process_should_exit_immediately_if_block_is_false session.channels[0] = stub("channel", closing?: false) session.channels[0].expects(:process).never process_times(0) end def test_can_open_channels_in_process # see #110 chid = session.send(:get_next_channel_id) session.channels[chid] = stub("channel", local_closed?: false) session.channels[chid].expects(:process).with() do session.open_channel true end IO.expects(:select).never process_times(2) end def test_process_should_exit_after_processing_if_block_is_true_then_false session.channels[0] = stub("channel", local_closed?: false) session.channels[0].expects(:process) IO.expects(:select).never process_times(2) end def test_process_should_not_process_channels_that_are_closing session.channels[0] = stub("channel", local_closed?: true) session.channels[0].expects(:process).never IO.expects(:select).never process_times(2) end def test_global_request_packets_should_be_silently_handled_if_no_handler_exists_for_them transport.return(GLOBAL_REQUEST, :string, "testing", :bool, false) process_times(2) assert transport.queue.empty? assert !socket.pending_write? end def test_global_request_packets_should_be_auto_replied_to_even_if_no_handler_exists transport.return(GLOBAL_REQUEST, :string, "testing", :bool, true) process_times(2) assert_equal P(:byte, REQUEST_FAILURE).to_s, socket.write_buffer end def test_global_request_handler_should_not_trigger_auto_reply_if_no_reply_is_wanted flag = false session.on_global_request("testing") { flag = true } assert !flag, "callback should not be invoked yet" transport.return(GLOBAL_REQUEST, :string, "testing", :bool, false) process_times(2) assert transport.queue.empty? assert !socket.pending_write? assert flag, "callback should have been invoked" end def test_global_request_handler_returning_true_should_trigger_success_auto_reply flag = false session.on_global_request("testing") { flag = true } transport.return(GLOBAL_REQUEST, :string, "testing", :bool, true) process_times(2) assert_equal P(:byte, REQUEST_SUCCESS).to_s, socket.write_buffer assert flag end def test_global_request_handler_returning_false_should_trigger_failure_auto_reply flag = false session.on_global_request("testing") { flag = true; false } transport.return(GLOBAL_REQUEST, :string, "testing", :bool, true) process_times(2) assert_equal P(:byte, REQUEST_FAILURE).to_s, socket.write_buffer assert flag end def test_global_request_handler_returning_sent_should_not_trigger_auto_reply flag = false session.on_global_request("testing") { flag = true; :sent } transport.return(GLOBAL_REQUEST, :string, "testing", :bool, true) process_times(2) assert !socket.pending_write? assert flag end def test_global_request_handler_returning_other_value_should_raise_error transport.expects(:closed?).at_least_once.returns(false) session.on_global_request("testing") { "bug" } transport.return(GLOBAL_REQUEST, :string, "testing", :bool, true) assert_raises(RuntimeError) { process_times(2) } end def test_request_success_packets_should_invoke_next_pending_request_with_true result = nil session.pending_requests << Proc.new { |*args| result = args } transport.return(REQUEST_SUCCESS) process_times(2) assert_equal [true, P(:byte, REQUEST_SUCCESS)], result assert session.pending_requests.empty? end def test_request_failure_packets_should_invoke_next_pending_request_with_false result = nil session.pending_requests << Proc.new { |*args| result = args } transport.return(REQUEST_FAILURE) process_times(2) assert_equal [false, P(:byte, REQUEST_FAILURE)], result assert session.pending_requests.empty? end def test_channel_open_packet_without_corresponding_channel_open_handler_should_result_in_channel_open_failure transport.return(CHANNEL_OPEN, :string, "auth-agent", :long, 14, :long, 0x20000, :long, 0x10000) process_times(2) assert_equal P(:byte, CHANNEL_OPEN_FAILURE, :long, 14, :long, 3, :string, "unknown channel type auth-agent", :string, "").to_s, socket.write_buffer end def test_channel_open_packet_with_corresponding_handler_should_result_in_channel_open_failure_when_handler_returns_an_error transport.return(CHANNEL_OPEN, :string, "auth-agent", :long, 14, :long, 0x20000, :long, 0x10000) session.on_open_channel "auth-agent" do |s, ch, p| raise Net::SSH::ChannelOpenFailed.new(1234, "we iz in ur channelz!") end process_times(2) assert_equal P(:byte, CHANNEL_OPEN_FAILURE, :long, 14, :long, 1234, :string, "we iz in ur channelz!", :string, "").to_s, socket.write_buffer end def test_channel_open_packet_with_corresponding_handler_should_result_in_channel_open_confirmation_when_handler_succeeds transport.return(CHANNEL_OPEN, :string, "auth-agent", :long, 14, :long, 0x20001, :long, 0x10001) result = nil session.on_open_channel("auth-agent") { |*args| result = args } process_times(2) assert_equal P(:byte, CHANNEL_OPEN_CONFIRMATION, :long, 14, :long, 0, :long, 0x20000, :long, 0x8000).to_s, socket.write_buffer assert_not_nil(ch = session.channels[0]) assert_equal [session, ch, P(:byte, CHANNEL_OPEN, :string, "auth-agent", :long, 14, :long, 0x20001, :long, 0x10001)], result assert_equal 0, ch.local_id assert_equal 14, ch.remote_id assert_equal 0x20001, ch.remote_maximum_window_size assert_equal 0x10001, ch.remote_maximum_packet_size assert_equal 0x20000, ch.local_maximum_window_size assert_equal 0x8000, ch.local_maximum_packet_size assert_equal "auth-agent", ch.type end def test_channel_open_failure_should_remove_channel_and_tell_channel_that_open_failed session.channels[1] = stub("channel") session.channels[1].expects(:do_open_failed).with(1234, "some reason") transport.return(CHANNEL_OPEN_FAILURE, :long, 1, :long, 1234, :string, "some reason", :string, "lang tag") process_times(2) assert session.channels.empty? end def test_channel_open_confirmation_packet_should_be_routed_to_corresponding_channel channel_at(14).expects(:do_open_confirmation).with(1234, 0x20001, 0x10001) transport.return(CHANNEL_OPEN_CONFIRMATION, :long, 14, :long, 1234, :long, 0x20001, :long, 0x10001) process_times(2) end def test_channel_window_adjust_packet_should_be_routed_to_corresponding_channel channel_at(14).expects(:do_window_adjust).with(5000) transport.return(CHANNEL_WINDOW_ADJUST, :long, 14, :long, 5000) process_times(2) end def test_channel_request_for_nonexistant_channel_should_be_ignored transport.return(CHANNEL_REQUEST, :long, 14, :string, "testing", :bool, false) assert_nothing_raised { process_times(2) } end def test_channel_request_packet_should_be_routed_to_corresponding_channel channel_at(14).expects(:do_request).with("testing", false, Net::SSH::Buffer.new) transport.return(CHANNEL_REQUEST, :long, 14, :string, "testing", :bool, false) process_times(2) end def test_channel_data_packet_should_be_routed_to_corresponding_channel channel_at(14).expects(:do_data).with("bring it on down") transport.return(CHANNEL_DATA, :long, 14, :string, "bring it on down") process_times(2) end def test_channel_extended_data_packet_should_be_routed_to_corresponding_channel channel_at(14).expects(:do_extended_data).with(1, "bring it on down") transport.return(CHANNEL_EXTENDED_DATA, :long, 14, :long, 1, :string, "bring it on down") process_times(2) end def test_channel_eof_packet_should_be_routed_to_corresponding_channel channel_at(14).expects(:do_eof).with() transport.return(CHANNEL_EOF, :long, 14) process_times(2) end def test_channel_success_packet_should_be_routed_to_corresponding_channel channel_at(14).expects(:do_success).with() transport.return(CHANNEL_SUCCESS, :long, 14) process_times(2) end def test_channel_failure_packet_should_be_routed_to_corresponding_channel channel_at(14).expects(:do_failure).with() transport.return(CHANNEL_FAILURE, :long, 14) process_times(2) end def test_channel_close_packet_should_be_routed_to_corresponding_channel_and_channel_should_be_closed_and_removed session.channels[14] = stub("channel") do # this simulates the case where we closed the channel first, sent # CHANNEL_CLOSE to server and are waiting for server's response. expects(:local_closed?).returns(true) expects(:do_close) expects(:close).with() expects(:remote_closed!).with() expects(:remote_closed?).with().returns(true) expects(:local_id).returns(14) end transport.return(CHANNEL_CLOSE, :long, 14) process_times(2) assert session.channels.empty? end def test_multiple_pending_dispatches_should_be_dispatched_together channel_at(14).expects(:do_eof).with() session.channels[14].expects(:do_success).with() transport.return(CHANNEL_SUCCESS, :long, 14) transport.return(CHANNEL_EOF, :long, 14) process_times(2) end def test_writers_without_pending_writes_should_not_be_considered_for_select IO.expects(:select).with([socket],[],nil,nil).returns([[],[],[]]) session.process end def test_writers_with_pending_writes_should_be_considered_for_select socket.enqueue("laksdjflasdkf") IO.expects(:select).with([socket],[socket],nil,nil).returns([[],[],[]]) session.process end def test_ready_readers_should_be_filled socket.expects(:recv).returns("this is some data") IO.expects(:select).with([socket],[],nil,nil).returns([[socket],[],[]]) session.process assert_equal [socket], session.listeners.keys end def test_ready_readers_that_cant_be_filled_should_be_removed socket.expects(:recv).returns("") socket.expects(:close) IO.expects(:select).with([socket],[],nil,nil).returns([[socket],[],[]]) session.process assert session.listeners.empty? end def test_ready_readers_that_are_registered_with_a_block_should_call_block_instead_of_fill io = stub("io", pending_write?: false) flag = false session.stop_listening_to(socket) # so that we only have to test the presence of a single IO object session.listen_to(io) { flag = true } IO.expects(:select).with([io],[],nil,nil).returns([[io],[],[]]) session.process assert flag, "callback should have been invoked" end def test_ready_writers_should_call_send_pending socket.enqueue("laksdjflasdkf") socket.expects(:send).with("laksdjflasdkf", 0).returns(13) IO.expects(:select).with([socket],[socket],nil,nil).returns([[],[socket],[]]) session.process end def test_process_should_call_rekey_as_needed transport.expects(:rekey_as_needed) IO.expects(:select).with([socket],[],nil,nil).returns([[],[],[]]) session.process end def test_process_should_call_enqueue_message_if_io_select_timed_out timeout = Net::SSH::Connection::Session::DEFAULT_IO_SELECT_TIMEOUT options = { keepalive: true } expected_packet = P(:byte, Net::SSH::Packet::GLOBAL_REQUEST, :string, "keepalive@openssh.com", :bool, true) IO.stubs(:select).with([socket],[],nil,timeout).returns(nil) transport.expects(:enqueue_message).with{ |msg| msg.content == expected_packet.content } session(options).process end def test_process_should_raise_if_keepalives_not_answered timeout = Net::SSH::Connection::Session::DEFAULT_IO_SELECT_TIMEOUT options = { keepalive: true, keepalive_interval: 300, keepalive_maxcount: 3 } expected_packet = P(:byte, Net::SSH::Packet::GLOBAL_REQUEST, :string, "keepalive@openssh.com", :bool, true) [1,2,3].each do |i| Time.stubs(:now).returns(Time.at(i*300)) IO.stubs(:select).with([socket],[],nil,timeout).returns(nil) transport.expects(:enqueue_message).with{ |msg| msg.content == expected_packet.content } session(options).process end Time.stubs(:now).returns(Time.at(4*300)) IO.stubs(:select).with([socket],[],nil,timeout).returns(nil) transport.expects(:enqueue_message).with{ |msg| msg.content == expected_packet.content } assert_raises(Net::SSH::Timeout) { session(options).process } end def test_process_should_not_call_enqueue_message_unless_io_select_timed_out timeout = Net::SSH::Connection::Session::DEFAULT_IO_SELECT_TIMEOUT options = { keepalive: true } IO.stubs(:select).with([socket],[],nil,timeout).returns([[socket],[],[]]) socket.stubs(:recv).returns("x") transport.expects(:enqueue_message).never session(options).process end def test_process_should_not_call_enqueue_message_unless_keepalive_interval_not_go_on timeout = 10 options = { keepalive: true, keepalive_interval: timeout } Time.stubs(:now).returns(Time.at(0), Time.at(9), Time.at(timeout)) IO.stubs(:select).with([socket],[],nil,timeout).returns(nil) transport.expects(:enqueue_message).times(2) 3.times { session(options).process } end def test_process_should_call_io_select_with_nil_as_last_arg_if_keepalive_disabled IO.expects(:select).with([socket],[],nil,nil).returns([[],[],[]]) session.process end def test_process_should_call_io_select_with_interval_as_last_arg_if_keepalive_interval_passed timeout = 10 options = { keepalive: true, keepalive_interval: timeout } IO.expects(:select).with([socket],[],nil,timeout).returns([[],[],[]]) session(options).process end def test_process_should_call_io_select_with_wait_if_provided_and_minimum timeout = 10 wait = 5 options = { keepalive: true, keepalive_interval: timeout } IO.expects(:select).with([socket],[],nil,wait).returns([[],[],[]]) session(options).process(wait) end def test_loop_should_call_process_until_process_returns_false session.expects(:process).with(0) session.expects(:process).with(nil).times(4).returns(true,true,true,false).yields n = 0 session.loop { n += 1 } assert_equal 4, n end def test_exec_should_open_channel_and_configure_default_callbacks prep_exec("ls", :stdout, "data packet", :stderr, "extended data packet") call = :first session.exec "ls" do |channel, type, data| if call == :first assert_equal :stdout, type assert_equal "data packet", data call = :second elsif call == :second assert_equal :stderr, type assert_equal "extended data packet", data call = :third else flunk "should never get here, call == #{call.inspect}" end end session.loop assert_equal :third, call end def test_exec_without_block_should_use_print_to_display_result prep_exec("ls", :stdout, "data packet", :stderr, "extended data packet") $stdout.expects(:print).with("data packet") $stderr.expects(:print).with("extended data packet") session.exec "ls" session.loop end def test_exec_bang_should_block_until_command_finishes prep_exec("ls", :stdout, "some data") called = false session.exec! "ls" do |channel, type, data| called = true assert_equal :stdout, type assert_equal "some data", data end assert called end def test_exec_bang_without_block_should_return_data_as_string prep_exec("ls", :stdout, "some data") assert_equal "some data", session.exec!("ls") end def test_exec_bang_without_block_should_return_empty_string_for_empty_command_output prep_exec('ls', :stdout, '') assert_equal "", session.exec!('ls') end def test_max_select_wait_time_should_return_keepalive_interval_when_keepalive_enabled options = { keepalive: true, keepalive_interval: 5 } assert_equal 5, session(options).max_select_wait_time end def test_max_select_wait_time_should_return_nil_when_keepalive_disabled options = {} assert_nil session(options).max_select_wait_time end private def prep_exec(command, *data) IO.expects(:select).with([socket],[],nil,0).returns([[],[],[]]) transport.mock_enqueue = true transport.expect do |t, p| assert_equal CHANNEL_OPEN, p.type t.return(CHANNEL_OPEN_CONFIRMATION, :long, p[:remote_id], :long, 0, :long, 0x20000, :long, 0x10000) t.expect do |t2, p2| assert_equal CHANNEL_REQUEST, p2.type assert_equal "exec", p2[:request] assert_equal true, p2[:want_reply] assert_equal "ls", p2.read_string t2.return(CHANNEL_SUCCESS, :long, p[:remote_id]) data.each_slice(2) do |type, datum| next if datum.empty? if type == :stdout t2.return(CHANNEL_DATA, :long, p[:remote_id], :string, datum) else t2.return(CHANNEL_EXTENDED_DATA, :long, p[:remote_id], :long, 1, :string, datum) end end t2.return(CHANNEL_CLOSE, :long, p[:remote_id]) t2.expect { |t3,p3| assert_equal CHANNEL_CLOSE, p3.type } end end end module MockSocket # so that we can easily test the contents that were enqueued, without # worrying about all the packet stream overhead def enqueue_packet(message) enqueue(message.to_s) end end def socket @socket ||= begin socket ||= Object.new socket.extend(Net::SSH::Transport::PacketStream) socket.extend(MockSocket) socket end end def channel_at(local_id) session.channels[local_id] = stub("channel", process: true, local_closed?: false) end def transport(options={}) @transport ||= MockTransport.new(options.merge(socket: socket)) end def session(options={}) @session ||= Net::SSH::Connection::Session.new(transport, options) end def process_times(n) i = 0 session.process { (i += 1) < n } end end end net-ssh-4.2.0/test/integration/000077500000000000000000000000001315376572000164115ustar00rootroot00000000000000net-ssh-4.2.0/test/integration/README.md000066400000000000000000000013131315376572000176660ustar00rootroot00000000000000# Integration tests with vagrant Requirements: * Vagrant (https://www.vagrantup.com/) * Ansible (http://docs.ansible.com/intro_installation.html) Setup: ansible-galaxy install rvm_io.ruby vagrant up ; vagrant ssh rvmsudo_secure_path=1 rvmsudo rvm all do gem install bundler rvm all do sh -c 'rm Gemfile.lock; bundle' rvm all do rake test # Debugging on travis Logging the ssh logs might be useful: ```yml script: - #NET_SSH_RUN_INTEGRATION_TESTS=1 bundle exec rake test - sudo tail -n 3 /var/log/auth.log - bundle exec ruby -Ilib:test ./test/integration/test_forward.rb -n test_client_close_should_be_handled_remote - sudo tail -n 60 /var/log/auth.log - bundle exec rubocop ``` net-ssh-4.2.0/test/integration/Vagrantfile000066400000000000000000000004731315376572000206020ustar00rootroot00000000000000VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "ubuntu/trusty64" config.vm.provision "ansible" do |ansible| ansible.playbook = "./playbook.yml" ansible.sudo = true ansible.verbose ='vvvv' end config.vm.synced_folder "../..", "/net-ssh" endnet-ssh-4.2.0/test/integration/common.rb000066400000000000000000000044441315376572000202340ustar00rootroot00000000000000$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../lib" require_relative '../common' require 'mocha/setup' require 'pty' require 'expect' module IntegrationTestHelpers VERBOSE = false def sh command puts "$ #{command}" if VERBOSE res = system(command) status = $? raise "Command: #{command} failed:#{status.exitstatus}" unless res end def tmpdir(&block) Dir.mktmpdir do |dir| yield(dir) end end def set_authorized_key(user,pubkey) authorized_key = "/home/#{user}/.ssh/authorized_keys" sh "sudo cp #{pubkey} #{authorized_key}" sh "sudo chown #{user} #{authorized_key}" sh "sudo chmod 0744 #{authorized_key}" end def sign_user_key(user,pubkey) cert = "/etc/ssh/users_ca" sh "sudo ssh-keygen -s #{cert} -I user_#{user} -n #{user} -V +52w #{pubkey}" end def with_agent(&block) puts "/usr/bin/ssh-agent -c" if VERBOSE agent_out = `/usr/bin/ssh-agent -c` agent_out.split("\n").each do |line| if line =~ /setenv (\S+) (\S+);/ ENV[$1] = $2 puts "ENV[#{$1}]=#{$2}" if VERBOSE end end begin yield ensure sh "/usr/bin/ssh-agent -k > /dev/null" end end def ssh_add(key,password) command = "ssh-add #{key}" status = nil PTY.spawn(command) do |reader, writer, pid| begin reader.expect(/Enter passphrase for .*:/) { |data| puts data } writer.puts(password) until reader.eof? do line = reader.readline puts line if VERBOSE end rescue Errno::EIO => _e end pid, status = Process.wait2 pid end raise "Command: #{command} failed:#{status.exitstatus}" unless status status.exitstatus end def with_sshd_config(sshd_config, &block) raise "Failed to copy config" unless system("sudo cp -f /etc/ssh/sshd_config /etc/ssh/sshd_config.original") begin Tempfile.open('sshd_config') do |f| f.write(sshd_config) f.close system("sudo cp -f #{f.path} /etc/ssh/sshd_config") end system("sudo chmod 0644 /etc/ssh/sshd_config") raise "Failed to restart sshd" unless system("sudo service ssh restart") yield ensure system("sudo cp -f /etc/ssh/sshd_config.original /etc/ssh/sshd_config") system("sudo service ssh restart") end end end net-ssh-4.2.0/test/integration/playbook.yml000066400000000000000000000106621315376572000207610ustar00rootroot00000000000000--- - hosts: all become: true vars: no_rvm: no myuser: vagrant mygroup: vagrant homedir: /home/vagrant ruby_version: '2.0.0-p598' ruby_versions: - '2.0.0-p598' - '2.3.0' # - 'rbx-3.19' # - 'jruby-9.0.5.0' rvm_install_path: '/usr/local/rvm' foopwd: "$6$mhOzf/yapZwS$3RwDl4GfWZ5VcfcsHrK9xNNTxyzLOJBsmMttDNaegIbXxMahV86.v/5HsNtit16MEl0EFf5CSW8Dz2yXV.8GB0" foo2pwd: "$6$JiB7y7.M0yI$Abt.ZGIc4DwkRWeI6nKxzzPUZcux7hLRXSdpoKoZvswJz1SZyg5GRQWn9pGID0dgC6e4wFglfW6ev/qZoTqGk/" pre_tasks: - name: get currently installed ruby version command: "{{rvm_install_path}}/rubies/ruby-{{ruby_version}}/bin/ruby -e 'puts \"#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}\"'" register: current_ruby_version ignore_errors: True roles: - { role: rvm_io.ruby, tags: ruby, become: yes, rvm1_user: 'root', rvm1_rubies: "{{ ruby_versions }}", rvm1_install_path: "{{rvm_install_path}}", rvm1_install_flags: '--auto-dotfiles', # Make sure RVM sets itself up so the user has access to it rvm1_gpg_key_server: pool.sks-keyservers.net, when: "'{{current_ruby_version.stdout|default()}}' != '{{ruby_version}}' and not no_rvm" } tasks: - group: name="{{mygroup}}" state=present - user: name=net_ssh_1 password="{{foopwd}}" group="{{mygroup}}" state=present - user: name=net_ssh_2 password="{{foo2pwd}}" group="{{mygroup}}" state=present - file: dest=/home/net_ssh_1/.ssh/ state=directory mode=0740 owner=net_ssh_1 - file: dest=/home/net_ssh_2/.ssh/ state=directory mode=0740 owner=net_ssh_2 - lineinfile: dest=/etc/sudoers.d/net_ssh_1 mode=0440 state=present create=yes line='net_ssh_1 ALL=(ALL) NOPASSWD:ALL' regexp=net_ssh_1 - lineinfile: dest=/etc/sudoers.d/net_ssh_1 mode=0440 state=present create=yes line='net_ssh_2 ALL=(ALL) NOPASSWD:ALL' regexp=net_ssh_2 - unarchive: src: https://ftp.spline.de/pub/OpenBSD/OpenSSH/portable/openssh-7.4p1.tar.gz dest: /tmp remote_src: True - name: building and installing openssh 7.4 (used in forward test) command: sh -c "./configure --prefix=/opt/net-ssh-openssh && make && sudo make install" args: chdir: /tmp/openssh-7.4p1/ - name: drop installed openssh etc/ in favor of symlink file: state: absent path: /opt/net-ssh-openssh/etc - name: creating symlink between system etc/ssh/ and our etc/ file: src: /etc/ssh dest: /opt/net-ssh-openssh/etc state: link - command: ssh-keygen -A args: creates: /etc/ssh/ssh_host_ed25519_key notify: restart sshd - name: sshd debug lineinfile: dest='/etc/ssh/sshd_config' line='LogLevel DEBUG' regexp=LogLevel notify: restart sshd - name: sshd allow interactive lineinfile: dest='/etc/ssh/sshd_config' line='ChallengeResponseAuthentication yes' regexp='^ChallengeResponseAuthentication.+' notify: restart sshd - command: ssh-keygen -f /etc/ssh/users_ca -N '' args: creates: /etc/ssh/users_ca.pub notify: restart sshd - name: sshd cert auth lineinfile: dest='/etc/ssh/sshd_config' line='TrustedUserCAKeys /etc/ssh/users_ca.pub' notify: restart sshd - name: sshd allow forward lineinfile: dest='/etc/ssh/sshd_config' line='AllowTcpForwarding all' regexp=LogLevel notify: restart sshd - name: sshd allow forward lineinfile: dest='/etc/ssh/sshd_config' line='GatewayPorts yes' regexp=LogLevel notify: restart sshd - name: put NET_SSH_RUN_INTEGRATION_TESTS=YES environment lineinfile: dest='/etc/environment' line='NET_SSH_RUN_INTEGRATION_TESTS=YES' - name: change dir in bashrc lineinfile: dest="{{homedir}}/.bashrc" owner="{{myuser}}" mode=0644 regexp='^cd ' line='cd /net-ssh' - name: add host aliases lineinfile: dest='/etc/hosts' owner='root' group='root' mode=0644 regexp='^127\.0\.0\.1\s+gateway.netssh' line='127.0.0.1 gateway.netssh' - apt: name="{{item}}" state=present with_items: - pv - libgmp3-dev - git - copy: content='echo "cd /net-ssh ; rake integration-test"' dest=/etc/update-motd.d/99-net-ssh-tests mode=0755 - name: add user to rvm group so they can change gem wrappers user: name: "{{myuser}}" groups: rvm append: yes when: "not no_rvm" handlers: - name: restart sshd service: name=ssh state=restarted net-ssh-4.2.0/test/integration/test_agent.rb000066400000000000000000000112311315376572000210710ustar00rootroot00000000000000require_relative 'common' require 'net/ssh' # rubocop:disable LineLength CERT = "\x00\x00\x00\x1Cssh-rsa-cert-v01@openssh.com\x00\x00\x00 Ir\xB9\xC9\x94l\x0ER\xA1h\xF5\xFDx\xB2J\xC6g\eHS\xDD\x162\x86\xF1\x90%\\$rf\xAF\x00\x00\x00\x03\x01\x00\x01\x00\x00\x01\x01\x00\xB3R\xBC\xF8\xEA\xA30\x90\x87\x85\xF6m\x80\xFB\x7F\x96%\xC0h\x85$\x05\x05J\x9BE\xD9\xDE\x81\xC0\xC9\xC2\xC0\x0F'\xD1TR\xCBb\xCD\xD0o\xA0\x15Q\x8B\xF26t\xC9!8\x85\xD2\f'\xC6\x14u\x1De\x90qyXl\a\x06\xA7\xD0\xB8 \xE1\xB3IP\xDE\xB5\xBE\x19\x0E\x97-M\xFDJT\x81\xE2\x8E>\xCD\x18\x9CJz\x1C\xB5}LsO\xF3\xAC\xAA\r\xAB\xF9\xD4\x83\x8DQ\x82\xE7F\xA4\x9F\x1C\x9A\xC5\xC3Y\x84k\x86\ef\xD7\x84\xE3\v\rlG\x15ya\xB0=\xDF\x11\x8D\x0FtZ/p\xBB\xB7g\xF5\xEBF8\xF5\x05}}\xDB\xFA\xA34dw\xE5\x80\xBC!=\x0E\x96\x18\bF\x10\a{\xFF\x9D2\xCA\xAAnu\x82\x82\xBA-F\x8C\x12\xBB\x04+nh\xE9N\xAF\fe\x16\x00Q\x9C\x1C\xCB\x94\x02\x8CQ\xFB,H[\x96\xF1Z4\nY]@\xE0\bs\x9Bh\x0E\xAA~\x105\x99\\\x8C\xA7q\x1A=\xA9\x9D\xBAbx\xF5`[\x8Aw\x80\b\xE0vy\x00\x00\x00\x00\x00\x00\x00c\x00\x00\x00\x01\x00\x00\x00\x06foobar\x00\x00\x00\b\x00\x00\x00\x04root\x00\x00\x00\x00Xk\\\x1C\x00\x00\x00\x00ZK>g\x00\x00\x00#\x00\x00\x00\rforce-command\x00\x00\x00\x0E\x00\x00\x00\n/bin/false\x00\x00\x00c\x00\x00\x00\x15permit-X11-forwarding\x00\x00\x00\x00\x00\x00\x00\x16permit-port-forwarding\x00\x00\x00\x00\x00\x00\x00\npermit-pty\x00\x00\x00\x00\x00\x00\x00\x0Epermit-user-rc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x17\x00\x00\x00\assh-rsa\x00\x00\x00\x03\x01\x00\x01\x00\x00\x01\x01\x00\x9DRU\x0E\x83\x8Eb}\x81vOn\xCA\xBA\x01%\xFE\x87\x80b\xB5\x98R%\xA9(\xC1\xAE\xEFq|\x82L\xADQ?\x1D\xC6o\xB8\xD8pI\e\xFC\xF8\xFE^\xAD*\xA4u;\x99S\fc\x11\xBE\xFD\x047B\x1C\xF2h\xBA\xB1\xB0\n\x12F\e\x16\xF7Z\x8D\xD3\xF2f\xC0\x1C\xD8\xBE\xCC\x82\x85Qka$\xB6\xBD\x1C)\x85B\xAAf\xC8\xF3V*\xC3\x1C\xAA\xDC\xC3I\xDDe\xEFu\x02M\x12\x1A\xE2};he\x9D\xB5\xA47\xE4\x12\x8F\xE0\xF1\xA5\x91/\xFB\xEA\t\x0F \x1E\xB4B@+6\x1F\xBD\xA7\xA9u\x80\x19\xAA\xAC\xFFK\\F\x8C\xD9u\f?\xB9#[M\xDF\xB0\xFC\xE8\xF6J\x98\xA4\x99\x8F\xF9]\x88\x1D|A%\xAB\e\x0EN\xAA\xD3 \xCF\xA7}c\xDE\xF5\xBA4\xC8\xD2\x81(\x13\xB3\x94@fC\xDC\xDF\xFD\xA1\e$?\x13\xA9m\xEB*\xCA'\xB3\x19\x19\xF0\xD2\xB3P\x00\x96ou\xE90\xC4-\x1F\xCF\x1Aw\x034\xC6\xDF\xA7\x8C\xCA^Ix\x15\xFA\x9A+\x00\x00\x01\x0F\x00\x00\x00\assh-rsa\x00\x00\x01\x00I\b%\x01\xB2\xCC\x87\xD7\e\xC5\x88\x93|\x9D\xEC}\xA4\x86\xD7\xBB\xB6\xD3\x93\xFD\\\xC73\xC2*\aV\xA2\x81\x05J\x91\x9AEKV\n\xB4\xEB\xF3\xBC\xBAr\x16\xE5\x9A\xB9\xDC(0\xB4\x1C\x9F\"\x9E\xF9\x91\xD0\x1F\x9Cp\r*\xE3\x8A\xD3\xB9W$[OI\xD2\x8F8\x9B\xA4\x9E\xFFuGg\x00\xA5\xCD\r\xDB\x95\xEE)_\xC3\xBCi\xA2\xCC\r\x86\xFD\xE9\xE6\x188\x92\xFD\xCC\n\x98t\x8C\x16\xF4O\xF6\xD5\xD4\xB7\\\xB95\x19\xA3\xBBW\xF3\xF7r<\xE6\x8C\xFC\xE5\x9F\xBF\xE0\xBF\x06\xE7v\xF2\x8Ek\xA4\x02\xB6fMd\xA5e\x87\xE1\x93\xF5\x81\xCF\xDF\x88\xDC\a\xA2\e\xD5\xCA\x14\xB2>\xF4\x8F|\xE5-w\xF5\x85\xD0\xF1F((\xD1\xEEE&\x1D\xA2+\xEC\x93\xE7\xC7\xAE\xE38\xE4\xAE\xF7 \xED\xC6\r\xD6\x1A\xE1#<\xA2)j\xB3TA\\\xFF;\xC5\xA6Tu\xAAap\xDE\xF4\xF7 p\xCA\xD2\xBA\xDC\xCDv\x17\xC2\xBCQ\xDF\xAB7^\xA1G\x18\xB9\xB2F\x81\x9Fq\x92\xD3".force_encoding('BINARY') ED25519 = <<-EOF -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW QyNTUxOQAAACDuVIPDUXcVkXOyNAaFsotbySHLNG/Gw6gc3j2k2zcRVAAAAKD6bG5++mxu fgAAAAtzc2gtZWQyNTUxOQAAACDuVIPDUXcVkXOyNAaFsotbySHLNG/Gw6gc3j2k2zcRVA AAAEAydU4FtZ9+5o5Y/m1aPNHFda37Fm0Us5FlUKx50tWw+e5Ug8NRdxWRc7I0BoWyi1vJ Ics0b8bDqBzePaTbNxFUAAAAGmJhcnRsZUBCYXJ0bGVzLU1hY0Jvb2stUHJvAQID -----END OPENSSH PRIVATE KEY----- EOF class TestAgent < NetSSHTest include IntegrationTestHelpers def setup @keys = [ OpenSSL::PKey::RSA.new(1024), OpenSSL::PKey::DSA.new(512), OpenSSL::PKey::EC.new("prime256v1").generate_key ] @keys << Net::SSH::Authentication::ED25519::PrivKey.new(ED25519, nil) if Net::SSH::Authentication::ED25519Loader::LOADED @keys += @keys.map do |key| cert = Net::SSH::Buffer.new(CERT).read_key cert.key = key cert.sign!(key) end end def test_ssh_agent_add_and_remove with_agent do agent = Net::SSH::Authentication::Agent.connect agent.remove_all_identities @keys.each do |key| agent.add_identity(key, "key") assert_equal [key.to_blob], agent.identities.map(&:to_blob) agent.remove_identity(key) assert agent.identities.empty? end end end def test_ssh_agent_add_and_remove_all_identities with_agent do agent = Net::SSH::Authentication::Agent.connect agent.remove_all_identities @keys.each do |key| agent.add_identity(key, "key") end assert_equal @keys.length, agent.identities.length agent.remove_all_identities assert agent.identities.empty? end end end net-ssh-4.2.0/test/integration/test_cert_user_auth.rb000066400000000000000000000017131315376572000230130ustar00rootroot00000000000000require_relative 'common' require 'net/ssh' # environment: see playbook for full details. # 1. cert files: /etc/ssh/users_ca and /etc/ssh/users_ca.pub and # 2. /etc/ssh/sshd_config: TrustedUserCAKeys /etc/ssh/users_ca.pub unless ENV['NET_SSH_NO_RBNACL'] class TestCertUserAuth < NetSSHTest include IntegrationTestHelpers def test_ed25519_with_cert Dir.mktmpdir do |dir| sh "rm -rf #{dir}/id_rsa_ed25519 #{dir}/id_rsa_ed25519.pub" sh "ssh-keygen -q -f #{dir}/id_rsa_ed25519 -t ed25519 -N ''" sign_user_key('net_ssh_1',"#{dir}/id_rsa_ed25519.pub") # sshopts = '-vvvv -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' # sh "ssh -i #{dir}/id_rsa_ed25519 #{sshopts} net_ssh_1@localhost echo 'hello'" ret = Net::SSH.start("localhost", "net_ssh_1", keys: "#{dir}/id_rsa_ed25519") do |ssh| ssh.exec! 'echo "hello from:$USER"' end assert_equal "hello from:net_ssh_1\n", ret end end end end net-ssh-4.2.0/test/integration/test_channel.rb000066400000000000000000000055551315376572000214170ustar00rootroot00000000000000require_relative 'common' require 'net/ssh/buffer' require 'net/ssh' require 'net/ssh/proxy/command' require 'timeout' require 'tempfile' class TestChannel < NetSSHTest include IntegrationTestHelpers def localhost 'localhost' end def user 'net_ssh_1' end def ssh_start_params(options = {}) [localhost, user, {keys: @key_id_rsa}.merge(options)] end def setup_ssh_env(&block) tmpdir do |dir| @key_id_rsa = "#{dir}/id_rsa" sh "rm -rf #{@key_id_rsa} #{@key_id_rsa}.pub" sh "ssh-keygen -q -f #{@key_id_rsa} -t rsa -N ''" set_authorized_key(user,"#{@key_id_rsa}.pub") yield end end def ssh_exec(ssh, command, channel_success_handler, &block) ssh.open_channel do |channel| channel.exec(command) do |_ch, success| raise "could not execute command: #{command.inspect}" unless success channel_success_handler.call channel.on_data do |ch2, data| yield(ch2, :stdout, data) end channel.on_extended_data do |ch2, _type, data| yield(ch2, :stderr, data) end end end end def test_transport_close_before_channel_close_should_raise setup_ssh_env do proxy = Net::SSH::Proxy::Command.new("/bin/nc localhost 22") res = nil Net::SSH.start(*ssh_start_params(proxy: proxy)) do |ssh| chanell_success_handler = lambda do sleep(0.1) system("killall /bin/nc") end channel = ssh_exec(ssh, "echo Begin ; sleep 100 ; echo End", chanell_success_handler) do |ch, _type, data| ch[:result] ||= "" ch[:result] << data end assert_raises(IOError) { channel.wait } res = channel[:result] assert_equal(res, "Begin\n") end assert_equal(res, "Begin\n") end end def test_transport_close_after_channel_close_should_not_raise setup_ssh_env do proxy = Net::SSH::Proxy::Command.new("/bin/nc localhost 22") res = nil Net::SSH.start(*ssh_start_params(proxy: proxy)) do |ssh| chanell_success_handler = lambda do sleep(0.1) system("killall /bin/nc") end channel = ssh_exec(ssh, "echo Hello!", chanell_success_handler) do |ch, _type, data| ch[:result] ||= "" ch[:result] << data end channel.wait res = channel[:result] assert_equal(res, "Hello!\n") end assert_equal(res, "Hello!\n") end end def test_transport_close_should_remote_close_channels setup_ssh_env do Net::SSH.start(*ssh_start_params) do |ssh| channel = ssh.open_channel do || ssh.transport.socket.close end remote_closed = nil begin channel.wait rescue StandardError remote_closed = channel.remote_closed? end assert_equal remote_closed, true end end end end net-ssh-4.2.0/test/integration/test_ed25519_pkeys.rb000066400000000000000000000060021315376572000222040ustar00rootroot00000000000000require_relative 'common' require 'fileutils' require 'tmpdir' require 'net/ssh' unless ENV['NET_SSH_NO_RBNACL'] # see Vagrantfile,playbook for env. # we're running as net_ssh_1 user password foo # and usually connecting to net_ssh_2 user password foo2pwd class TestED25519PKeys < NetSSHTest include IntegrationTestHelpers def test_in_file_no_password Dir.mktmpdir do |dir| sh "rm -rf #{dir}/id_rsa_ed25519 #{dir}/id_rsa_ed25519.pub" sh "ssh-keygen -q -f #{dir}/id_rsa_ed25519 -t ed25519 -N ''" set_authorized_key('net_ssh_1',"#{dir}/id_rsa_ed25519.pub") #sshopts = '-vvvv -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' #sh "ssh -i #{dir}/id_rsa_ed25519 #{sshopts} net_ssh_1@localhost echo 'hello'" ret = Net::SSH.start("localhost", "net_ssh_1", {keys: "#{dir}/id_rsa_ed25519"}) do |ssh| ssh.exec! 'echo "hello from:$USER"' end assert_equal "hello from:net_ssh_1\n", ret end end def test_ssh_agent Dir.mktmpdir do |dir| with_agent do sh "rm -rf #{dir}/id_rsa_ed25519 #{dir}/id_rsa_ed25519.pub" sh "ssh-keygen -q -f #{dir}/id_rsa_ed25519 -t ed25519 -N 'pwd'" set_authorized_key('net_ssh_1',"#{dir}/id_rsa_ed25519.pub") ssh_add("#{dir}/id_rsa_ed25519","pwd") # TODO: fix bug in net ssh which reads public key even if private key is there sh "mv #{dir}/id_rsa_ed25519.pub #{dir}/id_rsa_ed25519.pub.hidden" ret = Net::SSH.start("localhost", "net_ssh_1") do |ssh| ssh.exec! 'echo "hello from:$USER"' end assert_equal "hello from:net_ssh_1\n", ret end end end def test_in_file_with_password Dir.mktmpdir do |dir| sh "rm -rf #{dir}/id_rsa_ed25519 #{dir}/id_rsa_ed25519.pub" sh "ssh-keygen -q -f #{dir}/id_rsa_ed25519 -t ed25519 -N 'pwd'" set_authorized_key('net_ssh_1',"#{dir}/id_rsa_ed25519.pub") # TODO: fix bug in net ssh which reads public key even if private key is there sh "mv #{dir}/id_rsa_ed25519.pub #{dir}/id_rsa_ed25519.pub.hidden" #sshopts = '-vvvv -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' #sh "ssh -i #{dir}/id_rsa_ed25519 #{sshopts} net_ssh_1@localhost echo 'hello'" ret = Net::SSH.start("localhost", "net_ssh_1", {keys: "#{dir}/id_rsa_ed25519", passphrase:'pwd'}) do |ssh| ssh.exec! 'echo "hello from:$USER"' end assert_equal "hello from:net_ssh_1\n", ret end end def test_with_only_ed25519_host_key config_lines = File.read('/etc/ssh/sshd_config').split("\n") config_lines = config_lines.map do |line| if (line =~ /^HostKey /) && !(line =~ /ed25519/) "##{line}" else line end end Tempfile.open('empty_kh') do |f| f.close with_sshd_config(config_lines.join("\n")) do ret = Net::SSH.start("localhost", "net_ssh_1", password: 'foopwd', user_known_hosts_file: [f.path]) do |ssh| ssh.exec! "echo 'foo'" end assert_equal "foo\n", ret end end end end end net-ssh-4.2.0/test/integration/test_encoding.rb000066400000000000000000000013141315376572000215620ustar00rootroot00000000000000require_relative 'common' require 'fileutils' require 'tmpdir' require 'net/ssh' class TestEncoding < NetSSHTest def test_unicode_character ret = Net::SSH.start("localhost", "net_ssh_1", password: 'foopwd') do |ssh| ssh.exec! "echo \"hello from:$USER\" \u2603" end assert_equal ret, "hello from:net_ssh_1 \u2603\n" end def test_long_command_with_unicode_in_it string = "eeeeeeeeeeeeeeeeeeeeeeeeeewwowowowìeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" command = "echo \"#{string}\"" ret = Net::SSH.start("localhost", "net_ssh_1", password: 'foopwd') do |ssh| ssh.exec! command end assert_equal ret, "#{string}\n" end endnet-ssh-4.2.0/test/integration/test_exec.rb000066400000000000000000000010261315376572000207200ustar00rootroot00000000000000require_relative 'common' require 'net/ssh' class TestExec < NetSSHTest include IntegrationTestHelpers def test_error_exitstatus ret = Net::SSH.start("localhost", "net_ssh_1", password: 'foopwd') do |ssh| ssh.exec! "exit 42" end assert_equal "", ret assert_equal 42, ret.exitstatus end def test_ok_exitstatus ret = Net::SSH.start("localhost", "net_ssh_1", password: 'foopwd') do |ssh| ssh.exec! "echo 'foo'" end assert_equal "foo\n", ret assert_equal 0, ret.exitstatus end end net-ssh-4.2.0/test/integration/test_forward.rb000066400000000000000000000513071315376572000214470ustar00rootroot00000000000000# $ ruby -Ilib -Itest -rrubygems test/manual/test_forward.rb # Tests for the following patch: # # http://github.com/net-ssh/net-ssh/tree/portfwfix # # It fixes 3 issues, regarding closing forwarded ports: # # 1.) if client closes a forwarded connection, but the server is reading, net-ssh terminates with IOError socket closed. # 2.) if client force closes (RST) a forwarded connection, but server is reading, net-ssh terminates with # 3.) if server closes the sending side, the on_eof is not handled. # # More info: # # http://net-ssh.lighthouseapp.com/projects/36253/tickets/7 require_relative 'common' require 'net/ssh/buffer' require 'net/ssh' require 'net/ssh/proxy/command' require 'timeout' require 'tempfile' class ForwardTestBase < NetSSHTest include IntegrationTestHelpers # @yield [pid, port] def start_sshd_7_or_later(port = '2200') pid = spawn('sudo', '/opt/net-ssh-openssh/sbin/sshd', '-D', '-p', port) yield pid, port ensure # Our pid is sudo, -9 (KILL) on sudo will not clean up its children # properly, so we just have to hope that -15 (TERM) will manage to bring # down sshd. system('sudo', 'kill', '-15', pid.to_s) Process.wait(pid) end def localhost 'localhost' end def user 'net_ssh_1' end def ssh_start_params(options = {}) [localhost ,user , {keys: @key_id_rsa}.merge(options)] end def setup_ssh_env(&block) tmpdir do |dir| @key_id_rsa = "#{dir}/id_rsa" sh "rm -rf #{@key_id_rsa} #{@key_id_rsa}.pub" sh "ssh-keygen -q -f #{@key_id_rsa} -t rsa -N ''" set_authorized_key(user,"#{@key_id_rsa}.pub") yield end end def start_server_sending_lot_of_data(exceptions) server = TCPServer.open(0) Thread.start do loop do Thread.start(server.accept) do |client| begin 10000.times do |i| client.puts "item#{i}" end client.close rescue exceptions << $! raise end end end end return server end end class TestForward < ForwardTestBase def start_server_closing_soon(exceptions=nil) server = TCPServer.open(0) Thread.start do loop do Thread.start(server.accept) do |client| begin client.recv(1024) client.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, [1, 0].pack("ii")) client.close rescue exceptions << $! raise end end end end return server end def test_in_file_no_password setup_ssh_env do ret = Net::SSH.start(*ssh_start_params) do |ssh| ssh.exec! 'echo "hello from:$USER"' end assert_equal "hello from:net_ssh_1\n", ret end end def test_local_ephemeral_port_should_work_correctly setup_ssh_env do session = Net::SSH.start(*ssh_start_params) assert_nothing_raised do assigned_port = session.forward.local(0, localhost, 22) assert_not_nil assigned_port assert_operator assigned_port, :>, 0 end end end def test_remote_ephemeral_port_should_work_correctly setup_ssh_env do session = Net::SSH.start(*ssh_start_params) assert_nothing_raised do session.forward.remote(22, localhost, 0, localhost) session.loop { !(session.forward.active_remotes.length > 0) } assigned_port = session.forward.active_remotes.first[0] assert_not_nil assigned_port assert_operator assigned_port, :>, 0 end end end def test_remote_callback_should_fire setup_ssh_env do session = Net::SSH.start(*ssh_start_params) assert_nothing_raised do got_port = nil session.forward.remote(22, localhost, 0, localhost) do |port| got_port = port end session.loop { !(session.forward.active_remotes.length > 0) } assert_operator session.forward.active_remote_destinations.length, :==, 1 assert_operator session.forward.active_remote_destinations.keys.first, :==, [ 22, localhost ] assert_operator session.forward.active_remote_destinations.values.first, :==, [ got_port, localhost ] assert_operator session.forward.active_remotes.first, :==, [ got_port, localhost ] assigned_port = session.forward.active_remotes.first[0] assert_operator got_port, :==, assigned_port assert_not_nil assigned_port assert_operator assigned_port, :>, 0 end end end def test_remote_callback_should_fire_on_error_and_still_throw_exception setup_ssh_env do session = Net::SSH.start(*ssh_start_params) assert_nothing_raised do session.forward.remote(22, localhost, 22, localhost) do |port| assert_operator port, :==, :error end end assert_raises(Net::SSH::Exception) do session.loop { true } end end end def test_remote_callback_should_fire_on_error_but_not_throw_exception_if_asked_not_to setup_ssh_env do session = Net::SSH.start(*ssh_start_params) assert_nothing_raised do got_port = nil session.forward.remote(22, localhost, 22, localhost) do |port| assert_operator port, :==, :error got_port = port :no_exception end session.loop { !got_port } assert_operator got_port, :==, :error assert_operator session.forward.active_remotes.length, :==, 0 end end end def test_loop_should_not_abort_when_local_side_of_forward_is_closed setup_ssh_env do session = Net::SSH.start(*ssh_start_params) server_exc = Queue.new server = start_server_sending_lot_of_data(server_exc) remote_port = server.addr[1] local_port = 0 # request ephemeral port session.forward.local(local_port, localhost, remote_port) client_done = Queue.new Thread.start do begin client = TCPSocket.new(localhost, local_port) client.recv(1024) client.close sleep(0.2) ensure client_done << true end end session.loop(0.1) { client_done.empty? } assert_equal "Broken pipe", "#{server_exc.pop}" unless server_exc.empty? end end def test_loop_should_not_abort_when_local_side_of_forward_is_reset setup_ssh_env do session = Net::SSH.start(*ssh_start_params) server_exc = Queue.new server = start_server_sending_lot_of_data(server_exc) remote_port = server.addr[1] local_port = 0 # request ephemeral port session.forward.local(local_port, localhost, remote_port) client_done = Queue.new Thread.start do begin client = TCPSocket.new(localhost, local_port) client.recv(1024) client.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, [1, 0].pack("ii")) client.close sleep(0.1) ensure client_done << true end end session.loop(0.1) { client_done.empty? } assert_equal "Broken pipe", "#{server_exc.pop}" unless server_exc.empty? end end def test_loop_should_not_abort_when_server_side_of_forward_is_closed setup_ssh_env do session = Net::SSH.start(*ssh_start_params) server = start_server_closing_soon remote_port = server.addr[1] local_port = 0 # request ephemeral port session.forward.local(local_port, localhost, remote_port) client_done = Queue.new Thread.start do begin client = TCPSocket.new(localhost, local_port) 1.times do |i| client.puts "item#{i}" end client.close sleep(0.1) ensure client_done << true end end session.loop(0.1) { client_done.empty? } end end def start_server(server = nil, &block) server ||= TCPServer.open(0) Thread.start do loop do Thread.start(server.accept) do |client| yield(client) end end end return server end def test_client_close_should_be_handled_remote setup_ssh_env do message = "This is a small message!"*1000 session = Net::SSH.start(*ssh_start_params) server_done = Queue.new server = start_server do |client| begin data = client.read message.size server_done << data client.close rescue server_done << $! end end client_done = Queue.new got_remote_port = Queue.new local_port = server.addr[1] session.forward.remote(0, localhost, local_port, localhost) do |actual_remote_port| got_remote_port << actual_remote_port end session.loop(0.1) { got_remote_port.empty? } remote_port = got_remote_port.pop Thread.start do begin client = TCPSocket.new(localhost, remote_port) client.write(message) client.close client_done << true rescue client_done << $! end end Timeout.timeout(5) do session.loop(0.1) { server_done.empty? } assert_equal message, server_done.pop end end end class TCPProxy def initialize() @sockets = [] end attr_reader :sockets def open(host, port, connection_options = nil) socket = TCPSocket.new(host,port) @sockets << socket socket end def close_all sockets.each do |socket| socket.close end end end def test_transport_close_should_closes_channels_with_tcps setup_ssh_env do server = start_server do |client| client.puts "Hello" sleep(100) client.puts "Hallo" end proxy = TCPProxy.new() session = Net::SSH.start(*ssh_start_params(proxy: proxy)) remote_port = server.addr[1] local_port = session.forward.local(0, localhost, remote_port) # read on forwarded port client_done = Queue.new Thread.start do begin client = TCPSocket.new(localhost, local_port) client.read(6) proxy.close_all client.read(7) client.close client_done << true rescue client_done << $! end end server_error = nil Timeout.timeout(5) do begin session.loop(0.1) { true } rescue IOError, Errno::EBADF server_error = $! #puts "Error: #{$!} #{$!.backtrace.join("\n")}" end end begin Timeout.timeout(5) do assert_equal true, client_done.pop end rescue puts "Server error: #{server_error.class} #{server_error} bt:#{server_error.backtrace.join("\n")}" raise end end end def todo_test_transport_close_should_closes_channels_with_proxy setup_ssh_env do server = start_server do |client| client.puts "Hello" sleep(100) client.puts "Hallo" end proxy = Net::SSH::Proxy::Command.new("/bin/nc localhost 22") session = Net::SSH.start(*ssh_start_params(proxy: proxy)) remote_port = server.addr[1] local_port = session.forward.local(0, localhost, remote_port) # read on forwarded port client_done = Queue.new Thread.start do begin client = TCPSocket.new(localhost, local_port) client.read(6) system("killall /bin/nc") client.read(7) client.close client_done << true rescue client_done << $! end end Timeout.timeout(5) do begin session.loop(0.1) { true } rescue EOFError begin session.close rescue end #puts "Error: #{$!} #{$!.backtrace.join("\n")}" end assert_equal true, client_done.pop end end end def test_client_close_should_be_handled setup_ssh_env do message = "This is a small message!"*1000 session = Net::SSH.start(*ssh_start_params) server_done = Queue.new server = start_server do |client| begin data = client.read message.size server_done << data client.close rescue server_done << $! end end client_done = Queue.new remote_port = server.addr[1] local_port = session.forward.local(0, localhost, remote_port) Thread.start do begin client = TCPSocket.new(localhost, local_port) client.write(message) client.close client_done << true rescue client_done << $! end end Timeout.timeout(5) do session.loop(0.1) { server_done.empty? } assert_equal message, server_done.pop end end end def test_server_eof_should_be_handled_remote setup_ssh_env do message = "This is a small message!" session = Net::SSH.start(*ssh_start_params) server = start_server do |client| client.write message client.close end client_done = Queue.new got_remote_port = Queue.new local_port = server.addr[1] session.forward.remote(0, localhost, local_port, localhost) do |actual_remote_port| got_remote_port << actual_remote_port end session.loop(0.1) { got_remote_port.empty? } remote_port = got_remote_port.pop Thread.start do begin client = TCPSocket.new(localhost, remote_port) data = client.read(4096) client.close client_done << data rescue client_done << $! end end Timeout.timeout(5) do session.loop(0.1) { client_done.empty? } assert_equal message, client_done.pop end end end def test_server_eof_should_be_handled setup_ssh_env do message = "This is a small message!" session = Net::SSH.start(*ssh_start_params) server = start_server do |client| client.write message client.close end client_done = Queue.new remote_port = server.addr[1] local_port = session.forward.local(0, localhost, remote_port) Thread.start do begin client = TCPSocket.new(localhost, local_port) data = client.read(4096) client.close client_done << data rescue client_done << $! end end Timeout.timeout(5) do session.loop(0.1) { client_done.empty? } assert_equal message, client_done.pop end end end def _run_reading_client(client_done, local_port) Thread.start do begin client = TCPSocket.new(localhost, local_port) data = client.read(4096) client.close client_done << data rescue client_done << $! end end end def test_cannot_open_connection_should_allow_further_connections_on_different_forward setup_ssh_env do session = Net::SSH.start(*ssh_start_params) server = start_server do |client| _data = client.write "hello" client.close end # Forward to a non existing port non_existing_port = 1234 local_port = session.forward.local(0, localhost, non_existing_port) # should return connection refused client_done = Queue.new _run_reading_client(client_done, local_port) Timeout.timeout(5) do session.loop(0.1) { client_done.empty? } end assert_nil client_done.pop assert client_done.empty? # Forward to existing port remote_port = server.addr[1] local_port = session.forward.local(0, localhost, remote_port) _run_reading_client(client_done, local_port) Timeout.timeout(5) do session.loop(0.1) { client_done.empty? } end assert_equal "hello", client_done.pop assert client_done.empty? end end def test_cannot_open_connection_should_allow_further_connections_on_same setup_ssh_env do session = Net::SSH.start(*ssh_start_params) server = TCPServer.open(0) # Forward to a non existing port remote_port = server.addr[1] server.close local_port = session.forward.local(0, localhost, remote_port) # should return connection refused client_done = Queue.new _run_reading_client(client_done, local_port) Timeout.timeout(5) do session.loop(0.1) { client_done.empty? } end assert_nil client_done.pop assert client_done.empty? # start server server = TCPServer.open(remote_port) server = start_server(server) do |client| _data = client.write "hello" client.close end _run_reading_client(client_done, local_port) Timeout.timeout(5) do session.loop(0.1) { client_done.empty? } end assert_equal "hello", client_done.pop assert client_done.empty? end end def test_cancel_local setup_ssh_env do session = Net::SSH.start(*ssh_start_params) server = start_server(server) do |client| _data = client.write "hello" client.close end remote_port = server.addr[1] local_port = session.forward.local(0, localhost, remote_port) # run client client_done = Queue.new _run_reading_client(client_done, local_port) Timeout.timeout(5) do session.loop(0.1) { client_done.empty? } end assert_equal "hello", client_done.pop # cancel session.forward.cancel_local(local_port) session.loop(0.1) assert_equal({}, session.channels) end end end class TestForwardOnUnixSockets < ForwardTestBase if defined?(UNIXServer) && defined?(UNIXSocket) def create_local_socket(&blk) tempfile = Tempfile.new("net_ssh_forward_test") path = tempfile.path tempfile.delete yield UNIXServer.open(path) File.delete(path) end def test_forward_local_unix_socket_to_remote_port setup_ssh_env do session = Net::SSH.start(*ssh_start_params) server_exc = Queue.new server = start_server_sending_lot_of_data(server_exc) remote_port = server.addr[1] client_data = nil create_local_socket do |local_socket| session.forward.local(local_socket, localhost, remote_port) client_done = Queue.new Thread.start do begin client = UNIXSocket.new(local_socket.path) client_data = client.recv(1024) client.close sleep(0.2) ensure client_done << true end end begin session.loop(0.1) { client_done.empty? } rescue Errno::EPIPE end end assert_not_nil(client_data, "client should have received data") assert(client_data.match(/item\d/), 'client should have received the string item') end end end def test_forward_local_unix_socket_to_remote_socket setup_ssh_env do start_sshd_7_or_later do |_pid, port| session = Timeout.timeout(4) do begin # We have our own sshd, give it a chance to come up before # listening. Net::SSH.start(*ssh_start_params(port: port)) rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH sleep 0.25 retry end end create_local_socket do |remote_socket| # Make sure sshd can 'rw'. FileUtils.chmod(0o666, remote_socket.path) local_socket_path_file = Tempfile.new("net_ssh_forward_test_local") local_socket_path = local_socket_path_file.path session.forward.local_socket(local_socket_path, remote_socket.path) assert_equal([local_socket_path], session.forward.active_local_sockets) client_done = Queue.new Thread.start do begin # Ruby >= 2.4 Thread.current.report_on_exception = true rescue NoMethodError # Ruby <= 2.3 Thread.current.abort_on_exception = true end begin client = UNIXSocket.new(local_socket_path) client.puts "hi" assert_equal("hi", client.gets.strip) client.puts "bye" client_done << true ensure client.close end end Thread.start do begin # Ruby >= 2.4 Thread.current.report_on_exception = true rescue NoMethodError # Ruby <= 2.3 Thread.current.abort_on_exception = true end begin sock = remote_socket.accept assert_equal("hi", sock.gets.strip) sock.puts "hi" assert_equal("bye", sock.gets.strip) ensure sock.close end end session.loop(0.1) { client_done.empty? } session.forward.cancel_local_socket(local_socket_path) assert_equal([], session.forward.active_local_sockets) end end end end end net-ssh-4.2.0/test/integration/test_http_proxy.rb000066400000000000000000000037041315376572000222210ustar00rootroot00000000000000require_relative 'common' require 'net/ssh/buffer' require 'net/ssh' require 'timeout' require 'tempfile' require 'net/ssh/proxy/command' require 'net/ssh/proxy/http' require 'net/ssh/proxy/https' require 'webrick' require 'webrick/httpproxy' require 'webrick/https' class TestHTTPProxy < NetSSHTest include IntegrationTestHelpers def localhost 'localhost' end def user 'net_ssh_1' end def ssh_start_params(options) [localhost, user, {keys: @key_id_rsa}.merge(options)] end def setup_ssh_env(&block) tmpdir do |dir| @key_id_rsa = "#{dir}/id_rsa" sh "rm -rf #{@key_id_rsa} #{@key_id_rsa}.pub" sh "ssh-keygen -q -f #{@key_id_rsa} -t rsa -N ''" set_authorized_key(user,"#{@key_id_rsa}.pub") yield end end def with_http_proxy_server(&block) proxy = WEBrick::HTTPProxyServer.new Port: 0 Thread.start { proxy.start } begin yield(proxy) ensure proxy.shutdown end end def test_smoke setup_ssh_env do with_http_proxy_server do |http_proxy| msg = 'echo123' ret = Net::SSH.start(*ssh_start_params(proxy: Net::SSH::Proxy::HTTP.new('localhost', http_proxy.config[:Port]))) do |ssh| ssh.exec! "echo \"$USER:#{msg}\"" end assert_equal "net_ssh_1:#{msg}\n", ret end end end def with_http_tls_proxy_server(&block) cert_name = [%w(CN localhost)] proxy = WEBrick::HTTPProxyServer.new Port: 0, SSLEnable: true, SSLCertName: cert_name Thread.start { proxy.start } begin yield(proxy) ensure proxy.shutdown end end def test_smoke_tls setup_ssh_env do with_http_tls_proxy_server do |http_proxy| msg = 'echo123' ret = Net::SSH.start(*ssh_start_params(proxy: Net::SSH::Proxy::HTTPS.new('localhost', http_proxy.config[:Port]))) do |ssh| ssh.exec! "echo \"$USER:#{msg}\"" end assert_equal "net_ssh_1:#{msg}\n", ret end end end endnet-ssh-4.2.0/test/integration/test_id_rsa_keys.rb000066400000000000000000000063551315376572000223020ustar00rootroot00000000000000require_relative 'common' require 'fileutils' require 'tmpdir' require 'net/ssh' # see Vagrantfile,playbook for env. # we're running as net_ssh_1 user password foo # and usually connecting to net_ssh_2 user password foo2pwd class TestIDRSAPKeys < NetSSHTest include IntegrationTestHelpers def test_in_file_no_password tmpdir do |dir| sh "rm -rf #{dir}/id_rsa #{dir}/id_rsa.pub" sh "ssh-keygen -q -f #{dir}/id_rsa -t rsa -N ''" set_authorized_key('net_ssh_1',"#{dir}/id_rsa.pub") #sshopts = '-vvvv -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' #sh "ssh -i #{dir}/id_rsa #{sshopts} net_ssh_1@localhost echo 'hello'" ret = Net::SSH.start("localhost", "net_ssh_1", {keys: "#{dir}/id_rsa"}) do |ssh| ssh.exec! 'echo "hello from:$USER"' end assert_equal "hello from:net_ssh_1\n", ret end end def test_ssh_agent tmpdir do |dir| with_agent do sh "rm -rf #{dir}/id_rsa #{dir}/id_rsa.pub" sh "ssh-keygen -q -f #{dir}/id_rsa -t rsa -N 'pwd123'" set_authorized_key('net_ssh_1',"#{dir}/id_rsa.pub") ssh_add("#{dir}/id_rsa","pwd123") ret = Net::SSH.start("localhost", "net_ssh_1") do |ssh| ssh.exec! 'echo "hello from:$USER"' end assert_equal "hello from:net_ssh_1\n", ret end end end def test_ssh_agent_ignores_if_already_in_agent tmpdir do |dir| with_agent do sh "rm -rf #{dir}/id_rsa #{dir}/id_rsa.pub" sh "ssh-keygen -q -f #{dir}/id_rsa -t rsa -N 'pwd123'" set_authorized_key('net_ssh_1',"#{dir}/id_rsa.pub") ssh_add("#{dir}/id_rsa","pwd123") ret = Net::SSH.start("localhost", "net_ssh_1", keys: ["#{dir}/id_rsa"]) do |ssh| ssh.exec! 'echo "hello from:$USER"' end assert_equal "hello from:net_ssh_1\n", ret end end end def test_in_file_with_password tmpdir do |dir| sh "rm -rf #{dir}/id_rsa #{dir}/id_rsa.pub" sh "ssh-keygen -q -f #{dir}/id_rsa -t rsa -N 'pwd12'" set_authorized_key('net_ssh_1',"#{dir}/id_rsa.pub") #sshopts = '-vvvv -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' #sh "ssh -i #{dir}/id_rsa #{sshopts} net_ssh_1@localhost echo 'hello'" ret = Net::SSH.start("localhost", "net_ssh_1", {keys: "#{dir}/id_rsa", passphrase: 'pwd12'}) do |ssh| ssh.exec! 'echo "hello from:$USER"' end assert_equal "hello from:net_ssh_1\n", ret end end def test_asks_for_passwords_when_read_from_memory tmpdir do |dir| sh "rm -rf #{dir}/id_rsa #{dir}/id_rsa.pub" sh "ssh-keygen -q -f #{dir}/id_rsa -t rsa -N 'pwd12'" set_authorized_key('net_ssh_1',"#{dir}/id_rsa.pub") private_key = File.read("#{dir}/id_rsa") options = {keys: [], key_data: [private_key]} #key_manager = Net::SSH::Authentication::KeyManager.new(nil, options) prompt = MockPrompt.new sha = Digest::SHA256.digest(private_key) prompt.expects(:_ask).with('Enter passphrase for :', {type: 'private_key', filename: '', sha: sha}, false).returns('pwd12') Net::SSH.start("localhost", "net_ssh_1", options.merge(password_prompt: prompt)) do |ssh| ssh.exec! 'whoami' end end end end net-ssh-4.2.0/test/integration/test_password.rb000066400000000000000000000042351315376572000216430ustar00rootroot00000000000000require_relative 'common' require 'net/ssh' class TestPassword < NetSSHTest include IntegrationTestHelpers def test_with_password_parameter ret = Net::SSH.start("localhost", "net_ssh_1", password: 'foopwd') do |ssh| ssh.exec! 'echo "hello from:$USER"' end assert_equal ret, "hello from:net_ssh_1\n" end def test_keyboard_interactive_with_good_password ps = Object.new pt = Object.new pt.expects(:start).with(type: 'keyboard-interactive', name: '', instruction: '').returns(ps) ps.expects(:ask).with('Password: ', false).returns("foopwd") ps.expects(:success) ret = Net::SSH.start("localhost", "net_ssh_1", auth_methods: ['keyboard-interactive'], password_prompt: pt) do |ssh| ssh.exec! 'echo "hello from:$USER"' end assert_equal ret, "hello from:net_ssh_1\n" end def test_keyboard_interactive_with_one_failed_attempt ps = Object.new pt = Object.new pt.expects(:start).with(type: 'keyboard-interactive', name: '', instruction: '').returns(ps) ps.expects(:ask).twice.with('Password: ', false).returns("badpwd").then.with('Password: ', false).returns("foopwd") ps.expects(:success) ret = Net::SSH.start("localhost", "net_ssh_1", auth_methods: ['keyboard-interactive'], password_prompt: pt) do |ssh| ssh.exec! 'echo "hello from:$USER"' end assert_equal ret, "hello from:net_ssh_1\n" end def test_password_with_good_password ps = Object.new pt = Object.new pt.expects(:start).with(type: 'password', user: 'net_ssh_1', host: 'localhost').returns(ps) ps.expects(:ask).with("net_ssh_1@localhost's password:", false).returns("foopwd") ps.expects(:success) ret = Net::SSH.start("localhost", "net_ssh_1", auth_methods: ['password'], password_prompt: pt) do |ssh| ssh.exec! 'echo "hello from:$USER"' end assert_equal ret, "hello from:net_ssh_1\n" end def test_bad_password_should_throw_auth_invalid assert_raises Net::SSH::AuthenticationFailed do Net::SSH.start("localhost", "net_ssh_1", password: "wrong_password", auth_methods: ['password'], non_interactive: true) do |ssh| ssh.exec! 'echo "hello from:$USER"' end end end end net-ssh-4.2.0/test/integration/test_proxy.rb000066400000000000000000000110211315376572000211510ustar00rootroot00000000000000require_relative 'common' require 'net/ssh/buffer' require 'net/ssh' require 'timeout' require 'tempfile' require 'net/ssh/proxy/command' require 'net/ssh/proxy/jump' class TestProxy < NetSSHTest include IntegrationTestHelpers def localhost 'localhost' end def user 'net_ssh_1' end def ssh_start_params(options) [localhost ,user , {keys: @key_id_rsa}.merge(options)] end def setup_ssh_env(&block) tmpdir do |dir| @key_id_rsa = "#{dir}/id_rsa" sh "rm -rf #{@key_id_rsa} #{@key_id_rsa}.pub" sh "ssh-keygen -q -f #{@key_id_rsa} -t rsa -N ''" set_authorized_key(user,"#{@key_id_rsa}.pub") yield end end def setup_gateway(&block) gwhost = "gateway.netssh" gwuser = 'net_ssh_2' tmpdir do |dir| @gwkey_id_rsa = "#{dir}/id_rsa" sh "rm -rf #{@gwkey_id_rsa} #{@gwkey_id_rsa}.pub" sh "ssh-keygen -q -f #{@gwkey_id_rsa} -t rsa -N ''" set_authorized_key(gwuser,"#{@gwkey_id_rsa}.pub") config = "Host #{gwhost} IdentityFile #{@gwkey_id_rsa} StrictHostKeyChecking no " my_config = File.expand_path("~/.ssh/config") File.open(my_config, 'w') { |file| file.write(config) } begin FileUtils.chmod(0o600, my_config) yield gwuser, gwhost ensure FileUtils.rm(my_config) end end end def test_smoke setup_ssh_env do proxy = Net::SSH::Proxy::Command.new("/bin/nc localhost 22") msg = 'echo123' ret = Net::SSH.start(*ssh_start_params(proxy: proxy)) do |ssh| ssh.exec! "echo \"$USER:#{msg}\"" end assert_equal "net_ssh_1:#{msg}\n", ret end end def with_spurious_write_wakeup_emulate(rate=99,&block) orig_io_select = Net::SSH::Compat.method(:io_select) count = 0 Net::SSH::Compat.singleton_class.send(:define_method,:io_select) do |*params| count += 1 if (count % rate != 0) if params && params[1] && !params[1].empty? return [[],params[1],[]] end #if params && params[0] && !params[0].empty? #return [params[0],[],[]] #end end IO.select(*params) end begin yield ensure Net::SSH::Compat.singleton_class.send(:define_method,:io_select,&orig_io_select) end end def test_with_rate_limit_and_spurious_wakeup system("sudo sh -c 'echo 4096 > /proc/sys/fs/pipe-max-size'") begin setup_ssh_env do proxy = Net::SSH::Proxy::Command.new("/usr/bin/pv --rate-limit 100k | /bin/nc localhost 22") #proxy = Net::SSH::Proxy::Command.new("/bin/nc localhost 22") begin large_msg = 'echo123'*30000 ok = Net::SSH.start(*ssh_start_params(proxy: proxy)) do |ssh| with_spurious_write_wakeup_emulate do ret = ssh.exec! "echo \"$USER:#{large_msg}\"" #assert_equal "net_ssh_1:#{large_msg}\n", ret assert_equal "/bin/sh: Argument list too long\n", ret hello_count = 1000 ret = ssh.exec! "ruby -e 'puts \"Hello\"*#{hello_count}'" assert_equal "Hello"*hello_count+"\n", ret end :ok end end assert_equal :ok, ok end ensure system("sudo sh -c 'echo 1048576 > /proc/sys/fs/pipe-max-size'") end end def test_proxy_jump_through_localhost setup_ssh_env do setup_gateway do |gwuser, gwhost| proxy = Net::SSH::Proxy::Jump.new("#{gwuser}@#{gwhost}") #puts "ssh #{user}@#{localhost} -i #{@key_id_rsa} -J #{gwuser}@#{gwhost} -vvv" output = Net::SSH.start(*ssh_start_params(proxy: proxy)) do |ssh| ssh.exec! "echo \"$USER:echo123\"" end assert_equal "net_ssh_1:echo123\n", output end end end class DbgProxy attr_reader :io def initialize(origin) @origin = origin end def open(*args) @io = @origin.open(*args) @io end end def test_does_close_proxy_on_proxy_failure setup_ssh_env do proxy = DbgProxy.new(Net::SSH::Proxy::Command.new('sleep 2 && ssh -W %h:%p -o "PreferredAuthentications none" user@localhost')) msg = 'echo123' assert_raises Errno::EPIPE, Net::SSH::Proxy::ConnectError do Net::SSH.start(*ssh_start_params(proxy: proxy, password: 'bad', non_interactive: true, auth_methods: ['password'], verbose: :debug)) do |ssh| ssh.exec! "echo \"$USER:#{msg}\"" end end assert proxy.io.nil? || proxy.io.closed?, "Process #proxy.io.pid not closed" end end end net-ssh-4.2.0/test/known_hosts/000077500000000000000000000000001315376572000164425ustar00rootroot00000000000000net-ssh-4.2.0/test/known_hosts/github000066400000000000000000000006071315376572000176520ustar00rootroot00000000000000github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==net-ssh-4.2.0/test/known_hosts/github_hash000066400000000000000000000006721315376572000206570ustar00rootroot00000000000000|1|eKp+6E0rZ3lONgsIziurXEnaIik=|rcQB/rlJMUquUyFta64KugPjX4o= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== net-ssh-4.2.0/test/manual/000077500000000000000000000000001315376572000153435ustar00rootroot00000000000000net-ssh-4.2.0/test/manual/test_pageant.rb000066400000000000000000000020031315376572000203410ustar00rootroot00000000000000# # Tests for communication capability with Pageant (or KeeAgent) # process, to include the case where it is running in different UAC # context. # # To run: # - Ensure that Pageant is running (not as administrator). # - Open two command prompts, one as an administrator and one limited # (normal). # - Within each, from the root net/ssh project directory, execute: # ruby -Ilib -Itest -rrubygems test/manual/test_pageant.rb # require_relative '../common' require 'net/ssh/authentication/agent' module Authentication class TestPageapnt < NetSSHTest def test_agent_should_be_able_to_negotiate begin agent.negotiate! rescue Net::SSH::Authentication::AgentNotAvailable puts "Test failing connect now!.... :#{$!}" sleep 1800 raise end end private def agent(auto=:connect) @agent ||= begin agent = Net::SSH::Authentication::Agent.new agent.connect! if auto == :connect agent end end end end net-ssh-4.2.0/test/start/000077500000000000000000000000001315376572000152235ustar00rootroot00000000000000net-ssh-4.2.0/test/start/test_connection.rb000066400000000000000000000032471315376572000207540ustar00rootroot00000000000000require 'common' require 'net/ssh' module NetSSH class TestConnection < NetSSHTest attr_reader :connection_session def setup authentication_session = mock('authentication_session') authentication_session.stubs(:authenticate).returns(true) Net::SSH::Authentication::Session.stubs(:new).returns(authentication_session) @transport_session = mock('transport_session') Net::SSH::Transport::Session.stubs(:new).returns(@transport_session) @connection_session = mock('connection_session') Net::SSH::Connection::Session.expects(new: connection_session) end def test_close_connection_on_exception @connection_session.expects(:closed?).returns(false) @connection_session.expects(:close).once begin Net::SSH.start('localhost', 'testuser') { raise "error" } rescue RuntimeError # We aren't interested in the exception end end def test_close_connection_on_exception_only_if_still_open conn_open = states('conn').starts_as(true) @connection_session.expects(:close).then(conn_open.is(false)).once @connection_session.expects(:closed?).when(conn_open.is(false)).returns(true) begin Net::SSH.start('localhost', 'testuser') do |ssh| ssh.close raise "error" end rescue RuntimeError # We aren't interested in the exception end end def test_return_value_is_returned @connection_session.expects(:closed?).returns(false) @connection_session.expects(:close).once val = 1 retval = Net::SSH.start('localhost', 'testuser') { val } assert_equal(val, retval) end end end net-ssh-4.2.0/test/start/test_options.rb000066400000000000000000000065311315376572000203070ustar00rootroot00000000000000require_relative '../common' require 'net/ssh' module NetSSH class TestStartOptions < NetSSHTest def setup authentication_session = mock('authentication_session') authentication_session.stubs(:authenticate).returns(true) Net::SSH::Authentication::Session.stubs(:new).returns(authentication_session) Net::SSH::Transport::Session.stubs(:new).returns(mock('transport_session')) Net::SSH::Connection::Session.stubs(:new).returns(mock('connection_session')) end def test_start_should_accept_keepalive_option assert_nothing_raised do options = { keepalive: true } Net::SSH.start('localhost', 'testuser', options) end end def test_start_should_accept_keepalive_interval_option assert_nothing_raised do options = { keepalive_interval: 10 } Net::SSH.start('localhost', 'testuser', options) end end def test_start_should_accept_send_env_option assert_nothing_raised do options = { send_env: [ /^LC_.*$/, "LANG" ] } Net::SSH.start('localhost', 'testuser', options) end end def test_start_should_accept_number_of_password_prompts_option assert_nothing_raised do options = { number_of_password_prompts: 2 } Net::SSH.start('localhost', 'testuser', options) end end def test_start_should_accept_append_all_supported_algorithms_option assert_nothing_raised do options = { append_all_supported_algorithms: true } Net::SSH.start('localhost', 'testuser', options) end end def test_start_should_accept_non_interactive_option assert_nothing_raised do options = { non_interactive: true } Net::SSH.start('localhost', 'testuser', options) end end def test_start_should_accept_remote_user_option assert_nothing_raised do options = { remote_user: 'foo' } Net::SSH.start('localhost', 'testuser', options) end end def test_constructor_should_reject_options_set_to_nil Kernel.expects(:warn).with { |message| message =~ /remote_user/ }.once options = { remote_user: nil } Net::SSH.start('localhost', 'testuser', options) end def test_constructor_should_reject_options_set_to_array_of_nil Kernel.expects(:warn).with { |message| message =~ /keys/ }.once ENV.delete('no-such-env-variable') Net::SSH.start('localhost', 'testuser', keys: [ENV['no-such-env-variable']]) end def test_constructor_should_not_reject_nil_password_options_for_cap_v2_compatibility assert_nothing_raised do options = { password: nil } Net::SSH.start('localhost', 'testuser', options) end end def test_constructor_should_not_reject_nil_passpharse assert_nothing_raised do options = { passphrase: nil } Net::SSH.start('localhost', 'testuser', options) end end def test_constructor_should_reject_invalid_options assert_raises(ArgumentError) do options = { some_invalid_option: "some setting" } Net::SSH.start('localhost', 'testuser', options) end end def test_constructor_should_set_default_options options = { logger: nil, password_prompt: nil } Net::SSH.start('localhost', 'testuser', options) assert !options[:logger].nil? assert !options[:password_prompt].nil? end end end net-ssh-4.2.0/test/start/test_transport.rb000066400000000000000000000014761315376572000206530ustar00rootroot00000000000000require 'common' require 'net/ssh' module NetSSH class TestStart < NetSSHTest attr_reader :transport_session attr_reader :authentication_session def setup @transport_session = mock('transport_session') @authentication_session = mock('authentication_session') Net::SSH::Transport::Session.expects(new: transport_session) Net::SSH::Authentication::Session.expects(new: authentication_session) end def test_close_transport_when_authentication_fails authentication_session.expects(authenticate: false) transport_session.expects(:close).at_least_once begin Net::SSH.start('localhost', 'testuser') {} rescue Net::SSH::AuthenticationFailed # Authentication should fail, as it is part of the context end end end endnet-ssh-4.2.0/test/start/test_user_nil.rb000066400000000000000000000015761315376572000204400ustar00rootroot00000000000000require 'common' require 'net/ssh' module NetSSH class TestStartUserNil < NetSSHTest def setup @authentication_session = mock('authentication_session') Net::SSH::Authentication::Session.stubs(:new).returns(@authentication_session) Net::SSH::Transport::Session.stubs(:new).returns(mock('transport_session')) Net::SSH::Connection::Session.stubs(:new).returns(mock('connection_session')) end def test_start_should_accept_nil_user @authentication_session.stubs(:authenticate).returns(true) assert_nothing_raised do Net::SSH.start('localhost') end end def test_start_should_use_default_user_when_nil @authentication_session.stubs(:authenticate).with() {|_next_service, user, _password| user == Etc.getlogin }.returns(true) assert_nothing_raised do Net::SSH.start('localhost') end end end end net-ssh-4.2.0/test/test/000077500000000000000000000000001315376572000150455ustar00rootroot00000000000000net-ssh-4.2.0/test/test/test_test.rb000066400000000000000000000034701315376572000174140ustar00rootroot00000000000000require_relative '../common' require 'net/ssh/test' class TestNetSSHTest < NetSSHTest include Net::SSH::Test def test_example story do |session| channel = session.opens_channel channel.sends_exec "ls" channel.gets_data "result of ls" channel.gets_close channel.sends_close end assert_scripted do result = nil connection.open_channel do |ch| ch.exec("ls") do |_success| ch.on_data { |_c, data| result = data } ch.on_close { |c| c.close } end end connection.loop assert_equal "result of ls", result end end def test_pty story do |session| channel = session.opens_channel channel.sends_request_pty session.sends_channel_request(channel, "shell", false, nil, true) channel.sends_exec "ls" channel.gets_data "result of ls" channel.gets_close channel.sends_close end assert_scripted do result = nil connection.open_channel do |ch| ch.request_pty ch.send_channel_request("shell") ch.exec("ls") do |_success| ch.on_data { |_c, data| result = data } ch.on_close { |c| c.close } end end connection.loop assert_equal "result of ls", result end end def test_custom Packet.register_channel_request("custom", [:string, :string, :long]) story do |session| channel = session.opens_channel session.sends_channel_request(channel, "custom", false, ["hello", "hello", 42], true) channel.gets_close channel.sends_close end assert_scripted do connection.open_channel do |ch| ch.send_channel_request("custom", :string, "hello", :string, "hello", :long, 42) ch.on_close { |c| c.close } end connection.loop end end endnet-ssh-4.2.0/test/test_all.rb000066400000000000000000000011621315376572000162220ustar00rootroot00000000000000$: << '.' Dir.chdir(File.dirname(__FILE__)) do test_files = Dir['**/test_*.rb']-['test_all.rb'] # prevent circular require test_files -= Dir['integration/test_*.rb'] unless ENV['NET_SSH_RUN_INTEGRATION_TESTS'] test_files -= Dir['win_integration/test_*.rb'] unless ENV['NET_SSH_RUN_WIN_INTEGRATION_TESTS'] test_files -= Dir['test/test_*.rb'] test_files = test_files.reject { |f| f =~ /^manual/ } test_files = test_files.select { |f| f =~ Regexp.new(ENV['ONLY']) } if ENV['ONLY'] test_files = test_files.reject { |f| f =~ Regexp.new(ENV['EXCEPT']) } if ENV['EXCEPT'] test_files.each { |file| require(file) } endnet-ssh-4.2.0/test/test_buffer.rb000066400000000000000000000416701315376572000167330ustar00rootroot00000000000000# encoding: ASCII-8BIT require_relative 'common' require 'net/ssh/buffer' class TestBuffer < NetSSHTest def test_constructor_should_initialize_buffer_to_empty_by_default buffer = new assert buffer.empty? assert_equal 0, buffer.position end def test_constructor_with_string_should_initialize_buffer_to_the_string buffer = new("hello") assert !buffer.empty? assert_equal "hello", buffer.to_s assert_equal 0, buffer.position end def test_from_should_require_an_even_number_of_arguments assert_raises(ArgumentError) { Net::SSH::Buffer.from("this") } end def test_from_should_build_new_buffer_from_definition buffer = Net::SSH::Buffer.from(:byte, 1, :long, 2, :int64, 3, :string, "4", :bool, true, :bool, false, :bignum, OpenSSL::BN.new("1234567890", 10), :raw, "something") assert_equal "\1\0\0\0\2\0\0\0\0\0\0\0\3\0\0\0\0014\1\0\000\000\000\004I\226\002\322something", buffer.to_s end def test_from_should_build_new_buffer_that_includes_utf8_string_128_characters length = 128 # letter A has a 1 byte UTF8 representation buffer = Net::SSH::Buffer.from(:long, 2, :string, 'A' * length) # long of 2 + length 128 as network endian + 128 A's expected = "\x00\x00\x00\x02" + [length].pack('N*') + ("\x41" * length) assert_equal expected, buffer.to_s end def test_from_should_build_new_buffer_with_frozen_strings foo = 'foo'.freeze buffer = Net::SSH::Buffer.from(:string, foo) assert_equal "\0\0\0\3foo", buffer.to_s end def test_from_should_build_new_buffer_with_utf8_frozen_strings foo = "\u2603".freeze buffer = Net::SSH::Buffer.from(:string, foo) assert_equal "\0\0\0\3\u2603".force_encoding('BINARY'), buffer.to_s end def test_from_should_not_change_regular_paramaters foo = "\u2603" buffer = Net::SSH::Buffer.from(:string, foo) assert_equal "\0\0\0\3\u2603".force_encoding('BINARY'), buffer.to_s assert_equal foo.encoding.to_s, "UTF-8" end def test_from_with_array_argument_should_write_multiple_of_the_given_type buffer = Net::SSH::Buffer.from(:byte, [1,2,3,4,5]) assert_equal "\1\2\3\4\5", buffer.to_s end def test_from_should_measure_bytesize_of_utf_8_string_correctly buffer = Net::SSH::Buffer.from(:string, "\u2603") # Snowman is 3 bytes assert_equal "\0\0\0\3\u2603".force_encoding('BINARY'), buffer.to_s end def test_read_without_argument_should_read_to_end buffer = new("hello world") assert_equal "hello world", buffer.read assert buffer.eof? assert_equal 11, buffer.position end def test_read_with_argument_that_is_less_than_length_should_read_that_many_bytes buffer = new "hello world" assert_equal "hello", buffer.read(5) assert_equal 5, buffer.position end def test_read_with_argument_that_is_more_than_length_should_read_no_more_than_length buffer = new "hello world" assert_equal "hello world", buffer.read(500) assert_equal 11, buffer.position end def test_read_at_eof_should_return_empty_string buffer = new "hello" buffer.position = 5 assert_equal "", buffer.read end def test_consume_without_argument_should_resize_buffer_to_start_at_position buffer = new "hello world" buffer.read(5) assert_equal 5, buffer.position assert_equal 11, buffer.length buffer.consume! assert_equal 0, buffer.position assert_equal 6, buffer.length assert_equal " world", buffer.to_s end def test_consume_with_argument_should_resize_buffer_starting_at_n buffer = new "hello world" assert_equal 0, buffer.position buffer.consume!(5) assert_equal 0, buffer.position assert_equal 6, buffer.length assert_equal " world", buffer.to_s end def test_read_bang_should_read_and_consume_and_return_read_portion buffer = new "hello world" assert_equal "hello", buffer.read!(5) assert_equal 0, buffer.position assert_equal 6, buffer.length assert_equal " world", buffer.to_s end def test_available_should_return_length_after_position_to_end_of_string buffer = new "hello world" buffer.read(5) assert_equal 6, buffer.available end def test_clear_bang_should_reset_buffer_contents_and_counters buffer = new "hello world" buffer.read(5) buffer.clear! assert_equal 0, buffer.length assert_equal 0, buffer.position assert_equal "", buffer.to_s end def test_append_should_append_argument_without_changing_position_and_should_return_self buffer = new "hello world" buffer.read(5) buffer.append(" again") assert_equal 5, buffer.position assert_equal 12, buffer.available assert_equal 17, buffer.length assert_equal "hello world again", buffer.to_s end def test_remainder_as_buffer_should_return_a_new_buffer_filled_with_the_text_after_the_current_position buffer = new "hello world" buffer.read(6) b2 = buffer.remainder_as_buffer assert_equal 6, buffer.position assert_equal 0, b2.position assert_equal "world", b2.to_s end def test_read_int64_should_return_8_byte_integer buffer = new "\xff\xee\xdd\xcc\xbb\xaa\x99\x88" assert_equal 0xffeeddccbbaa9988, buffer.read_int64 assert_equal 8, buffer.position end def test_read_int64_should_return_nil_on_partial_read buffer = new "\0\0\0\0\0\0\0" assert_nil buffer.read_int64 assert buffer.eof? end def test_read_long_should_return_4_byte_integer buffer = new "\xff\xee\xdd\xcc\xbb\xaa\x99\x88" assert_equal 0xffeeddcc, buffer.read_long assert_equal 4, buffer.position end def test_read_long_should_return_nil_on_partial_read buffer = new "\0\0\0" assert_nil buffer.read_long assert buffer.eof? end def test_read_byte_should_return_single_byte_integer buffer = new "\xfe\xdc" assert_equal 0xfe, buffer.read_byte assert_equal 1, buffer.position end def test_read_byte_should_return_nil_at_eof assert_nil new.read_byte end def test_read_string_should_read_length_and_data_from_buffer buffer = new "\0\0\0\x0bhello world" assert_equal "hello world", buffer.read_string end def test_read_string_should_return_nil_if_4_byte_length_cannot_be_read assert_nil new("\0\1").read_string end def test_read_bool_should_return_true_if_non_zero_byte_is_read buffer = new "\1\2\3\4\5\6" 6.times { assert_equal true, buffer.read_bool } end def test_read_bool_should_return_false_if_zero_byte_is_read buffer = new "\0" assert_equal false, buffer.read_bool end def test_read_bool_should_return_nil_at_eof assert_nil new.read_bool end def test_read_bignum_should_read_openssl_formatted_bignum buffer = new("\000\000\000\004I\226\002\322") assert_equal OpenSSL::BN.new("1234567890", 10), buffer.read_bignum end def test_read_bignum_should_return_nil_if_length_cannot_be_read assert_nil new("\0\1\2").read_bignum end def test_read_key_blob_should_read_dsa_keys random_dss { |buffer| buffer.read_keyblob("ssh-dss") } end def test_read_key_blob_should_read_rsa_keys random_rsa { |buffer| buffer.read_keyblob("ssh-rsa") } end def test_read_key_should_read_dsa_key_type_and_keyblob random_dss do |buffer| b2 = Net::SSH::Buffer.from(:string, "ssh-dss", :raw, buffer) b2.read_key end end def test_read_key_should_read_rsa_key_type_and_keyblob random_rsa do |buffer| b2 = Net::SSH::Buffer.from(:string, "ssh-rsa", :raw, buffer) b2.read_key end end def test_read_buffer_should_read_a_string_and_return_it_wrapped_in_a_buffer buffer = new("\0\0\0\x0bhello world") b2 = buffer.read_buffer assert_equal 0, b2.position assert_equal 11, b2.length assert_equal "hello world", b2.read end def test_read_to_should_return_nil_if_pattern_does_not_exist_in_buffer buffer = new("one two three") assert_nil buffer.read_to("\n") end def test_read_to_should_grok_string_patterns buffer = new("one two three") assert_equal "one tw", buffer.read_to("tw") assert_equal 6, buffer.position end def test_read_to_should_grok_regex_patterns buffer = new("one two three") assert_equal "one tw", buffer.read_to(/tw/) assert_equal 6, buffer.position end def test_read_to_should_grok_fixnum_patterns buffer = new("one two three") assert_equal "one tw", buffer.read_to(?w) assert_equal 6, buffer.position end def test_reset_bang_should_reset_position_to_0 buffer = new("hello world") buffer.read(5) assert_equal 5, buffer.position buffer.reset! assert_equal 0, buffer.position end def test_write_should_write_arguments_directly_to_end_buffer buffer = new("start") buffer.write "hello", " ", "world" assert_equal "starthello world", buffer.to_s assert_equal 0, buffer.position end def test_write_int64_should_write_arguments_as_8_byte_integers_to_end_of_buffer buffer = new("start") buffer.write_int64 0xffeeddccbbaa9988, 0x7766554433221100 assert_equal "start\xff\xee\xdd\xcc\xbb\xaa\x99\x88\x77\x66\x55\x44\x33\x22\x11\x00", buffer.to_s end def test_write_long_should_write_arguments_as_4_byte_integers_to_end_of_buffer buffer = new("start") buffer.write_long 0xffeeddcc, 0xbbaa9988 assert_equal "start\xff\xee\xdd\xcc\xbb\xaa\x99\x88", buffer.to_s end def test_write_byte_should_write_arguments_as_1_byte_integers_to_end_of_buffer buffer = new("start") buffer.write_byte 1, 2, 3, 4, 5 assert_equal "start\1\2\3\4\5", buffer.to_s end def test_write_bool_should_write_arguments_as_1_byte_boolean_values_to_end_of_buffer buffer = new("start") buffer.write_bool nil, false, true, 1, Object.new assert_equal "start\0\0\1\1\1", buffer.to_s end def test_write_bignum_should_write_arguments_as_ssh_formatted_bignum_values_to_end_of_buffer buffer = new("start") buffer.write_bignum OpenSSL::BN.new('1234567890', 10) assert_equal "start\000\000\000\004I\226\002\322", buffer.to_s end def test_write_dss_key_should_write_argument_to_end_of_buffer buffer = new("start") key = OpenSSL::PKey::DSA.new if key.respond_to?(:set_pqg) key.set_pqg(0xffeeddccbbaa9988, 0x7766554433221100, 0xffddbb9977553311) key.set_key(0xeeccaa8866442200, nil) else key.p = 0xffeeddccbbaa9988 key.q = 0x7766554433221100 key.g = 0xffddbb9977553311 key.pub_key = 0xeeccaa8866442200 end buffer.write_key(key) assert_equal "start\0\0\0\7ssh-dss\0\0\0\011\0\xff\xee\xdd\xcc\xbb\xaa\x99\x88\0\0\0\010\x77\x66\x55\x44\x33\x22\x11\x00\0\0\0\011\0\xff\xdd\xbb\x99\x77\x55\x33\x11\0\0\0\011\0\xee\xcc\xaa\x88\x66\x44\x22\x00", buffer.to_s end def test_write_rsa_key_should_write_argument_to_end_of_buffer buffer = new("start") key = OpenSSL::PKey::RSA.new if key.respond_to?(:set_key) key.set_key(0x7766554433221100, 0xffeeddccbbaa9988, nil) else key.e = 0xffeeddccbbaa9988 key.n = 0x7766554433221100 end buffer.write_key(key) assert_equal "start\0\0\0\7ssh-rsa\0\0\0\011\0\xff\xee\xdd\xcc\xbb\xaa\x99\x88\0\0\0\010\x77\x66\x55\x44\x33\x22\x11\x00", buffer.to_s end if defined?(OpenSSL::PKey::EC) def test_read_key_blob_should_read_ecdsa_sha2_nistp256_keys random_ecdsa_sha2_nistp256 { |buffer| buffer.read_keyblob("ecdsa-sha2-nistp256") } end def test_read_key_blob_should_read_ecdsa_sha2_nistp384_keys random_ecdsa_sha2_nistp384 { |buffer| buffer.read_keyblob("ecdsa-sha2-nistp384") } end def test_read_key_blob_should_read_ecdsa_sha2_nistp521_keys random_ecdsa_sha2_nistp521 { |buffer| buffer.read_keyblob("ecdsa-sha2-nistp521") } end def test_read_key_should_read_ecdsa_sha2_nistp256_key_type_and_keyblob random_ecdsa_sha2_nistp256 do |buffer| b2 = Net::SSH::Buffer.from(:string, "ecdsa-sha2-nistp256", :raw, buffer) b2.read_key end end def test_read_key_should_read_ecdsa_sha2_nistp384_key_type_and_keyblob random_ecdsa_sha2_nistp384 do |buffer| b2 = Net::SSH::Buffer.from(:string, "ecdsa-sha2-nistp384", :raw, buffer) b2.read_key end end def test_read_key_should_read_ecdsa_sha2_nistp521_key_type_and_keyblob random_ecdsa_sha2_nistp521 do |buffer| b2 = Net::SSH::Buffer.from(:string, "ecdsa-sha2-nistp521", :raw, buffer) b2.read_key end end def test_write_ecdsa_sha2_nistp256_key_should_write_argument_to_end_of_buffer buffer = new("start") key = OpenSSL::PKey::EC.new("-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIISGj5vAJCWt2KPI8NwaWVDSNLl2vbRxDIOkY+n6O0VVoAoGCCqGSM49\nAwEHoUQDQgAEnKbs0yEogTKT4QRu8T9nb2svl2mEWXb6g224oCpD2o6TYNXNw54H\nmWkdCv+kFCqSlfSi5fqFhrXdfEY6zSzQYQ==\n-----END EC PRIVATE KEY-----\n") buffer.write_key(key) assert_equal "start\000\000\000\023ecdsa-sha2-nistp256\000\000\000\bnistp256\000\000\000A\004\234\246\354\323!(\2012\223\341\004n\361?gok/\227i\204Yv\372\203m\270\240*C\332\216\223`\325\315\303\236\a\231i\035\n\377\244\024*\222\225\364\242\345\372\205\206\265\335|F:\315,\320a", buffer.to_s end def test_write_ecdsa_sha2_nistp384_key_should_write_argument_to_end_of_buffer buffer = new("start") key = OpenSSL::PKey::EC.new("-----BEGIN EC PRIVATE KEY-----\nMIGkAgEBBDBAfxJpzhsR7O+wMol6BcDgualR8rJBvYegUDYbBUrDnPzDx2/gD1lZ\nnwG1FuD2s9igBwYFK4EEACKhZANiAATsfiU4Kxyvvj1DdvFYsdDnZIT7loRlan9I\n8geCWPPl6x7NFRP+awrnTaarMgieGqxG8IQaIA0SsDOICfbDBkuatRi0S1Et/in4\nZwVEZvO81Ro5YSrjuUDAsytnI6OXS28=\n-----END EC PRIVATE KEY-----\n") buffer.write_key(key) assert_equal "start\000\000\000\023ecdsa-sha2-nistp384\000\000\000\bnistp384\000\000\000a\004\354~%8+\034\257\276=Cv\361X\261\320\347d\204\373\226\204ej\177H\362\a\202X\363\345\353\036\315\025\023\376k\n\347M\246\2532\b\236\032\254F\360\204\032 \r\022\2603\210\t\366\303\006K\232\265\030\264KQ-\376)\370g\005Df\363\274\325\0329a*\343\271@\300\263+g#\243\227Ko", buffer.to_s end def test_write_ecdsa_sha2_nistp521_key_should_write_argument_to_end_of_buffer buffer = new("start") key = OpenSSL::PKey::EC.new("-----BEGIN EC PRIVATE KEY-----\nMIHbAgEBBEGhnQF/SFo4Vym88HnCfc6BR8WwYqDh9wNTPeqzR8auxIpp0GKQlCG2\nuHzyteJX5/YalV8empYhEzNmNLNn8x7j0aAHBgUrgQQAI6GBiQOBhgAEAYygOgV9\nVI8UyLQ3BDlv+rb3es+ufrIcj++cqcc9QcmRn237NiWRr/1NKy2AKijsEdACtZXo\nxPC0x9Vs9ieC2oR+ANOBubcxPl2giDnBYm8ywAmmlXsP5ByAM17k97CzW5O+Z/uO\nbxGUzzhoXTNcjqpAckhRVKdnh6FL/rKelT0tBYi+\n-----END EC PRIVATE KEY-----\n") buffer.write_key(key) assert_equal "start\000\000\000\023ecdsa-sha2-nistp521\000\000\000\bnistp521\000\000\000\205\004\001\214\240:\005}T\217\024\310\2647\0049o\372\266\367z\317\256~\262\034\217\357\234\251\307=A\311\221\237m\3736%\221\257\375M+-\200*(\354\021\320\002\265\225\350\304\360\264\307\325l\366'\202\332\204~\000\323\201\271\2671>]\240\2109\301bo2\300\t\246\225{\017\344\034\2003^\344\367\260\263[\223\276g\373\216o\021\224\3178h]3\\\216\252@rHQT\247g\207\241K\376\262\236\225=-\005\210\276", buffer.to_s end end private def random_rsa n1 = OpenSSL::BN.new(rand(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF).to_s, 10) n2 = OpenSSL::BN.new(rand(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF).to_s, 10) buffer = Net::SSH::Buffer.from(:bignum, [n1, n2]) key = yield(buffer) assert_equal "ssh-rsa", key.ssh_type assert_equal n1, key.e assert_equal n2, key.n end def random_dss n1 = OpenSSL::BN.new(rand(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF).to_s, 10) n2 = OpenSSL::BN.new(rand(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF).to_s, 10) n3 = OpenSSL::BN.new(rand(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF).to_s, 10) n4 = OpenSSL::BN.new(rand(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF).to_s, 10) buffer = Net::SSH::Buffer.from(:bignum, [n1, n2, n3, n4]) key = yield(buffer) assert_equal "ssh-dss", key.ssh_type assert_equal n1, key.p assert_equal n2, key.q assert_equal n3, key.g assert_equal n4, key.pub_key end if defined?(OpenSSL::PKey::EC) def random_ecdsa_sha2_nistp256 k = OpenSSL::PKey::EC.new("prime256v1").generate_key buffer = Net::SSH::Buffer.from(:string, "nistp256", :string, k.public_key.to_bn.to_s(2)) key = yield(buffer) assert_equal "ecdsa-sha2-nistp256", key.ssh_type assert_equal k.public_key, key.public_key end def random_ecdsa_sha2_nistp384 k = OpenSSL::PKey::EC.new("secp384r1").generate_key buffer = Net::SSH::Buffer.from(:string, "nistp384", :string, k.public_key.to_bn.to_s(2)) key = yield(buffer) assert_equal "ecdsa-sha2-nistp384", key.ssh_type assert_equal k.public_key, key.public_key end def random_ecdsa_sha2_nistp521 k = OpenSSL::PKey::EC.new("secp521r1").generate_key buffer = Net::SSH::Buffer.from(:string, "nistp521", :string, k.public_key.to_bn.to_s(2)) key = yield(buffer) assert_equal "ecdsa-sha2-nistp521", key.ssh_type assert_equal k.public_key, key.public_key end end def new(*args) Net::SSH::Buffer.new(*args) end end net-ssh-4.2.0/test/test_buffered_io.rb000066400000000000000000000035121315376572000177240ustar00rootroot00000000000000require 'common' require 'net/ssh/buffered_io' class TestBufferedIo < NetSSHTest def test_fill_should_pull_from_underlying_io io.expects(:recv).with(8192).returns("here is some data") assert_equal 17, io.fill assert_equal 17, io.available assert_equal "here is some data", io.read_available(20) end def test_enqueue_should_not_write_to_underlying_io assert !io.pending_write? io.expects(:send).never io.enqueue("here is some data") assert io.pending_write? end def test_send_pending_should_not_fail_when_no_writes_are_pending assert !io.pending_write? io.expects(:send).never assert_nothing_raised { io.send_pending } end def test_send_pending_with_pending_writes_should_write_to_underlying_io io.enqueue("here is some data") io.expects(:send).with("here is some data", 0).returns(17) assert io.pending_write? assert_nothing_raised { io.send_pending } assert !io.pending_write? end def test_wait_for_pending_sends_should_write_only_once_if_all_can_be_written_at_once io.enqueue("here is some data") io.expects(:send).with("here is some data", 0).returns(17) assert io.pending_write? assert_nothing_raised { io.wait_for_pending_sends } assert !io.pending_write? end def test_wait_for_pending_sends_should_write_multiple_times_if_first_write_was_partial io.enqueue("here is some data") io.expects(:send).with("here is some data", 0).returns(10) io.expects(:send).with("me data", 0).returns(4) io.expects(:send).with("ata", 0).returns(3) IO.expects(:select).times(2).with(nil, [io]).returns([[], [io]]) assert_nothing_raised { io.wait_for_pending_sends } assert !io.pending_write? end private def io @io ||= begin io = mock("io") io.extend(Net::SSH::BufferedIo) io end end endnet-ssh-4.2.0/test/test_config.rb000066400000000000000000000244501315376572000167240ustar00rootroot00000000000000require_relative './common' require 'net/ssh/config' require 'net/ssh' require 'pathname' require 'tempfile' class TestConfig < NetSSHTest def test_home_should_be_absolute_path assert Pathname.new(ENV['HOME']).absolute? end def test_load_for_non_existant_file_should_return_empty_hash bogus_file = File.expand_path("/bogus/file") File.expects(:readable?).with(bogus_file).returns(false) assert_equal({}, Net::SSH::Config.load(bogus_file, "host.name")) end def test_load_should_expand_path expected = File.expand_path("~/.ssh/config") File.expects(:readable?).with(expected).returns(false) Net::SSH::Config.load("~/.ssh/config", "host.name") end def test_load_with_exact_host_match_should_load_that_section config = Net::SSH::Config.load(config(:exact_match), "test.host") assert config['compression'] assert config['forwardagent'] assert_equal 1234, config['port'] end def test_load_with_wild_card_matches_should_load_all_matches_with_first_match_taking_precedence config = Net::SSH::Config.load(config(:wild_cards), "test.host") assert_equal 1234, config['port'] assert !config['compression'] assert config['forwardagent'] assert_equal %w(~/.ssh/id_dsa), config['identityfile'] assert !config.key?('rekeylimit') end def test_load_with_wild_card_and_negative_pattern_does_not_match config = Net::SSH::Config.load(config(:negative_match), "test.host") assert_equal 9876, config['port'] assert !config.key?('compression') end def test_load_with_pattern_does_match data = %q{ Host test.* Port 1234 Compression no } with_config_from_data data do |f| config = Net::SSH::Config.load(f, "test.host") assert_equal 1234, config['port'] end end def test_load_with_regex_chars data = %q{ Host | Port 1234 Compression no } with_config_from_data data do |f| config = Net::SSH::Config.load(f, "test.host") assert_nil config['port'] config = Net::SSH::Config.load(f, "|") assert_equal 1234, config['port'] end end def test_for_should_load_all_files_and_translate_to_net_ssh_options config = Net::SSH::Config.for("test.host", [config(:exact_match), config(:wild_cards)]) assert_equal 1234, config[:port] assert config[:compression] assert config[:forward_agent] assert_equal %w(~/.ssh/id_dsa), config[:keys] assert !config.key?(:rekey_limit) end def test_load_with_no_host config = Net::SSH::Config.load(config(:nohost), "test.host") assert_equal %w(~/.ssh/id_dsa ~/.ssh/id_rsa), config['identityfile'] assert_equal 1985, config['port'] end def test_load_with_multiple_hosts config = Net::SSH::Config.load(config(:multihost), "test.host") assert config['compression'] assert_equal '2G', config['rekeylimit'] assert_equal 1980, config['port'] end def test_load_with_multiple_hosts_and_config_should_match_for_both aconfig = Net::SSH::Config.load(config(:multihost), "test.host") bconfig = Net::SSH::Config.load(config(:multihost), "other.host") assert_equal aconfig['port'], bconfig['port'] assert_equal aconfig['compression'], bconfig['compression'] assert_equal aconfig['rekeylimit'], bconfig['rekeylimit'] end def test_load_should_parse_equal_sign_delimiters config = Net::SSH::Config.load(config(:eqsign), "test.test") assert config['compression'] assert_equal 1234, config['port'] end def test_translate_should_correctly_translate_from_openssh_to_net_ssh_names open_ssh = { 'bindaddress' => "127.0.0.1", 'ciphers' => "a,b,c", 'compression' => true, 'compressionlevel' => 6, 'connecttimeout' => 100, 'forwardagent' => true, 'hostbasedauthentication' => true, 'hostkeyalgorithms' => "d,e,f", 'identityfile' => %w(g h i), 'macs' => "j,k,l", 'passwordauthentication' => true, 'port' => 1234, 'pubkeyauthentication' => true, 'rekeylimit' => 1024, 'sendenv' => "LC_*", 'numberofpasswordprompts' => '123', 'serveraliveinterval' => '2', 'serveralivecountmax' => '4' } net_ssh = Net::SSH::Config.translate(open_ssh) assert_equal %w(a b c), net_ssh[:encryption] assert_equal true, net_ssh[:compression] assert_equal 6, net_ssh[:compression_level] assert_equal 100, net_ssh[:timeout] assert_equal true, net_ssh[:forward_agent] assert_equal %w(hostbased keyboard-interactive none password publickey), net_ssh[:auth_methods].sort assert_equal %w(d e f), net_ssh[:host_key] assert_equal %w(g h i), net_ssh[:keys] assert_equal %w(j k l), net_ssh[:hmac] assert_equal 1234, net_ssh[:port] assert_equal 1024, net_ssh[:rekey_limit] assert_equal "127.0.0.1", net_ssh[:bind_address] assert_equal [/^LC_.*$/], net_ssh[:send_env] assert_equal 123, net_ssh[:number_of_password_prompts] assert_equal 4, net_ssh[:keepalive_maxcount] assert_equal 2, net_ssh[:keepalive_interval] assert_equal true, net_ssh[:keepalive] end def test_translate_should_turn_off_authentication_methods open_ssh = { 'hostbasedauthentication' => false, 'passwordauthentication' => false, 'pubkeyauthentication' => false, 'challengeresponseauthentication' => false, 'kbdinteractiveauthentication' => false } net_ssh = Net::SSH::Config.translate(open_ssh) assert_equal %w(none), net_ssh[:auth_methods].sort end def test_translate_should_turn_on_authentication_methods open_ssh = { 'hostbasedauthentication' => true, 'passwordauthentication' => true, 'pubkeyauthentication' => true, 'challengeresponseauthentication' => true, 'kbdinteractiveauthentication' => true } net_ssh = Net::SSH::Config.translate(open_ssh) assert_equal %w(hostbased keyboard-interactive none password publickey), net_ssh[:auth_methods].sort end def test_translate_should_not_disable_keyboard_interactive_when_challange_or_keyboardinterective_is_on open_ssh = { 'kbdinteractiveauthentication' => false } net_ssh = Net::SSH::Config.translate(open_ssh) assert_equal %w(keyboard-interactive none password publickey), net_ssh[:auth_methods].sort open_ssh = { 'challengeresponseauthentication' => false } net_ssh = Net::SSH::Config.translate(open_ssh) assert_equal %w(keyboard-interactive none password publickey), net_ssh[:auth_methods].sort end def test_should_ddisable_keyboard_interactive_when_challeng_and_keyboardinteractive_is_off open_ssh = { 'challengeresponseauthentication' => false, 'kbdinteractiveauthentication' => false } net_ssh = Net::SSH::Config.translate(open_ssh) assert_equal %w(none password publickey), net_ssh[:auth_methods].sort end def test_for_should_turn_off_authentication_methods config = Net::SSH::Config.for("test.host", [config(:empty), config(:auth_off), config(:auth_on)]) assert_equal %w(none), config[:auth_methods].sort end def test_for_should_turn_on_authentication_methods config = Net::SSH::Config.for("test.host", [config(:empty), config(:auth_on), config(:auth_off)]) assert_equal %w(hostbased keyboard-interactive none password publickey), config[:auth_methods].sort end def test_configuration_for_when_HOME_is_null_should_not_raise with_home_env(nil) do with_restored_default_files do Net::SSH.configuration_for("test.host", true) end end end def test_config_for_when_HOME_is_null_should_not_raise with_home_env(nil) do with_restored_default_files do Net::SSH::Config.for("test.host") end end end def test_load_with_plus_sign_hosts config = Net::SSH::Config.load(config(:host_plus), "test.host") assert config['compression'] end def test_load_with_numeric_host config = Net::SSH::Config.load(config(:numeric_host), "1234") assert config['compression'] assert_equal '2G', config['rekeylimit'] assert_equal 1980, config['port'] end def test_load_wildcar_with_substitutes config = Net::SSH::Config.load(config(:substitutes), "toto") net_ssh = Net::SSH::Config.translate(config) assert_equal 'toto', net_ssh[:host_name] end def test_load_sufix_with_substitutes config = Net::SSH::Config.load(config(:substitutes), "test") net_ssh = Net::SSH::Config.translate(config) assert_equal 'test.sufix', net_ssh[:host_name] end def test_load_prefix_and_sufix_with_substitutes config = Net::SSH::Config.load(config(:substitutes), "1234") net_ssh = Net::SSH::Config.translate(config) assert_equal 'prefix.1234.sufix', net_ssh[:host_name] end def test_load_with_send_env config = Net::SSH::Config.load(config(:send_env), "1234") net_ssh = Net::SSH::Config.translate(config) assert_equal [/^GIT_.*$/, /^LANG$/, /^LC_.*$/], net_ssh[:send_env] end def test_load_with_remote_user config = Net::SSH::Config.load(config(:proxy_remote_user), "behind-proxy") net_ssh = Net::SSH::Config.translate(config) assert net_ssh[:proxy] end def test_load_with_proxy_jump config = Net::SSH::Config.load(config(:proxy_jump), "behind-proxy") net_ssh = Net::SSH::Config.translate(config) assert net_ssh[:proxy] end def test_load_with_include_keyword config = Net::SSH::Config.load(config(:include), "xyz") net_ssh = Net::SSH::Config.translate(config) assert_equal 'example.com', net_ssh[:host_name] assert_equal 'foo', net_ssh[:user] assert_equal 2345, net_ssh[:port] assert_equal true, net_ssh[:compression] assert net_ssh[:keys_only] assert_equal %w(~/.ssh/id.pem ~/.ssh/id2.pem ~/.ssh/id3.pem), net_ssh[:keys] end private def with_home_env(value,&block) env_home_before = ENV['HOME'] begin ENV['HOME'] = value yield ensure ENV['HOME'] = env_home_before end end def config(name) "test/configs/#{name}" end def with_config_from_data(data, &block) Tempfile.open('config') do |f| f.write(data) f.close yield(f.path) end end end net-ssh-4.2.0/test/test_key_factory.rb000066400000000000000000000265651315376572000200070ustar00rootroot00000000000000require_relative 'common' require 'net/ssh/key_factory' class TestKeyFactory < NetSSHTest def setup @key_file = File.expand_path("/key-file") end def test_load_unencrypted_private_RSA_key_should_return_key File.expects(:read).with(@key_file).returns(rsa_key.export) assert_equal rsa_key.to_der, Net::SSH::KeyFactory.load_private_key(@key_file).to_der end def test_load_unencrypted_private_DSA_key_should_return_key File.expects(:read).with(@key_file).returns(dsa_key.export) assert_equal dsa_key.to_der, Net::SSH::KeyFactory.load_private_key(@key_file).to_der end def test_load_encrypted_private_RSA_key_should_prompt_for_password_and_return_key prompt = MockPrompt.new File.expects(:read).with(@key_file).returns(encrypted(rsa_key, "password")) prompt.expects(:_ask).with("Enter passphrase for #{@key_file}:", has_entries(type: 'private_key', filename: @key_file), false).returns("password") assert_equal rsa_key.to_der, Net::SSH::KeyFactory.load_private_key(@key_file, nil, true, prompt).to_der end def test_load_encrypted_private_RSA_key_with_password_should_not_prompt_and_return_key File.expects(:read).with(@key_file).returns(encrypted(rsa_key, "password")) assert_equal rsa_key.to_der, Net::SSH::KeyFactory.load_private_key(@key_file, "password").to_der end def test_load_encrypted_private_DSA_key_should_prompt_for_password_and_return_key prompt = MockPrompt.new data = encrypted(dsa_key, "password") File.expects(:read).with(@key_file).returns(data) sha = Digest::SHA256.digest(data) prompt.expects(:_ask).with("Enter passphrase for #{@key_file}:", {type: 'private_key', filename: @key_file, sha: sha}, false).returns("password") assert_equal dsa_key.to_der, Net::SSH::KeyFactory.load_private_key(@key_file, nil, true, prompt).to_der end def test_load_encrypted_private_DSA_key_with_password_should_not_prompt_and_return_key File.expects(:read).with(@key_file).returns(encrypted(dsa_key, "password")) assert_equal dsa_key.to_der, Net::SSH::KeyFactory.load_private_key(@key_file, "password").to_der end def test_load_encrypted_private_key_should_give_three_tries_for_the_password_and_then_raise_exception prompt = MockPrompt.new File.expects(:read).with(@key_file).returns(encrypted(rsa_key, "password")) prompt.expects(:_ask).times(3).with("Enter passphrase for #{@key_file}:", has_entries(type: 'private_key', filename: @key_file), false).returns("passwod","passphrase","passwd") if OpenSSL::PKey.respond_to?(:read) error_class = [ArgumentError, OpenSSL::PKey::PKeyError] else error_class = [OpenSSL::PKey::RSAError] end assert_raises(*error_class) { Net::SSH::KeyFactory.load_private_key(@key_file, nil, true, prompt) } end def test_load_encrypted_private_key_should_raise_exception_without_asking_passphrase File.expects(:read).with(@key_file).returns(encrypted(rsa_key, "password")) Net::SSH::KeyFactory.expects(:prompt).never if OpenSSL::PKey.respond_to?(:read) error_class = [ArgumentError, OpenSSL::PKey::PKeyError] else error_class = [OpenSSL::PKey::RSAError] end assert_raises(*error_class) { Net::SSH::KeyFactory.load_private_key(@key_file, nil, false) } end def test_load_public_rsa_key_should_return_key File.expects(:read).with(@key_file).returns(public(rsa_key)) assert_equal rsa_key.to_blob, Net::SSH::KeyFactory.load_public_key(@key_file).to_blob end def test_load_public_rsa_key_with_comment_should_return_key File.expects(:read).with(@key_file).returns(public(rsa_key) + " key_comment") assert_equal rsa_key.to_blob, Net::SSH::KeyFactory.load_public_key(@key_file).to_blob end def test_load_public_rsa_key_with_options_should_return_key File.expects(:read).with(@key_file).returns(public(rsa_key, 'environment="FOO=bar"')) assert_equal rsa_key.to_blob, Net::SSH::KeyFactory.load_public_key(@key_file).to_blob end def test_load_public_rsa_key_with_options_and_comment_should_return_key File.expects(:read).with(@key_file).returns(public(rsa_key, 'environment="FOO=bar"') + " key_comment") assert_equal rsa_key.to_blob, Net::SSH::KeyFactory.load_public_key(@key_file).to_blob end if defined?(OpenSSL::PKey::EC) def test_load_unencrypted_private_ecdsa_sha2_nistp256_key_should_return_key File.expects(:read).with(@key_file).returns(ecdsa_sha2_nistp256_key.to_pem) assert_equal ecdsa_sha2_nistp256_key.to_der, Net::SSH::KeyFactory.load_private_key("/key-file").to_der end def test_load_unencrypted_private_ecdsa_sha2_nistp384_key_should_return_key File.expects(:read).with(@key_file).returns(ecdsa_sha2_nistp384_key.to_pem) assert_equal ecdsa_sha2_nistp384_key.to_der, Net::SSH::KeyFactory.load_private_key("/key-file").to_der end def test_load_unencrypted_private_ecdsa_sha2_nistp521_key_should_return_key File.expects(:read).with(@key_file).returns(ecdsa_sha2_nistp521_key.to_pem) assert_equal ecdsa_sha2_nistp521_key.to_der, Net::SSH::KeyFactory.load_private_key("/key-file").to_der end def test_load_public_ecdsa_sha2_nistp256_key_should_return_key File.expects(:read).with(@key_file).returns(public(ecdsa_sha2_nistp256_key)) assert_equal ecdsa_sha2_nistp256_key.to_blob, Net::SSH::KeyFactory.load_public_key("/key-file").to_blob end def test_load_public_ecdsa_sha2_nistp384_key_should_return_key File.expects(:read).with(@key_file).returns(public(ecdsa_sha2_nistp384_key)) assert_equal ecdsa_sha2_nistp384_key.to_blob, Net::SSH::KeyFactory.load_public_key("/key-file").to_blob end def test_load_public_ecdsa_sha2_nistp521_key_should_return_key File.expects(:read).with(@key_file).returns(public(ecdsa_sha2_nistp521_key)) assert_equal ecdsa_sha2_nistp521_key.to_blob, Net::SSH::KeyFactory.load_public_key("/key-file").to_blob end end def test_load_anonymous_private_key_should_return_key_or_raise_exception File.expects(:read).with(@key_file).returns(anonymous_private_key) if OpenSSL::PKey.respond_to?(:read) assert_equal OpenSSL::PKey::RSA.new(anonymous_private_key).to_der, Net::SSH::KeyFactory.load_private_key(@key_file).to_der else assert_raises(OpenSSL::PKey::PKeyError) { Net::SSH::KeyFactory.load_private_key(@key_file) } end end private def rsa_key # 512 bits @rsa_key ||= OpenSSL::PKey::RSA.new("0\202\001;\002\001\000\002A\000\235\236\374N\e@2E\321\3757\003\354c\276N\f\003\3479Ko\005\317\0027\a\255=\345!\306\220\340\211;\027u\331\260\362\2063x\332\301y4\353\v%\032\214v\312\304\212\271GJ\353\2701\031\002\003\001\000\001\002@\022Y\306*\031\306\031\224Cde\231QV3{\306\256U\2477\377\017\000\020\323\363R\332\027\351\034\224OU\020\227H|pUS\n\263+%\304\341\321\273/\271\e\004L\250\273\020&,\t\304By\002!\000\311c\246%a\002\305\277\262R\266\244\250\025V_\351]\264\016\265\341\355\305\223\347Z$8\205#\023\002!\000\310\\\367|\243I\363\350\020\307\246\302\365\ed\212L\273\2158M\223w\a\367 C\t\224A4\243\002!\000\262]+}\327\231\331\002\2331^\312\036\204'g\363\f&\271\020\245\365-\024}\306\374e\202\2459\002 }\231\341\276\3551\277\307{5\\\361\233\353G\024wS\237\fk}\004\302&\205\277\340rb\211\327\002!\000\223\307\025I:\215_\260\370\252\3757\256Y&X\364\354\342\215\350\203E8\227|\f\237M\375D|") end def dsa_key # 512 bits @dsa_key ||= OpenSSL::PKey::DSA.new("0\201\367\002\001\000\002A\000\203\316/\037u\272&J\265\003l3\315d\324h\372{\t8\252#\331_\026\006\035\270\266\255\343\353Z\302\276\335\336\306\220\375\202L\244\244J\206>\346\b\315\211\302L\246x\247u\a\376\366\345\302\016#\002\025\000\244\274\302\221Og\275/\302+\356\346\360\024\373wI\2573\361\002@\027\215\270r*\f\213\350C\245\021:\350 \006\\\376\345\022`\210b\262\3643\023XLKS\320\370\002\276\347A\nU\204\276\324\256`=\026\240\330\306J\316V\213\024\e\030\215\355\006\037q\337\356ln\002@\017\257\034\f\260\333'S\271#\237\230E\321\312\027\021\226\331\251Vj\220\305\316\036\v\266+\000\230\270\177B\003?t\a\305]e\344\261\334\023\253\323\251\223M\2175)a(\004\"lI8\312\303\307\a\002\024_\aznW\345\343\203V\326\246ua\203\376\201o\350\302\002") end if defined?(OpenSSL::PKey::EC) def ecdsa_sha2_nistp256_key @ecdsa_sha2_nistp256_key ||= OpenSSL::PKey::EC.new("-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEINv6pPVLlkqvT1v5MJlWgaSWGwqupISG4U79bUXQDNCaoAoGCCqGSM49\nAwEHoUQDQgAElqubvi/GkSme+bwtncU1NiE0dWQ0EO07VufUQg8lUJ5+Fi6f96qa\n95T1zwOMQhY1h8PP9rQIZr4S48vN/ZnQLw==\n-----END EC PRIVATE KEY-----\n") end def ecdsa_sha2_nistp384_key @ecdsa_sha2_nistp384_key ||= OpenSSL::PKey::EC.new("-----BEGIN EC PRIVATE KEY-----\nMIGkAgEBBDBxwkmydCn4mP4KMhlMpeBvIroQolWKVNoRPXpG7brFgK+Yiikqw8wd\nIZW5OlL4y3mgBwYFK4EEACKhZANiAARkoIR1oABi+aQJbKcmvzeYSKURQOyXM0HU\nR4T68v4hd/lJE4fFQRczj3wAaECe9u3CWI/oDlow4Vr0vab82ZGjIoblxblKQWYl\nyzENgzl226waGg1bLBo8Auilyf1B5yI=\n-----END EC PRIVATE KEY-----\n") end def ecdsa_sha2_nistp521_key @ecdsa_sha2_nistp521_key ||= OpenSSL::PKey::EC.new("-----BEGIN EC PRIVATE KEY-----\nMIHbAgEBBEHQ2i7kjEGQHQB4pUQW9a2eCLWR2S5Go8U3CDyfbRCrYEp/pTSgI8uu\nMXyR3bf3SjqFQgZ6MZk5lkyrissJuwmvZKAHBgUrgQQAI6GBiQOBhgAEAN14FACK\nbs/KTqw4rxijeozGTVJTh1hNzBl2XaIhM4Fv8o3fE/pvogymyFu53GCng6gC4dmx\n/hycF41iIM29xVKPAeBnRNl6MdFBjuthOmE8eCRezgk1Bak8aBDUrzNT8OQssscw\npvQK4nc6ga/wTDaQGy5kV8tCOHNs2wKH+p2LpWTJ\n-----END EC PRIVATE KEY-----\n") end end def anonymous_private_key @anonymous_key = <<-EOF -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC3id5gZ6bglJth yli8JNaRxhsqKwwPlReEI/mplzz5IP6gWQ92LogXbdBXtHf9ZpA53BeLmtcNBEY0 Ygd7sPBhlHABS5D5///zltSSX2+L5GCEiC6dpfGsySjqymWF+SZ2PaqfZbkWLmCD 9u4ysueaHf7xbF6txGprNp69efttWxdy+vU5tno7HVxemMZQUalpShFrdAYKKXEo cV7MtbkQjzubS14gaWGpWCXIl9uNKQeHpLKtre1Qn5Ft/zVpCHmhLQcYDuB1LAj9 7eoev4rIiOE2sfdkvKDlmFxvzq3myYH4o27WwAg9OZ5SBusn2zesKkRCBBEZ55rl uVknOGHXAgMBAAECggEAZE0U2OxsNxkfXS6+lXswQ5PW7pF90towcsdSPgrniGIu pKRnHbfKKbuaewOl+zZcpTIRL/rbgUKPtzrHSiJlC36aQyrvvJ/ZWV5ZJvC+vd19 nY/qob65NyrrkHwxRSjmiwGiR9/IaUXI+vUsMUqx5Ph1hawqhZ3sZlEAKR4LeDO8 M+OguG77jLaqj5/SNfi+GwyUDe85de4VfEG4S9HrMQk2Cp66rx0BqDnCLacyFQaI R0VczMXTU52q0uETmgUr8G9A1SaRc5ZWKAfZwxJTvqdIImWC9E+CY7wm+mZD4FE6 iVzVC0ngcdEd596kTDdU2BPVMluWzLkfqIrTt/5CeQKBgQDzgRzCPNxFtai6RAIi ekBSHqrDnrbeTaw32GVq5ACk1Zfk2I0svctz1iQ9qJ2SRINpygQhcyJKQ4r/LXi1 7Av9H/d6QV4T2AZzS4WcqBkxxRXFUfARtnKChzuCzNt9tNz4EZiv75RyQmztGZjV i94+ZvCyqup5be4Svf4MBxin9QKBgQDA9P4nHzFWZakTMei78LGb/4Auc+r0rZp7 8xg8Z92tvrDeJjMdesdhiFrPP1qiSYHnQ81MSWpn6BycBsHZqitejQmYnYput/s4 qG+m7SrkN8WL6rijYsbB+U14VDjMlBlOgcEgjlSNU2oeS+68u+uVI/fgyXcXn4Jq 33TSWSgfGwKBgA2tRdE/G9wqfOShZ0FKfoxePpcoNfs8f5zPYbrkPYkEmjh3VU6b Bm9mKrjv3JHXmU3608qRLe7f5lG42xvUu0OnZP4P59nTe2FEb6fB5VBfUn63wHUu OzZLpDMPkJB59SNV0a6oFT1pr7aNhoEQDxaQL5rJcMwLOaEB3OAOEft1AoGASz7+ 4Zi7b7rDPVYIMUpCqNfxT6wqovIUPWPmPqAuhXPIm0kAQ+2+VN2MtCc7m+/Ydawu IiK7GPweNAY6kDxZH00WweolstmSYVzl9Y2lXUwWgGKvUB/T7I7g1Bzb7YOPftsA ykZW2Kn/xwLLfdQ2oXleT82g4Jh2jmDHuMPF7qMCgYEA6QF45PvOgnrJessgmwO/ dEmkLl07PQYJPGZLaZteuWrvfMrn+AiW5aAdHzhzNaOtNy5B3T7zGUHtgxXegqgd /QdCVCJgnZUO/zdAxkr22dDn+WEXkL4wgBVStQvvnQp9C2NJcoOExvex5PLzKWQg WEKt5v3QsUEgVrzkM4K9UbI= -----END PRIVATE KEY----- EOF end def encrypted(key, password) cipher = OpenSSL::Cipher.new("des-ede3-cbc") key.export(cipher, password) end def public(key, args = nil) result = "" if !args.nil? result << "#{args} " end result << "#{key.ssh_type} " result << [Net::SSH::Buffer.from(:key, key).to_s].pack("m*").strip.tr("\n\r\t ", "") result << " joe@host.test" end end net-ssh-4.2.0/test/test_known_hosts.rb000066400000000000000000000036161315376572000200340ustar00rootroot00000000000000require 'common' class TestKnownHosts < NetSSHTest def perform_test(source) kh = Net::SSH::KnownHosts.new(source) keys = kh.keys_for("github.com") assert_equal(1, keys.count) assert_equal("ssh-rsa", keys[0].ssh_type) end def test_key_for_when_all_hosts_are_recognized perform_test(path("known_hosts/github")) end def test_key_for_when_an_host_hash_is_recognized perform_test(path("known_hosts/github_hash")) end def test_missing_then_add Tempfile.open('github') do |f| f.write(File.read(path("known_hosts/github"))) kh = Net::SSH::KnownHosts.new(f.path) keys = kh.keys_for("github2.com") assert_equal(0, keys.count) assert_equal([], keys.to_a) kh.add('github2.com',rsa_key) keys2 = kh.keys_for("github2.com") assert_equal([rsa_key.to_blob], keys2.to_a.map(&:to_blob)) end end def test_search_for options = {user_known_hosts_file: path("known_hosts/github")} keys = Net::SSH::KnownHosts.search_for('github.com',options) assert_equal(["ssh-rsa"], keys.map(&:ssh_type)) end def test_search_for_then_add Tempfile.open('github') do |f| f.write(File.read(path("known_hosts/github"))) options = {user_known_hosts_file: f.path} keys = Net::SSH::KnownHosts.search_for('github2.com',options) assert_equal(0, keys.count) keys.add_host_key(rsa_key) assert_equal([rsa_key.to_blob], keys.map(&:to_blob)) keys = Net::SSH::KnownHosts.search_for('github2.com',options) assert_equal([rsa_key.to_blob], keys.map(&:to_blob)) end end def path(relative_path) File.join(File.dirname(__FILE__), "known_hosts/github") end def rsa_key key = OpenSSL::PKey::RSA.new if key.respond_to?(:set_key) key.set_key(0x7766554433221100, 0xffeeddccbbaa9988, nil) else key.e = 0xffeeddccbbaa9988 key.n = 0x7766554433221100 end key end end net-ssh-4.2.0/test/test_proxy_jump.rb000066400000000000000000000033171315376572000176720ustar00rootroot00000000000000require_relative 'common' require 'net/ssh/proxy/jump' class TestProxyJump < NetSSHTest def test_is_a_proxy_command proxy = Net::SSH::Proxy::Jump.new("user@jumphost") assert proxy.is_a?(Net::SSH::Proxy::Command) end def test_host proxy = Net::SSH::Proxy::Jump.new("jumphost") proxy.build_proxy_command_equivalent assert_equal "ssh -W %h:%p jumphost", proxy.command_line_template end def test_user_host proxy = Net::SSH::Proxy::Jump.new("sally@proxy") proxy.build_proxy_command_equivalent assert_equal "ssh -l sally -W %h:%p proxy", proxy.command_line_template end def test_user_host_port proxy = Net::SSH::Proxy::Jump.new("bob@jump:2222") proxy.build_proxy_command_equivalent assert_equal "ssh -l bob -p 2222 -W %h:%p jump", proxy.command_line_template end def test_multiple_jump_proxies proxy = Net::SSH::Proxy::Jump.new("user1@proxy1,user2@proxy2,user3@proxy3") proxy.build_proxy_command_equivalent assert_equal "ssh -l user1 -J user2@proxy2,user3@proxy3 -W %h:%p proxy1", proxy.command_line_template end def test_config_override proxy = Net::SSH::Proxy::Jump.new("proxy") proxy.build_proxy_command_equivalent(config: "/home/user/.ssh/config2") assert_equal "ssh -F /home/user/.ssh/config2 -W %h:%p proxy", proxy.command_line_template end def test_config_false proxy = Net::SSH::Proxy::Jump.new("proxy") proxy.build_proxy_command_equivalent(config: false) assert_equal "ssh -W %h:%p proxy", proxy.command_line_template end def test_config_true proxy = Net::SSH::Proxy::Jump.new("proxy") proxy.build_proxy_command_equivalent(config: true) assert_equal "ssh -W %h:%p proxy", proxy.command_line_template end end net-ssh-4.2.0/test/transport/000077500000000000000000000000001315376572000161225ustar00rootroot00000000000000net-ssh-4.2.0/test/transport/hmac/000077500000000000000000000000001315376572000170325ustar00rootroot00000000000000net-ssh-4.2.0/test/transport/hmac/test_md5.rb000066400000000000000000000017541315376572000211120ustar00rootroot00000000000000# encoding: ASCII-8BIT require 'common' require 'net/ssh/transport/hmac/md5' module Transport; module HMAC class TestMD5 < NetSSHTest def test_expected_digest_class assert_equal OpenSSL::Digest::MD5, subject.digest_class assert_equal OpenSSL::Digest::MD5, subject.new.digest_class end def test_expected_key_length assert_equal 16, subject.key_length assert_equal 16, subject.new.key_length end def test_expected_mac_length assert_equal 16, subject.mac_length assert_equal 16, subject.new.mac_length end def test_expected_digest hmac = subject.new("1234567890123456") assert_equal "\275\345\006\307y~Oi\035<.\341\031\250<\257", hmac.digest("hello world") end def test_key_should_be_truncated_to_required_length hmac = subject.new("12345678901234567890") assert_equal "1234567890123456", hmac.key end private def subject Net::SSH::Transport::HMAC::MD5 end end end; endnet-ssh-4.2.0/test/transport/hmac/test_md5_96.rb000066400000000000000000000010671315376572000214250ustar00rootroot00000000000000# encoding: ASCII-8BIT require 'common' require 'transport/hmac/test_md5' require 'net/ssh/transport/hmac/md5_96' module Transport; module HMAC class TestMD5_96 < TestMD5 def test_expected_mac_length assert_equal 12, subject.mac_length assert_equal 12, subject.new.mac_length end def test_expected_digest hmac = subject.new("1234567890123456") assert_equal "\275\345\006\307y~Oi\035<.\341", hmac.digest("hello world") end private def subject Net::SSH::Transport::HMAC::MD5_96 end end end; endnet-ssh-4.2.0/test/transport/hmac/test_none.rb000066400000000000000000000013271315376572000213600ustar00rootroot00000000000000require 'common' require 'net/ssh/transport/hmac/none' module Transport; module HMAC class TestNone < NetSSHTest def test_expected_digest_class assert_nil subject.digest_class assert_nil subject.new.digest_class end def test_expected_key_length assert_equal 0, subject.key_length assert_equal 0, subject.new.key_length end def test_expected_mac_length assert_equal 0, subject.mac_length assert_equal 0, subject.new.mac_length end def test_expected_digest hmac = subject.new("1234567890123456") assert_equal "", hmac.digest("hello world") end private def subject Net::SSH::Transport::HMAC::None end end end; end net-ssh-4.2.0/test/transport/hmac/test_ripemd160.rb000066400000000000000000000015721315376572000221320ustar00rootroot00000000000000# encoding: ASCII-8BIT require 'common' require 'net/ssh/transport/hmac/ripemd160' module Transport; module HMAC class TestRipemd160 < NetSSHTest def test_expected_digest_class assert_equal OpenSSL::Digest::RIPEMD160, subject.digest_class assert_equal OpenSSL::Digest::RIPEMD160, subject.new.digest_class end def test_expected_key_length assert_equal 20, subject.key_length assert_equal 20, subject.new.key_length end def test_expected_mac_length assert_equal 20, subject.mac_length assert_equal 20, subject.new.mac_length end def test_expected_digest hmac = subject.new("1234567890123456") assert_equal "\xE4\x10\t\xB3\xD8,\x14\xA0k\x10\xB5\x0F?\x0E\x96q\x02\x16;E", hmac.digest("hello world") end private def subject Net::SSH::Transport::HMAC::RIPEMD160 end end end; end net-ssh-4.2.0/test/transport/hmac/test_sha1.rb000066400000000000000000000015421315376572000212540ustar00rootroot00000000000000# encoding: ASCII-8BIT require 'common' require 'net/ssh/transport/hmac/sha1' module Transport; module HMAC class TestSHA1 < NetSSHTest def test_expected_digest_class assert_equal OpenSSL::Digest::SHA1, subject.digest_class assert_equal OpenSSL::Digest::SHA1, subject.new.digest_class end def test_expected_key_length assert_equal 20, subject.key_length assert_equal 20, subject.new.key_length end def test_expected_mac_length assert_equal 20, subject.mac_length assert_equal 20, subject.new.mac_length end def test_expected_digest hmac = subject.new("1234567890123456") assert_equal "\000\004W\202\204+&\335\311\251P\266\250\214\276\206;\022U\365", hmac.digest("hello world") end private def subject Net::SSH::Transport::HMAC::SHA1 end end end; endnet-ssh-4.2.0/test/transport/hmac/test_sha1_96.rb000066400000000000000000000011021315376572000215620ustar00rootroot00000000000000# encoding: ASCII-8BIT require 'common' require 'transport/hmac/test_sha1' require 'net/ssh/transport/hmac/sha1_96' module Transport; module HMAC class TestSHA1_96 < TestSHA1 def test_expected_mac_length assert_equal 12, subject.mac_length assert_equal 12, subject.new.mac_length end def test_expected_digest hmac = subject.new("1234567890123456") assert_equal "\000\004W\202\204+&\335\311\251P\266", hmac.digest("hello world") end private def subject Net::SSH::Transport::HMAC::SHA1_96 end end end; endnet-ssh-4.2.0/test/transport/hmac/test_sha2_256.rb000066400000000000000000000016131315376572000216500ustar00rootroot00000000000000# encoding: ASCII-8BIT require 'common' require 'net/ssh/transport/hmac/sha2_256' module Transport; module HMAC class TestSHA2_256 < NetSSHTest def test_expected_digest_class assert_equal OpenSSL::Digest::SHA256, subject.digest_class assert_equal OpenSSL::Digest::SHA256, subject.new.digest_class end def test_expected_key_length assert_equal 32, subject.key_length assert_equal 32, subject.new.key_length end def test_expected_mac_length assert_equal 32, subject.mac_length assert_equal 32, subject.new.mac_length end def test_expected_digest hmac = subject.new("1234567890123456") assert_equal "\x16^>\x9FhO}\xB1>(\xBAF\xFBW\xB8\xF2\xFA\x824+\xC0\x94\x95\xC2\r\xE6\x88/\xEF\t\xF5%", hmac.digest("hello world") end private def subject Net::SSH::Transport::HMAC::SHA2_256 end end end; end net-ssh-4.2.0/test/transport/hmac/test_sha2_256_96.rb000066400000000000000000000011131315376572000221610ustar00rootroot00000000000000# encoding: ASCII-8BIT require 'common' require 'transport/hmac/test_sha2_256' require 'net/ssh/transport/hmac/sha2_256_96' module Transport; module HMAC class TestSHA2_256_96 < TestSHA2_256 def test_expected_mac_length assert_equal 12, subject.mac_length assert_equal 12, subject.new.mac_length end def test_expected_digest hmac = subject.new("1234567890123456") assert_equal "\x16^>\x9FhO}\xB1>(\xBAF", hmac.digest("hello world") end private def subject Net::SSH::Transport::HMAC::SHA2_256_96 end end end; end net-ssh-4.2.0/test/transport/hmac/test_sha2_512.rb000066400000000000000000000017421315376572000216460ustar00rootroot00000000000000# encoding: ASCII-8BIT require 'common' require 'net/ssh/transport/hmac/sha2_512' module Transport; module HMAC class TestSHA2_512 < NetSSHTest def test_expected_digest_class assert_equal OpenSSL::Digest::SHA512, subject.digest_class assert_equal OpenSSL::Digest::SHA512, subject.new.digest_class end def test_expected_key_length assert_equal 64, subject.key_length assert_equal 64, subject.new.key_length end def test_expected_mac_length assert_equal 64, subject.mac_length assert_equal 64, subject.new.mac_length end def test_expected_digest hmac = subject.new("1234567890123456") assert_equal "^\xB6\"\xED\x8B\xC4\xDE\xD4\xCF\xD0\r\x18\xA0<\xF4\xB5\x01Efz\xA80i\xFC\x18\xC1\x9A+\xDD\xFE<\xA2\xFDE1Ac\xF4\xADU\r\xFB^0\x90= \x837z\xCC\xD5p4a4\x83\xC6\x04m\xAA\xC1\xC0m", hmac.digest("hello world") end private def subject Net::SSH::Transport::HMAC::SHA2_512 end end end; end net-ssh-4.2.0/test/transport/hmac/test_sha2_512_96.rb000066400000000000000000000011341315376572000221570ustar00rootroot00000000000000# encoding: ASCII-8BIT require 'common' require 'transport/hmac/test_sha2_512' require 'net/ssh/transport/hmac/sha2_512_96' module Transport; module HMAC class TestSHA2_512_96 < TestSHA2_512 def test_expected_mac_length assert_equal 12, subject.mac_length assert_equal 12, subject.new.mac_length end def test_expected_digest hmac = subject.new("1234567890123456") assert_equal "^\xB6\"\xED\x8B\xC4\xDE\xD4\xCF\xD0\r\x18", hmac.digest("hello world") end private def subject Net::SSH::Transport::HMAC::SHA2_512_96 end end end; end net-ssh-4.2.0/test/transport/kex/000077500000000000000000000000001315376572000167115ustar00rootroot00000000000000net-ssh-4.2.0/test/transport/kex/test_diffie_hellman_group14_sha1.rb000066400000000000000000000005371315376572000255250ustar00rootroot00000000000000require 'common' require 'net/ssh/transport/kex/diffie_hellman_group14_sha1' require 'transport/kex/test_diffie_hellman_group1_sha1' require 'ostruct' module Transport; module Kex class TestDiffieHellmanGroup14SHA1 < TestDiffieHellmanGroup1SHA1 def subject Net::SSH::Transport::Kex::DiffieHellmanGroup14SHA1 end end end; end net-ssh-4.2.0/test/transport/kex/test_diffie_hellman_group1_sha1.rb000066400000000000000000000110151315376572000254320ustar00rootroot00000000000000require 'common' require 'net/ssh/transport/kex/diffie_hellman_group1_sha1' require 'ostruct' module Transport; module Kex class TestDiffieHellmanGroup1SHA1 < NetSSHTest include Net::SSH::Transport::Constants def setup @dh_options = @dh = @algorithms = @connection = @server_key = @packet_data = @shared_secret = nil end def digest_type OpenSSL::Digest::SHA1 end def test_exchange_keys_should_return_expected_results_when_successful result = exchange! assert_equal session_id, result[:session_id] assert_equal server_key.to_blob, result[:server_key].to_blob assert_equal shared_secret, result[:shared_secret] assert_equal digest_type, result[:hashing_algorithm] end def test_exchange_keys_with_unverifiable_host_should_raise_exception connection.verifier { false } assert_raises(Net::SSH::Exception) { exchange! } end def test_exchange_keys_with_signature_key_type_mismatch_should_raise_exception assert_raises(Net::SSH::Exception) { exchange! key_type: "ssh-dss" } end def test_exchange_keys_with_host_key_type_mismatch_should_raise_exception algorithms host_key: "ssh-dss" assert_raises(Net::SSH::Exception) { exchange! key_type: "ssh-dss" } end def test_exchange_keys_when_server_signature_could_not_be_verified_should_raise_exception @signature = "1234567890" assert_raises(Net::SSH::Exception) { exchange! } end def test_exchange_keys_should_pass_expected_parameters_to_host_key_verifier verified = false connection.verifier do |data| verified = true assert_equal server_key.to_blob, data[:key].to_blob blob = b(:key, data[:key]).to_s fingerprint = OpenSSL::Digest::MD5.hexdigest(blob).scan(/../).join(":") assert_equal blob, data[:key_blob] assert_equal fingerprint, data[:fingerprint] assert_equal connection, data[:session] true end assert_nothing_raised { exchange! } assert verified end private def exchange!(options={}) connection.expect do |t, buffer| assert_equal KEXDH_INIT, buffer.type assert_equal dh.dh.pub_key, buffer.read_bignum t.return(KEXDH_REPLY, :string, b(:key, server_key), :bignum, server_dh_pubkey, :string, b(:string, options[:key_type] || "ssh-rsa", :string, signature)) connection.expect do |t2, buffer2| assert_equal NEWKEYS, buffer2.type t2.return(NEWKEYS) end end dh.exchange_keys end def dh_options(options={}) @dh_options = options end def dh @dh ||= subject.new(algorithms, connection, packet_data.merge(need_bytes: 20).merge(@dh_options || {})) end def algorithms(options={}) @algorithms ||= OpenStruct.new(host_key: options[:host_key] || "ssh-rsa") end def connection @connection ||= MockTransport.new end def subject Net::SSH::Transport::Kex::DiffieHellmanGroup1SHA1 end # 512 bits is the smallest possible key that will work with this, so # we use it for speed reasons def server_key(bits=512) @server_key ||= OpenSSL::PKey::RSA.new(bits) end def packet_data @packet_data ||= { client_version_string: "client version string", server_version_string: "server version string", server_algorithm_packet: "server algorithm packet", client_algorithm_packet: "client algorithm packet" } end def server_dh_pubkey @server_dh_pubkey ||= bn(1234567890) end def shared_secret @shared_secret ||= OpenSSL::BN.new(dh.dh.compute_key(server_dh_pubkey), 2) end def session_id @session_id ||= begin buffer = Net::SSH::Buffer.from(:string, packet_data[:client_version_string], :string, packet_data[:server_version_string], :string, packet_data[:client_algorithm_packet], :string, packet_data[:server_algorithm_packet], :string, Net::SSH::Buffer.from(:key, server_key), :bignum, dh.dh.pub_key, :bignum, server_dh_pubkey, :bignum, shared_secret) OpenSSL::Digest::SHA1.digest(buffer.to_s) end end def signature @signature ||= server_key.ssh_do_sign(session_id) end def bn(number, base=10) OpenSSL::BN.new(number.to_s, base) end def b(*args) Net::SSH::Buffer.from(*args) end end end; endnet-ssh-4.2.0/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb000066400000000000000000000071311315376572000272170ustar00rootroot00000000000000require 'common' require 'transport/kex/test_diffie_hellman_group1_sha1' require 'net/ssh/transport/kex/diffie_hellman_group_exchange_sha1' module Transport; module Kex class TestDiffieHellmanGroupExchangeSHA1 < TestDiffieHellmanGroup1SHA1 KEXDH_GEX_GROUP = 31 KEXDH_GEX_INIT = 32 KEXDH_GEX_REPLY = 33 KEXDH_GEX_REQUEST = 34 def test_exchange_with_fewer_than_minimum_bits_uses_minimum_bits dh_options need_bytes: 20 assert_equal 1024, need_bits assert_nothing_raised { exchange! } end def test_exchange_with_optional_minimum_bits_declared dh_options minimum_dh_bits: 4096 assert_equal 4096, need_bits assert_nothing_raised { exchange! } end def test_exchange_with_fewer_than_maximum_bits_uses_need_bits dh_options need_bytes: 500 need_bits(8001) assert_nothing_raised { exchange! } end def test_exchange_with_more_than_maximum_bits_uses_maximum_bits dh_options need_bytes: 2000 need_bits(8192) assert_nothing_raised { exchange! } end def test_that_p_and_g_are_provided_by_the_server assert_nothing_raised { exchange! p: default_p+2, g: 3 } assert_equal default_p+2, dh.dh.p assert_equal 3, dh.dh.g end private def need_bits(bits=1024) @need_bits ||= need_minimum(bits) end def need_minimum(bits=1024) return @dh_options[:minimum_dh_bits] if @dh_options && @dh_options[:minimum_dh_bits] bits end def default_p 142326151570335518660743995281621698377057354949884468943021767573608899048361360422513557553514790045512299468953431585300812548859419857171094366358158903433167915517332113861059747425408670144201099811846875730766487278261498262568348338476437200556998366087779709990807518291581860338635288400119315130179 end def exchange!(options={}) connection.expect do |t, buffer| assert_equal KEXDH_GEX_REQUEST, buffer.type assert_equal need_minimum, buffer.read_long assert_equal need_bits, buffer.read_long assert_equal 8192, buffer.read_long t.return(KEXDH_GEX_GROUP, :bignum, bn(options[:p] || default_p), :bignum, bn(options[:g] || 2)) t.expect do |t2, buffer2| assert_equal KEXDH_GEX_INIT, buffer2.type assert_equal dh.dh.pub_key, buffer2.read_bignum t2.return(KEXDH_GEX_REPLY, :string, b(:key, server_key), :bignum, server_dh_pubkey, :string, b(:string, options[:key_type] || "ssh-rsa", :string, signature)) t2.expect do |t3, buffer3| assert_equal NEWKEYS, buffer3.type t3.return(NEWKEYS) end end end dh.exchange_keys end def subject Net::SSH::Transport::Kex::DiffieHellmanGroupExchangeSHA1 end def digest_type OpenSSL::Digest::SHA1 end def session_id @session_id ||= begin buffer = Net::SSH::Buffer.from(:string, packet_data[:client_version_string], :string, packet_data[:server_version_string], :string, packet_data[:client_algorithm_packet], :string, packet_data[:server_algorithm_packet], :string, Net::SSH::Buffer.from(:key, server_key), :long, 1024, :long, need_bits, # need bits, figure this part out, :long, 8192, :bignum, dh.dh.p, :bignum, dh.dh.g, :bignum, dh.dh.pub_key, :bignum, server_dh_pubkey, :bignum, shared_secret) digest_type.digest(buffer.to_s) end end end end; end net-ssh-4.2.0/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb000066400000000000000000000007021315376572000273700ustar00rootroot00000000000000require 'common' require 'net/ssh/transport/kex/diffie_hellman_group_exchange_sha1' require 'transport/kex/test_diffie_hellman_group_exchange_sha1' module Transport; module Kex class TestDiffieHellmanGroupExchangeSHA256 < TestDiffieHellmanGroupExchangeSHA1 private def subject Net::SSH::Transport::Kex::DiffieHellmanGroupExchangeSHA256 end def digest_type OpenSSL::Digest::SHA256 end end end; end net-ssh-4.2.0/test/transport/kex/test_ecdh_sha2_nistp256.rb000066400000000000000000000121301315376572000235640ustar00rootroot00000000000000require 'openssl' unless defined?(OpenSSL::PKey::EC) puts "Skipping tests for ecdh-sha2-nistp256 key exchange" else require 'common' require 'transport/kex/test_diffie_hellman_group1_sha1' require 'net/ssh/transport/kex/ecdh_sha2_nistp256' require 'ostruct' module Transport; module Kex class TestEcdhSHA2NistP256 < NetSSHTest include Net::SSH::Transport::Constants def setup @ecdh = @algorithms = @connection = @server_key = @packet_data = @shared_secret = nil end def test_exchange_keys_should_return_expected_results_when_successful result = exchange! assert_equal session_id, result[:session_id] assert_equal server_host_key.to_blob, result[:server_key].to_blob assert_equal shared_secret, result[:shared_secret] assert_equal digester, result[:hashing_algorithm] end def test_exchange_keys_with_unverifiable_host_should_raise_exception connection.verifier { false } assert_raises(Net::SSH::Exception) { exchange! } end def test_exchange_keys_with_signature_key_type_mismatch_should_raise_exception assert_raises(Net::SSH::Exception) { exchange! key_type: "ssh-dss" } end def test_exchange_keys_with_host_key_type_mismatch_should_raise_exception algorithms host_key: "ssh-dss" assert_raises(Net::SSH::Exception) { exchange! key_type: "ssh-dss" } end def test_exchange_keys_when_server_signature_could_not_be_verified_should_raise_exception @signature = "1234567890" assert_raises(Net::SSH::Exception) { exchange! } end def test_exchange_keys_should_pass_expected_parameters_to_host_key_verifier verified = false connection.verifier do |data| verified = true assert_equal server_host_key.to_blob, data[:key].to_blob blob = b(:key, data[:key]).to_s fingerprint = OpenSSL::Digest::MD5.hexdigest(blob).scan(/../).join(":") assert_equal blob, data[:key_blob] assert_equal fingerprint, data[:fingerprint] assert_equal connection, data[:session] true end assert_nothing_raised { exchange! } assert verified end private def digester OpenSSL::Digest::SHA256 end def subject Net::SSH::Transport::Kex::EcdhSHA2NistP256 end def ecparam "prime256v1" end def key_type "ecdsa-sha2-nistp256" end def exchange!(options={}) connection.expect do |t, buffer| assert_equal KEXECDH_INIT, buffer.type assert_equal ecdh.ecdh.public_key.to_bn.to_s(2), buffer.read_string t.return(KEXECDH_REPLY, :string, b(:key, server_host_key), :string, server_ecdh_pubkey.to_bn.to_s(2), :string, b(:string, options[:key_type] || key_type, :string, signature)) connection.expect do |t2, buffer2| assert_equal NEWKEYS, buffer2.type t2.return(NEWKEYS) end end ecdh.exchange_keys end def ecdh @ecdh ||= subject.new(algorithms, connection, packet_data) end def algorithms(options={}) @algorithms ||= OpenStruct.new(host_key: options[:server_host_key] || "ecdsa-sha2-nistp256") end def connection @connection ||= MockTransport.new end def server_key @server_key ||= OpenSSL::PKey::EC.new(ecparam).generate_key end def server_host_key @server_host_key ||= OpenSSL::PKey::EC.new("prime256v1").generate_key end def packet_data @packet_data ||= { client_version_string: "client version string", server_version_string: "server version string", server_algorithm_packet: "server algorithm packet", client_algorithm_packet: "client algorithm packet" } end def server_ecdh_pubkey @server_ecdh_pubkey ||= server_key.public_key end def shared_secret @shared_secret ||= OpenSSL::BN.new(ecdh.ecdh.dh_compute_key(server_ecdh_pubkey), 2) end def session_id @session_id ||= begin buffer = Net::SSH::Buffer.from(:string, packet_data[:client_version_string], :string, packet_data[:server_version_string], :string, packet_data[:client_algorithm_packet], :string, packet_data[:server_algorithm_packet], :string, Net::SSH::Buffer.from(:key, server_host_key), :string, ecdh.ecdh.public_key.to_bn.to_s(2), :string, server_ecdh_pubkey.to_bn.to_s(2), :bignum, shared_secret) digester.digest(buffer.to_s) end end def signature @signature ||= server_host_key.ssh_do_sign(session_id) end def b(*args) Net::SSH::Buffer.from(*args) end end end; end; end net-ssh-4.2.0/test/transport/kex/test_ecdh_sha2_nistp384.rb000066400000000000000000000017151315376572000235750ustar00rootroot00000000000000require 'openssl' unless defined?(OpenSSL::PKey::EC) puts "Skipping tests for ecdh-sha2-nistp384 key exchange" else require 'transport/kex/test_ecdh_sha2_nistp256' module Transport; module Kex class TestEcdhSHA2NistP384 < TestEcdhSHA2NistP256 def setup @ecdh = @algorithms = @connection = @server_key = @packet_data = @shared_secret = nil end def test_exchange_keys_should_return_expected_results_when_successful result = exchange! assert_equal session_id, result[:session_id] assert_equal server_host_key.to_blob, result[:server_key].to_blob assert_equal shared_secret, result[:shared_secret] assert_equal digester, result[:hashing_algorithm] end private def digester OpenSSL::Digest::SHA384 end def subject Net::SSH::Transport::Kex::EcdhSHA2NistP384 end def ecparam "secp384r1" end end end; end end net-ssh-4.2.0/test/transport/kex/test_ecdh_sha2_nistp521.rb000066400000000000000000000017151315376572000235660ustar00rootroot00000000000000require 'openssl' unless defined?(OpenSSL::PKey::EC) puts "Skipping tests for ecdh-sha2-nistp521 key exchange" else require 'transport/kex/test_ecdh_sha2_nistp256' module Transport; module Kex class TestEcdhSHA2NistP521 < TestEcdhSHA2NistP256 def setup @ecdh = @algorithms = @connection = @server_key = @packet_data = @shared_secret = nil end def test_exchange_keys_should_return_expected_results_when_successful result = exchange! assert_equal session_id, result[:session_id] assert_equal server_host_key.to_blob, result[:server_key].to_blob assert_equal shared_secret, result[:shared_secret] assert_equal digester, result[:hashing_algorithm] end private def digester OpenSSL::Digest::SHA512 end def subject Net::SSH::Transport::Kex::EcdhSHA2NistP521 end def ecparam "secp521r1" end end end; end end net-ssh-4.2.0/test/transport/test_algorithms.rb000066400000000000000000000437241315376572000216710ustar00rootroot00000000000000require_relative '../common' require 'net/ssh/transport/algorithms' module Transport class TestAlgorithms < NetSSHTest include Net::SSH::Transport::Constants def test_allowed_packets (0..255).each do |type| packet = stub("packet", type: type) case type when 1..4, 6..19, 21..49 then assert(Net::SSH::Transport::Algorithms.allowed_packet?(packet), "#{type} should be allowed during key exchange") else assert(!Net::SSH::Transport::Algorithms.allowed_packet?(packet), "#{type} should not be allowed during key exchange") end end end def test_constructor_should_build_default_list_of_preferred_algorithms assert_equal %w(ssh-rsa ssh-dss ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com)+ec_ed_host_keys, algorithms[:host_key] assert_equal %w(diffie-hellman-group-exchange-sha1 diffie-hellman-group1-sha1 diffie-hellman-group14-sha1 diffie-hellman-group-exchange-sha256)+ec_kex, algorithms[:kex] assert_equal %w(aes128-cbc 3des-cbc blowfish-cbc cast128-cbc aes192-cbc aes256-cbc rijndael-cbc@lysator.liu.se idea-cbc arcfour128 arcfour256 arcfour aes128-ctr aes192-ctr aes256-ctr cast128-ctr blowfish-ctr 3des-ctr none), algorithms[:encryption] if defined?(OpenSSL::Digest::SHA256) assert_equal %w(hmac-sha1 hmac-md5 hmac-sha1-96 hmac-md5-96 hmac-ripemd160 hmac-ripemd160@openssh.com hmac-sha2-256 hmac-sha2-512 hmac-sha2-256-96 hmac-sha2-512-96 none), algorithms[:hmac] else assert_equal %w(hmac-sha1 hmac-md5 hmac-sha1-96 hmac-md5-96 hmac-ripemd160 hmac-ripemd160@openssh.com none umac-128-etm@openssh.com), algorithms[:hmac] end assert_equal %w(none zlib@openssh.com zlib), algorithms[:compression] assert_equal %w(), algorithms[:language] end def test_constructor_should_set_client_and_server_prefs_identically %w(encryption hmac compression language).each do |key| assert_equal algorithms[key.to_sym], algorithms[:"#{key}_client"], key assert_equal algorithms[key.to_sym], algorithms[:"#{key}_server"], key end end def test_constructor_with_preferred_host_key_type_should_put_preferred_host_key_type_first assert_equal %w(ssh-dss ssh-rsa ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com)+ec_ed_host_keys, algorithms(host_key: "ssh-dss", append_all_supported_algorithms: true)[:host_key] end def test_constructor_with_known_hosts_reporting_known_host_key_should_use_that_host_key_type Net::SSH::KnownHosts.expects(:search_for).with("net.ssh.test,127.0.0.1", {}).returns([stub("key", ssh_type: "ssh-dss")]) assert_equal %w(ssh-dss ssh-rsa ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com)+ec_ed_host_keys, algorithms[:host_key] end def ed_host_keys if Net::SSH::Authentication::ED25519Loader::LOADED %w(ssh-ed25519) else [] end end def ec_host_keys if defined?(OpenSSL::PKey::EC) %w(ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521) else [] end end def ec_ed_host_keys ec_host_keys + ed_host_keys end def test_constructor_with_unrecognized_host_key_type_should_return_whats_supported assert_equal %w(ssh-rsa ssh-dss ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com)+ec_ed_host_keys, algorithms(host_key: "bogus ssh-rsa",append_all_supported_algorithms: true)[:host_key] end def ec_kex if defined?(OpenSSL::PKey::EC) %w(ecdh-sha2-nistp256 ecdh-sha2-nistp384 ecdh-sha2-nistp521) else [] end end def test_constructor_with_preferred_kex_should_put_preferred_kex_first assert_equal %w(diffie-hellman-group1-sha1 diffie-hellman-group-exchange-sha1 diffie-hellman-group14-sha1 diffie-hellman-group-exchange-sha256)+ec_kex, algorithms(kex: "diffie-hellman-group1-sha1", append_all_supported_algorithms: true)[:kex] end def test_constructor_with_unrecognized_kex_should_not_raise_exception assert_equal %w(diffie-hellman-group1-sha1 diffie-hellman-group-exchange-sha1 diffie-hellman-group14-sha1 diffie-hellman-group-exchange-sha256)+ec_kex, algorithms( kex: %w(bogus diffie-hellman-group1-sha1),append_all_supported_algorithms: true )[:kex] end def test_constructor_with_preferred_encryption_should_put_preferred_encryption_first assert_equal %w(aes256-cbc aes128-cbc 3des-cbc blowfish-cbc cast128-cbc aes192-cbc rijndael-cbc@lysator.liu.se idea-cbc arcfour128 arcfour256 arcfour aes128-ctr aes192-ctr aes256-ctr cast128-ctr blowfish-ctr 3des-ctr none), algorithms(encryption: "aes256-cbc", append_all_supported_algorithms: true)[:encryption] end def test_constructor_with_multiple_preferred_encryption_should_put_all_preferred_encryption_first assert_equal %w(aes256-cbc 3des-cbc idea-cbc aes128-cbc blowfish-cbc cast128-cbc aes192-cbc rijndael-cbc@lysator.liu.se arcfour128 arcfour256 arcfour aes128-ctr aes192-ctr aes256-ctr cast128-ctr blowfish-ctr 3des-ctr none), algorithms(encryption: %w(aes256-cbc 3des-cbc idea-cbc), append_all_supported_algorithms: true)[:encryption] end def test_constructor_with_unrecognized_encryption_should_keep_whats_supported assert_equal %w(aes256-cbc aes128-cbc 3des-cbc blowfish-cbc cast128-cbc aes192-cbc rijndael-cbc@lysator.liu.se idea-cbc arcfour128 arcfour256 arcfour aes128-ctr aes192-ctr aes256-ctr cast128-ctr blowfish-ctr 3des-ctr none), algorithms(encryption: %w(bogus aes256-cbc), append_all_supported_algorithms: true)[:encryption] end def test_constructor_with_preferred_hmac_should_put_preferred_hmac_first assert_equal %w(hmac-md5-96 hmac-sha1 hmac-md5 hmac-sha1-96 hmac-ripemd160 hmac-ripemd160@openssh.com hmac-sha2-256 hmac-sha2-512 hmac-sha2-256-96 hmac-sha2-512-96 none), algorithms(hmac: "hmac-md5-96", append_all_supported_algorithms: true)[:hmac] end def test_constructor_with_multiple_preferred_hmac_should_put_all_preferred_hmac_first assert_equal %w(hmac-md5-96 hmac-sha1-96 hmac-sha1 hmac-md5 hmac-ripemd160 hmac-ripemd160@openssh.com hmac-sha2-256 hmac-sha2-512 hmac-sha2-256-96 hmac-sha2-512-96 none), algorithms(hmac: %w(hmac-md5-96 hmac-sha1-96), append_all_supported_algorithms: true)[:hmac] end def test_constructor_with_unrecognized_hmac_should_ignore_those assert_equal %w(hmac-md5-96 hmac-sha1 hmac-md5 hmac-sha1-96 hmac-ripemd160 hmac-ripemd160@openssh.com hmac-sha2-256 hmac-sha2-512 hmac-sha2-256-96 hmac-sha2-512-96 none), algorithms(hmac: "hmac-md5-96", append_all_supported_algorithms: true)[:hmac] end def test_constructor_with_preferred_compression_should_put_preferred_compression_first assert_equal %w(zlib none zlib@openssh.com), algorithms(compression: "zlib", append_all_supported_algorithms: true)[:compression] end def test_constructor_with_multiple_preferred_compression_should_put_all_preferred_compression_first assert_equal %w(zlib@openssh.com zlib none), algorithms(compression: %w(zlib@openssh.com zlib), append_all_supported_algorithms: true)[:compression] end def test_constructor_with_general_preferred_compression_should_put_none_last assert_equal %w(zlib@openssh.com zlib none), algorithms( compression: true, append_all_supported_algorithms: true )[:compression] end def test_constructor_with_unrecognized_compression_should_return_whats_supported assert_equal %w(none zlib zlib@openssh.com), algorithms(compression: %w(bogus none zlib), append_all_supported_algorithms: true)[:compression] end def test_constructor_with_append_to_default default_host_keys = Net::SSH::Transport::Algorithms::ALGORITHMS[:host_key] assert_equal default_host_keys, algorithms(host_key: '+ssh-dss')[:host_key] end def test_initial_state_should_be_neither_pending_nor_initialized assert !algorithms.pending? assert !algorithms.initialized? end def test_key_exchange_when_initiated_by_server transport.expect do |t, buffer| assert_kexinit(buffer) install_mock_key_exchange(buffer) end install_mock_algorithm_lookups algorithms.accept_kexinit(kexinit) assert_exchange_results end def test_key_exchange_when_initiated_by_client state = nil transport.expect do |t, buffer| assert_kexinit(buffer) state = :sent_kexinit install_mock_key_exchange(buffer) end algorithms.rekey! assert_equal state, :sent_kexinit assert algorithms.pending? install_mock_algorithm_lookups algorithms.accept_kexinit(kexinit) assert_exchange_results end def test_key_exchange_when_server_does_not_support_preferred_kex_should_fallback_to_secondary kexinit kex: "diffie-hellman-group1-sha1" transport.expect do |t,buffer| assert_kexinit(buffer) install_mock_key_exchange(buffer, kex: Net::SSH::Transport::Kex::DiffieHellmanGroup1SHA1) end algorithms.accept_kexinit(kexinit) end def test_key_exchange_when_server_does_not_support_any_preferred_kex_should_raise_error kexinit kex: "something-obscure" transport.expect { |t,buffer| assert_kexinit(buffer) } assert_raises(Net::SSH::Exception) { algorithms.accept_kexinit(kexinit) } end def test_allow_when_not_pending_should_be_true_for_all_packets (0..255).each do |type| packet = stub("packet", type: type) assert algorithms.allow?(packet), type.to_s end end def test_allow_when_pending_should_be_true_only_for_packets_valid_during_key_exchange transport.expect! algorithms.rekey! assert algorithms.pending? (0..255).each do |type| packet = stub("packet", type: type) case type when 1..4, 6..19, 21..49 then assert(algorithms.allow?(packet), "#{type} should be allowed during key exchange") else assert(!algorithms.allow?(packet), "#{type} should not be allowed during key exchange") end end end def test_exchange_with_zlib_compression_enabled_sets_compression_to_standard algorithms compression: "zlib", append_all_supported_algorithms: true transport.expect do |t, buffer| assert_kexinit(buffer, compression_client: "zlib,none,zlib@openssh.com", compression_server: "zlib,none,zlib@openssh.com") install_mock_key_exchange(buffer) end install_mock_algorithm_lookups algorithms.accept_kexinit(kexinit) assert_equal :standard, transport.client_options[:compression] assert_equal :standard, transport.server_options[:compression] end def test_exchange_with_zlib_at_openssh_dot_com_compression_enabled_sets_compression_to_delayed algorithms compression: "zlib@openssh.com", append_all_supported_algorithms: true transport.expect do |t, buffer| assert_kexinit(buffer, compression_client: "zlib@openssh.com,none,zlib", compression_server: "zlib@openssh.com,none,zlib") install_mock_key_exchange(buffer) end install_mock_algorithm_lookups algorithms.accept_kexinit(kexinit) assert_equal :delayed, transport.client_options[:compression] assert_equal :delayed, transport.server_options[:compression] end # Verification for https://github.com/net-ssh/net-ssh/issues/483 def test_that_algorithm_undefined_doesnt_throw_exception # Create a logger explicitly with DEBUG logging string_io = StringIO.new("") debug_logger = Logger.new(string_io) debug_logger.level = Logger::DEBUG # Create our algorithm instance, with our logger sent to the underlying transport instance alg = algorithms( {}, logger: debug_logger ) # Here are our two lists - "ours" and "theirs" # # [a,b] overlap # [d] "unsupported" values ours = %w(a b c) theirs = %w(a b d) ## Hit the method directly alg.send( :compose_algorithm_list, ours, theirs ) assert string_io.string.include?(%(unsupported algorithm: `["d"]')) end private def install_mock_key_exchange(buffer, options={}) kex = options[:kex] || Net::SSH::Transport::Kex::DiffieHellmanGroupExchangeSHA1 Net::SSH::Transport::Kex::MAP.each do |name, klass| next if klass == kex klass.expects(:new).never end kex.expects(:new). with(algorithms, transport, client_version_string: Net::SSH::Transport::ServerVersion::PROTO_VERSION, server_version_string: transport.server_version.version, server_algorithm_packet: kexinit.to_s, client_algorithm_packet: buffer.to_s, need_bytes: 20, minimum_dh_bits: nil, logger: nil). returns(stub("kex", exchange_keys: { shared_secret: shared_secret, session_id: session_id, hashing_algorithm: hashing_algorithm })) end def install_mock_algorithm_lookups(options={}) params = { shared: shared_secret.to_ssh, hash: session_id, digester: hashing_algorithm } Net::SSH::Transport::CipherFactory.expects(:get). with(options[:client_cipher] || "aes128-cbc", params.merge(iv: key("A"), key: key("C"), encrypt: true)). returns(:client_cipher) Net::SSH::Transport::CipherFactory.expects(:get). with(options[:server_cipher] || "aes128-cbc", params.merge(iv: key("B"), key: key("D"), decrypt: true)). returns(:server_cipher) Net::SSH::Transport::HMAC.expects(:get).with(options[:client_hmac] || "hmac-sha1", key("E"), params).returns(:client_hmac) Net::SSH::Transport::HMAC.expects(:get).with(options[:server_hmac] || "hmac-sha1", key("F"), params).returns(:server_hmac) end def shared_secret @shared_secret ||= OpenSSL::BN.new("1234567890", 10) end def session_id @session_id ||= "this is the session id" end def hashing_algorithm OpenSSL::Digest::SHA1 end def key(salt) hashing_algorithm.digest(shared_secret.to_ssh + session_id + salt + session_id) end def cipher(type, options={}) Net::SSH::Transport::CipherFactory.get(type, options) end def kexinit(options={}) @kexinit ||= P(:byte, KEXINIT, :long, rand(0xFFFFFFFF), :long, rand(0xFFFFFFFF), :long, rand(0xFFFFFFFF), :long, rand(0xFFFFFFFF), :string, options[:kex] || "diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha256", :string, options[:host_key] || "ssh-rsa,ssh-dss", :string, options[:encryption_client] || "aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,idea-cbc", :string, options[:encryption_server] || "aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,idea-cbc", :string, options[:hmac_client] || "hmac-sha1,hmac-md5,hmac-sha1-96,hmac-md5-96", :string, options[:hmac_server] || "hmac-sha1,hmac-md5,hmac-sha1-96,hmac-md5-96", :string, options[:compression_client] || "none,zlib@openssh.com,zlib", :string, options[:compression_server] || "none,zlib@openssh.com,zlib", :string, options[:language_client] || "", :string, options[:language_server] || "", :bool, options[:first_kex_follows]) end def assert_kexinit(buffer, options={}) assert_equal KEXINIT, buffer.type assert_equal 16, buffer.read(16).length assert_equal options[:kex] || (%w(diffie-hellman-group-exchange-sha1 diffie-hellman-group1-sha1 diffie-hellman-group14-sha1 diffie-hellman-group-exchange-sha256)+ec_kex).join(','), buffer.read_string assert_equal options[:host_key] || (%w(ssh-rsa ssh-dss ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com)+ec_ed_host_keys).join(','), buffer.read_string assert_equal options[:encryption_client] || "aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,idea-cbc,arcfour128,arcfour256,arcfour,aes128-ctr,aes192-ctr,aes256-ctr,cast128-ctr,blowfish-ctr,3des-ctr,none", buffer.read_string assert_equal options[:encryption_server] || "aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,idea-cbc,arcfour128,arcfour256,arcfour,aes128-ctr,aes192-ctr,aes256-ctr,cast128-ctr,blowfish-ctr,3des-ctr,none", buffer.read_string assert_equal options[:hmac_client] || "hmac-sha1,hmac-md5,hmac-sha1-96,hmac-md5-96,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha2-256-96,hmac-sha2-512-96,none", buffer.read_string assert_equal options[:hmac_server] || "hmac-sha1,hmac-md5,hmac-sha1-96,hmac-md5-96,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha2-256-96,hmac-sha2-512-96,none", buffer.read_string assert_equal options[:compression_client] || "none,zlib@openssh.com,zlib", buffer.read_string assert_equal options[:compression_server] || "none,zlib@openssh.com,zlib", buffer.read_string assert_equal options[:language_client] || "", buffer.read_string assert_equal options[:language_server] || "", buffer.read_string assert_equal options[:first_kex_follows] || false, buffer.read_bool end def assert_exchange_results assert algorithms.initialized? assert !algorithms.pending? assert !transport.client_options[:compression] assert !transport.server_options[:compression] assert_equal :client_cipher, transport.client_options[:cipher] assert_equal :server_cipher, transport.server_options[:cipher] assert_equal :client_hmac, transport.client_options[:hmac] assert_equal :server_hmac, transport.server_options[:hmac] end def algorithms(algrothms_options={}, transport_options={}) @algorithms ||= Net::SSH::Transport::Algorithms.new( transport(transport_options), algrothms_options ) end def transport(transport_options={}) @transport ||= MockTransport.new(transport_options) end end end net-ssh-4.2.0/test/transport/test_cipher_factory.rb000066400000000000000000000307111315376572000225110ustar00rootroot00000000000000# encoding: ASCII-8BIT require 'common' require 'net/ssh/transport/cipher_factory' module Transport class TestCipherFactory < NetSSHTest def self.if_supported?(name) yield if Net::SSH::Transport::CipherFactory.supported?(name) end def test_lengths_for_none assert_equal [0,0], factory.get_lengths("none") assert_equal [0,0], factory.get_lengths("bogus") end def test_lengths_for_blowfish_cbc assert_equal [16,8], factory.get_lengths("blowfish-cbc") end if_supported?("idea-cbc") do def test_lengths_for_idea_cbc assert_equal [16,8], factory.get_lengths("idea-cbc") end end def test_lengths_for_rijndael_cbc assert_equal [32,16], factory.get_lengths("rijndael-cbc@lysator.liu.se") end def test_lengths_for_cast128_cbc assert_equal [16,8], factory.get_lengths("cast128-cbc") end def test_lengths_for_3des_cbc assert_equal [24,8], factory.get_lengths("3des-cbc") end def test_lengths_for_aes128_cbc assert_equal [16,16], factory.get_lengths("aes128-cbc") end def test_lengths_for_aes192_cbc assert_equal [24,16], factory.get_lengths("aes192-cbc") end def test_lengths_for_aes256_cbc assert_equal [32,16], factory.get_lengths("aes256-cbc") end def test_lengths_for_arcfour assert_equal [16,8], factory.get_lengths("arcfour") end def test_lengths_for_arcfour128 assert_equal [16,8], factory.get_lengths("arcfour128") end def test_lengths_for_arcfour256 assert_equal [32,8], factory.get_lengths("arcfour256") end def test_lengths_for_arcfour512 assert_equal [64,8], factory.get_lengths("arcfour512") end def test_lengths_for_3des_ctr assert_equal [24,8], factory.get_lengths("3des-ctr") end def test_lengths_for_aes128_ctr assert_equal [16,16], factory.get_lengths("aes128-ctr") end def test_lengths_for_aes192_ctr assert_equal [24,16], factory.get_lengths("aes192-ctr") end def test_lengths_for_aes256_ctr assert_equal [32,16], factory.get_lengths("aes256-ctr") end def test_lengths_for_blowfish_ctr assert_equal [16,8], factory.get_lengths("blowfish-ctr") end def test_lengths_for_cast128_ctr assert_equal [16,8], factory.get_lengths("cast128-ctr") end BLOWFISH_CBC = "\210\021\200\315\240_\026$\352\204g\233\244\242x\332e\370\001\327\224Nv@9_\323\037\252kb\037\036\237\375]\343/y\037\237\312Q\f7]\347Y\005\275%\377\0010$G\272\250B\265Nd\375\342\372\025r6}+Y\213y\n\237\267\\\374^\346BdJ$\353\220Ik\023<\236&H\277=\225" def test_blowfish_cbc_for_encryption assert_equal BLOWFISH_CBC, encrypt("blowfish-cbc") end def test_blowfish_cbc_for_decryption assert_equal TEXT, decrypt("blowfish-cbc", BLOWFISH_CBC) end if_supported?("idea-cbc") do IDEA_CBC = "W\234\017G\231\b\357\370H\b\256U]\343M\031k\233]~\023C\363\263\177\262-\261\341$\022\376mv\217\322\b\2763\270H\306\035\343z\313\312\3531\351\t\201\302U\022\360\300\354ul7$z\320O]\360g\024\305\005`V\005\335A\351\312\270c\320D\232\eQH1\340\265\2118\031g*\303v" def test_idea_cbc_for_encryption assert_equal IDEA_CBC, encrypt("idea-cbc") end def test_idea_cbc_for_decryption assert_equal TEXT, decrypt("idea-cbc", IDEA_CBC) end end RIJNDAEL = "$\253\271\255\005Z\354\336&\312\324\221\233\307Mj\315\360\310Fk\241EfN\037\231\213\361{'\310\204\347I\343\271\005\240`\325;\034\346uM>#\241\231C`\374\261\vo\226;Z\302:\b\250\366T\330\\#V\330\340\226\363\374!\bm\266\232\207!\232\347\340\t\307\370\356z\236\343=v\210\206y" def test_rijndael_cbc_for_encryption assert_equal RIJNDAEL, encrypt("rijndael-cbc@lysator.liu.se") end def test_rijndael_cbc_for_decryption assert_equal TEXT, decrypt("rijndael-cbc@lysator.liu.se", RIJNDAEL) end CAST128_CBC = "qW\302\331\333P\223t[9 ~(sg\322\271\227\272\022I\223\373p\255>k\326\314\260\2003\236C_W\211\227\373\205>\351\334\322\227\223\e\236\202Ii\032!P\214\035:\017\360h7D\371v\210\264\317\236a\262w1\2772\023\036\331\227\240:\f/X\351\324I\t[x\350\323E\2301\016m" def test_cast128_cbc_for_encryption assert_equal CAST128_CBC, encrypt("cast128-cbc") end def test_cast128_cbc_for_decryption assert_equal TEXT, decrypt("cast128-cbc", CAST128_CBC) end TRIPLE_DES_CBC = "\322\252\216D\303Q\375gg\367A{\177\313\3436\272\353%\223K?\257\206|\r&\353/%\340\336 \203E8rY\206\234\004\274\267\031\233T/{\"\227/B!i?[qGaw\306T\206\223\213n \212\032\244%]@\355\250\334\312\265E\251\017\361\270\357\230\274KP&^\031r+r%\370" def test_3des_cbc_for_encryption assert_equal TRIPLE_DES_CBC, encrypt("3des-cbc") end def test_3des_cbc_for_decryption assert_equal TEXT, decrypt("3des-cbc", TRIPLE_DES_CBC) end AES128_CBC = "k\026\350B\366-k\224\313\3277}B\035\004\200\035\r\233\024$\205\261\231Q\2214r\245\250\360\315\237\266hg\262C&+\321\346Pf\267v\376I\215P\327\345-\232&HK\375\326_\030<\a\276\212\303g\342C\242O\233\260\006\001a&V\345`\\T\e\236.\207\223l\233ri^\v\252\363\245" def test_aes128_cbc_for_encryption assert_equal AES128_CBC, encrypt("aes128-cbc") end def test_aes128_cbc_for_decryption assert_equal TEXT, decrypt("aes128-cbc", AES128_CBC) end AES192_CBC = "\256\017)x\270\213\336\303L\003f\235'jQ\3231k9\225\267\242\364C4\370\224\201\302~\217I\202\374\2167='\272\037\225\223\177Y\r\212\376(\275\n\3553\377\177\252C\254\236\016MA\274Z@H\331<\rL\317\205\323[\305X8\376\237=\374\352bH9\244\0231\353\204\352p\226\326~J\242" def test_aes192_cbc_for_encryption assert_equal AES192_CBC, encrypt("aes192-cbc") end def test_aes192_cbc_for_decryption assert_equal TEXT, decrypt("aes192-cbc", AES192_CBC) end AES256_CBC = "$\253\271\255\005Z\354\336&\312\324\221\233\307Mj\315\360\310Fk\241EfN\037\231\213\361{'\310\204\347I\343\271\005\240`\325;\034\346uM>#\241\231C`\374\261\vo\226;Z\302:\b\250\366T\330\\#V\330\340\226\363\374!\bm\266\232\207!\232\347\340\t\307\370\356z\236\343=v\210\206y" def test_aes256_cbc_for_encryption assert_equal AES256_CBC, encrypt("aes256-cbc") end def test_aes256_cbc_for_decryption assert_equal TEXT, decrypt("aes256-cbc", AES256_CBC) end ARCFOUR = "\xC1.\x1AdH\xD0+%\xF1CrG\x1C\xCC\xF6\xACho\xB0\x95\\\xBC\x02P\xF9\xAF\n\xBB<\x13\xF3\xCF\xEB\n\b(iO\xFB'\t^?\xA6\xE5a\xE2\x17\f\x97\xCAs\x9E\xFC\xF2\x88\xC93\v\x84\xCA\x82\x0E\x1D\x11\xEA\xE1\x82\x8E\xB3*\xC5\xFB\x8Cmgs\xB0\xFA\xF5\x9C\\\xE2\xB0\x95\x1F>LT" def test_arcfour_for_encryption assert_equal ARCFOUR, encrypt("arcfour") end def test_arcfour_for_decryption assert_equal TEXT, decrypt("arcfour", ARCFOUR) end ARCFOUR128 = "\n\x90\xED*\xD4\xBE\xCBg5\xA5\a\xEC]\x97\xB7L\x06)6\x12FL\x90@\xF4Sqxqh\r\x11\x1Aq \xC8\xE6v\xC6\x12\xD9 0 result << cipher.final end def decrypt(type, data) cipher = factory.get(type, OPTIONS.merge(decrypt: true)) result = cipher.update(data.dup) result << cipher.final result.strip end end end net-ssh-4.2.0/test/transport/test_hmac.rb000066400000000000000000000022611315376572000204170ustar00rootroot00000000000000require 'common' require 'net/ssh/transport/hmac' module Transport class TestHMAC < NetSSHTest Net::SSH::Transport::HMAC::MAP.each do |name, value| method = name.tr("-", "_") define_method("test_get_with_#{method}_returns_new_hmac_instance") do key = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!&$%"[0,Net::SSH::Transport::HMAC::MAP[name].key_length] hmac = Net::SSH::Transport::HMAC.get(name, key, { shared: "123", hash: "^&*", digester: OpenSSL::Digest::SHA1 }) assert_instance_of Net::SSH::Transport::HMAC::MAP[name], hmac assert_equal key, hmac.key end define_method("test_key_length_with_#{method}_returns_correct_key_length") do assert_equal Net::SSH::Transport::HMAC::MAP[name].key_length, Net::SSH::Transport::HMAC.key_length(name) end end def test_get_with_unrecognized_hmac_raises_argument_error assert_raises(ArgumentError) do Net::SSH::Transport::HMAC.get("bogus") end end def test_key_length_with_unrecognized_hmac_raises_argument_error assert_raises(ArgumentError) do Net::SSH::Transport::HMAC.get("bogus") end end end end net-ssh-4.2.0/test/transport/test_identity_cipher.rb000066400000000000000000000014151315376572000226720ustar00rootroot00000000000000require 'common' require 'net/ssh/transport/identity_cipher' module Transport class TestIdentityCipher < NetSSHTest def test_block_size_should_be_8 assert_equal 8, cipher.block_size end def test_encrypt_should_return_self assert_equal cipher, cipher.encrypt end def test_decrypt_should_return_self assert_equal cipher, cipher.decrypt end def test_update_should_return_argument assert_equal "hello, world", cipher.update("hello, world") end def test_final_should_return_empty_string assert_equal "", cipher.final end def test_name_should_be_identity assert_equal "identity", cipher.name end private def cipher Net::SSH::Transport::IdentityCipher end end end net-ssh-4.2.0/test/transport/test_packet_stream.rb000066400000000000000000003042171315376572000223370ustar00rootroot00000000000000# encoding: ASCII-8BIT require 'common' require 'timeout' require 'net/ssh/transport/packet_stream' module Transport class TestPacketStream < NetSSHTest # rubocop:disable Metrics/ClassLength include Net::SSH::Transport::Constants def test_client_name_when_getnameinfo_works stream.expects(:getsockname).returns(:sockaddr) Socket.expects(:getnameinfo).with(:sockaddr, Socket::NI_NAMEREQD).returns(["net.ssh.test"]) assert_equal "net.ssh.test", stream.client_name end def test_client_name_when_getnameinfo_fails_first_and_then_works stream.expects(:getsockname).returns(:sockaddr) Socket.expects(:getnameinfo).with(:sockaddr, Socket::NI_NAMEREQD).raises(SocketError) Socket.expects(:getnameinfo).with(:sockaddr).returns(["1.2.3.4"]) assert_equal "1.2.3.4", stream.client_name end def test_client_name_when_getnameinfo_fails_but_gethostbyname_works stream.expects(:getsockname).returns(:sockaddr) Socket.expects(:getnameinfo).with(:sockaddr, Socket::NI_NAMEREQD).raises(SocketError) Socket.expects(:getnameinfo).with(:sockaddr).raises(SocketError) Socket.expects(:gethostname).returns(:hostname) Socket.expects(:gethostbyname).with(:hostname).returns(["net.ssh.test"]) assert_equal "net.ssh.test", stream.client_name end def test_client_name_when_getnameinfo_and_gethostbyname_all_fail stream.expects(:getsockname).returns(:sockaddr) Socket.expects(:getnameinfo).with(:sockaddr, Socket::NI_NAMEREQD).raises(SocketError) Socket.expects(:getnameinfo).with(:sockaddr).raises(SocketError) Socket.expects(:gethostname).returns(:hostname) Socket.expects(:gethostbyname).with(:hostname).raises(SocketError) assert_equal "unknown", stream.client_name end def test_peer_ip_should_query_socket_for_info_about_peer stream.expects(:getpeername).returns(:sockaddr) Socket.expects(:getnameinfo).with(:sockaddr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV).returns(["1.2.3.4"]) assert_equal "1.2.3.4", stream.peer_ip end def test_peer_ip_should_return_no_hostip_when_socket_has_no_peername assert_equal false, stream.respond_to?(:getpeername) assert_equal Net::SSH::Transport::PacketStream::PROXY_COMMAND_HOST_IP, stream.peer_ip assert_equal '', stream.peer_ip end def test_available_for_read_should_return_nontrue_when_select_fails IO.expects(:select).returns(nil) assert !stream.available_for_read? end def test_available_for_read_should_return_nontrue_when_self_is_not_ready IO.expects(:select).with([stream], nil, nil, 0).returns([[],[],[]]) assert !stream.available_for_read? end def test_available_for_read_should_return_true_when_self_is_ready IO.expects(:select).with([stream], nil, nil, 0).returns([[self],[],[]]) assert stream.available_for_read? end def test_cleanup_should_delegate_cleanup_to_client_and_server_states stream.client.expects(:cleanup) stream.server.expects(:cleanup) stream.cleanup end def test_if_needs_rekey_should_not_yield_if_neither_client_nor_server_states_need_rekey stream.if_needs_rekey? { flunk "shouldn't need rekey" } assert(true) end def test_if_needs_rekey_should_yield_and_cleanup_if_client_needs_rekey stream.client.stubs(:needs_rekey?).returns(true) stream.client.expects(:reset!) stream.server.expects(:reset!).never rekeyed = false stream.if_needs_rekey? { rekeyed = true } assert(rekeyed) end def test_if_needs_rekey_should_yield_and_cleanup_if_server_needs_rekey stream.server.stubs(:needs_rekey?).returns(true) stream.server.expects(:reset!) stream.client.expects(:reset!).never rekeyed = false stream.if_needs_rekey? { rekeyed = true } assert(rekeyed) end def test_if_needs_rekey_should_yield_and_cleanup_if_both_need_rekey stream.server.stubs(:needs_rekey?).returns(true) stream.client.stubs(:needs_rekey?).returns(true) stream.server.expects(:reset!) stream.client.expects(:reset!) rekeyed = false stream.if_needs_rekey? { rekeyed = true } assert(rekeyed) end def test_next_packet_should_not_block_by_default IO.expects(:select).returns(nil) assert_nothing_raised do Timeout.timeout(1) { stream.next_packet } end end def test_next_packet_should_return_nil_when_non_blocking_and_not_ready IO.expects(:select).returns(nil) assert_nil stream.next_packet(:nonblock) end def test_next_packet_should_return_nil_when_non_blocking_and_partial_read IO.expects(:select).returns([[stream]]) stream.expects(:recv).returns([8].pack("N")) assert_nil stream.next_packet(:nonblock) assert !stream.read_buffer.empty? end def test_next_packet_should_return_packet_when_non_blocking_and_full_read IO.expects(:select).returns([[stream]]) stream.expects(:recv).returns(packet) packet = stream.next_packet(:nonblock) assert_not_nil packet assert_equal DEBUG, packet.type end def test_next_packet_should_eventually_return_packet_when_non_blocking_and_partial_read IO.stubs(:select).returns([[stream]]) stream.stubs(:recv).returns(packet[0,10], packet[10..-1]) assert_nil stream.next_packet(:nonblock) packet = stream.next_packet(:nonblock) assert_not_nil packet assert_equal DEBUG, packet.type end def test_nonblocking_next_packet_should_raise IO.stubs(:select).returns([[stream]]) stream.stubs(:recv).returns("") assert_raises(Net::SSH::Disconnect) { stream.next_packet(:nonblock) } end def test_nonblocking_next_packet_should_return_packet_before_raise IO.stubs(:select).returns([[stream]]) stream.send(:input).append(packet) stream.stubs(:recv).returns("") packet = stream.next_packet(:nonblock) assert_not_nil packet assert_equal DEBUG, packet.type assert_raises(Net::SSH::Disconnect) { stream.next_packet(:nonblock) } end def test_next_packet_should_block_when_requested_until_entire_packet_is_available IO.stubs(:select).returns([[stream]]) stream.stubs(:recv).returns(packet[0,10], packet[10,20], packet[20..-1]) packet = stream.next_packet(:block) assert_not_nil packet assert_equal DEBUG, packet.type end def test_next_packet_when_blocking_should_fail_when_fill_could_not_read_any_data IO.stubs(:select).returns([[stream]]) stream.stubs(:recv).returns("") assert_raises(Net::SSH::Disconnect) { stream.next_packet(:block) } end def test_next_packet_fails_with_invalid_argument assert_raises(ArgumentError) { stream.next_packet("invalid") } end def test_send_packet_should_enqueue_and_send_data_immediately stream.expects(:send).times(3).with { |a,b| a == stream.write_buffer && b == 0 }.returns(15) IO.expects(:select).times(2).returns([[], [stream]]) stream.send_packet(ssh_packet) assert !stream.pending_write? end def test_enqueue_short_packet_should_ensure_packet_is_at_least_16_bytes_long packet = Net::SSH::Buffer.from(:byte, 0) stream.enqueue_packet(packet) # 12 originally, plus the block-size (8), plus the 4-byte length field assert_equal 24, stream.write_buffer.length end def test_enqueue_utf_8_packet_should_ensure_packet_length_is_in_bytes_and_multiple_of_block_length packet = Net::SSH::Buffer.from(:string, "\u2603") # Snowman is 3 bytes stream.enqueue_packet(packet) # When bytesize is measured wrong using length, the result is off by 2. # With length instead of bytesize, you get 26 length buffer. assert_equal 0, stream.write_buffer.length % 8 end PACKETS = { "3des-cbc" => { "hmac-md5" => { false => "\003\352\031\261k\243\200\204\301\203]!\a\306\217\201\a[^\304\317\322\264\265~\361\017\n\205\272, \000\032w\312\t\306\374\271\345p\215\224\373\363\v\261", :standard => "\317\222v\316\234<\310\377\310\034\346\351\020:\025{\372PDS\246\344\312J\364\301\n\262\r<\037\231Mu\031\240\255\026\362\200\354=g\361\271[E\265\217\316\314\b\202\235\226\334", }, "hmac-md5-96" => { false => "\003\352\031\261k\243\200\204\301\203]!\a\306\217\201\a[^\304\317\322\264\265~\361\017\n\205\272, \000\032w\312\t\306\374\271\345p\215\224", :standard => "\317\222v\316\234<\310\377\310\034\346\351\020:\025{\372PDS\246\344\312J\364\301\n\262\r<\037\231Mu\031\240\255\026\362\200\354=g\361\271[E\265\217\316\314\b", }, "hmac-sha1" => { false => "\003\352\031\261k\243\200\204\301\203]!\a\306\217\201\a[^\304\317\322\264\265~\361\017\n\205\272, \004\a\200\n\004\202z\270\236\261\330m\275\005\f\202g\260g\376", :standard => "\317\222v\316\234<\310\377\310\034\346\351\020:\025{\372PDS\246\344\312J\364\301\n\262\r<\037\231Mu\031\240\255\026\362\200\2117U\266\3444(\235\034\023\377\376\335\301\253rI\215W\311", }, "hmac-sha1-96" => { false => "\003\352\031\261k\243\200\204\301\203]!\a\306\217\201\a[^\304\317\322\264\265~\361\017\n\205\272, \004\a\200\n\004\202z\270\236\261\330m", :standard => "\317\222v\316\234<\310\377\310\034\346\351\020:\025{\372PDS\246\344\312J\364\301\n\262\r<\037\231Mu\031\240\255\026\362\200\2117U\266\3444(\235\034\023\377\376", }, "hmac-ripemd160" => { false => "\003\352\031\261k\243\200\204\301\203]!\a\306\217\201\a[^\304\317\322\264\265~\361\017\n\205\272, F\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "\317\222v\316\234<\310\377\310\034\346\351\020:\025{\372PDS\246\344\312J\364\301\n\262\r<\037\231Mu\031\240\255\026\362\200)U\275\003U\333\225\221Y)\317\256\240\246\0000\351\032\363Y", }, "hmac-ripemd160@openssh.com" => { false => "\003\352\031\261k\243\200\204\301\203]!\a\306\217\201\a[^\304\317\322\264\265~\361\017\n\205\272, F\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "\317\222v\316\234<\310\377\310\034\346\351\020:\025{\372PDS\246\344\312J\364\301\n\262\r<\037\231Mu\031\240\255\026\362\200)U\275\003U\333\225\221Y)\317\256\240\246\0000\351\032\363Y", }, "none" => { false => "\003\352\031\261k\243\200\204\301\203]!\a\306\217\201\a[^\304\317\322\264\265~\361\017\n\205\272, ", :standard => "\317\222v\316\234<\310\377\310\034\346\351\020:\025{\372PDS\246\344\312J\364\301\n\262\r<\037\231Mu\031\240\255\026\362\200", }, }, "aes128-cbc" => { "hmac-md5" => { false => "\240\016\243k]0\330\253\030\320\334\261(\034E\211\230#\326\374\267\311O\211E(\234\325n\306NY\000\032w\312\t\306\374\271\345p\215\224\373\363\v\261", :standard => "\273\367\324\032\3762\334\026\r\246\342\022\016\325\024\270.\273\005\314\036\312\211\261\037A\361\362:W\316\352K\204\216b\2124>A\265g\331\177\233dK\251-\345\b\025\242#\336P8\343\361\263\\\241\326\311", }, "hmac-md5-96" => { false => "\240\016\243k]0\330\253\030\320\334\261(\034E\211\230#\326\374\267\311O\211E(\234\325n\306NY\000\032w\312\t\306\374\271\345p\215\224", :standard => "\273\367\324\032\3762\334\026\r\246\342\022\016\325\024\270.\273\005\314\036\312\211\261\037A\361\362:W\316\352K\204\216b\2124>A\265g\331\177\233dK\251-\345\b\025\242#\336P8\343\361\263", }, "hmac-sha1" => { false => "\240\016\243k]0\330\253\030\320\334\261(\034E\211\230#\326\374\267\311O\211E(\234\325n\306NY\004\a\200\n\004\202z\270\236\261\330m\275\005\f\202g\260g\376", :standard => "\273\367\324\032\3762\334\026\r\246\342\022\016\325\024\270.\273\005\314\036\312\211\261\037A\361\362:W\316\352K\204\216b\2124>A\265g\331\177\233dK\251yC\272\314@\301\n\346$\223\367\r\026\366\375(i'\212\351", }, "hmac-sha1-96" => { false => "\240\016\243k]0\330\253\030\320\334\261(\034E\211\230#\326\374\267\311O\211E(\234\325n\306NY\004\a\200\n\004\202z\270\236\261\330m", :standard => "\273\367\324\032\3762\334\026\r\246\342\022\016\325\024\270.\273\005\314\036\312\211\261\037A\361\362:W\316\352K\204\216b\2124>A\265g\331\177\233dK\251yC\272\314@\301\n\346$\223\367\r", }, "hmac-ripemd160" => { false => "\240\016\243k]0\330\253\030\320\334\261(\034E\211\230#\326\374\267\311O\211E(\234\325n\306NYF\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "\273\367\324\032\3762\334\026\r\246\342\022\016\325\024\270.\273\005\314\036\312\211\261\037A\361\362:W\316\352K\204\216b\2124>A\265g\331\177\233dK\251\3044\024\343q\356\023\032\262\201\e9\213d\265>^{\300\320", }, "hmac-ripemd160@openssh.com" => { false => "\240\016\243k]0\330\253\030\320\334\261(\034E\211\230#\326\374\267\311O\211E(\234\325n\306NYF\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "\273\367\324\032\3762\334\026\r\246\342\022\016\325\024\270.\273\005\314\036\312\211\261\037A\361\362:W\316\352K\204\216b\2124>A\265g\331\177\233dK\251\3044\024\343q\356\023\032\262\201\e9\213d\265>^{\300\320", }, "none" => { false => "\240\016\243k]0\330\253\030\320\334\261(\034E\211\230#\326\374\267\311O\211E(\234\325n\306NY", :standard => "\273\367\324\032\3762\334\026\r\246\342\022\016\325\024\270.\273\005\314\036\312\211\261\037A\361\362:W\316\352K\204\216b\2124>A\265g\331\177\233dK\251", }, }, "aes192-cbc" => { "hmac-md5" => { false => "P$\377\302\326\262\276\215\206\343&\257#\315>Mp\232P\345o\215\330\213\t\027\300\360\300\037\267\003\000\032w\312\t\306\374\271\345p\215\224\373\363\v\261", :standard => "se\347\230\026\311\212\250yH\241\302n\364:\276\270M=H1\317\222^\362\237D\225N\354:\343\205M\006[\313$U/yZ\330\235\032\307\320D-\345\b\025\242#\336P8\343\361\263\\\241\326\311", }, "hmac-md5-96" => { false => "P$\377\302\326\262\276\215\206\343&\257#\315>Mp\232P\345o\215\330\213\t\027\300\360\300\037\267\003\000\032w\312\t\306\374\271\345p\215\224", :standard => "se\347\230\026\311\212\250yH\241\302n\364:\276\270M=H1\317\222^\362\237D\225N\354:\343\205M\006[\313$U/yZ\330\235\032\307\320D-\345\b\025\242#\336P8\343\361\263", }, "hmac-sha1" => { false => "P$\377\302\326\262\276\215\206\343&\257#\315>Mp\232P\345o\215\330\213\t\027\300\360\300\037\267\003\004\a\200\n\004\202z\270\236\261\330m\275\005\f\202g\260g\376", :standard => "se\347\230\026\311\212\250yH\241\302n\364:\276\270M=H1\317\222^\362\237D\225N\354:\343\205M\006[\313$U/yZ\330\235\032\307\320DyC\272\314@\301\n\346$\223\367\r\026\366\375(i'\212\351", }, "hmac-sha1-96" => { false => "P$\377\302\326\262\276\215\206\343&\257#\315>Mp\232P\345o\215\330\213\t\027\300\360\300\037\267\003\004\a\200\n\004\202z\270\236\261\330m", :standard => "se\347\230\026\311\212\250yH\241\302n\364:\276\270M=H1\317\222^\362\237D\225N\354:\343\205M\006[\313$U/yZ\330\235\032\307\320DyC\272\314@\301\n\346$\223\367\r", }, "hmac-ripemd160" => { false => "P$\377\302\326\262\276\215\206\343&\257#\315>Mp\232P\345o\215\330\213\t\027\300\360\300\037\267\003F\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "se\347\230\026\311\212\250yH\241\302n\364:\276\270M=H1\317\222^\362\237D\225N\354:\343\205M\006[\313$U/yZ\330\235\032\307\320D\3044\024\343q\356\023\032\262\201\e9\213d\265>^{\300\320", }, "hmac-ripemd160@openssh.com" => { false => "P$\377\302\326\262\276\215\206\343&\257#\315>Mp\232P\345o\215\330\213\t\027\300\360\300\037\267\003F\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "se\347\230\026\311\212\250yH\241\302n\364:\276\270M=H1\317\222^\362\237D\225N\354:\343\205M\006[\313$U/yZ\330\235\032\307\320D\3044\024\343q\356\023\032\262\201\e9\213d\265>^{\300\320", }, "none" => { false => "P$\377\302\326\262\276\215\206\343&\257#\315>Mp\232P\345o\215\330\213\t\027\300\360\300\037\267\003", :standard => "se\347\230\026\311\212\250yH\241\302n\364:\276\270M=H1\317\222^\362\237D\225N\354:\343\205M\006[\313$U/yZ\330\235\032\307\320D", }, }, "aes256-cbc" => { "hmac-md5" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340\000\032w\312\t\306\374\271\345p\215\224\373\363\v\261", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365-\345\b\025\242#\336P8\343\361\263\\\241\326\311", }, "hmac-md5-96" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340\000\032w\312\t\306\374\271\345p\215\224", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365-\345\b\025\242#\336P8\343\361\263", }, "hmac-sha1" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340\004\a\200\n\004\202z\270\236\261\330m\275\005\f\202g\260g\376", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365yC\272\314@\301\n\346$\223\367\r\026\366\375(i'\212\351", }, "hmac-sha1-96" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340\004\a\200\n\004\202z\270\236\261\330m", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365yC\272\314@\301\n\346$\223\367\r", }, "hmac-ripemd160" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340F\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365\3044\024\343q\356\023\032\262\201\e9\213d\265>^{\300\320", }, "hmac-ripemd160@openssh.com" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340F\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365\3044\024\343q\356\023\032\262\201\e9\213d\265>^{\300\320", }, "none" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365", }, }, "blowfish-cbc" => { "hmac-md5" => { false => "vT\353\203\247\206L\255e\371\001 6B/\234g\332\371\224l\227\257\346\373E\237C2\212u)\000\032w\312\t\306\374\271\345p\215\224\373\363\v\261", :standard => "U\257\231e\347\274\bh\016X\232h\334\v\005\316e1G$-\367##\256$rW\000\210\335_\360\f\000\205#\370\201\006\354=g\361\271[E\265\217\316\314\b\202\235\226\334", }, "hmac-md5-96" => { false => "vT\353\203\247\206L\255e\371\001 6B/\234g\332\371\224l\227\257\346\373E\237C2\212u)\000\032w\312\t\306\374\271\345p\215\224", :standard => "U\257\231e\347\274\bh\016X\232h\334\v\005\316e1G$-\367##\256$rW\000\210\335_\360\f\000\205#\370\201\006\354=g\361\271[E\265\217\316\314\b", }, "hmac-sha1" => { false => "vT\353\203\247\206L\255e\371\001 6B/\234g\332\371\224l\227\257\346\373E\237C2\212u)\004\a\200\n\004\202z\270\236\261\330m\275\005\f\202g\260g\376", :standard => "U\257\231e\347\274\bh\016X\232h\334\v\005\316e1G$-\367##\256$rW\000\210\335_\360\f\000\205#\370\201\006\2117U\266\3444(\235\034\023\377\376\335\301\253rI\215W\311", }, "hmac-sha1-96" => { false => "vT\353\203\247\206L\255e\371\001 6B/\234g\332\371\224l\227\257\346\373E\237C2\212u)\004\a\200\n\004\202z\270\236\261\330m", :standard => "U\257\231e\347\274\bh\016X\232h\334\v\005\316e1G$-\367##\256$rW\000\210\335_\360\f\000\205#\370\201\006\2117U\266\3444(\235\034\023\377\376", }, "hmac-ripemd160" => { false => "vT\353\203\247\206L\255e\371\001 6B/\234g\332\371\224l\227\257\346\373E\237C2\212u)F\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "U\257\231e\347\274\bh\016X\232h\334\v\005\316e1G$-\367##\256$rW\000\210\335_\360\f\000\205#\370\201\006)U\275\003U\333\225\221Y)\317\256\240\246\0000\351\032\363Y", }, "hmac-ripemd160@openssh.com" => { false => "vT\353\203\247\206L\255e\371\001 6B/\234g\332\371\224l\227\257\346\373E\237C2\212u)F\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "U\257\231e\347\274\bh\016X\232h\334\v\005\316e1G$-\367##\256$rW\000\210\335_\360\f\000\205#\370\201\006)U\275\003U\333\225\221Y)\317\256\240\246\0000\351\032\363Y", }, "none" => { false => "vT\353\203\247\206L\255e\371\001 6B/\234g\332\371\224l\227\257\346\373E\237C2\212u)", :standard => "U\257\231e\347\274\bh\016X\232h\334\v\005\316e1G$-\367##\256$rW\000\210\335_\360\f\000\205#\370\201\006", }, }, "cast128-cbc" => { "hmac-md5" => { false => "\361\026\313!\31235|w~\n\261\257\277\e\277b\246b\342\333\eE\021N\345\343m\314\272\315\376\000\032w\312\t\306\374\271\345p\215\224\373\363\v\261", :standard => "\375i\253\004\311E\2011)\220$\251A\245\f(\371\263\314\242\353\260\272\367\276\"\031\224$\244\311W\307Oe\224\0017\336\325\354=g\361\271[E\265\217\316\314\b\202\235\226\334", }, "hmac-md5-96" => { false => "\361\026\313!\31235|w~\n\261\257\277\e\277b\246b\342\333\eE\021N\345\343m\314\272\315\376\000\032w\312\t\306\374\271\345p\215\224", :standard => "\375i\253\004\311E\2011)\220$\251A\245\f(\371\263\314\242\353\260\272\367\276\"\031\224$\244\311W\307Oe\224\0017\336\325\354=g\361\271[E\265\217\316\314\b", }, "hmac-sha1" => { false => "\361\026\313!\31235|w~\n\261\257\277\e\277b\246b\342\333\eE\021N\345\343m\314\272\315\376\004\a\200\n\004\202z\270\236\261\330m\275\005\f\202g\260g\376", :standard => "\375i\253\004\311E\2011)\220$\251A\245\f(\371\263\314\242\353\260\272\367\276\"\031\224$\244\311W\307Oe\224\0017\336\325\2117U\266\3444(\235\034\023\377\376\335\301\253rI\215W\311", }, "hmac-sha1-96" => { false => "\361\026\313!\31235|w~\n\261\257\277\e\277b\246b\342\333\eE\021N\345\343m\314\272\315\376\004\a\200\n\004\202z\270\236\261\330m", :standard => "\375i\253\004\311E\2011)\220$\251A\245\f(\371\263\314\242\353\260\272\367\276\"\031\224$\244\311W\307Oe\224\0017\336\325\2117U\266\3444(\235\034\023\377\376", }, "hmac-ripemd160" => { false => "\361\026\313!\31235|w~\n\261\257\277\e\277b\246b\342\333\eE\021N\345\343m\314\272\315\376F\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "\375i\253\004\311E\2011)\220$\251A\245\f(\371\263\314\242\353\260\272\367\276\"\031\224$\244\311W\307Oe\224\0017\336\325)U\275\003U\333\225\221Y)\317\256\240\246\0000\351\032\363Y", }, "hmac-ripemd160@openssh.com" => { false => "\361\026\313!\31235|w~\n\261\257\277\e\277b\246b\342\333\eE\021N\345\343m\314\272\315\376F\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "\375i\253\004\311E\2011)\220$\251A\245\f(\371\263\314\242\353\260\272\367\276\"\031\224$\244\311W\307Oe\224\0017\336\325)U\275\003U\333\225\221Y)\317\256\240\246\0000\351\032\363Y", }, "none" => { false => "\361\026\313!\31235|w~\n\261\257\277\e\277b\246b\342\333\eE\021N\345\343m\314\272\315\376", :standard => "\375i\253\004\311E\2011)\220$\251A\245\f(\371\263\314\242\353\260\272\367\276\"\031\224$\244\311W\307Oe\224\0017\336\325", }, }, "idea-cbc" => { "hmac-md5" => { false => "\342\255\202$\273\201\025#\245\2341F\263\005@{\000<\266&s\016\251NH=J\322/\220 H\000\032w\312\t\306\374\271\345p\215\224\373\363\v\261", :standard => "F\3048\360\357\265\215I\021)\a\254/\315%\354M\004\330\006\356\vFr\250K\225\223x\277+Q)\022\327\311K\025\322\317\354=g\361\271[E\265\217\316\314\b\202\235\226\334", }, "hmac-md5-96" => { false => "\342\255\202$\273\201\025#\245\2341F\263\005@{\000<\266&s\016\251NH=J\322/\220 H\000\032w\312\t\306\374\271\345p\215\224", :standard => "F\3048\360\357\265\215I\021)\a\254/\315%\354M\004\330\006\356\vFr\250K\225\223x\277+Q)\022\327\311K\025\322\317\354=g\361\271[E\265\217\316\314\b", }, "hmac-sha1" => { false => "\342\255\202$\273\201\025#\245\2341F\263\005@{\000<\266&s\016\251NH=J\322/\220 H\004\a\200\n\004\202z\270\236\261\330m\275\005\f\202g\260g\376", :standard => "F\3048\360\357\265\215I\021)\a\254/\315%\354M\004\330\006\356\vFr\250K\225\223x\277+Q)\022\327\311K\025\322\317\2117U\266\3444(\235\034\023\377\376\335\301\253rI\215W\311", }, "hmac-sha1-96" => { false => "\342\255\202$\273\201\025#\245\2341F\263\005@{\000<\266&s\016\251NH=J\322/\220 H\004\a\200\n\004\202z\270\236\261\330m", :standard => "F\3048\360\357\265\215I\021)\a\254/\315%\354M\004\330\006\356\vFr\250K\225\223x\277+Q)\022\327\311K\025\322\317\2117U\266\3444(\235\034\023\377\376", }, "hmac-ripemd160" => { false => "\342\255\202$\273\201\025#\245\2341F\263\005@{\000<\266&s\016\251NH=J\322/\220 HF\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "F\3048\360\357\265\215I\021)\a\254/\315%\354M\004\330\006\356\vFr\250K\225\223x\277+Q)\022\327\311K\025\322\317)U\275\003U\333\225\221Y)\317\256\240\246\0000\351\032\363Y", }, "hmac-ripemd160@openssh.com" => { false => "\342\255\202$\273\201\025#\245\2341F\263\005@{\000<\266&s\016\251NH=J\322/\220 HF\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "F\3048\360\357\265\215I\021)\a\254/\315%\354M\004\330\006\356\vFr\250K\225\223x\277+Q)\022\327\311K\025\322\317)U\275\003U\333\225\221Y)\317\256\240\246\0000\351\032\363Y", }, "none" => { false => "\342\255\202$\273\201\025#\245\2341F\263\005@{\000<\266&s\016\251NH=J\322/\220 H", :standard => "F\3048\360\357\265\215I\021)\a\254/\315%\354M\004\330\006\356\vFr\250K\225\223x\277+Q)\022\327\311K\025\322\317", }, }, "arcfour128" => { "hmac-md5" => { false => "e_\204\037\366\363>\024\263q\025\334\354AO.\026t\231nvD\030\226\234\263\257\335:\001\300\255\000\032w\312\t\306\374\271\345p\215\224\373\363\v\261", :standard => "e_\204'\367\217\243v\322\025|\330ios\004[P\270\306\272\017\037\344\214\253\354\272m\261\217/jW'V\277\341U\224\354=g\361\271[E\265\217\316\314\b\202\235\226\334", }, "hmac-md5-96" => { false => "e_\204\037\366\363>\024\263q\025\334\354AO.\026t\231nvD\030\226\234\263\257\335:\001\300\255\000\032w\312\t\306\374\271\345p\215\224", :standard => "e_\204'\367\217\243v\322\025|\330ios\004[P\270\306\272\017\037\344\214\253\354\272m\261\217/jW'V\277\341U\224\354=g\361\271[E\265\217\316\314\b", }, "hmac-sha1" => { false => "e_\204\037\366\363>\024\263q\025\334\354AO.\026t\231nvD\030\226\234\263\257\335:\001\300\255\004\a\200\n\004\202z\270\236\261\330m\275\005\f\202g\260g\376", :standard => "e_\204'\367\217\243v\322\025|\330ios\004[P\270\306\272\017\037\344\214\253\354\272m\261\217/jW'V\277\341U\224\2117U\266\3444(\235\034\023\377\376\335\301\253rI\215W\311", }, "hmac-sha1-96" => { false => "e_\204\037\366\363>\024\263q\025\334\354AO.\026t\231nvD\030\226\234\263\257\335:\001\300\255\004\a\200\n\004\202z\270\236\261\330m", :standard => "e_\204'\367\217\243v\322\025|\330ios\004[P\270\306\272\017\037\344\214\253\354\272m\261\217/jW'V\277\341U\224\2117U\266\3444(\235\034\023\377\376", }, "hmac-ripemd160" => { false => "e_\204\037\366\363>\024\263q\025\334\354AO.\026t\231nvD\030\226\234\263\257\335:\001\300\255F\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "e_\204'\367\217\243v\322\025|\330ios\004[P\270\306\272\017\037\344\214\253\354\272m\261\217/jW'V\277\341U\224)U\275\003U\333\225\221Y)\317\256\240\246\0000\351\032\363Y", }, "hmac-ripemd160@openssh.com" => { false => "e_\204\037\366\363>\024\263q\025\334\354AO.\026t\231nvD\030\226\234\263\257\335:\001\300\255F\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "e_\204'\367\217\243v\322\025|\330ios\004[P\270\306\272\017\037\344\214\253\354\272m\261\217/jW'V\277\341U\224)U\275\003U\333\225\221Y)\317\256\240\246\0000\351\032\363Y", }, "none" => { false => "e_\204\037\366\363>\024\263q\025\334\354AO.\026t\231nvD\030\226\234\263\257\335:\001\300\255", :standard => "e_\204'\367\217\243v\322\025|\330ios\004[P\270\306\272\017\037\344\214\253\354\272m\261\217/jW'V\277\341U\224", }, }, "arcfour256" => { "hmac-md5" => { false => "B\374\256V\035b\337\215\305h\031bE\271\312\361\017T+\302\024x\3016\315g%\032\331\004fr\000\032w\312\t\306\374\271\345p\215\224\373\363\v\261", :standard => "B\374\256n\034\036B\357\244\fpf\300\227\366\333Bp\nj\3303\306D\335\177f}\216\264)\360\325jU^M\357$\221\354=g\361\271[E\265\217\316\314\b\202\235\226\334", }, "hmac-md5-96" => { false => "B\374\256V\035b\337\215\305h\031bE\271\312\361\017T+\302\024x\3016\315g%\032\331\004fr\000\032w\312\t\306\374\271\345p\215\224", :standard => "B\374\256n\034\036B\357\244\fpf\300\227\366\333Bp\nj\3303\306D\335\177f}\216\264)\360\325jU^M\357$\221\354=g\361\271[E\265\217\316\314\b", }, "hmac-sha1" => { false => "B\374\256V\035b\337\215\305h\031bE\271\312\361\017T+\302\024x\3016\315g%\032\331\004fr\004\a\200\n\004\202z\270\236\261\330m\275\005\f\202g\260g\376", :standard => "B\374\256n\034\036B\357\244\fpf\300\227\366\333Bp\nj\3303\306D\335\177f}\216\264)\360\325jU^M\357$\221\2117U\266\3444(\235\034\023\377\376\335\301\253rI\215W\311", }, "hmac-sha1-96" => { false => "B\374\256V\035b\337\215\305h\031bE\271\312\361\017T+\302\024x\3016\315g%\032\331\004fr\004\a\200\n\004\202z\270\236\261\330m", :standard => "B\374\256n\034\036B\357\244\fpf\300\227\366\333Bp\nj\3303\306D\335\177f}\216\264)\360\325jU^M\357$\221\2117U\266\3444(\235\034\023\377\376", }, "hmac-ripemd160" => { false => "B\374\256V\035b\337\215\305h\031bE\271\312\361\017T+\302\024x\3016\315g%\032\331\004frF\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "B\374\256n\034\036B\357\244\fpf\300\227\366\333Bp\nj\3303\306D\335\177f}\216\264)\360\325jU^M\357$\221)U\275\003U\333\225\221Y)\317\256\240\246\0000\351\032\363Y", }, "hmac-ripemd160@openssh.com" => { false => "B\374\256V\035b\337\215\305h\031bE\271\312\361\017T+\302\024x\3016\315g%\032\331\004frF\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "B\374\256n\034\036B\357\244\fpf\300\227\366\333Bp\nj\3303\306D\335\177f}\216\264)\360\325jU^M\357$\221)U\275\003U\333\225\221Y)\317\256\240\246\0000\351\032\363Y", }, "none" => { false => "B\374\256V\035b\337\215\305h\031bE\271\312\361\017T+\302\024x\3016\315g%\032\331\004fr", :standard => "B\374\256n\034\036B\357\244\fpf\300\227\366\333Bp\nj\3303\306D\335\177f}\216\264)\360\325jU^M\357$\221", }, }, "arcfour512" => { "hmac-md5" => { false => "\n{\275\177Yw\307\f\277\221\247'\0318\237\223cR\340\361\356\017\357\235\342\374\005wL\267\330D\000\032w\312\t\306\374\271\345p\215\224\373\363\v\261", :standard => "\n{\275GX\vZn\336\365\316#\234\026\243\271.v\301Y\"D\350\357\362\344F\020\e\a\227\306\366\025:\246\2349\233\313\354=g\361\271[E\265\217\316\314\b\202\235\226\334", }, "hmac-md5-96" => { false => "\n{\275\177Yw\307\f\277\221\247'\0318\237\223cR\340\361\356\017\357\235\342\374\005wL\267\330D\000\032w\312\t\306\374\271\345p\215\224", :standard => "\n{\275GX\vZn\336\365\316#\234\026\243\271.v\301Y\"D\350\357\362\344F\020\e\a\227\306\366\025:\246\2349\233\313\354=g\361\271[E\265\217\316\314\b", }, "hmac-sha1" => { false => "\n{\275\177Yw\307\f\277\221\247'\0318\237\223cR\340\361\356\017\357\235\342\374\005wL\267\330D\004\a\200\n\004\202z\270\236\261\330m\275\005\f\202g\260g\376", :standard => "\n{\275GX\vZn\336\365\316#\234\026\243\271.v\301Y\"D\350\357\362\344F\020\e\a\227\306\366\025:\246\2349\233\313\2117U\266\3444(\235\034\023\377\376\335\301\253rI\215W\311", }, "hmac-sha1-96" => { false => "\n{\275\177Yw\307\f\277\221\247'\0318\237\223cR\340\361\356\017\357\235\342\374\005wL\267\330D\004\a\200\n\004\202z\270\236\261\330m", :standard => "\n{\275GX\vZn\336\365\316#\234\026\243\271.v\301Y\"D\350\357\362\344F\020\e\a\227\306\366\025:\246\2349\233\313\2117U\266\3444(\235\034\023\377\376", }, "hmac-ripemd160" => { false => "\n{\275\177Yw\307\f\277\221\247'\0318\237\223cR\340\361\356\017\357\235\342\374\005wL\267\330DF\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "\n{\275GX\vZn\336\365\316#\234\026\243\271.v\301Y\"D\350\357\362\344F\020\e\a\227\306\366\025:\246\2349\233\313)U\275\003U\333\225\221Y)\317\256\240\246\0000\351\032\363Y", }, "hmac-ripemd160@openssh.com" => { false => "\n{\275\177Yw\307\f\277\221\247'\0318\237\223cR\340\361\356\017\357\235\342\374\005wL\267\330DF\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "\n{\275GX\vZn\336\365\316#\234\026\243\271.v\301Y\"D\350\357\362\344F\020\e\a\227\306\366\025:\246\2349\233\313)U\275\003U\333\225\221Y)\317\256\240\246\0000\351\032\363Y", }, "none" => { false => "\n{\275\177Yw\307\f\277\221\247'\0318\237\223cR\340\361\356\017\357\235\342\374\005wL\267\330D", :standard => "\n{\275GX\vZn\336\365\316#\234\026\243\271.v\301Y\"D\350\357\362\344F\020\e\a\227\306\366\025:\246\2349\233\313", }, }, "3des-ctr" => { "hmac-md5" => { false => "\xED#\x86\xD5\xE1mP\v\f\xB9\xC1\xE6\xFD\xA0~,\xD3\x13\x12\x8Cp\xD4F\x92\xCB\xB6R>\xFA]\x9B\xB1\x00\x1Aw\xCA\t\xC6\xFC\xB9\xE5p\x8D\x94\xFB\xF3\v\xB1", :standard => "\xED#\x86\xED\xE0\x11\xCDim\xDD\xA8\xE2x\x8EB\x06\x9E73$\xBC\x9FA\xE0\xDB\xAE\x11Y\xAD\xED\xD43\x86N\x89\xFE\x14V\x91B\xEC=g\xF1\xB9[E\xB5\x8F\xCE\xCC\b\x82\x9D\x96\xDC", }, "hmac-md5-96" => { false => "\xED#\x86\xD5\xE1mP\v\f\xB9\xC1\xE6\xFD\xA0~,\xD3\x13\x12\x8Cp\xD4F\x92\xCB\xB6R>\xFA]\x9B\xB1\x00\x1Aw\xCA\t\xC6\xFC\xB9\xE5p\x8D\x94", :standard => "\xED#\x86\xED\xE0\x11\xCDim\xDD\xA8\xE2x\x8EB\x06\x9E73$\xBC\x9FA\xE0\xDB\xAE\x11Y\xAD\xED\xD43\x86N\x89\xFE\x14V\x91B\xEC=g\xF1\xB9[E\xB5\x8F\xCE\xCC\b", }, "hmac-sha1" => { false => "\xED#\x86\xD5\xE1mP\v\f\xB9\xC1\xE6\xFD\xA0~,\xD3\x13\x12\x8Cp\xD4F\x92\xCB\xB6R>\xFA]\x9B\xB1\x04\a\x80\n\x04\x82z\xB8\x9E\xB1\xD8m\xBD\x05\f\x82g\xB0g\xFE", :standard => "\xED#\x86\xED\xE0\x11\xCDim\xDD\xA8\xE2x\x8EB\x06\x9E73$\xBC\x9FA\xE0\xDB\xAE\x11Y\xAD\xED\xD43\x86N\x89\xFE\x14V\x91B\x897U\xB6\xE44(\x9D\x1C\x13\xFF\xFE\xDD\xC1\xABrI\x8DW\xC9", }, "hmac-sha1-96" => { false => "\xED#\x86\xD5\xE1mP\v\f\xB9\xC1\xE6\xFD\xA0~,\xD3\x13\x12\x8Cp\xD4F\x92\xCB\xB6R>\xFA]\x9B\xB1\x04\a\x80\n\x04\x82z\xB8\x9E\xB1\xD8m", :standard => "\xED#\x86\xED\xE0\x11\xCDim\xDD\xA8\xE2x\x8EB\x06\x9E73$\xBC\x9FA\xE0\xDB\xAE\x11Y\xAD\xED\xD43\x86N\x89\xFE\x14V\x91B\x897U\xB6\xE44(\x9D\x1C\x13\xFF\xFE", }, "hmac-ripemd160" => { false => "\xED#\x86\xD5\xE1mP\v\f\xB9\xC1\xE6\xFD\xA0~,\xD3\x13\x12\x8Cp\xD4F\x92\xCB\xB6R>\xFA]\x9B\xB1F\xC3\xC7\x87\xA5\x86\xD5~\xCD(\xF8\xD9\xCB\xC5\vHI\xCAL\x8E", :standard => "\xED#\x86\xED\xE0\x11\xCDim\xDD\xA8\xE2x\x8EB\x06\x9E73$\xBC\x9FA\xE0\xDB\xAE\x11Y\xAD\xED\xD43\x86N\x89\xFE\x14V\x91B)U\xBD\x03U\xDB\x95\x91Y)\xCF\xAE\xA0\xA6\x000\xE9\x1A\xF3Y", }, "hmac-ripemd160@openssh.com" => { false => "\xED#\x86\xD5\xE1mP\v\f\xB9\xC1\xE6\xFD\xA0~,\xD3\x13\x12\x8Cp\xD4F\x92\xCB\xB6R>\xFA]\x9B\xB1F\xC3\xC7\x87\xA5\x86\xD5~\xCD(\xF8\xD9\xCB\xC5\vHI\xCAL\x8E", :standard => "\xED#\x86\xED\xE0\x11\xCDim\xDD\xA8\xE2x\x8EB\x06\x9E73$\xBC\x9FA\xE0\xDB\xAE\x11Y\xAD\xED\xD43\x86N\x89\xFE\x14V\x91B)U\xBD\x03U\xDB\x95\x91Y)\xCF\xAE\xA0\xA6\x000\xE9\x1A\xF3Y", }, "none" => { false => "\xED#\x86\xD5\xE1mP\v\f\xB9\xC1\xE6\xFD\xA0~,\xD3\x13\x12\x8Cp\xD4F\x92\xCB\xB6R>\xFA]\x9B\xB1", :standard => "\xED#\x86\xED\xE0\x11\xCDim\xDD\xA8\xE2x\x8EB\x06\x9E73$\xBC\x9FA\xE0\xDB\xAE\x11Y\xAD\xED\xD43\x86N\x89\xFE\x14V\x91B", }, }, "blowfish-ctr" => { "hmac-md5" => { false => "\xF7gk6\xB8\xACK\x1D\xC4Ls\xB0{\x0F\xC7\xC4M\xC5>\xF6G8\xD4\xBCu\x152FoJ\xB0\xC0\x00\x1Aw\xCA\t\xC6\xFC\xB9\xE5p\x8D\x94\xFB\xF3\v\xB1", :standard => "\xF7gk\x0E\xB9\xD0\xD6\x7F\xA5(\x1A\xB4\xFE!\xFB\xEE\x00\xE1\x1F^\x8Bs\xD3\xCEe\rq!8\xFA\xFFB\r\xE9\xFC\xF6\xCA\xBC\x03\xA9\xEC=g\xF1\xB9[E\xB5\x8F\xCE\xCC\b\x82\x9D\x96\xDC", }, "hmac-md5-96" => { false => "\xF7gk6\xB8\xACK\x1D\xC4Ls\xB0{\x0F\xC7\xC4M\xC5>\xF6G8\xD4\xBCu\x152FoJ\xB0\xC0\x00\x1Aw\xCA\t\xC6\xFC\xB9\xE5p\x8D\x94", :standard => "\xF7gk\x0E\xB9\xD0\xD6\x7F\xA5(\x1A\xB4\xFE!\xFB\xEE\x00\xE1\x1F^\x8Bs\xD3\xCEe\rq!8\xFA\xFFB\r\xE9\xFC\xF6\xCA\xBC\x03\xA9\xEC=g\xF1\xB9[E\xB5\x8F\xCE\xCC\b", }, "hmac-sha1" => { false => "\xF7gk6\xB8\xACK\x1D\xC4Ls\xB0{\x0F\xC7\xC4M\xC5>\xF6G8\xD4\xBCu\x152FoJ\xB0\xC0\x04\a\x80\n\x04\x82z\xB8\x9E\xB1\xD8m\xBD\x05\f\x82g\xB0g\xFE", :standard => "\xF7gk\x0E\xB9\xD0\xD6\x7F\xA5(\x1A\xB4\xFE!\xFB\xEE\x00\xE1\x1F^\x8Bs\xD3\xCEe\rq!8\xFA\xFFB\r\xE9\xFC\xF6\xCA\xBC\x03\xA9\x897U\xB6\xE44(\x9D\x1C\x13\xFF\xFE\xDD\xC1\xABrI\x8DW\xC9", }, "hmac-sha1-96" => { false => "\xF7gk6\xB8\xACK\x1D\xC4Ls\xB0{\x0F\xC7\xC4M\xC5>\xF6G8\xD4\xBCu\x152FoJ\xB0\xC0\x04\a\x80\n\x04\x82z\xB8\x9E\xB1\xD8m", :standard => "\xF7gk\x0E\xB9\xD0\xD6\x7F\xA5(\x1A\xB4\xFE!\xFB\xEE\x00\xE1\x1F^\x8Bs\xD3\xCEe\rq!8\xFA\xFFB\r\xE9\xFC\xF6\xCA\xBC\x03\xA9\x897U\xB6\xE44(\x9D\x1C\x13\xFF\xFE", }, "hmac-ripemd160" => { false => "\xF7gk6\xB8\xACK\x1D\xC4Ls\xB0{\x0F\xC7\xC4M\xC5>\xF6G8\xD4\xBCu\x152FoJ\xB0\xC0F\xC3\xC7\x87\xA5\x86\xD5~\xCD(\xF8\xD9\xCB\xC5\vHI\xCAL\x8E", :standard => "\xF7gk\x0E\xB9\xD0\xD6\x7F\xA5(\x1A\xB4\xFE!\xFB\xEE\x00\xE1\x1F^\x8Bs\xD3\xCEe\rq!8\xFA\xFFB\r\xE9\xFC\xF6\xCA\xBC\x03\xA9)U\xBD\x03U\xDB\x95\x91Y)\xCF\xAE\xA0\xA6\x000\xE9\x1A\xF3Y", }, "hmac-ripemd160@openssh.com" => { false => "\xF7gk6\xB8\xACK\x1D\xC4Ls\xB0{\x0F\xC7\xC4M\xC5>\xF6G8\xD4\xBCu\x152FoJ\xB0\xC0F\xC3\xC7\x87\xA5\x86\xD5~\xCD(\xF8\xD9\xCB\xC5\vHI\xCAL\x8E", :standard => "\xF7gk\x0E\xB9\xD0\xD6\x7F\xA5(\x1A\xB4\xFE!\xFB\xEE\x00\xE1\x1F^\x8Bs\xD3\xCEe\rq!8\xFA\xFFB\r\xE9\xFC\xF6\xCA\xBC\x03\xA9)U\xBD\x03U\xDB\x95\x91Y)\xCF\xAE\xA0\xA6\x000\xE9\x1A\xF3Y", }, "none" => { false => "\xF7gk6\xB8\xACK\x1D\xC4Ls\xB0{\x0F\xC7\xC4M\xC5>\xF6G8\xD4\xBCu\x152FoJ\xB0\xC0", :standard => "\xF7gk\x0E\xB9\xD0\xD6\x7F\xA5(\x1A\xB4\xFE!\xFB\xEE\x00\xE1\x1F^\x8Bs\xD3\xCEe\rq!8\xFA\xFFB\r\xE9\xFC\xF6\xCA\xBC\x03\xA9", }, }, "aes128-ctr" => { "hmac-md5" => { false => "\xD6\x98\xC1n+6\xCA`s2\x06\xAA\x80\xFA\xF3\xF6\xCA\xF9\xC8[BB\xDC\x9F\xDC$\x88*\xA7\x00\x8E\xFD\x00\x1Aw\xCA\t\xC6\xFC\xB9\xE5p\x8D\x94\xFB\xF3\v\xB1", :standard => "\xD6\x98\xC1^2JW\x02\x12Vo\xAE\x05\xD4\xCF\xDC\x87\xDD\xE9\xF3\x8E\t\xDB\xED\xCC<\xCBM\xF0\xB0\xC1\x7F\xD7\x17\x931\xBC~\r\xF2\x87\xB89\x9B\x8B\xB3\x8E\x15-\xE5\b\x15\xA2#\xDEP8\xE3\xF1\xB3\\\xA1\xD6\xC9", }, "hmac-md5-96" => { false => "\xD6\x98\xC1n+6\xCA`s2\x06\xAA\x80\xFA\xF3\xF6\xCA\xF9\xC8[BB\xDC\x9F\xDC$\x88*\xA7\x00\x8E\xFD\x00\x1Aw\xCA\t\xC6\xFC\xB9\xE5p\x8D\x94", :standard => "\xD6\x98\xC1^2JW\x02\x12Vo\xAE\x05\xD4\xCF\xDC\x87\xDD\xE9\xF3\x8E\t\xDB\xED\xCC<\xCBM\xF0\xB0\xC1\x7F\xD7\x17\x931\xBC~\r\xF2\x87\xB89\x9B\x8B\xB3\x8E\x15-\xE5\b\x15\xA2#\xDEP8\xE3\xF1\xB3", }, "hmac-sha1" => { false => "\xD6\x98\xC1n+6\xCA`s2\x06\xAA\x80\xFA\xF3\xF6\xCA\xF9\xC8[BB\xDC\x9F\xDC$\x88*\xA7\x00\x8E\xFD\x04\a\x80\n\x04\x82z\xB8\x9E\xB1\xD8m\xBD\x05\f\x82g\xB0g\xFE", :standard => "\xD6\x98\xC1^2JW\x02\x12Vo\xAE\x05\xD4\xCF\xDC\x87\xDD\xE9\xF3\x8E\t\xDB\xED\xCC<\xCBM\xF0\xB0\xC1\x7F\xD7\x17\x931\xBC~\r\xF2\x87\xB89\x9B\x8B\xB3\x8E\x15yC\xBA\xCC@\xC1\n\xE6$\x93\xF7\r\x16\xF6\xFD(i'\x8A\xE9", }, "hmac-sha1-96" => { false => "\xD6\x98\xC1n+6\xCA`s2\x06\xAA\x80\xFA\xF3\xF6\xCA\xF9\xC8[BB\xDC\x9F\xDC$\x88*\xA7\x00\x8E\xFD\x04\a\x80\n\x04\x82z\xB8\x9E\xB1\xD8m", :standard => "\xD6\x98\xC1^2JW\x02\x12Vo\xAE\x05\xD4\xCF\xDC\x87\xDD\xE9\xF3\x8E\t\xDB\xED\xCC<\xCBM\xF0\xB0\xC1\x7F\xD7\x17\x931\xBC~\r\xF2\x87\xB89\x9B\x8B\xB3\x8E\x15yC\xBA\xCC@\xC1\n\xE6$\x93\xF7\r", }, "hmac-ripemd160" => { false => "\xD6\x98\xC1n+6\xCA`s2\x06\xAA\x80\xFA\xF3\xF6\xCA\xF9\xC8[BB\xDC\x9F\xDC$\x88*\xA7\x00\x8E\xFDF\xC3\xC7\x87\xA5\x86\xD5~\xCD(\xF8\xD9\xCB\xC5\vHI\xCAL\x8E", :standard => "\xD6\x98\xC1^2JW\x02\x12Vo\xAE\x05\xD4\xCF\xDC\x87\xDD\xE9\xF3\x8E\t\xDB\xED\xCC<\xCBM\xF0\xB0\xC1\x7F\xD7\x17\x931\xBC~\r\xF2\x87\xB89\x9B\x8B\xB3\x8E\x15\xC44\x14\xE3q\xEE\x13\x1A\xB2\x81\e9\x8Bd\xB5>^{\xC0\xD0", }, "hmac-ripemd160@openssh.com" => { false => "\xD6\x98\xC1n+6\xCA`s2\x06\xAA\x80\xFA\xF3\xF6\xCA\xF9\xC8[BB\xDC\x9F\xDC$\x88*\xA7\x00\x8E\xFDF\xC3\xC7\x87\xA5\x86\xD5~\xCD(\xF8\xD9\xCB\xC5\vHI\xCAL\x8E", :standard => "\xD6\x98\xC1^2JW\x02\x12Vo\xAE\x05\xD4\xCF\xDC\x87\xDD\xE9\xF3\x8E\t\xDB\xED\xCC<\xCBM\xF0\xB0\xC1\x7F\xD7\x17\x931\xBC~\r\xF2\x87\xB89\x9B\x8B\xB3\x8E\x15\xC44\x14\xE3q\xEE\x13\x1A\xB2\x81\e9\x8Bd\xB5>^{\xC0\xD0", }, "none" => { false => "\xD6\x98\xC1n+6\xCA`s2\x06\xAA\x80\xFA\xF3\xF6\xCA\xF9\xC8[BB\xDC\x9F\xDC$\x88*\xA7\x00\x8E\xFD", :standard => "\xD6\x98\xC1^2JW\x02\x12Vo\xAE\x05\xD4\xCF\xDC\x87\xDD\xE9\xF3\x8E\t\xDB\xED\xCC<\xCBM\xF0\xB0\xC1\x7F\xD7\x17\x931\xBC~\r\xF2\x87\xB89\x9B\x8B\xB3\x8E\x15", }, }, "aes192-ctr" => { "hmac-md5" => { false => "\xA8\x02\xB4-\xFBYo4F\"\xCF\xB8\x92\xF08\xAC\xE8\xECk\xECO\xE7\xF8\x01\xF8\xB0\x9E\x05\xFB\xA7\xA7\x91\x00\x1Aw\xCA\t\xC6\xFC\xB9\xE5p\x8D\x94\xFB\xF3\v\xB1", :standard => "\xA8\x02\xB4\x1D\xE2%\xF2V'F\xA6\xBC\x17\xDE\x04\x86\xA5\xC8JD\x83\xAC\xFFs\xE8\xA8\xDDb\xAC\x17\xE8\x13\x92V\x9E\x00!\x1F\xD4\x00\x92T\x15\xDE\xA4\xCA\xE9\xC1-\xE5\b\x15\xA2#\xDEP8\xE3\xF1\xB3\\\xA1\xD6\xC9", }, "hmac-md5-96" => { false => "\xA8\x02\xB4-\xFBYo4F\"\xCF\xB8\x92\xF08\xAC\xE8\xECk\xECO\xE7\xF8\x01\xF8\xB0\x9E\x05\xFB\xA7\xA7\x91\x00\x1Aw\xCA\t\xC6\xFC\xB9\xE5p\x8D\x94", :standard => "\xA8\x02\xB4\x1D\xE2%\xF2V'F\xA6\xBC\x17\xDE\x04\x86\xA5\xC8JD\x83\xAC\xFFs\xE8\xA8\xDDb\xAC\x17\xE8\x13\x92V\x9E\x00!\x1F\xD4\x00\x92T\x15\xDE\xA4\xCA\xE9\xC1-\xE5\b\x15\xA2#\xDEP8\xE3\xF1\xB3", }, "hmac-sha1" => { false => "\xA8\x02\xB4-\xFBYo4F\"\xCF\xB8\x92\xF08\xAC\xE8\xECk\xECO\xE7\xF8\x01\xF8\xB0\x9E\x05\xFB\xA7\xA7\x91\x04\a\x80\n\x04\x82z\xB8\x9E\xB1\xD8m\xBD\x05\f\x82g\xB0g\xFE", :standard => "\xA8\x02\xB4\x1D\xE2%\xF2V'F\xA6\xBC\x17\xDE\x04\x86\xA5\xC8JD\x83\xAC\xFFs\xE8\xA8\xDDb\xAC\x17\xE8\x13\x92V\x9E\x00!\x1F\xD4\x00\x92T\x15\xDE\xA4\xCA\xE9\xC1yC\xBA\xCC@\xC1\n\xE6$\x93\xF7\r\x16\xF6\xFD(i'\x8A\xE9", }, "hmac-sha1-96" => { false => "\xA8\x02\xB4-\xFBYo4F\"\xCF\xB8\x92\xF08\xAC\xE8\xECk\xECO\xE7\xF8\x01\xF8\xB0\x9E\x05\xFB\xA7\xA7\x91\x04\a\x80\n\x04\x82z\xB8\x9E\xB1\xD8m", :standard => "\xA8\x02\xB4\x1D\xE2%\xF2V'F\xA6\xBC\x17\xDE\x04\x86\xA5\xC8JD\x83\xAC\xFFs\xE8\xA8\xDDb\xAC\x17\xE8\x13\x92V\x9E\x00!\x1F\xD4\x00\x92T\x15\xDE\xA4\xCA\xE9\xC1yC\xBA\xCC@\xC1\n\xE6$\x93\xF7\r", }, "hmac-ripemd160" => { false => "\xA8\x02\xB4-\xFBYo4F\"\xCF\xB8\x92\xF08\xAC\xE8\xECk\xECO\xE7\xF8\x01\xF8\xB0\x9E\x05\xFB\xA7\xA7\x91F\xC3\xC7\x87\xA5\x86\xD5~\xCD(\xF8\xD9\xCB\xC5\vHI\xCAL\x8E", :standard => "\xA8\x02\xB4\x1D\xE2%\xF2V'F\xA6\xBC\x17\xDE\x04\x86\xA5\xC8JD\x83\xAC\xFFs\xE8\xA8\xDDb\xAC\x17\xE8\x13\x92V\x9E\x00!\x1F\xD4\x00\x92T\x15\xDE\xA4\xCA\xE9\xC1\xC44\x14\xE3q\xEE\x13\x1A\xB2\x81\e9\x8Bd\xB5>^{\xC0\xD0", }, "hmac-ripemd160@openssh.com" => { false => "\xA8\x02\xB4-\xFBYo4F\"\xCF\xB8\x92\xF08\xAC\xE8\xECk\xECO\xE7\xF8\x01\xF8\xB0\x9E\x05\xFB\xA7\xA7\x91F\xC3\xC7\x87\xA5\x86\xD5~\xCD(\xF8\xD9\xCB\xC5\vHI\xCAL\x8E", :standard => "\xA8\x02\xB4\x1D\xE2%\xF2V'F\xA6\xBC\x17\xDE\x04\x86\xA5\xC8JD\x83\xAC\xFFs\xE8\xA8\xDDb\xAC\x17\xE8\x13\x92V\x9E\x00!\x1F\xD4\x00\x92T\x15\xDE\xA4\xCA\xE9\xC1\xC44\x14\xE3q\xEE\x13\x1A\xB2\x81\e9\x8Bd\xB5>^{\xC0\xD0", }, "none" => { false => "\xA8\x02\xB4-\xFBYo4F\"\xCF\xB8\x92\xF08\xAC\xE8\xECk\xECO\xE7\xF8\x01\xF8\xB0\x9E\x05\xFB\xA7\xA7\x91", :standard => "\xA8\x02\xB4\x1D\xE2%\xF2V'F\xA6\xBC\x17\xDE\x04\x86\xA5\xC8JD\x83\xAC\xFFs\xE8\xA8\xDDb\xAC\x17\xE8\x13\x92V\x9E\x00!\x1F\xD4\x00\x92T\x15\xDE\xA4\xCA\xE9\xC1", }, }, "aes256-ctr" => { "hmac-md5" => { false => "M\x1DcA\r]\\\x95?&\xE3D[\xCC1\x9B\xE0\xAF\x96\xA8\x86Y\xBD\x16\xE5xR%u\xC9(\r\x00\x1Aw\xCA\t\xC6\xFC\xB9\xE5p\x8D\x94\xFB\xF3\v\xB1", :standard => "M\x1Dcq\x14!\xC1\xF7^B\x8A@\xDE\xE2\r\xB1\xAD\x8B\xB7\x00J\x12\xBAd\xF5`\x11B\"yg\x8F\x9F\xAB\xC8 d\xB4\xE7^w\xC4\x89\a\x17\x15\x82\n-\xE5\b\x15\xA2#\xDEP8\xE3\xF1\xB3\\\xA1\xD6\xC9", }, "hmac-md5-96" => { false => "M\x1DcA\r]\\\x95?&\xE3D[\xCC1\x9B\xE0\xAF\x96\xA8\x86Y\xBD\x16\xE5xR%u\xC9(\r\x00\x1Aw\xCA\t\xC6\xFC\xB9\xE5p\x8D\x94", :standard => "M\x1Dcq\x14!\xC1\xF7^B\x8A@\xDE\xE2\r\xB1\xAD\x8B\xB7\x00J\x12\xBAd\xF5`\x11B\"yg\x8F\x9F\xAB\xC8 d\xB4\xE7^w\xC4\x89\a\x17\x15\x82\n-\xE5\b\x15\xA2#\xDEP8\xE3\xF1\xB3", }, "hmac-sha1" => { false => "M\x1DcA\r]\\\x95?&\xE3D[\xCC1\x9B\xE0\xAF\x96\xA8\x86Y\xBD\x16\xE5xR%u\xC9(\r\x04\a\x80\n\x04\x82z\xB8\x9E\xB1\xD8m\xBD\x05\f\x82g\xB0g\xFE", :standard => "M\x1Dcq\x14!\xC1\xF7^B\x8A@\xDE\xE2\r\xB1\xAD\x8B\xB7\x00J\x12\xBAd\xF5`\x11B\"yg\x8F\x9F\xAB\xC8 d\xB4\xE7^w\xC4\x89\a\x17\x15\x82\nyC\xBA\xCC@\xC1\n\xE6$\x93\xF7\r\x16\xF6\xFD(i'\x8A\xE9", }, "hmac-sha1-96" => { false => "M\x1DcA\r]\\\x95?&\xE3D[\xCC1\x9B\xE0\xAF\x96\xA8\x86Y\xBD\x16\xE5xR%u\xC9(\r\x04\a\x80\n\x04\x82z\xB8\x9E\xB1\xD8m", :standard => "M\x1Dcq\x14!\xC1\xF7^B\x8A@\xDE\xE2\r\xB1\xAD\x8B\xB7\x00J\x12\xBAd\xF5`\x11B\"yg\x8F\x9F\xAB\xC8 d\xB4\xE7^w\xC4\x89\a\x17\x15\x82\nyC\xBA\xCC@\xC1\n\xE6$\x93\xF7\r", }, "hmac-ripemd160" => { false => "M\x1DcA\r]\\\x95?&\xE3D[\xCC1\x9B\xE0\xAF\x96\xA8\x86Y\xBD\x16\xE5xR%u\xC9(\rF\xC3\xC7\x87\xA5\x86\xD5~\xCD(\xF8\xD9\xCB\xC5\vHI\xCAL\x8E", :standard => "M\x1Dcq\x14!\xC1\xF7^B\x8A@\xDE\xE2\r\xB1\xAD\x8B\xB7\x00J\x12\xBAd\xF5`\x11B\"yg\x8F\x9F\xAB\xC8 d\xB4\xE7^w\xC4\x89\a\x17\x15\x82\n\xC44\x14\xE3q\xEE\x13\x1A\xB2\x81\e9\x8Bd\xB5>^{\xC0\xD0", }, "hmac-ripemd160@openssh.com" => { false => "M\x1DcA\r]\\\x95?&\xE3D[\xCC1\x9B\xE0\xAF\x96\xA8\x86Y\xBD\x16\xE5xR%u\xC9(\rF\xC3\xC7\x87\xA5\x86\xD5~\xCD(\xF8\xD9\xCB\xC5\vHI\xCAL\x8E", :standard => "M\x1Dcq\x14!\xC1\xF7^B\x8A@\xDE\xE2\r\xB1\xAD\x8B\xB7\x00J\x12\xBAd\xF5`\x11B\"yg\x8F\x9F\xAB\xC8 d\xB4\xE7^w\xC4\x89\a\x17\x15\x82\n\xC44\x14\xE3q\xEE\x13\x1A\xB2\x81\e9\x8Bd\xB5>^{\xC0\xD0", }, "none" => { false => "M\x1DcA\r]\\\x95?&\xE3D[\xCC1\x9B\xE0\xAF\x96\xA8\x86Y\xBD\x16\xE5xR%u\xC9(\r", :standard => "M\x1Dcq\x14!\xC1\xF7^B\x8A@\xDE\xE2\r\xB1\xAD\x8B\xB7\x00J\x12\xBAd\xF5`\x11B\"yg\x8F\x9F\xAB\xC8 d\xB4\xE7^w\xC4\x89\a\x17\x15\x82\n", }, }, "cast128-ctr" => { "hmac-md5" => { false => "\x10\xA0cJ6W\xC9\xC7\x02\xF8\xCD\xE31\xF9\xE7n\x0Fj\x7F\x99\x8A\f\x84\x80\x80\xE8p\x9C\x14\x83\x1C\xC7\x00\x1Aw\xCA\t\xC6\xFC\xB9\xE5p\x8D\x94\xFB\xF3\v\xB1", :standard => "\x10\xA0cr7+T\xA5c\x9C\xA4\xE7\xB4\xD7\xDBDBN^1FG\x83\xF2\x90\xF03\xFBC3SE\xF7x;q\x89\xA80\xEA\xEC=g\xF1\xB9[E\xB5\x8F\xCE\xCC\b\x82\x9D\x96\xDC", }, "hmac-md5-96" => { false => "\x10\xA0cJ6W\xC9\xC7\x02\xF8\xCD\xE31\xF9\xE7n\x0Fj\x7F\x99\x8A\f\x84\x80\x80\xE8p\x9C\x14\x83\x1C\xC7\x00\x1Aw\xCA\t\xC6\xFC\xB9\xE5p\x8D\x94", :standard => "\x10\xA0cr7+T\xA5c\x9C\xA4\xE7\xB4\xD7\xDBDBN^1FG\x83\xF2\x90\xF03\xFBC3SE\xF7x;q\x89\xA80\xEA\xEC=g\xF1\xB9[E\xB5\x8F\xCE\xCC\b", }, "hmac-sha1" => { false => "\x10\xA0cJ6W\xC9\xC7\x02\xF8\xCD\xE31\xF9\xE7n\x0Fj\x7F\x99\x8A\f\x84\x80\x80\xE8p\x9C\x14\x83\x1C\xC7\x04\a\x80\n\x04\x82z\xB8\x9E\xB1\xD8m\xBD\x05\f\x82g\xB0g\xFE", :standard => "\x10\xA0cr7+T\xA5c\x9C\xA4\xE7\xB4\xD7\xDBDBN^1FG\x83\xF2\x90\xF03\xFBC3SE\xF7x;q\x89\xA80\xEA\x897U\xB6\xE44(\x9D\x1C\x13\xFF\xFE\xDD\xC1\xABrI\x8DW\xC9", }, "hmac-sha1-96" => { false => "\x10\xA0cJ6W\xC9\xC7\x02\xF8\xCD\xE31\xF9\xE7n\x0Fj\x7F\x99\x8A\f\x84\x80\x80\xE8p\x9C\x14\x83\x1C\xC7\x04\a\x80\n\x04\x82z\xB8\x9E\xB1\xD8m", :standard => "\x10\xA0cr7+T\xA5c\x9C\xA4\xE7\xB4\xD7\xDBDBN^1FG\x83\xF2\x90\xF03\xFBC3SE\xF7x;q\x89\xA80\xEA\x897U\xB6\xE44(\x9D\x1C\x13\xFF\xFE", }, "hmac-ripemd160" => { false => "\x10\xA0cJ6W\xC9\xC7\x02\xF8\xCD\xE31\xF9\xE7n\x0Fj\x7F\x99\x8A\f\x84\x80\x80\xE8p\x9C\x14\x83\x1C\xC7F\xC3\xC7\x87\xA5\x86\xD5~\xCD(\xF8\xD9\xCB\xC5\vHI\xCAL\x8E", :standard => "\x10\xA0cr7+T\xA5c\x9C\xA4\xE7\xB4\xD7\xDBDBN^1FG\x83\xF2\x90\xF03\xFBC3SE\xF7x;q\x89\xA80\xEA)U\xBD\x03U\xDB\x95\x91Y)\xCF\xAE\xA0\xA6\x000\xE9\x1A\xF3Y", }, "hmac-ripemd160@openssh.com" => { false => "\x10\xA0cJ6W\xC9\xC7\x02\xF8\xCD\xE31\xF9\xE7n\x0Fj\x7F\x99\x8A\f\x84\x80\x80\xE8p\x9C\x14\x83\x1C\xC7F\xC3\xC7\x87\xA5\x86\xD5~\xCD(\xF8\xD9\xCB\xC5\vHI\xCAL\x8E", :standard => "\x10\xA0cr7+T\xA5c\x9C\xA4\xE7\xB4\xD7\xDBDBN^1FG\x83\xF2\x90\xF03\xFBC3SE\xF7x;q\x89\xA80\xEA)U\xBD\x03U\xDB\x95\x91Y)\xCF\xAE\xA0\xA6\x000\xE9\x1A\xF3Y", }, "none" => { false => "\x10\xA0cJ6W\xC9\xC7\x02\xF8\xCD\xE31\xF9\xE7n\x0Fj\x7F\x99\x8A\f\x84\x80\x80\xE8p\x9C\x14\x83\x1C\xC7", :standard => "\x10\xA0cr7+T\xA5c\x9C\xA4\xE7\xB4\xD7\xDBDBN^1FG\x83\xF2\x90\xF03\xFBC3SE\xF7x;q\x89\xA80\xEA", }, }, "none" => { "hmac-md5" => { false => "\000\000\000\034\b\004\001\000\000\000\tdebugging\000\000\000\000\b\030CgWO\260\212\000\032w\312\t\306\374\271\345p\215\224\373\363\v\261", :standard => "\000\000\000$\tx\234bad``\340LIM*MO\317\314K\ar\030\000\000\000\000\377\377\b\030CgWO\260\212^\354=g\361\271[E\265\217\316\314\b\202\235\226\334", }, "hmac-md5-96" => { false => "\000\000\000\034\b\004\001\000\000\000\tdebugging\000\000\000\000\b\030CgWO\260\212\000\032w\312\t\306\374\271\345p\215\224", :standard => "\000\000\000$\tx\234bad``\340LIM*MO\317\314K\ar\030\000\000\000\000\377\377\b\030CgWO\260\212^\354=g\361\271[E\265\217\316\314\b", }, "hmac-sha1-96" => { false => "\000\000\000\034\b\004\001\000\000\000\tdebugging\000\000\000\000\b\030CgWO\260\212\004\a\200\n\004\202z\270\236\261\330m", :standard => "\000\000\000$\tx\234bad``\340LIM*MO\317\314K\ar\030\000\000\000\000\377\377\b\030CgWO\260\212^\2117U\266\3444(\235\034\023\377\376", }, "hmac-sha1" => { false => "\000\000\000\034\b\004\001\000\000\000\tdebugging\000\000\000\000\b\030CgWO\260\212\004\a\200\n\004\202z\270\236\261\330m\275\005\f\202g\260g\376", :standard => "\000\000\000$\tx\234bad``\340LIM*MO\317\314K\ar\030\000\000\000\000\377\377\b\030CgWO\260\212^\2117U\266\3444(\235\034\023\377\376\335\301\253rI\215W\311", }, "hmac-ripemd160" => { false => "\000\000\000\034\b\004\001\000\000\000\tdebugging\000\000\000\000\b\030CgWO\260\212F\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "\000\000\000$\tx\234bad``\340LIM*MO\317\314K\ar\030\000\000\000\000\377\377\b\030CgWO\260\212^)U\275\003U\333\225\221Y)\317\256\240\246\0000\351\032\363Y", }, "hmac-ripemd160@openssh.com" => { false => "\000\000\000\034\b\004\001\000\000\000\tdebugging\000\000\000\000\b\030CgWO\260\212F\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "\000\000\000$\tx\234bad``\340LIM*MO\317\314K\ar\030\000\000\000\000\377\377\b\030CgWO\260\212^)U\275\003U\333\225\221Y)\317\256\240\246\0000\351\032\363Y", }, "none" => { false => "\000\000\000\034\b\004\001\000\000\000\tdebugging\000\000\000\000\b\030CgWO\260\212", :standard => "\000\000\000$\tx\234bad``\340LIM*MO\317\314K\ar\030\000\000\000\000\377\377\b\030CgWO\260\212^", }, }, "rijndael-cbc@lysator.liu.se" => { "hmac-md5" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340\000\032w\312\t\306\374\271\345p\215\224\373\363\v\261", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365-\345\b\025\242#\336P8\343\361\263\\\241\326\311", }, "hmac-md5-96" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340\000\032w\312\t\306\374\271\345p\215\224", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365-\345\b\025\242#\336P8\343\361\263", }, "hmac-sha1" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340\004\a\200\n\004\202z\270\236\261\330m\275\005\f\202g\260g\376", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365yC\272\314@\301\n\346$\223\367\r\026\366\375(i'\212\351", }, "hmac-sha1-96" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340\004\a\200\n\004\202z\270\236\261\330m", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365yC\272\314@\301\n\346$\223\367\r", }, "hmac-ripemd160" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340F\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365\3044\024\343q\356\023\032\262\201\e9\213d\265>^{\300\320", }, "hmac-ripemd160@openssh.com" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340F\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365\3044\024\343q\356\023\032\262\201\e9\213d\265>^{\300\320", }, "none" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365", }, }, "arcfour" => { "hmac-md5" => { false => "O\"\200JH\004\200\020Y\323\334\334\273B^\226DL\370\205V\350[>\257\361\223\270\262%\331`\000\032w\312\t\306\374\271\345p\215\224\373\363\v\261", :standard => "O\"\200rIx\035r8\267\265\330>lb\274\th\331-\232\243\\L\277\351\320\337\345\225\226\342>b\350\215\253\n\002\017\354=g\361\271[E\265\217\316\314\b\202\235\226\334", }, "hmac-md5-96" => { false => "O\"\200JH\004\200\020Y\323\334\334\273B^\226DL\370\205V\350[>\257\361\223\270\262%\331`\000\032w\312\t\306\374\271\345p\215\224", :standard => "O\"\200rIx\035r8\267\265\330>lb\274\th\331-\232\243\\L\277\351\320\337\345\225\226\342>b\350\215\253\n\002\017\354=g\361\271[E\265\217\316\314\b", }, "hmac-sha1" => { false => "O\"\200JH\004\200\020Y\323\334\334\273B^\226DL\370\205V\350[>\257\361\223\270\262%\331`\004\a\200\n\004\202z\270\236\261\330m\275\005\f\202g\260g\376", :standard => "O\"\200rIx\035r8\267\265\330>lb\274\th\331-\232\243\\L\277\351\320\337\345\225\226\342>b\350\215\253\n\002\017\2117U\266\3444(\235\034\023\377\376\335\301\253rI\215W\311", }, "hmac-sha1-96" => { false => "O\"\200JH\004\200\020Y\323\334\334\273B^\226DL\370\205V\350[>\257\361\223\270\262%\331`\004\a\200\n\004\202z\270\236\261\330m", :standard => "O\"\200rIx\035r8\267\265\330>lb\274\th\331-\232\243\\L\277\351\320\337\345\225\226\342>b\350\215\253\n\002\017\2117U\266\3444(\235\034\023\377\376", }, "hmac-ripemd160" => { false => "O\"\200JH\004\200\020Y\323\334\334\273B^\226DL\370\205V\350[>\257\361\223\270\262%\331`F\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "O\"\200rIx\035r8\267\265\330>lb\274\th\331-\232\243\\L\277\351\320\337\345\225\226\342>b\350\215\253\n\002\017)U\275\003U\333\225\221Y)\317\256\240\246\0000\351\032\363Y", }, "hmac-ripemd160@openssh.com" => { false => "O\"\200JH\004\200\020Y\323\334\334\273B^\226DL\370\205V\350[>\257\361\223\270\262%\331`F\303\307\207\245\206\325~\315(\370\331\313\305\vHI\312L\216", :standard => "O\"\200rIx\035r8\267\265\330>lb\274\th\331-\232\243\\L\277\351\320\337\345\225\226\342>b\350\215\253\n\002\017)U\275\003U\333\225\221Y)\317\256\240\246\0000\351\032\363Y", }, "none" => { false => "O\"\200JH\004\200\020Y\323\334\334\273B^\226DL\370\205V\350[>\257\361\223\270\262%\331`", :standard => "O\"\200rIx\035r8\267\265\330>lb\274\th\331-\232\243\\L\277\351\320\337\345\225\226\342>b\350\215\253\n\002\017", }, }, } if defined?(OpenSSL::Digest::SHA256) sha2_packets = { "3des-cbc" => { "hmac-sha2-256" => { false => "\003\352\031\261k\243\200\204\301\203]!\a\306\217\201\a[^\304\317\322\264\265~\361\017\n\205\272, 7{\320\316\365Wy\"c\036y\260-\275\312~\217\020U\355\001\377\225F\345\206\255\307\023N\350J", :standard => "\317\222v\316\234<\310\377\310\034\346\351\020:\025{\372PDS\246\344\312J\364\301\n\262\r<\037\231Mu\031\240\255\026\362\200\367F\231v\265o\f9$\224\201\e\364+\226H\374\377=\ts\202`\026\e,\347\t\217\206t\307", }, "hmac-sha2-256-96" => { false => "\003\352\031\261k\243\200\204\301\203]!\a\306\217\201\a[^\304\317\322\264\265~\361\017\n\205\272, 7{\320\316\365Wy\"c\036y\260", :standard => "\317\222v\316\234<\310\377\310\034\346\351\020:\025{\372PDS\246\344\312J\364\301\n\262\r<\037\231Mu\031\240\255\026\362\200\367F\231v\265o\f9$\224\201\e", }, "hmac-sha2-512" => { false => "\003\352\031\261k\243\200\204\301\203]!\a\306\217\201\a[^\304\317\322\264\265~\361\017\n\205\272, #/\317\000\340I\274\363_\225U*\327z\201\316c\303\275A\362\330^J\277\3005oI\272\362\352\206\370h\213\262\3109\310v\037\004\022\200]&\365\310\300\220D[\350\036\225\211\353\361\366\237\267\204\325", :standard => "\317\222v\316\234<\310\377\310\034\346\351\020:\025{\372PDS\246\344\312J\364\301\n\262\r<\037\231Mu\031\240\255\026\362\200Q\3112O\223\361\216\235\022\216\0162\256\343\214\320\v\321\366/$\017]2\302\3435\217\324\245\037\301\225p\270\221c\307\302u\213b 4#\202PFI\371\267l\374\311\001\262z(\335|\334\2446\226", }, "hmac-sha2-512-96" => { false => "\003\352\031\261k\243\200\204\301\203]!\a\306\217\201\a[^\304\317\322\264\265~\361\017\n\205\272, #/\317\000\340I\274\363_\225U*", :standard => "\317\222v\316\234<\310\377\310\034\346\351\020:\025{\372PDS\246\344\312J\364\301\n\262\r<\037\231Mu\031\240\255\026\362\200Q\3112O\223\361\216\235\022\216\0162", }, }, "aes128-cbc" => { "hmac-sha2-256" => { false => "\240\016\243k]0\330\253\030\320\334\261(\034E\211\230#\326\374\267\311O\211E(\234\325n\306NY7{\320\316\365Wy\"c\036y\260-\275\312~\217\020U\355\001\377\225F\345\206\255\307\023N\350J", :standard => "\273\367\324\032\3762\334\026\r\246\342\022\016\325\024\270.\273\005\314\036\312\211\261\037A\361\362:W\316\352K\204\216b\2124>A\265g\331\177\233dK\251\373\035\334\340M\032B\307\324\232\211m'\347k\253\371\341\326\254\356\263[\2412\302R\320\274\365\255\003", }, "hmac-sha2-256-96" => { false => "\240\016\243k]0\330\253\030\320\334\261(\034E\211\230#\326\374\267\311O\211E(\234\325n\306NY7{\320\316\365Wy\"c\036y\260", :standard => "\273\367\324\032\3762\334\026\r\246\342\022\016\325\024\270.\273\005\314\036\312\211\261\037A\361\362:W\316\352K\204\216b\2124>A\265g\331\177\233dK\251\373\035\334\340M\032B\307\324\232\211m", }, "hmac-sha2-512" => { false => "\240\016\243k]0\330\253\030\320\334\261(\034E\211\230#\326\374\267\311O\211E(\234\325n\306NY#/\317\000\340I\274\363_\225U*\327z\201\316c\303\275A\362\330^J\277\3005oI\272\362\352\206\370h\213\262\3109\310v\037\004\022\200]&\365\310\300\220D[\350\036\225\211\353\361\366\237\267\204\325", :standard => "\273\367\324\032\3762\334\026\r\246\342\022\016\325\024\270.\273\005\314\036\312\211\261\037A\361\362:W\316\352K\204\216b\2124>A\265g\331\177\233dK\251N\005f\275u\230\344xF\354+RSTS\360\235\004\311$cW\357o\"fy\031\321yX\tYK\347\363kd\a\022\307r\177[ \274\0164\222\300 \037\330<\264\001^\246\337\004\365\233\202\310", }, "hmac-sha2-512-96" => { false => "\240\016\243k]0\330\253\030\320\334\261(\034E\211\230#\326\374\267\311O\211E(\234\325n\306NY#/\317\000\340I\274\363_\225U*", :standard => "\273\367\324\032\3762\334\026\r\246\342\022\016\325\024\270.\273\005\314\036\312\211\261\037A\361\362:W\316\352K\204\216b\2124>A\265g\331\177\233dK\251N\005f\275u\230\344xF\354+R", }, }, "aes192-cbc" => { "hmac-sha2-256" => { false => "P$\377\302\326\262\276\215\206\343&\257#\315>Mp\232P\345o\215\330\213\t\027\300\360\300\037\267\0037{\320\316\365Wy\"c\036y\260-\275\312~\217\020U\355\001\377\225F\345\206\255\307\023N\350J", :standard => "se\347\230\026\311\212\250yH\241\302n\364:\276\270M=H1\317\222^\362\237D\225N\354:\343\205M\006[\313$U/yZ\330\235\032\307\320D\373\035\334\340M\032B\307\324\232\211m'\347k\253\371\341\326\254\356\263[\2412\302R\320\274\365\255\003", }, "hmac-sha2-512" => { false => "P$\377\302\326\262\276\215\206\343&\257#\315>Mp\232P\345o\215\330\213\t\027\300\360\300\037\267\003#/\317\000\340I\274\363_\225U*\327z\201\316c\303\275A\362\330^J\277\3005oI\272\362\352\206\370h\213\262\3109\310v\037\004\022\200]&\365\310\300\220D[\350\036\225\211\353\361\366\237\267\204\325", :standard => "se\347\230\026\311\212\250yH\241\302n\364:\276\270M=H1\317\222^\362\237D\225N\354:\343\205M\006[\313$U/yZ\330\235\032\307\320DN\005f\275u\230\344xF\354+RSTS\360\235\004\311$cW\357o\"fy\031\321yX\tYK\347\363kd\a\022\307r\177[ \274\0164\222\300 \037\330<\264\001^\246\337\004\365\233\202\310", }, "hmac-sha2-256-96" => { false => "P$\377\302\326\262\276\215\206\343&\257#\315>Mp\232P\345o\215\330\213\t\027\300\360\300\037\267\0037{\320\316\365Wy\"c\036y\260", :standard => "se\347\230\026\311\212\250yH\241\302n\364:\276\270M=H1\317\222^\362\237D\225N\354:\343\205M\006[\313$U/yZ\330\235\032\307\320D\373\035\334\340M\032B\307\324\232\211m", }, "hmac-sha2-512-96" => { false => "P$\377\302\326\262\276\215\206\343&\257#\315>Mp\232P\345o\215\330\213\t\027\300\360\300\037\267\003#/\317\000\340I\274\363_\225U*", :standard => "se\347\230\026\311\212\250yH\241\302n\364:\276\270M=H1\317\222^\362\237D\225N\354:\343\205M\006[\313$U/yZ\330\235\032\307\320DN\005f\275u\230\344xF\354+R", }, }, "aes256-cbc" => { "hmac-sha2-256" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\3407{\320\316\365Wy\"c\036y\260-\275\312~\217\020U\355\001\377\225F\345\206\255\307\023N\350J", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365\373\035\334\340M\032B\307\324\232\211m'\347k\253\371\341\326\254\356\263[\2412\302R\320\274\365\255\003", }, "hmac-sha2-256-96" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\3407{\320\316\365Wy\"c\036y\260", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365\373\035\334\340M\032B\307\324\232\211m", }, "hmac-sha2-512" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340#/\317\000\340I\274\363_\225U*\327z\201\316c\303\275A\362\330^J\277\3005oI\272\362\352\206\370h\213\262\3109\310v\037\004\022\200]&\365\310\300\220D[\350\036\225\211\353\361\366\237\267\204\325", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365N\005f\275u\230\344xF\354+RSTS\360\235\004\311$cW\357o\"fy\031\321yX\tYK\347\363kd\a\022\307r\177[ \274\0164\222\300 \037\330<\264\001^\246\337\004\365\233\202\310", }, "hmac-sha2-512-96" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340#/\317\000\340I\274\363_\225U*", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365N\005f\275u\230\344xF\354+R", }, }, "blowfish-cbc" => { "hmac-sha2-256" => { false => "vT\353\203\247\206L\255e\371\001 6B/\234g\332\371\224l\227\257\346\373E\237C2\212u)7{\320\316\365Wy\"c\036y\260-\275\312~\217\020U\355\001\377\225F\345\206\255\307\023N\350J", :standard => "U\257\231e\347\274\bh\016X\232h\334\v\005\316e1G$-\367##\256$rW\000\210\335_\360\f\000\205#\370\201\006\367F\231v\265o\f9$\224\201\e\364+\226H\374\377=\ts\202`\026\e,\347\t\217\206t\307", }, "hmac-sha2-256-96" => { false => "vT\353\203\247\206L\255e\371\001 6B/\234g\332\371\224l\227\257\346\373E\237C2\212u)7{\320\316\365Wy\"c\036y\260", :standard => "U\257\231e\347\274\bh\016X\232h\334\v\005\316e1G$-\367##\256$rW\000\210\335_\360\f\000\205#\370\201\006\367F\231v\265o\f9$\224\201\e", }, "hmac-sha2-512" => { false => "vT\353\203\247\206L\255e\371\001 6B/\234g\332\371\224l\227\257\346\373E\237C2\212u)#/\317\000\340I\274\363_\225U*\327z\201\316c\303\275A\362\330^J\277\3005oI\272\362\352\206\370h\213\262\3109\310v\037\004\022\200]&\365\310\300\220D[\350\036\225\211\353\361\366\237\267\204\325", :standard => "U\257\231e\347\274\bh\016X\232h\334\v\005\316e1G$-\367##\256$rW\000\210\335_\360\f\000\205#\370\201\006Q\3112O\223\361\216\235\022\216\0162\256\343\214\320\v\321\366/$\017]2\302\3435\217\324\245\037\301\225p\270\221c\307\302u\213b 4#\202PFI\371\267l\374\311\001\262z(\335|\334\2446\226", }, "hmac-sha2-512-96" => { false => "vT\353\203\247\206L\255e\371\001 6B/\234g\332\371\224l\227\257\346\373E\237C2\212u)#/\317\000\340I\274\363_\225U*", :standard => "U\257\231e\347\274\bh\016X\232h\334\v\005\316e1G$-\367##\256$rW\000\210\335_\360\f\000\205#\370\201\006Q\3112O\223\361\216\235\022\216\0162", }, }, "cast128-cbc" => { "hmac-sha2-256" => { false => "\361\026\313!\31235|w~\n\261\257\277\e\277b\246b\342\333\eE\021N\345\343m\314\272\315\3767{\320\316\365Wy\"c\036y\260-\275\312~\217\020U\355\001\377\225F\345\206\255\307\023N\350J", :standard => "\375i\253\004\311E\2011)\220$\251A\245\f(\371\263\314\242\353\260\272\367\276\"\031\224$\244\311W\307Oe\224\0017\336\325\367F\231v\265o\f9$\224\201\e\364+\226H\374\377=\ts\202`\026\e,\347\t\217\206t\307", }, "hmac-sha2-256-96" => { false => "\361\026\313!\31235|w~\n\261\257\277\e\277b\246b\342\333\eE\021N\345\343m\314\272\315\3767{\320\316\365Wy\"c\036y\260", :standard => "\375i\253\004\311E\2011)\220$\251A\245\f(\371\263\314\242\353\260\272\367\276\"\031\224$\244\311W\307Oe\224\0017\336\325\367F\231v\265o\f9$\224\201\e", }, "hmac-sha2-512" => { false => "\361\026\313!\31235|w~\n\261\257\277\e\277b\246b\342\333\eE\021N\345\343m\314\272\315\376#/\317\000\340I\274\363_\225U*\327z\201\316c\303\275A\362\330^J\277\3005oI\272\362\352\206\370h\213\262\3109\310v\037\004\022\200]&\365\310\300\220D[\350\036\225\211\353\361\366\237\267\204\325", :standard => "\375i\253\004\311E\2011)\220$\251A\245\f(\371\263\314\242\353\260\272\367\276\"\031\224$\244\311W\307Oe\224\0017\336\325Q\3112O\223\361\216\235\022\216\0162\256\343\214\320\v\321\366/$\017]2\302\3435\217\324\245\037\301\225p\270\221c\307\302u\213b 4#\202PFI\371\267l\374\311\001\262z(\335|\334\2446\226", }, "hmac-sha2-512-96" => { false => "\361\026\313!\31235|w~\n\261\257\277\e\277b\246b\342\333\eE\021N\345\343m\314\272\315\376#/\317\000\340I\274\363_\225U*", :standard => "\375i\253\004\311E\2011)\220$\251A\245\f(\371\263\314\242\353\260\272\367\276\"\031\224$\244\311W\307Oe\224\0017\336\325Q\3112O\223\361\216\235\022\216\0162", }, }, "idea-cbc" => { "hmac-sha2-256" => { false => "\342\255\202$\273\201\025#\245\2341F\263\005@{\000<\266&s\016\251NH=J\322/\220 H7{\320\316\365Wy\"c\036y\260-\275\312~\217\020U\355\001\377\225F\345\206\255\307\023N\350J", :standard => "F\3048\360\357\265\215I\021)\a\254/\315%\354M\004\330\006\356\vFr\250K\225\223x\277+Q)\022\327\311K\025\322\317\367F\231v\265o\f9$\224\201\e\364+\226H\374\377=\ts\202`\026\e,\347\t\217\206t\307", }, "hmac-sha2-512" => { false => "\342\255\202$\273\201\025#\245\2341F\263\005@{\000<\266&s\016\251NH=J\322/\220 H#/\317\000\340I\274\363_\225U*\327z\201\316c\303\275A\362\330^J\277\3005oI\272\362\352\206\370h\213\262\3109\310v\037\004\022\200]&\365\310\300\220D[\350\036\225\211\353\361\366\237\267\204\325", :standard => "F\3048\360\357\265\215I\021)\a\254/\315%\354M\004\330\006\356\vFr\250K\225\223x\277+Q)\022\327\311K\025\322\317Q\3112O\223\361\216\235\022\216\0162\256\343\214\320\v\321\366/$\017]2\302\3435\217\324\245\037\301\225p\270\221c\307\302u\213b 4#\202PFI\371\267l\374\311\001\262z(\335|\334\2446\226", }, "hmac-sha2-256-96" => { false => "\342\255\202$\273\201\025#\245\2341F\263\005@{\000<\266&s\016\251NH=J\322/\220 H7{\320\316\365Wy\"c\036y\260", :standard => "F\3048\360\357\265\215I\021)\a\254/\315%\354M\004\330\006\356\vFr\250K\225\223x\277+Q)\022\327\311K\025\322\317\367F\231v\265o\f9$\224\201\e", }, "hmac-sha2-512-96" => { false => "\342\255\202$\273\201\025#\245\2341F\263\005@{\000<\266&s\016\251NH=J\322/\220 H#/\317\000\340I\274\363_\225U*", :standard => "F\3048\360\357\265\215I\021)\a\254/\315%\354M\004\330\006\356\vFr\250K\225\223x\277+Q)\022\327\311K\025\322\317Q\3112O\223\361\216\235\022\216\0162", }, }, "arcfour128" => { "hmac-sha2-256" => { false => "e_\204\037\366\363>\024\263q\025\334\354AO.\026t\231nvD\030\226\234\263\257\335:\001\300\2557{\320\316\365Wy\"c\036y\260-\275\312~\217\020U\355\001\377\225F\345\206\255\307\023N\350J", :standard => "e_\204'\367\217\243v\322\025|\330ios\004[P\270\306\272\017\037\344\214\253\354\272m\261\217/jW'V\277\341U\224\367F\231v\265o\f9$\224\201\e\364+\226H\374\377=\ts\202`\026\e,\347\t\217\206t\307", }, "hmac-sha2-512" => { false => "e_\204\037\366\363>\024\263q\025\334\354AO.\026t\231nvD\030\226\234\263\257\335:\001\300\255#/\317\000\340I\274\363_\225U*\327z\201\316c\303\275A\362\330^J\277\3005oI\272\362\352\206\370h\213\262\3109\310v\037\004\022\200]&\365\310\300\220D[\350\036\225\211\353\361\366\237\267\204\325", :standard => "e_\204'\367\217\243v\322\025|\330ios\004[P\270\306\272\017\037\344\214\253\354\272m\261\217/jW'V\277\341U\224Q\3112O\223\361\216\235\022\216\0162\256\343\214\320\v\321\366/$\017]2\302\3435\217\324\245\037\301\225p\270\221c\307\302u\213b 4#\202PFI\371\267l\374\311\001\262z(\335|\334\2446\226", }, "hmac-sha2-256-96" => { false => "e_\204\037\366\363>\024\263q\025\334\354AO.\026t\231nvD\030\226\234\263\257\335:\001\300\2557{\320\316\365Wy\"c\036y\260", :standard => "e_\204'\367\217\243v\322\025|\330ios\004[P\270\306\272\017\037\344\214\253\354\272m\261\217/jW'V\277\341U\224\367F\231v\265o\f9$\224\201\e", }, "hmac-sha2-512-96" => { false => "e_\204\037\366\363>\024\263q\025\334\354AO.\026t\231nvD\030\226\234\263\257\335:\001\300\255#/\317\000\340I\274\363_\225U*", :standard => "e_\204'\367\217\243v\322\025|\330ios\004[P\270\306\272\017\037\344\214\253\354\272m\261\217/jW'V\277\341U\224Q\3112O\223\361\216\235\022\216\0162", }, }, "arcfour256" => { "hmac-sha2-256" => { false => "B\374\256V\035b\337\215\305h\031bE\271\312\361\017T+\302\024x\3016\315g%\032\331\004fr7{\320\316\365Wy\"c\036y\260-\275\312~\217\020U\355\001\377\225F\345\206\255\307\023N\350J", :standard => "B\374\256n\034\036B\357\244\fpf\300\227\366\333Bp\nj\3303\306D\335\177f}\216\264)\360\325jU^M\357$\221\367F\231v\265o\f9$\224\201\e\364+\226H\374\377=\ts\202`\026\e,\347\t\217\206t\307", }, "hmac-sha2-512" => { false => "B\374\256V\035b\337\215\305h\031bE\271\312\361\017T+\302\024x\3016\315g%\032\331\004fr#/\317\000\340I\274\363_\225U*\327z\201\316c\303\275A\362\330^J\277\3005oI\272\362\352\206\370h\213\262\3109\310v\037\004\022\200]&\365\310\300\220D[\350\036\225\211\353\361\366\237\267\204\325", :standard => "B\374\256n\034\036B\357\244\fpf\300\227\366\333Bp\nj\3303\306D\335\177f}\216\264)\360\325jU^M\357$\221Q\3112O\223\361\216\235\022\216\0162\256\343\214\320\v\321\366/$\017]2\302\3435\217\324\245\037\301\225p\270\221c\307\302u\213b 4#\202PFI\371\267l\374\311\001\262z(\335|\334\2446\226", }, "hmac-sha2-256-96" => { false => "B\374\256V\035b\337\215\305h\031bE\271\312\361\017T+\302\024x\3016\315g%\032\331\004fr7{\320\316\365Wy\"c\036y\260", :standard => "B\374\256n\034\036B\357\244\fpf\300\227\366\333Bp\nj\3303\306D\335\177f}\216\264)\360\325jU^M\357$\221\367F\231v\265o\f9$\224\201\e", }, "hmac-sha2-512-96" => { false => "B\374\256V\035b\337\215\305h\031bE\271\312\361\017T+\302\024x\3016\315g%\032\331\004fr#/\317\000\340I\274\363_\225U*", :standard => "B\374\256n\034\036B\357\244\fpf\300\227\366\333Bp\nj\3303\306D\335\177f}\216\264)\360\325jU^M\357$\221Q\3112O\223\361\216\235\022\216\0162", }, }, "arcfour512" => { "hmac-sha2-256" => { false => "\n{\275\177Yw\307\f\277\221\247'\0318\237\223cR\340\361\356\017\357\235\342\374\005wL\267\330D7{\320\316\365Wy\"c\036y\260-\275\312~\217\020U\355\001\377\225F\345\206\255\307\023N\350J", :standard => "\n{\275GX\vZn\336\365\316#\234\026\243\271.v\301Y\"D\350\357\362\344F\020\e\a\227\306\366\025:\246\2349\233\313\367F\231v\265o\f9$\224\201\e\364+\226H\374\377=\ts\202`\026\e,\347\t\217\206t\307", }, "hmac-sha2-512" => { false => "\n{\275\177Yw\307\f\277\221\247'\0318\237\223cR\340\361\356\017\357\235\342\374\005wL\267\330D#/\317\000\340I\274\363_\225U*\327z\201\316c\303\275A\362\330^J\277\3005oI\272\362\352\206\370h\213\262\3109\310v\037\004\022\200]&\365\310\300\220D[\350\036\225\211\353\361\366\237\267\204\325", :standard => "\n{\275GX\vZn\336\365\316#\234\026\243\271.v\301Y\"D\350\357\362\344F\020\e\a\227\306\366\025:\246\2349\233\313Q\3112O\223\361\216\235\022\216\0162\256\343\214\320\v\321\366/$\017]2\302\3435\217\324\245\037\301\225p\270\221c\307\302u\213b 4#\202PFI\371\267l\374\311\001\262z(\335|\334\2446\226", }, "hmac-sha2-256-96" => { false => "\n{\275\177Yw\307\f\277\221\247'\0318\237\223cR\340\361\356\017\357\235\342\374\005wL\267\330D7{\320\316\365Wy\"c\036y\260", :standard => "\n{\275GX\vZn\336\365\316#\234\026\243\271.v\301Y\"D\350\357\362\344F\020\e\a\227\306\366\025:\246\2349\233\313\367F\231v\265o\f9$\224\201\e", }, "hmac-sha2-512-96" => { false => "\n{\275\177Yw\307\f\277\221\247'\0318\237\223cR\340\361\356\017\357\235\342\374\005wL\267\330D#/\317\000\340I\274\363_\225U*", :standard => "\n{\275GX\vZn\336\365\316#\234\026\243\271.v\301Y\"D\350\357\362\344F\020\e\a\227\306\366\025:\246\2349\233\313Q\3112O\223\361\216\235\022\216\0162", }, }, "arcfour" => { "hmac-sha2-256" => { false => "O\"\200JH\004\200\020Y\323\334\334\273B^\226DL\370\205V\350[>\257\361\223\270\262%\331`7{\320\316\365Wy\"c\036y\260-\275\312~\217\020U\355\001\377\225F\345\206\255\307\023N\350J", :standard => "O\"\200rIx\035r8\267\265\330>lb\274\th\331-\232\243\\L\277\351\320\337\345\225\226\342>b\350\215\253\n\002\017\367F\231v\265o\f9$\224\201\e\364+\226H\374\377=\ts\202`\026\e,\347\t\217\206t\307", }, "hmac-sha2-512" => { false => "O\"\200JH\004\200\020Y\323\334\334\273B^\226DL\370\205V\350[>\257\361\223\270\262%\331`#/\317\000\340I\274\363_\225U*\327z\201\316c\303\275A\362\330^J\277\3005oI\272\362\352\206\370h\213\262\3109\310v\037\004\022\200]&\365\310\300\220D[\350\036\225\211\353\361\366\237\267\204\325", :standard => "O\"\200rIx\035r8\267\265\330>lb\274\th\331-\232\243\\L\277\351\320\337\345\225\226\342>b\350\215\253\n\002\017Q\3112O\223\361\216\235\022\216\0162\256\343\214\320\v\321\366/$\017]2\302\3435\217\324\245\037\301\225p\270\221c\307\302u\213b 4#\202PFI\371\267l\374\311\001\262z(\335|\334\2446\226", }, "hmac-sha2-256-96" => { false => "O\"\200JH\004\200\020Y\323\334\334\273B^\226DL\370\205V\350[>\257\361\223\270\262%\331`7{\320\316\365Wy\"c\036y\260", :standard => "O\"\200rIx\035r8\267\265\330>lb\274\th\331-\232\243\\L\277\351\320\337\345\225\226\342>b\350\215\253\n\002\017\367F\231v\265o\f9$\224\201\e", }, "hmac-sha2-512-96" => { false => "O\"\200JH\004\200\020Y\323\334\334\273B^\226DL\370\205V\350[>\257\361\223\270\262%\331`#/\317\000\340I\274\363_\225U*", :standard => "O\"\200rIx\035r8\267\265\330>lb\274\th\331-\232\243\\L\277\351\320\337\345\225\226\342>b\350\215\253\n\002\017Q\3112O\223\361\216\235\022\216\0162", }, }, "3des-ctr" => { "hmac-sha2-256" => { false => "\xED#\x86\xD5\xE1mP\v\f\xB9\xC1\xE6\xFD\xA0~,\xD3\x13\x12\x8Cp\xD4F\x92\xCB\xB6R>\xFA]\x9B\xB17{\xD0\xCE\xF5Wy\"c\x1Ey\xB0-\xBD\xCA~\x8F\x10U\xED\x01\xFF\x95F\xE5\x86\xAD\xC7\x13N\xE8J", :standard => "\xED#\x86\xED\xE0\x11\xCDim\xDD\xA8\xE2x\x8EB\x06\x9E73$\xBC\x9FA\xE0\xDB\xAE\x11Y\xAD\xED\xD43\x86N\x89\xFE\x14V\x91B\xF7F\x99v\xB5o\f9$\x94\x81\e\xF4+\x96H\xFC\xFF=\ts\x82`\x16\e,\xE7\t\x8F\x86t\xC7", }, "hmac-sha2-256-96" => { false => "\xED#\x86\xD5\xE1mP\v\f\xB9\xC1\xE6\xFD\xA0~,\xD3\x13\x12\x8Cp\xD4F\x92\xCB\xB6R>\xFA]\x9B\xB17{\xD0\xCE\xF5Wy\"c\x1Ey\xB0", :standard => "\xED#\x86\xED\xE0\x11\xCDim\xDD\xA8\xE2x\x8EB\x06\x9E73$\xBC\x9FA\xE0\xDB\xAE\x11Y\xAD\xED\xD43\x86N\x89\xFE\x14V\x91B\xF7F\x99v\xB5o\f9$\x94\x81\e", }, "hmac-sha2-512" => { false => "\xED#\x86\xD5\xE1mP\v\f\xB9\xC1\xE6\xFD\xA0~,\xD3\x13\x12\x8Cp\xD4F\x92\xCB\xB6R>\xFA]\x9B\xB1#/\xCF\x00\xE0I\xBC\xF3_\x95U*\xD7z\x81\xCEc\xC3\xBDA\xF2\xD8^J\xBF\xC05oI\xBA\xF2\xEA\x86\xF8h\x8B\xB2\xC89\xC8v\x1F\x04\x12\x80]&\xF5\xC8\xC0\x90D[\xE8\x1E\x95\x89\xEB\xF1\xF6\x9F\xB7\x84\xD5", :standard => "\xED#\x86\xED\xE0\x11\xCDim\xDD\xA8\xE2x\x8EB\x06\x9E73$\xBC\x9FA\xE0\xDB\xAE\x11Y\xAD\xED\xD43\x86N\x89\xFE\x14V\x91BQ\xC92O\x93\xF1\x8E\x9D\x12\x8E\x0E2\xAE\xE3\x8C\xD0\v\xD1\xF6/$\x0F]2\xC2\xE35\x8F\xD4\xA5\x1F\xC1\x95p\xB8\x91c\xC7\xC2u\x8Bb 4#\x82PFI\xF9\xB7l\xFC\xC9\x01\xB2z(\xDD|\xDC\xA46\x96", }, "hmac-sha2-512-96" => { false => "\xED#\x86\xD5\xE1mP\v\f\xB9\xC1\xE6\xFD\xA0~,\xD3\x13\x12\x8Cp\xD4F\x92\xCB\xB6R>\xFA]\x9B\xB1#/\xCF\x00\xE0I\xBC\xF3_\x95U*", :standard => "\xED#\x86\xED\xE0\x11\xCDim\xDD\xA8\xE2x\x8EB\x06\x9E73$\xBC\x9FA\xE0\xDB\xAE\x11Y\xAD\xED\xD43\x86N\x89\xFE\x14V\x91BQ\xC92O\x93\xF1\x8E\x9D\x12\x8E\x0E2", }, }, "blowfish-ctr" => { "hmac-sha2-256" => { false => "\xF7gk6\xB8\xACK\x1D\xC4Ls\xB0{\x0F\xC7\xC4M\xC5>\xF6G8\xD4\xBCu\x152FoJ\xB0\xC07{\xD0\xCE\xF5Wy\"c\x1Ey\xB0-\xBD\xCA~\x8F\x10U\xED\x01\xFF\x95F\xE5\x86\xAD\xC7\x13N\xE8J", :standard => "\xF7gk\x0E\xB9\xD0\xD6\x7F\xA5(\x1A\xB4\xFE!\xFB\xEE\x00\xE1\x1F^\x8Bs\xD3\xCEe\rq!8\xFA\xFFB\r\xE9\xFC\xF6\xCA\xBC\x03\xA9\xF7F\x99v\xB5o\f9$\x94\x81\e\xF4+\x96H\xFC\xFF=\ts\x82`\x16\e,\xE7\t\x8F\x86t\xC7", }, "hmac-sha2-256-96" => { false => "\xF7gk6\xB8\xACK\x1D\xC4Ls\xB0{\x0F\xC7\xC4M\xC5>\xF6G8\xD4\xBCu\x152FoJ\xB0\xC07{\xD0\xCE\xF5Wy\"c\x1Ey\xB0", :standard => "\xF7gk\x0E\xB9\xD0\xD6\x7F\xA5(\x1A\xB4\xFE!\xFB\xEE\x00\xE1\x1F^\x8Bs\xD3\xCEe\rq!8\xFA\xFFB\r\xE9\xFC\xF6\xCA\xBC\x03\xA9\xF7F\x99v\xB5o\f9$\x94\x81\e", }, "hmac-sha2-512" => { false => "\xF7gk6\xB8\xACK\x1D\xC4Ls\xB0{\x0F\xC7\xC4M\xC5>\xF6G8\xD4\xBCu\x152FoJ\xB0\xC0#/\xCF\x00\xE0I\xBC\xF3_\x95U*\xD7z\x81\xCEc\xC3\xBDA\xF2\xD8^J\xBF\xC05oI\xBA\xF2\xEA\x86\xF8h\x8B\xB2\xC89\xC8v\x1F\x04\x12\x80]&\xF5\xC8\xC0\x90D[\xE8\x1E\x95\x89\xEB\xF1\xF6\x9F\xB7\x84\xD5", :standard => "\xF7gk\x0E\xB9\xD0\xD6\x7F\xA5(\x1A\xB4\xFE!\xFB\xEE\x00\xE1\x1F^\x8Bs\xD3\xCEe\rq!8\xFA\xFFB\r\xE9\xFC\xF6\xCA\xBC\x03\xA9Q\xC92O\x93\xF1\x8E\x9D\x12\x8E\x0E2\xAE\xE3\x8C\xD0\v\xD1\xF6/$\x0F]2\xC2\xE35\x8F\xD4\xA5\x1F\xC1\x95p\xB8\x91c\xC7\xC2u\x8Bb 4#\x82PFI\xF9\xB7l\xFC\xC9\x01\xB2z(\xDD|\xDC\xA46\x96", }, "hmac-sha2-512-96" => { false => "\xF7gk6\xB8\xACK\x1D\xC4Ls\xB0{\x0F\xC7\xC4M\xC5>\xF6G8\xD4\xBCu\x152FoJ\xB0\xC0#/\xCF\x00\xE0I\xBC\xF3_\x95U*", :standard => "\xF7gk\x0E\xB9\xD0\xD6\x7F\xA5(\x1A\xB4\xFE!\xFB\xEE\x00\xE1\x1F^\x8Bs\xD3\xCEe\rq!8\xFA\xFFB\r\xE9\xFC\xF6\xCA\xBC\x03\xA9Q\xC92O\x93\xF1\x8E\x9D\x12\x8E\x0E2", }, }, "aes128-ctr" => { "hmac-sha2-256" => { false => "\xD6\x98\xC1n+6\xCA`s2\x06\xAA\x80\xFA\xF3\xF6\xCA\xF9\xC8[BB\xDC\x9F\xDC$\x88*\xA7\x00\x8E\xFD7{\xD0\xCE\xF5Wy\"c\x1Ey\xB0-\xBD\xCA~\x8F\x10U\xED\x01\xFF\x95F\xE5\x86\xAD\xC7\x13N\xE8J", :standard => "\xD6\x98\xC1^2JW\x02\x12Vo\xAE\x05\xD4\xCF\xDC\x87\xDD\xE9\xF3\x8E\t\xDB\xED\xCC<\xCBM\xF0\xB0\xC1\x7F\xD7\x17\x931\xBC~\r\xF2\x87\xB89\x9B\x8B\xB3\x8E\x15\xFB\x1D\xDC\xE0M\x1AB\xC7\xD4\x9A\x89m'\xE7k\xAB\xF9\xE1\xD6\xAC\xEE\xB3[\xA12\xC2R\xD0\xBC\xF5\xAD\x03", }, "hmac-sha2-256-96" => { false => "\xD6\x98\xC1n+6\xCA`s2\x06\xAA\x80\xFA\xF3\xF6\xCA\xF9\xC8[BB\xDC\x9F\xDC$\x88*\xA7\x00\x8E\xFD7{\xD0\xCE\xF5Wy\"c\x1Ey\xB0", :standard => "\xD6\x98\xC1^2JW\x02\x12Vo\xAE\x05\xD4\xCF\xDC\x87\xDD\xE9\xF3\x8E\t\xDB\xED\xCC<\xCBM\xF0\xB0\xC1\x7F\xD7\x17\x931\xBC~\r\xF2\x87\xB89\x9B\x8B\xB3\x8E\x15\xFB\x1D\xDC\xE0M\x1AB\xC7\xD4\x9A\x89m", }, "hmac-sha2-512" => { false => "\xD6\x98\xC1n+6\xCA`s2\x06\xAA\x80\xFA\xF3\xF6\xCA\xF9\xC8[BB\xDC\x9F\xDC$\x88*\xA7\x00\x8E\xFD#/\xCF\x00\xE0I\xBC\xF3_\x95U*\xD7z\x81\xCEc\xC3\xBDA\xF2\xD8^J\xBF\xC05oI\xBA\xF2\xEA\x86\xF8h\x8B\xB2\xC89\xC8v\x1F\x04\x12\x80]&\xF5\xC8\xC0\x90D[\xE8\x1E\x95\x89\xEB\xF1\xF6\x9F\xB7\x84\xD5", :standard => "\xD6\x98\xC1^2JW\x02\x12Vo\xAE\x05\xD4\xCF\xDC\x87\xDD\xE9\xF3\x8E\t\xDB\xED\xCC<\xCBM\xF0\xB0\xC1\x7F\xD7\x17\x931\xBC~\r\xF2\x87\xB89\x9B\x8B\xB3\x8E\x15N\x05f\xBDu\x98\xE4xF\xEC+RSTS\xF0\x9D\x04\xC9$cW\xEFo\"fy\x19\xD1yX\tYK\xE7\xF3kd\a\x12\xC7r\x7F[ \xBC\x0E4\x92\xC0 \x1F\xD8<\xB4\x01^\xA6\xDF\x04\xF5\x9B\x82\xC8", }, "hmac-sha2-512-96" => { false => "\xD6\x98\xC1n+6\xCA`s2\x06\xAA\x80\xFA\xF3\xF6\xCA\xF9\xC8[BB\xDC\x9F\xDC$\x88*\xA7\x00\x8E\xFD#/\xCF\x00\xE0I\xBC\xF3_\x95U*", :standard => "\xD6\x98\xC1^2JW\x02\x12Vo\xAE\x05\xD4\xCF\xDC\x87\xDD\xE9\xF3\x8E\t\xDB\xED\xCC<\xCBM\xF0\xB0\xC1\x7F\xD7\x17\x931\xBC~\r\xF2\x87\xB89\x9B\x8B\xB3\x8E\x15N\x05f\xBDu\x98\xE4xF\xEC+R", }, }, "aes192-ctr" => { "hmac-sha2-256" => { false => "\xA8\x02\xB4-\xFBYo4F\"\xCF\xB8\x92\xF08\xAC\xE8\xECk\xECO\xE7\xF8\x01\xF8\xB0\x9E\x05\xFB\xA7\xA7\x917{\xD0\xCE\xF5Wy\"c\x1Ey\xB0-\xBD\xCA~\x8F\x10U\xED\x01\xFF\x95F\xE5\x86\xAD\xC7\x13N\xE8J", :standard => "\xA8\x02\xB4\x1D\xE2%\xF2V'F\xA6\xBC\x17\xDE\x04\x86\xA5\xC8JD\x83\xAC\xFFs\xE8\xA8\xDDb\xAC\x17\xE8\x13\x92V\x9E\x00!\x1F\xD4\x00\x92T\x15\xDE\xA4\xCA\xE9\xC1\xFB\x1D\xDC\xE0M\x1AB\xC7\xD4\x9A\x89m'\xE7k\xAB\xF9\xE1\xD6\xAC\xEE\xB3[\xA12\xC2R\xD0\xBC\xF5\xAD\x03", }, "hmac-sha2-256-96" => { false => "\xA8\x02\xB4-\xFBYo4F\"\xCF\xB8\x92\xF08\xAC\xE8\xECk\xECO\xE7\xF8\x01\xF8\xB0\x9E\x05\xFB\xA7\xA7\x917{\xD0\xCE\xF5Wy\"c\x1Ey\xB0", :standard => "\xA8\x02\xB4\x1D\xE2%\xF2V'F\xA6\xBC\x17\xDE\x04\x86\xA5\xC8JD\x83\xAC\xFFs\xE8\xA8\xDDb\xAC\x17\xE8\x13\x92V\x9E\x00!\x1F\xD4\x00\x92T\x15\xDE\xA4\xCA\xE9\xC1\xFB\x1D\xDC\xE0M\x1AB\xC7\xD4\x9A\x89m", }, "hmac-sha2-512" => { false => "\xA8\x02\xB4-\xFBYo4F\"\xCF\xB8\x92\xF08\xAC\xE8\xECk\xECO\xE7\xF8\x01\xF8\xB0\x9E\x05\xFB\xA7\xA7\x91#/\xCF\x00\xE0I\xBC\xF3_\x95U*\xD7z\x81\xCEc\xC3\xBDA\xF2\xD8^J\xBF\xC05oI\xBA\xF2\xEA\x86\xF8h\x8B\xB2\xC89\xC8v\x1F\x04\x12\x80]&\xF5\xC8\xC0\x90D[\xE8\x1E\x95\x89\xEB\xF1\xF6\x9F\xB7\x84\xD5", :standard => "\xA8\x02\xB4\x1D\xE2%\xF2V'F\xA6\xBC\x17\xDE\x04\x86\xA5\xC8JD\x83\xAC\xFFs\xE8\xA8\xDDb\xAC\x17\xE8\x13\x92V\x9E\x00!\x1F\xD4\x00\x92T\x15\xDE\xA4\xCA\xE9\xC1N\x05f\xBDu\x98\xE4xF\xEC+RSTS\xF0\x9D\x04\xC9$cW\xEFo\"fy\x19\xD1yX\tYK\xE7\xF3kd\a\x12\xC7r\x7F[ \xBC\x0E4\x92\xC0 \x1F\xD8<\xB4\x01^\xA6\xDF\x04\xF5\x9B\x82\xC8", }, "hmac-sha2-512-96" => { false => "\xA8\x02\xB4-\xFBYo4F\"\xCF\xB8\x92\xF08\xAC\xE8\xECk\xECO\xE7\xF8\x01\xF8\xB0\x9E\x05\xFB\xA7\xA7\x91#/\xCF\x00\xE0I\xBC\xF3_\x95U*", :standard => "\xA8\x02\xB4\x1D\xE2%\xF2V'F\xA6\xBC\x17\xDE\x04\x86\xA5\xC8JD\x83\xAC\xFFs\xE8\xA8\xDDb\xAC\x17\xE8\x13\x92V\x9E\x00!\x1F\xD4\x00\x92T\x15\xDE\xA4\xCA\xE9\xC1N\x05f\xBDu\x98\xE4xF\xEC+R", }, }, "aes256-ctr" => { "hmac-sha2-256" => { false => "M\x1DcA\r]\\\x95?&\xE3D[\xCC1\x9B\xE0\xAF\x96\xA8\x86Y\xBD\x16\xE5xR%u\xC9(\r7{\xD0\xCE\xF5Wy\"c\x1Ey\xB0-\xBD\xCA~\x8F\x10U\xED\x01\xFF\x95F\xE5\x86\xAD\xC7\x13N\xE8J", :standard => "M\x1Dcq\x14!\xC1\xF7^B\x8A@\xDE\xE2\r\xB1\xAD\x8B\xB7\x00J\x12\xBAd\xF5`\x11B\"yg\x8F\x9F\xAB\xC8 d\xB4\xE7^w\xC4\x89\a\x17\x15\x82\n\xFB\x1D\xDC\xE0M\x1AB\xC7\xD4\x9A\x89m'\xE7k\xAB\xF9\xE1\xD6\xAC\xEE\xB3[\xA12\xC2R\xD0\xBC\xF5\xAD\x03", }, "hmac-sha2-256-96" => { false => "M\x1DcA\r]\\\x95?&\xE3D[\xCC1\x9B\xE0\xAF\x96\xA8\x86Y\xBD\x16\xE5xR%u\xC9(\r7{\xD0\xCE\xF5Wy\"c\x1Ey\xB0", :standard => "M\x1Dcq\x14!\xC1\xF7^B\x8A@\xDE\xE2\r\xB1\xAD\x8B\xB7\x00J\x12\xBAd\xF5`\x11B\"yg\x8F\x9F\xAB\xC8 d\xB4\xE7^w\xC4\x89\a\x17\x15\x82\n\xFB\x1D\xDC\xE0M\x1AB\xC7\xD4\x9A\x89m", }, "hmac-sha2-512" => { false => "M\x1DcA\r]\\\x95?&\xE3D[\xCC1\x9B\xE0\xAF\x96\xA8\x86Y\xBD\x16\xE5xR%u\xC9(\r#/\xCF\x00\xE0I\xBC\xF3_\x95U*\xD7z\x81\xCEc\xC3\xBDA\xF2\xD8^J\xBF\xC05oI\xBA\xF2\xEA\x86\xF8h\x8B\xB2\xC89\xC8v\x1F\x04\x12\x80]&\xF5\xC8\xC0\x90D[\xE8\x1E\x95\x89\xEB\xF1\xF6\x9F\xB7\x84\xD5", :standard => "M\x1Dcq\x14!\xC1\xF7^B\x8A@\xDE\xE2\r\xB1\xAD\x8B\xB7\x00J\x12\xBAd\xF5`\x11B\"yg\x8F\x9F\xAB\xC8 d\xB4\xE7^w\xC4\x89\a\x17\x15\x82\nN\x05f\xBDu\x98\xE4xF\xEC+RSTS\xF0\x9D\x04\xC9$cW\xEFo\"fy\x19\xD1yX\tYK\xE7\xF3kd\a\x12\xC7r\x7F[ \xBC\x0E4\x92\xC0 \x1F\xD8<\xB4\x01^\xA6\xDF\x04\xF5\x9B\x82\xC8", }, "hmac-sha2-512-96" => { false => "M\x1DcA\r]\\\x95?&\xE3D[\xCC1\x9B\xE0\xAF\x96\xA8\x86Y\xBD\x16\xE5xR%u\xC9(\r#/\xCF\x00\xE0I\xBC\xF3_\x95U*", :standard => "M\x1Dcq\x14!\xC1\xF7^B\x8A@\xDE\xE2\r\xB1\xAD\x8B\xB7\x00J\x12\xBAd\xF5`\x11B\"yg\x8F\x9F\xAB\xC8 d\xB4\xE7^w\xC4\x89\a\x17\x15\x82\nN\x05f\xBDu\x98\xE4xF\xEC+R", }, }, "cast128-ctr" => { "hmac-sha2-256" => { false => "\x10\xA0cJ6W\xC9\xC7\x02\xF8\xCD\xE31\xF9\xE7n\x0Fj\x7F\x99\x8A\f\x84\x80\x80\xE8p\x9C\x14\x83\x1C\xC77{\xD0\xCE\xF5Wy\"c\x1Ey\xB0-\xBD\xCA~\x8F\x10U\xED\x01\xFF\x95F\xE5\x86\xAD\xC7\x13N\xE8J", :standard => "\x10\xA0cr7+T\xA5c\x9C\xA4\xE7\xB4\xD7\xDBDBN^1FG\x83\xF2\x90\xF03\xFBC3SE\xF7x;q\x89\xA80\xEA\xF7F\x99v\xB5o\f9$\x94\x81\e\xF4+\x96H\xFC\xFF=\ts\x82`\x16\e,\xE7\t\x8F\x86t\xC7", }, "hmac-sha2-256-96" => { false => "\x10\xA0cJ6W\xC9\xC7\x02\xF8\xCD\xE31\xF9\xE7n\x0Fj\x7F\x99\x8A\f\x84\x80\x80\xE8p\x9C\x14\x83\x1C\xC77{\xD0\xCE\xF5Wy\"c\x1Ey\xB0", :standard => "\x10\xA0cr7+T\xA5c\x9C\xA4\xE7\xB4\xD7\xDBDBN^1FG\x83\xF2\x90\xF03\xFBC3SE\xF7x;q\x89\xA80\xEA\xF7F\x99v\xB5o\f9$\x94\x81\e", }, "hmac-sha2-512" => { false => "\x10\xA0cJ6W\xC9\xC7\x02\xF8\xCD\xE31\xF9\xE7n\x0Fj\x7F\x99\x8A\f\x84\x80\x80\xE8p\x9C\x14\x83\x1C\xC7#/\xCF\x00\xE0I\xBC\xF3_\x95U*\xD7z\x81\xCEc\xC3\xBDA\xF2\xD8^J\xBF\xC05oI\xBA\xF2\xEA\x86\xF8h\x8B\xB2\xC89\xC8v\x1F\x04\x12\x80]&\xF5\xC8\xC0\x90D[\xE8\x1E\x95\x89\xEB\xF1\xF6\x9F\xB7\x84\xD5", :standard => "\x10\xA0cr7+T\xA5c\x9C\xA4\xE7\xB4\xD7\xDBDBN^1FG\x83\xF2\x90\xF03\xFBC3SE\xF7x;q\x89\xA80\xEAQ\xC92O\x93\xF1\x8E\x9D\x12\x8E\x0E2\xAE\xE3\x8C\xD0\v\xD1\xF6/$\x0F]2\xC2\xE35\x8F\xD4\xA5\x1F\xC1\x95p\xB8\x91c\xC7\xC2u\x8Bb 4#\x82PFI\xF9\xB7l\xFC\xC9\x01\xB2z(\xDD|\xDC\xA46\x96", }, "hmac-sha2-512-96" => { false => "\x10\xA0cJ6W\xC9\xC7\x02\xF8\xCD\xE31\xF9\xE7n\x0Fj\x7F\x99\x8A\f\x84\x80\x80\xE8p\x9C\x14\x83\x1C\xC7#/\xCF\x00\xE0I\xBC\xF3_\x95U*", :standard => "\x10\xA0cr7+T\xA5c\x9C\xA4\xE7\xB4\xD7\xDBDBN^1FG\x83\xF2\x90\xF03\xFBC3SE\xF7x;q\x89\xA80\xEAQ\xC92O\x93\xF1\x8E\x9D\x12\x8E\x0E2", }, }, "none" => { "hmac-sha2-256" => { false => "\000\000\000\034\b\004\001\000\000\000\tdebugging\000\000\000\000\b\030CgWO\260\2127{\320\316\365Wy\"c\036y\260-\275\312~\217\020U\355\001\377\225F\345\206\255\307\023N\350J", :standard => "\000\000\000$\tx\234bad``\340LIM*MO\317\314K\ar\030\000\000\000\000\377\377\b\030CgWO\260\212^\367F\231v\265o\f9$\224\201\e\364+\226H\374\377=\ts\202`\026\e,\347\t\217\206t\307", }, "hmac-sha2-256-96" => { false => "\000\000\000\034\b\004\001\000\000\000\tdebugging\000\000\000\000\b\030CgWO\260\2127{\320\316\365Wy\"c\036y\260", :standard => "\000\000\000$\tx\234bad``\340LIM*MO\317\314K\ar\030\000\000\000\000\377\377\b\030CgWO\260\212^\367F\231v\265o\f9$\224\201\e", }, "hmac-sha2-512" => { false => "\000\000\000\034\b\004\001\000\000\000\tdebugging\000\000\000\000\b\030CgWO\260\212#/\317\000\340I\274\363_\225U*\327z\201\316c\303\275A\362\330^J\277\3005oI\272\362\352\206\370h\213\262\3109\310v\037\004\022\200]&\365\310\300\220D[\350\036\225\211\353\361\366\237\267\204\325", :standard => "\000\000\000$\tx\234bad``\340LIM*MO\317\314K\ar\030\000\000\000\000\377\377\b\030CgWO\260\212^Q\3112O\223\361\216\235\022\216\0162\256\343\214\320\v\321\366/$\017]2\302\3435\217\324\245\037\301\225p\270\221c\307\302u\213b 4#\202PFI\371\267l\374\311\001\262z(\335|\334\2446\226", }, "hmac-sha2-512-96" => { false => "\000\000\000\034\b\004\001\000\000\000\tdebugging\000\000\000\000\b\030CgWO\260\212#/\317\000\340I\274\363_\225U*", :standard => "\000\000\000$\tx\234bad``\340LIM*MO\317\314K\ar\030\000\000\000\000\377\377\b\030CgWO\260\212^Q\3112O\223\361\216\235\022\216\0162", }, }, "rijndael-cbc@lysator.liu.se" => { "hmac-sha2-256" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\3407{\320\316\365Wy\"c\036y\260-\275\312~\217\020U\355\001\377\225F\345\206\255\307\023N\350J", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365\373\035\334\340M\032B\307\324\232\211m'\347k\253\371\341\326\254\356\263[\2412\302R\320\274\365\255\003", }, "hmac-sha2-256-96" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\3407{\320\316\365Wy\"c\036y\260", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365\373\035\334\340M\032B\307\324\232\211m", }, "hmac-sha2-512" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340#/\317\000\340I\274\363_\225U*\327z\201\316c\303\275A\362\330^J\277\3005oI\272\362\352\206\370h\213\262\3109\310v\037\004\022\200]&\365\310\300\220D[\350\036\225\211\353\361\366\237\267\204\325", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365N\005f\275u\230\344xF\354+RSTS\360\235\004\311$cW\357o\"fy\031\321yX\tYK\347\363kd\a\022\307r\177[ \274\0164\222\300 \037\330<\264\001^\246\337\004\365\233\202\310", }, "hmac-sha2-512-96" => { false => "\266\001oG(\201s\255[\202j\031-\354\353]\022\374\367j2\257\b#\273r\275\341\232\264\255\340#/\317\000\340I\274\363_\225U*", :standard => "\251!O/_\253\321\217e\225\202\202W\261p\r\357\357\375\231\264Y,nZ/\366\225G\256\3000\036\223\237\353\265vG\231\215cvY\236%\315\365N\005f\275u\230\344xF\354+R", }, }, } sha2_packets.each do |key, val| PACKETS[key].merge!(val) end end ciphers = Net::SSH::Transport::CipherFactory::SSH_TO_OSSL.keys hmacs = Net::SSH::Transport::HMAC::MAP.keys ciphers.each do |cipher_name| unless Net::SSH::Transport::CipherFactory.supported?(cipher_name) && PACKETS.key?(cipher_name) puts "Skipping packet stream test for #{cipher_name}" next end # JRuby Zlib implementation (1.4 & 1.5) does not have byte-to-byte compatibility with MRI's. # skip these 80 or more tests under JRuby. if defined?(JRUBY_VERSION) puts "Skipping zlib tests for JRuby" next end hmacs.each do |hmac_name| [false, :standard].each do |compress| cipher_method_name = cipher_name.gsub(/\W/, "_") hmac_method_name = hmac_name.gsub(/\W/, "_") define_method("test_next_packet_with_#{cipher_method_name}_and_#{hmac_method_name}_and_#{compress}_compression") do opts = { shared: "123", hash: "^&*", digester: OpenSSL::Digest::SHA1 } cipher = Net::SSH::Transport::CipherFactory.get(cipher_name, opts.merge(key: "ABC", decrypt: true, iv: "abc")) hmac = Net::SSH::Transport::HMAC.get(hmac_name, "{}|", opts) stream.server.set cipher: cipher, hmac: hmac, compression: compress stream.stubs(:recv).returns(PACKETS[cipher_name][hmac_name][compress]) IO.stubs(:select).returns([[stream]]) packet = stream.next_packet(:nonblock) assert_not_nil packet assert_equal DEBUG, packet.type assert packet[:always_display] assert_equal "debugging", packet[:message] assert_equal "", packet[:language] stream.cleanup end define_method("test_enqueue_packet_with_#{cipher_method_name}_and_#{hmac_method_name}_and_#{compress}_compression") do opts = { shared: "123", digester: OpenSSL::Digest::SHA1, hash: "^&*" } cipher = Net::SSH::Transport::CipherFactory.get(cipher_name, opts.merge(key: "ABC", iv: "abc", encrypt: true)) hmac = Net::SSH::Transport::HMAC.get(hmac_name, "{}|", opts) srand(100) stream.client.set cipher: cipher, hmac: hmac, compression: compress stream.enqueue_packet(ssh_packet) assert_equal PACKETS[cipher_name][hmac_name][compress], stream.write_buffer stream.cleanup end end end end private def stream @stream ||= begin stream = mock("packet_stream") stream.extend(Net::SSH::Transport::PacketStream) stream end end def ssh_packet Net::SSH::Buffer.from(:byte, DEBUG, :bool, true, :string, "debugging", :string, "") end def packet @packet ||= begin data = ssh_packet length = data.length + 4 + 1 # length + padding length padding = stream.server.cipher.block_size - (length % stream.server.cipher.block_size) padding += stream.server.cipher.block_size if padding < 4 Net::SSH::Buffer.from(:long, length + padding - 4, :byte, padding, :raw, data, :raw, "\0" * padding).to_s end end end end net-ssh-4.2.0/test/transport/test_server_version.rb000066400000000000000000000051211315376572000225600ustar00rootroot00000000000000require 'common' require 'net/ssh/transport/server_version' module Transport class TestServerVersion < NetSSHTest def test_1_99_server_version_should_be_acceptible s = subject(socket(true, "SSH-1.99-Testing_1.0\r\n")) assert s.header.empty? assert_equal "SSH-1.99-Testing_1.0", s.version end def test_2_0_server_version_should_be_acceptible s = subject(socket(true, "SSH-2.0-Testing_1.0\r\n")) assert s.header.empty? assert_equal "SSH-2.0-Testing_1.0", s.version end def test_trailing_whitespace_should_be_preserved # some servers, like Mocana, send a version string with trailing # spaces, which are significant when exchanging keys later. s = subject(socket(true, "SSH-2.0-Testing_1.0 \r\n")) assert_equal "SSH-2.0-Testing_1.0 ", s.version end def test_unacceptible_server_version_should_raise_exception assert_raises(Net::SSH::Exception) { subject(socket(false, "SSH-1.4-Testing_1.0\r\n")) } end def test_unexpected_server_close_should_raise_exception assert_raises(Net::SSH::Disconnect) { subject(socket(false, "\r\nDestination server does not have Ssh activated.\r\nContact Cisco Systems, Inc to purchase a\r\nlicense key to activate Ssh.\r\n", true)) } end def test_header_lines_should_be_accumulated s = subject(socket(true, "Welcome\r\nAnother line\r\nSSH-2.0-Testing_1.0\r\n")) assert_equal "Welcome\r\nAnother line\r\n", s.header assert_equal "SSH-2.0-Testing_1.0", s.version end def test_server_disconnect_should_raise_exception assert_raises(Net::SSH::Disconnect) { subject(socket(false, "SSH-2.0-Aborting")) } end private def socket(good, version_header, raise_eot=false) socket = mock("socket") socket.expects(:write).with("#{Net::SSH::Transport::ServerVersion::PROTO_VERSION}\r\n") socket.expects(:flush) data = version_header.split('') recv_times = data.length recv_times += 1 if data[-1] != "\n" unless raise_eot # socket.expects(:recv).with(1).times(recv_times).returns(*data).then.returns(nil) # socket.expects(:readchar).times(recv_times).returns(*data).then.returns(nil) socket.expects(:readpartial).with(1).times(recv_times).returns(*data).then.returns(nil) else socket.expects(:readpartial).with(1).times(recv_times+1).returns(*data).then.raises(EOFError, "end of file reached") end socket end def subject(socket) Net::SSH::Transport::ServerVersion.new(socket, nil) end end end net-ssh-4.2.0/test/transport/test_session.rb000066400000000000000000000305161315376572000211760ustar00rootroot00000000000000require_relative '../common' require 'net/ssh/transport/session' require 'net/ssh/proxy/http' # mocha adds #verify to Object, which throws off the host-key-verifier part of # these tests. # can't use .include? because ruby18 uses strings and ruby19 uses symbols :/ if Object.instance_methods.any? { |v| v.to_sym == :verify } Object.send(:undef_method, :verify) end module Transport class TestSession < NetSSHTest include Net::SSH::Transport::Constants TEST_HOST = "net.ssh.test" TEST_PORT = 22 def test_constructor_defaults assert_equal TEST_HOST, session.host assert_equal TEST_PORT, session.port assert_instance_of Net::SSH::Verifiers::Lenient, session.host_key_verifier end def test_verify_host_key_true_uses_lenient_verifier assert_instance_of Net::SSH::Verifiers::Lenient, session(verify_host_key: true).host_key_verifier end def test_verify_host_key_very_uses_strict_verifier assert_instance_of Net::SSH::Verifiers::Strict, session(verify_host_key: :very).host_key_verifier end def test_verify_host_key_secure_uses_secure_verifier assert_instance_of Net::SSH::Verifiers::Secure, session(verify_host_key: :secure).host_key_verifier end def test_verify_host_key_false_uses_null_verifier assert_instance_of Net::SSH::Verifiers::Null, session(verify_host_key: false).host_key_verifier end def test_unknown_verify_host_key_value_raises_exception_if_value_does_not_respond_to_verify assert_raises(ArgumentError) { session(verify_host_key: :bogus).host_key_verifier } end def test_verify_host_key_value_responding_to_verify_should_pass_muster object = stub("thingy", verify: true) assert_equal object, session(verify_host_key: object).host_key_verifier end def test_host_as_string_should_return_host_and_ip_when_port_is_default session! socket.stubs(:peer_ip).returns("1.2.3.4") assert_equal "#{TEST_HOST},1.2.3.4", session.host_as_string end def test_host_as_string_should_return_host_and_ip_with_port_when_port_is_not_default session(port: 1234) # force session to be instantiated socket.stubs(:peer_ip).returns("1.2.3.4") assert_equal "[#{TEST_HOST}]:1234,[1.2.3.4]:1234", session.host_as_string end def test_host_as_string_should_return_only_host_when_host_is_ip session!(host: "1.2.3.4") socket.stubs(:peer_ip).returns("1.2.3.4") assert_equal "1.2.3.4", session.host_as_string end def test_host_as_string_should_return_only_host_and_port_when_host_is_ip_and_port_is_not_default session!(host: "1.2.3.4", port: 1234) socket.stubs(:peer_ip).returns("1.2.3.4") assert_equal "[1.2.3.4]:1234", session.host_as_string end def test_host_as_string_should_return_only_host_when_proxy_command_is_set session!(host: "1.2.3.4") socket.stubs(:peer_ip).returns(Net::SSH::Transport::PacketStream::PROXY_COMMAND_HOST_IP) assert_equal "1.2.3.4", session.host_as_string end def test_host_as_string_should_return_only_host_and_port_when_host_is_ip_and_port_is_not_default_and_proxy_command_is_set session!(host: "1.2.3.4", port: 1234) socket.stubs(:peer_ip).returns(Net::SSH::Transport::PacketStream::PROXY_COMMAND_HOST_IP) assert_equal "[1.2.3.4]:1234", session.host_as_string end def test_close_should_cleanup_and_close_socket session! socket.expects(:cleanup) socket.expects(:close) session.close end def test_service_request_should_return_buffer assert_equal "\005\000\000\000\004sftp", session.service_request('sftp').to_s end def test_rekey_when_kex_is_pending_should_do_nothing algorithms.stubs(pending?: true) algorithms.expects(:rekey!).never session.rekey! end def test_rekey_when_no_kex_is_pending_should_initiate_rekey_and_block_until_it_completes algorithms.stubs(pending?: false) algorithms.expects(:rekey!) session.expects(:wait).yields algorithms.expects(:initialized?).returns(true) session.rekey! end def test_rekey_as_needed_when_kex_is_pending_should_do_nothing session! algorithms.stubs(pending?: true) socket.expects(:if_needs_rekey?).never session.rekey_as_needed end def test_rekey_as_needed_when_no_kex_is_pending_and_no_rekey_is_needed_should_do_nothing session! algorithms.stubs(pending?: false) socket.stubs(if_needs_rekey?: false) session.expects(:rekey!).never session.rekey_as_needed end def test_rekey_as_needed_when_no_kex_is_pending_and_rekey_is_needed_should_initiate_rekey_and_block session! algorithms.stubs(pending?: false) socket.expects(:if_needs_rekey?).yields session.expects(:rekey!) session.rekey_as_needed end def test_peer_should_return_hash_of_info_about_peer session! socket.stubs(peer_ip: "1.2.3.4") assert_equal({ip: "1.2.3.4", port: TEST_PORT, host: TEST_HOST, canonized: "net.ssh.test,1.2.3.4"}, session.peer) end def test_next_message_should_block_until_next_message_is_available session.expects(:poll_message).with(:block) session.next_message end def test_poll_message_should_query_next_packet_using_the_given_blocking_parameter session! socket.expects(:next_packet).with(:blocking_parameter).returns(nil) session.poll_message(:blocking_parameter) end def test_poll_message_should_default_to_non_blocking session! socket.expects(:next_packet).with(:nonblock).returns(nil) session.poll_message end def test_poll_message_should_silently_handle_disconnect_packets session! socket.expects(:next_packet).returns(P(:byte, DISCONNECT, :long, 1, :string, "testing", :string, "")) assert_raises(Net::SSH::Disconnect) { session.poll_message } end def test_poll_message_should_silently_handle_ignore_packets session! socket.expects(:next_packet).times(2).returns(P(:byte, IGNORE, :string, "test"), nil) assert_nil session.poll_message end def test_poll_message_should_silently_handle_unimplemented_packets session! socket.expects(:next_packet).times(2).returns(P(:byte, UNIMPLEMENTED, :long, 15), nil) assert_nil session.poll_message end def test_poll_message_should_silently_handle_debug_packets_with_always_display session! socket.expects(:next_packet).times(2).returns(P(:byte, DEBUG, :bool, true, :string, "testing", :string, ""), nil) assert_nil session.poll_message end def test_poll_message_should_silently_handle_debug_packets_without_always_display session! socket.expects(:next_packet).times(2).returns(P(:byte, DEBUG, :bool, false, :string, "testing", :string, ""), nil) assert_nil session.poll_message end def test_poll_message_should_silently_handle_kexinit_packets session! packet = P(:byte, KEXINIT, :raw, "lasdfalksdjfa;slkdfja;slkfjsdfaklsjdfa;df") socket.expects(:next_packet).times(2).returns(packet, nil) algorithms.expects(:accept_kexinit).with(packet) assert_nil session.poll_message end def test_poll_message_should_return_other_packets session! packet = P(:byte, SERVICE_ACCEPT, :string, "test") socket.expects(:next_packet).returns(packet) assert_equal packet, session.poll_message end def test_poll_message_should_enqueue_packets_when_algorithm_disallows_packet session! packet = P(:byte, SERVICE_ACCEPT, :string, "test") algorithms.stubs(:allow?).with(packet).returns(false) socket.expects(:next_packet).times(2).returns(packet, nil) assert_nil session.poll_message assert_equal [packet], session.queue end def test_poll_message_should_read_from_queue_when_next_in_queue_is_allowed_and_consume_queue_is_true session! packet = P(:byte, SERVICE_ACCEPT, :string, "test") session.push(packet) socket.expects(:next_packet).never assert_equal packet, session.poll_message assert session.queue.empty? end def test_poll_message_should_not_read_from_queue_when_next_in_queue_is_not_allowed session! packet = P(:byte, SERVICE_ACCEPT, :string, "test") algorithms.stubs(:allow?).with(packet).returns(false) session.push(packet) socket.expects(:next_packet).returns(nil) assert_nil session.poll_message assert_equal [packet], session.queue end def test_poll_message_should_not_read_from_queue_when_consume_queue_is_false session! packet = P(:byte, SERVICE_ACCEPT, :string, "test") session.push(packet) socket.expects(:next_packet).returns(nil) assert_nil session.poll_message(:nonblock, false) assert_equal [packet], session.queue end def test_wait_with_block_should_return_immediately_if_block_returns_truth session.expects(:poll_message).never session.wait { true } end def test_wait_should_not_consume_queue_on_reads n = 0 session.expects(:poll_message).with(:nonblock, false).returns(nil) session.wait { (n += 1) > 1 } end def test_wait_without_block_should_return_after_first_read session.expects(:poll_message).returns(nil) session.wait end def test_wait_should_enqueue_packets session! p1 = P(:byte, SERVICE_REQUEST, :string, "test") p2 = P(:byte, SERVICE_ACCEPT, :string, "test") socket.expects(:next_packet).times(2).returns(p1, p2) n = 0 session.wait { (n += 1) > 2 } assert_equal [p1, p2], session.queue end def test_push_should_enqueue_packet packet = P(:byte, SERVICE_ACCEPT, :string, "test") session.push(packet) assert_equal [packet], session.queue end def test_send_message_should_delegate_to_socket session! packet = P(:byte, SERVICE_ACCEPT, :string, "test") socket.expects(:send_packet).with(packet) session.send_message(packet) end def test_enqueue_message_should_delegate_to_socket session! packet = P(:byte, SERVICE_ACCEPT, :string, "test") socket.expects(:enqueue_packet).with(packet) session.enqueue_message(packet) end def test_configure_client_should_pass_options_to_socket_client_state session.configure_client compression: :standard assert_equal :standard, socket.client.compression end def test_configure_server_should_pass_options_to_socket_server_state session.configure_server compression: :standard assert_equal :standard, socket.server.compression end def test_hint_should_set_hint_on_socket assert !socket.hints[:authenticated] session.hint :authenticated assert socket.hints[:authenticated] end class TestLogger < Logger def initialize @strio = StringIO.new super(@strio) end def messages @strio.string end end def test_log_correct_debug_with_proxy logger = TestLogger.new proxy = Net::SSH::Proxy::HTTP.new("") session!(logger: logger, proxy: proxy) assert_match "establishing connection to #{TEST_HOST}:#{TEST_PORT} through proxy", logger.messages end def test_log_correct_debug_without_proxy logger = TestLogger.new session!(logger: logger) assert_match "establishing connection to #{TEST_HOST}:#{TEST_PORT}", logger.messages end private def socket @socket ||= stub("socket", hints: {}) end def server_version @server_version ||= stub("server_version") end def algorithms @algorithms ||= stub("algorithms", initialized?: true, allow?: true, start: true) end def session(options={}) @session ||= begin host = options.delete(:host) || TEST_HOST if (proxy = options[:proxy]) proxy.stubs("open").returns(socket) else Socket.stubs(:tcp).with(host, options[:port] || TEST_PORT, nil, nil, { connect_timeout: options[:timeout] }).returns(socket) end Net::SSH::Transport::ServerVersion.stubs(:new).returns(server_version) Net::SSH::Transport::Algorithms.stubs(:new).returns(algorithms) Net::SSH::Transport::Session.new(host, options) end end # a simple alias to make the tests more self-documenting. the bang # version makes it look more like the session is being instantiated alias session! session end end net-ssh-4.2.0/test/transport/test_state.rb000066400000000000000000000134421315376572000206320ustar00rootroot00000000000000# encoding: ASCII-8BIT require 'common' require 'net/ssh/transport/state' module Transport class TestState < NetSSHTest def setup @socket = @state = @deflater = @inflater = nil end def teardown if @deflater @deflater.finish if !@deflater.finished? @deflater.close end if @inflater @inflater.finish if !@inflater.finished? @inflater.close end state.cleanup end def test_constructor_should_initialize_all_values assert_equal 0, state.sequence_number assert_equal 0, state.packets assert_equal 0, state.blocks assert_nil state.compression assert_nil state.compression_level assert_nil state.max_packets assert_nil state.max_blocks assert_nil state.rekey_limit assert_equal "identity", state.cipher.name assert_instance_of Net::SSH::Transport::HMAC::None, state.hmac end def test_increment_should_increment_counters state.increment(24) assert_equal 1, state.sequence_number assert_equal 1, state.packets assert_equal 3, state.blocks end def test_reset_should_reset_counters_and_fix_defaults_for_maximums state.increment(24) state.reset! assert_equal 1, state.sequence_number assert_equal 0, state.packets assert_equal 0, state.blocks assert_equal 2147483648, state.max_packets assert_equal 134217728, state.max_blocks end def test_set_should_set_variables_and_reset_counters state.expects(:reset!) state.set cipher: :a, hmac: :b, compression: :c, compression_level: :d, max_packets: 500, max_blocks: 1000, rekey_limit: 1500 assert_equal :a, state.cipher assert_equal :b, state.hmac assert_equal :c, state.compression assert_equal :d, state.compression_level assert_equal 500, state.max_packets assert_equal 1000, state.max_blocks assert_equal 1500, state.rekey_limit end def test_set_with_max_packets_should_respect_max_packets_setting state.set max_packets: 500 assert_equal 500, state.max_packets end def test_set_with_max_blocks_should_respect_max_blocks_setting state.set max_blocks: 1000 assert_equal 1000, state.max_blocks end def test_set_with_rekey_limit_should_include_rekey_limit_in_computation_of_max_blocks state.set rekey_limit: 4000 assert_equal 500, state.max_blocks end def test_compressor_defaults_to_default_zlib_compression expect = deflater.deflate("hello world") assert_equal expect, state.compressor.deflate("hello world") end def test_compressor_uses_compression_level_when_given state.set compression_level: 1 expect = deflater(1).deflate("hello world") assert_equal expect, state.compressor.deflate("hello world") end def test_compress_when_no_compression_is_active_returns_text assert_equal "hello everybody", state.compress("hello everybody") end def test_decompress_when_no_compression_is_active_returns_text assert_equal "hello everybody", state.decompress("hello everybody") end def test_compress_when_compression_is_delayed_and_no_auth_hint_is_set_should_return_text state.set compression: :delayed assert_equal "hello everybody", state.compress("hello everybody") end def test_decompress_when_compression_is_delayed_and_no_auth_hint_is_set_should_return_text state.set compression: :delayed assert_equal "hello everybody", state.decompress("hello everybody") end def test_compress_when_compression_is_enabled_should_return_compressed_text state.set compression: :standard # JRuby Zlib implementation (1.4 & 1.5) does not have byte-to-byte compatibility with MRI's. # skip this test under JRuby. return if defined?(JRUBY_VERSION) assert_equal "x\234\312H\315\311\311WH-K-\252L\312O\251\004\000\000\000\377\377", state.compress("hello everybody") end def test_decompress_when_compression_is_enabled_should_return_decompressed_text state.set compression: :standard # JRuby Zlib implementation (1.4 & 1.5) does not have byte-to-byte compatibility with MRI's. # skip this test under JRuby. return if defined?(JRUBY_VERSION) assert_equal "hello everybody", state.decompress("x\234\312H\315\311\311WH-K-\252L\312O\251\004\000\000\000\377\377") end def test_compress_when_compression_is_delayed_and_auth_hint_is_set_should_return_compressed_text socket.hints[:authenticated] = true state.set compression: :delayed assert_equal "x\234\312H\315\311\311WH-K-\252L\312O\251\004\000\000\000\377\377", state.compress("hello everybody") end def test_decompress_when_compression_is_delayed_and_auth_hint_is_set_should_return_decompressed_text socket.hints[:authenticated] = true state.set compression: :delayed assert_equal "hello everybody", state.decompress("x\234\312H\315\311\311WH-K-\252L\312O\251\004\000\000\000\377\377") end def test_needs_rekey_should_be_true_if_packets_exceeds_max_packets state.set max_packets: 2 state.increment(8) state.increment(8) assert !state.needs_rekey? state.increment(8) assert state.needs_rekey? end def test_needs_rekey_should_be_true_if_blocks_exceeds_max_blocks state.set max_blocks: 10 assert !state.needs_rekey? state.increment(88) assert state.needs_rekey? end private def deflater(level=Zlib::DEFAULT_COMPRESSION) @deflater ||= Zlib::Deflate.new(level) end def inflater @inflater ||= Zlib::Inflate.new(nil) end def socket @socket ||= stub("socket", hints: {}) end def state @state ||= Net::SSH::Transport::State.new(socket, :test) end end endnet-ssh-4.2.0/test/verifiers/000077500000000000000000000000001315376572000160645ustar00rootroot00000000000000net-ssh-4.2.0/test/verifiers/test_secure.rb000066400000000000000000000022361315376572000207410ustar00rootroot00000000000000require 'common' require 'net/ssh/verifiers/secure' require 'ostruct' class TestSecure < NetSSHTest def test_raises_unknown_key_error_if_empty secure_verifier = Net::SSH::Verifiers::Secure.new host_keys = [] def host_keys.host 'foo' end assert_raises(Net::SSH::HostKeyUnknown) { secure_verifier.verify(session:OpenStruct.new(host_keys:host_keys)) } end def test_passess_if_sam secure_verifier = Net::SSH::Verifiers::Secure.new key = OpenStruct.new(ssh_type:'key_type',to_blob:'keyblob') host_keys = [key] def host_keys.host 'foo' end secure_verifier.verify(session:OpenStruct.new(host_keys:host_keys), key:key) end def test_raises_mismatch_error_if_not_the_same secure_verifier = Net::SSH::Verifiers::Secure.new key_in_known_hosts = OpenStruct.new(ssh_type:'key_type',to_blob:'keyblob') key_actual = OpenStruct.new(ssh_type:'key_type',to_blob:'not keyblob') host_keys = [key_in_known_hosts] def host_keys.host 'foo' end assert_raises(Net::SSH::HostKeyMismatch) { secure_verifier.verify(session:OpenStruct.new(host_keys:host_keys), key:key_actual) } end endnet-ssh-4.2.0/test/win_integration/000077500000000000000000000000001315376572000172665ustar00rootroot00000000000000net-ssh-4.2.0/test/win_integration/FreeSSHDService.ini000066400000000000000000000022351315376572000226550ustar00rootroot00000000000000[Telnet server] TelnetListenAddress=0.0.0.0 TelnetListenPort=23 TelnetMaxConnections=0 TelnetTimeout=0 TelnetBanner= TelnetCMD=C:\Windows\system32\cmd.exe TelnetRun=0 TelnetNewConsole=1 [SSH server] SSHListenAddress=127.0.0.1 SSHListenPort=2223 SSHMaxConnections=0 SSHTimeout=0 SSHBanner=Hello from freesshd SSHCMD=C:\Windows\system32\cmd.exe SSHRun=1 SSHNewConsole=1 SSHCiphers=0 SSHMACs=65535 SSHPasswordAuth=0 SSHPublickeyAuth=0 SSHPublickeyPath=C:\Program Files\freeSSHd RSAKeyPath=C:\Program Files\freeSSHd\RSAKey.cfg DSAKeyPath=C:\Program Files\freeSSHd\DSAKey.cfg [SSH tunneling] SSHLocalTunnel=0 SSHLocalTunnelOnly=0 SSHRemoteTunnel=0 SSHRemoteTunnelOnly=0 [SFTP] SFTPHomePath=$HOME\ [Access filtering] HostRestrictions= HostRestrictionsAllow=0 [Logging] LogEvents=0 LogFilePath=C:\Program Files\freeSSHdfreesshd.log LogResolveIP=0 [Automatic updates] UpdateCheckOnStartup=1 UpdateDontPrompt=0 UpdateShowMessages=1 UpdateLastMessageID=1 [Users] UserCount=2 [User0] Name=foo Auth=1 Password=0BEEC7B5EA3F0FDBC95D0DD47F3C5BC275DA8A3373 Domain= Shell=1 SFTP=0 Tunnel=0 [User1] Name=bar Auth=1 Password=62CDB7020FF920E5AA642C3D4066950DD1F01F4DE1 Domain= Shell=1 SFTP=0 Tunnel=0 net-ssh-4.2.0/test/win_integration/FreeSSHDService32.ini000066400000000000000000000022651315376572000230250ustar00rootroot00000000000000[Telnet server] TelnetListenAddress=0.0.0.0 TelnetListenPort=23 TelnetMaxConnections=0 TelnetTimeout=0 TelnetBanner= TelnetCMD=C:\Windows\system32\cmd.exe TelnetRun=0 TelnetNewConsole=1 [SSH server] SSHListenAddress=127.0.0.1 SSHListenPort=2223 SSHMaxConnections=0 SSHTimeout=0 SSHBanner=Hello from freesshd SSHCMD=C:\Windows\system32\cmd.exe SSHRun=1 SSHNewConsole=1 SSHCiphers=0 SSHMACs=65535 SSHPasswordAuth=0 SSHPublickeyAuth=0 SSHPublickeyPath=C:\Program Files (x86)\freeSSHd RSAKeyPath=C:\Program Files (x86)\freeSSHd\RSAKey.cfg DSAKeyPath=C:\Program Files (x86)\freeSSHd\DSAKey.cfg [SSH tunneling] SSHLocalTunnel=0 SSHLocalTunnelOnly=0 SSHRemoteTunnel=0 SSHRemoteTunnelOnly=0 [SFTP] SFTPHomePath=$HOME\ [Access filtering] HostRestrictions= HostRestrictionsAllow=0 [Logging] LogEvents=0 LogFilePath=C:\Program Files (x86)\freeSSHdfreesshd.log LogResolveIP=0 [Automatic updates] UpdateCheckOnStartup=1 UpdateDontPrompt=0 UpdateShowMessages=1 UpdateLastMessageID=1 [Users] UserCount=2 [User0] Name=foo Auth=1 Password=0BEEC7B5EA3F0FDBC95D0DD47F3C5BC275DA8A3373 Domain= Shell=1 SFTP=0 Tunnel=0 [User1] Name=bar Auth=1 Password=62CDB7020FF920E5AA642C3D4066950DD1F01F4DE1 Domain= Shell=1 SFTP=0 Tunnel=0 net-ssh-4.2.0/test/win_integration/common.rb000066400000000000000000000002371315376572000211050ustar00rootroot00000000000000$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../lib" require_relative '../common' require 'minitest' require 'mocha/setup' require 'pty' require 'expect' net-ssh-4.2.0/test/win_integration/test_freesshd.rb000066400000000000000000000004541315376572000224600ustar00rootroot00000000000000require_relative '../common' require 'net/ssh' class TestPageapnt < NetSSHTest def test_connect ret = Net::SSH.start("localhost", "foo", password: 'foo', port: '2223') do |ssh| ssh.exec! "cmd /c echo hello from windows!" end assert_equal "hello from windows!\r\n", ret end end net-ssh-4.2.0/test/win_integration/test_pageant.rb000066400000000000000000000017171315376572000222770ustar00rootroot00000000000000require_relative '../common' require 'net/ssh/authentication/agent' module Authentication unless RUBY_PLATFORM == "java" class TestPageapnt < NetSSHTest def with_pagent pageant_path = 'C:\ProgramData\chocolatey\lib\putty.portable\tools\pageant.exe' raise "No pageant found at:#{pageant_path}" unless File.executable?(pageant_path) pageant_pid = Process.spawn(pageant_path) sleep 4 yield ensure Process.kill(9, pageant_pid) end def test_agent_should_be_able_to_negotiate_with_pagent with_pagent do agent.negotiate! end end def test_agent_should_raise_without_pagent assert_raises Net::SSH::Authentication::AgentNotAvailable do agent.negotiate! end end private def agent(auto=:connect) @agent ||= begin agent = Net::SSH::Authentication::Agent.new agent.connect! if auto == :connect agent end end end end end