diff --git a/modules/mesh-files/cmd/mesh-files/files_test.go b/modules/mesh-files/cmd/mesh-files/files_test.go index 73e6b9068..aa809d1e3 100644 --- a/modules/mesh-files/cmd/mesh-files/files_test.go +++ b/modules/mesh-files/cmd/mesh-files/files_test.go @@ -32,6 +32,12 @@ func (r *fakeRoot) Run(bin string, args ...string) ([]byte, error) { r.mu.Lock() defer r.mu.Unlock() r.calls = append(r.calls, bin+" "+strings.Join(args, " ")) + for _, a := range append([]string{bin}, args...) { + if strings.ContainsRune(a, 0) { + // The kernel refuses a program argument holding a NUL byte, as exec does on a node. + return nil, fmt.Errorf("fork/exec /usr/bin/sudo: invalid argument") + } + } if e, ok := r.fail[bin+" "+strings.Join(args, " ")]; ok { return nil, e } @@ -672,7 +678,7 @@ func TestTheReadsFallBackToRoot(t *testing.T) { t.Errorf("readlink through root: %+v %v", rl, err) } // list - g.root.out["/usr/bin/find -H "+in+" -mindepth 1 -maxdepth 1 -printf %y\t%U\t%G\t%m\t%s\t%l\t%f\x00"] = + g.root.out["/usr/bin/find -H "+in+" -mindepth 1 -maxdepth 1 -printf %y\t%U\t%G\t%m\t%s\t%l\t%f\\0"] = "f\t1000\t998\t640\t2\t\tf\x00d\t1000\t1000\t755\t4096\t\tsub dir\x00l\t1000\t1000\t777\t1\tf\tl\x00" ls, err := g.List(in) if err != nil || ls.Count != 3 { @@ -689,7 +695,7 @@ func TestTheReadsFallBackToRoot(t *testing.T) { t.Errorf("entries: %+v", ls.Entries) } // search: the files are listed by find, the secret ones skipped by name, the rest read through cat - g.root.out["/usr/bin/find -H "+locked+" -maxdepth 5 -xdev -type f -printf %p\x00"] = + g.root.out["/usr/bin/find -H "+locked+" -maxdepth 5 -xdev -type f -printf %p\\0"] = in + "/f\x00" + locked + "/.ssh/id_rsa\x00" g.root.cat[in+"/f"] = "one\nneedle here\n" g.root.reach(in + "/f") @@ -706,7 +712,7 @@ func TestTheReadsFallBackToRoot(t *testing.T) { // a secret place is listed, as root lists it, its entries marked and never their content (ADR 0394 §7); a path // that is not there under a directory the account may not enter is said missing (design 39 parts §15) secretDir := locked + "/.ssh" - g.root.out["/usr/bin/find -H "+secretDir+" -mindepth 1 -maxdepth 1 -printf %y\t%U\t%G\t%m\t%s\t%l\t%f\x00"] = + g.root.out["/usr/bin/find -H "+secretDir+" -mindepth 1 -maxdepth 1 -printf %y\t%U\t%G\t%m\t%s\t%l\t%f\\0"] = "f\t1000\t1000\t600\t9\t\tid_rsa\x00" if ls, err := g.List(secretDir); err != nil || ls.Secret == "" || len(ls.Entries) != 1 || ls.Entries[0].Name != "id_rsa" { t.Errorf("a secret place was not listed through root: %+v %v", ls, err) diff --git a/modules/mesh-files/cmd/mesh-files/reads.go b/modules/mesh-files/cmd/mesh-files/reads.go index fc9c67f3e..e68452047 100644 --- a/modules/mesh-files/cmd/mesh-files/reads.go +++ b/modules/mesh-files/cmd/mesh-files/reads.go @@ -610,7 +610,7 @@ func (r Reader) finishList(path string, all []Entry, total int, secret string) L // a name cannot confuse it: the fields end with a NUL, and the name is the last. func (r Reader) listAsRoot(path string, names accounts) ([]Entry, error) { out, err := r.Root.Run("/usr/bin/find", "-H", path, "-mindepth", "1", "-maxdepth", "1", "-printf", - "%y\t%U\t%G\t%m\t%s\t%l\t%f\x00") + "%y\t%U\t%G\t%m\t%s\t%l\t%f\\0") // find writes the NUL: an argument may not hold one if err != nil { return nil, fmt.Errorf("%s cannot be listed, not even through sudo: %v", path, err) } @@ -967,7 +967,7 @@ func (r Reader) Search(ask SearchAsk) (SearchAnswer, error) { // and searches each: the names are judged first, so no credential file is read. func (r Reader) searchBelowAsRoot(p string, depth int, needle string, fold bool, bound int, deadline time.Time, a *SearchAnswer) error { out, err := r.Root.Run("/usr/bin/find", "-H", p, "-maxdepth", strconv.Itoa(depth), "-xdev", "-type", "f", - "-printf", "%p\x00") + "-printf", "%p\\0") // find writes the NUL: an argument may not hold one if err != nil { a.Unreadable++ return nil