github.com/psyb0t/mattermost-server@v4.6.1-0.20180125161845-5503a1351abf+incompatible/utils/license.go (about) 1 // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package utils 5 6 import ( 7 "crypto" 8 "crypto/md5" 9 "crypto/rsa" 10 "crypto/sha512" 11 "crypto/x509" 12 "encoding/base64" 13 "encoding/pem" 14 "fmt" 15 "io/ioutil" 16 "os" 17 "strconv" 18 "strings" 19 "sync/atomic" 20 21 l4g "github.com/alecthomas/log4go" 22 23 "github.com/mattermost/mattermost-server/model" 24 ) 25 26 var isLicensedInt32 int32 27 var licenseValue atomic.Value 28 var clientLicenseValue atomic.Value 29 30 var publicKey []byte = []byte(`-----BEGIN PUBLIC KEY----- 31 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyZmShlU8Z8HdG0IWSZ8r 32 tSyzyxrXkJjsFUf0Ke7bm/TLtIggRdqOcUF3XEWqQk5RGD5vuq7Rlg1zZqMEBk8N 33 EZeRhkxyaZW8pLjxwuBUOnXfJew31+gsTNdKZzRjrvPumKr3EtkleuoxNdoatu4E 34 HrKmR/4Yi71EqAvkhk7ZjQFuF0osSWJMEEGGCSUYQnTEqUzcZSh1BhVpkIkeu8Kk 35 1wCtptODixvEujgqVe+SrE3UlZjBmPjC/CL+3cYmufpSNgcEJm2mwsdaXp2OPpfn 36 a0v85XL6i9ote2P+fLZ3wX9EoioHzgdgB7arOxY50QRJO7OyCqpKFKv6lRWTXuSt 37 hwIDAQAB 38 -----END PUBLIC KEY-----`) 39 40 func init() { 41 SetLicense(nil) 42 } 43 44 func IsLicensed() bool { 45 return atomic.LoadInt32(&isLicensedInt32) == 1 46 } 47 48 func SetIsLicensed(v bool) { 49 if v { 50 atomic.StoreInt32(&isLicensedInt32, 1) 51 } else { 52 atomic.StoreInt32(&isLicensedInt32, 0) 53 } 54 } 55 56 func License() *model.License { 57 return licenseValue.Load().(*model.License) 58 } 59 60 func SetClientLicense(m map[string]string) { 61 clientLicenseValue.Store(m) 62 } 63 64 func ClientLicense() map[string]string { 65 return clientLicenseValue.Load().(map[string]string) 66 } 67 68 func LoadLicense(licenseBytes []byte) { 69 if success, licenseStr := ValidateLicense(licenseBytes); success { 70 license := model.LicenseFromJson(strings.NewReader(licenseStr)) 71 SetLicense(license) 72 return 73 } 74 75 l4g.Warn(T("utils.license.load_license.invalid.warn")) 76 } 77 78 var licenseListeners = map[string]func(){} 79 80 func AddLicenseListener(listener func()) string { 81 id := model.NewId() 82 licenseListeners[id] = listener 83 return id 84 } 85 86 func RemoveLicenseListener(id string) { 87 delete(licenseListeners, id) 88 } 89 90 func SetLicense(license *model.License) bool { 91 defer func() { 92 for _, listener := range licenseListeners { 93 listener() 94 } 95 }() 96 97 if license == nil { 98 SetIsLicensed(false) 99 license = &model.License{ 100 Features: new(model.Features), 101 } 102 license.Features.SetDefaults() 103 licenseValue.Store(license) 104 105 SetClientLicense(map[string]string{"IsLicensed": "false"}) 106 107 return false 108 } else { 109 license.Features.SetDefaults() 110 111 if !license.IsExpired() { 112 licenseValue.Store(license) 113 SetIsLicensed(true) 114 clientLicenseValue.Store(getClientLicense(license)) 115 return true 116 } 117 118 return false 119 } 120 } 121 122 func RemoveLicense() { 123 SetLicense(nil) 124 } 125 126 func ValidateLicense(signed []byte) (bool, string) { 127 decoded := make([]byte, base64.StdEncoding.DecodedLen(len(signed))) 128 129 _, err := base64.StdEncoding.Decode(decoded, signed) 130 if err != nil { 131 l4g.Error(T("utils.license.validate_license.decode.error"), err.Error()) 132 return false, "" 133 } 134 135 if len(decoded) <= 256 { 136 l4g.Error(T("utils.license.validate_license.not_long.error")) 137 return false, "" 138 } 139 140 // remove null terminator 141 for decoded[len(decoded)-1] == byte(0) { 142 decoded = decoded[:len(decoded)-1] 143 } 144 145 plaintext := decoded[:len(decoded)-256] 146 signature := decoded[len(decoded)-256:] 147 148 block, _ := pem.Decode(publicKey) 149 150 public, err := x509.ParsePKIXPublicKey(block.Bytes) 151 if err != nil { 152 l4g.Error(T("utils.license.validate_license.signing.error"), err.Error()) 153 return false, "" 154 } 155 156 rsaPublic := public.(*rsa.PublicKey) 157 158 h := sha512.New() 159 h.Write(plaintext) 160 d := h.Sum(nil) 161 162 err = rsa.VerifyPKCS1v15(rsaPublic, crypto.SHA512, d, signature) 163 if err != nil { 164 l4g.Error(T("utils.license.validate_license.invalid.error"), err.Error()) 165 return false, "" 166 } 167 168 return true, string(plaintext) 169 } 170 171 func GetAndValidateLicenseFileFromDisk(location string) (*model.License, []byte) { 172 fileName := GetLicenseFileLocation(location) 173 174 if _, err := os.Stat(fileName); err != nil { 175 l4g.Debug("We could not find the license key in the database or on disk at %v", fileName) 176 return nil, nil 177 } 178 179 l4g.Info("License key has not been uploaded. Loading license key from disk at %v", fileName) 180 licenseBytes := GetLicenseFileFromDisk(fileName) 181 182 if success, licenseStr := ValidateLicense(licenseBytes); !success { 183 l4g.Error("Found license key at %v but it appears to be invalid.", fileName) 184 return nil, nil 185 } else { 186 return model.LicenseFromJson(strings.NewReader(licenseStr)), licenseBytes 187 } 188 } 189 190 func GetLicenseFileFromDisk(fileName string) []byte { 191 file, err := os.Open(fileName) 192 if err != nil { 193 l4g.Error("Failed to open license key from disk at %v err=%v", fileName, err.Error()) 194 return nil 195 } 196 defer file.Close() 197 198 licenseBytes, err := ioutil.ReadAll(file) 199 if err != nil { 200 l4g.Error("Failed to read license key from disk at %v err=%v", fileName, err.Error()) 201 return nil 202 } 203 204 return licenseBytes 205 } 206 207 func GetLicenseFileLocation(fileLocation string) string { 208 if fileLocation == "" { 209 configDir, _ := FindDir("config") 210 return configDir + "mattermost.mattermost-license" 211 } else { 212 return fileLocation 213 } 214 } 215 216 func getClientLicense(l *model.License) map[string]string { 217 props := make(map[string]string) 218 219 props["IsLicensed"] = strconv.FormatBool(IsLicensed()) 220 221 if IsLicensed() { 222 props["Id"] = l.Id 223 props["Users"] = strconv.Itoa(*l.Features.Users) 224 props["LDAP"] = strconv.FormatBool(*l.Features.LDAP) 225 props["MFA"] = strconv.FormatBool(*l.Features.MFA) 226 props["SAML"] = strconv.FormatBool(*l.Features.SAML) 227 props["Cluster"] = strconv.FormatBool(*l.Features.Cluster) 228 props["Metrics"] = strconv.FormatBool(*l.Features.Metrics) 229 props["GoogleOAuth"] = strconv.FormatBool(*l.Features.GoogleOAuth) 230 props["Office365OAuth"] = strconv.FormatBool(*l.Features.Office365OAuth) 231 props["Compliance"] = strconv.FormatBool(*l.Features.Compliance) 232 props["CustomBrand"] = strconv.FormatBool(*l.Features.CustomBrand) 233 props["MHPNS"] = strconv.FormatBool(*l.Features.MHPNS) 234 props["PasswordRequirements"] = strconv.FormatBool(*l.Features.PasswordRequirements) 235 props["Announcement"] = strconv.FormatBool(*l.Features.Announcement) 236 props["Elasticsearch"] = strconv.FormatBool(*l.Features.Elasticsearch) 237 props["DataRetention"] = strconv.FormatBool(*l.Features.DataRetention) 238 props["IssuedAt"] = strconv.FormatInt(l.IssuedAt, 10) 239 props["StartsAt"] = strconv.FormatInt(l.StartsAt, 10) 240 props["ExpiresAt"] = strconv.FormatInt(l.ExpiresAt, 10) 241 props["Name"] = l.Customer.Name 242 props["Email"] = l.Customer.Email 243 props["Company"] = l.Customer.Company 244 props["PhoneNumber"] = l.Customer.PhoneNumber 245 props["EmailNotificationContents"] = strconv.FormatBool(*l.Features.EmailNotificationContents) 246 props["MessageExport"] = strconv.FormatBool(*l.Features.MessageExport) 247 } 248 249 return props 250 } 251 252 func GetClientLicenseEtag(useSanitized bool) string { 253 value := "" 254 255 lic := ClientLicense() 256 257 if useSanitized { 258 lic = GetSanitizedClientLicense() 259 } 260 261 for k, v := range lic { 262 value += fmt.Sprintf("%s:%s;", k, v) 263 } 264 265 return model.Etag(fmt.Sprintf("%x", md5.Sum([]byte(value)))) 266 } 267 268 func GetSanitizedClientLicense() map[string]string { 269 sanitizedLicense := make(map[string]string) 270 271 for k, v := range ClientLicense() { 272 sanitizedLicense[k] = v 273 } 274 275 if IsLicensed() { 276 delete(sanitizedLicense, "Id") 277 delete(sanitizedLicense, "Name") 278 delete(sanitizedLicense, "Email") 279 delete(sanitizedLicense, "PhoneNumber") 280 delete(sanitizedLicense, "IssuedAt") 281 delete(sanitizedLicense, "StartsAt") 282 delete(sanitizedLicense, "ExpiresAt") 283 } 284 285 return sanitizedLicense 286 }