xs/hkexauth.go

122 lines
2.5 KiB
Go

// Authentication routines for the HKExSh
//
// Copyright (c) 2017-2018 Russell Magee
// Licensed under the terms of the MIT license (see LICENSE.mit in this
// distribution)
//
// golang implementation by Russ Magee (rmagee_at_gmail.com)
package hkexsh
import (
"bytes"
"encoding/csv"
"fmt"
"io"
"io/ioutil"
"log"
"os/user"
"runtime"
"strings"
"github.com/jameskeane/bcrypt"
)
func userExistsOnSystem(who string) bool {
_, userErr := user.Lookup(who)
if userErr != nil {
return false
} else {
return true
}
}
func AuthUserByPasswd(username string, auth string, fname string) (valid bool, allowedCmds string) {
b, e := ioutil.ReadFile(fname)
if e != nil {
valid = false
log.Println("ERROR: Cannot read hkexsh.passwd file!")
log.Fatal(e)
}
r := csv.NewReader(bytes.NewReader(b))
r.Comma = ':'
r.Comment = '#'
r.FieldsPerRecord = 3 // username:salt:authCookie [TODO:disallowedCmdList (a,b,...)]
for {
record, err := r.Read()
if err == io.EOF {
// Use dummy entry if user not found
// (prevent user enumeration attack via obvious timing diff;
// ie., not attempting any auth at all)
record = []string{"$nosuchuser$",
"$2a$12$l0coBlRDNEJeQVl6GdEPbU",
"$2a$12$l0coBlRDNEJeQVl6GdEPbUC/xmuOANvqgmrMVum6S4i.EXPgnTXy6"}
username = "$nosuchuser$"
err = nil
}
if err != nil {
log.Fatal(err)
}
if username == record[0] {
tmp, _ := bcrypt.Hash(auth, record[1])
if tmp == record[2] && username != "$nosuchuser$" {
valid = true
}
break
}
}
// Security scrub
for i := range b {
b[i] = 0
}
b = nil
r = nil
runtime.GC()
if !userExistsOnSystem(username) {
valid = false
}
return
}
func AuthUserByToken(username string, connhostname string, auth string) (valid bool) {
auth = strings.TrimSpace(auth)
u, ue := user.Lookup(username)
if ue != nil {
return false
}
b, e := ioutil.ReadFile(fmt.Sprintf("%s/.hkexsh_id", u.HomeDir))
if e != nil {
log.Printf("INFO: Cannot read %s/.hkexsh_id\n", u.HomeDir)
return false
}
r := csv.NewReader(bytes.NewReader(b))
r.Comma = ':'
r.Comment = '#'
r.FieldsPerRecord = 2 // connhost:authtoken
for {
record, err := r.Read()
if err == io.EOF {
return false
}
record[0] = strings.TrimSpace(record[0])
record[1] = strings.TrimSpace(record[1])
//fmt.Println("auth:", auth, "record:",
// strings.Join([]string{record[0], record[1]}, ":"))
if (connhostname == record[0]) &&
(auth == strings.Join([]string{record[0], record[1]}, ":")) {
return true
}
}
if !userExistsOnSystem(username) {
valid = false
}
return
}