auth.go 6.0 KB

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