refactor some package and add some documentation

This commit is contained in:
criyle 2019-05-21 22:46:55 -07:00
parent a122ceed5a
commit 23c02267e6
11 changed files with 253 additions and 204 deletions

View File

@ -1,59 +0,0 @@
package main
// getConf return file access check set, syscall counter, allow and traced syscall arrays and new args
func getConf(pType, workPath string, args, addRead, addWrite []string, allowProc bool) (*fileSets, syscallCounter, []string, []string, []string) {
var (
fs = newFileSets()
sc = newSyscallCounter()
allow = append([]string{}, defaultSyscallAllows...)
trace = append([]string{}, defaultSyscallTraces...)
)
fs.Readable.AddRange(defaultReadableFiles)
fs.Writable.AddRange(defaultWritableFiles)
fs.addFilePermission(args[0], filePermRead)
fs.addFilePermission(workPath, filePermRead)
fs.Readable.AddRange(addRead)
fs.Writable.AddRange(addWrite)
if c, o := runprogramConfig[pType]; o {
allow = append(allow, c.Syscall.ExtraAllow...)
trace = append(trace, c.Syscall.ExtraBan...)
sc.addRange(c.Syscall.ExtraCount)
fs.Readable.AddRange(c.FileAccess.ExtraRead)
fs.Writable.AddRange(c.FileAccess.ExtraWrite)
fs.Statable.AddRange(c.FileAccess.ExtraStat)
fs.SoftBan.AddRange(c.FileAccess.ExtraBan)
args = append(c.RunCommand, args...)
}
if allowProc {
allow = append(allow, defaultProcSyscalls...)
}
allow, trace = cleanTrace(allow, trace)
return fs, sc, allow, trace, args
}
func keySetToSlice(m map[string]bool) []string {
rt := make([]string, 0, len(m))
for k := range m {
rt = append(rt, k)
}
return rt
}
func cleanTrace(allow, trace []string) ([]string, []string) {
// make sure allow, trace no duplicate
traceMap := make(map[string]bool)
for _, s := range trace {
traceMap[s] = true
}
allowMap := make(map[string]bool)
for _, s := range allow {
if !traceMap[s] {
allowMap[s] = true
}
}
return keySetToSlice(allowMap), keySetToSlice(traceMap)
}

View File

@ -1,61 +0,0 @@
package main
import (
"fmt"
"os"
"github.com/criyle/go-judger/runprogram"
)
type handler struct {
fs *fileSets
sc syscallCounter
showDetails bool
}
func (h *handler) print(v ...interface{}) {
if h.showDetails {
fmt.Fprintln(os.Stderr, v...)
}
}
func (h *handler) onDgsFileDetect(name string) runprogram.TraceAction {
if h.fs.isSoftBanFile(name) {
return runprogram.TraceBan
}
h.print("Dangerous fileopen: ", name)
return runprogram.TraceKill
}
func (h *handler) CheckRead(fn string) runprogram.TraceAction {
if !h.fs.isReadableFile(fn) {
return h.onDgsFileDetect(fn)
}
return runprogram.TraceAllow
}
func (h *handler) CheckWrite(fn string) runprogram.TraceAction {
if !h.fs.isWritableFile(fn) {
return h.onDgsFileDetect(fn)
}
return runprogram.TraceAllow
}
func (h *handler) CheckStat(fn string) runprogram.TraceAction {
if !h.fs.isStatableFile(fn) {
return h.onDgsFileDetect(fn)
}
return runprogram.TraceAllow
}
func (h *handler) CheckSyscall(syscallName string) runprogram.TraceAction {
// if it is traced, then try to count syscall
if inside, allow := h.sc.check(syscallName); inside {
if allow {
return runprogram.TraceAllow
}
return runprogram.TraceKill
}
// if it is traced but not counted, it should be soft banned
return runprogram.TraceBan
}

View File

