//go:build linux package meshcli import ( "errors" "fmt" "net" "os" "syscall" ) // peerCredentials is the user id of the process at the other end, as the kernel recorded it when it connected // (SO_PEERCRED). Nothing the process says is read. func peerCredentials(conn *net.UnixConn) (uint32, int32, error) { raw, err := conn.SyscallConn() if err != nil { return 0, 0, err } var cred *syscall.Ucred var inner error if err := raw.Control(func(fd uintptr) { cred, inner = syscall.GetsockoptUcred(int(fd), syscall.SOL_SOCKET, syscall.SO_PEERCRED) }); err != nil { return 0, 0, err } if inner != nil { return 0, 0, inner } if cred == nil { return 0, 0, errors.New("the kernel gave no credentials") } return cred.Uid, cred.Pid, nil } // namespacesOf refuses a process whose mount or user namespace is not this one's, read from the kernel's own // names for them. One that cannot be read is refused, never assumed the same. func namespacesOf(pid int32) error { for _, ns := range []string{"mnt", "user"} { mine, err := os.Readlink("/proc/self/ns/" + ns) if err != nil { return fmt.Errorf("the engine's own %s namespace cannot be read: %w", ns, err) } theirs, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/%s", pid, ns)) if err != nil { return fmt.Errorf("process %d's %s namespace cannot be read: %w", pid, ns, err) } if theirs != mine { return fmt.Errorf("process %d is in %s namespace %s, the engine in %s", pid, ns, theirs, mine) } } return nil } // notOwnDirectory says what is wrong with the socket's directory, or nothing: it must be a directory, not a // link, owned by the account the engine runs as. func notOwnDirectory(dir string) string { info, err := os.Lstat(dir) if err != nil { return err.Error() } if info.Mode()&os.ModeSymlink != 0 || !info.IsDir() { return dir + " is not a directory the engine made" } if st, ok := info.Sys().(*syscall.Stat_t); ok && int(st.Uid) != os.Geteuid() { return fmt.Sprintf("%s belongs to user id %d, not to the engine's account", dir, st.Uid) } return "" }