transport

package
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 3, 2026 License: MIT Imports: 29 Imported by: 2

Documentation

Overview

Package transport is declared here for nmf.go; see nns.go for the package-level doc comment.

Package transport implements the NNS (.NET NegotiateStream) and NMF (.NET Message Framing) protocol layers used by Active Directory Web Services (ADWS) on port 9389.

Protocol stack (bottom to top):

TCP Socket (net.tcp://dc:9389)
NNS - .NET NegotiateStream (SPNEGO/NTLM/Kerberos authentication + message protection)
NMF - .NET Message Framing (record boundaries + binary XML encoding negotiation)
SOAP/XML (application layer)

Package transport - PKINIT support using RedTeamPentesting/adauth's pkinit package.

Performs a PKINIT AS exchange (RFC 4556) to obtain a Kerberos TGT from an RSA certificate, writes it to a temporary CCache file, and hands it off to the NNS layer via the existing CredentialCCache path.

Index

Constants

View Source
const (
	ModeSingleton     uint8 = 0x01
	ModeDuplex        uint8 = 0x02 // Most common for ADWS
	ModeSimplex       uint8 = 0x03
	ModeDuplexSession uint8 = 0x04
)

Connection modes (from [MC-NMF] 2.2.3.1.2)

View Source
const (
	// SOAP 1.1 encodings
	EncodingSOAP11UTF8    uint8 = 0x00
	EncodingSOAP11UTF16   uint8 = 0x01
	EncodingSOAP11UTF16LE uint8 = 0x02

	// SOAP 1.2 encodings; ADWS requires EncodingSOAP12BinaryDict (0x08)
	EncodingSOAP12UTF8       uint8 = 0x03
	EncodingSOAP12UTF16      uint8 = 0x04
	EncodingSOAP12UTF16LE    uint8 = 0x05
	EncodingSOAP12MTOM       uint8 = 0x06
	EncodingSOAP12Binary     uint8 = 0x07 // Binary XML (NBFS)
	EncodingSOAP12BinaryDict uint8 = 0x08 // Most efficient - used by ADWS
)

Known encodings (from [MC-NMF] 2.2.3.4.1)

View Source
const (
	RecordTypeVersion     uint8 = 0x00 // Version Record
	RecordTypeMode        uint8 = 0x01 // Mode Record
	RecordTypeVia         uint8 = 0x02 // Via Record
	RecordTypeEncoding    uint8 = 0x03 // Known Encoding Record
	RecordTypeExtEncoding uint8 = 0x04 // Extensible Encoding Record
	RecordTypeUnsizedEnv  uint8 = 0x05 // Unsized Envelope Record
	RecordTypeSizedEnv    uint8 = 0x06 // Sized Envelope Record
	RecordTypeEnd         uint8 = 0x07 // End Record
	RecordTypeFault       uint8 = 0x08 // Fault Record
	RecordTypeUpgradeReq  uint8 = 0x09 // Upgrade Request Record
	RecordTypeUpgradeRes  uint8 = 0x0A // Upgrade Response Record
	RecordTypePreambleAck uint8 = 0x0B // Preamble Ack Record (was incorrectly 0x01)
	RecordTypePreambleEnd uint8 = 0x0C // Preamble End Record (was incorrectly 0x02)
)

NMF Record Types (from [MC-NMF] 2.2.1 Record Types table)

View Source
const (
	MessageIDInProgress byte = 0x16 // HandshakeInProgress: Continue negotiation
	MessageIDDone       byte = 0x14 // HandshakeDone: Authentication completed successfully
	MessageIDError      byte = 0x15 // HandshakeError: Authentication failed
)

MessageID constants for NNS handshake/data (per MS-NNS 2.2.1)

Variables

This section is empty.

Functions

func DecodeNBFS

func DecodeNBFS(input []byte) (string, error)

DecodeNBFS decodes NBFX/NBFS records to XML (no StringTable prefix).

func DecodeNBFSE

func DecodeNBFSE(input []byte) (string, error)

func DialADWS added in v1.1.0

func DialADWS(ctx context.Context, host string, port int, opts ResolverOptions) (net.Conn, error)

DialADWS dials a TCP connection to the ADWS port on host and returns the raw net.Conn. The returned connection should be passed to NewNNSConnection (or one of its variants) and then to NewNMFConnection.

Name resolution uses the resolver built from opts, so a custom DNS server and DNS-over-TCP are honoured consistently for both discovery and dialling.

If port is 0, the default ADWS port 9389 is used.

func DiscoverDC added in v1.1.0

func DiscoverDC(ctx context.Context, domain string, opts ResolverOptions) (string, error)

DiscoverDC finds a domain controller for domain by querying SRV records.

It queries (in order):

_ldap._tcp.<domain>
_kerberos._tcp.<domain>

The target of the first record (sorted by priority then weight, as returned by net.Resolver.LookupSRV) is returned. An error is returned when no SRV records are found for either service.

opts controls which DNS server and transport protocol are used for the lookup.

func EncodeNBFS

func EncodeNBFS(input string) ([]byte, error)

EncodeNBFS encodes XML using NBFX records with the NBFS static dictionary (no StringTable). This matches the [MC-NBFS] SOAP Data Structure framing (without [MC-NBFSE] extension).

func EncodeNBFSE

func EncodeNBFSE(input string) ([]byte, error)

func LoadPEM

func LoadPEM(certFile, keyFile string) (*rsa.PrivateKey, *x509.Certificate, error)

LoadPEM loads an RSA private key and certificate from separate PEM files.

func LoadPFX

func LoadPFX(pfxFile, password string) (*rsa.PrivateKey, *x509.Certificate, error)

LoadPFX decodes a PKCS#12 (.pfx/.p12) file and returns the RSA private key and certificate. It delegates to adauth.DecodePFX which correctly handles the Microsoft-specific cert/chain ordering quirk in pkcs12.DecodeChain.

func PKINITAuthenticate

func PKINITAuthenticate(ctx context.Context, username, domain, kdcHost string, cert *x509.Certificate, key *rsa.PrivateKey, opts ResolverOptions) (tempCCachePath string, err error)

PKINITAuthenticate performs a PKINIT AS exchange against kdcHost:88, returning the TGT as a temporary ccache file path. The caller must delete the file. opts controls name resolution and TCP/UDP transport used to reach the KDC.

func ResolveIPToFQDN added in v1.1.0

func ResolveIPToFQDN(ctx context.Context, host string, opts ResolverOptions) (string, error)

ResolveIPToFQDN returns the FQDN for host by performing a reverse DNS (PTR) lookup when host is an IP address, and returns host unchanged otherwise.

The first hostname returned by the PTR lookup is used. A trailing dot (canonical DNS form) is stripped automatically. If the lookup fails the error is returned so the caller can decide whether to fall back to the raw IP.

Types

type CredentialType

type CredentialType int

CredentialType defines the type of credential provided for authentication.

const (
	// Password-based authentication
	CredentialPassword CredentialType = iota
	// NTLM hash authentication (hex-encoded MD4 of password)
	CredentialNTHash
	// Kerberos credential cache (ccache file path or from KRB5CCNAME env)
	CredentialCCache
	// Anonymous authentication (empty credentials)
	CredentialAnonymous
	// PKINIT certificate-based Kerberos pre-authentication (RFC 4556)
	CredentialClientCert
	// Kerberos AES session key (hex-encoded AES-128 or AES-256 key)
	CredentialAESKey
)

type NBFSECodec

type NBFSECodec struct {
	// contains filtered or unexported fields
}

NBFSECodec maintains [MC-NBFSE] StringTable mappings across multiple documents.

[MC-NBFSE] 2.1: the first StringTable entry has ID 1, and each subsequent String is assigned the next-higher odd number. A consumer MUST maintain this mapping until there are no further documents to process.

This codec is used by the transport layer to decode a sequence of SOAP envelopes over a single NMF/NNS connection.

func NewNBFSECodec

func NewNBFSECodec() *NBFSECodec

func (*NBFSECodec) Decode

func (c *NBFSECodec) Decode(input []byte) (string, error)

func (*NBFSECodec) Encode

func (c *NBFSECodec) Encode(xml string) ([]byte, error)

type NMFConnection

type NMFConnection struct {
	// contains filtered or unexported fields
}

NMFConnection represents a .NET Message Framing protocol connection.

NMF provides message boundaries and encoding negotiation on top of NNS.

References:

  • [MC-NMF]: .NET Message Framing Protocol
  • Used by WCF (Windows Communication Foundation)

func NewNMFConnection

func NewNMFConnection(transport *NNSConnection, fqdn string, port int) *NMFConnection

NewNMFConnection creates an NMF connection over the given NNS transport. Authentication is performed later when Connect is called.

func (*NMFConnection) Connect

func (nmf *NMFConnection) Connect(resource string) error

Connect establishes the NMF connection to the specified ADWS resource endpoint.

Connection sequence (per MC-NMF spec):

  1. Send NMF Preamble on raw TCP (pre-auth)
  2. Send Upgrade Request on raw TCP (pre-auth)
  3. Receive Upgrade Response on raw TCP (pre-auth)
  4. Perform NNS authentication handshake on raw TCP (handshake messages unprotected per MS-NNS 2.2.1)
  5. Send Preamble End via NNS transport (post-auth, protected)
  6. Receive Preamble Ack via NNS transport (post-auth, protected)

Endpoints:

  • Windows/Enumeration - LDAP queries (Enumerate/Pull)
  • Windows/Resource - Get/Put operations
  • Windows/ResourceFactory - Create objects
  • Windows/AccountManagement - Account operations
  • Windows/TopologyManagement - DC operations

func (*NMFConnection) Recv

func (nmf *NMFConnection) Recv() (string, error)

Recv receives a SOAP message from an NMF SizedEnvelope record.

func (*NMFConnection) Send

func (nmf *NMFConnection) Send(soapMessage string) error

Send sends a SOAP message encapsulated in an NMF SizedEnvelope record.

SizedEnvelope format:

[0]     RecordType (0x06)
[1:.]   EncodedSize (variable-length integer)
[...]   SOAP payload (binary XML when using SOAP12Binary/Dict)

type NNSConnection

type NNSConnection struct {
	// contains filtered or unexported fields
}

NNSConnection represents a .NET NegotiateStream connection with SPNEGO/NTLM/Kerberos authentication and message signing/sealing capabilities.

References:

  • [MS-NNS]: .NET NegotiateStream Protocol
  • Uses go-msrpc/ssp for GSS-API authentication (SPNEGO, NTLM, Kerberos)

func NewNNSConnection

func NewNNSConnection(conn net.Conn, domain, username, password, targetSPN string, useKerberos bool, protectionLevel ProtectionLevel) *NNSConnection

NewNNSConnection creates an NNS connection over an established TCP socket using password credentials. If targetSPN is empty, the SPN is derived as "host/<domain>" during the authentication handshake.

func NewNNSConnectionAnonymous

func NewNNSConnectionAnonymous(conn net.Conn, targetSPN string, useKerberos bool, protectionLevel ProtectionLevel) *NNSConnection

NewNNSConnectionAnonymous creates a new NNS connection using anonymous authentication.

func NewNNSConnectionWithAESKey

func NewNNSConnectionWithAESKey(conn net.Conn, domain, username, aesKey, targetSPN string, protectionLevel ProtectionLevel) *NNSConnection

NewNNSConnectionWithAESKey creates a new NNS connection using a Kerberos AES session key (overpass-the-hash with AES; works even when RC4-HMAC is disabled on the DC). aesKey must be a hex-encoded 16-byte (AES-128) or 32-byte (AES-256) key.

func NewNNSConnectionWithCCache

func NewNNSConnectionWithCCache(conn net.Conn, domain, username, ccachePath, targetSPN string, protectionLevel ProtectionLevel) *NNSConnection

NewNNSConnectionWithCCache creates a new NNS connection using Kerberos credential cache. ccachePath can be empty to use the KRB5CCNAME environment variable.

func NewNNSConnectionWithNTHash

func NewNNSConnectionWithNTHash(conn net.Conn, domain, username, ntHash, targetSPN string, useKerberos bool, protectionLevel ProtectionLevel) *NNSConnection

NewNNSConnectionWithNTHash creates a new NNS connection using NTLM hash authentication.

func NewNNSConnectionWithPKINIT

func NewNNSConnectionWithPKINIT(conn net.Conn, domain, username string, cert *x509.Certificate, key *rsa.PrivateKey, targetSPN string, protectionLevel ProtectionLevel) *NNSConnection

NewNNSConnectionWithPKINIT creates a new NNS connection using PKINIT certificate-based Kerberos pre-authentication (RFC 4556 / MS-PKCA). The cert and key must be RSA.

func (*NNSConnection) Authenticate

func (nns *NNSConnection) Authenticate() error

Authenticate performs authentication handshake over NNS protocol using GSS-API.

Authentication Logic:

  • Always use SPNEGO
  • If useKerberos = false: SPNEGO with NTLM-only
  • If useKerberos = true: SPNEGO with Kerberos-only

Flow:

  1. Initialize GSS-API security context with credentials and mechanisms
  2. Client → Server: InitSecContext() → HandshakeInProgress
  3. Server → Client: AcceptSecContext() → HandshakeInProgress or HandshakeDone
  4. [Optional] Client → Server: Continue until HandshakeDone

Returns error if authentication fails.

func (*NNSConnection) Close

func (nns *NNSConnection) Close() error

Close closes the underlying connection.

func (*NNSConnection) IsAuthenticated

func (nns *NNSConnection) IsAuthenticated() bool

IsAuthenticated returns whether the connection has completed authentication.

func (*NNSConnection) Recv

func (nns *NNSConnection) Recv(buf []byte) (int, error)

Recv receives data with optional signature verification and unsealing using GSS-API Unwrap.

After authentication, NNS Data Messages have the format:

[0:4]   PayloadSize (uint32, little-endian)
[4:.]   Payload (signed/encrypted data)

Uses GSS_Unwrap from the negotiated security mechanism.

func (*NNSConnection) Send

func (nns *NNSConnection) Send(data []byte) error

Send sends data with optional signing and sealing using GSS-API Wrap.

After authentication, NNS Data Messages have the format:

[0:4]   PayloadSize (uint32, little-endian) - size of wrapped payload
[4:.]   Payload (signed/encrypted data from GSS_Wrap)

Uses GSS_Wrap from the negotiated security mechanism.

func (*NNSConnection) WithResolverOptions added in v1.1.0

func (nns *NNSConnection) WithResolverOptions(opts ResolverOptions) *NNSConnection

WithResolverOptions sets the ResolverOptions used for all KDC TCP connections made by this NNSConnection (Kerberos AS/TGS exchanges and PKINIT). It returns the receiver so calls can be chained onto any New* constructor.

type ProtectionLevel

type ProtectionLevel int

ProtectionLevel defines the message protection requested during NNS negotiation. It controls whether GSS-API wraps messages with signing only or with encryption.

const (
	// ProtectionNone - No message protection requested (still performs the normal NNS/SPNEGO handshake).
	ProtectionNone ProtectionLevel = iota
	// ProtectionSign - Message integrity (uses SPNEGO, can negotiate Kerberos or NTLM)
	ProtectionSign
	// ProtectionEncryptAndSign - Message confidentiality and integrity (uses SPNEGO)
	ProtectionEncryptAndSign
)

type ResolverOptions added in v1.1.0

type ResolverOptions struct {
	// NameServer is an optional custom DNS server address (host or host:port).
	// If empty, the system resolver is used.
	NameServer string

	// UseTCP forces DNS queries to be sent over TCP instead of UDP.
	// Useful when UDP DNS is blocked, when large SRV responses are expected,
	// or when DNS-over-TCP is required by policy.
	// When NameServer is empty and UseTCP is true, the system-selected server
	// is still contacted but via TCP.
	UseTCP bool
}

ResolverOptions configures DNS resolution behaviour used for DC discovery and for dialling the ADWS TCP connection.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL