diff options
Diffstat (limited to 'pkg/sentry/strace/socket.go')
-rw-r--r-- | pkg/sentry/strace/socket.go | 674 |
1 files changed, 674 insertions, 0 deletions
diff --git a/pkg/sentry/strace/socket.go b/pkg/sentry/strace/socket.go new file mode 100644 index 000000000..48c072e96 --- /dev/null +++ b/pkg/sentry/strace/socket.go @@ -0,0 +1,674 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package strace + +import ( + "fmt" + "strings" + + "gvisor.googlesource.com/gvisor/pkg/abi" + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/binary" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/control" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/epsocket" + "gvisor.googlesource.com/gvisor/pkg/sentry/socket/netlink" + slinux "gvisor.googlesource.com/gvisor/pkg/sentry/syscalls/linux" + "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" +) + +// SocketFamily are the possible socket(2) families. +var SocketFamily = abi.ValueSet{ + { + Value: linux.AF_UNSPEC, + Name: "AF_UNSPEC", + }, + { + Value: linux.AF_UNIX, + Name: "AF_UNIX", + }, + { + Value: linux.AF_INET, + Name: "AF_INET", + }, + { + Value: linux.AF_AX25, + Name: "AF_AX25", + }, + { + Value: linux.AF_IPX, + Name: "AF_IPX", + }, + { + Value: linux.AF_APPLETALK, + Name: "AF_APPLETALK", + }, + { + Value: linux.AF_NETROM, + Name: "AF_NETROM", + }, + { + Value: linux.AF_BRIDGE, + Name: "AF_BRIDGE", + }, + { + Value: linux.AF_ATMPVC, + Name: "AF_ATMPVC", + }, + { + Value: linux.AF_X25, + Name: "AF_X25", + }, + { + Value: linux.AF_INET6, + Name: "AF_INET6", + }, + { + Value: linux.AF_ROSE, + Name: "AF_ROSE", + }, + { + Value: linux.AF_DECnet, + Name: "AF_DECnet", + }, + { + Value: linux.AF_NETBEUI, + Name: "AF_NETBEUI", + }, + { + Value: linux.AF_SECURITY, + Name: "AF_SECURITY", + }, + { + Value: linux.AF_KEY, + Name: "AF_KEY", + }, + { + Value: linux.AF_NETLINK, + Name: "AF_NETLINK", + }, + { + Value: linux.AF_PACKET, + Name: "AF_PACKET", + }, + { + Value: linux.AF_ASH, + Name: "AF_ASH", + }, + { + Value: linux.AF_ECONET, + Name: "AF_ECONET", + }, + { + Value: linux.AF_ATMSVC, + Name: "AF_ATMSVC", + }, + { + Value: linux.AF_RDS, + Name: "AF_RDS", + }, + { + Value: linux.AF_SNA, + Name: "AF_SNA", + }, + { + Value: linux.AF_IRDA, + Name: "AF_IRDA", + }, + { + Value: linux.AF_PPPOX, + Name: "AF_PPPOX", + }, + { + Value: linux.AF_WANPIPE, + Name: "AF_WANPIPE", + }, + { + Value: linux.AF_LLC, + Name: "AF_LLC", + }, + { + Value: linux.AF_IB, + Name: "AF_IB", + }, + { + Value: linux.AF_MPLS, + Name: "AF_MPLS", + }, + { + Value: linux.AF_CAN, + Name: "AF_CAN", + }, + { + Value: linux.AF_TIPC, + Name: "AF_TIPC", + }, + { + Value: linux.AF_BLUETOOTH, + Name: "AF_BLUETOOTH", + }, + { + Value: linux.AF_IUCV, + Name: "AF_IUCV", + }, + { + Value: linux.AF_RXRPC, + Name: "AF_RXRPC", + }, + { + Value: linux.AF_ISDN, + Name: "AF_ISDN", + }, + { + Value: linux.AF_PHONET, + Name: "AF_PHONET", + }, + { + Value: linux.AF_IEEE802154, + Name: "AF_IEEE802154", + }, + { + Value: linux.AF_CAIF, + Name: "AF_CAIF", + }, + { + Value: linux.AF_ALG, + Name: "AF_ALG", + }, + { + Value: linux.AF_NFC, + Name: "AF_NFC", + }, + { + Value: linux.AF_VSOCK, + Name: "AF_VSOCK", + }, +} + +// SocketType are the possible socket(2) types. +var SocketType = abi.ValueSet{ + { + Value: linux.SOCK_STREAM, + Name: "SOCK_STREAM", + }, + { + Value: linux.SOCK_DGRAM, + Name: "SOCK_DGRAM", + }, + { + Value: linux.SOCK_RAW, + Name: "SOCK_RAW", + }, + { + Value: linux.SOCK_RDM, + Name: "SOCK_RDM", + }, + { + Value: linux.SOCK_SEQPACKET, + Name: "SOCK_SEQPACKET", + }, + { + Value: linux.SOCK_DCCP, + Name: "SOCK_DCCP", + }, + { + Value: linux.SOCK_PACKET, + Name: "SOCK_PACKET", + }, +} + +// SocketFlagSet are the possible socket(2) flags. +var SocketFlagSet = abi.FlagSet{ + { + Flag: linux.SOCK_CLOEXEC, + Name: "SOCK_CLOEXEC", + }, + { + Flag: linux.SOCK_NONBLOCK, + Name: "SOCK_NONBLOCK", + }, +} + +// ipProtocol are the possible socket(2) types for INET and INET6 sockets. +var ipProtocol = abi.ValueSet{ + { + Value: linux.IPPROTO_IP, + Name: "IPPROTO_IP", + }, + { + Value: linux.IPPROTO_ICMP, + Name: "IPPROTO_ICMP", + }, + { + Value: linux.IPPROTO_IGMP, + Name: "IPPROTO_IGMP", + }, + { + Value: linux.IPPROTO_IPIP, + Name: "IPPROTO_IPIP", + }, + { + Value: linux.IPPROTO_TCP, + Name: "IPPROTO_TCP", + }, + { + Value: linux.IPPROTO_EGP, + Name: "IPPROTO_EGP", + }, + { + Value: linux.IPPROTO_PUP, + Name: "IPPROTO_PUP", + }, + { + Value: linux.IPPROTO_UDP, + Name: "IPPROTO_UDP", + }, + { + Value: linux.IPPROTO_IDP, + Name: "IPPROTO_IDP", + }, + { + Value: linux.IPPROTO_TP, + Name: "IPPROTO_TP", + }, + { + Value: linux.IPPROTO_DCCP, + Name: "IPPROTO_DCCP", + }, + { + Value: linux.IPPROTO_IPV6, + Name: "IPPROTO_IPV6", + }, + { + Value: linux.IPPROTO_RSVP, + Name: "IPPROTO_RSVP", + }, + { + Value: linux.IPPROTO_GRE, + Name: "IPPROTO_GRE", + }, + { + Value: linux.IPPROTO_ESP, + Name: "IPPROTO_ESP", + }, + { + Value: linux.IPPROTO_AH, + Name: "IPPROTO_AH", + }, + { + Value: linux.IPPROTO_MTP, + Name: "IPPROTO_MTP", + }, + { + Value: linux.IPPROTO_BEETPH, + Name: "IPPROTO_BEETPH", + }, + { + Value: linux.IPPROTO_ENCAP, + Name: "IPPROTO_ENCAP", + }, + { + Value: linux.IPPROTO_PIM, + Name: "IPPROTO_PIM", + }, + { + Value: linux.IPPROTO_COMP, + Name: "IPPROTO_COMP", + }, + { + Value: linux.IPPROTO_SCTP, + Name: "IPPROTO_SCTP", + }, + { + Value: linux.IPPROTO_UDPLITE, + Name: "IPPROTO_UDPLITE", + }, + { + Value: linux.IPPROTO_MPLS, + Name: "IPPROTO_MPLS", + }, + { + Value: linux.IPPROTO_RAW, + Name: "IPPROTO_RAW", + }, +} + +// SocketProtocol are the possible socket(2) protocols for each protocol family. +var SocketProtocol = map[int32]abi.ValueSet{ + linux.AF_INET: ipProtocol, + linux.AF_INET6: ipProtocol, + linux.AF_NETLINK: { + { + Value: linux.NETLINK_ROUTE, + Name: "NETLINK_ROUTE", + }, + { + Value: linux.NETLINK_UNUSED, + Name: "NETLINK_UNUSED", + }, + { + Value: linux.NETLINK_USERSOCK, + Name: "NETLINK_USERSOCK", + }, + { + Value: linux.NETLINK_FIREWALL, + Name: "NETLINK_FIREWALL", + }, + { + Value: linux.NETLINK_SOCK_DIAG, + Name: "NETLINK_SOCK_DIAG", + }, + { + Value: linux.NETLINK_NFLOG, + Name: "NETLINK_NFLOG", + }, + { + Value: linux.NETLINK_XFRM, + Name: "NETLINK_XFRM", + }, + { + Value: linux.NETLINK_SELINUX, + Name: "NETLINK_SELINUX", + }, + { + Value: linux.NETLINK_ISCSI, + Name: "NETLINK_ISCSI", + }, + { + Value: linux.NETLINK_AUDIT, + Name: "NETLINK_AUDIT", + }, + { + Value: linux.NETLINK_FIB_LOOKUP, + Name: "NETLINK_FIB_LOOKUP", + }, + { + Value: linux.NETLINK_CONNECTOR, + Name: "NETLINK_CONNECTOR", + }, + { + Value: linux.NETLINK_NETFILTER, + Name: "NETLINK_NETFILTER", + }, + { + Value: linux.NETLINK_IP6_FW, + Name: "NETLINK_IP6_FW", + }, + { + Value: linux.NETLINK_DNRTMSG, + Name: "NETLINK_DNRTMSG", + }, + { + Value: linux.NETLINK_KOBJECT_UEVENT, + Name: "NETLINK_KOBJECT_UEVENT", + }, + { + Value: linux.NETLINK_GENERIC, + Name: "NETLINK_GENERIC", + }, + { + Value: linux.NETLINK_SCSITRANSPORT, + Name: "NETLINK_SCSITRANSPORT", + }, + { + Value: linux.NETLINK_ECRYPTFS, + Name: "NETLINK_ECRYPTFS", + }, + { + Value: linux.NETLINK_RDMA, + Name: "NETLINK_RDMA", + }, + { + Value: linux.NETLINK_CRYPTO, + Name: "NETLINK_CRYPTO", + }, + }, +} + +var controlMessageType = map[int32]string{ + linux.SCM_RIGHTS: "SCM_RIGHTS", + linux.SCM_CREDENTIALS: "SCM_CREDENTIALS", +} + +func cmsghdr(t *kernel.Task, addr usermem.Addr, length uint64, maxBytes uint64) string { + if length > maxBytes { + return fmt.Sprintf("%#x (error decoding control: invalid length (%d))", addr, length) + } + + buf := make([]byte, length) + if _, err := t.CopyIn(addr, &buf); err != nil { + return fmt.Sprintf("%#x (error decoding control: %v)", addr, err) + } + + var strs []string + + for i := 0; i < len(buf); { + if i+linux.SizeOfControlMessageHeader > len(buf) { + strs = append(strs, "{invalid control message (too short)}") + break + } + + var h linux.ControlMessageHeader + binary.Unmarshal(buf[i:i+linux.SizeOfControlMessageHeader], usermem.ByteOrder, &h) + i += linux.SizeOfControlMessageHeader + + var skipData bool + level := "SOL_SOCKET" + if h.Level != linux.SOL_SOCKET { + skipData = true + level = fmt.Sprint(h.Level) + } + + typ, ok := controlMessageType[h.Type] + if !ok { + skipData = true + typ = fmt.Sprint(h.Type) + } + + if h.Length > uint64(len(buf)-i) { + strs = append(strs, fmt.Sprintf( + "{level=%s, type=%s, length=%d, content extends beyond buffer}", + level, + typ, + h.Length, + )) + break + } + + width := t.Arch().Width() + length := int(h.Length) - linux.SizeOfControlMessageHeader + + if skipData { + strs = append(strs, fmt.Sprintf("{level=%s, type=%s, length=%d}", level, typ, h.Length)) + i += control.AlignUp(i+length, width) + continue + } + + switch h.Type { + case linux.SCM_RIGHTS: + rightsSize := control.AlignDown(length, linux.SizeOfControlMessageRight) + + numRights := rightsSize / linux.SizeOfControlMessageRight + fds := make(linux.ControlMessageRights, numRights) + binary.Unmarshal(buf[i:i+rightsSize], usermem.ByteOrder, &fds) + + rights := make([]string, 0, len(fds)) + for _, fd := range fds { + rights = append(rights, fmt.Sprint(fd)) + } + + strs = append(strs, fmt.Sprintf( + "{level=%s, type=%s, length=%d, content: %s}", + level, + typ, + h.Length, + strings.Join(rights, ","), + )) + + i += control.AlignUp(length, width) + + case linux.SCM_CREDENTIALS: + if length < linux.SizeOfControlMessageCredentials { + strs = append(strs, fmt.Sprintf( + "{level=%s, type=%s, length=%d, content too short}", + level, + typ, + h.Length, + )) + i += control.AlignUp(length, width) + break + } + + var creds linux.ControlMessageCredentials + binary.Unmarshal(buf[i:i+linux.SizeOfControlMessageCredentials], usermem.ByteOrder, &creds) + + strs = append(strs, fmt.Sprintf( + "{level=%s, type=%s, length=%d, pid: %d, uid: %d, gid: %d}", + level, + typ, + h.Length, + creds.PID, + creds.UID, + creds.GID, + )) + + i += control.AlignUp(length, width) + + default: + panic("unreachable") + } + } + + return fmt.Sprintf("%#x %s", addr, strings.Join(strs, ", ")) +} + +func msghdr(t *kernel.Task, addr usermem.Addr, printContent bool, maxBytes uint64) string { + var msg slinux.MessageHeader64 + if err := slinux.CopyInMessageHeader64(t, addr, &msg); err != nil { + return fmt.Sprintf("%#x (error decoding msghdr: %v)", addr, err) + } + s := fmt.Sprintf( + "%#x {name=%#x, namelen=%d, iovecs=%s", + addr, + msg.Name, + msg.NameLen, + iovecs(t, usermem.Addr(msg.Iov), int(msg.IovLen), printContent, maxBytes), + ) + if printContent { + s = fmt.Sprintf("%s, control={%s}", s, cmsghdr(t, usermem.Addr(msg.Control), msg.ControlLen, maxBytes)) + } else { + s = fmt.Sprintf("%s, control=%#x, control_len=%d", s, msg.Control, msg.ControlLen) + } + return fmt.Sprintf("%s, flags=%d}", s, msg.Flags) +} + +func sockAddr(t *kernel.Task, addr usermem.Addr, length uint32) string { + if addr == 0 { + return "null" + } + + b, err := slinux.CaptureAddress(t, addr, length) + if err != nil { + return fmt.Sprintf("%#x {error reading address: %v}", addr, err) + } + + // Extract address family. + if len(b) < 2 { + return fmt.Sprintf("%#x {address too short: %d bytes}", addr, len(b)) + } + family := usermem.ByteOrder.Uint16(b) + + familyStr := SocketFamily.Parse(uint64(family)) + + switch family { + case linux.AF_INET, linux.AF_INET6, linux.AF_UNIX: + fa, err := epsocket.GetAddress(int(family), b) + if err != nil { + return fmt.Sprintf("%#x {Family: %s, error extracting address: %v}", addr, familyStr, err) + } + + if family == linux.AF_UNIX { + return fmt.Sprintf("%#x {Family: %s, Addr: %q}", addr, familyStr, string(fa.Addr)) + } + + return fmt.Sprintf("%#x {Family: %s, Addr: %v, Port: %d}", addr, familyStr, fa.Addr, fa.Port) + case linux.AF_NETLINK: + sa, err := netlink.ExtractSockAddr(b) + if err != nil { + return fmt.Sprintf("%#x {Family: %s, error extracting address: %v}", addr, familyStr, err) + } + return fmt.Sprintf("%#x {Family: %s, PortID: %d, Groups: %d}", addr, familyStr, sa.PortID, sa.Groups) + default: + return fmt.Sprintf("%#x {Family: %s, family addr format unknown}", addr, familyStr) + } +} + +func postSockAddr(t *kernel.Task, addr usermem.Addr, lengthPtr usermem.Addr) string { + if addr == 0 { + return "null" + } + + if lengthPtr == 0 { + return fmt.Sprintf("%#x {length null}", addr) + } + + l, err := copySockLen(t, lengthPtr) + if err != nil { + return fmt.Sprintf("%#x {error reading length: %v}", addr, err) + } + + return sockAddr(t, addr, l) +} + +func copySockLen(t *kernel.Task, addr usermem.Addr) (uint32, error) { + // socklen_t is 32-bits. + var l uint32 + _, err := t.CopyIn(addr, &l) + return l, err +} + +func sockLenPointer(t *kernel.Task, addr usermem.Addr) string { + if addr == 0 { + return "null" + } + l, err := copySockLen(t, addr) + if err != nil { + return fmt.Sprintf("%#x {error reading length: %v}", addr, err) + } + return fmt.Sprintf("%#x {length=%v}", addr, l) +} + +func sockType(stype int32) string { + s := SocketType.Parse(uint64(stype & linux.SOCK_TYPE_MASK)) + if flags := SocketFlagSet.Parse(uint64(stype &^ linux.SOCK_TYPE_MASK)); flags != "" { + s += "|" + flags + } + return s +} + +func sockProtocol(family, protocol int32) string { + protocols, ok := SocketProtocol[family] + if !ok { + return fmt.Sprintf("%#x", protocol) + } + return protocols.Parse(uint64(protocol)) +} + +func sockFlags(flags int32) string { + if flags == 0 { + return "0" + } + return SocketFlagSet.Parse(uint64(flags)) +} |