| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- package auth
- import (
- "crypto/rand"
- "crypto/sha256"
- "encoding/base64"
- "encoding/hex"
- "encoding/json"
- "fmt"
- "goseg/config"
- "goseg/structs"
- "net"
- "net/http"
- "strings"
- "sync"
- "time"
- "github.com/gorilla/websocket"
- "golang.org/x/crypto/nacl/secretbox"
- )
- var (
- // maps a websocket conn to a tokenid
- // tokenid's can be referenced from the global conf
- AuthenticatedClients = struct {
- Conns map[*websocket.Conn]string
- sync.RWMutex
- }{
- Conns: make(map[*websocket.Conn]string),
- }
- UnauthClients = struct {
- Conns map[*websocket.Conn]string
- sync.RWMutex
- }{
- Conns: make(map[*websocket.Conn]string),
- }
- )
- // check if websocket session is in the auth map
- func WsIsAuthenticated(conn *websocket.Conn) bool {
- AuthenticatedClients.Lock()
- defer AuthenticatedClients.Unlock()
- _, exists := AuthenticatedClients.Conns[conn]
- return exists
- }
- // // CheckToken checks the validity of the token.
- // func CheckToken(token string, conn *websocket.Conn, setup bool) (bool, string, error) {
- // if token == "" {
- // authStatus := false
- // if setup {
- // authStatus = true
- // }
- // newToken, err := CreateToken(conn, setup)
- // if err != nil {
- // return false, "", err
- // }
- // return authStatus, newToken["token"], nil
- // }
- // return false, "", nil
- // }
- // CreateToken creates a new session token.
- func CreateToken(conn *websocket.Conn, r *http.Request, setup bool) (map[string]string, error) {
- // extract conn info
- var ip string
- if forwarded := r.Header.Get("X-Forwarded-For"); forwarded != "" {
- ip = strings.Split(forwarded, ",")[0]
- } else {
- ip, _, _ = net.SplitHostPort(r.RemoteAddr)
- }
- userAgent := r.Header.Get("User-Agent")
- conf := config.Conf()
- now := time.Now().Format("2006-01-02_15:04:05")
- // generate random strings for id, secret, and padding
- id := config.RandString(32)
- secret := config.RandString(128)
- padding := config.RandString(32)
- contents := map[string]string{
- "id": id,
- "ip": ip,
- "user_agent": userAgent,
- "secret": secret,
- "padding": padding,
- "authorized": fmt.Sprintf("%v", setup),
- "created": now,
- }
- // encrypt the contents
- key := conf.KeyFile
- encryptedText, err := KeyfileEncrypt(contents, key)
- if err != nil {
- return nil, fmt.Errorf("failed to encrypt token: %v", err)
- }
- hashed := sha256.Sum256([]byte(encryptedText))
- hash := hex.EncodeToString(hashed[:])
- // Update sessions in the system's configuration
- AddSession(id, hash, now, setup)
- return map[string]string{
- "id": id,
- "token": encryptedText,
- }, nil
- }
- // take session details and add to SysConfig
- func AddSession(tokenID string, hash string, created string, authorized bool) error {
- session := structs.SessionInfo{
- Hash: hash,
- Created: created,
- }
- if authorized {
- update := map[string]interface{}{
- "Sessions": map[string]interface{}{
- "Authorized": map[string]string{
- "Hash": session.Hash,
- "Created": session.Created,
- },
- },
- }
- if err := config.UpdateConf(update); err != nil {
- return fmt.Errorf("Error adding session: %v", err)
- }
- if err := config.RemoveSession(tokenID, false); err != nil {
- return fmt.Errorf("Error removing session: %v", err)
- }
- } else {
- update := map[string]interface{}{
- "Sessions": map[string]interface{}{
- "Unauthorized": map[string]string{
- "Hash": session.Hash,
- "Created": session.Created,
- },
- },
- }
- if err := config.UpdateConf(update); err != nil {
- return fmt.Errorf("Error adding session: %v", err)
- }
- if err := config.RemoveSession(tokenID, true); err != nil {
- return fmt.Errorf("Error removing session: %v", err)
- }
- }
- return nil
- }
- // encrypt the contents using stored keyfile val
- func KeyfileEncrypt(contents map[string]string, key string) (string, error) {
- contentBytes, err := json.Marshal(contents)
- if err != nil {
- return "", err
- }
- // convert key to bytes
- keyBytes := []byte(key)
- if len(keyBytes) != 32 {
- return "", fmt.Errorf("key must be 32 bytes in length")
- }
- var keyArray [32]byte
- copy(keyArray[:], keyBytes)
- // generate nonce
- var nonce [24]byte
- if _, err := rand.Read(nonce[:]); err != nil {
- return "", err
- }
- // encrypt contents
- encrypted := secretbox.Seal(nonce[:], contentBytes, &nonce, &keyArray)
- return base64.URLEncoding.EncodeToString(encrypted), nil
- }
- // decrypt routine
- func KeyfileDecrypt(encryptedText string, key string) (map[string]string, error) {
- // get bytes
- keyBytes := []byte(key)
- var keyArray [32]byte
- copy(keyArray[:], keyBytes)
- encryptedBytes, err := base64.URLEncoding.DecodeString(encryptedText)
- if err != nil {
- return nil, err
- }
- // get nonce
- var nonce [24]byte
- copy(nonce[:], encryptedBytes[:24])
- // attempt decrypt
- decrypted, ok := secretbox.Open(nil, encryptedBytes[24:], &nonce, &keyArray)
- if !ok {
- return nil, fmt.Errorf("Decryption failed")
- }
- var contents map[string]string
- if err := json.Unmarshal(decrypted, &contents); err != nil {
- return nil, err
- }
- return contents, nil
- }
- // salted sha256
- func Hasher(password string) string {
- conf := config.Conf()
- salt := conf.Salt
- toHash := salt + password
- res := sha256.Sum256([]byte(toHash))
- return hex.EncodeToString(res[:])
- }
- // check if pw matches sysconfig
- func AuthenticateLogin(password string) bool {
- conf := config.Conf()
- if Hasher(password) == conf.PwHash {
- return true
- } else {
- return false
- }
- }
|