aboutsummaryrefslogtreecommitdiffhomepage
path: root/hst
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-11-04 08:00:37 +0900
committerOphestra <cat@gensokyo.uk>2025-11-04 08:24:41 +0900
commitcb9ebf0e1541c0e49380cda6d4c2d619a0e9933f (patch)
tree8bd0bfe3714fd5975dcacb69c0a2afcd2e04c687 /hst
parent9a2a7b749f41f73c2c11e4a4557516307838457b (diff)
hst/grp_pwd: specify new uid format
This leaves slots available for additional uid ranges in Rosa OS. This breaks all existing installations! Users are required to fix ownership manually. Closes #18. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'hst')
-rw-r--r--hst/config.go2
-rw-r--r--hst/container.go5
-rw-r--r--hst/grp_pwd.go61
-rw-r--r--hst/grp_pwd_test.go37
4 files changed, 99 insertions, 6 deletions
diff --git a/hst/config.go b/hst/config.go
index ee31a555..d7078551 100644
--- a/hst/config.go
+++ b/hst/config.go
@@ -59,7 +59,7 @@ func (config *Config) Validate() error {
}
// this is checked again in hsu
- if config.Identity < IdentityMin || config.Identity > IdentityMax {
+ if config.Identity < IdentityStart || config.Identity > IdentityEnd {
return &AppError{Step: "validate configuration", Err: ErrIdentityBounds,
Msg: "identity " + strconv.Itoa(config.Identity) + " out of range"}
}
diff --git a/hst/container.go b/hst/container.go
index 75643913..f9cc95ed 100644
--- a/hst/container.go
+++ b/hst/container.go
@@ -20,11 +20,6 @@ const (
WaitDelayDefault = 5 * time.Second
// WaitDelayMax is used if WaitDelay exceeds its value.
WaitDelayMax = 30 * time.Second
-
- // IdentityMin is the minimum value of [Config.Identity]. This is enforced by cmd/hsu.
- IdentityMin = 0
- // IdentityMax is the maximum value of [Config.Identity]. This is enforced by cmd/hsu.
- IdentityMax = 9999
)
const (
diff --git a/hst/grp_pwd.go b/hst/grp_pwd.go
new file mode 100644
index 00000000..1a5c962e
--- /dev/null
+++ b/hst/grp_pwd.go
@@ -0,0 +1,61 @@
+package hst
+
+import (
+ "fmt"
+ "strconv"
+)
+
+const (
+ // UserOffset is the offset for UID and GID ranges for each user.
+ UserOffset = 100000
+ // RangeSize is the size of each UID and GID range.
+ RangeSize = UserOffset / 10
+
+ // IdentityStart is the first [Config.Identity] value. This is enforced in cmd/hsu.
+ IdentityStart = 0
+ // IdentityEnd is the last [Config.Identity] value. This is enforced in cmd/hsu.
+ IdentityEnd = AppEnd - AppStart
+
+ // AppStart is the first app user UID and GID.
+ AppStart = RangeSize * 1
+ // AppEnd is the last app user UID and GID.
+ AppEnd = AppStart + RangeSize - 1
+
+ /* these are for Rosa OS: use the ranges below to determine whether a process is isolated */
+
+ // IsolatedStart is the start of UID and GID for fully isolated sandboxed processes.
+ IsolatedStart = RangeSize * 9
+ // IsolatedEnd is the end of UID and GID for fully isolated sandboxed processes.
+ IsolatedEnd = IsolatedStart + RangeSize - 1
+)
+
+// A UID represents a kernel uid in the init namespace.
+type UID uint32
+
+// String returns the username corresponding to this uid.
+//
+// Not safe against untrusted input.
+func (uid UID) String() string {
+ appid := uid % UserOffset
+ userid := uid / UserOffset
+ if appid >= IsolatedStart && appid <= IsolatedEnd {
+ return fmt.Sprintf("u%d_i%d", userid, appid-IsolatedStart)
+ } else if appid >= AppStart && appid <= AppEnd {
+ return fmt.Sprintf("u%d_a%d", userid, appid-AppStart)
+ } else {
+ return strconv.Itoa(int(uid))
+ }
+}
+
+// A GID represents a kernel gid in the init namespace.
+type GID uint32
+
+// String returns the group name corresponding to this gid.
+//
+// Not safe against untrusted input.
+func (gid GID) String() string { return UID(gid).String() }
+
+// ToUser returns a [hst.UID] value from userid and appid.
+//
+// Not safe against untrusted input.
+func ToUser[U int | uint32](userid, appid U) U { return userid*UserOffset + AppStart + appid }
diff --git a/hst/grp_pwd_test.go b/hst/grp_pwd_test.go
new file mode 100644
index 00000000..2d01c122
--- /dev/null
+++ b/hst/grp_pwd_test.go
@@ -0,0 +1,37 @@
+package hst_test
+
+import (
+ "strconv"
+ "testing"
+
+ "hakurei.app/hst"
+)
+
+func TestUIDString(t *testing.T) {
+ t.Parallel()
+
+ testCases := []struct {
+ val uint32
+ want string
+ }{
+ {hst.AppStart + hst.IdentityStart, "u0_a0"}, // uidStart
+ {hst.ToUser[uint32](hst.RangeSize-1, hst.IdentityEnd), "u9999_a9999"}, // uidEnd
+
+ {hst.IsolatedStart + hst.IdentityStart, "u0_i0"}, // isolatedStart
+ {(hst.RangeSize-1)*hst.UserOffset + hst.IsolatedEnd, "u9999_i9999"}, // isolatedEnd
+
+ {0, "0"}, // out of bounds
+ }
+ for _, tc := range testCases {
+ t.Run(strconv.Itoa(int(tc.val)), func(t *testing.T) {
+ t.Parallel()
+
+ if got := hst.UID(tc.val).String(); got != tc.want {
+ t.Fatalf("UID.String: %q, want %q", got, tc.want)
+ }
+ if got := hst.GID(tc.val).String(); got != tc.want {
+ t.Fatalf("GID.String: %q, want %q", got, tc.want)
+ }
+ })
+ }
+}