/
kext.go
335 lines (271 loc) Β· 8.03 KB
/
kext.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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
//go:build windows
// +build windows
package windowskext
import (
"errors"
"fmt"
"sync"
"syscall"
"unsafe"
"github.com/safing/portbase/log"
"github.com/safing/portmaster/network"
"github.com/safing/portmaster/network/packet"
"golang.org/x/sys/windows"
)
// Package errors
var (
ErrKextNotReady = errors.New("the windows kernel extension (driver) is not ready to accept commands")
ErrNoPacketID = errors.New("the packet has no ID, possibly because it was fast-tracked by the kernel extension")
kextLock sync.RWMutex
driverPath string
kextHandle windows.Handle
service *KextService
)
const (
winErrInvalidData = uintptr(windows.ERROR_INVALID_DATA)
winInvalidHandleValue = windows.Handle(^uintptr(0)) // Max value
driverName = "PortmasterKext"
)
// Init initializes the DLL and the Kext (Kernel Driver).
func Init(path string) error {
kextHandle = winInvalidHandleValue
driverPath = path
return nil
}
// Start intercepting.
func Start() error {
kextLock.Lock()
defer kextLock.Unlock()
// initialize and start driver service
var err error
service, err = createKextService(driverName, driverPath)
if err != nil {
return fmt.Errorf("failed to create service: %w", err)
}
running, err := service.isRunning()
if err == nil && !running {
err = service.start(true)
if err != nil {
return fmt.Errorf("failed to start service: %w", err)
}
} else if err != nil {
return fmt.Errorf("service not initialized: %w", err)
}
// Open the driver
filename := `\\.\` + driverName
kextHandle, err = openDriver(filename)
// driver was not installed
if err != nil {
return fmt.Errorf("failed to open driver: %q %w", filename, err)
}
return nil
}
// Stop intercepting.
func Stop() error {
// Prepare kernel for shutdown
err := shutdownRequest()
if err != nil {
log.Warningf("winkext: shutdown request failed: %s", err)
}
kextLock.Lock()
defer kextLock.Unlock()
err = closeDriver(kextHandle)
if err != nil {
log.Warningf("winkext: failed to close the handle: %s", err)
}
err = service.stop(true)
if err != nil {
log.Warningf("winkext: failed to stop service: %s", err)
}
// Driver file may change on the next start so it's better to delete the service
err = service.delete()
if err != nil {
log.Warningf("winkext: failed to delete service: %s", err)
}
kextHandle = winInvalidHandleValue
return nil
}
func shutdownRequest() error {
kextLock.RLock()
defer kextLock.RUnlock()
if kextHandle == winInvalidHandleValue {
return ErrKextNotReady
}
// Sent a shutdown request so the kernel extension can prepare.
_, err := deviceIOControl(kextHandle, IOCTL_SHUTDOWN_REQUEST, nil, nil)
return err
}
// RecvVerdictRequest waits for the next verdict request from the kext. If a timeout is reached, both *VerdictRequest and error will be nil.
func RecvVerdictRequest() (*VerdictRequest, error) {
kextLock.RLock()
defer kextLock.RUnlock()
if kextHandle == winInvalidHandleValue {
return nil, ErrKextNotReady
}
// DEBUG:
// timestamp := time.Now()
// defer log.Tracef("winkext: getting verdict request took %s", time.Since(timestamp))
// Initialize struct for the output data
var new VerdictRequest
// Make driver request
data := asByteArray(&new)
bytesRead, err := deviceIOControl(kextHandle, IOCTL_RECV_VERDICT_REQ, nil, data)
if err != nil {
return nil, err
}
if bytesRead == 0 {
return nil, nil // no error, no new verdict request
}
return &new, nil
}
// SetVerdict sets the verdict for a packet and/or connection.
func SetVerdict(pkt *Packet, verdict network.Verdict) error {
if pkt.verdictRequest.pid != 0 {
return nil // Ignore info only packets
}
if pkt.verdictRequest.id == 0 {
log.Tracer(pkt.Ctx()).Errorf("kext: failed to set verdict %s: no packet ID", verdict)
return ErrNoPacketID
}
kextLock.RLock()
defer kextLock.RUnlock()
if kextHandle == winInvalidHandleValue {
log.Tracer(pkt.Ctx()).Errorf("kext: failed to set verdict %s: kext not ready", verdict)
return ErrKextNotReady
}
verdictInfo := VerdictInfo{pkt.verdictRequest.id, verdict}
// Make driver request
data := asByteArray(&verdictInfo)
_, err := deviceIOControl(kextHandle, IOCTL_SET_VERDICT, data, nil)
if err != nil {
log.Tracer(pkt.Ctx()).Errorf("kext: failed to set verdict %s on packet %d", verdict, pkt.verdictRequest.id)
return err
}
return nil
}
// GetPayload returns the payload of a packet.
func GetPayload(packetID uint32, packetSize uint32) ([]byte, error) {
if packetID == 0 {
return nil, ErrNoPacketID
}
// Check if driver is initialized
kextLock.RLock()
defer kextLock.RUnlock()
if kextHandle == winInvalidHandleValue {
return nil, ErrKextNotReady
}
buf := make([]byte, packetSize)
// Combine id and length
payload := struct {
id uint32
length uint32
}{packetID, packetSize}
// Make driver request
data := asByteArray(&payload)
bytesRead, err := deviceIOControl(kextHandle, IOCTL_GET_PAYLOAD, data, unsafe.Slice(&buf[0], packetSize))
if err != nil {
return nil, err
}
// check the result and return
if bytesRead == 0 {
return nil, errors.New("windows kext did not return any data")
}
if bytesRead < uint32(len(buf)) {
return buf[:bytesRead], nil
}
return buf, nil
}
func ClearCache() error {
kextLock.RLock()
defer kextLock.RUnlock()
// Check if driver is initialized
if kextHandle == winInvalidHandleValue {
log.Error("kext: failed to clear the cache: kext not ready")
return ErrKextNotReady
}
// Make driver request
_, err := deviceIOControl(kextHandle, IOCTL_CLEAR_CACHE, nil, nil)
return err
}
func UpdateVerdict(conn *network.Connection) error {
kextLock.RLock()
defer kextLock.RUnlock()
// Check if driver is initialized
if kextHandle == winInvalidHandleValue {
log.Error("kext: failed to clear the cache: kext not ready")
return ErrKextNotReady
}
var isIpv6 uint8 = 0
if conn.IPVersion == packet.IPv6 {
isIpv6 = 1
}
// initialize variables
info := VerdictUpdateInfo{
ipV6: isIpv6,
protocol: uint8(conn.IPProtocol),
localIP: ipAddressToArray(conn.LocalIP, isIpv6 == 1),
localPort: conn.LocalPort,
remoteIP: ipAddressToArray(conn.Entity.IP, isIpv6 == 1),
remotePort: conn.Entity.Port,
verdict: uint8(conn.Verdict.Active),
}
// Make driver request
data := asByteArray(&info)
_, err := deviceIOControl(kextHandle, IOCTL_UPDATE_VERDICT, data, nil)
return err
}
func GetVersion() (*VersionInfo, error) {
kextLock.RLock()
defer kextLock.RUnlock()
// Check if driver is initialized
if kextHandle == winInvalidHandleValue {
log.Error("kext: failed to clear the cache: kext not ready")
return nil, ErrKextNotReady
}
data := make([]uint8, 4)
_, err := deviceIOControl(kextHandle, IOCTL_VERSION, nil, data)
if err != nil {
return nil, err
}
version := &VersionInfo{
major: data[0],
minor: data[1],
revision: data[2],
build: data[3],
}
return version, nil
}
var sizeOfConnectionStat = uint32(unsafe.Sizeof(ConnectionStat{}))
func GetConnectionsStats() ([]ConnectionStat, error) {
kextLock.RLock()
defer kextLock.RUnlock()
// Check if driver is initialized
if kextHandle == winInvalidHandleValue {
log.Error("kext: failed to clear the cache: kext not ready")
return nil, ErrKextNotReady
}
var data [100]ConnectionStat
size := len(data)
bytesReturned, err := deviceIOControl(kextHandle, IOCTL_GET_CONNECTIONS_STAT, asByteArray(&size), asByteArray(&data))
if err != nil {
return nil, err
}
return data[:bytesReturned/sizeOfConnectionStat], nil
}
func openDriver(filename string) (windows.Handle, error) {
u16filename, err := syscall.UTF16FromString(filename)
if err != nil {
return winInvalidHandleValue, fmt.Errorf("failed to convert driver filename to UTF16 string %w", err)
}
handle, err := windows.CreateFile(&u16filename[0], windows.GENERIC_READ|windows.GENERIC_WRITE, 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL|windows.FILE_FLAG_OVERLAPPED, 0)
if err != nil {
return winInvalidHandleValue, err
}
return handle, nil
}
func closeDriver(handle windows.Handle) error {
if kextHandle == winInvalidHandleValue {
return ErrKextNotReady
}
return windows.CloseHandle(handle)
}