mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-03-21 17:44:58 -05:00
database: Support whitelisting certain profiles without keys
This commit is contained in:
parent
93269e8b8d
commit
65f0eb460e
|
|
@ -29,7 +29,7 @@ const (
|
|||
SELECT has_ban, ban_tos, ng_device_id, ban_reason
|
||||
FROM users
|
||||
WHERE has_ban = true
|
||||
AND (profile_id = $2
|
||||
AND ((profile_id = $2 OR allow_default_keys = FALSE)
|
||||
OR ng_device_id && (SELECT * FROM known_ng_device_ids)
|
||||
OR last_ip_address = $3
|
||||
OR ($4 != '' AND last_ip_address = $4))
|
||||
|
|
@ -38,11 +38,12 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
ErrDeviceIDMismatch = errors.New("NG device ID mismatch")
|
||||
ErrProfileBannedTOS = errors.New("profile is banned for violating the Terms of Service")
|
||||
ErrDeviceIDMismatch = errors.New("NG device ID mismatch")
|
||||
ErrProhibitedDeviceID = errors.New("used prohibited NG device ID in request")
|
||||
ErrProfileBannedTOS = errors.New("profile is banned for violating the Terms of Service")
|
||||
)
|
||||
|
||||
func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsbrcd string, profileId uint32, ngDeviceId uint32, ipAddress string, ingamesn string, deviceAuth bool) (User, error) {
|
||||
func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsbrcd string, profileId uint32, defaultKey bool, ngDeviceId uint32, ipAddress string, ingamesn string, deviceAuth bool) (User, error) {
|
||||
var exists bool
|
||||
err := pool.QueryRow(ctx, DoesUserExist, userId, gsbrcd).Scan(&exists)
|
||||
if err != nil {
|
||||
|
|
@ -76,12 +77,17 @@ func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsb
|
|||
} else {
|
||||
var firstName *string
|
||||
var lastName *string
|
||||
var allowDefaultKeys bool
|
||||
|
||||
err := pool.QueryRow(ctx, GetUserProfileID, userId, gsbrcd).Scan(&user.ProfileId, &user.NgDeviceId, &user.Email, &user.UniqueNick, &firstName, &lastName, &user.OpenHost, &lastIPAddress)
|
||||
err := pool.QueryRow(ctx, GetUserProfileID, userId, gsbrcd).Scan(&user.ProfileId, &user.NgDeviceId, &user.Email, &user.UniqueNick, &firstName, &lastName, &user.OpenHost, &lastIPAddress, &allowDefaultKeys)
|
||||
if err != nil {
|
||||
return User{}, err
|
||||
}
|
||||
|
||||
if defaultKey && !allowDefaultKeys && !common.GetConfig().AllowDefaultDolphinKeys {
|
||||
return User{}, ErrProhibitedDeviceID
|
||||
}
|
||||
|
||||
if firstName != nil {
|
||||
user.FirstName = *firstName
|
||||
}
|
||||
|
|
@ -95,15 +101,13 @@ func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsb
|
|||
for index, id := range user.NgDeviceId {
|
||||
if id == ngDeviceId {
|
||||
validDeviceId = true
|
||||
break
|
||||
}
|
||||
|
||||
if id == 0 {
|
||||
if !validDeviceId && id == 0 {
|
||||
// Replace the 0 with the actual device ID
|
||||
user.NgDeviceId[index] = ngDeviceId
|
||||
_, err = pool.Exec(ctx, UpdateUserNGDeviceID, user.ProfileId, user.NgDeviceId)
|
||||
validDeviceId = true
|
||||
break
|
||||
}
|
||||
|
||||
deviceIdList += aurora.Cyan(fmt.Sprintf("%08x", id)).String() + ", "
|
||||
|
|
@ -171,7 +175,6 @@ func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, userId uint64, gsb
|
|||
var banTOS bool
|
||||
var bannedDeviceIdList []uint32
|
||||
var banReason string
|
||||
|
||||
timeNow := time.Now().UTC()
|
||||
err = pool.QueryRow(ctx, SearchUserBan, user.NgDeviceId, user.ProfileId, ipAddress, *lastIPAddress, timeNow).Scan(&banExists, &banTOS, &bannedDeviceIdList, &banReason)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ func UpdateTables(pool *pgxpool.Pool, ctx context.Context) {
|
|||
ADD IF NOT EXISTS ban_reason_hidden character varying,
|
||||
ADD IF NOT EXISTS ban_moderator character varying,
|
||||
ADD IF NOT EXISTS ban_tos boolean,
|
||||
ADD IF NOT EXISTS open_host boolean DEFAULT false;
|
||||
|
||||
ADD IF NOT EXISTS open_host boolean DEFAULT false,
|
||||
ADD IF NOT EXISTS allow_default_keys boolean DEFAULT false;
|
||||
`)
|
||||
|
||||
pool.Exec(ctx, `
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const (
|
|||
DoesUserExist = `SELECT EXISTS(SELECT 1 FROM users WHERE user_id = $1 AND gsbrcd = $2)`
|
||||
IsProfileIDInUse = `SELECT EXISTS(SELECT 1 FROM users WHERE profile_id = $1)`
|
||||
DeleteUserSession = `DELETE FROM sessions WHERE profile_id = $1`
|
||||
GetUserProfileID = `SELECT profile_id, ng_device_id, email, unique_nick, firstname, lastname, open_host, last_ip_address FROM users WHERE user_id = $1 AND gsbrcd = $2`
|
||||
GetUserProfileID = `SELECT profile_id, ng_device_id, email, unique_nick, firstname, lastname, open_host, last_ip_address, allow_default_keys FROM users WHERE user_id = $1 AND gsbrcd = $2`
|
||||
UpdateUserLastIPAddress = `UPDATE users SET last_ip_address = $2, last_ingamesn = $3 WHERE profile_id = $1`
|
||||
UpdateUserBan = `UPDATE users SET has_ban = true, ban_issued = $2, ban_expires = $3, ban_reason = $4, ban_reason_hidden = $5, ban_moderator = $6, ban_tos = $7 WHERE profile_id = $1`
|
||||
DisableUserBan = `UPDATE users SET has_ban = false WHERE profile_id = $1`
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ var msPublicKey = []byte{
|
|||
}
|
||||
|
||||
var commonDeviceIds = []uint32{
|
||||
0x02000001, // Internal use (leaked)
|
||||
0x02000001, // Internal use
|
||||
0x0403ac68, // Dolphin default
|
||||
|
||||
// Publicly shared key dumps
|
||||
|
|
@ -69,10 +69,13 @@ var commonDeviceIds = []uint32{
|
|||
0x247dd10b,
|
||||
}
|
||||
|
||||
func verifySignature(moduleName string, authToken string, signature string) uint32 {
|
||||
func verifySignature(moduleName string, authToken string, signature string) (defaultKey bool, result uint32) {
|
||||
result = 0
|
||||
defaultKey = false
|
||||
|
||||
sigBytes, err := common.Base64DwcEncoding.DecodeString(signature)
|
||||
if err != nil || (len(sigBytes) != 0x144 && len(sigBytes) != 0x148) {
|
||||
return 0
|
||||
return
|
||||
}
|
||||
|
||||
ngId := sigBytes[0x000:0x004]
|
||||
|
|
@ -81,7 +84,12 @@ func verifySignature(moduleName string, authToken string, signature string) uint
|
|||
// Skip authentication signature verification for common device IDs (the caller should handle this)
|
||||
for _, defaultDeviceId := range commonDeviceIds {
|
||||
if binary.BigEndian.Uint32(ngId) == defaultDeviceId {
|
||||
return defaultDeviceId
|
||||
if !allowDefaultDolphinKeys {
|
||||
logging.Warn(moduleName, "Using default NG device ID")
|
||||
}
|
||||
result = defaultDeviceId
|
||||
defaultKey = true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -115,7 +123,7 @@ func verifySignature(moduleName string, authToken string, signature string) uint
|
|||
|
||||
if !verifyECDSA(msPublicKey, msSignature, ngCertBlobHash[:]) {
|
||||
logging.Error(moduleName, "NG cert verify failed")
|
||||
return 0
|
||||
return
|
||||
}
|
||||
logging.Info(moduleName, "NG cert verified")
|
||||
|
||||
|
|
@ -134,18 +142,19 @@ func verifySignature(moduleName string, authToken string, signature string) uint
|
|||
|
||||
if !verifyECDSA(ngPublicKey, ngSignature, apCertBlobHash[:]) {
|
||||
logging.Error(moduleName, "AP cert verify failed")
|
||||
return 0
|
||||
return
|
||||
}
|
||||
logging.Info(moduleName, "AP cert verified")
|
||||
|
||||
authTokenHash := sha1.Sum([]byte(authToken))
|
||||
if !verifyECDSA(apPublicKey, apSignature, authTokenHash[:]) {
|
||||
logging.Error(moduleName, "Auth token signature failed")
|
||||
return 0
|
||||
return
|
||||
}
|
||||
logging.Notice(moduleName, "Auth token signature verified; NG ID:", aurora.Cyan(fmt.Sprintf("%08x", ngId)))
|
||||
|
||||
return binary.BigEndian.Uint32(ngId)
|
||||
result = binary.BigEndian.Uint32(ngId)
|
||||
return
|
||||
}
|
||||
|
||||
func (g *GameSpySession) login(command common.GameSpyCommand) {
|
||||
|
|
@ -206,6 +215,7 @@ func (g *GameSpySession) login(command common.GameSpyCommand) {
|
|||
}
|
||||
|
||||
deviceAuth := false
|
||||
defaultKey := false
|
||||
if g.UnitCode == UnitCodeWii {
|
||||
if isLocalhost && !payloadVerExists && !signatureExists {
|
||||
// Players using the DNS, need patching using a QR2 exploit
|
||||
|
|
@ -222,7 +232,7 @@ func (g *GameSpySession) login(command common.GameSpyCommand) {
|
|||
g.NeedsExploit = true
|
||||
deviceAuth = false
|
||||
} else {
|
||||
deviceId = g.verifyExLoginInfo(command, authToken)
|
||||
defaultKey, deviceId = g.verifyExLoginInfo(command, authToken)
|
||||
if deviceId == 0 {
|
||||
return
|
||||
}
|
||||
|
|
@ -261,7 +271,7 @@ func (g *GameSpySession) login(command common.GameSpyCommand) {
|
|||
cmdProfileId = uint32(cmdProfileId2)
|
||||
}
|
||||
|
||||
if !g.performLoginWithDatabase(userId, gsbrcd, cmdProfileId, deviceId, deviceAuth) {
|
||||
if !g.performLoginWithDatabase(userId, gsbrcd, cmdProfileId, defaultKey, deviceId, deviceAuth) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -348,12 +358,12 @@ func (g *GameSpySession) exLogin(command common.GameSpyCommand) {
|
|||
return
|
||||
}
|
||||
|
||||
deviceId := g.verifyExLoginInfo(command, g.AuthToken)
|
||||
defaultKey, deviceId := g.verifyExLoginInfo(command, g.AuthToken)
|
||||
if deviceId == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if !g.performLoginWithDatabase(g.User.UserId, g.User.GsbrCode, 0, deviceId, true) {
|
||||
if !g.performLoginWithDatabase(g.User.UserId, g.User.GsbrCode, 0, defaultKey, deviceId, true) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -361,10 +371,11 @@ func (g *GameSpySession) exLogin(command common.GameSpyCommand) {
|
|||
qr2.SetDeviceAuthenticated(g.User.ProfileId)
|
||||
}
|
||||
|
||||
func (g *GameSpySession) verifyExLoginInfo(command common.GameSpyCommand, authToken string) uint32 {
|
||||
func (g *GameSpySession) verifyExLoginInfo(command common.GameSpyCommand, authToken string) (defaultKey bool, deviceId uint32) {
|
||||
payloadVer, payloadVerExists := command.OtherValues["wl:ver"]
|
||||
signature, signatureExists := command.OtherValues["wl:sig"]
|
||||
deviceId := uint32(0)
|
||||
defaultKey = false
|
||||
deviceId = 0
|
||||
|
||||
if !payloadVerExists || payloadVer != "5" {
|
||||
g.replyError(GPError{
|
||||
|
|
@ -373,7 +384,7 @@ func (g *GameSpySession) verifyExLoginInfo(command common.GameSpyCommand, authTo
|
|||
Fatal: true,
|
||||
WWFCMessage: WWFCMsgPayloadInvalid,
|
||||
})
|
||||
return 0
|
||||
return
|
||||
}
|
||||
|
||||
if !signatureExists {
|
||||
|
|
@ -383,59 +394,32 @@ func (g *GameSpySession) verifyExLoginInfo(command common.GameSpyCommand, authTo
|
|||
Fatal: true,
|
||||
WWFCMessage: WWFCMsgUnknownLoginError,
|
||||
})
|
||||
return 0
|
||||
return
|
||||
}
|
||||
|
||||
if deviceId = verifySignature(g.ModuleName, authToken, signature); deviceId == 0 {
|
||||
defaultKey, deviceId = verifySignature(g.ModuleName, authToken, signature)
|
||||
if deviceId == 0 {
|
||||
g.replyError(GPError{
|
||||
ErrorCode: ErrLogin.ErrorCode,
|
||||
ErrorString: "The authentication signature is invalid.",
|
||||
Fatal: true,
|
||||
WWFCMessage: WWFCMsgUnknownLoginError,
|
||||
})
|
||||
return 0
|
||||
return
|
||||
}
|
||||
|
||||
g.DeviceId = deviceId
|
||||
|
||||
if !allowDefaultDolphinKeys {
|
||||
// Check common device IDs
|
||||
for _, defaultDeviceId := range commonDeviceIds {
|
||||
if deviceId != defaultDeviceId {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(g.HostPlatform, "Dolphin") {
|
||||
g.replyError(GPError{
|
||||
ErrorCode: ErrLogin.ErrorCode,
|
||||
ErrorString: "Prohibited device ID used in signature.",
|
||||
Fatal: true,
|
||||
WWFCMessage: WWFCMsgDolphinSetupRequired,
|
||||
})
|
||||
} else {
|
||||
g.replyError(GPError{
|
||||
ErrorCode: ErrLogin.ErrorCode,
|
||||
ErrorString: "Prohibited device ID used in signature.",
|
||||
Fatal: true,
|
||||
WWFCMessage: WWFCMsgUnknownLoginError,
|
||||
})
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
return deviceId
|
||||
return
|
||||
}
|
||||
|
||||
func (g *GameSpySession) performLoginWithDatabase(userId uint64, gsbrCode string, profileId uint32, deviceId uint32, deviceAuth bool) bool {
|
||||
func (g *GameSpySession) performLoginWithDatabase(userId uint64, gsbrCode string, profileId uint32, defaultKey bool, deviceId uint32, deviceAuth bool) bool {
|
||||
// Get IP address without port
|
||||
ipAddress := g.RemoteAddr
|
||||
if strings.Contains(ipAddress, ":") {
|
||||
ipAddress = ipAddress[:strings.Index(ipAddress, ":")]
|
||||
}
|
||||
|
||||
user, err := database.LoginUserToGPCM(pool, ctx, userId, gsbrCode, profileId, deviceId, ipAddress, g.InGameName, deviceAuth)
|
||||
user, err := database.LoginUserToGPCM(pool, ctx, userId, gsbrCode, profileId, defaultKey, deviceId, ipAddress, g.InGameName, deviceAuth)
|
||||
g.User = user
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -471,6 +455,22 @@ func (g *GameSpySession) performLoginWithDatabase(userId uint64, gsbrCode string
|
|||
WWFCMessage: WWFCMsgConsoleMismatch,
|
||||
})
|
||||
}
|
||||
} else if err == database.ErrProhibitedDeviceID {
|
||||
if strings.HasPrefix(g.HostPlatform, "Dolphin") {
|
||||
g.replyError(GPError{
|
||||
ErrorCode: ErrLogin.ErrorCode,
|
||||
ErrorString: "Prohibited device ID used in signature.",
|
||||
Fatal: true,
|
||||
WWFCMessage: WWFCMsgDolphinSetupRequired,
|
||||
})
|
||||
} else {
|
||||
g.replyError(GPError{
|
||||
ErrorCode: ErrLogin.ErrorCode,
|
||||
ErrorString: "Prohibited device ID used in signature.",
|
||||
Fatal: true,
|
||||
WWFCMessage: WWFCMsgUnknownLoginError,
|
||||
})
|
||||
}
|
||||
} else if err == database.ErrProfileBannedTOS {
|
||||
g.replyError(GPError{
|
||||
ErrorCode: ErrLogin.ErrorCode,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user