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, newUUID func() uuid.UUID, fn func(screenName string) (state.User, error)) (wire.SNACMessage, error)
  16. FLAPLogin(frame wire.FLAPSignonFrame, newUUIDFn func() uuid.UUID, newUserFn func(screenName string) (state.User, error)) (wire.TLVRestBlock, error)
  17. RetrieveBOSSession(sessionID string) (*state.Session, error)
  18. RetrieveChatSession(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. }
  29. // Start starts the authentication server and listens for new connections.
  30. func (rt AuthServer) Start() {
  31. addr := config.Address("", rt.Config.AuthPort)
  32. listener, err := net.Listen("tcp", addr)
  33. if err != nil {
  34. rt.Logger.Error("unable to bind auth server address", "err", err.Error())
  35. os.Exit(1)
  36. }
  37. defer listener.Close()
  38. rt.Logger.Info("starting auth service", "addr", addr)
  39. for {
  40. conn, err := listener.Accept()
  41. if err != nil {
  42. rt.Logger.Error(err.Error())
  43. continue
  44. }
  45. go func() {
  46. if err := rt.handleNewConnection(conn); err != nil {
  47. rt.Logger.Info("user session failed", "err", err.Error())
  48. }
  49. }()
  50. }
  51. }
  52. func (rt AuthServer) handleNewConnection(rwc io.ReadWriteCloser) error {
  53. defer rwc.Close()
  54. flapc := flapClient{
  55. r: rwc,
  56. sequence: 100,
  57. w: rwc,
  58. }
  59. signonFrame, err := flapc.SignonHandshake()
  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 flapClient) error {
  69. tlv, err := rt.AuthService.FLAPLogin(signonFrame, uuid.New, state.NewStubUser)
  70. if err != nil {
  71. return err
  72. }
  73. return flapc.SendSignoffFrame(tlv)
  74. }
  75. func (rt AuthServer) processBUCPAuth(flapc 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, uuid.New, state.NewStubUser)
  92. if err != nil {
  93. return err
  94. }
  95. return flapc.SendSNAC(outSNAC.Frame, outSNAC.Body)
  96. }