github.com/haalcala/mattermost-server-change-repo/v5@v5.33.2/app/plugin_signature.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package app 5 6 import ( 7 "bytes" 8 "io" 9 "io/ioutil" 10 "net/http" 11 "path/filepath" 12 13 "github.com/pkg/errors" 14 "golang.org/x/crypto/openpgp" 15 "golang.org/x/crypto/openpgp/armor" 16 17 "github.com/mattermost/mattermost-server/v5/mlog" 18 "github.com/mattermost/mattermost-server/v5/model" 19 "github.com/mattermost/mattermost-server/v5/utils" 20 ) 21 22 // GetPluginPublicKeyFiles returns all public keys listed in the config. 23 func (a *App) GetPluginPublicKeyFiles() ([]string, *model.AppError) { 24 return a.Config().PluginSettings.SignaturePublicKeyFiles, nil 25 } 26 27 // GetPublicKey will return the actual public key saved in the `name` file. 28 func (a *App) GetPublicKey(name string) ([]byte, *model.AppError) { 29 data, err := a.Srv().configStore.GetFile(name) 30 if err != nil { 31 return nil, model.NewAppError("GetPublicKey", "app.plugin.get_public_key.get_file.app_error", nil, err.Error(), http.StatusInternalServerError) 32 } 33 return data, nil 34 } 35 36 // AddPublicKey will add plugin public key to the config. Overwrites the previous file 37 func (a *App) AddPublicKey(name string, key io.Reader) *model.AppError { 38 if model.IsSamlFile(&a.Config().SamlSettings, name) { 39 return model.NewAppError("AddPublicKey", "app.plugin.modify_saml.app_error", nil, "", http.StatusInternalServerError) 40 } 41 data, err := ioutil.ReadAll(key) 42 if err != nil { 43 return model.NewAppError("AddPublicKey", "app.plugin.write_file.read.app_error", nil, err.Error(), http.StatusInternalServerError) 44 } 45 err = a.Srv().configStore.SetFile(name, data) 46 if err != nil { 47 return model.NewAppError("AddPublicKey", "app.plugin.write_file.saving.app_error", nil, err.Error(), http.StatusInternalServerError) 48 } 49 50 a.UpdateConfig(func(cfg *model.Config) { 51 if !utils.StringInSlice(name, cfg.PluginSettings.SignaturePublicKeyFiles) { 52 cfg.PluginSettings.SignaturePublicKeyFiles = append(cfg.PluginSettings.SignaturePublicKeyFiles, name) 53 } 54 }) 55 56 return nil 57 } 58 59 // DeletePublicKey will delete plugin public key from the config. 60 func (a *App) DeletePublicKey(name string) *model.AppError { 61 if model.IsSamlFile(&a.Config().SamlSettings, name) { 62 return model.NewAppError("AddPublicKey", "app.plugin.modify_saml.app_error", nil, "", http.StatusInternalServerError) 63 } 64 filename := filepath.Base(name) 65 if err := a.Srv().configStore.RemoveFile(filename); err != nil { 66 return model.NewAppError("DeletePublicKey", "app.plugin.delete_public_key.delete.app_error", nil, err.Error(), http.StatusInternalServerError) 67 } 68 69 a.UpdateConfig(func(cfg *model.Config) { 70 cfg.PluginSettings.SignaturePublicKeyFiles = utils.RemoveStringFromSlice(filename, cfg.PluginSettings.SignaturePublicKeyFiles) 71 }) 72 73 return nil 74 } 75 76 // VerifyPlugin checks that the given signature corresponds to the given plugin and matches a trusted certificate. 77 func (a *App) VerifyPlugin(plugin, signature io.ReadSeeker) *model.AppError { 78 if err := verifySignature(bytes.NewReader(mattermostPluginPublicKey), plugin, signature); err == nil { 79 return nil 80 } 81 publicKeys, appErr := a.GetPluginPublicKeyFiles() 82 if appErr != nil { 83 return appErr 84 } 85 for _, pk := range publicKeys { 86 pkBytes, appErr := a.GetPublicKey(pk) 87 if appErr != nil { 88 mlog.Warn("Unable to get public key for ", mlog.String("filename", pk)) 89 continue 90 } 91 publicKey := bytes.NewReader(pkBytes) 92 plugin.Seek(0, 0) 93 signature.Seek(0, 0) 94 if err := verifySignature(publicKey, plugin, signature); err == nil { 95 return nil 96 } 97 } 98 return model.NewAppError("VerifyPlugin", "api.plugin.verify_plugin.app_error", nil, "", http.StatusInternalServerError) 99 } 100 101 func verifySignature(publicKey, message, signatrue io.Reader) error { 102 pk, err := decodeIfArmored(publicKey) 103 if err != nil { 104 return errors.Wrap(err, "can't decode public key") 105 } 106 s, err := decodeIfArmored(signatrue) 107 if err != nil { 108 return errors.Wrap(err, "can't decode signature") 109 } 110 return verifyBinarySignature(pk, message, s) 111 } 112 113 func verifyBinarySignature(publicKey, signedFile, signature io.Reader) error { 114 keyring, err := openpgp.ReadKeyRing(publicKey) 115 if err != nil { 116 return errors.Wrap(err, "can't read public key") 117 } 118 if _, err = openpgp.CheckDetachedSignature(keyring, signedFile, signature); err != nil { 119 return errors.Wrap(err, "error while checking the signature") 120 } 121 return nil 122 } 123 124 func decodeIfArmored(reader io.Reader) (io.Reader, error) { 125 readBytes, err := ioutil.ReadAll(reader) 126 if err != nil { 127 return nil, errors.Wrap(err, "can't read the file") 128 } 129 block, err := armor.Decode(bytes.NewReader(readBytes)) 130 if err != nil { 131 return bytes.NewReader(readBytes), nil 132 } 133 return block.Body, nil 134 }