carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1//go:build linux
2
3package hostbox
4
5import (
6 "errors"
7 "golang.org/x/sys/unix"
8 "os"
9 "path/filepath"
10 "strings"
11)
12
13// PrepareRootJournalDirectory walks from / through root-owned, non-writable
14// ancestors using directory descriptors. An untrusted UID cannot rename any
15// component, or access the final directory, throughout SQLite's lifetime.
16func PrepareRootJournalDirectory(path string) error { return prepareJournalDirectory(path, 0) }
17func prepareJournalDirectory(path string, uid uint32) error {
18 if !filepath.IsAbs(path) || filepath.Clean(path) != path || path == "/" {
19 return ErrTypedJournal
20 }
21 fd, err := unix.Open("/", unix.O_RDONLY|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
22 if err != nil {
23 return err
24 }
25 defer func() { unix.Close(fd) }()
26 parts := strings.Split(strings.TrimPrefix(path, "/"), "/")
27 for i, part := range parts {
28 var parent unix.Stat_t
29 if err = unix.Fstat(fd, &parent); err != nil {
30 return err
31 }
32 if parent.Uid != uid || parent.Mode&0022 != 0 {
33 return ErrTypedJournal
34 }
35 next, e := unix.Openat(fd, part, unix.O_RDONLY|unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0)
36 if errors.Is(e, unix.ENOENT) {
37 if e = unix.Mkdirat(fd, part, 0700); e != nil {
38 return e
39 }
40 if e = unix.Fsync(fd); e != nil {
41 return e
42 }
43 next, e = unix.Openat(fd, part, unix.O_RDONLY|unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0)
44 }
45 if e != nil {
46 return e
47 }
48 unix.Close(fd)
49 fd = next
50 var st unix.Stat_t
51 if e = unix.Fstat(fd, &st); e != nil {
52 return e
53 }
54 if st.Uid != uid || st.Mode&0022 != 0 {
55 return ErrTypedJournal
56 }
57 if i == len(parts)-1 && st.Mode&0077 != 0 {
58 return ErrTypedJournal
59 }
60 }
61 entries, err := os.ReadDir(path)
62 if err != nil {
63 return err
64 }
65 for _, entry := range entries {
66 var st unix.Stat_t
67 if err = unix.Fstatat(fd, entry.Name(), &st, unix.AT_SYMLINK_NOFOLLOW); err != nil {
68 return err
69 }
70 if st.Uid == uid && st.Mode&unix.S_IFMT == unix.S_IFDIR && st.Mode&0077 == 0 && (strings.HasPrefix(entry.Name(), "migration-") || entry.Name() == "retire" || entry.Name() == "authority" || entry.Name() == "held-release" || entry.Name() == "unit-bindings" || entry.Name() == "unit-materializations") {
71 continue
72 }
73 if st.Uid != uid || st.Mode&unix.S_IFMT != unix.S_IFREG || st.Nlink != 1 {
74 return ErrTypedJournal
75 }
76 }
77 return unix.Fsync(fd)
78}
79