File: sameuser_linux.go

package info (click to toggle)
delve 1.24.0-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 14,092 kB
  • sloc: ansic: 111,943; sh: 169; asm: 141; makefile: 43; python: 23
file content (131 lines) | stat: -rw-r--r-- 4,434 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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
}