@ -5,11 +5,17 @@ import (
"fmt"
"os"
"github.com/criyle/go-judger/runconfig"
"github.com/criyle/go-judger/runprogram"
"github.com/criyle/go-judger/tracer"
)
// TODO: syscall handle, file access checker
func printUsage() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] <args>\n", os.Args[0])
flag.PrintDefaults()
os.Exit(2)
}
func main() {
var (
addReadable, addWritable, addRawReadable, addRawWritable arrayFlags
@ -19,6 +25,7 @@ func main() {
inputFileName, outputFileName, errorFileName, workPath string
)
flag.Usage = printUsage
flag.UintVar(&timeLimit, "tl", 1, "Set time limit (in second)")
flag.UintVar(&realTimeLimit, "rtl", 0, "Set real time limit (in second)")
flag.UintVar(&memoryLimit, "ml", 256, "Set memory limit (in mb)")
@ -37,12 +44,18 @@ func main() {
flag.BoolVar(&allowProc, "allow-proc", false, "Allow fork, exec... etc.")
flag.Var(&addRawReadable, "add-readable-raw", "Add a readable file (don't transform to its real path)")
flag.Var(&addRawWritable, "add-writable-raw", "Add a writable file (don't transform to its real path)")
flag.Parse()
args := flag.Args()
addRead := getExtraSet(addReadable, addRawReadable)
addWrite := getExtraSet(addWritable, addRawWritable)
if len(args) == 0 {
printUsage()
}
println := func(v ...interface{}) {
if showDetails {
fmt.Fprintln(os.Stderr, v...)
}
}
if realTimeLimit < timeLimit {
realTimeLimit = timeLimit + 2
@ -54,13 +67,9 @@ func main() {
workPath, _ = os.Getwd()
}
fs, sc, allow, trace, args := getConf(pType, workPath, args, addRead, addWrite, allowProc)
println := func(v ...interface{}) {
if showDetails {
fmt.Fprintln(os.Stderr, v...)
}
}
addRead := runconfig.GetExtraSet(addReadable, addRawReadable)
addWrite := runconfig.GetExtraSet(addWritable, addRawWritable)
h := runconfig.GetConf(pType, workPath, args, addRead, addWrite, allowProc, showDetails)
// open input / output / err files
files, err := prepareFiles(inputFileName, outputFileName, errorFileName)
@ -81,7 +90,7 @@ func main() {
}
runner := &runprogram.RunProgram{
Args: args,
Args: h.Args,
Env: []string{"PATH=/"},
WorkDir: workPath,
RLimits: runprogram.RLimits{
@ -96,11 +105,11 @@ func main() {
MemoryLimit: memoryLimit << 10,
},
Files: fds,
SyscallAllowed: allow,
SyscallTraced: trace,
SyscallAllowed: h.SyscallAllow,
SyscallTraced: h.SyscallTrace,
ShowDetails: showDetails,
Unsafe: unsafe,
Handler: &handler{fs, sc, showDetails},
Handler: h,
}
var f *os.File

View File

@ -1,30 +0,0 @@
package main
type syscallCounter map[string]int
func newSyscallCounter() syscallCounter {
return syscallCounter(make(map[string]int))
}
func (s syscallCounter) add(name string, count int) {
s[name] = count
}
func (s syscallCounter) addRange(m map[string]int) {
for k, v := range m {
s[k] = v
}
}
// check return inside, allow
func (s syscallCounter) check(name string) (bool, bool) {
n, o := s[name]
if o {
s[name] = n - 1
if n <= 1 {
return true, false
}
return true, true
}
return false, true
}

View File

@ -1,4 +1,4 @@
package main
package runconfig
// This file includes configs for the run program settings

View File

@ -1,4 +1,4 @@
package main
package runconfig
// This file includes configs for the run program settings

View File

@ -0,0 +1,66 @@
package runconfig
// GetConf return file access check set, syscall counter, allow and traced syscall arrays and new args
func GetConf(pType, workPath string, args, addRead, addWrite []string, allowProc, showDetails bool) *Handler {
var (
fs = NewFileSets()
sc = NewSyscallCounter()
allow = append([]string{}, defaultSyscallAllows...)
trace = append([]string{}, defaultSyscallTraces...)
)
fs.Readable.AddRange(defaultReadableFiles, workPath)
fs.Writable.AddRange(defaultWritableFiles, workPath)
fs.AddFilePermission(args[0], FilePermRead)
fs.AddFilePermission(workPath, FilePermRead)
fs.Readable.AddRange(addRead, workPath)
fs.Writable.AddRange(addWrite, workPath)
if c, o := runprogramConfig[pType]; o {
allow = append(allow, c.Syscall.ExtraAllow...)
trace = append(trace, c.Syscall.ExtraBan...)
sc.AddRange(c.Syscall.ExtraCount)
fs.Readable.AddRange(c.FileAccess.ExtraRead, workPath)
fs.Writable.AddRange(c.FileAccess.ExtraWrite, workPath)
fs.Statable.AddRange(c.FileAccess.ExtraStat, workPath)
fs.SoftBan.AddRange(c.FileAccess.ExtraBan, workPath)
args = append(c.RunCommand, args...)
}
if allowProc {
allow = append(allow, defaultProcSyscalls...)
}
allow, trace = cleanTrace(allow, trace)
return &Handler{
SyscallAllow: allow,
SyscallTrace: trace,
Args: args,
FileSet: fs,
SyscallCounter: sc,
ShowDetails: showDetails,
}
}
func keySetToSlice(m map[string]bool) []string {
rt := make([]string, 0, len(m))
for k := range m {
rt = append(rt, k)
}
return rt
}
func cleanTrace(allow, trace []string) ([]string, []string) {
// make sure allow, trace no duplicate
traceMap := make(map[string]bool)
for _, s := range trace {
traceMap[s] = true
}
allowMap := make(map[string]bool)
for _, s := range allow {
if !traceMap[s] {
allowMap[s] = true
}
}
return keySetToSlice(allowMap), keySetToSlice(traceMap)
}

View File

@ -1,4 +1,4 @@
package main
package runconfig
// ProgramConfig defines the extra config apply to program type
type ProgramConfig struct {

View File

@ -1,4 +1,4 @@
package main
package runconfig
import (
"fmt"
@ -8,26 +8,29 @@ import (
"strings"
)
// fileSet stores the file permissions
type fileSet struct {
// FileSet stores the file permissions in the hierarchical set
type FileSet struct {
Set map[string]bool
SystemRoot bool
}
type filePerm int
// FilePerm stores the permission apply to the file
type FilePerm int
// FilePermWrite / Read / Stat are permissions
const (
filePermWrite = iota + 1
filePermRead
filePermStat
FilePermWrite = iota + 1
FilePermRead
FilePermStat
)
func newFileSet() fileSet {
return fileSet{make(map[string]bool), false}
// NewFileSet creates the new file set
func NewFileSet() FileSet {
return FileSet{make(map[string]bool), false}
}
// IsInSetSmart same from uoj-judger
func (s *fileSet) IsInSetSmart(name string) bool {
func (s *FileSet) IsInSetSmart(name string) bool {
if s.Set[name] {
return true
}
@ -54,46 +57,60 @@ func (s *fileSet) IsInSetSmart(name string) bool {
return false
}
func (s *fileSet) Add(name string) {
// Add adds a single file path into the FileSet
func (s *FileSet) Add(name string) {
s.Set[name] = true
}
func (s *fileSet) AddRange(names []string) {
// AddRange adds multiple files into the FileSet
// If path is relative path, add according to the workPath
func (s *FileSet) AddRange(names []string, workPath string) {
for _, n := range names {
s.Set[n] = true
if filepath.IsAbs(n) {
s.Set[n] = true
} else {
s.Set[filepath.Join(workPath, n)] = true
}
}
}
type fileSets struct {
Writable, Readable, Statable, SoftBan fileSet
// FileSets agregates multiple permissions including write / read / stat / soft ban
type FileSets struct {
Writable, Readable, Statable, SoftBan FileSet
}
func newFileSets() *fileSets {
return &fileSets{newFileSet(), newFileSet(), newFileSet(), newFileSet()}
// NewFileSets creates new FileSets struct
func NewFileSets() *FileSets {
return &FileSets{NewFileSet(), NewFileSet(), NewFileSet(), NewFileSet()}
}
func (s *fileSets) isWritableFile(name string) bool {
// IsWritableFile determines whether the file path inside the write set
func (s *FileSets) IsWritableFile(name string) bool {
return s.Writable.IsInSetSmart(name) || s.Writable.IsInSetSmart(realPath(name))
}
func (s *fileSets) isReadableFile(name string) bool {
return s.isWritableFile(name) || s.Readable.IsInSetSmart(name) || s.Readable.IsInSetSmart(realPath(name))
// IsReadableFile determines whether the file path inside the read / write set
func (s *FileSets) IsReadableFile(name string) bool {
return s.IsWritableFile(name) || s.Readable.IsInSetSmart(name) || s.Readable.IsInSetSmart(realPath(name))
}
func (s *fileSets) isStatableFile(name string) bool {
return s.isReadableFile(name) || s.Statable.IsInSetSmart(name) || s.Statable.IsInSetSmart(realPath(name))
// IsStatableFile determines whether the file path inside the stat / read / write set
func (s *FileSets) IsStatableFile(name string) bool {
return s.IsReadableFile(name) || s.Statable.IsInSetSmart(name) || s.Statable.IsInSetSmart(realPath(name))
}
func (s *fileSets) isSoftBanFile(name string) bool {
// IsSoftBanFile determines whether the file path inside the softban set
func (s *FileSets) IsSoftBanFile(name string) bool {
return s.SoftBan.IsInSetSmart(name) || s.SoftBan.IsInSetSmart(realPath(name))
}
func (s *fileSets) addFilePermission(name string, mode filePerm) {
if mode == filePermWrite {
// AddFilePermission adds the file into fileSets according to the given permission
func (s *FileSets) AddFilePermission(name string, mode FilePerm) {
if mode == FilePermWrite {
s.Writable.Add(name)
} else if mode == filePermRead {
} else if mode == FilePermRead {
s.Readable.Add(name)
} else if mode == filePermStat {
} else if mode == FilePermStat {
s.Statable.Add(name)
}
for name = dirname(name); name != ""; name = dirname(name) {
@ -101,6 +118,16 @@ func (s *fileSets) addFilePermission(name string, mode filePerm) {
}
}
// GetExtraSet evaluates the concated file set according to real path or raw path
func GetExtraSet(extra, raw []string) []string {
rt := make([]string, 0, len(extra)+len(raw))
rt = append(rt, raw...)
for _, v := range extra {
rt = append(rt, realPath(v))
}
return rt
}
// basename return path with last "/"
func basename(path string) string {
if p := strings.LastIndex(path, "/"); p >= 0 {
@ -147,12 +174,3 @@ func realPath(p string) string {
}
return f
}
func getExtraSet(extra, raw []string) []string {
rt := make([]string, 0, len(extra)+len(raw))
rt = append(rt, raw...)
for _, v := range extra {
rt = append(rt, realPath(v))
}
return rt
}

72
runconfig/handle.go Normal file
View File

@ -0,0 +1,72 @@
package runconfig
import (
"fmt"
"os"
"github.com/criyle/go-judger/runprogram"
)
// Handler defines file access restricted handler to call the runprogram
// safe runner
type Handler struct {
SyscallAllow, SyscallTrace, Args []string
FileSet *FileSets
SyscallCounter SyscallCounter
ShowDetails bool
}
// CheckRead checks whether the file have read permission
func (h *Handler) CheckRead(fn string) runprogram.TraceAction {
if !h.FileSet.IsReadableFile(fn) {
return h.onDgsFileDetect(fn)
}
return runprogram.TraceAllow
}
// CheckWrite checks whether the file have write permission
func (h *Handler) CheckWrite(fn string) runprogram.TraceAction {
if !h.FileSet.IsWritableFile(fn) {
return h.onDgsFileDetect(fn)
}
return runprogram.TraceAllow
}
// CheckStat checks whether the file have stat permission
func (h *Handler) CheckStat(fn string) runprogram.TraceAction {
if !h.FileSet.IsStatableFile(fn) {
return h.onDgsFileDetect(fn)
}
return runprogram.TraceAllow
}
// CheckSyscall checks syscalls other than allowed and traced agianst the
// SyscallCounter
func (h *Handler) CheckSyscall(syscallName string) runprogram.TraceAction {
// if it is traced, then try to count syscall
if inside, allow := h.SyscallCounter.Check(syscallName); inside {
if allow {
return runprogram.TraceAllow
}
return runprogram.TraceKill
}
// if it is traced but not counted, it should be soft banned
return runprogram.TraceBan
}
// onDgsFileDetect soft ban file if in soft ban set
// otherwise stops the trace process
func (h *Handler) onDgsFileDetect(name string) runprogram.TraceAction {
if h.FileSet.IsSoftBanFile(name) {
return runprogram.TraceBan
}
h.print("Dangerous fileopen: ", name)
return runprogram.TraceKill
}
// print is used to print debug information
func (h *Handler) print(v ...interface{}) {
if h.ShowDetails {
fmt.Fprintln(os.Stderr, v...)
}
}

View File

@ -0,0 +1,34 @@
package runconfig
// SyscallCounter defines a count-down for each each syscall occurs
type SyscallCounter map[string]int
// NewSyscallCounter creates a new SyscallCounter
func NewSyscallCounter() SyscallCounter {
return SyscallCounter(make(map[string]int))
}
// Add adds single counter to SyscallCounter
func (s SyscallCounter) Add(name string, count int) {
s[name] = count
}
// AddRange add multiple counter to SyscallCounter
func (s SyscallCounter) AddRange(m map[string]int) {
for k, v := range m {
s[k] = v
}
}
// Check return inside, allow
func (s SyscallCounter) Check(name string) (bool, bool) {
n, o := s[name]
if o {
s[name] = n - 1
if n <= 1 {
return true, false
}
return true, true
}
return false, true
}