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