Skip to content

Commit fd75619

Browse files
committed
feat(secure): Server-side check for friends and friends-of-friends
1 parent 75d247c commit fd75619

File tree

4 files changed

+128
-2
lines changed

4 files changed

+128
-2
lines changed

globals/get_user_friend_pids.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package globals
2+
3+
import (
4+
"context"
5+
6+
pbfriends "github.com/PretendoNetwork/grpc-go/friends"
7+
"github.com/PretendoNetwork/nex-protocols-go/v2/globals"
8+
"google.golang.org/grpc/metadata"
9+
)
10+
11+
func GetUserFriendPIDs(pid uint32) []uint32 {
12+
ctx := metadata.NewOutgoingContext(context.Background(), GRPCFriendsCommonMetadata)
13+
14+
response, err := GRPCFriendsClient.GetUserFriendPIDs(ctx, &pbfriends.GetUserFriendPIDsRequest{Pid: pid})
15+
if err != nil {
16+
globals.Logger.Error(err.Error())
17+
return make([]uint32, 0)
18+
}
19+
20+
return response.Pids
21+
}

globals/globals.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ package globals
22

33
import (
44
"database/sql"
5-
pb "github.com/PretendoNetwork/grpc-go/account"
5+
pbaccount "github.com/PretendoNetwork/grpc-go/account"
6+
pbfriends "github.com/PretendoNetwork/grpc-go/friends"
67
"github.com/PretendoNetwork/nex-go/v2"
78
"github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
89
"github.com/PretendoNetwork/plogger-go"
@@ -23,5 +24,9 @@ var Postgres *sql.DB
2324
var MatchmakingManager *common_globals.MatchmakingManager
2425

2526
var GRPCAccountClientConnection *grpc.ClientConn
26-
var GRPCAccountClient pb.AccountClient
27+
var GRPCAccountClient pbaccount.AccountClient
2728
var GRPCAccountCommonMetadata metadata.MD
29+
30+
var GRPCFriendsClientConnection *grpc.ClientConn
31+
var GRPCFriendsClient pbfriends.FriendsClient
32+
var GRPCFriendsCommonMetadata metadata.MD

init.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"database/sql"
55
"fmt"
6+
pbfriends "github.com/PretendoNetwork/grpc-go/friends"
67
"os"
78
"strconv"
89
"strings"
@@ -33,6 +34,9 @@ func init() {
3334
accountGRPCHost := os.Getenv("PN_MINECRAFT_ACCOUNT_GRPC_HOST")
3435
accountGRPCPort := os.Getenv("PN_MINECRAFT_ACCOUNT_GRPC_PORT")
3536
accountGRPCAPIKey := os.Getenv("PN_MINECRAFT_ACCOUNT_GRPC_API_KEY")
37+
friendsGRPCHost := os.Getenv("PN_MINECRAFT_FRIENDS_GRPC_HOST")
38+
friendsGRPCPort := os.Getenv("PN_MINECRAFT_FRIENDS_GRPC_PORT")
39+
friendsGRPCAPIKey := os.Getenv("PN_MINECRAFT_FRIENDS_GRPC_API_KEY")
3640

3741
if strings.TrimSpace(kerberosPassword) == "" {
3842
globals.Logger.Warningf("PN_MINECRAFT_KERBEROS_PASSWORD environment variable not set. Using default password: %q", globals.KerberosPassword)
@@ -106,6 +110,39 @@ func init() {
106110
"X-API-Key", accountGRPCAPIKey,
107111
)
108112

113+
if strings.TrimSpace(friendsGRPCHost) == "" {
114+
globals.Logger.Error("PN_MINECRAFT_FRIENDS_GRPC_HOST environment variable not set")
115+
os.Exit(0)
116+
}
117+
118+
if strings.TrimSpace(friendsGRPCPort) == "" {
119+
globals.Logger.Error("PN_MINECRAFT_FRIENDS_GRPC_PORT environment variable not set")
120+
os.Exit(0)
121+
}
122+
123+
if port, err := strconv.Atoi(friendsGRPCPort); err != nil {
124+
globals.Logger.Errorf("PN_MINECRAFT_FRIENDS_GRPC_PORT is not a valid port. Expected 0-65535, got %s", accountGRPCPort)
125+
os.Exit(0)
126+
} else if port < 0 || port > 65535 {
127+
globals.Logger.Errorf("PN_MINECRAFT_FRIENDS_GRPC_PORT is not a valid port. Expected 0-65535, got %s", accountGRPCPort)
128+
os.Exit(0)
129+
}
130+
131+
if strings.TrimSpace(friendsGRPCAPIKey) == "" {
132+
globals.Logger.Warning("Insecure gRPC server detected. PN_MINECRAFT_FRIENDS_GRPC_API_KEY environment variable not set")
133+
}
134+
135+
globals.GRPCFriendsClientConnection, err = grpc.Dial(fmt.Sprintf("%s:%s", friendsGRPCHost, friendsGRPCPort), grpc.WithTransportCredentials(insecure.NewCredentials()))
136+
if err != nil {
137+
globals.Logger.Criticalf("Failed to connect to friends gRPC server: %v", err)
138+
os.Exit(0)
139+
}
140+
141+
globals.GRPCFriendsClient = pbfriends.NewFriendsClient(globals.GRPCFriendsClientConnection)
142+
globals.GRPCFriendsCommonMetadata = metadata.Pairs(
143+
"X-API-Key", friendsGRPCAPIKey,
144+
)
145+
109146
globals.Postgres, err = sql.Open("postgres", os.Getenv("PN_MINECRAFT_POSTGRES_URI"))
110147
if err != nil {
111148
globals.Logger.Critical(err.Error())

nex/register_common_secure_server_protocols.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ import (
44
"github.com/PretendoNetwork/minecraft-wiiu/globals"
55
"github.com/PretendoNetwork/nex-go/v2"
66
"github.com/PretendoNetwork/nex-go/v2/types"
7+
commonglobals "github.com/PretendoNetwork/nex-protocols-common-go/v2/globals"
8+
"github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making/database"
79
commonnattraversal "github.com/PretendoNetwork/nex-protocols-common-go/v2/nat-traversal"
810
commonsecure "github.com/PretendoNetwork/nex-protocols-common-go/v2/secure-connection"
911
nattraversal "github.com/PretendoNetwork/nex-protocols-go/v2/nat-traversal"
1012
secure "github.com/PretendoNetwork/nex-protocols-go/v2/secure-connection"
1113
"os"
14+
"slices"
1215

1316
commonmatchmaking "github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making"
1417
commonmatchmakingext "github.com/PretendoNetwork/nex-protocols-common-go/v2/match-making-ext"
@@ -69,6 +72,63 @@ func stubBrowseMatchmakeSession(err error, packet nex.PacketInterface, callID ui
6972
return rmcResponse, nil
7073
}
7174

75+
func gameSpecificCanJoinMatchmakeSession(manager *commonglobals.MatchmakingManager, pid *types.PID, session *matchmakingtypes.MatchmakeSession) *nex.Error {
76+
if !session.OpenParticipation.Value {
77+
return nex.NewError(nex.ResultCodes.RendezVous.PermissionDenied, "Gathering is not open to new participants")
78+
}
79+
80+
isPublic := false
81+
attrib, err := session.Attributes.Get(0)
82+
if err == nil {
83+
// * I wish this was a joke. top 8 bits are GameMode
84+
// * This is the only difference between a public and a Friends match
85+
isPublic = (attrib.Value & 0xFFFFFF) == 0x30881
86+
}
87+
88+
if isPublic && os.Getenv("PN_MINECRAFT_ALLOW_PUBLIC_MATCHMAKING") == "1" {
89+
//globals.Logger.Info("Game is public")
90+
return nil
91+
}
92+
93+
host := session.OwnerPID
94+
hostFriends := manager.GetUserFriendPIDs(host.LegacyValue())
95+
if slices.Contains(hostFriends, pid.LegacyValue()) {
96+
//globals.Logger.Info("User is friend of host")
97+
return nil
98+
}
99+
100+
isFriendsOfFriends := false
101+
if len(session.ApplicationBuffer.Value) > 0xc3 {
102+
isFriendsOfFriends = session.ApplicationBuffer.Value[0xc3] == 0x8F
103+
}
104+
105+
if !isFriendsOfFriends {
106+
return nex.NewError(nex.ResultCodes.RendezVous.NotFriend, "User is not a friend of host")
107+
}
108+
109+
// * Get the participants of this gathering so we don't have to check all 100whatever of host's friends
110+
_, _, participants, _, nerr := database.FindGatheringByID(manager, session.ID.Value)
111+
if err != nil {
112+
globals.Logger.Errorf("Can't find gathering for pariticpation check: %v", nerr)
113+
return nerr
114+
}
115+
116+
for _, friend := range hostFriends {
117+
// * Make sure this friend is actually in-game
118+
// * This cast feels bad
119+
if slices.Contains(participants, uint64(friend)) {
120+
// * Are you a friend of the host's friend?
121+
friendsFriends := manager.GetUserFriendPIDs(friend)
122+
if slices.Contains(friendsFriends, pid.LegacyValue()) {
123+
//globals.Logger.Infof("User is friend of host's friend %v", friend)
124+
return nil
125+
}
126+
}
127+
}
128+
129+
return nex.NewError(nex.ResultCodes.RendezVous.NotFriend, "User is not a friend of host's friends")
130+
}
131+
72132
func registerCommonSecureServerProtocols() {
73133
secureProtocol := secure.NewProtocol()
74134
globals.SecureEndpoint.RegisterServiceProtocol(secureProtocol)
@@ -95,6 +155,9 @@ func registerCommonSecureServerProtocols() {
95155
commonMatchmakeExtensionProtocol := commonmatchmakeextension.NewCommonProtocol(matchmakeExtensionProtocol)
96156
commonMatchmakeExtensionProtocol.SetManager(globals.MatchmakingManager)
97157

158+
globals.MatchmakingManager.GetUserFriendPIDs = globals.GetUserFriendPIDs
159+
globals.MatchmakingManager.CanJoinMatchmakeSession = gameSpecificCanJoinMatchmakeSession
160+
98161
commonMatchmakeExtensionProtocol.CleanupSearchMatchmakeSession = cleanupSearchMatchmakeSessionHandler
99162
if os.Getenv("PN_MINECRAFT_ALLOW_PUBLIC_MATCHMAKING") != "1" {
100163
globals.Logger.Warning("Public minigames are disabled for safety reasons.")

0 commit comments

Comments
 (0)