2021-06-15 13:36:39 +00:00
//go:build linux
2019-11-25 17:05:45 +00:00
2021-05-19 17:29:05 +00:00
package sameuser
2019-11-25 17:05:45 +00:00
import (
"bytes"
"encoding/binary"
"fmt"
"log"
"net"
"os"
"strings"
2020-10-22 15:23:55 +00:00
"github.com/go-delve/delve/pkg/logflags"
2019-11-25 17:05:45 +00:00
)
// for testing
var (
uid = os . Getuid ( )
2023-09-25 18:41:59 +00:00
readFile = os . ReadFile
2019-11-25 17:05:45 +00:00
)
2020-02-11 17:06:29 +00:00
type errConnectionNotFound struct {
filename string
}
func ( e * errConnectionNotFound ) Error ( ) string {
return fmt . Sprintf ( "connection not found in %s" , e . filename )
}
2021-08-10 09:40:39 +00:00
func sameUserForHexLocalAddr ( filename , localAddr , remoteAddr string ) ( bool , error ) {
2019-11-25 17:05:45 +00:00
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 (
2021-08-10 09:40:39 +00:00
sl int
readLocalAddr , readRemoteAddr string
state int
queue , timer string
retransmit int
remoteUID uint
2019-11-25 17:05:45 +00:00
)
// Note that we must use %d where the kernel format uses %5u:
// %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 , "%4d: %s %s %02X %s %s %08X %d" ,
2021-08-10 09:40:39 +00:00
& sl , & readLocalAddr , & readRemoteAddr , & state , & queue , & timer , & retransmit , & remoteUID )
2019-11-25 17:05:45 +00:00
if n != 8 || err != nil {
continue // invalid line (e.g. header line)
}
2021-08-10 09:40:39 +00:00
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.
2019-11-25 17:05:45 +00:00
continue
}
2021-06-15 13:36:39 +00:00
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
2019-11-25 17:05:45 +00:00
}
2020-02-11 17:06:29 +00:00
return false , & errConnectionNotFound { filename }
2019-11-25 17:05:45 +00:00
}
2021-08-10 09:40:39 +00:00
func addrToHex4 ( addr * net . TCPAddr ) string {
2019-11-25 17:05:45 +00:00
// 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
2021-08-10 09:40:39 +00:00
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 ) )
2020-02-11 17:06:29 +00:00
if _ , isNotFound := err . ( * errConnectionNotFound ) ; isNotFound {
// See Issue #1835
2021-08-10 09:40:39 +00:00
r , err2 := sameUserForHexLocalAddr ( "/proc/net/tcp6" , "0000000000000000FFFF0000" + addrToHex4 ( localAddr ) , "0000000000000000FFFF0000" + addrToHex4 ( remoteAddr ) )
2020-02-11 17:06:29 +00:00
if err2 == nil {
return r , nil
}
}
return r , err
2019-11-25 17:05:45 +00:00
}
2021-08-10 09:40:39 +00:00
func sameUserForRemoteAddr6 ( localAddr , remoteAddr * net . TCPAddr ) ( bool , error ) {
return sameUserForHexLocalAddr ( "/proc/net/tcp6" , addrToHex6 ( localAddr ) , addrToHex6 ( remoteAddr ) )
2019-11-25 17:05:45 +00:00
}
2021-08-10 09:40:39 +00:00
func sameUserForRemoteAddr ( localAddr , remoteAddr * net . TCPAddr ) ( bool , error ) {
2019-11-25 17:05:45 +00:00
if remoteAddr . IP . To4 ( ) == nil {
2021-08-10 09:40:39 +00:00
return sameUserForRemoteAddr6 ( localAddr , remoteAddr )
2019-11-25 17:05:45 +00:00
}
2021-08-10 09:40:39 +00:00
return sameUserForRemoteAddr4 ( localAddr , remoteAddr )
2019-11-25 17:05:45 +00:00
}
2021-08-10 09:40:39 +00:00
func CanAccept ( listenAddr , localAddr , remoteAddr net . Addr ) bool {
2019-11-25 17:05:45 +00:00
laddr , ok := listenAddr . ( * net . TCPAddr )
if ! ok || ! laddr . IP . IsLoopback ( ) {
return true
}
2021-08-10 09:40:39 +00:00
remoteaAddrTCP := remoteAddr . ( * net . TCPAddr )
localAddrTCP := localAddr . ( * net . TCPAddr )
same , err := sameUserForRemoteAddr ( localAddrTCP , remoteaAddrTCP )
2019-11-25 17:05:45 +00:00
if err != nil {
log . Printf ( "cannot check remote address: %v" , err )
}
if ! same {
2020-10-22 15:23:55 +00:00
if logflags . Any ( ) {
2021-08-10 09:40:39 +00:00
log . Printf ( "closing connection from different user (%v): connections to localhost are only accepted from the same UNIX user for security reasons" , remoteaAddrTCP )
2020-10-22 15:23:55 +00:00
} else {
2021-08-10 09:40:39 +00:00
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 )
2020-10-22 15:23:55 +00:00
}
2019-11-25 17:05:45 +00:00
return false
}
return true
}