feat: initial daemon implementation
This commit is contained in:
		
							
								
								
									
										215
									
								
								config/config.go
									
									
									
									
									
								
							
							
						
						
									
										215
									
								
								config/config.go
									
									
									
									
									
								
							| @@ -5,214 +5,14 @@ | ||||
| package config | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"code.dumpstack.io/tools/out-of-tree/distro" | ||||
|  | ||||
| 	"github.com/naoina/toml" | ||||
| ) | ||||
|  | ||||
| type Kernel struct { | ||||
| 	// TODO | ||||
| 	// Version string | ||||
| 	// From    string | ||||
| 	// To      string | ||||
|  | ||||
| 	// prev. ReleaseMask | ||||
| 	Regex        string | ||||
| 	ExcludeRegex string | ||||
| } | ||||
|  | ||||
| // Target defines the kernel | ||||
| type Target struct { | ||||
| 	Distro distro.Distro | ||||
|  | ||||
| 	Kernel Kernel | ||||
| } | ||||
|  | ||||
| // DockerName is returns stable name for docker container | ||||
| func (km Target) DockerName() string { | ||||
| 	distro := strings.ToLower(km.Distro.ID.String()) | ||||
| 	release := strings.Replace(km.Distro.Release, ".", "__", -1) | ||||
| 	return fmt.Sprintf("out_of_tree_%s_%s", distro, release) | ||||
| } | ||||
|  | ||||
| // ArtifactType is the kernel module or exploit | ||||
| type ArtifactType int | ||||
|  | ||||
| const ( | ||||
| 	// KernelModule is any kind of kernel module | ||||
| 	KernelModule ArtifactType = iota | ||||
| 	// KernelExploit is the privilege escalation exploit | ||||
| 	KernelExploit | ||||
| 	// Script for information gathering or automation | ||||
| 	Script | ||||
| ) | ||||
|  | ||||
| func (at ArtifactType) String() string { | ||||
| 	return [...]string{"module", "exploit", "script"}[at] | ||||
| } | ||||
|  | ||||
| // UnmarshalTOML is for support github.com/naoina/toml | ||||
| func (at *ArtifactType) UnmarshalTOML(data []byte) (err error) { | ||||
| 	stype := strings.Trim(string(data), `"`) | ||||
| 	stypelower := strings.ToLower(stype) | ||||
| 	if strings.Contains(stypelower, "module") { | ||||
| 		*at = KernelModule | ||||
| 	} else if strings.Contains(stypelower, "exploit") { | ||||
| 		*at = KernelExploit | ||||
| 	} else if strings.Contains(stypelower, "script") { | ||||
| 		*at = Script | ||||
| 	} else { | ||||
| 		err = fmt.Errorf("Type %s is unsupported", stype) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // MarshalTOML is for support github.com/naoina/toml | ||||
| func (at ArtifactType) MarshalTOML() (data []byte, err error) { | ||||
| 	s := "" | ||||
| 	switch at { | ||||
| 	case KernelModule: | ||||
| 		s = "module" | ||||
| 	case KernelExploit: | ||||
| 		s = "exploit" | ||||
| 	case Script: | ||||
| 		s = "script" | ||||
| 	default: | ||||
| 		err = fmt.Errorf("Cannot marshal %d", at) | ||||
| 	} | ||||
| 	data = []byte(`"` + s + `"`) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // Duration type with toml unmarshalling support | ||||
| type Duration struct { | ||||
| 	time.Duration | ||||
| } | ||||
|  | ||||
| // UnmarshalTOML for Duration | ||||
| func (d *Duration) UnmarshalTOML(data []byte) (err error) { | ||||
| 	duration := strings.Replace(string(data), "\"", "", -1) | ||||
| 	d.Duration, err = time.ParseDuration(duration) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // MarshalTOML for Duration | ||||
| func (d Duration) MarshalTOML() (data []byte, err error) { | ||||
| 	data = []byte(`"` + d.Duration.String() + `"`) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| type PreloadModule struct { | ||||
| 	Repo             string | ||||
| 	Path             string | ||||
| 	TimeoutAfterLoad Duration | ||||
| } | ||||
|  | ||||
| // Extra test files to copy over | ||||
| type FileTransfer struct { | ||||
| 	User   string | ||||
| 	Local  string | ||||
| 	Remote string | ||||
| } | ||||
|  | ||||
| type Patch struct { | ||||
| 	Path   string | ||||
| 	Source string | ||||
| 	Script string | ||||
| } | ||||
|  | ||||
| // Artifact is for .out-of-tree.toml | ||||
| type Artifact struct { | ||||
| 	Name       string | ||||
| 	Type       ArtifactType | ||||
| 	TestFiles  []FileTransfer | ||||
| 	SourcePath string | ||||
| 	Targets    []Target | ||||
|  | ||||
| 	Script string | ||||
|  | ||||
| 	Qemu struct { | ||||
| 		Cpus    int | ||||
| 		Memory  int | ||||
| 		Timeout Duration | ||||
| 	} | ||||
|  | ||||
| 	Docker struct { | ||||
| 		Timeout Duration | ||||
| 	} | ||||
|  | ||||
| 	Mitigations struct { | ||||
| 		DisableSmep  bool | ||||
| 		DisableSmap  bool | ||||
| 		DisableKaslr bool | ||||
| 		DisableKpti  bool | ||||
| 	} | ||||
|  | ||||
| 	Patches []Patch | ||||
|  | ||||
| 	Make struct { | ||||
| 		Target string | ||||
| 	} | ||||
|  | ||||
| 	StandardModules bool | ||||
|  | ||||
| 	Preload []PreloadModule | ||||
| } | ||||
|  | ||||
| func (ka Artifact) checkSupport(ki distro.KernelInfo, target Target) ( | ||||
| 	supported bool, err error) { | ||||
|  | ||||
| 	if target.Distro.Release == "" { | ||||
| 		if ki.Distro.ID != target.Distro.ID { | ||||
| 			return | ||||
| 		} | ||||
| 	} else { | ||||
| 		if !ki.Distro.Equal(target.Distro) { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	r, err := regexp.Compile(target.Kernel.Regex) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	exr, err := regexp.Compile(target.Kernel.ExcludeRegex) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if !r.MatchString(ki.KernelRelease) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if target.Kernel.ExcludeRegex != "" && exr.MatchString(ki.KernelRelease) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	supported = true | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // Supported returns true if given kernel is supported by artifact | ||||
| func (ka Artifact) Supported(ki distro.KernelInfo) (supported bool, err error) { | ||||
| 	for _, km := range ka.Targets { | ||||
| 		supported, err = ka.checkSupport(ki, km) | ||||
| 		if supported { | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // KernelConfig is the ~/.out-of-tree/kernels.toml configuration description | ||||
| type KernelConfig struct { | ||||
| 	Kernels []distro.KernelInfo | ||||
| @@ -225,7 +25,7 @@ func readFileAll(path string) (buf []byte, err error) { | ||||
| 	} | ||||
| 	defer f.Close() | ||||
|  | ||||
| 	buf, err = ioutil.ReadAll(f) | ||||
| 	buf, err = io.ReadAll(f) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| @@ -243,14 +43,3 @@ func ReadKernelConfig(path string) (kernelCfg KernelConfig, err error) { | ||||
|  | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // ReadArtifactConfig is for read .out-of-tree.toml | ||||
| func ReadArtifactConfig(path string) (ka Artifact, err error) { | ||||
| 	buf, err := readFileAll(path) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	err = toml.Unmarshal(buf, &ka) | ||||
| 	return | ||||
| } | ||||
|   | ||||
| @@ -1,40 +0,0 @@ | ||||
| // Copyright 2018 Mikhail Klementev. All rights reserved. | ||||
| // Use of this source code is governed by a AGPLv3 license | ||||
| // (or later) that can be found in the LICENSE file. | ||||
|  | ||||
| package config | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"code.dumpstack.io/tools/out-of-tree/distro" | ||||
|  | ||||
| 	"github.com/naoina/toml" | ||||
| ) | ||||
|  | ||||
| func TestMarshalUnmarshal(t *testing.T) { | ||||
| 	artifactCfg := Artifact{ | ||||
| 		Name: "Put name here", | ||||
| 		Type: KernelModule, | ||||
| 	} | ||||
| 	artifactCfg.Targets = append(artifactCfg.Targets, | ||||
| 		Target{ | ||||
| 			Distro: distro.Distro{ | ||||
| 				ID:      distro.Ubuntu, | ||||
| 				Release: "18.04", | ||||
| 			}, | ||||
| 			Kernel: Kernel{ | ||||
| 				Regex: ".*", | ||||
| 			}, | ||||
| 		}) | ||||
| 	buf, err := toml.Marshal(&artifactCfg) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	var artifactCfgNew Artifact | ||||
| 	err = toml.Unmarshal(buf, &artifactCfgNew) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| } | ||||
| @@ -1,4 +1,4 @@ | ||||
| package config | ||||
| package dotfiles | ||||
| 
 | ||||
| import ( | ||||
| 	"os" | ||||
| @@ -1,7 +1,6 @@ | ||||
| package config | ||||
| package dotfiles | ||||
| 
 | ||||
| import ( | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"testing" | ||||
| @@ -18,7 +17,7 @@ func TestDirectory(t *testing.T) { | ||||
| } | ||||
| 
 | ||||
| func TestDir(t *testing.T) { | ||||
| 	tmpdir, err := ioutil.TempDir("", "out-of-tree_") | ||||
| 	tmpdir, err := os.MkdirTemp("", "out-of-tree_") | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| @@ -64,7 +63,7 @@ func TestDir(t *testing.T) { | ||||
| } | ||||
| 
 | ||||
| func TestFile(t *testing.T) { | ||||
| 	tmpdir, err := ioutil.TempDir("", "out-of-tree_") | ||||
| 	tmpdir, err := os.MkdirTemp("", "out-of-tree_") | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| @@ -9,6 +9,8 @@ import ( | ||||
| 	"os" | ||||
| 	"time" | ||||
|  | ||||
| 	"code.dumpstack.io/tools/out-of-tree/artifact" | ||||
| 	"code.dumpstack.io/tools/out-of-tree/config/dotfiles" | ||||
| 	"code.dumpstack.io/tools/out-of-tree/distro" | ||||
|  | ||||
| 	"github.com/alecthomas/kong" | ||||
| @@ -16,11 +18,6 @@ import ( | ||||
| 	"github.com/naoina/toml" | ||||
| ) | ||||
|  | ||||
| type DockerCommand struct { | ||||
| 	Distro  distro.Distro | ||||
| 	Command string | ||||
| } | ||||
|  | ||||
| type OutOfTree struct { | ||||
| 	// Directory for all files if not explicitly specified | ||||
| 	Directory string | ||||
| @@ -31,16 +28,16 @@ type OutOfTree struct { | ||||
| 	Database string | ||||
|  | ||||
| 	Qemu struct { | ||||
| 		Timeout Duration | ||||
| 		Timeout artifact.Duration | ||||
| 	} | ||||
|  | ||||
| 	Docker struct { | ||||
| 		Timeout  Duration | ||||
| 		Timeout  artifact.Duration | ||||
| 		Registry string | ||||
|  | ||||
| 		// Commands that will be executed before | ||||
| 		// the base layer of Dockerfile | ||||
| 		Commands []DockerCommand | ||||
| 		Commands []distro.Command | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -82,21 +79,21 @@ func ReadOutOfTreeConf(path string) (c OutOfTree, err error) { | ||||
| 	} | ||||
|  | ||||
| 	if c.Directory != "" { | ||||
| 		Directory = c.Directory | ||||
| 		dotfiles.Directory = c.Directory | ||||
| 	} else { | ||||
| 		c.Directory = Dir("") | ||||
| 		c.Directory = dotfiles.Dir("") | ||||
| 	} | ||||
|  | ||||
| 	if c.Kernels == "" { | ||||
| 		c.Kernels = File("kernels.toml") | ||||
| 		c.Kernels = dotfiles.File("kernels.toml") | ||||
| 	} | ||||
|  | ||||
| 	if c.UserKernels == "" { | ||||
| 		c.UserKernels = File("kernels.user.toml") | ||||
| 		c.UserKernels = dotfiles.File("kernels.user.toml") | ||||
| 	} | ||||
|  | ||||
| 	if c.Database == "" { | ||||
| 		c.Database = File("db.sqlite") | ||||
| 		c.Database = dotfiles.File("db.sqlite") | ||||
| 	} | ||||
|  | ||||
| 	if c.Qemu.Timeout.Duration == 0 { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user