Skip to content

Commit 1fddc8d

Browse files
manninglucasnixprime
authored andcommitted
Add support for passing credentials over host FDs.
Credentials are set up using the sentry PID since sandboxed pids are nonsense in the host context. Co-authored-by: Jamie Liu <jamieliu@google.com> PiperOrigin-RevId: 833990261
1 parent 2d94e66 commit 1fddc8d

File tree

3 files changed

+58
-7
lines changed

3 files changed

+58
-7
lines changed

pkg/sentry/socket/unix/transport/host.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ func (c *HostConnectedEndpoint) Send(ctx context.Context, data [][]byte, control
164164
c.mu.RLock()
165165
defer c.mu.RUnlock()
166166

167-
if !controlMessages.Empty() {
167+
if controlMessages.Rights != nil {
168168
return 0, false, syserr.ErrInvalidEndpointState
169169
}
170170

@@ -176,7 +176,7 @@ func (c *HostConnectedEndpoint) Send(ctx context.Context, data [][]byte, control
176176
// only as much of the message as fits in the send buffer.
177177
truncate := c.stype == linux.SOCK_STREAM
178178

179-
n, totalLen, err := fdWriteVec(c.fd, data, c.SendMaxQueueSize(), truncate)
179+
n, totalLen, err := fdWriteVec(c.fd, data, c.SendMaxQueueSize(), truncate, controlMessages.Credentials != nil)
180180
if n < totalLen && err == nil {
181181
// The host only returns a short write if it would otherwise
182182
// block (and only for stream sockets).
@@ -236,8 +236,7 @@ func (c *HostConnectedEndpoint) Writable() bool {
236236

237237
// Passcred implements ConnectedEndpoint.Passcred.
238238
func (c *HostConnectedEndpoint) Passcred() bool {
239-
// We don't support credential passing for host sockets.
240-
return false
239+
return passcredsEnabled(c.fd)
241240
}
242241

243242
// GetLocalAddress implements ConnectedEndpoint.GetLocalAddress.
@@ -269,6 +268,11 @@ func (c *HostConnectedEndpoint) Recv(ctx context.Context, data [][]byte, args Re
269268

270269
// N.B. Unix sockets don't have a receive buffer, the send buffer
271270
// serves both purposes.
271+
//
272+
// We ignore args.Creds because we don't translate sandbox socket options to
273+
// host socket options, so the fd won't have the SO_PASSCRED option set. By
274+
// default, the sentry will always return credentials with PID 0 and UID/GID
275+
// 65534 (nobody).
272276
out := RecvOutput{Source: Address{Addr: c.addr}}
273277
var err error
274278
var controlLen uint64

pkg/sentry/socket/unix/transport/host_unsafe.go

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,15 @@
1515
package transport
1616

1717
import (
18+
"time"
1819
"unsafe"
1920

2021
"golang.org/x/sys/unix"
22+
"gvisor.dev/gvisor/pkg/log"
2123
)
2224

25+
var usingRootCredsLogger = log.BasicRateLimitedLogger(time.Minute)
26+
2327
// fdReadVec receives from fd to bufs.
2428
//
2529
// If the total length of bufs is > maxlen, fdReadVec will do a partial read
@@ -72,9 +76,9 @@ func fdReadVec(fd int, bufs [][]byte, control []byte, peek bool, maxlen int64) (
7276

7377
// fdWriteVec sends from bufs to fd.
7478
//
75-
// If the total length of bufs is > maxlen && truncate, fdWriteVec will do a
76-
// partial write and err will indicate why the message was truncated.
77-
func fdWriteVec(fd int, bufs [][]byte, maxlen int64, truncate bool) (int64, int64, error) {
79+
// If the total length of bufs is > maxlen && truncate, fdWriteVec will do
80+
// a partial write and err will indicate why the message was truncated.
81+
func fdWriteVec(fd int, bufs [][]byte, maxlen int64, truncate bool, creds bool) (int64, int64, error) {
7882
length, iovecs, intermediate, err := buildIovec(bufs, maxlen, truncate)
7983
if err != nil && len(iovecs) == 0 {
8084
// No partial write to do, return error immediately.
@@ -92,6 +96,36 @@ func fdWriteVec(fd int, bufs [][]byte, maxlen int64, truncate bool) (int64, int6
9296
msg.Iovlen = uint64(len(iovecs))
9397
}
9498

99+
if creds {
100+
// We can't pass arbitrary application credentials to the host. Pass the
101+
// sentry's credentials instead.
102+
cmsgSlice := make([]byte, unix.CmsgSpace(unix.SizeofUcred))
103+
cmsg := (*unix.Cmsghdr)(unsafe.Pointer(&cmsgSlice[0]))
104+
cmsg.Level = unix.SOL_SOCKET
105+
cmsg.Type = unix.SCM_CREDENTIALS
106+
cmsgLen := unix.CmsgLen(unix.SizeofUcred)
107+
cmsg.SetLen(cmsgLen)
108+
cmsgData := (*unix.Ucred)(unsafe.Pointer(&cmsgSlice[unix.CmsgLen(0)]))
109+
// Collect owner information for fd. Gete(uid|gid) are forbidden by the
110+
// sentry's seccomp filters.
111+
s := unix.Stat_t{}
112+
if err := unix.Fstat(fd, &s); err != nil {
113+
return 0, length, err
114+
}
115+
if s.Uid == 0 || s.Gid == 0 {
116+
usingRootCredsLogger.Warningf("gVisor does not support setting UID or GID to root in SCM credentials sent from the sentry")
117+
return 0, length, unix.EINVAL
118+
}
119+
pid, _, err := unix.RawSyscall(unix.SYS_GETPID, 0, 0, 0)
120+
if err != 0 {
121+
return 0, length, err
122+
}
123+
*cmsgData = unix.Ucred{Pid: int32(pid), Uid: s.Uid, Gid: s.Gid}
124+
125+
msg.Control = &cmsgSlice[0]
126+
msg.SetControllen(cmsgLen)
127+
}
128+
95129
n, _, e := unix.RawSyscall(unix.SYS_SENDMSG, uintptr(fd), uintptr(unsafe.Pointer(&msg)), unix.MSG_DONTWAIT|unix.MSG_NOSIGNAL)
96130
if e != 0 {
97131
// N.B. prioritize the syscall error over the buildIovec error.
@@ -100,3 +134,11 @@ func fdWriteVec(fd int, bufs [][]byte, maxlen int64, truncate bool) (int64, int6
100134

101135
return int64(n), length, err
102136
}
137+
138+
func passcredsEnabled(fd int) bool {
139+
enabled, err := unix.GetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_PASSCRED)
140+
if err != nil {
141+
return false
142+
}
143+
return enabled != 0
144+
}

runsc/boot/filter/config/config_main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@ var allowedSyscalls = seccomp.MakeSyscallRules(map[uintptr]seccomp.SyscallRule{
105105
seccomp.EqualTo(unix.SOL_SOCKET),
106106
seccomp.EqualTo(unix.SO_SNDBUF),
107107
},
108+
seccomp.PerArg{
109+
seccomp.AnyValue{},
110+
seccomp.EqualTo(unix.SOL_SOCKET),
111+
seccomp.EqualTo(unix.SO_PASSCRED),
112+
},
108113
},
109114
unix.SYS_GETTID: seccomp.MatchAll{},
110115
unix.SYS_GETTIMEOFDAY: seccomp.MatchAll{},

0 commit comments

Comments
 (0)