From afa7a0800da2bb44fd9daaea1180c046ea111db8 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Wed, 24 Sep 2025 21:10:13 +0900 Subject: [PATCH] cmd/hsu: return hsurc id The uid format is stable, this value is what caller has to obtain through hsu. Closes #14. Signed-off-by: Ophestra --- cmd/hsu/main.go | 37 +++++++++-------- internal/app/process.go | 2 +- internal/sys/hsu.go | 89 ++++++++++++++++------------------------- 3 files changed, 56 insertions(+), 72 deletions(-) diff --git a/cmd/hsu/main.go b/cmd/hsu/main.go index 28571b9..cb82c20 100644 --- a/cmd/hsu/main.go +++ b/cmd/hsu/main.go @@ -15,7 +15,7 @@ import ( const ( hsuConfFile = "/etc/hsurc" envShim = "HAKUREI_SHIM" - envAID = "HAKUREI_APP_ID" + envIdentity = "HAKUREI_IDENTITY" envGroups = "HAKUREI_GROUPS" PR_SET_NO_NEW_PRIVS = 0x26 @@ -48,8 +48,8 @@ func main() { } // uid = 1000000 + - // fid * 10000 + - // aid + // id * 10000 + + // identity uid := 1000000 // refuse to run if hsurc is not protected correctly @@ -62,29 +62,25 @@ func main() { } // authenticate before accepting user input + var id int if f, err := os.Open(hsuConfFile); err != nil { log.Fatal(err) - } else if fid, ok := mustParseConfig(f, puid); !ok { + } else if v, ok := mustParseConfig(f, puid); !ok { log.Fatalf("uid %d is not in the hsurc file", puid) } else { - uid += fid * 10000 - } + id = v + if err = f.Close(); err != nil { + log.Fatal(err) + } - // allowed aid range 0 to 9999 - if as, ok := os.LookupEnv(envAID); !ok { - log.Fatal("HAKUREI_APP_ID not set") - } else if aid, err := parseUint32Fast(as); err != nil || aid < 0 || aid > 9999 { - log.Fatal("invalid aid") - } else { - uid += aid + uid += id * 10000 } // pass through setup fd to shim var shimSetupFd string if s, ok := os.LookupEnv(envShim); !ok { - // hakurei requests target uid - // print resolved uid and exit - fmt.Print(uid) + // hakurei requests hsurc user id + fmt.Print(id) os.Exit(0) } else if len(s) != 1 || s[0] > '9' || s[0] < '3' { log.Fatal("HAKUREI_SHIM holds an invalid value") @@ -92,6 +88,15 @@ func main() { shimSetupFd = s } + // allowed identity range 0 to 9999 + if as, ok := os.LookupEnv(envIdentity); !ok { + log.Fatal("HAKUREI_IDENTITY not set") + } else if identity, err := parseUint32Fast(as); err != nil || identity < 0 || identity > 9999 { + log.Fatal("invalid identity") + } else { + uid += identity + } + // supplementary groups var suppGroups, suppCurrent []int diff --git a/internal/app/process.go b/internal/app/process.go index a182fa6..368b655 100644 --- a/internal/app/process.go +++ b/internal/app/process.go @@ -255,7 +255,7 @@ func (seal *outcome) main() { // passed through to shim by hsu shimEnv + "=" + strconv.Itoa(fd), // interpreted by hsu - "HAKUREI_APP_ID=" + seal.user.identity.String(), + "HAKUREI_IDENTITY=" + seal.user.identity.String(), } } diff --git a/internal/sys/hsu.go b/internal/sys/hsu.go index 2be12d1..39fb4f1 100644 --- a/internal/sys/hsu.go +++ b/internal/sys/hsu.go @@ -17,69 +17,48 @@ import ( // Hsu caches responses from cmd/hsu. type Hsu struct { - uidOnce sync.Once - uidCopy map[int]struct { - uid int - err error - } - uidMu sync.RWMutex + idOnce sync.Once + idErr error + id int } var ErrHsuAccess = errors.New("current user is not in the hsurc file") func (h *Hsu) Uid(identity int) (int, error) { - h.uidOnce.Do(func() { - h.uidCopy = make(map[int]struct { - uid int - err error - }) + h.idOnce.Do(func() { + h.id = -1 + hsuPath := internal.MustHsuPath() + + cmd := exec.Command(hsuPath) + cmd.Path = hsuPath + cmd.Stderr = os.Stderr // pass through fatal messages + cmd.Env = make([]string, 0) + cmd.Dir = container.FHSRoot + var ( + p []byte + exitError *exec.ExitError + ) + + const step = "obtain uid from hsu" + if p, h.idErr = cmd.Output(); h.idErr == nil { + h.id, h.idErr = strconv.Atoi(string(p)) + if h.idErr != nil { + h.idErr = &hst.AppError{Step: step, Err: h.idErr, Msg: "invalid uid string from hsu"} + } + } else if errors.As(h.idErr, &exitError) && exitError != nil && exitError.ExitCode() == 1 { + // hsu prints an error message in this case + h.idErr = &hst.AppError{Step: step, Err: ErrHsuAccess} + } else if os.IsNotExist(h.idErr) { + h.idErr = &hst.AppError{Step: step, Err: os.ErrNotExist, + Msg: fmt.Sprintf("the setuid helper is missing: %s", hsuPath)} + } }) - { - h.uidMu.RLock() - u, ok := h.uidCopy[identity] - h.uidMu.RUnlock() - if ok { - return u.uid, u.err - } + uid := -1 + if h.id >= 0 { + uid = 1000000 + h.id*10000 + identity } - - h.uidMu.Lock() - defer h.uidMu.Unlock() - - u := struct { - uid int - err error - }{} - defer func() { h.uidCopy[identity] = u }() - - u.uid = -1 - hsuPath := internal.MustHsuPath() - - cmd := exec.Command(hsuPath) - cmd.Path = hsuPath - cmd.Stderr = os.Stderr // pass through fatal messages - cmd.Env = []string{"HAKUREI_APP_ID=" + strconv.Itoa(identity)} - cmd.Dir = container.FHSRoot - var ( - p []byte - exitError *exec.ExitError - ) - - const step = "obtain uid from hsu" - if p, u.err = cmd.Output(); u.err == nil { - u.uid, u.err = strconv.Atoi(string(p)) - if u.err != nil { - u.err = &hst.AppError{Step: step, Err: u.err, Msg: "invalid uid string from hsu"} - } - } else if errors.As(u.err, &exitError) && exitError != nil && exitError.ExitCode() == 1 { - // hsu prints an error message in this case - u.err = &hst.AppError{Step: step, Err: ErrHsuAccess} - } else if os.IsNotExist(u.err) { - u.err = &hst.AppError{Step: step, Err: os.ErrNotExist, - Msg: fmt.Sprintf("the setuid helper is missing: %s", hsuPath)} - } - return u.uid, u.err + return uid, h.idErr } // MustUid calls [State.Uid] and terminates on error.