Skip to content

Commit 9e0189f

Browse files
authored
Modify npipe to directly get the user SID (#62)
Remove the dependency on user.Current(). It fetches more information than is required to configure the named pipe. This only needs to know the SID in order to build the SDDL. This avoids the possibility of lengthy delays fetching the unnecessary metadata about the current user. Relates elastic/beats#31810 Porting over elastic/beats@1fcdee6
1 parent 5b85345 commit 9e0189f

File tree

2 files changed

+58
-24
lines changed

2 files changed

+58
-24
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
88

99
### Changed
1010

11+
- Modify npipe package to directly get the user SID.
12+
1113
### Deprecated
1214

1315
### Removed

api/npipe/listener_windows.go

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,15 @@ import (
2424
"context"
2525
"fmt"
2626
"net"
27-
"os/user"
2827
"strings"
28+
"syscall"
2929

30-
winio "github.com/Microsoft/go-winio"
30+
"github.com/Microsoft/go-winio"
3131
)
3232

33+
// ntAuthoritySystemSID is a well-known SID used by the NT AUTHORITY\SYSTEM account.
34+
const ntAuthoritySystemSID = "S-1-5-18"
35+
3336
// NewListener creates a new Listener receiving events over a named pipe.
3437
func NewListener(name, sd string) (net.Listener, error) {
3538
c := &winio.PipeConfig{
@@ -44,18 +47,14 @@ func NewListener(name, sd string) (net.Listener, error) {
4447
return l, nil
4548
}
4649

47-
// TransformString takes an input type name defined as a URI like `npipe:///hello` and transform it into
48-
// `\\.\pipe\hello`
50+
// TransformString takes an input type name defined as a URI like
51+
// `npipe:///hello` and transforms it into // `\\.\pipe\hello`
4952
func TransformString(name string) string {
5053
if strings.HasPrefix(name, "npipe:///") {
5154
path := strings.TrimPrefix(name, "npipe:///")
5255
return `\\.\pipe\` + path
5356
}
5457

55-
if strings.HasPrefix(name, `\\.\pipe\`) {
56-
return name
57-
}
58-
5958
return name
6059
}
6160

@@ -73,37 +72,70 @@ func Dial(npipe string) func(string, string) (net.Conn, error) {
7372
}
7473
}
7574

76-
// DefaultSD returns a default SecurityDescriptor which is the minimal required permissions to be
75+
// DefaultSD returns a default SecurityDescriptor that specifies the minimal required permissions to be
7776
// able to write to the named pipe. The security descriptor is returned in SDDL format.
7877
//
7978
// Docs: https://docs.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-string-format
8079
func DefaultSD(forUser string) (string, error) {
81-
var u *user.User
82-
var err error
83-
// No user configured we fallback to the current running user.
84-
if len(forUser) == 0 {
85-
u, err = user.Current()
86-
if err != nil {
87-
return "", fmt.Errorf("failed to retrieve the current user: %w", err)
88-
}
89-
} else {
90-
u, err = user.Lookup(forUser)
91-
if err != nil {
92-
return "", fmt.Errorf("failed to retrieve the user %s: %w", forUser, err)
93-
}
80+
sid, err := lookupSID(forUser)
81+
if err != nil {
82+
return "", err
9483
}
9584

9685
// Named pipe security and access rights.
9786
// We create the pipe and the specific users should only be able to write to it.
9887
// See docs: https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipe-security-and-access-rights
9988
// String definition: https://docs.microsoft.com/en-us/windows/win32/secauthz/ace-strings
10089
// Give generic read/write access to the specified user.
101-
descriptor := "D:P(A;;GA;;;" + u.Uid + ")"
102-
if u.Username == "NT AUTHORITY\\SYSTEM" {
90+
descriptor := "D:P(A;;GA;;;" + sid + ")"
91+
if sid == ntAuthoritySystemSID {
10392
// running as SYSTEM, include Administrators group so Administrators can talk over
10493
// the named pipe to the running Elastic Agent system process
10594
// https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems
10695
descriptor += "(A;;GA;;;S-1-5-32-544)" // Administrators group
10796
}
10897
return descriptor, nil
10998
}
99+
100+
// lookupSID returns the SID of the specified username. If username is empty the
101+
// SID of the current user is returned.
102+
func lookupSID(username string) (string, error) {
103+
if username == "" {
104+
sid, err := currentUserSID()
105+
if err != nil {
106+
return "", fmt.Errorf("failed to lookup the SID of current user: %w", err)
107+
}
108+
return sid, nil
109+
}
110+
111+
sid, _, _, err := syscall.LookupSID("", username)
112+
if err != nil {
113+
return "", fmt.Errorf("failed to lookup the SID for user %q: %w", username, err)
114+
}
115+
sidString, err := sid.String()
116+
if err != nil {
117+
return "", fmt.Errorf("failed to convert the SID for user %q to string: %w", username, err)
118+
}
119+
return sidString, nil
120+
}
121+
122+
// currentUserSID returns the SID of the user running the current process.
123+
func currentUserSID() (string, error) {
124+
t, err := syscall.OpenCurrentProcessToken()
125+
if err != nil {
126+
return "", err
127+
}
128+
defer t.Close()
129+
130+
u, err := t.GetTokenUser()
131+
if err != nil {
132+
return "", err
133+
}
134+
135+
sid, err := u.User.Sid.String()
136+
if err != nil {
137+
return "", err
138+
}
139+
140+
return sid, nil
141+
}

0 commit comments

Comments
 (0)