gb vendor update github.com/matrix-org/gomatrixserverlib
parent
9ed609b9df
commit
f7aa96fc9e
|
@ -116,7 +116,7 @@
|
||||||
{
|
{
|
||||||
"importpath": "github.com/matrix-org/gomatrixserverlib",
|
"importpath": "github.com/matrix-org/gomatrixserverlib",
|
||||||
"repository": "https://github.com/matrix-org/gomatrixserverlib",
|
"repository": "https://github.com/matrix-org/gomatrixserverlib",
|
||||||
"revision": "ce6f4766251e31487906dfaaebd7d7cfea147252",
|
"revision": "f3be4cb492f23eb30a9f2ab5fc5bd85ee9c3add6",
|
||||||
"branch": "master"
|
"branch": "master"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -389,6 +389,8 @@ func (e Event) CheckFields() error { // nolint: gocyclo
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
origin := e.fields.Origin
|
||||||
|
|
||||||
senderDomain, err := checkID(e.fields.Sender, "user", '@')
|
senderDomain, err := checkID(e.fields.Sender, "user", '@')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -406,34 +408,33 @@ func (e Event) CheckFields() error { // nolint: gocyclo
|
||||||
// Since both domains must be valid domains, and there is no good reason for them
|
// Since both domains must be valid domains, and there is no good reason for them
|
||||||
// to be different we might as well ensure that they are the same since it
|
// to be different we might as well ensure that they are the same since it
|
||||||
// makes the signature checks simpler.
|
// makes the signature checks simpler.
|
||||||
if e.fields.Origin != ServerName(eventDomain) {
|
if origin != ServerName(eventDomain) {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"gomatrixserverlib: event ID domain doesn't match origin: %q != %q",
|
"gomatrixserverlib: event ID domain doesn't match origin: %q != %q",
|
||||||
eventDomain, e.fields.Origin,
|
eventDomain, origin,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if eventDomain != senderDomain {
|
if origin != ServerName(senderDomain) {
|
||||||
// For the most part all events should be sent by a user on the
|
// For the most part all events should be sent by a user on the
|
||||||
// originating server
|
// originating server.
|
||||||
|
//
|
||||||
// However "m.room.member" events created from third-party invites
|
// However "m.room.member" events created from third-party invites
|
||||||
// are allowed to have a different sender because they have the same
|
// are allowed to have a different sender because they have the same
|
||||||
// sender as the "m.room.third_party_invite" event they derived from.
|
// sender as the "m.room.third_party_invite" event they derived from.
|
||||||
// https://github.com/matrix-org/synapse/blob/v0.21.0/synapse/event_auth.py#L58-L64
|
// https://github.com/matrix-org/synapse/blob/v0.21.0/synapse/event_auth.py#L58-L64
|
||||||
|
//
|
||||||
|
// Also, some old versions of synapse had a bug wherein some
|
||||||
|
// joins/leaves used the origin and event id supplied by the helping
|
||||||
|
// server instead of the joining/leaving server.
|
||||||
|
//
|
||||||
|
// So in general we allow the sender to be different from the
|
||||||
|
// origin for m.room.member events. In any case, we check it was
|
||||||
|
// signed by both servers later.
|
||||||
if e.fields.Type != MRoomMember {
|
if e.fields.Type != MRoomMember {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"gomatrixserverlib: sender domain doesn't match origin: %q != %q",
|
"gomatrixserverlib: sender domain doesn't match origin: %q != %q",
|
||||||
senderDomain, e.fields.Origin,
|
senderDomain, origin,
|
||||||
)
|
|
||||||
}
|
|
||||||
c, err := newMemberContentFromEvent(e)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if c.Membership != invite || c.ThirdPartyInvite == nil {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"gomatrixserverlib: sender domain doesn't match origin: %q != %q",
|
|
||||||
senderDomain, e.fields.Origin,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,19 +189,33 @@ func verifyEventSignature(signingName string, keyID KeyID, publicKey ed25519.Pub
|
||||||
|
|
||||||
// VerifyEventSignatures checks that each event in a list of events has valid
|
// VerifyEventSignatures checks that each event in a list of events has valid
|
||||||
// signatures from the server that sent it.
|
// signatures from the server that sent it.
|
||||||
func VerifyEventSignatures(ctx context.Context, events []Event, keyRing KeyRing) error { // nolint: gocyclo
|
func VerifyEventSignatures(ctx context.Context, events []Event, keyRing JSONVerifier) error { // nolint: gocyclo
|
||||||
var toVerify []VerifyJSONRequest
|
var toVerify []VerifyJSONRequest
|
||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
redactedJSON, err := redactEvent(event.eventJSON)
|
redactedJSON, err := redactEvent(event.eventJSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v := VerifyJSONRequest{
|
|
||||||
Message: redactedJSON,
|
domains := make(map[ServerName]bool)
|
||||||
AtTS: event.OriginServerTS(),
|
domains[event.Origin()] = true
|
||||||
ServerName: event.Origin(),
|
|
||||||
|
// in general, we expect the domain of the sender id to be the
|
||||||
|
// same as the origin; however there was a bug in an old version
|
||||||
|
// of synapse which meant that some joins/leaves used the origin
|
||||||
|
// and event id supplied by the helping server instead of the
|
||||||
|
// joining/leaving server.
|
||||||
|
//
|
||||||
|
// That's ok, provided it's signed by the sender's server too.
|
||||||
|
//
|
||||||
|
// XXX we may have to exclude 3pid invites here, as per
|
||||||
|
// https://github.com/matrix-org/synapse/blob/v0.21.0/synapse/event_auth.py#L58-L64.
|
||||||
|
//
|
||||||
|
senderDomain, err := domainFromID(event.Sender())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
toVerify = append(toVerify, v)
|
domains[ServerName(senderDomain)] = true
|
||||||
|
|
||||||
// MRoomMember invite events are signed by both the server sending
|
// MRoomMember invite events are signed by both the server sending
|
||||||
// the invite and the server the invite is for.
|
// the invite and the server the invite is for.
|
||||||
|
@ -216,12 +230,20 @@ func VerifyEventSignatures(ctx context.Context, events []Event, keyRing KeyRing)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if c.Membership == invite {
|
if c.Membership == invite {
|
||||||
v.ServerName = ServerName(targetDomain)
|
domains[ServerName(targetDomain)] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for domain := range domains {
|
||||||
|
v := VerifyJSONRequest{
|
||||||
|
Message: redactedJSON,
|
||||||
|
AtTS: event.OriginServerTS(),
|
||||||
|
ServerName: domain,
|
||||||
|
}
|
||||||
toVerify = append(toVerify, v)
|
toVerify = append(toVerify, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
results, err := keyRing.VerifyJSONs(ctx, toVerify)
|
results, err := keyRing.VerifyJSONs(ctx, toVerify)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -17,7 +17,10 @@ package gomatrixserverlib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"golang.org/x/crypto/ed25519"
|
"golang.org/x/crypto/ed25519"
|
||||||
|
@ -258,3 +261,127 @@ func TestSignEventTestVectors(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`)
|
}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type StubVerifier struct {
|
||||||
|
requests []VerifyJSONRequest
|
||||||
|
results []VerifyJSONResult
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *StubVerifier) VerifyJSONs(ctx context.Context, requests []VerifyJSONRequest) ([]VerifyJSONResult, error) {
|
||||||
|
v.requests = append(v.requests, requests...)
|
||||||
|
return v.results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVerifyEventSignatures(t *testing.T) {
|
||||||
|
verifier := StubVerifier{}
|
||||||
|
|
||||||
|
eventJSON := []byte(`{
|
||||||
|
"type": "m.room.name",
|
||||||
|
"state_key": "",
|
||||||
|
"event_id": "$test:localhost",
|
||||||
|
"room_id": "!test:localhost",
|
||||||
|
"sender": "@test:localhost",
|
||||||
|
"origin": "originserver",
|
||||||
|
"content": {
|
||||||
|
"name": "Hello World"
|
||||||
|
},
|
||||||
|
"origin_server_ts": 123456
|
||||||
|
}`)
|
||||||
|
|
||||||
|
var event Event
|
||||||
|
if err := json.Unmarshal(eventJSON, &event.fields); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
event.eventJSON = eventJSON
|
||||||
|
|
||||||
|
events := []Event{event}
|
||||||
|
if err := VerifyEventSignatures(context.Background(), events, &verifier); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// There should be two verification requests
|
||||||
|
if len(verifier.requests) != 2 {
|
||||||
|
t.Fatalf("Number of requests: got %d, want 2", len(verifier.requests))
|
||||||
|
}
|
||||||
|
wantContent, err := redactEvent(eventJSON)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
servers := []string{}
|
||||||
|
|
||||||
|
for i, rq := range verifier.requests {
|
||||||
|
if !bytes.Equal(rq.Message, wantContent) {
|
||||||
|
t.Errorf("Verify content %d: got %s, want %s", i, rq.Message, wantContent)
|
||||||
|
}
|
||||||
|
if rq.AtTS != 123456 {
|
||||||
|
t.Errorf("Verify time %d: got %d, want %d", i, rq.AtTS, 123456)
|
||||||
|
}
|
||||||
|
servers = append(servers, string(rq.ServerName))
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(servers)
|
||||||
|
if servers[0] != "localhost" {
|
||||||
|
t.Errorf("Verify server 0: got %s, want %s", servers[0], "localhost")
|
||||||
|
}
|
||||||
|
if servers[1] != "originserver" {
|
||||||
|
t.Errorf("Verify server 1: got %s, want %s", servers[1], "originserver")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVerifyEventSignaturesForInvite(t *testing.T) {
|
||||||
|
verifier := StubVerifier{}
|
||||||
|
|
||||||
|
eventJSON := []byte(`{
|
||||||
|
"type": "m.room.member",
|
||||||
|
"state_key": "@bob:bobserver",
|
||||||
|
"event_id": "$test:aliceserver",
|
||||||
|
"room_id": "!test:room",
|
||||||
|
"sender": "@alice:aliceserver",
|
||||||
|
"origin": "aliceserver",
|
||||||
|
"content": {
|
||||||
|
"membership": "invite"
|
||||||
|
},
|
||||||
|
"origin_server_ts": 123456
|
||||||
|
}`)
|
||||||
|
|
||||||
|
var event Event
|
||||||
|
if err := json.Unmarshal(eventJSON, &event.fields); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
event.eventJSON = eventJSON
|
||||||
|
|
||||||
|
events := []Event{event}
|
||||||
|
if err := VerifyEventSignatures(context.Background(), events, &verifier); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// There should be two verification requests
|
||||||
|
if len(verifier.requests) != 2 {
|
||||||
|
t.Fatalf("Number of requests: got %d, want 2", len(verifier.requests))
|
||||||
|
}
|
||||||
|
wantContent, err := redactEvent(eventJSON)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
servers := []string{}
|
||||||
|
|
||||||
|
for i, rq := range verifier.requests {
|
||||||
|
if !bytes.Equal(rq.Message, wantContent) {
|
||||||
|
t.Errorf("Verify content %d: got %s, want %s", i, rq.Message, wantContent)
|
||||||
|
}
|
||||||
|
if rq.AtTS != 123456 {
|
||||||
|
t.Errorf("Verify time %d: got %d, want %d", i, rq.AtTS, 123456)
|
||||||
|
}
|
||||||
|
servers = append(servers, string(rq.ServerName))
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(servers)
|
||||||
|
if servers[0] != "aliceserver" {
|
||||||
|
t.Errorf("Verify server 0: got %s, want %s", servers[0], "aliceserver")
|
||||||
|
}
|
||||||
|
if servers[1] != "bobserver" {
|
||||||
|
t.Errorf("Verify server 1: got %s, want %s", servers[1], "bobserver")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ func (r RespState) Events() ([]Event, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that a response to /state is valid.
|
// Check that a response to /state is valid.
|
||||||
func (r RespState) Check(ctx context.Context, keyRing KeyRing) error {
|
func (r RespState) Check(ctx context.Context, keyRing JSONVerifier) error {
|
||||||
var allEvents []Event
|
var allEvents []Event
|
||||||
for _, event := range r.AuthEvents {
|
for _, event := range r.AuthEvents {
|
||||||
if event.StateKey() == nil {
|
if event.StateKey() == nil {
|
||||||
|
@ -214,8 +214,10 @@ type respSendJoinFields struct {
|
||||||
// Check that a response to /send_join is valid.
|
// Check that a response to /send_join is valid.
|
||||||
// This checks that it would be valid as a response to /state
|
// This checks that it would be valid as a response to /state
|
||||||
// This also checks that the join event is allowed by the state.
|
// This also checks that the join event is allowed by the state.
|
||||||
func (r RespSendJoin) Check(ctx context.Context, keyRing KeyRing, joinEvent Event) error {
|
func (r RespSendJoin) Check(ctx context.Context, keyRing JSONVerifier, joinEvent Event) error {
|
||||||
// First check that the state is valid.
|
// First check that the state is valid and that the events in the response
|
||||||
|
// are correctly signed.
|
||||||
|
//
|
||||||
// The response to /send_join has the same data as a response to /state
|
// The response to /send_join has the same data as a response to /state
|
||||||
// and the checks for a response to /state also apply.
|
// and the checks for a response to /state also apply.
|
||||||
if err := RespState(r).Check(ctx, keyRing); err != nil {
|
if err := RespState(r).Check(ctx, keyRing); err != nil {
|
||||||
|
|
|
@ -69,12 +69,18 @@ type VerifyJSONResult struct {
|
||||||
Error error
|
Error error
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyJSONs performs bulk JSON signature verification for a list of VerifyJSONRequests.
|
// A JSONVerifier is an object which can verify the signatures of JSON messages.
|
||||||
// Returns a list of VerifyJSONResults with the same length and order as the request list.
|
type JSONVerifier interface {
|
||||||
// The caller should check the Result field for each entry to see if it was valid.
|
// VerifyJSONs performs bulk JSON signature verification for a list of VerifyJSONRequests.
|
||||||
// Returns an error if there was a problem talking to the database or one of the other methods
|
// Returns a list of VerifyJSONResults with the same length and order as the request list.
|
||||||
// of fetching the public keys.
|
// The caller should check the Result field for each entry to see if it was valid.
|
||||||
func (k *KeyRing) VerifyJSONs(ctx context.Context, requests []VerifyJSONRequest) ([]VerifyJSONResult, error) { // nolint: gocyclo
|
// Returns an error if there was a problem talking to the database or one of the other methods
|
||||||
|
// of fetching the public keys.
|
||||||
|
VerifyJSONs(ctx context.Context, requests []VerifyJSONRequest) ([]VerifyJSONResult, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyJSONs implements JSONVerifier.
|
||||||
|
func (k KeyRing) VerifyJSONs(ctx context.Context, requests []VerifyJSONRequest) ([]VerifyJSONResult, error) { // nolint: gocyclo
|
||||||
results := make([]VerifyJSONResult, len(requests))
|
results := make([]VerifyJSONResult, len(requests))
|
||||||
keyIDs := make([][]KeyID, len(requests))
|
keyIDs := make([][]KeyID, len(requests))
|
||||||
|
|
||||||
|
|
|
@ -184,7 +184,7 @@ func isSafeInHTTPQuotedString(text string) bool { // nolint: gocyclo
|
||||||
// It consumes the body of the request.
|
// It consumes the body of the request.
|
||||||
// The JSON content can be accessed using FederationRequest.Content()
|
// The JSON content can be accessed using FederationRequest.Content()
|
||||||
// Returns an 400 error if there was a problem parsing the request.
|
// Returns an 400 error if there was a problem parsing the request.
|
||||||
// It authenticates the request using an ed25519 signature using the KeyRing.
|
// It authenticates the request using an ed25519 signature using the JSONVerifier.
|
||||||
// The origin server can be accessed using FederationRequest.Origin()
|
// The origin server can be accessed using FederationRequest.Origin()
|
||||||
// Returns a 401 error if there was a problem authenticating the request.
|
// Returns a 401 error if there was a problem authenticating the request.
|
||||||
// HTTP handlers using this should be careful that they only use the parts of
|
// HTTP handlers using this should be careful that they only use the parts of
|
||||||
|
@ -192,7 +192,7 @@ func isSafeInHTTPQuotedString(text string) bool { // nolint: gocyclo
|
||||||
// the query parameters, and the JSON content. In particular the version of
|
// the query parameters, and the JSON content. In particular the version of
|
||||||
// HTTP and the headers aren't protected by the signature.
|
// HTTP and the headers aren't protected by the signature.
|
||||||
func VerifyHTTPRequest(
|
func VerifyHTTPRequest(
|
||||||
req *http.Request, now time.Time, destination ServerName, keys KeyRing,
|
req *http.Request, now time.Time, destination ServerName, keys JSONVerifier,
|
||||||
) (*FederationRequest, util.JSONResponse) {
|
) (*FederationRequest, util.JSONResponse) {
|
||||||
request, err := readHTTPRequest(req)
|
request, err := readHTTPRequest(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in New Issue