forked from hirochachacha/go-smb2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
initiator.go
167 lines (128 loc) · 3.5 KB
/
initiator.go
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package smb2
import (
"encoding/asn1"
"github.com/hirochachacha/go-smb2/internal/ntlm"
"github.com/hirochachacha/go-smb2/internal/spnego"
)
type Initiator interface {
oid() asn1.ObjectIdentifier
init(ctx *interface{}, inputToken []byte) (outputToken []byte, done bool, err error) // GSS_Init_sec_context with GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG
sum(ctx *interface{}, input []byte) []byte // GSS_getMIC
sessionKey(ctx *interface{}) []byte // QueryContextAttributes(ctx, SECPKG_ATTR_SESSION_KEY, &out)
}
type spnegoInitiatorContext struct {
mechList []asn1.ObjectIdentifier
ctx interface{}
}
type spnegoInitiator struct {
i Initiator
}
func (i *spnegoInitiator) init(ctx *interface{}, inputToken []byte) (outputToken []byte, done bool, err error) {
if *ctx == nil {
c := new(spnegoInitiatorContext)
// DecodeNegTokenInit2
outputToken, done, err = i.i.init(&c.ctx, nil)
if err != nil {
return nil, false, err
}
c.mechList = []asn1.ObjectIdentifier{i.i.oid()}
negTokenInitBytes, err := spnego.EncodeNegTokenInit(c.mechList, outputToken)
if err != nil {
return nil, false, err
}
*ctx = c
return negTokenInitBytes, done, nil
}
c, ok := (*ctx).(*spnegoInitiatorContext)
if !ok {
return nil, false, &InvalidResponseError{"invalid spnego context"}
}
negTokenResp, err := spnego.DecodeNegTokenResp(inputToken)
if err != nil {
return nil, false, err
}
outputToken, done, err = i.i.init(&c.ctx, negTokenResp.ResponseToken)
if err != nil {
return nil, false, err
}
ms, err := asn1.Marshal(c.mechList)
if err != nil {
return nil, false, err
}
mechListMIC := i.i.sum(&c.ctx, ms)
negTokenRespBytes, err := spnego.EncodeNegTokenResp(1, nil, outputToken, mechListMIC)
if err != nil {
return nil, false, err
}
return negTokenRespBytes, done, nil
}
type ntlmInitiatorContext struct {
c *ntlm.Client
nmsg []byte
cs *ntlm.Session
seqNum uint32
}
// NTLMInitiator implements session-setup through NTLMv2.
// It doesn't support NTLMv1. You can use Hash instead of Password.
type NTLMInitiator struct {
User string
Password string
Hash []byte
Domain string
Workstation string
TargetSPN string
}
func (i *NTLMInitiator) oid() asn1.ObjectIdentifier {
return spnego.NlmpOid
}
func (i *NTLMInitiator) init(ctx *interface{}, inputToken []byte) (outputToken []byte, done bool, err error) {
if *ctx == nil { // Negotiate
c := &ntlmInitiatorContext{
c: &ntlm.Client{
User: i.User,
Password: i.Password,
Hash: i.Hash,
Domain: i.Domain,
Workstation: i.Workstation,
TargetSPN: i.TargetSPN,
},
}
nmsg, err := c.c.Negotiate()
if err != nil {
return nil, false, err
}
c.nmsg = nmsg
*ctx = c
return nmsg, false, nil
}
// Authenticate
c, ok := (*ctx).(*ntlmInitiatorContext)
if !ok {
return nil, false, &InvalidResponseError{"invalid ntlm context"}
}
cs, amsg, err := c.c.Authenticate(c.nmsg, inputToken)
if err != nil {
return nil, false, err
}
c.cs = cs
return amsg, true, nil
}
func (i *NTLMInitiator) sum(ctx *interface{}, input []byte) []byte {
if *ctx == nil {
return nil
}
if c, ok := (*ctx).(*ntlmInitiatorContext); ok {
sum, _ := c.cs.Sum(input, c.seqNum)
return sum
}
return nil
}
func (i *NTLMInitiator) sessionKey(ctx *interface{}) []byte {
if *ctx == nil {
return nil
}
if c, ok := (*ctx).(*ntlmInitiatorContext); ok {
return c.cs.SessionKey()
}
return nil
}