cmd/fsu: check parse behaviour
Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
be30e2f11e
commit
1fd571d561
@ -61,8 +61,19 @@ func main() {
|
|||||||
// aid
|
// aid
|
||||||
uid := 1000000
|
uid := 1000000
|
||||||
|
|
||||||
|
// refuse to run if fsurc is not protected correctly
|
||||||
|
if s, err := os.Stat(fsuConfFile); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
} else if s.Mode().Perm() != 0400 {
|
||||||
|
log.Fatal("bad fsurc perm")
|
||||||
|
} else if st := s.Sys().(*syscall.Stat_t); st.Uid != 0 || st.Gid != 0 {
|
||||||
|
log.Fatal("fsurc must be owned by uid 0")
|
||||||
|
}
|
||||||
|
|
||||||
// authenticate before accepting user input
|
// authenticate before accepting user input
|
||||||
if fid, ok := parseConfig(fsuConfFile, puid); !ok {
|
if f, err := os.Open(fsuConfFile); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
} else if fid, ok := mustParseConfig(f, puid); !ok {
|
||||||
log.Fatalf("uid %d is not in the fsurc file", puid)
|
log.Fatalf("uid %d is not in the fsurc file", puid)
|
||||||
} else {
|
} else {
|
||||||
uid += fid * 10000
|
uid += fid * 10000
|
||||||
|
@ -4,10 +4,9 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseUint32Fast(s string) (int, error) {
|
func parseUint32Fast(s string) (int, error) {
|
||||||
@ -30,48 +29,39 @@ func parseUint32Fast(s string) (int, error) {
|
|||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseConfig(p string, puid int) (fid int, ok bool) {
|
func parseConfig(r io.Reader, puid int) (fid int, ok bool, err error) {
|
||||||
// refuse to run if fsurc is not protected correctly
|
|
||||||
if s, err := os.Stat(p); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
} else if s.Mode().Perm() != 0400 {
|
|
||||||
log.Fatal("bad fsurc perm")
|
|
||||||
} else if st := s.Sys().(*syscall.Stat_t); st.Uid != 0 || st.Gid != 0 {
|
|
||||||
log.Fatal("fsurc must be owned by uid 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
if r, err := os.Open(p); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
return -1, false
|
|
||||||
} else {
|
|
||||||
s := bufio.NewScanner(r)
|
s := bufio.NewScanner(r)
|
||||||
var line int
|
var line, puid0 int
|
||||||
for s.Scan() {
|
for s.Scan() {
|
||||||
line++
|
line++
|
||||||
|
|
||||||
// <puid> <fid>
|
// <puid> <fid>
|
||||||
lf := strings.SplitN(s.Text(), " ", 2)
|
lf := strings.SplitN(s.Text(), " ", 2)
|
||||||
if len(lf) != 2 {
|
if len(lf) != 2 {
|
||||||
log.Fatalf("invalid entry on line %d", line)
|
return -1, false, fmt.Errorf("invalid entry on line %d", line)
|
||||||
}
|
}
|
||||||
|
|
||||||
var puid0 int
|
puid0, err = parseUint32Fast(lf[0])
|
||||||
if puid0, err = parseUint32Fast(lf[0]); err != nil || puid0 < 1 {
|
if err != nil || puid0 < 1 {
|
||||||
log.Fatalf("invalid parent uid on line %d", line)
|
return -1, false, fmt.Errorf("invalid parent uid on line %d", line)
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = puid0 == puid
|
ok = puid0 == puid
|
||||||
if ok {
|
if ok {
|
||||||
// allowed fid range 0 to 99
|
// allowed fid range 0 to 99
|
||||||
if fid, err = parseUint32Fast(lf[1]); err != nil || fid < 0 || fid > 99 {
|
if fid, err = parseUint32Fast(lf[1]); err != nil || fid < 0 || fid > 99 {
|
||||||
log.Fatalf("invalid fortify uid on line %d", line)
|
return -1, false, fmt.Errorf("invalid fortify uid on line %d", line)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err = s.Err(); err != nil {
|
return -1, false, s.Err()
|
||||||
log.Fatalf("cannot read fsurc: %v", err)
|
}
|
||||||
}
|
|
||||||
return -1, false
|
func mustParseConfig(r io.Reader, puid int) (int, bool) {
|
||||||
}
|
fid, ok, err := parseConfig(r, puid)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return fid, ok
|
||||||
}
|
}
|
||||||
|
96
cmd/fsu/parse_test.go
Normal file
96
cmd/fsu/parse_test.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_parseUint32Fast(t *testing.T) {
|
||||||
|
t.Run("zero-length", func(t *testing.T) {
|
||||||
|
if _, err := parseUint32Fast(""); err == nil || err.Error() != "zero length string" {
|
||||||
|
t.Errorf(`parseUint32Fast(""): error = %v`, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("overflow", func(t *testing.T) {
|
||||||
|
if _, err := parseUint32Fast("10000000000"); err == nil || err.Error() != "string too long" {
|
||||||
|
t.Errorf("parseUint32Fast: error = %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("invalid byte", func(t *testing.T) {
|
||||||
|
if _, err := parseUint32Fast("meow"); err == nil || err.Error() != "invalid character 'm' at index 0" {
|
||||||
|
t.Errorf(`parseUint32Fast("meow"): error = %v`, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("full range", func(t *testing.T) {
|
||||||
|
testRange := func(i, end int) {
|
||||||
|
for ; i < end; i++ {
|
||||||
|
s := strconv.Itoa(i)
|
||||||
|
w := i
|
||||||
|
t.Run("parse "+s, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
v, err := parseUint32Fast(s)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("parseUint32Fast(%q): error = %v",
|
||||||
|
s, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if v != w {
|
||||||
|
t.Errorf("parseUint32Fast(%q): got %v",
|
||||||
|
s, v)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testRange(0, 5000)
|
||||||
|
testRange(105000, 110000)
|
||||||
|
testRange(23005000, 23010000)
|
||||||
|
testRange(456005000, 456010000)
|
||||||
|
testRange(7890005000, 7890010000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_parseConfig(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
puid, want int
|
||||||
|
wantErr string
|
||||||
|
rc string
|
||||||
|
}{
|
||||||
|
{"empty", 0, -1, "", ``},
|
||||||
|
{"invalid field", 0, -1, "invalid entry on line 1", `9`},
|
||||||
|
{"invalid puid", 0, -1, "invalid parent uid on line 1", `f 9`},
|
||||||
|
{"invalid fid", 1000, -1, "invalid fortify uid on line 1", `1000 f`},
|
||||||
|
{"match", 1000, 0, "", `1000 0`},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
fid, ok, err := parseConfig(bytes.NewBufferString(tc.rc), tc.puid)
|
||||||
|
if err == nil && tc.wantErr != "" {
|
||||||
|
t.Errorf("parseConfig: error = %v; wantErr %q",
|
||||||
|
err, tc.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil && err.Error() != tc.wantErr {
|
||||||
|
t.Errorf("parseConfig: error = %q; wantErr %q",
|
||||||
|
err, tc.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ok == (tc.want == -1) {
|
||||||
|
t.Errorf("parseConfig: ok = %v; want %v",
|
||||||
|
ok, tc.want)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if fid != tc.want {
|
||||||
|
t.Errorf("parseConfig: fid = %v; want %v",
|
||||||
|
fid, tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user