metasploit-framework/lib/rex/proto/kerberos/client.rb

214 lines
7.1 KiB
Ruby
Raw Normal View History

2014-12-12 07:20:14 +00:00
# -*- coding: binary -*-
module Rex
module Proto
module Kerberos
2014-12-19 17:08:48 +00:00
# This class is a representation of a kerberos client.
2014-12-12 07:20:14 +00:00
class Client
2014-12-19 17:08:48 +00:00
# @!attribute host
# @return [String] The kerberos server host
attr_accessor :host
2014-12-14 06:51:44 +00:00
# @!attribute port
# @return [Fixnum] The kerberos server port
2014-12-12 07:20:14 +00:00
attr_accessor :port
2014-12-19 17:08:48 +00:00
# @!attribute timeout
# @return [Fixnum] The connect / read timeout
attr_accessor :timeout
# @todo Support UDP
2014-12-14 06:51:44 +00:00
# @!attribute protocol
# @return [String] The transport protocol used (tcp/udp)
2014-12-12 07:20:14 +00:00
attr_accessor :protocol
2014-12-14 06:51:44 +00:00
# @!attribute connection
2014-12-19 17:08:48 +00:00
# @return [IO] The connection established through Rex sockets
2014-12-12 07:20:14 +00:00
attr_accessor :connection
2014-12-19 17:08:48 +00:00
# @!attribute context
# @return [Hash] The Msf context where the connection belongs to
attr_accessor :context
2014-12-12 07:20:14 +00:00
def initialize(opts = {})
2014-12-19 17:08:48 +00:00
self.host = opts[:host]
self.port = (opts[:port] || 88).to_i
self.timeout = (opts[:timeout] || 10).to_i
2014-12-12 07:20:14 +00:00
self.protocol = opts[:protocol] || 'tcp'
self.context = opts[:context] || {}
end
2014-12-14 06:51:44 +00:00
# Creates a connection through a Rex socket
#
# @return [Rex::Socket::Tcp]
# @raise [RuntimeError] if the connection can not be created
2014-12-12 07:20:14 +00:00
def connect
return connection if connection
2014-12-14 06:51:44 +00:00
case protocol
when 'tcp'
self.connection = create_tcp_connection
when 'udp'
2014-12-24 01:59:37 +00:00
raise ::NotImplementedError, 'Kerberos Client: UDP not supported'
2014-12-14 06:51:44 +00:00
else
raise ::RuntimeError, 'Kerberos Client: unknown transport protocol'
end
2014-12-12 07:20:14 +00:00
connection
end
2014-12-14 06:51:44 +00:00
# Closes the connection
2014-12-12 07:20:14 +00:00
def close
if connection
connection.shutdown
connection.close unless connection.closed?
end
self.connection = nil
end
2014-12-14 06:51:44 +00:00
# Sends a kerberos request through the connection
#
# @param req [Rex::Proto::Kerberos::Model::KdcRequest] the request to send
2014-12-14 06:51:44 +00:00
# @return [Fixnum] the number of bytes sent
2014-12-24 01:59:37 +00:00
# @raise [RuntimeError] if the transport protocol is unknown
# @raise [NotImplementedError] if the transport protocol isn't supported
2014-12-12 07:20:14 +00:00
def send_request(req)
connect
sent = 0
case protocol
when 'tcp'
sent = send_request_tcp(req)
when 'udp'
sent = send_request_udp(req)
else
raise ::RuntimeError, 'Kerberos Client: unknown transport protocol'
end
sent
end
2014-12-14 06:51:44 +00:00
# Receives a kerberos response through the connection
#
2014-12-19 17:08:48 +00:00
# @return [<Rex::Proto::Kerberos::Model::KrbError, Rex::Proto::Kerberos::Model::KdcResponse>] the kerberos
# response message
# @raise [RuntimeError] if the connection isn't established, the transport protocol is unknown, not supported
# or the response can't be parsed
2014-12-24 01:59:37 +00:00
# @raise [NotImplementedError] if the transport protocol isn't supported
2014-12-12 07:20:14 +00:00
def recv_response
if connection.nil?
raise ::RuntimeError, 'Kerberos Client: connection not established'
end
res = nil
case protocol
when 'tcp'
res = recv_response_tcp
when 'udp'
res = recv_response_udp
else
raise ::RuntimeError, 'Kerberos Client: unknown transport protocol'
end
res
end
2014-12-14 06:51:44 +00:00
# Sends a kerberos request, and reads the response through the connection
#
2014-12-19 17:08:48 +00:00
# @param req [Rex::Proto::Kerberos::Model::KdcRequest] the request to send
2014-12-17 05:21:13 +00:00
# @return [<Rex::Proto::Kerberos::Model::KrbError, Rex::Proto::Kerberos::Model::KdcResponse>] The kerberos message
2014-12-24 01:59:37 +00:00
# @raise [RuntimeError] if the transport protocol is unknown or the response can't be parsed.
# @raise [NotImplementedError] if the transport protocol isn't supported
2014-12-12 07:20:14 +00:00
def send_recv(req)
send_request(req)
2014-12-13 08:46:00 +00:00
res = recv_response
2014-12-12 07:20:14 +00:00
res
end
private
2014-12-19 17:08:48 +00:00
# Creates a TCP connection using Rex::Socket::Tcp
2014-12-14 06:51:44 +00:00
#
# @return [Rex::Socket::Tcp]
def create_tcp_connection
self.connection = Rex::Socket::Tcp.create(
2014-12-19 17:08:48 +00:00
'PeerHost' => host,
2014-12-14 06:51:44 +00:00
'PeerPort' => port.to_i,
'Context' => context,
'Timeout' => timeout
)
end
2014-12-12 07:20:14 +00:00
# Sends a Kerberos Request over a tcp connection
#
# @param req [Rex::Proto::Kerberos::Model::KdcRequest] the request to send
2014-12-12 07:20:14 +00:00
# @return [Fixnum] the number of bytes sent
# @raise [RuntimeError] if the request can't be encoded
def send_request_tcp(req)
data = req.encode
2014-12-14 06:51:44 +00:00
length = [data.length].pack('N')
connection.put(length + data)
2014-12-12 07:20:14 +00:00
end
2014-12-19 17:08:48 +00:00
# UDP isn't supported
#
2014-12-24 01:59:37 +00:00
# @raise [NotImplementedError]
2014-12-12 07:20:14 +00:00
def send_request_udp(req)
2014-12-24 01:59:37 +00:00
raise ::NotImplementedError, 'Kerberos Client: UDP unsupported'
2014-12-12 07:20:14 +00:00
end
# Receives a Kerberos Response over a tcp connection
#
2014-12-17 05:21:13 +00:00
# @return [<Rex::Proto::Kerberos::Model::KrbError, Rex::Proto::Kerberos::Model::KdcResponse>] the kerberos message response
# @raise [RuntimeError] if the response can't be processed
# @raise [EOFError] if expected data can't be read
2014-12-12 07:20:14 +00:00
def recv_response_tcp
2014-12-19 17:08:48 +00:00
length_raw = connection.get_once(4, timeout)
unless length_raw && length_raw.length == 4
raise ::RuntimeError, 'Kerberos Client: failed to read response'
end
length = length_raw.unpack('N')[0]
2014-12-12 07:20:14 +00:00
2014-12-19 17:08:48 +00:00
data = connection.get_once(length, timeout)
unless data && data.length == length
raise ::RuntimeError, 'Kerberos Client: failed to read response'
end
res = decode_kerb_response(data)
res
2014-12-12 07:20:14 +00:00
end
2014-12-19 17:08:48 +00:00
# UDP isn't supported
#
2014-12-24 01:59:37 +00:00
# @raise [NotImplementedError]
2014-12-12 07:20:14 +00:00
def recv_response_udp
2014-12-24 01:59:37 +00:00
raise ::NotImplementedError, 'Kerberos Client: UDP unsupported'
2014-12-12 07:20:14 +00:00
end
private
# Decodes a Kerberos response
#
2015-04-02 11:14:25 +00:00
# @param data [String] the raw response message
2014-12-17 05:21:13 +00:00
# @return [<Rex::Proto::Kerberos::Model::KrbError, Rex::Proto::Kerberos::Model::KdcResponse>] the kerberos message response
# @raise [RuntimeError] if the response can't be processed
def decode_kerb_response(data)
asn1 = OpenSSL::ASN1.decode(data)
msg_type = asn1.value[0].value[1].value[0].value
case msg_type
when Rex::Proto::Kerberos::Model::KRB_ERROR
res = Rex::Proto::Kerberos::Model::KrbError.decode(asn1)
2014-12-17 05:21:13 +00:00
when Rex::Proto::Kerberos::Model::AS_REP
res = Rex::Proto::Kerberos::Model::KdcResponse.decode(asn1)
when Rex::Proto::Kerberos::Model::TGS_REP
res = Rex::Proto::Kerberos::Model::KdcResponse.decode(asn1)
else
raise ::RuntimeError, 'Kerberos Client: Unknown response'
end
res
end
2014-12-12 07:20:14 +00:00
end
end
end
end