You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

resume.go 2.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. // Copyright (c) 2019 Shivaram Lingamneni <slingamn@cs.stanford.edu>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "sync"
  6. "github.com/oragono/oragono/irc/utils"
  7. )
  8. // implements draft/resume-0.3, in particular the issuing, management, and verification
  9. // of resume tokens with two components: a unique ID and a secret key
  10. type resumeTokenPair struct {
  11. client *Client
  12. secret string
  13. }
  14. type ResumeManager struct {
  15. sync.RWMutex // level 2
  16. resumeIDtoCreds map[string]resumeTokenPair
  17. server *Server
  18. }
  19. func (rm *ResumeManager) Initialize(server *Server) {
  20. rm.resumeIDtoCreds = make(map[string]resumeTokenPair)
  21. rm.server = server
  22. }
  23. // GenerateToken generates a resume token for a client. If the client has
  24. // already been assigned one, it returns "".
  25. func (rm *ResumeManager) GenerateToken(client *Client) (token string) {
  26. id := utils.GenerateSecretToken()
  27. secret := utils.GenerateSecretToken()
  28. rm.Lock()
  29. defer rm.Unlock()
  30. if client.ResumeID() != "" {
  31. return
  32. }
  33. client.SetResumeID(id)
  34. rm.resumeIDtoCreds[id] = resumeTokenPair{
  35. client: client,
  36. secret: secret,
  37. }
  38. return id + secret
  39. }
  40. // VerifyToken looks up the client corresponding to a resume token, returning
  41. // nil if there is no such client or the token is invalid.
  42. func (rm *ResumeManager) VerifyToken(token string) (client *Client) {
  43. if len(token) != 2*utils.SecretTokenLength {
  44. return
  45. }
  46. rm.RLock()
  47. defer rm.RUnlock()
  48. id := token[:utils.SecretTokenLength]
  49. pair, ok := rm.resumeIDtoCreds[id]
  50. if ok {
  51. if utils.SecretTokensMatch(pair.secret, token[utils.SecretTokenLength:]) {
  52. // disallow resume of an unregistered client; this prevents the use of
  53. // resume as an auth bypass
  54. if pair.client.Registered() {
  55. return pair.client
  56. }
  57. }
  58. }
  59. return
  60. }
  61. // Delete stops tracking a client's resume token.
  62. func (rm *ResumeManager) Delete(client *Client) {
  63. rm.Lock()
  64. defer rm.Unlock()
  65. currentID := client.ResumeID()
  66. if currentID != "" {
  67. delete(rm.resumeIDtoCreds, currentID)
  68. }
  69. }