diff --git a/config/config.go b/config/config.go index 2d3aea35..559b5440 100644 --- a/config/config.go +++ b/config/config.go @@ -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 } diff --git a/config/flag/command_line.go b/config/flag/command_line.go index 3b144923..9bea2a7d 100644 --- a/config/flag/command_line.go +++ b/config/flag/command_line.go @@ -18,7 +18,8 @@ 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") diff --git a/dcerpc/conn.go b/dcerpc/conn.go index 3c248553..c6826011 100644 --- a/dcerpc/conn.go +++ b/dcerpc/conn.go @@ -24,11 +24,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 } diff --git a/dcerpc/packet.go b/dcerpc/packet.go index 6e0ced6c..64fa565d 100644 --- a/dcerpc/packet.go +++ b/dcerpc/packet.go @@ -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,20 @@ 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 { + 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) diff --git a/dcerpc/pdu.go b/dcerpc/pdu.go index 1ab53218..baf3e2cc 100644 --- a/dcerpc/pdu.go +++ b/dcerpc/pdu.go @@ -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" } diff --git a/dcerpc/transport.go b/dcerpc/transport.go index 53babd41..a460d3d1 100644 --- a/dcerpc/transport.go +++ b/dcerpc/transport.go @@ -150,7 +150,6 @@ func (c *transport) ExportSMBSecurity(o *Security) { o.SetAttribute(gssapi.AttributeSMBEffectiveSessionKey, pipe.SessionKey()) } } - } // AlterContext function establishes new presentation or security (or both) context(s). @@ -193,11 +192,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) } @@ -384,6 +385,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) @@ -397,11 +410,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. @@ -414,7 +422,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. diff --git a/dcerpc/types.go b/dcerpc/types.go index 0a7af863..4e8e1073 100644 --- a/dcerpc/types.go +++ b/dcerpc/types.go @@ -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