Skip to content

Commit

Permalink
Added password generator. Finished and tested files, postgres and jwt…
Browse files Browse the repository at this point in the history
… backends.
  • Loading branch information
Ignacio Gómez committed Nov 30, 2017
1 parent 6d779bc commit f495575
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 25 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
all:
go build -buildmode=c-shared -o go-auth.so
go build pw-gen/pw.go
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# mosquitto-go-auth-plugin
Go auth methods plugin for mosquitto using golang

Build with: go build -buildmode=c-shared -o go-auth.so
Build with: go build -buildmode=c-shared -o go-auth.so

or simply:

make
17 changes: 14 additions & 3 deletions backends/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ func (o Files) readAcls() (int, error) {
//Set currentUser as empty string
currentUser := ""

file, fErr := os.Open(o.PasswordPath)
file, fErr := os.Open(o.AclPath)
defer file.Close()
if fErr != nil {
return linesCount, fmt.Errorf("Files backend error: couldn't open acl file: %s\n", fErr)
Expand All @@ -152,14 +152,16 @@ func (o Files) readAcls() (int, error) {

for scanner.Scan() {
index++
line := scanner.Text()

log.Printf("Read: %s %s\n", index, line)

//Check comment or empty line to skip them.
if checkCommentOrEmpty(scanner.Text()) {
log.Printf("Found empty line at %d\n", index)
continue
}

line := scanner.Text()

//If we see a user line, change the current user.
if strings.Contains(line, "user") {
//Try to get username
Expand Down Expand Up @@ -218,6 +220,8 @@ func (o Files) readAcls() (int, error) {
o.AclRecords = append(o.AclRecords, aclRecord)
}

log.Printf("created aclrecord %v for user %s\n", aclRecord, currentUser)

linesCount++

} else {
Expand Down Expand Up @@ -278,6 +282,9 @@ func checkCommentOrEmpty(line string) bool {

//GetUser checks that user exists and password is correct.
func (o Files) GetUser(username, password string) bool {

log.Printf("checking user %s with pass %s\n", username, password)

fileUser, ok := o.Users[username]
if !ok {
log.Printf("no such user: %s\n", username)
Expand All @@ -302,6 +309,7 @@ func (o Files) GetSuperuser(username string) bool {
//CheckAcl checks that the topic may be read/written by the given user/clientid.
func (o Files) CheckAcl(username, topic, clientid string, acc int32) bool {
//If there are no acls, all access is allowed.
log.Printf("Files acl check with user %s, topic: %s, clientid: %s and acc: %d\n", username, topic, clientid, acc)
if !o.CheckAcls {
return true
}
Expand All @@ -312,6 +320,7 @@ func (o Files) CheckAcl(username, topic, clientid string, acc int32) bool {
if ok {
for _, aclRecord := range fileUser.AclRecords {
if common.TopicsMatch(aclRecord.Topic, topic) && acc <= int32(aclRecord.Acc) {
log.Printf("Files acl check passed.")
return true
}
}
Expand All @@ -321,11 +330,13 @@ func (o Files) CheckAcl(username, topic, clientid string, acc int32) bool {
aclTopic := strings.Replace(aclRecord.Topic, "%c", clientid, -1)
aclTopic = strings.Replace(aclTopic, "%u", username, -1)
if common.TopicsMatch(aclTopic, topic) {
log.Printf("Files acl check passed.")
return true
}
}
}

log.Printf("Files acl check failed.")
return false

}
Expand Down
18 changes: 4 additions & 14 deletions backends/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,6 @@ type Response struct {
Error string `json:"error"`
}

/*var allowedOpts = map[string]bool{
"remote": true,
"secret": true,
"method": true,
"user_uri": true,
"superuser_uri": true,
"acl_uri": true,
"hostname": true,
"port": true,
"ip": true,
"with_tls": true,
"verify_peer": true,
}*/

func NewJWT(authOpts map[string]string) (JWT, error) {

//Initialize with defaults
Expand Down Expand Up @@ -138,6 +124,7 @@ func NewJWT(authOpts map[string]string) (JWT, error) {

func (o JWT) GetUser(token, password string) bool {

log.Printf("jwt getuser for %s\n", token)
if o.Remote {
dataMap := map[string]interface{}{
"password": token,
Expand All @@ -152,6 +139,7 @@ func (o JWT) GetUser(token, password string) bool {

func (o JWT) GetSuperuser(token string) bool {

log.Printf("jwt superuser for %s\n", token)
if o.Remote {
var dataMap map[string]interface{}
return httpRequest(o.Method, o.Ip, o.SuperuserUri, token, o.WithTLS, o.VerifyPeer, dataMap, o.Port)
Expand All @@ -164,6 +152,7 @@ func (o JWT) GetSuperuser(token string) bool {

func (o JWT) CheckAcl(token, topic, clientid string, acc int32) bool {

log.Printf("jwt acl for %s\n", token)
if o.Remote {
dataMap := map[string]interface{}{
"clientid": clientid,
Expand Down Expand Up @@ -246,6 +235,7 @@ func httpRequest(method, host, uri, token string, withTLS, verifyPeer bool, data
return false
}

log.Printf("jwt request approved for %s\n", token)
return true

}
Expand Down
4 changes: 3 additions & 1 deletion backends/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,9 @@ func (o Postgres) CheckAcl(username, topic, clientid string, acc int32) bool {

var acls []string

err := o.DB.Select(&acls, o.SuperuserQuery, username, acc)
log.Printf("PG superuser query: %s\n", o.AclQuery)

err := o.DB.Select(&acls, o.AclQuery, username, acc)

if err != nil {
log.Printf("PG check acl error: %s\n", err)
Expand Down
23 changes: 17 additions & 6 deletions common/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package common
import (
"bytes"
"crypto/rand"
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"fmt"
"log"
"strconv"
"strings"

Expand Down Expand Up @@ -50,23 +53,29 @@ func match(route []string, topic []string) bool {
// NOTE: We store the details of the hashing algorithm with the hash itself,
// making it easy to recreate the hash for password checking, even if we change
// the default criteria here.
func hash(password string, saltSize int, iterations int) (string, error) {
func Hash(password string, saltSize int, iterations int, algorithm string) (string, error) {
// Generate a random salt value, 128 bits.
salt := make([]byte, saltSize)
_, err := rand.Read(salt)
if err != nil {
return "", errors.Wrap(err, "read random bytes error")
}

return hashWithSalt(password, salt, iterations), nil
return hashWithSalt(password, salt, iterations, algorithm), nil
}

func hashWithSalt(password string, salt []byte, iterations int) string {
func hashWithSalt(password string, salt []byte, iterations int, algorithm string) string {
// Generate the hash. This should be a little painful, adjust ITERATIONS
// if it needs performance tweeking. Greatly depends on the hardware.
// NOTE: We store these details with the returned hash, so changes will not
// affect our ability to do password compares.
hash := pbkdf2.Key([]byte(password), salt, iterations, sha512.Size, sha512.New)
shaSize := sha512.Size
shaHash := sha512.New
if algorithm == "sha256" {
shaSize = sha256.Size
shaHash = sha256.New
}
hash := pbkdf2.Key([]byte(password), salt, iterations, shaSize, shaHash)

// Build up the parameters and hash into a single string so we can compare
// other string to the same hash. Note that the hash algorithm is hard-
Expand All @@ -75,7 +84,7 @@ func hashWithSalt(password string, salt []byte, iterations int) string {
var buffer bytes.Buffer

buffer.WriteString("PBKDF2$")
buffer.WriteString("sha512$")
buffer.WriteString(fmt.Sprintf("%s$", algorithm))
buffer.WriteString(strconv.Itoa(iterations))
buffer.WriteString("$")
buffer.WriteString(base64.StdEncoding.EncodeToString(salt))
Expand All @@ -95,6 +104,8 @@ func HashCompare(password string, passwordHash string) bool {
// being compared.cre
iterations, _ := strconv.Atoi(hashSplit[2])
salt, _ := base64.StdEncoding.DecodeString(hashSplit[3])
newHash := hashWithSalt(password, salt, iterations)
algorithm := hashSplit[1]
newHash := hashWithSalt(password, salt, iterations, algorithm)
log.Printf("Comparing:\nnewHash: %s\npwHash: %s\n", newHash, passwordHash)
return newHash == passwordHash
}
3 changes: 3 additions & 0 deletions go-auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ func AuthUnpwdCheck(username, password string) bool {

if backend.GetUser(username, password) {
authenticated = true
log.Printf("user %s authenticated with backend %s\n", username, backend.GetName())
break
}
}
Expand Down Expand Up @@ -181,6 +182,7 @@ func AuthAclCheck(clientid, username, topic string, acc int) bool {

fmt.Printf("Superuser check with backend %s\n", backend.GetName())
if backend.GetSuperuser(username) {
log.Printf("superuser %s acl authenticated with backend %s\n", username, backend.GetName())
aclCheck = true
break
}
Expand All @@ -201,6 +203,7 @@ func AuthAclCheck(clientid, username, topic string, acc int) bool {

fmt.Printf("Acl check with backend %s\n", backend.GetName())
if backend.CheckAcl(username, topic, clientid, int32(acc)) {
log.Printf("user %s acl authenticated with backend %s\n", username, backend.GetName())
aclCheck = true
break
}
Expand Down
Binary file added pw
Binary file not shown.
28 changes: 28 additions & 0 deletions pw-gen/pw.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package main

import (
"flag"
"fmt"

"github.com/iegomez/mosquitto-go-auth-plugin/common"
)

// saltSize defines the salt size
const saltSize = 16

func main() {

var algorithm = flag.String("a", "sha512", "algorithm (sha256 or default: sha512)")
var HashIterations = flag.Int("i", 100000, "hash iterations (default: 100000)")
var password = flag.String("p", "", "password")

flag.Parse()

pwHash, err := common.Hash(*password, saltSize, *HashIterations, *algorithm)
if err != nil {
fmt.Errorf("error: %s\n", err)
} else {
fmt.Println(pwHash)
}

}

0 comments on commit f495575

Please sign in to comment.