File: driver.rb

package info (click to toggle)
libnet-ssh-ruby 1.1.2-1
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 3,472 kB
  • ctags: 2,465
  • sloc: ruby: 10,848; makefile: 17
file content (183 lines) | stat: -rw-r--r-- 6,564 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#--
# =============================================================================
# Copyright (c) 2004,2005 Jamis Buck (jamis@37signals.com)
# All rights reserved.
#
# This source file is distributed as part of the Net::SSH Secure Shell Client
# library for Ruby. This file (and the library as a whole) may be used only as
# allowed by either the BSD license, or the Ruby license (or, by association
# with the Ruby license, the GPL). See the "doc" subdirectory of the Net::SSH
# distribution for the texts of these licenses.
# -----------------------------------------------------------------------------
# net-ssh website : http://net-ssh.rubyforge.org
# project website: http://rubyforge.org/projects/net-ssh
# =============================================================================
#++

require 'net/ssh/errors'
require 'net/ssh/userauth/constants'
require 'net/ssh/transport/constants'
require 'ostruct'

module Net
  module SSH
    module UserAuth

      # A wrapper around the transport layer that represents the functionality
      # of user authentication.
      class Driver

        include Net::SSH::UserAuth::Constants
        include Net::SSH::Transport::Constants

        # The UserKeyManager instance used by the auth service.
        attr_writer :key_manager

        # The SSH (transport) session to use for communication.
        attr_writer :session

        # The array of auth-method names (as strings), giving the order in
        # which each auth-method will be tried.
        attr_reader :order

        # Create a new user-auth service on top of the given session.
        def initialize( log, buffers, methods, order )
          @log = log
          @buffers = buffers
          @methods = methods
          @on_banner = proc { |msg,lang| }
          @order = order.dup
          @allowed_auth_methods = nil
        end

        # Causes the set of on-disk key files to be used to be set to the
        # given array. Any key files that were specified previously are
        # lost.
        def set_key_files( files )
          @key_manager.clear!
          files.each { |file| @key_manager << file }
        end

        # Causes the set of on-disk host key files to be used to be set to the
        # given array. Any host key files that were specified previously are
        # lost.
        def set_host_key_files( files )
          @key_manager.clear_host!
          files.each { |file| @key_manager.add_host_key file }
        end

        # Changes the set of authentication methods to try to the given array.
        # Methods are tried in the order in which they are listed in the
        # array.
        def set_auth_method_order( *methods )
          @order = methods.flatten
        end

        # Specify the callback to use when the server sends a banner message
        # at login time.
        def on_banner( &block )
          @on_banner = block
        end

        # Sends the message by delegating to the session's #send_message
        # method. (This is a convenience method for the authentication
        # implementations.)
        def send_message( message )
          @session.send_message message
        end

        # Wraps the Net::SSH::Transport::Session#wait_for_message method,
        # doing special checking for authentication-related messages.
        def wait_for_message
          loop do
            type, buffer = @session.wait_for_message

            case type
              when USERAUTH_BANNER
                message = buffer.read_string
                language = buffer.read_string

                if @log.debug?
                  @log.debug "got USERAUTH_BANNER (#{message}:#{language})"
                end

                @on_banner.call( message, language )

              when USERAUTH_FAILURE
                authentications = buffer.read_string
                @allowed_auth_methods = authentications.split(/,/)
                partial_success = buffer.read_bool
                return OpenStruct.new( :message_type    => type,
                                       :authentications => authentications,
                                       :partial_success => partial_success )

              when USERAUTH_SUCCESS
                return OpenStruct.new( :message_type => type )

              when SERVICE_ACCEPT
                return OpenStruct.new( :message_type => type,
                                       :service_name => buffer.read_string )
              
              # authmethod-specific codes
              when 60..79
                return OpenStruct.new( :message_type => type,
                                       :buffer       => buffer )

              else
                raise Net::SSH::Exception,
                      "unexpected message type '#{type}' (#{buffer.to_s})"
            end
          end
        end

        # Processes the authentication of the given username. The
        # 'next_service' parameter should be set to the SSH service that will
        # be requested once the authentication succeeds (usually
        # 'ssh-connection').
        #
        # This will return +true+ if the user is accepted by the server, and
        # +false+ otherwise.
        def authenticate( next_service, username, password=nil )
          msg = @buffers.writer
          msg.write_byte SERVICE_REQUEST
          msg.write_string "ssh-userauth"
          send_message msg

          message = wait_for_message
          unless message.message_type == SERVICE_ACCEPT
            raise Net::SSH::Exception,
              "expected SERVICE_ACCEPT, got #{message.inspect}"
          end

          data = { :password => password,
                   :key_manager => @key_manager }

          @order.each do |auth_method|
            # if the server has reported a list of auth methods that are
            # allowed to continue, only consider those auth methods.
            next if @allowed_auth_methods &&
              !@allowed_auth_methods.include?( auth_method )

            @log.debug "trying #{auth_method.inspect}" if @log.debug?

            impl = @methods[ auth_method.downcase.gsub(/-/,"_").intern ]
            if impl.nil?
              raise NotImplementedError,
                "`#{auth_method}' authentication is not implemented"
            end

            return true if impl.authenticate( next_service, username, data )
          end

          @log.debug "all authorization methods failed" if @log.debug?
          return false

        ensure
          @key_manager.finish
        end

      end

    end
  end
end