perms: /p/ is owner+user-group rw, agent-group read-only, other denied

uid=ollie gid=user; dir=0750 files=0640. Agents can read prompts
but cannot modify them. Users (non-agent connections) have full access.
This commit is contained in:
Levi Neely 2026-05-19 15:59:19 +02:00
parent c6da56b1d8
commit 629a841cf5
1 changed files with 10 additions and 6 deletions

View File

@ -332,6 +332,7 @@ func (s *Server) InGroup(group, user string) bool {
// fileOwnerGroup returns the uid and gid for a given path. // fileOwnerGroup returns the uid and gid for a given path.
// Session files are owned by the session's principal with group "agent". // Session files are owned by the session's principal with group "agent".
// Prompt library (/p/) is owned by "ollie" with group "user".
// Everything else is owned by "ollie" with group "ollie". // Everything else is owned by "ollie" with group "ollie".
func (s *Server) fileOwnerGroup(path string) (uid, gid string) { func (s *Server) fileOwnerGroup(path string) (uid, gid string) {
if strings.HasPrefix(path, "/s/") { if strings.HasPrefix(path, "/s/") {
@ -342,16 +343,19 @@ func (s *Server) fileOwnerGroup(path string) (uid, gid string) {
} }
} }
} }
if path == "/p" || strings.HasPrefix(path, "/p/") {
return "ollie", "user"
}
return "ollie", "ollie" return "ollie", "ollie"
} }
// checkPerm verifies that uname has the requested access (mode) to path. // checkPerm verifies that uname has the requested access (mode) to path.
// mode is the 9P open mode: OREAD=0, OWRITE=1, ORDWR=2, OEXEC=3. // mode is the 9P open mode: OREAD=0, OWRITE=1, ORDWR=2, OEXEC=3.
// Only enforced on session files (/s/{session_id}/...). // Enforced on session files and group-restricted paths (e.g. /p/).
func (s *Server) checkPerm(uname, path string, mode uint8) error { func (s *Server) checkPerm(uname, path string, mode uint8) error {
uid, gid := s.fileOwnerGroup(path) uid, gid := s.fileOwnerGroup(path)
if uid == "ollie" { if uid == "ollie" && gid == "ollie" {
return nil // non-session files: no enforcement return nil // no enforcement
} }
dir := s.makeStat(path) dir := s.makeStat(path)
perm := uint32(dir.Mode) & 0777 perm := uint32(dir.Mode) & 0777
@ -1457,7 +1461,7 @@ func (s *Server) readDir(path string, offset uint64, count uint32) []byte {
dirs = append(dirs, makeDir("backends", "/backends", false, 0444)) dirs = append(dirs, makeDir("backends", "/backends", false, 0444))
dirs = append(dirs, makeDir("help", "/help", false, 0444)) dirs = append(dirs, makeDir("help", "/help", false, 0444))
dirs = append(dirs, makeDir("m", "/m", true, plan9.DMDIR|0755)) dirs = append(dirs, makeDir("m", "/m", true, plan9.DMDIR|0755))
dirs = append(dirs, makeDir("p", "/p", true, plan9.DMDIR|0555)) dirs = append(dirs, makeDir("p", "/p", true, plan9.DMDIR|0750))
dirs = append(dirs, makeDir("s", "/s", true, plan9.DMDIR|0555)) dirs = append(dirs, makeDir("s", "/s", true, plan9.DMDIR|0555))
dirs = append(dirs, makeDir("sk", "/sk", true, plan9.DMDIR|0555)) dirs = append(dirs, makeDir("sk", "/sk", true, plan9.DMDIR|0555))
dirs = append(dirs, makeDir("tmp", "/tmp", true, plan9.DMDIR|0755)) dirs = append(dirs, makeDir("tmp", "/tmp", true, plan9.DMDIR|0755))
@ -1475,7 +1479,7 @@ func (s *Server) readDir(path string, offset uint64, count uint32) []byte {
entries, _ := s.promptStore.List() entries, _ := s.promptStore.List()
for _, e := range entries { for _, e := range entries {
if !e.IsDir() { if !e.IsDir() {
dirs = append(dirs, makeDir(e.Name(), "/p/"+e.Name(), false, 0666)) dirs = append(dirs, makeDir(e.Name(), "/p/"+e.Name(), false, 0640))
} }
} }
} else if path == "/m" { } else if path == "/m" {
@ -1667,7 +1671,7 @@ func (s *Server) makeStat(path string) plan9.Dir {
} else if strings.HasPrefix(path, "/a/") { } else if strings.HasPrefix(path, "/a/") {
mode = 0666 mode = 0666
} else if strings.HasPrefix(path, "/p/") { } else if strings.HasPrefix(path, "/p/") {
mode = 0444 mode = 0640
} else if strings.HasPrefix(path, "/m/") { } else if strings.HasPrefix(path, "/m/") {
mode = 0666 mode = 0666
} else if path == "/sk/idx" { } else if path == "/sk/idx" {