Skip to content

Commit 8f5502b

Browse files
committed
sockets: implement WithAdditionalUsersAndGroups for windows
- Implement a WithAdditionalUsersAndGroups (windows daemon allows specifying multiple additional users and groups for named pipes and unix-sockets). - Implement NewUnixSocket that accepts (optional) additional users and groups. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
1 parent d2fda56 commit 8f5502b

1 file changed

Lines changed: 100 additions & 1 deletion

File tree

sockets/unix_socket_windows.go

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,105 @@
11
package sockets
22

3-
import "net"
3+
import (
4+
"errors"
5+
"fmt"
6+
"net"
7+
"strings"
8+
9+
"github.com/Microsoft/go-winio"
10+
"golang.org/x/sys/windows"
11+
)
12+
13+
// DefaultPermissions defines the default DACL, which allows Administrators
14+
// and LocalSystem full access (similar to defaults used in [moby]);
15+
//
16+
// - D:P: DACL without inheritance (protected, (P)).
17+
// - (A;;GA;;;BA): Allow full access (GA) for built-in Administrators (BA).
18+
// - (A;;GA;;;SY); Allow full access (GA) for LocalSystem (SY).
19+
// - Any other user is denied access.
20+
//
21+
// [moby]: https://github.com/moby/moby/blob/6b45c76a233b1b8b56465f76c21c09fd7920e82d/daemon/listeners/listeners_windows.go#L53-L59
22+
const DefaultPermissions = "D:P(A;;GA;;;BA)(A;;GA;;;SY)"
23+
24+
// WithAdditionalUsersAndGroups modifies the socket file's DACL to grant
25+
// access to additional users and groups.
26+
//
27+
// By default, it grants [DefaultPermissions], but allows for additional
28+
// users and groups to get generic read (GR) and write (GW) access. It
29+
// returns an error when failing to resolve any of the additional users
30+
// and groups, or when failing to apply the ACL.
31+
func WithAdditionalUsersAndGroups(sids []string) SockOption {
32+
return func(path string) error {
33+
if len(sids) == 0 {
34+
return errors.New("no additional users specified")
35+
}
36+
sd, err := getSecurityDescriptor(sids)
37+
if err != nil {
38+
return fmt.Errorf("looking up SID: %w", err)
39+
}
40+
return applySecurityDescriptor(path, sd)
41+
}
42+
}
43+
44+
// NewUnixSocket creates a unix socket.
45+
//
46+
// By default, it grants [DefaultPermissions], but allows for additional
47+
// users and groups to get generic read (GR) and write (GW) access. It
48+
// returns an error when failing to resolve any of the additional users
49+
// and groups, or when failing to apply the ACL.
50+
func NewUnixSocket(path string, additionalUsersAndGroups []string) (net.Listener, error) {
51+
return NewUnixSocketWithOpts(path, WithAdditionalUsersAndGroups(additionalUsersAndGroups))
52+
}
53+
54+
// getSecurityDescriptor returns the DACL for the Unix socket.
55+
//
56+
// By default, it grants [DefaultPermissions], but allows for additional
57+
// users and groups to get generic read (GR) and write (GW) access. It
58+
// returns an error when failing to resolve any of the additional users
59+
// and groups.
60+
func getSecurityDescriptor(additionalUsersAndGroups []string) (*windows.SECURITY_DESCRIPTOR, error) {
61+
sddl := DefaultPermissions
62+
63+
// Grant generic read (GR) and write (GW) access to whatever
64+
// additional users or groups were specified.
65+
for _, g := range additionalUsersAndGroups {
66+
sid, err := winio.LookupSidByName(strings.TrimSpace(g))
67+
if err != nil {
68+
return nil, fmt.Errorf("looking up SID: %w", err)
69+
}
70+
sddl += fmt.Sprintf("(A;;GRGW;;;%s)", sid)
71+
}
72+
sd, err := windows.SecurityDescriptorFromString(sddl)
73+
if err != nil {
74+
return nil, fmt.Errorf("parsing SDDL: %w", err)
75+
}
76+
return sd, nil
77+
}
78+
79+
func applySecurityDescriptor(path string, sd *windows.SECURITY_DESCRIPTOR) error {
80+
dacl, defaulted, err := sd.DACL()
81+
if err != nil {
82+
return fmt.Errorf("extracting DACL: %w", err)
83+
}
84+
// These should never be hit with our [DefaultPermissions] as start,
85+
// as it contains "D:" and "P" (protected, don't inherit)/
86+
if dacl == nil || defaulted {
87+
return errors.New("no DACL found in security descriptor or defaulted")
88+
}
89+
group, _, err := sd.Group()
90+
if err != nil {
91+
return fmt.Errorf("extracting group SID: %w", err)
92+
}
93+
return windows.SetNamedSecurityInfo(
94+
path,
95+
windows.SE_FILE_OBJECT,
96+
windows.DACL_SECURITY_INFORMATION|windows.OWNER_SECURITY_INFORMATION,
97+
nil, // keep SYSTEM as owner.
98+
group, // group may be nil if no extra groups are specified.
99+
dacl,
100+
nil,
101+
)
102+
}
4103

5104
func listenUnix(path string) (net.Listener, error) {
6105
return net.Listen("unix", path)

0 commit comments

Comments
 (0)