Просмотр исходного кода

validate roasted kerberos auth passwords (aim 6.9+)

Mike 11 месяцев назад
Родитель
Сommit
b9e56bdead
5 измененных файлов с 180 добавлено и 29 удалено
  1. 24 13
      foodgroup/auth.go
  2. 128 2
      foodgroup/auth_test.go
  3. 6 0
      state/user.go
  4. 13 14
      wire/snacs.go
  5. 9 0
      wire/user.go

+ 24 - 13
foodgroup/auth.go

@@ -283,7 +283,12 @@ func (s AuthService) KerberosLogin(ctx context.Context, inBody wire.SNAC_0x050C_
 
 	list := wire.TLVList{
 		wire.NewTLVBE(wire.LoginTLVTagsScreenName, inBody.ClientPrincipal),
-		wire.NewTLVBE(wire.LoginTLVTagsPlaintextPassword, info.Password),
+	}
+
+	if info.Version >= 4 {
+		list = append(list, wire.NewTLVBE(wire.LoginTLVTagsRoastedKerberosPassword, info.Password))
+	} else {
+		list = append(list, wire.NewTLVBE(wire.LoginTLVTagsPlaintextPassword, info.Password))
 	}
 
 	result, err := s.login(ctx, list, newUserFn, "") //todo
@@ -352,16 +357,17 @@ func (s AuthService) KerberosLogin(ctx context.Context, inBody wire.SNAC_0x050C_
 
 // loginProperties represents the properties sent by the client at login.
 type loginProperties struct {
-	clientID          string
-	isBUCPAuth        bool
-	isFLAPAuth        bool
-	isFLAPJavaAuth    bool
-	isKerberosAuth    bool
-	isTOCAuth         bool
-	passwordHash      []byte
-	plaintextPassword []byte
-	roastedPass       []byte
-	screenName        state.DisplayScreenName
+	clientID                string
+	isBUCPAuth              bool
+	isFLAPAuth              bool
+	isFLAPJavaAuth          bool
+	isKerberosPlaintextAuth bool
+	isKerberosRoastedAuth   bool
+	isTOCAuth               bool
+	passwordHash            []byte
+	plaintextPassword       []byte
+	roastedPass             []byte
+	screenName              state.DisplayScreenName
 }
 
 // fromTLV creates an instance of loginProperties from a TLV list.
@@ -401,7 +407,10 @@ func (l *loginProperties) fromTLV(list wire.TLVList) error {
 		l.isTOCAuth = true
 	case list.HasTag(wire.LoginTLVTagsPlaintextPassword):
 		l.plaintextPassword, _ = list.Bytes(wire.LoginTLVTagsPlaintextPassword)
-		l.isKerberosAuth = true
+		l.isKerberosPlaintextAuth = true
+	case list.HasTag(wire.LoginTLVTagsRoastedKerberosPassword):
+		l.roastedPass, _ = list.Bytes(wire.LoginTLVTagsRoastedKerberosPassword)
+		l.isKerberosRoastedAuth = true
 	default:
 		l.isFLAPAuth = true
 	}
@@ -457,8 +466,10 @@ func (s AuthService) login(ctx context.Context, tlv wire.TLVList, newUserFn func
 		loginOK = user.ValidateRoastedJavaPass(props.roastedPass)
 	case props.isTOCAuth:
 		loginOK = user.ValidateRoastedTOCPass(props.roastedPass)
-	case props.isKerberosAuth:
+	case props.isKerberosPlaintextAuth:
 		loginOK = user.ValidatePlaintextPass(props.plaintextPassword)
+	case props.isKerberosRoastedAuth:
+		loginOK = user.ValidateRoastedKerberosPass(props.roastedPass)
 	}
 
 	if !loginOK {

+ 128 - 2
foodgroup/auth_test.go

@@ -1117,7 +1117,7 @@ func TestAuthService_KerberosLogin(t *testing.T) {
 				TicketRequestMetadata: wire.TLVBlock{
 					TLVList: wire.TLVList{
 						wire.NewTLVBE(wire.KerberosTLVTicketRequest, wire.KerberosLoginRequestTicket{
-							Password: "the_password",
+							Password: []byte("the_password"),
 						}),
 					},
 				},
@@ -1201,7 +1201,7 @@ func TestAuthService_KerberosLogin(t *testing.T) {
 				TicketRequestMetadata: wire.TLVBlock{
 					TLVList: wire.TLVList{
 						wire.NewTLVBE(wire.KerberosTLVTicketRequest, wire.KerberosLoginRequestTicket{
-							Password: "the_WRONG_password",
+							Password: []byte("the_WRONG_password"),
 						}),
 					},
 				},
@@ -1229,6 +1229,132 @@ func TestAuthService_KerberosLogin(t *testing.T) {
 				},
 			},
 		},
+		{
+			name:           "AIM account exists, correct roasted password, login OK",
+			advertisedHost: "127.0.0.1:5190",
+			timeNow: func() time.Time {
+				return time.Unix(1000, 0)
+			},
+			inputSNAC: wire.SNAC_0x050C_0x0002_KerberosLoginRequest{
+				RequestID:       54321,
+				ClientPrincipal: user.DisplayScreenName.String(),
+				TicketRequestMetadata: wire.TLVBlock{
+					TLVList: wire.TLVList{
+						wire.NewTLVBE(wire.KerberosTLVTicketRequest, wire.KerberosLoginRequestTicket{
+							Version:  4,
+							Password: wire.RoastKerberosPassword([]byte("the_password")),
+						}),
+					},
+				},
+			},
+			mockParams: mockParams{
+				userManagerParams: userManagerParams{
+					getUserParams: getUserParams{
+						{
+							screenName: user.IdentScreenName,
+							result:     &user,
+						},
+					},
+				},
+				cookieBakerParams: cookieBakerParams{
+					cookieIssueParams: cookieIssueParams{
+						{
+							dataIn: func() []byte {
+								loginCookie := state.ServerCookie{
+									ScreenName: user.DisplayScreenName,
+								}
+								buf := &bytes.Buffer{}
+								assert.NoError(t, wire.MarshalBE(loginCookie, buf))
+								return buf.Bytes()
+							}(),
+							cookieOut: []byte("the-cookie"),
+						},
+					},
+				},
+			},
+			expectOutput: wire.SNACMessage{
+				Frame: wire.SNACFrame{
+					FoodGroup: wire.Kerberos,
+					SubGroup:  wire.KerberosLoginSuccessResponse,
+				},
+				Body: wire.SNAC_0x050C_0x0003_KerberosLoginSuccessResponse{
+					RequestID:       54321,
+					Epoch:           1000,
+					ClientPrincipal: user.DisplayScreenName.String(),
+					ClientRealm:     "AOL",
+					Tickets: []wire.KerberosTicket{
+						{
+							PVNO:             0x5,
+							EncTicket:        []uint8{},
+							TicketRealm:      "AOL",
+							ServicePrincipal: "im/boss",
+							ClientRealm:      "AOL",
+							ClientPrincipal:  user.DisplayScreenName.String(),
+							AuthTime:         1000,
+							StartTime:        1000,
+							EndTime:          87400,
+							Unknown4:         0x60000000,
+							Unknown5:         0x40000000,
+							ConnectionMetadata: wire.TLVBlock{
+								TLVList: wire.TLVList{
+									wire.NewTLVBE(wire.KerberosTLVBOSServerInfo, wire.KerberosBOSServerInfo{
+										Unknown: 1,
+										ConnectionInfo: wire.TLVBlock{
+											TLVList: wire.TLVList{
+												wire.NewTLVBE(wire.KerberosTLVHostname, "127.0.0.1:5190"),
+												wire.NewTLVBE(wire.KerberosTLVCookie, []byte("the-cookie")),
+												wire.NewTLVBE(wire.KerberosTLVConnSettings, wire.KerberosConnUseSSL),
+											},
+										},
+									}),
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		{
+			name:           "AIM account exists, incorrect roasted password, login failed",
+			advertisedHost: "127.0.0.1:5190",
+			timeNow: func() time.Time {
+				return time.Unix(1000, 0)
+			},
+			inputSNAC: wire.SNAC_0x050C_0x0002_KerberosLoginRequest{
+				RequestID:       54321,
+				ClientPrincipal: user.DisplayScreenName.String(),
+				TicketRequestMetadata: wire.TLVBlock{
+					TLVList: wire.TLVList{
+						wire.NewTLVBE(wire.KerberosTLVTicketRequest, wire.KerberosLoginRequestTicket{
+							Version:  4,
+							Password: wire.RoastKerberosPassword([]byte("the_WRONG_password")),
+						}),
+					},
+				},
+			},
+			mockParams: mockParams{
+				userManagerParams: userManagerParams{
+					getUserParams: getUserParams{
+						{
+							screenName: user.IdentScreenName,
+							result:     &user,
+						},
+					},
+				},
+			},
+			expectOutput: wire.SNACMessage{
+				Frame: wire.SNACFrame{
+					FoodGroup: wire.Kerberos,
+					SubGroup:  wire.KerberosKerberosLoginErrResponse,
+				},
+				Body: wire.SNAC_0x050C_0x0004_KerberosLoginErrResponse{
+					KerbRequestID: 54321,
+					ScreenName:    user.DisplayScreenName.String(),
+					ErrCode:       wire.KerberosErrAuthFailure,
+					Message:       "Auth failure",
+				},
+			},
+		},
 	}
 
 	for _, tc := range cases {

+ 6 - 0
state/user.go

@@ -430,6 +430,12 @@ func (u *User) ValidatePlaintextPass(plaintextPass []byte) bool {
 	return bytes.Equal(u.WeakMD5Pass, md5Hash)
 }
 
+func (u *User) ValidateRoastedKerberosPass(roastedPass []byte) bool {
+	clearPass := wire.RoastKerberosPassword(roastedPass)
+	md5Hash := wire.WeakMD5PasswordHash(string(clearPass), u.AuthKey) // todo remove string conversion
+	return bytes.Equal(u.WeakMD5Pass, md5Hash)
+}
+
 // HashPassword computes MD5 hashes of the user's password. It computes both
 // weak and strong variants and stores them in the struct.
 func (u *User) HashPassword(passwd string) error {

+ 13 - 14
wire/snacs.go

@@ -85,15 +85,16 @@ const (
 //
 
 const (
-	LoginTLVTagsScreenName          uint16 = 0x01
-	LoginTLVTagsRoastedPassword     uint16 = 0x02
-	LoginTLVTagsClientIdentity      uint16 = 0x03
-	LoginTLVTagsReconnectHere       uint16 = 0x05
-	LoginTLVTagsAuthorizationCookie uint16 = 0x06
-	LoginTLVTagsErrorSubcode        uint16 = 0x08
-	LoginTLVTagsPasswordHash        uint16 = 0x25
-	LoginTLVTagsRoastedTOCPassword  uint16 = 0x1337
-	LoginTLVTagsPlaintextPassword   uint16 = 0x1338
+	LoginTLVTagsScreenName              uint16 = 0x01
+	LoginTLVTagsRoastedPassword         uint16 = 0x02
+	LoginTLVTagsClientIdentity          uint16 = 0x03
+	LoginTLVTagsReconnectHere           uint16 = 0x05
+	LoginTLVTagsAuthorizationCookie     uint16 = 0x06
+	LoginTLVTagsErrorSubcode            uint16 = 0x08
+	LoginTLVTagsPasswordHash            uint16 = 0x25
+	LoginTLVTagsRoastedKerberosPassword uint16 = 0x1335
+	LoginTLVTagsRoastedTOCPassword      uint16 = 0x1337
+	LoginTLVTagsPlaintextPassword       uint16 = 0x1338
 )
 
 const (
@@ -2313,13 +2314,11 @@ type KerberosLoginRequestTicket struct {
 	// Flags contains unknown flags.
 	Flags uint32
 
-	// PwdLen is the length (in bytes) of the following Password string.
-	// The field is kept explicit even though the struct tag also carries
-	// the length-prefix rule.
-	PwdLen uint16
+	// Unknown is an unknown field.
+	Unknown uint16
 
 	// Password holds the user’s password.
-	Password string `oscar:"len_prefix=uint16"`
+	Password []byte `oscar:"len_prefix=uint16"`
 
 	// PasswordMetadata may hold additional metadata about the password.
 	PasswordMetadata TLVBlock

+ 9 - 0
wire/user.go

@@ -38,6 +38,15 @@ func RoastOSCARPassword(roastedPass []byte) []byte {
 	return roastPass(roastedPass, roastTable)
 }
 
+// RoastKerberosPassword roasts a Kerberos client password.
+func RoastKerberosPassword(roastedPass []byte) []byte {
+	var roastTable = []byte{
+		0x76, 0x91, 0xc5, 0xe7, 0xd0, 0xd9, 0x95, 0xdd,
+		0x9e, 0x2F, 0xea, 0xd8, 0x6B, 0x21, 0xc2, 0xbc,
+	}
+	return roastPass(roastedPass, roastTable)
+}
+
 // RoastOSCARJavaPassword roasts a Java OSCAR client password.
 func RoastOSCARJavaPassword(roastedPass []byte) []byte {
 	var roastTable = []byte{