aboutsummaryrefslogtreecommitdiff
path: root/pkg/client/dcerpc/dcerpc.go
blob: a22bff75357221d6528b4b8b577a7396acb2c0b1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package dcerpc

import (
	"context"
	"errors"
	"fmt"
	"github.com/bryanmcnulty/adauth"
	"github.com/bryanmcnulty/adauth/dcerpcauth"
	"github.com/oiweiwei/go-msrpc/dcerpc"
	"github.com/oiweiwei/go-msrpc/msrpc/epm/epm/v3"
	"github.com/oiweiwei/go-msrpc/msrpc/scmr/svcctl/v2"
	"github.com/oiweiwei/go-msrpc/smb2"
	"github.com/oiweiwei/go-msrpc/ssp/gssapi"
	"github.com/rs/zerolog"
)

const (
	DceDefaultProto string = "ncacn_np"
	DceDefaultPort  uint16 = 445
)

type DCEClient struct {
	Port  uint16
	Proto string

	log      zerolog.Logger
	conn     dcerpc.Conn
	opts     []dcerpc.Option
	authOpts dcerpcauth.Options
}

func NewDCEClient(ctx context.Context, insecure bool, smbConfig *SmbConfig) (client *DCEClient) {
	client = &DCEClient{
		Port:     DceDefaultPort,
		Proto:    DceDefaultProto,
		log:      zerolog.Ctx(ctx).With().Str("client", "DCE").Logger(),
		authOpts: dcerpcauth.Options{},
	}
	client.opts = []dcerpc.Option{dcerpc.WithLogger(client.log)}
	client.authOpts = dcerpcauth.Options{Debug: client.log.Trace().Msgf}

	if smbConfig != nil {
		if smbConfig.Port != 0 {
			client.Port = smbConfig.Port
			client.opts = append(client.opts, dcerpc.WithSMBPort(int(smbConfig.Port)))
		}
	}
	if insecure {
		client.log.Debug().Msg("Using insecure DCERPC connection")
		client.opts = append(client.opts, dcerpc.WithInsecure())
	} else {
		client.log.Debug().Msg("Using secure DCERPC connection")
		client.authOpts.SMBOptions = append(client.authOpts.SMBOptions, smb2.WithSeal())
	}
	return
}

func (client *DCEClient) OpenSvcctl(ctx context.Context) (ctl svcctl.SvcctlClient, err error) {
	if client.conn == nil {
		return nil, errors.New("DCE connection not open")
	}
	if ctl, err = svcctl.NewSvcctlClient(ctx, client.conn, dcerpc.WithInsecure()); err != nil {
		client.log.Debug().Err(err).Msg("Failed to open Svcctl client")
	}
	return
}

func (client *DCEClient) Connect(ctx context.Context, creds *adauth.Credential, target *adauth.Target, dialOpts ...dcerpc.Option) (err error) {
	if creds != nil && target != nil {
		authCtx := gssapi.NewSecurityContext(ctx)

		binding := fmt.Sprintf(`%s:%s`, client.Proto, target.AddressWithoutPort())
		mapper := epm.EndpointMapper(ctx, fmt.Sprintf("%s:%d", target.AddressWithoutPort(), client.Port), dcerpc.WithLogger(client.log))
		dceOpts := []dcerpc.Option{dcerpc.WithLogger(client.log), dcerpc.WithSeal()}

		if dceOpts, err = dcerpcauth.AuthenticationOptions(authCtx, creds, target, &client.authOpts); err == nil {
			dceOpts = append(dceOpts, mapper)
			dceOpts = append(dceOpts, client.opts...)
			dceOpts = append(dceOpts, dialOpts...)

			if client.conn, err = dcerpc.Dial(authCtx, binding, dceOpts...); err == nil {
				client.log.Debug().Msg("Bind successful")
				return nil
			}
			client.log.Debug().Err(err).Msg("DCERPC bind failed")
			return errors.New("bind failed")
		}
		return errors.New("unable to parse DCE authentication options")
	}
	return errors.New("invalid arguments")
}

func (client *DCEClient) Close(ctx context.Context) (err error) {
	if client.conn == nil {
		client.log.Debug().Msg("Connection already closed")
	} else if err = client.conn.Close(ctx); err == nil {
		client.log.Debug().Msg("Connection closed successfully")
	} else {
		client.log.Error().Err(err).Msg("Failed to close connection")
	}
	return
}