Module: RubySMB::Gss

Defined in:
lib/ruby_smb/gss.rb,
lib/ruby_smb/gss/provider.rb,
lib/ruby_smb/gss/provider/ntlm.rb,
lib/ruby_smb/gss/provider/multi.rb,
lib/ruby_smb/gss/provider/kerberos.rb,
lib/ruby_smb/gss/spnego_neg_token_init.rb,
lib/ruby_smb/gss/spnego_neg_token_targ.rb,
lib/ruby_smb/gss/provider/authenticator.rb

Overview

module containing methods required for using the GSS-API for Secure Protected Negotiation(SPNEGO) in SMB Authentication.

Defined Under Namespace

Modules: Provider Classes: ContextFlags, GeneralString, MechType, MechTypeList, NegHints, NegTokenInit, SpnegoNegTokenInit, SpnegoNegTokenTarg

Constant Summary collapse

OID_SPNEGO =
OpenSSL::ASN1::ObjectId.new('1.3.6.1.5.5.2')
OID_NEGOEX =
OpenSSL::ASN1::ObjectId.new('1.3.6.1.4.1.311.2.2.30')
OID_NTLMSSP =
OpenSSL::ASN1::ObjectId.new('1.3.6.1.4.1.311.2.2.10')
OID_KERBEROS_5 =

The Kerberos v5 GSS-API mechanism (RFC 4121). Microsoft's SPNEGO implementation also uses a legacy OID that differs by a single arc, and clients may offer or select either, so both are defined here.

OpenSSL::ASN1::ObjectId.new('1.2.840.113554.1.2.2')
OID_MICROSOFT_KERBEROS_5 =
OpenSSL::ASN1::ObjectId.new('1.2.840.48018.1.2.2')

Class Method Summary collapse

Class Method Details

.asn1dig(asn, *path) ⇒ OpenSSL::ASN1::Sequence?

Allow safe navigation of a decoded ASN.1 data structure. Similar to Ruby's builtin Hash#dig method but using the #value attribute of each ASN object.

Parameters:

  • asn —

    The ASN object to apply the traversal path on.

  • path (Array) —

    The path to traverse, each element is passed to the ASN object's #value's #[] operator.

Returns:

  • (OpenSSL::ASN1::Sequence, nil)


25
26
27
28
29
30
31
32
# File 'lib/ruby_smb/gss.rb', line 25

def self.asn1dig(asn, *path)
  path.each do |part|
    return nil unless asn&.value
    asn = asn.value[part]
  end

  asn
end

.asn1encode(str = '') ⇒ Object

TODO:

Document these magic numbers

Cargo culted from Rex. Hacked Together ASN1 encoding that works for our GSS purposes



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/ruby_smb/gss.rb', line 36

def self.asn1encode(str = '')
  # If the high bit of the first byte is 1, it contains the number of
  # length bytes that follow
  case str.length
  when 0..0x7F
    encoded_string = [str.length].pack('C') + str
  when 0x80..0xFF
    encoded_string = [0x81, str.length].pack('CC') + str
  when 0x100..0xFFFF
    encoded_string = [0x82, str.length].pack('Cn') + str
  when  0x10000..0xffffff
    encoded_string = [0x83, str.length >> 16, str.length & 0xFFFF].pack('CCn') + str
  when  0x1000000..0xffffffff
    encoded_string = [0x84, str.length].pack('CN') + str
  else
    raise RubySMB::Error::ASN1Encoding, "Source string is too long. Size is #{str.length}"
  end
  encoded_string
end

.gss_neg_token_init(mech_types) ⇒ String

Build the SPNEGO NegTokenInit that a server sends to advertise the authentication mechanisms it supports, per RFC 4178 section 4.2.1.

The mechTypes list is supplied by the caller so that it reflects every mechanism the server actually offers, rather than being fixed to a single mechanism by whichever provider happens to build the token.

Parameters:

  • mech_types (Array<OpenSSL::ASN1::ObjectId>) —

    the mechanisms to advertise, in preference order (most preferred first).

Returns:

  • (String) —

    the DER encoded NegTokenInit.

Raises:

  • (ArgumentError)


66
67
68
69
70
# File 'lib/ruby_smb/gss.rb', line 66

def self.gss_neg_token_init(mech_types)
  raise ArgumentError, 'at least one mechanism must be advertised' if mech_types.nil? || mech_types.empty?

  SpnegoNegTokenInit.build(mech_types)
end

.gss_type1(type1) ⇒ Object

Create a GSS Security Blob of an NTLM Type 1 Message.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/ruby_smb/gss.rb', line 73

def self.gss_type1(type1)
  OpenSSL::ASN1::ASN1Data.new([
    OID_SPNEGO,
    OpenSSL::ASN1::ASN1Data.new([
      OpenSSL::ASN1::Sequence.new([
        OpenSSL::ASN1::ASN1Data.new([
          OpenSSL::ASN1::Sequence.new([
            OID_NTLMSSP
          ])
        ], 0, :CONTEXT_SPECIFIC),
        OpenSSL::ASN1::ASN1Data.new([
          OpenSSL::ASN1::OctetString.new(type1)
        ], 2, :CONTEXT_SPECIFIC)
      ])
    ], 0, :CONTEXT_SPECIFIC)
  ], 0, :APPLICATION).to_der
end

.gss_type2(type2) ⇒ Object

Create a GSS Security Blob of an NTLM Type 2 Message.



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/ruby_smb/gss.rb', line 92

def self.gss_type2(type2)
  OpenSSL::ASN1::ASN1Data.new([
   OpenSSL::ASN1::Sequence.new([
     OpenSSL::ASN1::ASN1Data.new([
       OpenSSL::ASN1::Enumerated.new(OpenSSL::BN.new(1))
     ], 0, :CONTEXT_SPECIFIC),
     OpenSSL::ASN1::ASN1Data.new([
       OID_NTLMSSP
     ], 1, :CONTEXT_SPECIFIC),
     OpenSSL::ASN1::ASN1Data.new([
       OpenSSL::ASN1::OctetString.new(type2)
     ], 2, :CONTEXT_SPECIFIC)
   ])
  ], 1, :CONTEXT_SPECIFIC).to_der
end

.gss_type3(type3) ⇒ Object

Create a GSS Security Blob of an NTLM Type 3 Message.



109
110
111
112
113
114
115
116
117
# File 'lib/ruby_smb/gss.rb', line 109

def self.gss_type3(type3)
  OpenSSL::ASN1::ASN1Data.new([
    OpenSSL::ASN1::Sequence.new([
      OpenSSL::ASN1::ASN1Data.new([
        OpenSSL::ASN1::OctetString.new(type3)
      ], 2, :CONTEXT_SPECIFIC)
    ])
  ], 1, :CONTEXT_SPECIFIC).to_der
end