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  }