whatcanGOwrong
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
// Package sameuser provides utilities for checking users of a local connection.
|
||||
// Only works in Linux.
|
||||
package sameuser
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build !linux
|
||||
|
||||
package sameuser
|
||||
|
||||
import "net"
|
||||
|
||||
func CanAccept(_, _, _ net.Addr) bool {
|
||||
return true
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
package sameuser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/go-delve/delve/pkg/logflags"
|
||||
)
|
||||
|
||||
// for testing
|
||||
var (
|
||||
uid = os.Getuid()
|
||||
readFile = os.ReadFile
|
||||
)
|
||||
|
||||
type errConnectionNotFound struct {
|
||||
filename string
|
||||
}
|
||||
|
||||
func (e *errConnectionNotFound) Error() string {
|
||||
return fmt.Sprintf("connection not found in %s", e.filename)
|
||||
}
|
||||
|
||||
func sameUserForHexLocalAddr(filename, localAddr, remoteAddr string) (bool, error) {
|
||||
b, err := readFile(filename)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, line := range strings.Split(strings.TrimSpace(string(b)), "\n") {
|
||||
// The format contains whitespace padding (%4d, %5u), so we use
|
||||
// fmt.Sscanf instead of splitting on whitespace.
|
||||
var (
|
||||
sl int
|
||||
readLocalAddr, readRemoteAddr string
|
||||
state int
|
||||
queue, timer string
|
||||
retransmit int
|
||||
remoteUID uint
|
||||
)
|
||||
// Note that we must use %d where the kernel format uses %4d or %5u:
|
||||
// - %4d fails to parse for large number of entries (len(sl) > 4)
|
||||
// - %u is not understood by the fmt package (%U is something else)
|
||||
// - %5d cuts off longer uids (e.g. 149098 on gLinux)
|
||||
n, err := fmt.Sscanf(line, "%d: %s %s %02X %s %s %08X %d",
|
||||
&sl, &readLocalAddr, &readRemoteAddr, &state, &queue, &timer, &retransmit, &remoteUID)
|
||||
if n != 8 || err != nil {
|
||||
continue // invalid line (e.g. header line)
|
||||
}
|
||||
if readLocalAddr != remoteAddr || readRemoteAddr != localAddr {
|
||||
// this check is deliberately crossed, the (readLocalAddr,
|
||||
// readRemoteAddr) pair is from the point of view of the client, the
|
||||
// (localAddr, remoteAddr) is from the point of view of the server.
|
||||
continue
|
||||
}
|
||||
same := uid == int(remoteUID)
|
||||
if !same && logflags.Any() {
|
||||
log.Printf("connection from different user (remote: %d, local: %d) detected: %v", remoteUID, uid, line)
|
||||
}
|
||||
return same, nil
|
||||
}
|
||||
return false, &errConnectionNotFound{filename}
|
||||
}
|
||||
|
||||
func addrToHex4(addr *net.TCPAddr) string {
|
||||
// For details about the format, see the kernel side implementation:
|
||||
// https://elixir.bootlin.com/linux/v5.2.2/source/net/ipv4/tcp_ipv4.c#L2375
|
||||
b := addr.IP.To4()
|
||||
return fmt.Sprintf("%02X%02X%02X%02X:%04X", b[3], b[2], b[1], b[0], addr.Port)
|
||||
}
|
||||
|
||||
func addrToHex6(addr *net.TCPAddr) string {
|
||||
a16 := addr.IP.To16()
|
||||
// For details about the format, see the kernel side implementation:
|
||||
// https://elixir.bootlin.com/linux/v5.2.2/source/net/ipv6/tcp_ipv6.c#L1792
|
||||
words := make([]uint32, 4)
|
||||
if err := binary.Read(bytes.NewReader(a16), binary.LittleEndian, words); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return fmt.Sprintf("%08X%08X%08X%08X:%04X", words[0], words[1], words[2], words[3], addr.Port)
|
||||
}
|
||||
|
||||
func sameUserForRemoteAddr4(localAddr, remoteAddr *net.TCPAddr) (bool, error) {
|
||||
r, err := sameUserForHexLocalAddr("/proc/net/tcp", addrToHex4(localAddr), addrToHex4(remoteAddr))
|
||||
if _, isNotFound := err.(*errConnectionNotFound); isNotFound {
|
||||
// See Issue #1835
|
||||
r, err2 := sameUserForHexLocalAddr("/proc/net/tcp6", "0000000000000000FFFF0000"+addrToHex4(localAddr), "0000000000000000FFFF0000"+addrToHex4(remoteAddr))
|
||||
if err2 == nil {
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
return r, err
|
||||
}
|
||||
|
||||
func sameUserForRemoteAddr6(localAddr, remoteAddr *net.TCPAddr) (bool, error) {
|
||||
return sameUserForHexLocalAddr("/proc/net/tcp6", addrToHex6(localAddr), addrToHex6(remoteAddr))
|
||||
}
|
||||
|
||||
func sameUserForRemoteAddr(localAddr, remoteAddr *net.TCPAddr) (bool, error) {
|
||||
if remoteAddr.IP.To4() == nil {
|
||||
return sameUserForRemoteAddr6(localAddr, remoteAddr)
|
||||
}
|
||||
return sameUserForRemoteAddr4(localAddr, remoteAddr)
|
||||
}
|
||||
|
||||
func CanAccept(listenAddr, localAddr, remoteAddr net.Addr) bool {
|
||||
laddr, ok := listenAddr.(*net.TCPAddr)
|
||||
if !ok || !laddr.IP.IsLoopback() {
|
||||
return true
|
||||
}
|
||||
remoteAddrTCP := remoteAddr.(*net.TCPAddr)
|
||||
localAddrTCP := localAddr.(*net.TCPAddr)
|
||||
|
||||
same, err := sameUserForRemoteAddr(localAddrTCP, remoteAddrTCP)
|
||||
if err != nil {
|
||||
log.Printf("cannot check remote address: %v", err)
|
||||
}
|
||||
if !same {
|
||||
if logflags.Any() {
|
||||
log.Printf("closing connection from different user (%v): connections to localhost are only accepted from the same UNIX user for security reasons", remoteAddrTCP)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "closing connection from different user (%v): connections to localhost are only accepted from the same UNIX user for security reasons\n", remoteAddrTCP)
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package sameuser
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSameUserForRemoteAddr(t *testing.T) {
|
||||
uid = 149098
|
||||
var proc string
|
||||
readFile = func(string) ([]byte, error) {
|
||||
return []byte(proc), nil
|
||||
}
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
proc string
|
||||
localAddr, remoteAddr *net.TCPAddr
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "ipv4-same",
|
||||
proc: ` sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
|
||||
21: 0100007F:E682 0100007F:0FC8 01 00000000:00000000 00:00000000 00000000 149098 0 8420541 2 0000000000000000 20 0 0 10 -1 `,
|
||||
localAddr: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 4040},
|
||||
remoteAddr: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 59010},
|
||||
want: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: "ipv4-not-found",
|
||||
proc: ` sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
|
||||
21: 0100007F:E682 0100007F:0FC8 01 00000000:00000000 00:00000000 00000000 149098 0 8420541 2 0000000000000000 20 0 0 10 -1 `,
|
||||
localAddr: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 4040},
|
||||
remoteAddr: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 2342},
|
||||
want: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "ipv4-different-uid",
|
||||
proc: ` sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
|
||||
21: 0100007F:E682 0100007F:0FC8 01 00000000:00000000 00:00000000 00000000 149097 0 8420541 2 0000000000000000 20 0 0 10 -1 `,
|
||||
localAddr: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 4040},
|
||||
remoteAddr: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 59010},
|
||||
want: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "ipv6-same",
|
||||
proc: ` sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
|
||||
5: 00000000000000000000000001000000:D3E4 00000000000000000000000001000000:0FC8 01 00000000:00000000 00:00000000 00000000 149098 0 8425526 2 0000000000000000 20 0 0 10 -1
|
||||
6: 00000000000000000000000001000000:0FC8 00000000000000000000000001000000:D3E4 01 00000000:00000000 00:00000000 00000000 149098 0 8424744 1 0000000000000000 20 0 0 10 -1`,
|
||||
localAddr: &net.TCPAddr{IP: net.ParseIP("::1"), Port: 4040},
|
||||
remoteAddr: &net.TCPAddr{IP: net.ParseIP("::1"), Port: 54244},
|
||||
want: true,
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
proc = tt.proc
|
||||
// The returned error is for reporting only.
|
||||
same, _ := sameUserForRemoteAddr(tt.localAddr, tt.remoteAddr)
|
||||
if got, want := same, tt.want; got != want {
|
||||
t.Errorf("sameUserForRemoteAddr(%v, %v) = %v, want %v", tt.localAddr, tt.remoteAddr, got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user