auth.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. package handler
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "github.com/google/uuid"
  9. "github.com/mkaminski/goaim/oscar"
  10. "github.com/mkaminski/goaim/server"
  11. "github.com/mkaminski/goaim/state"
  12. )
  13. func NewAuthService(cfg server.Config, sessionManager SessionManager, messageRelayer MessageRelayer, feedbagManager FeedbagManager, userManager UserManager, cchatRegistry *state.ChatRegistry) *AuthService {
  14. return &AuthService{
  15. chatRegistry: cchatRegistry,
  16. config: cfg,
  17. feedbagManager: feedbagManager,
  18. messageRelayer: messageRelayer,
  19. sessionManager: sessionManager,
  20. userManager: userManager,
  21. }
  22. }
  23. type AuthService struct {
  24. chatRegistry *state.ChatRegistry
  25. config server.Config
  26. feedbagManager FeedbagManager
  27. messageRelayer MessageRelayer
  28. sessionManager SessionManager
  29. userManager UserManager
  30. }
  31. func (s AuthService) RetrieveChatSession(ctx context.Context, chatID string, sessID string) (*state.Session, error) {
  32. _, chatSessMgr, err := s.chatRegistry.Retrieve(chatID)
  33. if err != nil {
  34. return nil, err
  35. }
  36. chatSess, found := chatSessMgr.(SessionManager).Retrieve(sessID)
  37. if !found {
  38. return nil, fmt.Errorf("unable to find user for session. chat id: %s, sess id: %s", chatID, sessID)
  39. }
  40. return chatSess, nil
  41. }
  42. func (s AuthService) Signout(ctx context.Context, sess *state.Session) error {
  43. if err := broadcastDeparture(ctx, sess, s.messageRelayer, s.feedbagManager); err != nil {
  44. return err
  45. }
  46. s.sessionManager.Remove(sess)
  47. return nil
  48. }
  49. func (s AuthService) SignoutChat(ctx context.Context, sess *state.Session, chatID string) {
  50. chatRoom, chatSessMgr, err := s.chatRegistry.Retrieve(chatID)
  51. if err != nil {
  52. fmt.Println("error getting chat room to remove")
  53. return
  54. }
  55. alertUserLeft(ctx, sess, chatSessMgr.(ChatMessageRelayer))
  56. chatSessMgr.(SessionManager).Remove(sess)
  57. if chatSessMgr.(SessionManager).Empty() {
  58. s.chatRegistry.RemoveRoom(chatRoom.Cookie)
  59. }
  60. }
  61. func (s AuthService) VerifyLogin(rwc io.ReadWriteCloser) (*state.Session, uint32, error) {
  62. seq := uint32(100)
  63. flap, err := s.SendAndReceiveSignonFrame(rwc, &seq)
  64. if err != nil {
  65. return nil, 0, err
  66. }
  67. var ok bool
  68. ID, ok := flap.GetSlice(oscar.OServiceTLVTagsLoginCookie)
  69. if !ok {
  70. return nil, 0, errors.New("unable to get session id from payload")
  71. }
  72. sess, ok := s.sessionManager.Retrieve(string(ID))
  73. if !ok {
  74. return nil, 0, fmt.Errorf("unable to find session by id %s", ID)
  75. }
  76. return sess, seq, nil
  77. }
  78. func (s AuthService) SendAndReceiveSignonFrame(rw io.ReadWriter, sequence *uint32) (oscar.FLAPSignonFrame, error) {
  79. flapFrameOut := oscar.FLAPFrame{
  80. StartMarker: 42,
  81. FrameType: oscar.FLAPFrameSignon,
  82. Sequence: uint16(*sequence),
  83. PayloadLength: 4, // size of FLAPSignonFrame
  84. }
  85. if err := oscar.Marshal(flapFrameOut, rw); err != nil {
  86. return oscar.FLAPSignonFrame{}, err
  87. }
  88. flapSignonFrameOut := oscar.FLAPSignonFrame{
  89. FLAPVersion: 1,
  90. }
  91. if err := oscar.Marshal(flapSignonFrameOut, rw); err != nil {
  92. return oscar.FLAPSignonFrame{}, err
  93. }
  94. // receive
  95. flapFrameIn := oscar.FLAPFrame{}
  96. if err := oscar.Unmarshal(&flapFrameIn, rw); err != nil {
  97. return oscar.FLAPSignonFrame{}, err
  98. }
  99. b := make([]byte, flapFrameIn.PayloadLength)
  100. if _, err := rw.Read(b); err != nil {
  101. return oscar.FLAPSignonFrame{}, err
  102. }
  103. flapSignonFrameIn := oscar.FLAPSignonFrame{}
  104. if err := oscar.Unmarshal(&flapSignonFrameIn, bytes.NewBuffer(b)); err != nil {
  105. return oscar.FLAPSignonFrame{}, err
  106. }
  107. *sequence++
  108. return flapSignonFrameIn, nil
  109. }
  110. func (s AuthService) VerifyChatLogin(rw io.ReadWriter) (*server.ChatCookie, uint32, error) {
  111. seq := uint32(100)
  112. flap, err := s.SendAndReceiveSignonFrame(rw, &seq)
  113. if err != nil {
  114. return nil, 0, err
  115. }
  116. var ok bool
  117. buf, ok := flap.GetSlice(oscar.OServiceTLVTagsLoginCookie)
  118. if !ok {
  119. return nil, 0, errors.New("unable to get session id from payload")
  120. }
  121. cookie := server.ChatCookie{}
  122. err = oscar.Unmarshal(&cookie, bytes.NewBuffer(buf))
  123. return &cookie, seq, err
  124. }
  125. func (s AuthService) ReceiveAndSendAuthChallenge(bodyIn oscar.SNAC_0x17_0x06_BUCPChallengeRequest, newUUID func() uuid.UUID) (oscar.SNACMessage, error) {
  126. screenName, exists := bodyIn.GetString(oscar.TLVScreenName)
  127. if !exists {
  128. return oscar.SNACMessage{}, errors.New("screen name doesn't exist in tlv")
  129. }
  130. var authKey string
  131. u, err := s.userManager.GetUser(screenName)
  132. switch {
  133. case err != nil:
  134. return oscar.SNACMessage{}, err
  135. case u != nil:
  136. // user lookup succeeded
  137. authKey = u.AuthKey
  138. case s.config.DisableAuth:
  139. // can't find user, generate stub auth key
  140. authKey = newUUID().String()
  141. default:
  142. // can't find user, return login error
  143. snacFrameOut := oscar.SNACFrame{
  144. FoodGroup: oscar.BUCP,
  145. SubGroup: oscar.BUCPLoginResponse,
  146. }
  147. snacPayloadOut := oscar.SNAC_0x17_0x03_BUCPLoginResponse{}
  148. snacPayloadOut.AddTLV(oscar.NewTLV(oscar.TLVErrorSubcode, uint16(0x01)))
  149. return oscar.SNACMessage{
  150. Frame: snacFrameOut,
  151. Body: snacPayloadOut,
  152. }, nil
  153. }
  154. return oscar.SNACMessage{
  155. Frame: oscar.SNACFrame{
  156. FoodGroup: oscar.BUCP,
  157. SubGroup: oscar.BUCPChallengeResponse,
  158. },
  159. Body: oscar.SNAC_0x17_0x07_BUCPChallengeResponse{
  160. AuthKey: authKey,
  161. },
  162. }, nil
  163. }
  164. func (s AuthService) ReceiveAndSendBUCPLoginRequest(bodyIn oscar.SNAC_0x17_0x02_BUCPLoginRequest, newUUID func() uuid.UUID) (oscar.SNACMessage, error) {
  165. screenName, found := bodyIn.GetString(oscar.TLVScreenName)
  166. if !found {
  167. return oscar.SNACMessage{}, errors.New("screen name doesn't exist in tlv")
  168. }
  169. md5Hash, found := bodyIn.GetSlice(oscar.TLVPasswordHash)
  170. if !found {
  171. return oscar.SNACMessage{}, errors.New("password hash doesn't exist in tlv")
  172. }
  173. loginOK := false
  174. u, err := s.userManager.GetUser(screenName)
  175. switch {
  176. case err != nil:
  177. return oscar.SNACMessage{}, err
  178. case u != nil && bytes.Equal(u.PassHash, md5Hash):
  179. // password check succeeded
  180. loginOK = true
  181. case s.config.DisableAuth:
  182. // login failed but let them in anyway
  183. newUser, err := newStubUser(screenName)
  184. if err != nil {
  185. return oscar.SNACMessage{}, err
  186. }
  187. if err := s.userManager.UpsertUser(newUser); err != nil {
  188. return oscar.SNACMessage{}, err
  189. }
  190. loginOK = true
  191. }
  192. snacPayloadOut := oscar.SNAC_0x17_0x03_BUCPLoginResponse{}
  193. snacPayloadOut.AddTLV(oscar.NewTLV(oscar.TLVScreenName, screenName))
  194. if loginOK {
  195. sess := s.sessionManager.NewSessionWithSN(newUUID().String(), screenName)
  196. snacPayloadOut.AddTLVList([]oscar.TLV{
  197. oscar.NewTLV(oscar.TLVReconnectHere, server.Address(s.config.OSCARHost, s.config.BOSPort)),
  198. oscar.NewTLV(oscar.TLVAuthorizationCookie, sess.ID()),
  199. })
  200. } else {
  201. snacPayloadOut.AddTLVList([]oscar.TLV{
  202. oscar.NewTLV(oscar.TLVErrorSubcode, uint16(0x01)),
  203. })
  204. }
  205. return oscar.SNACMessage{
  206. Frame: oscar.SNACFrame{
  207. FoodGroup: oscar.BUCP,
  208. SubGroup: oscar.BUCPLoginResponse,
  209. },
  210. Body: snacPayloadOut,
  211. }, nil
  212. }
  213. func newStubUser(screenName string) (state.User, error) {
  214. u := state.User{ScreenName: screenName}
  215. uid, err := uuid.NewRandom()
  216. if err != nil {
  217. return u, err
  218. }
  219. u.AuthKey = uid.String()
  220. if err := u.HashPassword("welcome1"); err != nil {
  221. return u, err
  222. }
  223. return u, u.HashPassword("welcome1")
  224. }