auth.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. package auth
  2. import (
  3. "crypto/rand"
  4. "crypto/sha256"
  5. "encoding/base64"
  6. "encoding/hex"
  7. "encoding/json"
  8. "fmt"
  9. "goseg/config"
  10. "goseg/structs"
  11. "net"
  12. "net/http"
  13. "strings"
  14. "sync"
  15. "time"
  16. "github.com/gorilla/websocket"
  17. "golang.org/x/crypto/nacl/secretbox"
  18. )
  19. var (
  20. // maps a websocket conn to a tokenid
  21. // tokenid's can be referenced from the global conf
  22. AuthenticatedClients = struct {
  23. Conns map[*websocket.Conn]string
  24. sync.Mutex
  25. }{
  26. Conns: make(map[*websocket.Conn]string),
  27. }
  28. UnauthClients = struct {
  29. Conns map[*websocket.Conn]string
  30. sync.Mutex
  31. }{
  32. Conns: make(map[*websocket.Conn]string),
  33. }
  34. )
  35. // check if websocket session is in the auth map
  36. func WsIsAuthenticated(conn *websocket.Conn) bool {
  37. AuthenticatedClients.Lock()
  38. _, exists := AuthenticatedClients.Conns[conn]
  39. AuthenticatedClients.Unlock()
  40. return exists
  41. }
  42. // // CheckToken checks the validity of the token.
  43. // func CheckToken(token string, conn *websocket.Conn, setup bool) (bool, string, error) {
  44. // if token == "" {
  45. // authStatus := false
  46. // if setup {
  47. // authStatus = true
  48. // }
  49. // newToken, err := CreateToken(conn, setup)
  50. // if err != nil {
  51. // return false, "", err
  52. // }
  53. // return authStatus, newToken["token"], nil
  54. // }
  55. // return false, "", nil
  56. // }
  57. // CreateToken creates a new session token.
  58. func CreateToken(conn *websocket.Conn, r *http.Request, setup bool) (map[string]string, error) {
  59. // extract conn info
  60. var ip string
  61. if forwarded := r.Header.Get("X-Forwarded-For"); forwarded != "" {
  62. ip = strings.Split(forwarded, ",")[0]
  63. } else {
  64. ip, _, _ = net.SplitHostPort(r.RemoteAddr)
  65. }
  66. userAgent := r.Header.Get("User-Agent")
  67. conf := config.Conf()
  68. now := time.Now().Format("2006-01-02_15:04:05")
  69. // generate random strings for id, secret, and padding
  70. id := config.RandString(32)
  71. secret := config.RandString(128)
  72. padding := config.RandString(32)
  73. contents := map[string]string{
  74. "id": id,
  75. "ip": ip,
  76. "user_agent": userAgent,
  77. "secret": secret,
  78. "padding": padding,
  79. "authorized": fmt.Sprintf("%v", setup),
  80. "created": now,
  81. }
  82. // encrypt the contents
  83. key := conf.KeyFile
  84. encryptedText, err := KeyfileEncrypt(contents, key)
  85. if err != nil {
  86. return nil, fmt.Errorf("failed to encrypt token: %v", err)
  87. }
  88. hashed := sha256.Sum256([]byte(encryptedText))
  89. hash := hex.EncodeToString(hashed[:])
  90. // Update sessions in the system's configuration
  91. AddSession(id, hash, now, setup)
  92. return map[string]string{
  93. "id": id,
  94. "token": encryptedText,
  95. }, nil
  96. }
  97. // take session details and add to SysConfig
  98. func AddSession(tokenID string, hash string, created string, authorized bool) error {
  99. session := structs.SessionInfo{
  100. Hash: hash,
  101. Created: created,
  102. }
  103. if authorized {
  104. update := map[string]interface{}{
  105. "Sessions": map[string]interface{}{
  106. "Authorized": map[string]string{
  107. "Hash": session.Hash,
  108. "Created": session.Created,
  109. },
  110. },
  111. }
  112. if err := config.UpdateConf(update); err != nil {
  113. return fmt.Errorf("Error adding session: %v", err)
  114. }
  115. } else {
  116. update := map[string]interface{}{
  117. "Sessions": map[string]interface{}{
  118. "Unauthorized": map[string]string{
  119. "Hash": session.Hash,
  120. "Created": session.Created,
  121. },
  122. },
  123. }
  124. if err := config.UpdateConf(update); err != nil {
  125. return fmt.Errorf("Error adding session: %v", err)
  126. }
  127. }
  128. return nil
  129. }
  130. // encrypt the contents using stored keyfile val
  131. func KeyfileEncrypt(contents map[string]string, key string) (string, error) {
  132. contentBytes, err := json.Marshal(contents)
  133. if err != nil {
  134. return "", err
  135. }
  136. // convert key to bytes
  137. keyBytes := []byte(key)
  138. if len(keyBytes) != 32 {
  139. return "", fmt.Errorf("key must be 32 bytes in length")
  140. }
  141. var keyArray [32]byte
  142. copy(keyArray[:], keyBytes)
  143. // generate nonce
  144. var nonce [24]byte
  145. if _, err := rand.Read(nonce[:]); err != nil {
  146. return "", err
  147. }
  148. // encrypt contents
  149. encrypted := secretbox.Seal(nonce[:], contentBytes, &nonce, &keyArray)
  150. return base64.URLEncoding.EncodeToString(encrypted), nil
  151. }
  152. // decrypt routine
  153. func KeyfileDecrypt(encryptedText string, key string) (map[string]string, error) {
  154. // get bytes
  155. keyBytes := []byte(key)
  156. var keyArray [32]byte
  157. copy(keyArray[:], keyBytes)
  158. encryptedBytes, err := base64.URLEncoding.DecodeString(encryptedText)
  159. if err != nil {
  160. return nil, err
  161. }
  162. // get nonce
  163. var nonce [24]byte
  164. copy(nonce[:], encryptedBytes[:24])
  165. // attempt decrypt
  166. decrypted, ok := secretbox.Open(nil, encryptedBytes[24:], &nonce, &keyArray)
  167. if !ok {
  168. return nil, fmt.Errorf("Decryption failed")
  169. }
  170. var contents map[string]string
  171. if err := json.Unmarshal(decrypted, &contents); err != nil {
  172. return nil, err
  173. }
  174. return contents, nil
  175. }
  176. // salted sha256
  177. func Hasher(password string) string {
  178. conf := config.Conf()
  179. salt := conf.Salt
  180. toHash := salt + password
  181. res := sha256.Sum256([]byte(toHash))
  182. return hex.EncodeToString(res[:])
  183. }
  184. // check if pw matches sysconfig
  185. func AuthenticateLogin(password string) bool {
  186. conf := config.Conf()
  187. if Hasher(password) == conf.PwHash {
  188. return true
  189. } else {
  190. return false
  191. }
  192. }