github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/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 "net/http" 15 "os" 16 "path/filepath" 17 "strconv" 18 "strings" 19 20 "github.com/masterhung0112/hk_server/v5/model" 21 "github.com/masterhung0112/hk_server/v5/shared/mlog" 22 "github.com/masterhung0112/hk_server/v5/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 var LicenseValidator LicenseValidatorIface 36 37 func init() { 38 if LicenseValidator == nil { 39 LicenseValidator = &LicenseValidatorImpl{} 40 } 41 } 42 43 type LicenseValidatorIface interface { 44 LicenseFromBytes(licenseBytes []byte) (*model.License, *model.AppError) 45 ValidateLicense(signed []byte) (bool, string) 46 } 47 48 type LicenseValidatorImpl struct { 49 } 50 51 func (l *LicenseValidatorImpl) LicenseFromBytes(licenseBytes []byte) (*model.License, *model.AppError) { 52 success, licenseStr := l.ValidateLicense(licenseBytes) 53 if !success { 54 return nil, model.NewAppError("LicenseFromBytes", model.INVALID_LICENSE_ERROR, nil, "", http.StatusBadRequest) 55 } 56 57 license := model.LicenseFromJson(strings.NewReader(licenseStr)) 58 return license, nil 59 } 60 61 func (l *LicenseValidatorImpl) ValidateLicense(signed []byte) (bool, string) { 62 decoded := make([]byte, base64.StdEncoding.DecodedLen(len(signed))) 63 64 _, err := base64.StdEncoding.Decode(decoded, signed) 65 if err != nil { 66 mlog.Error("Encountered error decoding license", mlog.Err(err)) 67 return false, "" 68 } 69 70 if len(decoded) <= 256 { 71 mlog.Error("Signed license not long enough") 72 return false, "" 73 } 74 75 // remove null terminator 76 for decoded[len(decoded)-1] == byte(0) { 77 decoded = decoded[:len(decoded)-1] 78 } 79 80 plaintext := decoded[:len(decoded)-256] 81 signature := decoded[len(decoded)-256:] 82 83 block, _ := pem.Decode(publicKey) 84 85 public, err := x509.ParsePKIXPublicKey(block.Bytes) 86 if err != nil { 87 mlog.Error("Encountered error signing license", mlog.Err(err)) 88 return false, "" 89 } 90 91 rsaPublic := public.(*rsa.PublicKey) 92 93 h := sha512.New() 94 h.Write(plaintext) 95 d := h.Sum(nil) 96 97 err = rsa.VerifyPKCS1v15(rsaPublic, crypto.SHA512, d, signature) 98 if err != nil { 99 mlog.Error("Invalid signature", mlog.Err(err)) 100 return false, "" 101 } 102 103 return true, string(plaintext) 104 } 105 106 func GetAndValidateLicenseFileFromDisk(location string) (*model.License, []byte) { 107 fileName := GetLicenseFileLocation(location) 108 109 if _, err := os.Stat(fileName); err != nil { 110 mlog.Debug("We could not find the license key in the database or on disk at", mlog.String("filename", fileName)) 111 return nil, nil 112 } 113 114 mlog.Info("License key has not been uploaded. Loading license key from disk at", mlog.String("filename", fileName)) 115 licenseBytes := GetLicenseFileFromDisk(fileName) 116 117 success, licenseStr := LicenseValidator.ValidateLicense(licenseBytes) 118 if !success { 119 mlog.Error("Found license key at %v but it appears to be invalid.", mlog.String("filename", fileName)) 120 return nil, nil 121 } 122 return model.LicenseFromJson(strings.NewReader(licenseStr)), licenseBytes 123 } 124 125 func GetLicenseFileFromDisk(fileName string) []byte { 126 file, err := os.Open(fileName) 127 if err != nil { 128 mlog.Error("Failed to open license key from disk at", mlog.String("filename", fileName), mlog.Err(err)) 129 return nil 130 } 131 defer file.Close() 132 133 licenseBytes, err := ioutil.ReadAll(file) 134 if err != nil { 135 mlog.Error("Failed to read license key from disk at", mlog.String("filename", fileName), mlog.Err(err)) 136 return nil 137 } 138 139 return licenseBytes 140 } 141 142 func GetLicenseFileLocation(fileLocation string) string { 143 if fileLocation == "" { 144 configDir, _ := fileutils.FindDir("config") 145 return filepath.Join(configDir, "mattermost.mattermost-license") 146 } 147 return fileLocation 148 } 149 150 func GetClientLicense(l *model.License) map[string]string { 151 props := make(map[string]string) 152 153 props["IsLicensed"] = strconv.FormatBool(l != nil) 154 155 if l != nil { 156 props["Id"] = l.Id 157 props["SkuName"] = l.SkuName 158 props["SkuShortName"] = l.SkuShortName 159 props["Users"] = strconv.Itoa(*l.Features.Users) 160 props["LDAP"] = strconv.FormatBool(*l.Features.LDAP) 161 props["LDAPGroups"] = strconv.FormatBool(*l.Features.LDAPGroups) 162 props["MFA"] = strconv.FormatBool(*l.Features.MFA) 163 props["SAML"] = strconv.FormatBool(*l.Features.SAML) 164 props["Cluster"] = strconv.FormatBool(*l.Features.Cluster) 165 props["Metrics"] = strconv.FormatBool(*l.Features.Metrics) 166 props["GoogleOAuth"] = strconv.FormatBool(*l.Features.GoogleOAuth) 167 props["Office365OAuth"] = strconv.FormatBool(*l.Features.Office365OAuth) 168 props["OpenId"] = strconv.FormatBool(*l.Features.OpenId) 169 props["Compliance"] = strconv.FormatBool(*l.Features.Compliance) 170 props["MHPNS"] = strconv.FormatBool(*l.Features.MHPNS) 171 props["Announcement"] = strconv.FormatBool(*l.Features.Announcement) 172 props["Elasticsearch"] = strconv.FormatBool(*l.Features.Elasticsearch) 173 props["DataRetention"] = strconv.FormatBool(*l.Features.DataRetention) 174 props["IDLoadedPushNotifications"] = strconv.FormatBool(*l.Features.IDLoadedPushNotifications) 175 props["IssuedAt"] = strconv.FormatInt(l.IssuedAt, 10) 176 props["StartsAt"] = strconv.FormatInt(l.StartsAt, 10) 177 props["ExpiresAt"] = strconv.FormatInt(l.ExpiresAt, 10) 178 props["Name"] = l.Customer.Name 179 props["Email"] = l.Customer.Email 180 props["Company"] = l.Customer.Company 181 props["EmailNotificationContents"] = strconv.FormatBool(*l.Features.EmailNotificationContents) 182 props["MessageExport"] = strconv.FormatBool(*l.Features.MessageExport) 183 props["CustomPermissionsSchemes"] = strconv.FormatBool(*l.Features.CustomPermissionsSchemes) 184 props["GuestAccounts"] = strconv.FormatBool(*l.Features.GuestAccounts) 185 props["GuestAccountsPermissions"] = strconv.FormatBool(*l.Features.GuestAccountsPermissions) 186 props["CustomTermsOfService"] = strconv.FormatBool(*l.Features.CustomTermsOfService) 187 props["LockTeammateNameDisplay"] = strconv.FormatBool(*l.Features.LockTeammateNameDisplay) 188 props["Cloud"] = strconv.FormatBool(*l.Features.Cloud) 189 props["SharedChannels"] = strconv.FormatBool(*l.Features.SharedChannels) 190 props["RemoteClusterService"] = strconv.FormatBool(*l.Features.RemoteClusterService) 191 props["IsTrial"] = strconv.FormatBool(l.IsTrial) 192 } 193 194 return props 195 } 196 197 func GetSanitizedClientLicense(l map[string]string) map[string]string { 198 sanitizedLicense := make(map[string]string) 199 200 for k, v := range l { 201 sanitizedLicense[k] = v 202 } 203 204 delete(sanitizedLicense, "Id") 205 delete(sanitizedLicense, "Name") 206 delete(sanitizedLicense, "Email") 207 delete(sanitizedLicense, "IssuedAt") 208 delete(sanitizedLicense, "StartsAt") 209 delete(sanitizedLicense, "ExpiresAt") 210 delete(sanitizedLicense, "SkuName") 211 delete(sanitizedLicense, "SkuShortName") 212 213 return sanitizedLicense 214 }