mirror of
https://github.com/grafana/grafana.git
synced 2024-11-29 20:24:18 -06:00
f326b79cc1
* Security: Add gosec G304 auditing annotations Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * add G304 auditing comment Signed-off-by: bergquist <carl.bergquist@gmail.com> * add G304 auditing comment Signed-off-by: bergquist <carl.bergquist@gmail.com> * add G304 auditing comment Signed-off-by: bergquist <carl.bergquist@gmail.com> * add G304 auditing comment Signed-off-by: bergquist <carl.bergquist@gmail.com> * add G304 auditing comment Signed-off-by: bergquist <carl.bergquist@gmail.com> * add G304 auditing comment Signed-off-by: bergquist <carl.bergquist@gmail.com> * add G304 auditing comment Signed-off-by: bergquist <carl.bergquist@gmail.com> * add G304 auditing comment Signed-off-by: bergquist <carl.bergquist@gmail.com> * Add gosec annotations Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * add G304 auditing comment Signed-off-by: bergquist <carl.bergquist@gmail.com> * add G304 auditing comment Signed-off-by: bergquist <carl.bergquist@gmail.com> * Add gosec annotations Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * add G304 auditing comment Signed-off-by: bergquist <carl.bergquist@gmail.com> * add G304 auditing comment Signed-off-by: bergquist <carl.bergquist@gmail.com> * add G304 auditing comment Signed-off-by: bergquist <carl.bergquist@gmail.com> * Add gosec annotations Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * space Signed-off-by: bergquist <carl.bergquist@gmail.com> * Add gosec annotations Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> Co-authored-by: bergquist <carl.bergquist@gmail.com>
145 lines
4.6 KiB
Go
145 lines
4.6 KiB
Go
package plugins
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
"github.com/grafana/grafana/pkg/util/errutil"
|
|
|
|
"golang.org/x/crypto/openpgp"
|
|
"golang.org/x/crypto/openpgp/clearsign"
|
|
)
|
|
|
|
// Soon we can fetch keys from:
|
|
// https://grafana.com/api/plugins/ci/keys
|
|
const publicKeyText = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
Version: OpenPGP.js v4.10.1
|
|
Comment: https://openpgpjs.org
|
|
|
|
xpMEXpTXXxMFK4EEACMEIwQBiOUQhvGbDLvndE0fEXaR0908wXzPGFpf0P0Z
|
|
HJ06tsq+0higIYHp7WTNJVEZtcwoYLcPRGaa9OQqbUU63BEyZdgAkPTz3RFd
|
|
5+TkDWZizDcaVFhzbDd500yTwexrpIrdInwC/jrgs7Zy/15h8KA59XXUkdmT
|
|
YB6TR+OA9RKME+dCJozNGUdyYWZhbmEgPGVuZ0BncmFmYW5hLmNvbT7CvAQQ
|
|
EwoAIAUCXpTXXwYLCQcIAwIEFQgKAgQWAgEAAhkBAhsDAh4BAAoJEH5NDGpw
|
|
iGbnaWoCCQGQ3SQnCkRWrG6XrMkXOKfDTX2ow9fuoErN46BeKmLM4f1EkDZQ
|
|
Tpq3SE8+My8B5BIH3SOcBeKzi3S57JHGBdFA+wIJAYWMrJNIvw8GeXne+oUo
|
|
NzzACdvfqXAZEp/HFMQhCKfEoWGJE8d2YmwY2+3GufVRTI5lQnZOHLE8L/Vc
|
|
1S5MXESjzpcEXpTXXxIFK4EEACMEIwQBtHX/SD5Qm3v4V92qpaIZQgtTX0sT
|
|
cFPjYWAHqsQ1iENrYN/vg1wU3ADlYATvydOQYvkTyT/tbDvx2Fse8PL84MQA
|
|
YKKQ6AJ3gLVvmeouZdU03YoV4MYaT8KbnJUkZQZkqdz2riOlySNI9CG3oYmv
|
|
omjUAtzgAgnCcurfGLZkkMxlmY8DAQoJwqQEGBMKAAkFAl6U118CGwwACgkQ
|
|
fk0ManCIZuc0jAIJAVw2xdLr4ZQqPUhubrUyFcqlWoW8dQoQagwO8s8ubmby
|
|
KuLA9FWJkfuuRQr+O9gHkDVCez3aism7zmJBqIOi38aNAgjJ3bo6leSS2jR/
|
|
x5NqiKVi83tiXDPncDQYPymOnMhW0l7CVA7wj75HrFvvlRI/4MArlbsZ2tBn
|
|
N1c5v9v/4h6qeA==
|
|
=DNbR
|
|
-----END PGP PUBLIC KEY BLOCK-----
|
|
`
|
|
|
|
// pluginManifest holds details for the file manifest
|
|
type pluginManifest struct {
|
|
Plugin string `json:"plugin"`
|
|
Version string `json:"version"`
|
|
KeyID string `json:"keyId"`
|
|
Time int64 `json:"time"`
|
|
Files map[string]string `json:"files"`
|
|
}
|
|
|
|
// readPluginManifest attempts to read and verify the plugin manifest
|
|
// if any error occurs or the manifest is not valid, this will return an error
|
|
func readPluginManifest(body []byte) (*pluginManifest, error) {
|
|
block, _ := clearsign.Decode(body)
|
|
if block == nil {
|
|
return nil, errors.New("unable to decode manifest")
|
|
}
|
|
|
|
// Convert to a well typed object
|
|
manifest := &pluginManifest{}
|
|
err := json.Unmarshal(block.Plaintext, &manifest)
|
|
if err != nil {
|
|
return nil, errutil.Wrap("Error parsing manifest JSON", err)
|
|
}
|
|
|
|
keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(publicKeyText))
|
|
if err != nil {
|
|
return nil, errutil.Wrap("failed to parse public key", err)
|
|
}
|
|
|
|
if _, err := openpgp.CheckDetachedSignature(keyring,
|
|
bytes.NewBuffer(block.Bytes),
|
|
block.ArmoredSignature.Body); err != nil {
|
|
return nil, errutil.Wrap("failed to check signature", err)
|
|
}
|
|
|
|
return manifest, nil
|
|
}
|
|
|
|
// getPluginSignatureState returns the signature state for a plugin.
|
|
func getPluginSignatureState(log log.Logger, plugin *PluginBase) PluginSignature {
|
|
log.Debug("Getting signature state of plugin", "plugin", plugin.Id, "isBackend", plugin.Backend)
|
|
manifestPath := filepath.Join(plugin.PluginDir, "MANIFEST.txt")
|
|
|
|
// nolint:gosec
|
|
// We can ignore the gosec G304 warning on this one because `manifestPath` is based
|
|
// on plugin the folder structure on disk and not user input.
|
|
byteValue, err := ioutil.ReadFile(manifestPath)
|
|
if err != nil || len(byteValue) < 10 {
|
|
log.Debug("Plugin is unsigned", "id", plugin.Id)
|
|
return PluginSignatureUnsigned
|
|
}
|
|
|
|
manifest, err := readPluginManifest(byteValue)
|
|
if err != nil {
|
|
log.Debug("Plugin signature invalid", "id", plugin.Id)
|
|
return PluginSignatureInvalid
|
|
}
|
|
|
|
// Make sure the versions all match
|
|
if manifest.Plugin != plugin.Id || manifest.Version != plugin.Info.Version {
|
|
return PluginSignatureModified
|
|
}
|
|
|
|
// Verify the manifest contents
|
|
log.Debug("Verifying contents of plugin manifest", "plugin", plugin.Id)
|
|
for p, hash := range manifest.Files {
|
|
// Open the file
|
|
fp := filepath.Join(plugin.PluginDir, p)
|
|
|
|
// nolint:gosec
|
|
// We can ignore the gosec G304 warning on this one because `fp` is based
|
|
// on the manifest file for a plugin and not user input.
|
|
f, err := os.Open(fp)
|
|
if err != nil {
|
|
return PluginSignatureModified
|
|
}
|
|
defer func() {
|
|
if err := f.Close(); err != nil {
|
|
log.Warn("Failed to close plugin file", "path", fp, "err", err)
|
|
}
|
|
}()
|
|
|
|
h := sha256.New()
|
|
if _, err := io.Copy(h, f); err != nil {
|
|
log.Warn("Couldn't read plugin file", "plugin", plugin.Id, "filename", fp)
|
|
return PluginSignatureModified
|
|
}
|
|
sum := hex.EncodeToString(h.Sum(nil))
|
|
if sum != hash {
|
|
log.Warn("Plugin file's signature has been modified versus manifest", "plugin", plugin.Id, "filename", fp)
|
|
return PluginSignatureModified
|
|
}
|
|
}
|
|
|
|
// Everything OK
|
|
log.Debug("Plugin signature valid", "id", plugin.Id)
|
|
return PluginSignatureValid
|
|
}
|