gb vendor update github.com/matrix-org/gomatrixserverlib

main
Richard van der Hoff 2017-09-26 12:54:22 +01:00
parent 9ed609b9df
commit f7aa96fc9e
7 changed files with 193 additions and 35 deletions

2
vendor/manifest vendored
View File

@ -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"
}, },
{ {

View File

@ -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,
) )
} }
} }

View File

@ -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 {

View File

@ -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")
}
}

View File

@ -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 {

View File

@ -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))

View File

@ -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 {