github.com/undoio/delve@v1.9.0/service/internal/sameuser/sameuser_linux.go (about) 1 //go:build linux 2 // +build linux 3 4 package sameuser 5 6 import ( 7 "bytes" 8 "encoding/binary" 9 "fmt" 10 "io/ioutil" 11 "log" 12 "net" 13 "os" 14 "strings" 15 16 "github.com/undoio/delve/pkg/logflags" 17 ) 18 19 // for testing 20 var ( 21 uid = os.Getuid() 22 readFile = ioutil.ReadFile 23 ) 24 25 type errConnectionNotFound struct { 26 filename string 27 } 28 29 func (e *errConnectionNotFound) Error() string { 30 return fmt.Sprintf("connection not found in %s", e.filename) 31 } 32 33 func sameUserForHexLocalAddr(filename, localAddr, remoteAddr string) (bool, error) { 34 b, err := readFile(filename) 35 if err != nil { 36 return false, err 37 } 38 for _, line := range strings.Split(strings.TrimSpace(string(b)), "\n") { 39 // The format contains whitespace padding (%4d, %5u), so we use 40 // fmt.Sscanf instead of splitting on whitespace. 41 var ( 42 sl int 43 readLocalAddr, readRemoteAddr string 44 state int 45 queue, timer string 46 retransmit int 47 remoteUID uint 48 ) 49 // Note that we must use %d where the kernel format uses %5u: 50 // %u is not understood by the fmt package (%U is something else), 51 // %5d cuts off longer uids (e.g. 149098 on gLinux). 52 n, err := fmt.Sscanf(line, "%4d: %s %s %02X %s %s %08X %d", 53 &sl, &readLocalAddr, &readRemoteAddr, &state, &queue, &timer, &retransmit, &remoteUID) 54 if n != 8 || err != nil { 55 continue // invalid line (e.g. header line) 56 } 57 if readLocalAddr != remoteAddr || readRemoteAddr != localAddr { 58 // this check is deliberately crossed, the (readLocalAddr, 59 // readRemoteAddr) pair is from the point of view of the client, the 60 // (localAddr, remoteAddr) is from the point of view of the server. 61 continue 62 } 63 same := uid == int(remoteUID) 64 if !same && logflags.Any() { 65 log.Printf("connection from different user (remote: %d, local: %d) detected: %v", remoteUID, uid, line) 66 } 67 return same, nil 68 } 69 return false, &errConnectionNotFound{filename} 70 } 71 72 func addrToHex4(addr *net.TCPAddr) string { 73 // For details about the format, see the kernel side implementation: 74 // https://elixir.bootlin.com/linux/v5.2.2/source/net/ipv4/tcp_ipv4.c#L2375 75 b := addr.IP.To4() 76 return fmt.Sprintf("%02X%02X%02X%02X:%04X", b[3], b[2], b[1], b[0], addr.Port) 77 } 78 79 func addrToHex6(addr *net.TCPAddr) string { 80 a16 := addr.IP.To16() 81 // For details about the format, see the kernel side implementation: 82 // https://elixir.bootlin.com/linux/v5.2.2/source/net/ipv6/tcp_ipv6.c#L1792 83 words := make([]uint32, 4) 84 if err := binary.Read(bytes.NewReader(a16), binary.LittleEndian, words); err != nil { 85 panic(err) 86 } 87 return fmt.Sprintf("%08X%08X%08X%08X:%04X", words[0], words[1], words[2], words[3], addr.Port) 88 } 89 90 func sameUserForRemoteAddr4(localAddr, remoteAddr *net.TCPAddr) (bool, error) { 91 r, err := sameUserForHexLocalAddr("/proc/net/tcp", addrToHex4(localAddr), addrToHex4(remoteAddr)) 92 if _, isNotFound := err.(*errConnectionNotFound); isNotFound { 93 // See Issue #1835 94 r, err2 := sameUserForHexLocalAddr("/proc/net/tcp6", "0000000000000000FFFF0000"+addrToHex4(localAddr), "0000000000000000FFFF0000"+addrToHex4(remoteAddr)) 95 if err2 == nil { 96 return r, nil 97 } 98 } 99 return r, err 100 } 101 102 func sameUserForRemoteAddr6(localAddr, remoteAddr *net.TCPAddr) (bool, error) { 103 return sameUserForHexLocalAddr("/proc/net/tcp6", addrToHex6(localAddr), addrToHex6(remoteAddr)) 104 } 105 106 func sameUserForRemoteAddr(localAddr, remoteAddr *net.TCPAddr) (bool, error) { 107 if remoteAddr.IP.To4() == nil { 108 return sameUserForRemoteAddr6(localAddr, remoteAddr) 109 } 110 return sameUserForRemoteAddr4(localAddr, remoteAddr) 111 } 112 113 func CanAccept(listenAddr, localAddr, remoteAddr net.Addr) bool { 114 laddr, ok := listenAddr.(*net.TCPAddr) 115 if !ok || !laddr.IP.IsLoopback() { 116 return true 117 } 118 remoteaAddrTCP := remoteAddr.(*net.TCPAddr) 119 localAddrTCP := localAddr.(*net.TCPAddr) 120 121 same, err := sameUserForRemoteAddr(localAddrTCP, remoteaAddrTCP) 122 if err != nil { 123 log.Printf("cannot check remote address: %v", err) 124 } 125 if !same { 126 if logflags.Any() { 127 log.Printf("closing connection from different user (%v): connections to localhost are only accepted from the same UNIX user for security reasons", remoteaAddrTCP) 128 } else { 129 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", remoteaAddrTCP) 130 } 131 return false 132 } 133 return true 134 }