auth.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. package oscar
  2. import (
  3. "context"
  4. "io"
  5. "log/slog"
  6. "net"
  7. "os"
  8. "github.com/mk6i/retro-aim-server/config"
  9. "github.com/mk6i/retro-aim-server/state"
  10. "github.com/mk6i/retro-aim-server/wire"
  11. "github.com/google/uuid"
  12. )
  13. type AuthService interface {
  14. BUCPChallenge(bodyIn wire.SNAC_0x17_0x06_BUCPChallengeRequest, newUUID func() uuid.UUID) (wire.SNACMessage, error)
  15. BUCPLogin(bodyIn wire.SNAC_0x17_0x02_BUCPLoginRequest, fn func(screenName string) (state.User, error)) (wire.SNACMessage, error)
  16. FLAPLogin(frame wire.FLAPSignonFrame, newUserFn func(screenName string) (state.User, error)) (wire.TLVRestBlock, error)
  17. RegisterBOSSession(sessionID string) (*state.Session, error)
  18. RegisterChatSession(loginCookie []byte) (*state.Session, error)
  19. Signout(ctx context.Context, sess *state.Session) error
  20. SignoutChat(ctx context.Context, sess *state.Session) error
  21. }
  22. // AuthServer is an authentication server for both FLAP (AIM v1.0-3.0) and BUCP
  23. // (AIM v3.5-5.9) authentication flows.
  24. type AuthServer struct {
  25. AuthService
  26. config.Config
  27. Logger *slog.Logger
  28. CookieCracker
  29. }
  30. // Start starts the authentication server and listens for new connections.
  31. func (rt AuthServer) Start() {
  32. addr := net.JoinHostPort("", rt.Config.AuthPort)
  33. listener, err := net.Listen("tcp", addr)
  34. if err != nil {
  35. rt.Logger.Error("unable to bind server address", "host", addr, "err", err.Error())
  36. os.Exit(1)
  37. }
  38. defer listener.Close()
  39. rt.Logger.Info("starting server", "listen_host", addr, "oscar_host", rt.Config.OSCARHost)
  40. for {
  41. conn, err := listener.Accept()
  42. if err != nil {
  43. rt.Logger.Error(err.Error())
  44. continue
  45. }
  46. go func() {
  47. if err := rt.handleNewConnection(conn); err != nil {
  48. rt.Logger.Info("user session failed", "err", err.Error())
  49. }
  50. }()
  51. }
  52. }
  53. func (rt AuthServer) handleNewConnection(rwc io.ReadWriteCloser) error {
  54. defer rwc.Close()
  55. flapc := wire.NewFlapClient(100, rwc, rwc)
  56. if err := flapc.SendSignonFrame(nil); err != nil {
  57. return err
  58. }
  59. signonFrame, err := flapc.ReceiveSignonFrame()
  60. if err != nil {
  61. return err
  62. }
  63. if _, hasRoastedPassword := signonFrame.Uint16(wire.LoginTLVTagsRoastedPassword); hasRoastedPassword {
  64. return rt.processFLAPAuth(signonFrame, flapc)
  65. }
  66. return rt.processBUCPAuth(flapc, err)
  67. }
  68. func (rt AuthServer) processFLAPAuth(signonFrame wire.FLAPSignonFrame, flapc *wire.FlapClient) error {
  69. tlv, err := rt.AuthService.FLAPLogin(signonFrame, state.NewStubUser)
  70. if err != nil {
  71. return err
  72. }
  73. return flapc.SendSignoffFrame(tlv)
  74. }
  75. func (rt AuthServer) processBUCPAuth(flapc *wire.FlapClient, err error) error {
  76. challengeRequest := wire.SNAC_0x17_0x06_BUCPChallengeRequest{}
  77. if err := flapc.ReceiveSNAC(&wire.SNACFrame{}, &challengeRequest); err != nil {
  78. return err
  79. }
  80. outSNAC, err := rt.BUCPChallenge(challengeRequest, uuid.New)
  81. if err != nil {
  82. return err
  83. }
  84. if err := flapc.SendSNAC(outSNAC.Frame, outSNAC.Body); err != nil {
  85. return err
  86. }
  87. loginRequest := wire.SNAC_0x17_0x02_BUCPLoginRequest{}
  88. if err := flapc.ReceiveSNAC(&wire.SNACFrame{}, &loginRequest); err != nil {
  89. return err
  90. }
  91. outSNAC, err = rt.BUCPLogin(loginRequest, state.NewStubUser)
  92. if err != nil {
  93. return err
  94. }
  95. return flapc.SendSNAC(outSNAC.Frame, outSNAC.Body)
  96. }