-
-
Notifications
You must be signed in to change notification settings - Fork 307
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/systemd query events #1728
Changes from 11 commits
9d26cd2
811a3d9
194eac2
b3b1615
8345c17
a873a01
bf6d8b5
1a6237b
f189adc
6137358
5019a12
7f2b8fc
1b4f5f1
dff2f34
eda62f4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,3 +52,4 @@ go.work.sum | |
|
||
# Kext releases | ||
windows_kext/release/kext_release_*.zip | ||
windows_core_dll/.vs/windows_core_dll |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,7 +24,6 @@ linters: | |
- interfacer | ||
- ireturn | ||
- lll | ||
- mnd | ||
- musttag | ||
- nestif | ||
- nilnil | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,8 +43,24 @@ func PreventBypassing(ctx context.Context, conn *network.Connection) (endpoints. | |
return endpoints.NoMatch, "", nil | ||
} | ||
|
||
// If Portmaster resolver is disabled allow requests going to system dns resolver. | ||
// And allow all connections out of the System Resolver. | ||
if module.instance.Resolver().IsDisabled() { | ||
// TODO(vladimir): Is there a more specific check that can be done? | ||
if conn.Process().IsSystemResolver() { | ||
return endpoints.NoMatch, "", nil | ||
} | ||
if conn.Entity.Port == 53 && conn.Entity.IPScope.IsLocalhost() { | ||
return endpoints.NoMatch, "", nil | ||
} | ||
Comment on lines
+53
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can allow any normal DNS, as we can see the packets. We only need to prevent encrypted DNS connections from apps. This is getting complicated. Maybe we can simplify and regroup the logic here? Let's talk about options. |
||
} | ||
vlabo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Block bypass attempts using an (encrypted) DNS server. | ||
switch { | ||
case looksLikeOutgoingDNSRequest(conn) && module.instance.Resolver().IsDisabled(): | ||
// Allow. Packet will be analyzed and blocked if its not a dns request, before sent. | ||
conn.Inspecting = true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. conn.Inspecting is currently unused. Are you using it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
return endpoints.NoMatch, "", nil | ||
Comment on lines
+60
to
+63
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Security: Enhance DNS inspection mechanism The current implementation allows packets before inspection completes, which could create a small window of opportunity for attacks. Consider these improvements:
case looksLikeOutgoingDNSRequest(conn) && module.instance.Resolver().IsDisabled():
- // Allow. Packet will be analyzed and blocked if its not a dns request, before sent.
conn.Inspecting = true
+ conn.SetInspectionTimeout(time.Second * 5) // Add timeout
+ conn.BufferInitialPackets = true // Buffer until inspection
return endpoints.NoMatch, "", nil
|
||
case conn.Entity.Port == 53: | ||
return endpoints.Denied, | ||
"blocked DNS query, manual dns setup required", | ||
|
@@ -62,3 +78,17 @@ func PreventBypassing(ctx context.Context, conn *network.Connection) (endpoints. | |
|
||
return endpoints.NoMatch, "", nil | ||
} | ||
|
||
func looksLikeOutgoingDNSRequest(conn *network.Connection) bool { | ||
// Outbound on remote port 53, UDP. | ||
if conn.Inbound { | ||
return false | ||
} | ||
if conn.Entity.Port != 53 { | ||
return false | ||
} | ||
if conn.IPProtocol != packet.UDP { | ||
return false | ||
} | ||
return true | ||
} | ||
vlabo marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
//go:build windows | ||
// +build windows | ||
|
||
package dnsmonitor | ||
|
||
import ( | ||
"fmt" | ||
"runtime" | ||
"sync" | ||
"sync/atomic" | ||
|
||
"github.com/safing/portmaster/service/integration" | ||
"golang.org/x/sys/windows" | ||
) | ||
|
||
type ETWSession struct { | ||
i integration.ETWFunctions | ||
|
||
shutdownGuard atomic.Bool | ||
shutdownMutex sync.Mutex | ||
|
||
state uintptr | ||
} | ||
|
||
// NewSession creates new ETW event listener and initilizes it. This is a low level interface, make sure to call DestorySession when you are done using it. | ||
func NewSession(etwInterface integration.ETWFunctions, callback func(domain string, result string)) (*ETWSession, error) { | ||
etwSession := &ETWSession{ | ||
i: etwInterface, | ||
} | ||
|
||
// Make sure session from previous instances are not running. | ||
_ = etwSession.i.StopOldSession() | ||
|
||
// Initialize notification activated callback | ||
win32Callback := windows.NewCallback(func(domain *uint16, result *uint16) uintptr { | ||
callback(windows.UTF16PtrToString(domain), windows.UTF16PtrToString(result)) | ||
return 0 | ||
}) | ||
// The function only allocates memory it will not fail. | ||
etwSession.state = etwSession.i.CreateState(win32Callback) | ||
|
||
// Make sure DestroySession is called even if caller forgets to call it. | ||
runtime.SetFinalizer(etwSession, func(s *ETWSession) { | ||
_ = s.i.DestroySession(s.state) | ||
}) | ||
|
||
// Initialize session. | ||
err := etwSession.i.InitializeSession(etwSession.state) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to initialzie session: %q", err) | ||
} | ||
|
||
return etwSession, nil | ||
} | ||
|
||
// StartTrace starts the tracing session of dns events. This is a blocking call. It will not return until the trace is stopped. | ||
func (l *ETWSession) StartTrace() error { | ||
return l.i.StartTrace(l.state) | ||
} | ||
Comment on lines
+57
to
+59
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prevent method calls after session is destroyed The methods Add checks to ensure the session is still running before executing these methods. Apply the following changes to add safety checks: func (l *ETWSession) StartTrace() error {
+ if l.shutdownGuard.Load() {
+ return fmt.Errorf("cannot start trace; session has been destroyed")
+ }
return l.i.StartTrace(l.state)
}
func (l *ETWSession) StopTrace() error {
+ if l.shutdownGuard.Load() {
+ return fmt.Errorf("cannot stop trace; session has been destroyed")
+ }
return l.i.StopTrace(l.state)
} Also applies to: 81-82 |
||
|
||
// IsRunning returns true if DestroySession has NOT been called. | ||
func (l *ETWSession) IsRunning() bool { | ||
return !l.shutdownGuard.Load() | ||
} | ||
|
||
// FlushTrace flushes the trace buffer. | ||
func (l *ETWSession) FlushTrace() error { | ||
l.shutdownMutex.Lock() | ||
defer l.shutdownMutex.Unlock() | ||
|
||
// Make sure session is still running. | ||
if l.shutdownGuard.Load() { | ||
return nil | ||
} | ||
|
||
return l.i.FlushTrace(l.state) | ||
} | ||
|
||
// StopTrace stopes the trace. This will cause StartTrace to return. | ||
func (l *ETWSession) StopTrace() error { | ||
return l.i.StopTrace(l.state) | ||
} | ||
|
||
// DestroySession closes the session and frees the allocated memory. Listener cannot be used after this function is called. | ||
func (l *ETWSession) DestroySession() error { | ||
l.shutdownMutex.Lock() | ||
defer l.shutdownMutex.Unlock() | ||
|
||
if l.shutdownGuard.Swap(true) { | ||
return nil | ||
} | ||
|
||
err := l.i.DestroySession(l.state) | ||
if err != nil { | ||
return err | ||
} | ||
l.state = 0 | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
//go:build !linux && !windows | ||
// +build !linux,!windows | ||
|
||
package dnsmonitor | ||
|
||
type Listener struct{} | ||
|
||
func newListener(_ *DNSMonitor) (*Listener, error) { | ||
return &Listener{}, nil | ||
} | ||
|
||
func (l *Listener) flush() error { | ||
// Nothing to flush | ||
return nil | ||
} | ||
|
||
func (l *Listener) stop() error { | ||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Missing Resolver() implementation in spn/instance.go
The verification reveals that
spn/instance.go
implementsNetEnv()
but lacks the newly addedResolver()
method. This implementation needs to be updated to maintain interface compatibility.spn/instance.go
: Add the missingResolver()
method to match the interface requirementsservice/instance.go
and test files are correctly updated🔗 Analysis chain
Verify interface implementation across the codebase.
The addition of
Resolver()
method to the interface looks good, but we should verify that all types implementing this interface have been updated.The previous attempt might have failed due to complex AST pattern. Let's try using ripgrep to:
The previous output shows we have potentially found an issue - the
spn/instance.go
implementation hasNetEnv()
but might be missingResolver()
. Let's verify the complete interface definition and check the full implementation in spn/instance.go to confirm this.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
Length of output: 95
Script:
Length of output: 695
Script:
Length of output: 649