From c1c34388e4638301ff1ed010eac0e4a593be852b Mon Sep 17 00:00:00 2001 From: Brendan Abolivier Date: Fri, 8 Sep 2017 14:55:23 +0100 Subject: [PATCH] gb vendor update github.com/matrix-org/gomatrixserverlib --- vendor/manifest | 2 +- .../matrix-org/gomatrixserverlib/base64.go | 9 +- .../gomatrixserverlib/base64_test.go | 13 ++ .../matrix-org/gomatrixserverlib/event.go | 4 +- .../gomatrixserverlib/event_examples_test.go | 27 ++++ .../matrix-org/gomatrixserverlib/eventauth.go | 62 ++++++-- .../gomatrixserverlib/eventauth_test.go | 149 +++++++++++++++++- .../gomatrixserverlib/eventcontent.go | 39 ++++- 8 files changed, 287 insertions(+), 18 deletions(-) create mode 100644 vendor/src/github.com/matrix-org/gomatrixserverlib/event_examples_test.go diff --git a/vendor/manifest b/vendor/manifest index 8646a7df9..d2c71edae 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -116,7 +116,7 @@ { "importpath": "github.com/matrix-org/gomatrixserverlib", "repository": "https://github.com/matrix-org/gomatrixserverlib", - "revision": "768a8767051a4aca7f5e41f912954ae04d5f1efb", + "revision": "2e9caead882bcdeb999cf4677cfff47e39d3271e", "branch": "master" }, { diff --git a/vendor/src/github.com/matrix-org/gomatrixserverlib/base64.go b/vendor/src/github.com/matrix-org/gomatrixserverlib/base64.go index 54e42ca36..63ff90bcc 100644 --- a/vendor/src/github.com/matrix-org/gomatrixserverlib/base64.go +++ b/vendor/src/github.com/matrix-org/gomatrixserverlib/base64.go @@ -18,6 +18,7 @@ package gomatrixserverlib import ( "encoding/base64" "encoding/json" + "strings" ) // A Base64String is a string of bytes that are base64 encoded when used in JSON. @@ -43,6 +44,12 @@ func (b64 *Base64String) UnmarshalJSON(raw []byte) (err error) { if err = json.Unmarshal(raw, &str); err != nil { return } - *b64, err = base64.RawStdEncoding.DecodeString(str) + // We must check whether the string was encoded in a URL-safe way in order + // to use the appropriate encoding. + if strings.ContainsAny(str, "-_") { + *b64, err = base64.RawURLEncoding.DecodeString(str) + } else { + *b64, err = base64.RawStdEncoding.DecodeString(str) + } return } diff --git a/vendor/src/github.com/matrix-org/gomatrixserverlib/base64_test.go b/vendor/src/github.com/matrix-org/gomatrixserverlib/base64_test.go index aa637aa88..9ef94046a 100644 --- a/vendor/src/github.com/matrix-org/gomatrixserverlib/base64_test.go +++ b/vendor/src/github.com/matrix-org/gomatrixserverlib/base64_test.go @@ -45,6 +45,19 @@ func TestUnmarshalBase64(t *testing.T) { } } +func TestUnmarshalUrlSafeBase64(t *testing.T) { + input := []byte(`"dGhpc_9pc_9h_3Rlc3Q"`) + want := "this\xffis\xffa\xfftest" + var got Base64String + err := json.Unmarshal(input, &got) + if err != nil { + t.Fatal(err) + } + if string(got) != want { + t.Fatalf("json.Unmarshal(%q): wanted %q got %q", string(input), want, string(got)) + } +} + func TestMarshalBase64Struct(t *testing.T) { input := struct{ Value Base64String }{Base64String("this\xffis\xffa\xfftest")} want := `{"Value":"dGhpc/9pc/9h/3Rlc3Q"}` diff --git a/vendor/src/github.com/matrix-org/gomatrixserverlib/event.go b/vendor/src/github.com/matrix-org/gomatrixserverlib/event.go index e8c2de0f1..c41109b97 100644 --- a/vendor/src/github.com/matrix-org/gomatrixserverlib/event.go +++ b/vendor/src/github.com/matrix-org/gomatrixserverlib/event.go @@ -422,7 +422,7 @@ func (e Event) CheckFields() error { if e.fields.Type != MRoomMember { return fmt.Errorf( "gomatrixserverlib: sender domain doesn't match origin: %q != %q", - eventDomain, e.fields.Origin, + senderDomain, e.fields.Origin, ) } c, err := newMemberContentFromEvent(e) @@ -432,7 +432,7 @@ func (e Event) CheckFields() error { if c.Membership != invite || c.ThirdPartyInvite == nil { return fmt.Errorf( "gomatrixserverlib: sender domain doesn't match origin: %q != %q", - eventDomain, e.fields.Origin, + senderDomain, e.fields.Origin, ) } } diff --git a/vendor/src/github.com/matrix-org/gomatrixserverlib/event_examples_test.go b/vendor/src/github.com/matrix-org/gomatrixserverlib/event_examples_test.go new file mode 100644 index 000000000..4882f8e56 --- /dev/null +++ b/vendor/src/github.com/matrix-org/gomatrixserverlib/event_examples_test.go @@ -0,0 +1,27 @@ +/* Copyright 2016-2017 Vector Creations Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package gomatrixserverlib + +import "fmt" + +func ExampleSplitID() { + localpart, domain, err := SplitID('@', "@alice:localhost:8080") + if err != nil { + panic(err) + } + fmt.Println(localpart, domain) + // Output: alice localhost:8080 +} diff --git a/vendor/src/github.com/matrix-org/gomatrixserverlib/eventauth.go b/vendor/src/github.com/matrix-org/gomatrixserverlib/eventauth.go index f9b938a32..16e369ccc 100644 --- a/vendor/src/github.com/matrix-org/gomatrixserverlib/eventauth.go +++ b/vendor/src/github.com/matrix-org/gomatrixserverlib/eventauth.go @@ -18,6 +18,9 @@ package gomatrixserverlib import ( "encoding/json" "fmt" + "strings" + + "golang.org/x/crypto/ed25519" "github.com/matrix-org/util" ) @@ -224,15 +227,7 @@ func accumulateStateNeeded(result *StateNeeded, eventType, sender string, stateK } // thirdPartyInviteToken extracts the token from the third_party_invite. -func thirdPartyInviteToken(thirdPartyInviteData rawJSON) (string, error) { - var thirdPartyInvite struct { - Signed struct { - Token string `json:"token"` - } `json:"signed"` - } - if err := json.Unmarshal(thirdPartyInviteData, &thirdPartyInvite); err != nil { - return "", err - } +func thirdPartyInviteToken(thirdPartyInvite *memberThirdPartyInvite) (string, error) { if thirdPartyInvite.Signed.Token == "" { return "", fmt.Errorf("missing 'third_party_invite.signed.token' JSON key") } @@ -774,6 +769,8 @@ type membershipAllower struct { powerLevels powerLevelContent // The m.room.join_rules content for the room. joinRule joinRuleContent + // The m.room.third_party_invite content referenced by this event. + thirdPartyInvite thirdPartyInviteContent } // newMembershipAllower loads the information needed to authenticate the m.room.member event @@ -808,6 +805,13 @@ func newMembershipAllower(authEvents AuthEventProvider, event Event) (m membersh return } } + // If this event comes from a third_party_invite, we need to check it against the original event. + if m.newMember.ThirdPartyInvite != nil { + token := m.newMember.ThirdPartyInvite.Signed.Token + if m.thirdPartyInvite, err = newThirdPartyInviteContentFromAuthEvents(authEvents, token); err != nil { + return + } + } return } @@ -840,10 +844,10 @@ func (m *membershipAllower) membershipAllowed(event Event) error { // Otherwise fall back to the normal checks. } - if m.newMember.Membership == invite && len(m.newMember.ThirdPartyInvite) != 0 { + if m.newMember.Membership == invite && m.newMember.ThirdPartyInvite != nil { // Special case third party invites // https://github.com/matrix-org/synapse/blob/v0.18.5/synapse/api/auth.py#L393 - panic(fmt.Errorf("ThirdPartyInvite not implemented")) + return m.membershipAllowedFromThirdPartyInvite() } if m.targetID == m.senderID { @@ -855,6 +859,42 @@ func (m *membershipAllower) membershipAllowed(event Event) error { return m.membershipAllowedOther() } +// membershipAllowedFronThirdPartyInvite determines if the member events is following +// up the third_party_invite event it claims. +func (m *membershipAllower) membershipAllowedFromThirdPartyInvite() error { + // Check if the event's target matches with the Matrix ID provided by the + // identity server. + if m.targetID != m.newMember.ThirdPartyInvite.Signed.MXID { + return errorf( + "The invite target %s doesn't match with the Matrix ID provided by the identity server %s", + m.targetID, m.newMember.ThirdPartyInvite.Signed.MXID, + ) + } + // Marshal the "signed" so it can be verified by VerifyJSON. + marshalledSigned, err := json.Marshal(m.newMember.ThirdPartyInvite.Signed) + if err != nil { + return err + } + // Check each signature with each public key. If one signature could be + // verified with one public key, accept the event. + for _, publicKey := range m.thirdPartyInvite.PublicKeys { + for domain, signatures := range m.newMember.ThirdPartyInvite.Signed.Signatures { + for keyID := range signatures { + if strings.HasPrefix(keyID, "ed25519") { + if err = VerifyJSON( + domain, KeyID(keyID), + ed25519.PublicKey(publicKey.PublicKey), + marshalledSigned, + ); err == nil { + return nil + } + } + } + } + } + return errorf("Couldn't verify signature on third-party invite for %s", m.targetID) +} + // membershipAllowedSelf determines if the change made by the user to their own membership is allowed. func (m *membershipAllower) membershipAllowedSelf() error { if m.newMember.Membership == join { diff --git a/vendor/src/github.com/matrix-org/gomatrixserverlib/eventauth_test.go b/vendor/src/github.com/matrix-org/gomatrixserverlib/eventauth_test.go index ca11c42e7..ab59cb579 100644 --- a/vendor/src/github.com/matrix-org/gomatrixserverlib/eventauth_test.go +++ b/vendor/src/github.com/matrix-org/gomatrixserverlib/eventauth_test.go @@ -169,7 +169,12 @@ func TestStateNeededForInvite3PID(t *testing.T) { StateKey: &skey, Sender: "@u1:a", } - b.SetContent(memberContent{"invite", rawJSON(`{"signed":{"token":"my_token"}}`)}) + + b.SetContent(memberContent{"invite", &memberThirdPartyInvite{ + Signed: memberThirdPartyInviteSigned{ + Token: "my_token", + }, + }}) testStateNeededForAuth(t, `[{ "type": "m.room.member", "state_key": "@u2:b", @@ -280,7 +285,7 @@ func testEventAllowed(t *testing.T, testCaseJSON string) { panic(err) } if err := Allowed(event, &tc.AuthEvents); err == nil { - t.Fatalf("Expected %q to not be allowed but it was: %q", string(data), err) + t.Fatalf("Expected %q to not be allowed but it was", string(data)) } } } @@ -532,6 +537,146 @@ func TestAllowedWithNoPowerLevels(t *testing.T) { }`) } +func TestAllowedInviteFrom3PID(t *testing.T) { + testEventAllowed(t, `{ + "auth_events": { + "create": { + "type": "m.room.create", + "state_key": "", + "sender": "@u1:a", + "room_id": "!r1:a", + "event_id": "$e1:a", + "content": {"creator": "@u1:a"} + }, + "member": { + "@u1:a": { + "type": "m.room.member", + "sender": "@u1:a", + "room_id": "!r1:a", + "state_key": "@u1:a", + "event_id": "$e2:a", + "content": {"membership": "join"} + } + }, + "third_party_invite": { + "my_token": { + "type": "m.room.third_party_invite", + "sender": "@u1:a", + "room_id": "!r1:a", + "state_key": "my_token", + "event_id": "$e3:a", + "content": { + "display_name": "foo...@bar...", + "public_key": "pubkey", + "key_validity_url": "https://example.tld/isvalid", + "public_keys": [ + { + "public_key": "mrV51jApZKahGjfMhlevp+QtSSTDKCLaLVCzYc4HELY", + "key_validity_url": "https://example.tld/isvalid" + } + ] + } + } + } + }, + "allowed": [{ + "type": "m.room.member", + "sender": "@u1:a", + "room_id": "!r1:a", + "state_key": "@u2:a", + "event_id": "$e4:a", + "content": { + "membership": "invite", + "third_party_invite": { + "display_name": "foo...@bar...", + "signed": { + "token": "my_token", + "mxid": "@u2:a", + "signatures": { + "example.tld": { + "ed25519:0": "CibGFS0vX93quJFppsQbYQKJFIwxiYEK87lNmekS/fdetUMXPdR2wwNDd09J1jJ28GCH3GogUTuFDB1ScPFxBg" + } + } + } + } + } + }], + "not_allowed": [{ + "type": "m.room.member", + "sender": "@u1:a", + "room_id": "!r1:a", + "state_key": "@u2:a", + "event_id": "$e4:a", + "content": { + "membership": "invite", + "third_party_invite": { + "display_name": "foo...@bar...", + "signed": { + "token": "my_token", + "mxid": "@u2:a", + "signatures": { + "example.tld": { + "ed25519:0": "some_signature" + } + } + } + } + }, + "unsigned": { + "not_allowed": "Bad signature" + } + }, { + "type": "m.room.member", + "sender": "@u1:a", + "room_id": "!r1:a", + "state_key": "@u2:a", + "event_id": "$e5:a", + "content": { + "membership": "invite", + "third_party_invite": { + "display_name": "foo...@bar...", + "signed": { + "token": "my_token", + "mxid": "@u3:a", + "signatures": { + "example.tld": { + "ed25519:0": "CibGFS0vX93quJFppsQbYQKJFIwxiYEK87lNmekS/fdetUMXPdR2wwNDd09J1jJ28GCH3GogUTuFDB1ScPFxBg" + } + } + } + } + }, + "unsigned": { + "not_allowed": "MXID doesn't match state key" + } + }, { + "type": "m.room.member", + "sender": "@u1:a", + "room_id": "!r1:a", + "state_key": "@u2:a", + "event_id": "$e6:a", + "content": { + "membership": "invite", + "third_party_invite": { + "display_name": "foo...@bar...", + "signed": { + "token": "my_other_token", + "mxid": "@u2:a", + "signatures": { + "example.tld": { + "ed25519:0": "CibGFS0vX93quJFppsQbYQKJFIwxiYEK87lNmekS/fdetUMXPdR2wwNDd09J1jJ28GCH3GogUTuFDB1ScPFxBg" + } + } + } + } + }, + "unsigned": { + "not_allowed": "Token doesn't refer to a known third-party invite" + } + }] + }`) +} + func TestAllowedNoFederation(t *testing.T) { testEventAllowed(t, `{ "auth_events": { diff --git a/vendor/src/github.com/matrix-org/gomatrixserverlib/eventcontent.go b/vendor/src/github.com/matrix-org/gomatrixserverlib/eventcontent.go index 27babcb4a..ad4e77513 100644 --- a/vendor/src/github.com/matrix-org/gomatrixserverlib/eventcontent.go +++ b/vendor/src/github.com/matrix-org/gomatrixserverlib/eventcontent.go @@ -108,7 +108,18 @@ type memberContent struct { // We use the membership key in order to check if the user is in the room. Membership string `json:"membership"` // We use the third_party_invite key to special case thirdparty invites. - ThirdPartyInvite rawJSON `json:"third_party_invite,omitempty"` + ThirdPartyInvite *memberThirdPartyInvite `json:"third_party_invite,omitempty"` +} + +type memberThirdPartyInvite struct { + DisplayName string `json:"display_name"` + Signed memberThirdPartyInviteSigned `json:"signed"` +} + +type memberThirdPartyInviteSigned struct { + MXID string `json:"mxid"` + Signatures map[string]map[string]string `json:"signatures"` + Token string `json:"token"` } // newMemberContentFromAuthEvents loads the member content from the member event for the user ID in the auth events. @@ -137,6 +148,32 @@ func newMemberContentFromEvent(event Event) (c memberContent, err error) { return } +// thirdPartyInviteContent is the JSON content of a m.room.third_party_invite event needed for auth checks. +// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-third-party-invite for descriptions of the fields. +type thirdPartyInviteContent struct { + // Public keys are used to verify the signature of a m.room.member event that + // came from a m.room.third_party_invite event + PublicKeys []struct { + PublicKey Base64String `json:"public_key"` + } `json:"public_keys"` +} + +func newThirdPartyInviteContentFromAuthEvents(authEvents AuthEventProvider, token string) (t thirdPartyInviteContent, err error) { + var thirdPartyInviteEvent *Event + if thirdPartyInviteEvent, err = authEvents.ThirdPartyInvite(token); err != nil { + return + } + if thirdPartyInviteEvent == nil { + // If there isn't a third_party_invite event, then we return with an error + err = errorf("Couldn't find third party invite event") + return + } + if err = json.Unmarshal(thirdPartyInviteEvent.Content(), &t); err != nil { + err = errorf("unparsable third party invite event content: %s", err.Error()) + } + return +} + // joinRuleContent is the JSON content of a m.room.join_rules event needed for auth checks. // See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-join-rules for descriptions of the fields. type joinRuleContent struct {