From 73c3097c557b4acf072af859e649ec24eb132e60 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 14 Nov 2022 17:06:46 +0000 Subject: [PATCH] Signing identity tweaks --- clientapi/routing/membership.go | 7 +++++- clientapi/routing/profile.go | 10 ++++++-- clientapi/routing/redaction.go | 7 +++++- clientapi/routing/sendevent.go | 8 ++++++- clientapi/threepid/invites.go | 7 +++++- federationapi/routing/join.go | 12 +++++++++- federationapi/routing/leave.go | 13 ++++++++++- internal/eventutil/events.go | 21 +++++++---------- roomserver/internal/alias.go | 12 +++++++++- roomserver/internal/perform/perform_admin.go | 23 +++++++++++++++++-- roomserver/internal/perform/perform_join.go | 15 ++++++++---- roomserver/internal/perform/perform_leave.go | 14 +++++------ .../internal/perform/perform_upgrade.go | 15 +++++++++++- setup/config/config_global.go | 11 ++++++++- 14 files changed, 138 insertions(+), 37 deletions(-) diff --git a/clientapi/routing/membership.go b/clientapi/routing/membership.go index f3a687017..482c1f5f7 100644 --- a/clientapi/routing/membership.go +++ b/clientapi/routing/membership.go @@ -323,7 +323,12 @@ func buildMembershipEvent( return nil, err } - return eventutil.QueryAndBuildEvent(ctx, &builder, cfg.Matrix, evTime, rsAPI, nil) + identity, err := cfg.Matrix.SigningIdentityFor(device.UserDomain()) + if err != nil { + return nil, err + } + + return eventutil.QueryAndBuildEvent(ctx, &builder, cfg.Matrix, identity, evTime, rsAPI, nil) } // loadProfile lookups the profile of a given user from the database and returns diff --git a/clientapi/routing/profile.go b/clientapi/routing/profile.go index 5ccddcaff..92a75fc78 100644 --- a/clientapi/routing/profile.go +++ b/clientapi/routing/profile.go @@ -284,7 +284,7 @@ func updateProfile( } events, err := buildMembershipEvents( - ctx, res.RoomIDs, *profile, userID, cfg, evTime, rsAPI, + ctx, device, res.RoomIDs, *profile, userID, cfg, evTime, rsAPI, ) switch e := err.(type) { case nil: @@ -349,6 +349,7 @@ func getProfile( func buildMembershipEvents( ctx context.Context, + device *userapi.Device, roomIDs []string, newProfile authtypes.Profile, userID string, cfg *config.ClientAPI, evTime time.Time, rsAPI api.ClientRoomserverAPI, @@ -380,7 +381,12 @@ func buildMembershipEvents( return nil, err } - event, err := eventutil.QueryAndBuildEvent(ctx, &builder, cfg.Matrix, evTime, rsAPI, nil) + identity, err := cfg.Matrix.SigningIdentityFor(device.UserDomain()) + if err != nil { + return nil, err + } + + event, err := eventutil.QueryAndBuildEvent(ctx, &builder, cfg.Matrix, identity, evTime, rsAPI, nil) if err != nil { return nil, err } diff --git a/clientapi/routing/redaction.go b/clientapi/routing/redaction.go index c343b4a1e..7841b3b07 100644 --- a/clientapi/routing/redaction.go +++ b/clientapi/routing/redaction.go @@ -123,8 +123,13 @@ func SendRedaction( return jsonerror.InternalServerError() } + identity, err := cfg.Matrix.SigningIdentityFor(device.UserDomain()) + if err != nil { + return jsonerror.InternalServerError() + } + var queryRes roomserverAPI.QueryLatestEventsAndStateResponse - e, err := eventutil.QueryAndBuildEvent(req.Context(), &builder, cfg.Matrix, time.Now(), rsAPI, &queryRes) + e, err := eventutil.QueryAndBuildEvent(req.Context(), &builder, cfg.Matrix, identity, time.Now(), rsAPI, &queryRes) if err == eventutil.ErrRoomNoExists { return util.JSONResponse{ Code: http.StatusNotFound, diff --git a/clientapi/routing/sendevent.go b/clientapi/routing/sendevent.go index 48625eadf..90af9ac4d 100644 --- a/clientapi/routing/sendevent.go +++ b/clientapi/routing/sendevent.go @@ -276,8 +276,14 @@ func generateSendEvent( return nil, &resErr } + identity, err := cfg.Matrix.SigningIdentityFor(device.UserDomain()) + if err != nil { + resErr := jsonerror.InternalServerError() + return nil, &resErr + } + var queryRes api.QueryLatestEventsAndStateResponse - e, err := eventutil.QueryAndBuildEvent(ctx, &builder, cfg.Matrix, evTime, rsAPI, &queryRes) + e, err := eventutil.QueryAndBuildEvent(ctx, &builder, cfg.Matrix, identity, evTime, rsAPI, &queryRes) if err == eventutil.ErrRoomNoExists { return nil, &util.JSONResponse{ Code: http.StatusNotFound, diff --git a/clientapi/threepid/invites.go b/clientapi/threepid/invites.go index a5a52ffb9..1f294a032 100644 --- a/clientapi/threepid/invites.go +++ b/clientapi/threepid/invites.go @@ -359,8 +359,13 @@ func emit3PIDInviteEvent( return err } + identity, err := cfg.Matrix.SigningIdentityFor(device.UserDomain()) + if err != nil { + return err + } + queryRes := api.QueryLatestEventsAndStateResponse{} - event, err := eventutil.QueryAndBuildEvent(ctx, builder, cfg.Matrix, evTime, rsAPI, &queryRes) + event, err := eventutil.QueryAndBuildEvent(ctx, builder, cfg.Matrix, identity, evTime, rsAPI, &queryRes) if err != nil { return err } diff --git a/federationapi/routing/join.go b/federationapi/routing/join.go index 74d065e59..03809df75 100644 --- a/federationapi/routing/join.go +++ b/federationapi/routing/join.go @@ -131,10 +131,20 @@ func MakeJoin( return jsonerror.InternalServerError() } + identity, err := cfg.Matrix.SigningIdentityFor(request.Destination()) + if err != nil { + return util.JSONResponse{ + Code: http.StatusNotFound, + JSON: jsonerror.NotFound( + fmt.Sprintf("Server name %q does not exist", request.Destination()), + ), + } + } + queryRes := api.QueryLatestEventsAndStateResponse{ RoomVersion: verRes.RoomVersion, } - event, err := eventutil.QueryAndBuildEvent(httpReq.Context(), &builder, cfg.Matrix, time.Now(), rsAPI, &queryRes) + event, err := eventutil.QueryAndBuildEvent(httpReq.Context(), &builder, cfg.Matrix, identity, time.Now(), rsAPI, &queryRes) if err == eventutil.ErrRoomNoExists { return util.JSONResponse{ Code: http.StatusNotFound, diff --git a/federationapi/routing/leave.go b/federationapi/routing/leave.go index a67e4e28b..f1e9f49ba 100644 --- a/federationapi/routing/leave.go +++ b/federationapi/routing/leave.go @@ -13,6 +13,7 @@ package routing import ( + "fmt" "net/http" "time" @@ -60,8 +61,18 @@ func MakeLeave( return jsonerror.InternalServerError() } + identity, err := cfg.Matrix.SigningIdentityFor(request.Destination()) + if err != nil { + return util.JSONResponse{ + Code: http.StatusNotFound, + JSON: jsonerror.NotFound( + fmt.Sprintf("Server name %q does not exist", request.Destination()), + ), + } + } + var queryRes api.QueryLatestEventsAndStateResponse - event, err := eventutil.QueryAndBuildEvent(httpReq.Context(), &builder, cfg.Matrix, time.Now(), rsAPI, &queryRes) + event, err := eventutil.QueryAndBuildEvent(httpReq.Context(), &builder, cfg.Matrix, identity, time.Now(), rsAPI, &queryRes) if err == eventutil.ErrRoomNoExists { return util.JSONResponse{ Code: http.StatusNotFound, diff --git a/internal/eventutil/events.go b/internal/eventutil/events.go index 5b5dbf10b..c572d8830 100644 --- a/internal/eventutil/events.go +++ b/internal/eventutil/events.go @@ -38,7 +38,8 @@ var ErrRoomNoExists = errors.New("room does not exist") // Returns an error if something else went wrong func QueryAndBuildEvent( ctx context.Context, - builder *gomatrixserverlib.EventBuilder, cfg *config.Global, evTime time.Time, + builder *gomatrixserverlib.EventBuilder, cfg *config.Global, + identity *gomatrixserverlib.SigningIdentity, evTime time.Time, rsAPI api.QueryLatestEventsAndStateAPI, queryRes *api.QueryLatestEventsAndStateResponse, ) (*gomatrixserverlib.HeaderedEvent, error) { if queryRes == nil { @@ -50,30 +51,24 @@ func QueryAndBuildEvent( // This can pass through a ErrRoomNoExists to the caller return nil, err } - return BuildEvent(ctx, builder, cfg, evTime, eventsNeeded, queryRes) + return BuildEvent(ctx, builder, cfg, identity, evTime, eventsNeeded, queryRes) } // BuildEvent builds a Matrix event from the builder and QueryLatestEventsAndStateResponse // provided. func BuildEvent( ctx context.Context, - builder *gomatrixserverlib.EventBuilder, cfg *config.Global, evTime time.Time, + builder *gomatrixserverlib.EventBuilder, cfg *config.Global, + identity *gomatrixserverlib.SigningIdentity, evTime time.Time, eventsNeeded *gomatrixserverlib.StateNeeded, queryRes *api.QueryLatestEventsAndStateResponse, ) (*gomatrixserverlib.HeaderedEvent, error) { - err := addPrevEventsToEvent(builder, eventsNeeded, queryRes) - if err != nil { + if err := addPrevEventsToEvent(builder, eventsNeeded, queryRes); err != nil { return nil, err } - _, domain, err := cfg.SplitLocalID('@', builder.Sender) - if err != nil { - return nil, err - } - - // TODO: Fix key ID and private key here event, err := builder.Build( - evTime, domain, cfg.KeyID, - cfg.PrivateKey, queryRes.RoomVersion, + evTime, identity.ServerName, identity.KeyID, + identity.PrivateKey, queryRes.RoomVersion, ) if err != nil { return nil, err diff --git a/roomserver/internal/alias.go b/roomserver/internal/alias.go index 8e06e3d5a..329e6af7f 100644 --- a/roomserver/internal/alias.go +++ b/roomserver/internal/alias.go @@ -195,6 +195,16 @@ func (r *RoomserverInternalAPI) RemoveRoomAlias( sender = ev.Sender() } + _, senderDomain, err := r.Cfg.Matrix.SplitLocalID('@', sender) + if err != nil { + return err + } + + identity, err := r.Cfg.Matrix.SigningIdentityFor(senderDomain) + if err != nil { + return err + } + builder := &gomatrixserverlib.EventBuilder{ Sender: sender, RoomID: ev.RoomID(), @@ -216,7 +226,7 @@ func (r *RoomserverInternalAPI) RemoveRoomAlias( return err } - newEvent, err := eventutil.BuildEvent(ctx, builder, r.Cfg.Matrix, time.Now(), &eventsNeeded, stateRes) + newEvent, err := eventutil.BuildEvent(ctx, builder, r.Cfg.Matrix, identity, time.Now(), &eventsNeeded, stateRes) if err != nil { return err } diff --git a/roomserver/internal/perform/perform_admin.go b/roomserver/internal/perform/perform_admin.go index 131ff3c6e..d42f4e45d 100644 --- a/roomserver/internal/perform/perform_admin.go +++ b/roomserver/internal/perform/perform_admin.go @@ -139,7 +139,12 @@ func (r *Admin) PerformAdminEvacuateRoom( return nil } - event, err := eventutil.BuildEvent(ctx, fledglingEvent, r.Cfg.Matrix, time.Now(), &eventsNeeded, latestRes) + identity, err := r.Cfg.Matrix.SigningIdentityFor(senderDomain) + if err != nil { + continue + } + + event, err := eventutil.BuildEvent(ctx, fledglingEvent, r.Cfg.Matrix, identity, time.Now(), &eventsNeeded, latestRes) if err != nil { res.Error = &api.PerformError{ Code: api.PerformErrorBadRequest, @@ -242,6 +247,15 @@ func (r *Admin) PerformAdminDownloadState( req *api.PerformAdminDownloadStateRequest, res *api.PerformAdminDownloadStateResponse, ) error { + _, senderDomain, err := r.Cfg.Matrix.SplitLocalID('@', req.UserID) + if err != nil { + res.Error = &api.PerformError{ + Code: api.PerformErrorBadRequest, + Msg: fmt.Sprintf("r.Cfg.Matrix.SplitLocalID: %s", err), + } + return nil + } + roomInfo, err := r.DB.RoomInfo(ctx, req.RoomID) if err != nil { res.Error = &api.PerformError{ @@ -331,7 +345,12 @@ func (r *Admin) PerformAdminDownloadState( Depth: depth, } - ev, err := eventutil.BuildEvent(ctx, builder, r.Cfg.Matrix, time.Now(), &eventsNeeded, queryRes) + identity, err := r.Cfg.Matrix.SigningIdentityFor(senderDomain) + if err != nil { + return err + } + + ev, err := eventutil.BuildEvent(ctx, builder, r.Cfg.Matrix, identity, time.Now(), &eventsNeeded, queryRes) if err != nil { res.Error = &api.PerformError{ Code: api.PerformErrorBadRequest, diff --git a/roomserver/internal/perform/perform_join.go b/roomserver/internal/perform/perform_join.go index 7cb51c219..4de008c66 100644 --- a/roomserver/internal/perform/perform_join.go +++ b/roomserver/internal/perform/perform_join.go @@ -196,7 +196,7 @@ func (r *Joiner) performJoinRoomByID( // Prepare the template for the join event. userID := req.UserID - _, userDomain, err := gomatrixserverlib.SplitID('@', userID) + _, userDomain, err := r.Cfg.Matrix.SplitLocalID('@', userID) if err != nil { return "", "", &rsAPI.PerformError{ Code: rsAPI.PerformErrorBadRequest, @@ -282,7 +282,7 @@ func (r *Joiner) performJoinRoomByID( // locally on the homeserver. // TODO: Check what happens if the room exists on the server // but everyone has since left. I suspect it does the wrong thing. - event, buildRes, err := buildEvent(ctx, r.DB, r.Cfg.Matrix, &eb) + event, buildRes, err := buildEvent(ctx, r.DB, r.Cfg.Matrix, userDomain, &eb) switch err { case nil: @@ -409,7 +409,9 @@ func (r *Joiner) populateAuthorisedViaUserForRestrictedJoin( } func buildEvent( - ctx context.Context, db storage.Database, cfg *config.Global, builder *gomatrixserverlib.EventBuilder, + ctx context.Context, db storage.Database, cfg *config.Global, + senderDomain gomatrixserverlib.ServerName, + builder *gomatrixserverlib.EventBuilder, ) (*gomatrixserverlib.HeaderedEvent, *rsAPI.QueryLatestEventsAndStateResponse, error) { eventsNeeded, err := gomatrixserverlib.StateNeededForEventBuilder(builder) if err != nil { @@ -437,7 +439,12 @@ func buildEvent( } } - ev, err := eventutil.BuildEvent(ctx, builder, cfg, time.Now(), &eventsNeeded, &queryRes) + identity, err := cfg.SigningIdentityFor(senderDomain) + if err != nil { + return nil, nil, err + } + + ev, err := eventutil.BuildEvent(ctx, builder, cfg, identity, time.Now(), &eventsNeeded, &queryRes) if err != nil { return nil, nil, err } diff --git a/roomserver/internal/perform/perform_leave.go b/roomserver/internal/perform/perform_leave.go index 49e4b479a..fa998e3e1 100644 --- a/roomserver/internal/perform/perform_leave.go +++ b/roomserver/internal/perform/perform_leave.go @@ -162,21 +162,21 @@ func (r *Leaver) performLeaveRoomByID( return nil, fmt.Errorf("eb.SetUnsigned: %w", err) } + // Get the sender domain. + _, senderDomain, serr := r.Cfg.Matrix.SplitLocalID('@', eb.Sender) + if serr != nil { + return nil, fmt.Errorf("sender %q is invalid", eb.Sender) + } + // We know that the user is in the room at this point so let's build // a leave event. // TODO: Check what happens if the room exists on the server // but everyone has since left. I suspect it does the wrong thing. - event, buildRes, err := buildEvent(ctx, r.DB, r.Cfg.Matrix, &eb) + event, buildRes, err := buildEvent(ctx, r.DB, r.Cfg.Matrix, senderDomain, &eb) if err != nil { return nil, fmt.Errorf("eventutil.BuildEvent: %w", err) } - // Get the sender domain. - _, senderDomain, serr := gomatrixserverlib.SplitID('@', event.Sender()) - if serr != nil { - return nil, fmt.Errorf("sender %q is invalid", event.Sender()) - } - // Give our leave event to the roomserver input stream. The // roomserver will process the membership change and notify // downstream automatically. diff --git a/roomserver/internal/perform/perform_upgrade.go b/roomserver/internal/perform/perform_upgrade.go index f627e86fc..02a19911c 100644 --- a/roomserver/internal/perform/perform_upgrade.go +++ b/roomserver/internal/perform/perform_upgrade.go @@ -595,8 +595,21 @@ func (r *Upgrader) makeHeaderedEvent(ctx context.Context, evTime time.Time, user Msg: fmt.Sprintf("Failed to set new %q event content: %s", builder.Type, err), } } + // Get the sender domain. + _, senderDomain, serr := r.Cfg.Matrix.SplitLocalID('@', builder.Sender) + if serr != nil { + return nil, &api.PerformError{ + Msg: fmt.Sprintf("Failed to split user ID %q: %s", builder.Sender, err), + } + } + identity, err := r.Cfg.Matrix.SigningIdentityFor(senderDomain) + if err != nil { + return nil, &api.PerformError{ + Msg: fmt.Sprintf("Failed to get signing identity for %q: %s", senderDomain, err), + } + } var queryRes api.QueryLatestEventsAndStateResponse - headeredEvent, err := eventutil.QueryAndBuildEvent(ctx, &builder, r.Cfg.Matrix, evTime, r.URSAPI, &queryRes) + headeredEvent, err := eventutil.QueryAndBuildEvent(ctx, &builder, r.Cfg.Matrix, identity, evTime, r.URSAPI, &queryRes) if err == eventutil.ErrRoomNoExists { return nil, &api.PerformError{ Code: api.PerformErrorNoRoom, diff --git a/setup/config/config_global.go b/setup/config/config_global.go index c78b5c8d0..f2fdd021e 100644 --- a/setup/config/config_global.go +++ b/setup/config/config_global.go @@ -146,11 +146,20 @@ func (c *Global) SplitLocalID(sigil byte, id string) (string, gomatrixserverlib. return u, s, err } if !c.IsLocalServerName(s) { - return u, s, fmt.Errorf("server name not locally configured") + return u, s, fmt.Errorf("server name %q not known", s) } return u, s, nil } +func (c *Global) SigningIdentityFor(serverName gomatrixserverlib.ServerName) (*gomatrixserverlib.SigningIdentity, error) { + for _, id := range c.SigningIdentities() { + if id.ServerName == serverName { + return id, nil + } + } + return nil, fmt.Errorf("no signing identity %q", serverName) +} + func (c *Global) SigningIdentities() []*gomatrixserverlib.SigningIdentity { identities := make([]*gomatrixserverlib.SigningIdentity, 0, len(c.VirtualHosts)+1) identities = append(identities, &gomatrixserverlib.SigningIdentity{