Class: RubySMB::Gss::Provider::Kerberos

Inherits:
Base
  • Object
show all
Defined in:
lib/ruby_smb/gss/provider/kerberos.rb

Overview

A GSS provider that advertises Kerberos and surfaces the mechanism token a client sends, without interpreting it.

A Kerberos AP-REQ is encrypted to the service the client believes it is talking to, so a server that does not hold that service's key cannot read it. This provider therefore does not attempt to: it hands the token to a handler and lets that decide what to tell the client. That is enough for a server to observe or forward Kerberos authentication, and it keeps Kerberos message parsing out of this library entirely.

Accepting Kerberos properly, by decrypting the ticket with a service key and validating the PAC, is a separate concern and is not implemented here.

The token handed to the handler is the mechanism token exactly as the client sent it. For Kerberos that is a GSS-API InitialContextToken (RFC 2743 section 3.1), which wraps the mechanism OID and the token identifier around the Kerberos message:

60 82 0c 0e                 InitialContextToken
06 09 2a 86 48 ..         the mechanism OID
01 00                     the token id, here KRB_AP_REQ
6e 82 0b fd ..            the AP-REQ itself

Note that the token id follows the OID rather than starting the token, and that the framing around it is not valid ASN.1, so OpenSSL::ASN1.decode will not parse it. Kerberos.token_id reads it without decoding the payload.

Examples:

Capture the token a client sends

provider = RubySMB::Gss::Provider::Kerberos.new
provider.on_mech_token do |token, authenticator|
  if RubySMB::Gss::Provider::Kerberos.token_id(token) == RubySMB::Gss::Provider::Kerberos::TOK_ID_KRB_AP_REQ
    # forward or record the AP-REQ, then decide how to reply
  end
  # a handler must return a Result; there is no service key here to validate the ticket, so refuse it
  RubySMB::Gss::Provider::Result.new(nil, WindowsError::NTStatus::STATUS_LOGON_FAILURE)
end

Defined Under Namespace

Classes: Authenticator

Constant Summary collapse

TOK_ID_KRB_AP_REQ =

The GSS token identifiers that may appear in a Kerberos mechanism token, per RFC 4121 section 4.1. They are provided so a handler can tell the messages apart without decoding the payload.

"\x01\x00".b.freeze
TOK_ID_KRB_AP_REP =
"\x02\x00".b.freeze
TOK_ID_KRB_ERROR =
"\x03\x00".b.freeze

Instance Attribute Summary

Attributes inherited from Base

#allow_anonymous, #allow_guests

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#supports_mech_type?

Constructor Details

#initialize(&block) ⇒ Kerberos

Returns a new instance of Kerberos.

Parameters:

  • block (Proc, nil) —

    an optional handler for received mechanism tokens, see #on_mech_token.



65
66
67
68
69
# File 'lib/ruby_smb/gss/provider/kerberos.rb', line 65

def initialize(&block)
  @on_mech_token = block
  @allow_anonymous = false
  @allow_guests = false
end

Class Method Details

.token_id(token) ⇒ String?

Read the token identifier out of a GSS-API InitialContextToken, so a handler can tell an AP-REQ from an AP-REP or a KRB-ERROR. The identifier follows the mechanism OID rather than starting the token, and the framing is not valid ASN.1, so it is located by walking the lengths rather than by decoding.

Parameters:

  • token (String) —

    the mechanism token as received

Returns:

  • (String, nil) —

    the two byte identifier, or nil if the token is not shaped as expected



52
53
54
55
56
57
58
59
60
61
62
# File 'lib/ruby_smb/gss/provider/kerberos.rb', line 52

def self.token_id(token)
  return nil if token.nil? || token.bytesize < 4 || token.getbyte(0) != 0x60

  length_byte = token.getbyte(1)
  # a long form length says how many bytes carry the length, a short form is the length itself
  offset = length_byte > 0x80 ? 2 + (length_byte & 0x7f) : 2
  return nil if token.getbyte(offset) != 0x06 # the mechanism OID must follow

  offset += 2 + token.getbyte(offset + 1)
  token.byteslice(offset, 2)
end

Instance Method Details

#mech_types ⇒ Object



75
76
77
78
# File 'lib/ruby_smb/gss/provider/kerberos.rb', line 75

def mech_types
  # both are advertised because Microsoft clients may select either
  [Gss::OID_KERBEROS_5, Gss::OID_MICROSOFT_KERBEROS_5]
end

#new_authenticator(server_client) ⇒ Object



71
72
73
# File 'lib/ruby_smb/gss/provider/kerberos.rb', line 71

def new_authenticator(server_client)
  Authenticator.new(self, server_client)
end

#on_mech_token(token = nil, authenticator = nil, &block) ⇒ Result?

Set or invoke the handler called when a client sends a Kerberos mechanism token.

The handler receives the opaque token and the authenticator that received it, and returns the Result to reply with. When no handler is set the authentication attempt is rejected, since this provider cannot validate a ticket on its own.

Parameters:

  • token (String) (defaults to: nil) —

    the mechanism token, as sent by the client

  • authenticator (Authenticator) (defaults to: nil) —

    the authenticator that received it

Returns:



90
91
92
93
94
95
96
97
98
# File 'lib/ruby_smb/gss/provider/kerberos.rb', line 90

def on_mech_token(token=nil, authenticator=nil, &block)
  if block.nil?
    return nil if @on_mech_token.nil?

    @on_mech_token.call(token, authenticator)
  else
    @on_mech_token = block
  end
end