github.com/haalcala/mattermost-server-change-repo/v5@v5.33.2/utils/license.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package utils 5 6 import ( 7 "crypto" 8 "crypto/rsa" 9 "crypto/sha512" 10 "crypto/x509" 11 "encoding/base64" 12 "encoding/pem" 13 "io/ioutil" 14 "os" 15 "path/filepath" 16 "strconv" 17 "strings" 18 19 "github.com/mattermost/mattermost-server/v5/mlog" 20 "github.com/mattermost/mattermost-server/v5/model" 21 "github.com/mattermost/mattermost-server/v5/utils/fileutils" 22 ) 23 24 var publicKey []byte = []byte(`-----BEGIN PUBLIC KEY----- 25 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyZmShlU8Z8HdG0IWSZ8r 26 tSyzyxrXkJjsFUf0Ke7bm/TLtIggRdqOcUF3XEWqQk5RGD5vuq7Rlg1zZqMEBk8N 27 EZeRhkxyaZW8pLjxwuBUOnXfJew31+gsTNdKZzRjrvPumKr3EtkleuoxNdoatu4E 28 HrKmR/4Yi71EqAvkhk7ZjQFuF0osSWJMEEGGCSUYQnTEqUzcZSh1BhVpkIkeu8Kk 29 1wCtptODixvEujgqVe+SrE3UlZjBmPjC/CL+3cYmufpSNgcEJm2mwsdaXp2OPpfn 30 a0v85XL6i9ote2P+fLZ3wX9EoioHzgdgB7arOxY50QRJO7OyCqpKFKv6lRWTXuSt 31 hwIDAQAB 32 -----END PUBLIC KEY-----`) 33 34 func ValidateLicense(signed []byte) (bool, string) { 35 decoded := make([]byte, base64.StdEncoding.DecodedLen(len(signed))) 36 37 _, err := base64.StdEncoding.Decode(decoded, signed) 38 if err != nil { 39 mlog.Error("Encountered error decoding license", mlog.Err(err)) 40 return false, "" 41 } 42 43 if len(decoded) <= 256 { 44 mlog.Error("Signed license not long enough") 45 return false, "" 46 } 47 48 // remove null terminator 49 for decoded[len(decoded)-1] == byte(0) { 50 decoded = decoded[:len(decoded)-1] 51 } 52 53 plaintext := decoded[:len(decoded)-256] 54 signature := decoded[len(decoded)-256:] 55 56 block, _ := pem.Decode(publicKey) 57 58 public, err := x509.ParsePKIXPublicKey(block.Bytes) 59 if err != nil { 60 mlog.Error("Encountered error signing license", mlog.Err(err)) 61 return false, "" 62 } 63 64 rsaPublic := public.(*rsa.PublicKey) 65 66 h := sha512.New() 67 h.Write(plaintext) 68 d := h.Sum(nil) 69 70 err = rsa.VerifyPKCS1v15(rsaPublic, crypto.SHA512, d, signature) 71 if err != nil { 72 mlog.Error("Invalid signature", mlog.Err(err)) 73 return false, "" 74 } 75 76 return true, string(plaintext) 77 } 78 79 func GetAndValidateLicenseFileFromDisk(location string) (*model.License, []byte) { 80 fileName := GetLicenseFileLocation(location) 81 82 if _, err := os.Stat(fileName); err != nil { 83 mlog.Debug("We could not find the license key in the database or on disk at", mlog.String("filename", fileName)) 84 return nil, nil 85 } 86 87 mlog.Info("License key has not been uploaded. Loading license key from disk at", mlog.String("filename", fileName)) 88 licenseBytes := GetLicenseFileFromDisk(fileName) 89 90 success, licenseStr := ValidateLicense(licenseBytes) 91 if !success { 92 mlog.Error("Found license key at %v but it appears to be invalid.", mlog.String("filename", fileName)) 93 return nil, nil 94 } 95 return model.LicenseFromJson(strings.NewReader(licenseStr)), licenseBytes 96 } 97 98 func GetLicenseFileFromDisk(fileName string) []byte { 99 file, err := os.Open(fileName) 100 if err != nil { 101 mlog.Error("Failed to open license key from disk at", mlog.String("filename", fileName), mlog.Err(err)) 102 return nil 103 } 104 defer file.Close() 105 106 licenseBytes, err := ioutil.ReadAll(file) 107 if err != nil { 108 mlog.Error("Failed to read license key from disk at", mlog.String("filename", fileName), mlog.Err(err)) 109 return nil 110 } 111 112 return licenseBytes 113 } 114 115 func GetLicenseFileLocation(fileLocation string) string { 116 if fileLocation == "" { 117 configDir, _ := fileutils.FindDir("config") 118 return filepath.Join(configDir, "mattermost.mattermost-license") 119 } 120 return fileLocation 121 } 122 123 func GetClientLicense(l *model.License) map[string]string { 124 props := make(map[string]string) 125 126 props["IsLicensed"] = strconv.FormatBool(l != nil) 127 128 if l != nil { 129 props["Id"] = l.Id 130 props["SkuName"] = l.SkuName 131 props["SkuShortName"] = l.SkuShortName 132 props["Users"] = strconv.Itoa(*l.Features.Users) 133 props["LDAP"] = strconv.FormatBool(*l.Features.LDAP) 134 props["LDAPGroups"] = strconv.FormatBool(*l.Features.LDAPGroups) 135 props["MFA"] = strconv.FormatBool(*l.Features.MFA) 136 props["SAML"] = strconv.FormatBool(*l.Features.SAML) 137 props["Cluster"] = strconv.FormatBool(*l.Features.Cluster) 138 props["Metrics"] = strconv.FormatBool(*l.Features.Metrics) 139 props["GoogleOAuth"] = strconv.FormatBool(*l.Features.GoogleOAuth) 140 props["Office365OAuth"] = strconv.FormatBool(*l.Features.Office365OAuth) 141 props["OpenId"] = strconv.FormatBool(*l.Features.OpenId) 142 props["Compliance"] = strconv.FormatBool(*l.Features.Compliance) 143 props["MHPNS"] = strconv.FormatBool(*l.Features.MHPNS) 144 props["Announcement"] = strconv.FormatBool(*l.Features.Announcement) 145 props["Elasticsearch"] = strconv.FormatBool(*l.Features.Elasticsearch) 146 props["DataRetention"] = strconv.FormatBool(*l.Features.DataRetention) 147 props["IDLoadedPushNotifications"] = strconv.FormatBool(*l.Features.IDLoadedPushNotifications) 148 props["IssuedAt"] = strconv.FormatInt(l.IssuedAt, 10) 149 props["StartsAt"] = strconv.FormatInt(l.StartsAt, 10) 150 props["ExpiresAt"] = strconv.FormatInt(l.ExpiresAt, 10) 151 props["Name"] = l.Customer.Name 152 props["Email"] = l.Customer.Email 153 props["Company"] = l.Customer.Company 154 props["EmailNotificationContents"] = strconv.FormatBool(*l.Features.EmailNotificationContents) 155 props["MessageExport"] = strconv.FormatBool(*l.Features.MessageExport) 156 props["CustomPermissionsSchemes"] = strconv.FormatBool(*l.Features.CustomPermissionsSchemes) 157 props["GuestAccounts"] = strconv.FormatBool(*l.Features.GuestAccounts) 158 props["GuestAccountsPermissions"] = strconv.FormatBool(*l.Features.GuestAccountsPermissions) 159 props["CustomTermsOfService"] = strconv.FormatBool(*l.Features.CustomTermsOfService) 160 props["LockTeammateNameDisplay"] = strconv.FormatBool(*l.Features.LockTeammateNameDisplay) 161 props["Cloud"] = strconv.FormatBool(*l.Features.Cloud) 162 props["SharedChannels"] = strconv.FormatBool(*l.Features.SharedChannels) 163 props["RemoteClusterService"] = strconv.FormatBool(*l.Features.RemoteClusterService) 164 } 165 166 return props 167 }