From 209270a179b173c5bcb819f16363699fb966c854 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Tue, 10 Dec 2024 13:18:54 -0500 Subject: [PATCH] Add untested plugin support --- environment.go | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ error.go | 11 +++++++---- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/environment.go b/environment.go index 96075d9..356138b 100644 --- a/environment.go +++ b/environment.go @@ -5,7 +5,9 @@ import "io" import "fmt" import "time" import "io/fs" +import "plugin" import "errors" +import "syscall" import "context" import "path/filepath" import "html/template" @@ -103,6 +105,55 @@ func (this *Environment) Unload (name string) { delete(documents, name) } +// LoadProviderPlugin loads a plugin given its file path. The file must: +// +// - Be a shared library +// - Be built with go -buildmode=plugin +// - Be built with the same version of Go +// - Be built with the same version of STEP +// - Be owned by root +// +// Plugins cannot be unloaded from the current program once they are loaded. +// Sorgy :( its Go's fault. +func (this *Environment) LoadProviderPlugin (name string) (Provider, error) { + return this.loadProviderPlugin(name, true) +} + +// LoadProviderPluginUnsafe is like LoadProviderPlugin, but does not check to +// see that the file is owned by root, thereby making it easier to run a random +// plugin you just compiled. This should not be used otherwise. +func (this *Environment) LoadProviderPluginUnsafe (name string) (Provider, error) { + return this.loadProviderPlugin(name, false) +} + +func (this *Environment) loadProviderPlugin (name string, checkRoot bool) (Provider, error) { + plugin, err := this.loadPlugin(name, checkRoot) + if err != nil { return nil, err } + providerSymbol, err := plugin.Lookup("Provider") + if err != nil { return nil, err } + providerFactory, ok := providerSymbol.(func () Provider) + if !ok { return nil, ErrPluginBadSymbol } + provider := providerFactory() + this.Providers = append(this.Providers, provider) + return provider, nil +} + +func (this *Environment) loadPlugin (name string, checkRoot bool) (*plugin.Plugin, error) { + name = filepath.Clean(name) + if checkRoot { + info, err := os.Stat(name) + if err != nil { return nil, err } + if info, ok := info.Sys().(*syscall.Stat_t); ok { + if info.Uid != 0 { + return nil, ErrPluginNotOwnedByRoot + } + } else { + return nil, ErrInsufficientSystem + } + } + return plugin.Open(name) +} + func (this *Environment) parse (name string, modTime time.Time, input io.Reader) (*Document, error) { documents, done := this.documents.Borrow() defer done() diff --git a/error.go b/error.go index 4695ba0..46a1317 100644 --- a/error.go +++ b/error.go @@ -2,10 +2,13 @@ package step // Error enumerates errors common to this package. type Error string; const ( - ErrCircularInheritance Error = "circular inheritance" - ErrMetaMalformed Error = "metadata is malformed" - ErrMetaNeverClosed Error = "metadata is never closed" - ErrTypeMismatch Error = "type mismatch" + ErrCircularInheritance Error = "circular inheritance" + ErrMetaMalformed Error = "metadata is malformed" + ErrMetaNeverClosed Error = "metadata is never closed" + ErrTypeMismatch Error = "type mismatch" + ErrPluginBadSymbol Error = "plugin has an incorrect symbol" + ErrPluginNotOwnedByRoot Error = "plugin is not owned by the root user" + ErrInsufficientSystem Error = "the system cannot perform this action" ) // Error fulfills the error interface.