gitlab.com/Raven-IO/raven-delve@v1.22.4/service/internal/sameuser/sameuser_linux.go (about)

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