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  }