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
|