From dc954878fe8e04d5159d444ac73d06bd4aa12735 Mon Sep 17 00:00:00 2001 From: Andrei Marinescu Date: Thu, 1 Oct 2026 16:18:29 +0300 Subject: [PATCH] svcauth, articles: read file owners through a portable helper The release matrix includes windows/amd64, where syscall.Stat_t does not exist, so the v0.4.0 release build failed. internal/fileowner reads the uid on unix and reports none elsewhere, which keeps both owner checks failing closed off unix. --- internal/articles/thresholds.go | 7 ++++--- internal/fileowner/owner_other.go | 9 +++++++++ internal/fileowner/owner_unix.go | 18 ++++++++++++++++++ internal/svcauth/config.go | 6 +++--- 4 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 internal/fileowner/owner_other.go create mode 100644 internal/fileowner/owner_unix.go diff --git a/internal/articles/thresholds.go b/internal/articles/thresholds.go index d49ccbb..dd0c8f3 100644 --- a/internal/articles/thresholds.go +++ b/internal/articles/thresholds.go @@ -8,7 +8,8 @@ import ( "io" "io/fs" "os" - "syscall" + + "github.com/pilot-protocol/cosift/internal/fileowner" ) const DefaultThresholdsPath = "/etc/cosift/articles.json" @@ -49,11 +50,11 @@ func LoadThresholds(path string) (Thresholds, error) { if err != nil { return Thresholds{}, err } - st, ok := fi.Sys().(*syscall.Stat_t) + owner, ok := fileowner.UID(fi) switch { case !fi.Mode().IsRegular(): return Thresholds{}, errors.New("not a regular file") - case !ok || st.Uid != thresholdsOwner: + case !ok || owner != thresholdsOwner: return Thresholds{}, fmt.Errorf("owner must be uid %d", thresholdsOwner) case fi.Mode().Perm()&0o027 != 0: return Thresholds{}, errors.New("mode must not grant group write or any other access") diff --git a/internal/fileowner/owner_other.go b/internal/fileowner/owner_other.go new file mode 100644 index 0000000..b570b3f --- /dev/null +++ b/internal/fileowner/owner_other.go @@ -0,0 +1,9 @@ +//go:build !unix + +// Package fileowner reads a file's owning uid where the platform has one. +package fileowner + +import "os" + +// UID reports no owner here, so every owner check refuses. +func UID(os.FileInfo) (uint32, bool) { return 0, false } diff --git a/internal/fileowner/owner_unix.go b/internal/fileowner/owner_unix.go new file mode 100644 index 0000000..8fbdc67 --- /dev/null +++ b/internal/fileowner/owner_unix.go @@ -0,0 +1,18 @@ +//go:build unix + +// Package fileowner reads a file's owning uid where the platform has one. +package fileowner + +import ( + "os" + "syscall" +) + +// UID returns the file's owner uid; ok is false when it cannot be read. +func UID(fi os.FileInfo) (uid uint32, ok bool) { + st, ok := fi.Sys().(*syscall.Stat_t) + if !ok { + return 0, false + } + return st.Uid, true +} diff --git a/internal/svcauth/config.go b/internal/svcauth/config.go index e403857..24fe008 100644 --- a/internal/svcauth/config.go +++ b/internal/svcauth/config.go @@ -15,9 +15,9 @@ import ( "slices" "strconv" "strings" - "syscall" "time" + "github.com/pilot-protocol/cosift/internal/fileowner" v1 "github.com/pilot-protocol/cosift/internal/v1" ) @@ -149,8 +149,8 @@ func ReadFile(path string, uid uint32) ([]byte, error) { if !st.Mode().IsRegular() { return nil, errors.New("not a regular file") } - sys, ok := st.Sys().(*syscall.Stat_t) - if !ok || sys.Uid != uid { + owner, ok := fileowner.UID(st) + if !ok || owner != uid { return nil, fmt.Errorf("owner must be uid %d", uid) } if st.Mode().Perm()&0o027 != 0 {