76 lines
2.2 KiB
Go
76 lines
2.2 KiB
Go
//go:build linux
|
|
|
|
package main
|
|
|
|
import (
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// llmListenerTrusted: the program listening on the loopback port addr names
|
|
// runs as root, as this bastion's own user, or as the machine's recorded
|
|
// owner. Found without anyone setting PRIVATAE_LLM_ADDR, a model server is
|
|
// handed the Host's prompts, and any account on the machine can listen on
|
|
// 127.0.0.1:8080 while llama.cpp is not running; a server of another account
|
|
// is not taken (the variable still names one on purpose). The kernel's own
|
|
// table says who owns each listening socket (/proc/net/tcp, tcp6).
|
|
func llmListenerTrusted(addr string) bool {
|
|
port := portOfAddr(addr)
|
|
if port <= 0 {
|
|
return false
|
|
}
|
|
var uids []uint32
|
|
for _, f := range []string{"/proc/net/tcp", "/proc/net/tcp6"} {
|
|
if raw, err := os.ReadFile(f); err == nil {
|
|
uids = append(uids, loopbackListenerUIDs(string(raw), port)...)
|
|
}
|
|
}
|
|
owner, _, ownerKnown, _ := ownerAccountLoad()
|
|
return listenersTrusted(uids, uint32(os.Getuid()), owner, ownerKnown)
|
|
}
|
|
|
|
// listenersTrusted: there is a listener, and every one is root's, self's or
|
|
// the owner's.
|
|
func listenersTrusted(uids []uint32, self, owner uint32, ownerKnown bool) bool {
|
|
if len(uids) == 0 {
|
|
return false
|
|
}
|
|
for _, u := range uids {
|
|
if u != 0 && u != self && (!ownerKnown || u != owner) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// loopbackListenerUIDs reads a /proc/net/tcp table: the uid of every socket
|
|
// LISTENing on port at a loopback or any-address local address.
|
|
func loopbackListenerUIDs(table string, port int) []uint32 {
|
|
var out []uint32
|
|
for _, line := range strings.Split(table, "\n")[1:] {
|
|
f := strings.Fields(line)
|
|
if len(f) < 8 || f[3] != "0A" {
|
|
continue
|
|
}
|
|
host, p, ok := strings.Cut(f[1], ":")
|
|
if !ok {
|
|
continue
|
|
}
|
|
if n, err := strconv.ParseUint(p, 16, 16); err != nil || int(n) != port {
|
|
continue
|
|
}
|
|
switch host {
|
|
case "0100007F", "00000000", "00000000000000000000000001000000", "00000000000000000000000000000000":
|
|
default:
|
|
if !strings.HasSuffix(host, "7F") || len(host) != 8 { // 127.x.y.z, little-endian
|
|
continue
|
|
}
|
|
}
|
|
if u, err := strconv.ParseUint(f[7], 10, 32); err == nil {
|
|
out = append(out, uint32(u))
|
|
}
|
|
}
|
|
return out
|
|
}
|