| 1 | //go:build linux |
| 2 | |
| 3 | package instancectl |
| 4 | |
| 5 | import ( |
| 6 | "fmt" |
| 7 | "net" |
| 8 | "os" |
| 9 | "strconv" |
| 10 | "strings" |
| 11 | ) |
| 12 | |
| 13 | type AuthenticatedPeer struct { |
| 14 | UID uint32 |
| 15 | PID int |
| 16 | ProcessStart, BootID, ControllerID string |
| 17 | } |
| 18 | |
| 19 | func procStart(pid int) (string, error) { |
| 20 | b, err := os.ReadFile(fmt.Sprintf("/proc/%d/stat", pid)) |
| 21 | if err != nil { |
| 22 | return "", err |
| 23 | } |
| 24 | i := strings.LastIndexByte(string(b), ')') |
| 25 | if i < 0 { |
| 26 | return "", fmt.Errorf("malformed proc stat") |
| 27 | } |
| 28 | f := strings.Fields(string(b[i+1:])) |
| 29 | if len(f) <= 19 { |
| 30 | return "", fmt.Errorf("malformed proc stat") |
| 31 | } |
| 32 | if _, err := strconv.ParseUint(f[19], 10, 64); err != nil { |
| 33 | return "", err |
| 34 | } |
| 35 | return f[19], nil |
| 36 | } |
| 37 | |
| 38 | // AuthenticatePeerTuple binds the accepted Unix peer to kernel identity and |
| 39 | // rereads starttime to reject PID reuse during collection. |
| 40 | func AuthenticatePeerTuple(conn net.Conn, expectedUID uint32, expectedPID int, controllerID string) (AuthenticatedPeer, error) { |
| 41 | if conn == nil || expectedPID <= 0 || controllerID == "" { |
| 42 | return AuthenticatedPeer{}, fmt.Errorf("invalid peer expectation") |
| 43 | } |
| 44 | u, err := peerCred(conn) |
| 45 | if err != nil { |
| 46 | return AuthenticatedPeer{}, err |
| 47 | } |
| 48 | if u.Uid != expectedUID || int(u.Pid) != expectedPID { |
| 49 | return AuthenticatedPeer{}, fmt.Errorf("peer identity mismatch") |
| 50 | } |
| 51 | start, err := procStart(expectedPID) |
| 52 | if err != nil { |
| 53 | return AuthenticatedPeer{}, err |
| 54 | } |
| 55 | boot, err := os.ReadFile("/proc/sys/kernel/random/boot_id") |
| 56 | if err != nil { |
| 57 | return AuthenticatedPeer{}, err |
| 58 | } |
| 59 | start2, err := procStart(expectedPID) |
| 60 | if err != nil || start2 != start { |
| 61 | return AuthenticatedPeer{}, fmt.Errorf("peer process changed during authentication") |
| 62 | } |
| 63 | return AuthenticatedPeer{UID: u.Uid, PID: int(u.Pid), ProcessStart: start, BootID: strings.TrimSpace(string(boot)), ControllerID: controllerID}, nil |
| 64 | } |
| 65 | |