summaryrefslogtreecommitdiffstats
path: root/utils/license.go
diff options
context:
space:
mode:
authorJoramWilander <jwawilander@gmail.com>2016-01-04 12:44:22 -0500
committerJoramWilander <jwawilander@gmail.com>2016-01-14 08:24:09 -0500
commit9110dd54a15f3d0fcf6f60936e01d816b667b93c (patch)
treebe6ddca1c4dc47b71de4c7ad8af59cb21a594b56 /utils/license.go
parent53b0cd8f2a24798c67505aa447b1d53b9f14197e (diff)
downloadchat-9110dd54a15f3d0fcf6f60936e01d816b667b93c.tar.gz
chat-9110dd54a15f3d0fcf6f60936e01d816b667b93c.tar.bz2
chat-9110dd54a15f3d0fcf6f60936e01d816b667b93c.zip
Added license validation and settings
Diffstat (limited to 'utils/license.go')
-rw-r--r--utils/license.go152
1 files changed, 152 insertions, 0 deletions
diff --git a/utils/license.go b/utils/license.go
new file mode 100644
index 000000000..1f8e24f32
--- /dev/null
+++ b/utils/license.go
@@ -0,0 +1,152 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package utils
+
+import (
+ "bytes"
+ "crypto"
+ "crypto/rsa"
+ "crypto/sha256"
+ "crypto/x509"
+ "encoding/base64"
+ "encoding/pem"
+ "io"
+ "os"
+ "strconv"
+ "strings"
+
+ l4g "code.google.com/p/log4go"
+
+ "github.com/mattermost/platform/model"
+)
+
+const (
+ LICENSE_FILE_LOC = "./data/active.dat"
+)
+
+var IsLicensed bool = false
+var License *model.License = &model.License{}
+var ClientLicense map[string]string = make(map[string]string)
+
+// test public key
+var publicKey []byte = []byte(`-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3/k3Al9q1Xe+xngQ/yGn
+0suaJopea3Cpf6NjIHdO8sYTwLlxqt0Mdb+qBR9LbCjZfcNmqc5mZONvsyCEoN/5
+VoLdlv1m9ao2BSAWphUxE2CPdUWdLOsDbQWliSc5//UhiYeR+67Xxon0Hg0LKXF6
+PumRIWQenRHJWqlUQZ147e7/1v9ySVRZksKpvlmMDzgq+kCH/uyM1uVP3z7YXhlN
+K7vSSQYbt4cghvWQxDZFwpLlsChoY+mmzClgq+Yv6FLhj4/lk94twdOZau/AeZFJ
+NxpC+5KFhU+xSeeklNqwCgnlOyZ7qSTxmdJHb+60SwuYnnGIYzLJhY4LYDr4J+KR
+1wIDAQAB
+-----END PUBLIC KEY-----`)
+
+func LoadLicense() {
+ file, err := os.Open(LICENSE_FILE_LOC)
+ if err != nil {
+ l4g.Warn("Unable to open/find license file")
+ return
+ }
+ defer file.Close()
+
+ buf := bytes.NewBuffer(nil)
+ io.Copy(buf, file)
+
+ if success, licenseStr := ValidateLicense(buf.Bytes()); success {
+ license := model.LicenseFromJson(strings.NewReader(licenseStr))
+ if !license.IsExpired() && license.IsStarted() && license.StartsAt > License.StartsAt {
+ License = license
+ IsLicensed = true
+ ClientLicense = getClientLicense(license)
+ return
+ }
+ }
+
+ l4g.Warn("No valid enterprise license found")
+}
+
+func SetLicense(license *model.License) bool {
+ if !license.IsExpired() && license.IsStarted() {
+ License = license
+ IsLicensed = true
+ ClientLicense = getClientLicense(license)
+ return true
+ }
+
+ return false
+}
+
+func RemoveLicense() {
+ License = &model.License{}
+ IsLicensed = false
+ ClientLicense = getClientLicense(License)
+
+ if err := os.Remove(LICENSE_FILE_LOC); err != nil {
+ l4g.Error("Unable to remove license file, err=%v", err.Error())
+ }
+}
+
+func ValidateLicense(signed []byte) (bool, string) {
+ decoded := make([]byte, base64.StdEncoding.DecodedLen(len(signed)))
+
+ _, err := base64.StdEncoding.Decode(decoded, signed)
+ if err != nil {
+ l4g.Error("Encountered error decoding license, err=%v", err.Error())
+ return false, ""
+ }
+
+ if len(decoded) <= 256 {
+ l4g.Error("Signed license not long enough")
+ return false, ""
+ }
+
+ // remove null terminator
+ if decoded[len(decoded)-1] == byte(0) {
+ decoded = decoded[:len(decoded)-1]
+ }
+
+ plaintext := decoded[:len(decoded)-256]
+ signature := decoded[len(decoded)-256:]
+
+ block, _ := pem.Decode(publicKey)
+
+ public, err := x509.ParsePKIXPublicKey(block.Bytes)
+ if err != nil {
+ l4g.Error("Encountered error signing license, err=%v", err.Error())
+ return false, ""
+ }
+
+ rsaPublic := public.(*rsa.PublicKey)
+
+ h := sha256.New()
+ h.Write(plaintext)
+ d := h.Sum(nil)
+
+ err = rsa.VerifyPKCS1v15(rsaPublic, crypto.SHA256, d, signature)
+ if err != nil {
+ l4g.Error("Invalid signature, err=%v", err.Error())
+ return false, ""
+ }
+
+ return true, string(plaintext)
+}
+
+func getClientLicense(l *model.License) map[string]string {
+ props := make(map[string]string)
+
+ props["IsLicensed"] = strconv.FormatBool(IsLicensed)
+
+ if IsLicensed {
+ props["Users"] = strconv.Itoa(l.Features.Users)
+ props["LDAP"] = strconv.FormatBool(l.Features.LDAP)
+ props["GoogleSSO"] = strconv.FormatBool(l.Features.GoogleSSO)
+ props["IssuedAt"] = strconv.FormatInt(l.IssuedAt, 10)
+ props["StartsAt"] = strconv.FormatInt(l.StartsAt, 10)
+ props["ExpiresAt"] = strconv.FormatInt(l.ExpiresAt, 10)
+ props["Name"] = l.Customer.Name
+ props["Email"] = l.Customer.Email
+ props["Company"] = l.Customer.Company
+ props["PhoneNumber"] = l.Customer.PhoneNumber
+ }
+
+ return props
+}