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