feat: support large kerberos tokens (#83)

* feat: fix auth too large

* fix: alter-context large token
This commit is contained in:
oiweiwei
2026-02-08 14:26:08 +01:00
committed by GitHub
parent 63357b3154
commit bdc388ce9a
9 changed files with 117 additions and 26 deletions
+1
View File
@@ -3,3 +3,4 @@ rpc/y.out
develop/.vagrant
examples/testing
test/
.vimrc
+18 -6
View File
@@ -169,7 +169,10 @@ type Config struct {
Protocol string `json:"protocol"`
// The transfer encoding to use (ndr20, ndr64)
TrasnferEncoding string `json:"transfer_encoding"`
TransferEncoding string `json:"transfer_encoding"`
// The transfer window size.
TransportXmitSize int `json:"transport_xmit_size"`
// The flag that indicates whether credentials and mechanisms should be
// included into connection options. If GlobalCredentials is true, then
@@ -203,7 +206,8 @@ func New() *Config {
cfg.Auth.KRB5.DisablePAFXFAST = true
cfg.Auth.KRB5.AnyServiceClassSPN = true
cfg.TrasnferEncoding = "ndr20"
cfg.TransferEncoding = "ndr20"
cfg.TransportXmitSize = dcerpc.DefaultXmitSize
return cfg
}
@@ -373,7 +377,7 @@ func (cfg *Config) ClientOptions(ctx context.Context) []dcerpc.Option {
options := []dcerpc.Option{}
switch cfg.TrasnferEncoding {
switch cfg.TransferEncoding {
case "ndr20":
options = append(options, dcerpc.WithNDR20())
case "ndr64":
@@ -513,6 +517,10 @@ func (cfg *Config) getDialOptions() []dcerpc.Option {
options = append(options, dcerpc.WithSMBDialer(smb2.NewDialer(dialer...)))
}
if cfg.TransportXmitSize > 0 {
options = append(options, dcerpc.WithFragmentSize(cfg.TransportXmitSize))
}
if !cfg.useGlobalCredentials {
if cfg.useMachineAccount {
for _, cred := range cfg.MachineAccountCredentials() {
@@ -733,9 +741,9 @@ func (cfg *Config) ParseServerAddr() error {
switch extra {
// transfer encoding.
case "ndr20":
cfg.TrasnferEncoding = "ndr20"
cfg.TransferEncoding = "ndr20"
case "ndr64":
cfg.TrasnferEncoding = "ndr64"
cfg.TransferEncoding = "ndr64"
// auth type keywords.
case "spnego":
cfg.Auth.SPNEGO = true
@@ -806,10 +814,14 @@ func (cfg *Config) Validate() error {
}
}
if err := ValidateTransferEncoding(cfg.TrasnferEncoding); err != nil {
if err := ValidateTransferEncoding(cfg.TransferEncoding); err != nil {
return err
}
if cfg.TransportXmitSize <= 1024 {
return fmt.Errorf("transport xmit size must be greater than 1024")
}
if err := ValidateAuthLevel(cfg.Auth.Level); err != nil {
return err
}
+3 -1
View File
@@ -18,7 +18,9 @@ func BindFlags(c *config.Config, flagSet *flag.FlagSet) {
flagSet.DurationVar(&c.Timeout, "timeout", c.Timeout, "timeout")
flagSet.StringVar(&c.TrasnferEncoding, "transfer-encoding", c.TrasnferEncoding, "transfer encoding: ndr20, ndr64")
flagSet.StringVar(&c.TransferEncoding, "transfer-encoding", c.TransferEncoding, "transfer encoding: ndr20, ndr64")
flagSet.IntVar(&c.TransportXmitSize, "transport-xmit-size", c.TransportXmitSize, "transport xmit size")
flagSet.StringVar(&c.Credential.Password, "password", c.Credential.Password, "password to authenticate with")
flagSet.StringVar(&c.Credential.NTHash, "nthash", c.Credential.NTHash, "NT hash to authenticate with")
+8 -4
View File
@@ -25,11 +25,15 @@ type BufferedConn struct {
cur, total []byte
}
func (conn *BufferedConn) Resized(sz int) *BufferedConn {
if sz > len(conn.total) {
conn.total = make([]byte, sz)
func resizeBuffer(buf []byte, sz int) []byte {
if sz > len(buf) {
return make([]byte, sz)
}
conn.cur, conn.total = nil, conn.total[:sz]
return buf[:sz]
}
func (conn *BufferedConn) Resized(sz int) *BufferedConn {
conn.cur, conn.total = nil, resizeBuffer(conn.total, sz)
return conn
}
+27 -3
View File
@@ -38,6 +38,21 @@ type Packet struct {
start, end int
}
func (p *Packet) Unset(flag PacketFlag) {
p.Header.PacketFlags &^= flag
}
func (p *Packet) Set(flag PacketFlag) {
p.Header.PacketFlags |= flag
}
func (p *Packet) PDUHeaderSize() int {
if sizer := p.PDU.(interface{ Size() int }); sizer != nil {
return HeaderSize + sizer.Size() + MaxPad + SecurityTrailerSize
}
return 0
}
func (p *Packet) IsLastFrag() bool {
return p.Header.PacketFlags&PacketFlagLastFrag != 0
}
@@ -332,7 +347,7 @@ func (c *transport) EncodePacket(ctx context.Context, pkt *Packet, raw []byte) e
pkt.raw, pkt.end = raw, len(raw)
// set packet rpc version.
pkt.Header.RPCVersion, pkt.Header.RPCVersionMinor = 5, 0
pkt.Header.RPCVersion = 5
// set packet drep.
pkt.Header.PacketDRep = c.settings.DataRepresentation
// set packet type.
@@ -364,14 +379,23 @@ func (c *transport) EncodePacket(ctx context.Context, pkt *Packet, raw []byte) e
// adjust stub buffer to include security trailer.
if pkt.Header.AuthLength > 0 {
pkt.end -= MaxPad + SecurityTrailerSize + int(pkt.Header.AuthLength)
sz := MaxPad + SecurityTrailerSize + int(pkt.Header.AuthLength)
if pkt.end -= sz; pkt.end <= 0 {
if pkt.end, raw = cap(raw)-sz, raw[:cap(raw)]; pkt.end <= 0 || pkt.Header.PacketType != PacketTypeAlterContext {
// XXX: allow exceeding capacity for alter-context requests (as they may contain large krb tickets)
return fmt.Errorf("encode_packet: insufficient buffer size for security trailer (%d bytes)", sz)
}
}
}
// XXX: verification is computed for every fragment, however it's being
// written only for last fragment.
verifyLen := pkt.VerificationTrailer.Size()
if verifyLen > 0 {
pkt.end -= VerificationTrailerMaxPad + verifyLen /* VerificationMaxPad */
sz := VerificationTrailerMaxPad + verifyLen /* VerificationMaxPad */
if pkt.end -= sz; pkt.end <= 0 {
return fmt.Errorf("encode_packet: insufficient buffer size for verification trailer (%d bytes)", verifyLen)
}
}
w := c.Codec(raw, pkt.Header.PacketDRep)
+22
View File
@@ -70,6 +70,10 @@ type Auth3 struct {
Pad [4]byte
}
func (pdu *Auth3) Size() int {
return 4
}
func (pdu *Auth3) MarshalZerologObject(e *zerolog.Event) {}
// marshal function ...
@@ -101,6 +105,14 @@ type AlterContext struct {
ContextList []*Context
}
func (pdu *AlterContext) Size() int {
size := 8 + 4
for _, ctx := range pdu.ContextList {
size += ctx.Size()
}
return size
}
func (pdu *AlterContext) MarshalZerologObject(e *zerolog.Event) {}
func (pdu *AlterContext) WriteTo(ctx context.Context, w ndr.Writer) error {
@@ -201,6 +213,14 @@ type Bind struct {
ContextList []*Context
}
func (pdu *Bind) Size() int {
size := 8 + 4
for _, ctx := range pdu.ContextList {
size += ctx.Size()
}
return size
}
func (pdu *Bind) MarshalZerologObject(e *zerolog.Event) {}
func (pdu *Bind) WriteTo(ctx context.Context, w ndr.Writer) error {
@@ -305,6 +325,8 @@ func (pdu *BindNak) Error() string {
return "bind: authentication type was not recognized"
case InvalidChecksum:
return "bind: invalid checksum"
case ProtocolVersionNotSupported:
return "bind: protocol version not supported"
default:
return "bind: unknown error"
}
+21 -11
View File
@@ -154,7 +154,6 @@ func (c *transport) ExportSMBSecurity(o *Security) {
o.SetAttribute(gssapi.AttributeSMBEffectiveSessionKey, pipe.SessionKey())
}
}
}
// AlterContext function establishes new presentation or security (or both) context(s).
@@ -197,11 +196,13 @@ func (c *transport) AlterContext(ctx context.Context, opts ...Option) (Conn, err
if pkt.AuthData, err = o.Security.Init(ctx, nil); err != nil {
return nil, fmt.Errorf("alter context: init security: %w", err)
}
// write bind pdu.
if err = c.WritePacket(ctx, call, pkt); err != nil {
return nil, fmt.Errorf("alter context: write packet: %w", err)
// write alter-context pdu.
if err := c.WritePacket(ctx, call, pkt); err != nil {
return nil, err
}
// read bind response (bind-ack, bind-nak).
// read alter-context response (alter-context-response).
if pkt, err = c.ReadPacket(ctx, call, pkt); err != nil {
return nil, fmt.Errorf("alter context: read packet: %w", err)
}
@@ -389,6 +390,18 @@ func (c *transport) Bind(ctx context.Context, opts ...Option) (Conn, error) {
if pkt.AuthData, err = o.Security.Init(ctx, nil); err != nil {
return nil, fmt.Errorf("bind: %w", err)
}
// XXX: adjust max xmit frag size if auth data is too large.
if len(pkt.AuthData) > c.settings.MaxXmitFrag-pkt.PDUHeaderSize() {
c.logger.Warn().Int("auth_data_size", len(pkt.AuthData)).
Int("max_xmit_frag", c.settings.MaxXmitFrag).
Msg("adjusting transmit buffer size to fit auth data")
sz := len(pkt.AuthData) + pkt.PDUHeaderSize()
c.settings.MaxXmitFrag = sz
// reset buffered connector.
c.cc, c.tx, c.rx = c.cc.Resized(sz), resizeBuffer(c.tx, sz), resizeBuffer(c.rx, sz)
}
// write bind pdu.
if err = c.WritePacket(ctx, call, pkt); err != nil {
return nil, fmt.Errorf("bind: write packet: %w", err)
@@ -402,11 +415,6 @@ func (c *transport) Bind(ctx context.Context, opts ...Option) (Conn, error) {
case *BindAck:
if c.settings.MaxRecvFrag != int(pdu.MaxRecvFrag) {
// reset buffered connector.
c.cc = c.cc.Resized(int(pdu.MaxRecvFrag))
}
sz := c.settings.FragmentSize()
// save retrieved parameters.
@@ -419,7 +427,9 @@ func (c *transport) Bind(ctx context.Context, opts ...Option) (Conn, error) {
o.Group.SetID(int(pdu.AssocGroupID))
if sz != c.settings.FragmentSize() {
c.tx, c.rx = make([]byte, c.settings.FragmentSize()), make([]byte, c.settings.FragmentSize())
c.tx, c.rx = resizeBuffer(c.tx, c.settings.FragmentSize()), resizeBuffer(c.rx, c.settings.FragmentSize())
// reset buffered connector.
c.cc = c.cc.Resized(c.settings.FragmentSize())
}
// save negotiated header sign parameter.
+4 -1
View File
@@ -112,7 +112,10 @@ func (c *call) WriteBuffer(ctx context.Context, hdr Header, p []byte) error {
func (c *transport) WriteBuffer(ctx context.Context, hdr Header, p []byte) error {
if int(hdr.FragLength) > c.settings.MaxXmitFrag {
return ErrPacketTooLong
if hdr.PacketType != PacketTypeAlterContext {
// XXX: allow exceeding capacity for alter-context requests (as they may contain large krb tickets)
return ErrPacketTooLong
}
}
p = p[:hdr.FragLength]
+13
View File
@@ -14,6 +14,10 @@ type SyntaxID struct {
IfVersionMinor uint16
}
func (v *SyntaxID) Size() int {
return 16 + 2 + 2
}
func (v *SyntaxID) Is(other *SyntaxID) bool {
if v == nil || other == nil {
return false
@@ -85,6 +89,14 @@ type Context struct {
TransferSyntaxes []*SyntaxID
}
func (c *Context) Size() int {
size := 4 + c.AbstractSyntax.Size()
for i := range c.TransferSyntaxes {
size += c.TransferSyntaxes[i].Size()
}
return size
}
// marshal function ...
func (c *Context) WriteTo(ctx context.Context, w ndr.Writer) error {
w.WriteData(c.ContextID)
@@ -140,6 +152,7 @@ const (
AbstractSyntaxNotSupported ProviderReason = 0x0001
ProposedTransferSyntaxesNotSupported ProviderReason = 0x0002
LocalLimitExceeded ProviderReason = 0x0003
ProtocolVersionNotSupported ProviderReason = 0x0004
AuthTypeNotRecognized ProviderReason = 0x0008
InvalidChecksum ProviderReason = 0x0009