First pass at server ACLs (not efficient)

This commit is contained in:
Neil Alexander 2020-08-11 14:12:21 +01:00
parent cce3678abe
commit adfefb5c12
No known key found for this signature in database
GPG key ID: A02A2019A2BB0944
3 changed files with 189 additions and 1 deletions

View file

@ -2,9 +2,15 @@ package api
import ( import (
"context" "context"
"encoding/json"
"fmt"
"net"
"regexp"
"strings"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util" "github.com/matrix-org/util"
"github.com/sirupsen/logrus"
) )
// GetEvent returns the current state event in the room or nil. // GetEvent returns the current state event in the room or nil.
@ -25,6 +31,74 @@ func GetEvent(ctx context.Context, stateAPI CurrentStateInternalAPI, roomID stri
return nil return nil
} }
// IsServerBannedFromRoom returns whether the server is banned from a room by server ACLs.
func IsServerBannedFromRoom(ctx context.Context, stateAPI CurrentStateInternalAPI, roomID string, serverName gomatrixserverlib.ServerName) bool {
tuple := gomatrixserverlib.StateKeyTuple{
EventType: "m.room.server_acl",
StateKey: "",
}
req := &QueryCurrentStateRequest{
RoomID: roomID,
StateTuples: []gomatrixserverlib.StateKeyTuple{tuple},
}
res := &QueryCurrentStateResponse{}
if err := stateAPI.QueryCurrentState(ctx, req, res); err != nil {
logrus.WithError(err).Error("Failed to query current state for server ACL")
return true
}
state, ok := res.StateEvents[tuple]
if !ok {
return false
}
acls := struct {
Allowed []string `json:"allow"`
Denied []string `json:"deny"`
AllowIPLiterals bool `json:"allow_ip_literals"`
}{}
if err := json.Unmarshal(state.Content(), &acls); err != nil {
return true
}
// First, check to see if this is an IP literal.
if _, _, err := net.ParseCIDR(fmt.Sprintf("%s/0", serverName)); err == nil {
if !acls.AllowIPLiterals {
return true
}
}
// Next, build up a list of regular expressions for allowed and denied.
allowed := []*regexp.Regexp{}
denied := []*regexp.Regexp{}
for _, orig := range acls.Allowed {
escaped := regexp.QuoteMeta(orig)
escaped = strings.Replace(escaped, "\\?", "(.)", -1)
escaped = strings.Replace(escaped, "\\*", "(.*)", -1)
if expr, err := regexp.Compile(escaped); err == nil {
allowed = append(allowed, expr)
}
}
for _, orig := range acls.Denied {
escaped := regexp.QuoteMeta(orig)
escaped = strings.Replace(escaped, "\\?", "(.)", -1)
escaped = strings.Replace(escaped, "\\*", "(.*)", -1)
if expr, err := regexp.Compile(escaped); err == nil {
denied = append(denied, expr)
}
}
// Now see if we match any of the denied hosts.
for _, expr := range denied {
if expr.MatchString(string(serverName)) {
return true
}
}
// Finally, see if we match any of the allowed hosts.
for _, expr := range allowed {
if expr.MatchString(string(serverName)) {
return false
}
}
// Failing all else deny.
return true
}
// PopulatePublicRooms extracts PublicRoom information for all the provided room IDs. The IDs are not checked to see if they are visible in the // PopulatePublicRooms extracts PublicRoom information for all the provided room IDs. The IDs are not checked to see if they are visible in the
// published room directory. // published room directory.
// due to lots of switches // due to lots of switches

View file

@ -82,7 +82,7 @@ func Setup(
func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse {
return Send( return Send(
httpReq, request, gomatrixserverlib.TransactionID(vars["txnID"]), httpReq, request, gomatrixserverlib.TransactionID(vars["txnID"]),
cfg, rsAPI, eduAPI, keyAPI, keys, federation, cfg, rsAPI, eduAPI, keyAPI, stateAPI, keys, federation,
) )
}, },
)).Methods(http.MethodPut, http.MethodOptions) )).Methods(http.MethodPut, http.MethodOptions)
@ -90,6 +90,12 @@ func Setup(
v1fedmux.Handle("/invite/{roomID}/{eventID}", httputil.MakeFedAPI( v1fedmux.Handle("/invite/{roomID}/{eventID}", httputil.MakeFedAPI(
"federation_invite", cfg.Matrix.ServerName, keys, wakeup, "federation_invite", cfg.Matrix.ServerName, keys, wakeup,
func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse {
if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("Forbidden by server ACLs"),
}
}
res := InviteV1( res := InviteV1(
httpReq, request, vars["roomID"], vars["eventID"], httpReq, request, vars["roomID"], vars["eventID"],
cfg, rsAPI, keys, cfg, rsAPI, keys,
@ -106,6 +112,12 @@ func Setup(
v2fedmux.Handle("/invite/{roomID}/{eventID}", httputil.MakeFedAPI( v2fedmux.Handle("/invite/{roomID}/{eventID}", httputil.MakeFedAPI(
"federation_invite", cfg.Matrix.ServerName, keys, wakeup, "federation_invite", cfg.Matrix.ServerName, keys, wakeup,
func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse {
if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("Forbidden by server ACLs"),
}
}
return InviteV2( return InviteV2(
httpReq, request, vars["roomID"], vars["eventID"], httpReq, request, vars["roomID"], vars["eventID"],
cfg, rsAPI, keys, cfg, rsAPI, keys,
@ -140,6 +152,12 @@ func Setup(
v1fedmux.Handle("/state/{roomID}", httputil.MakeFedAPI( v1fedmux.Handle("/state/{roomID}", httputil.MakeFedAPI(
"federation_get_state", cfg.Matrix.ServerName, keys, wakeup, "federation_get_state", cfg.Matrix.ServerName, keys, wakeup,
func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse {
if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("Forbidden by server ACLs"),
}
}
return GetState( return GetState(
httpReq.Context(), request, rsAPI, vars["roomID"], httpReq.Context(), request, rsAPI, vars["roomID"],
) )
@ -149,6 +167,12 @@ func Setup(
v1fedmux.Handle("/state_ids/{roomID}", httputil.MakeFedAPI( v1fedmux.Handle("/state_ids/{roomID}", httputil.MakeFedAPI(
"federation_get_state_ids", cfg.Matrix.ServerName, keys, wakeup, "federation_get_state_ids", cfg.Matrix.ServerName, keys, wakeup,
func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse {
if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("Forbidden by server ACLs"),
}
}
return GetStateIDs( return GetStateIDs(
httpReq.Context(), request, rsAPI, vars["roomID"], httpReq.Context(), request, rsAPI, vars["roomID"],
) )
@ -158,6 +182,12 @@ func Setup(
v1fedmux.Handle("/event_auth/{roomID}/{eventID}", httputil.MakeFedAPI( v1fedmux.Handle("/event_auth/{roomID}/{eventID}", httputil.MakeFedAPI(
"federation_get_event_auth", cfg.Matrix.ServerName, keys, wakeup, "federation_get_event_auth", cfg.Matrix.ServerName, keys, wakeup,
func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse {
if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("Forbidden by server ACLs"),
}
}
return GetEventAuth( return GetEventAuth(
httpReq.Context(), request, rsAPI, vars["roomID"], vars["eventID"], httpReq.Context(), request, rsAPI, vars["roomID"], vars["eventID"],
) )
@ -194,6 +224,12 @@ func Setup(
v1fedmux.Handle("/make_join/{roomID}/{eventID}", httputil.MakeFedAPI( v1fedmux.Handle("/make_join/{roomID}/{eventID}", httputil.MakeFedAPI(
"federation_make_join", cfg.Matrix.ServerName, keys, wakeup, "federation_make_join", cfg.Matrix.ServerName, keys, wakeup,
func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse {
if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("Forbidden by server ACLs"),
}
}
roomID := vars["roomID"] roomID := vars["roomID"]
eventID := vars["eventID"] eventID := vars["eventID"]
queryVars := httpReq.URL.Query() queryVars := httpReq.URL.Query()
@ -219,6 +255,12 @@ func Setup(
v1fedmux.Handle("/send_join/{roomID}/{eventID}", httputil.MakeFedAPI( v1fedmux.Handle("/send_join/{roomID}/{eventID}", httputil.MakeFedAPI(
"federation_send_join", cfg.Matrix.ServerName, keys, wakeup, "federation_send_join", cfg.Matrix.ServerName, keys, wakeup,
func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse {
if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("Forbidden by server ACLs"),
}
}
roomID := vars["roomID"] roomID := vars["roomID"]
eventID := vars["eventID"] eventID := vars["eventID"]
res := SendJoin( res := SendJoin(
@ -245,6 +287,12 @@ func Setup(
v2fedmux.Handle("/send_join/{roomID}/{eventID}", httputil.MakeFedAPI( v2fedmux.Handle("/send_join/{roomID}/{eventID}", httputil.MakeFedAPI(
"federation_send_join", cfg.Matrix.ServerName, keys, wakeup, "federation_send_join", cfg.Matrix.ServerName, keys, wakeup,
func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse {
if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("Forbidden by server ACLs"),
}
}
roomID := vars["roomID"] roomID := vars["roomID"]
eventID := vars["eventID"] eventID := vars["eventID"]
return SendJoin( return SendJoin(
@ -256,6 +304,12 @@ func Setup(
v1fedmux.Handle("/make_leave/{roomID}/{eventID}", httputil.MakeFedAPI( v1fedmux.Handle("/make_leave/{roomID}/{eventID}", httputil.MakeFedAPI(
"federation_make_leave", cfg.Matrix.ServerName, keys, wakeup, "federation_make_leave", cfg.Matrix.ServerName, keys, wakeup,
func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse {
if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("Forbidden by server ACLs"),
}
}
roomID := vars["roomID"] roomID := vars["roomID"]
eventID := vars["eventID"] eventID := vars["eventID"]
return MakeLeave( return MakeLeave(
@ -264,9 +318,47 @@ func Setup(
}, },
)).Methods(http.MethodGet) )).Methods(http.MethodGet)
v1fedmux.Handle("/send_leave/{roomID}/{eventID}", httputil.MakeFedAPI(
"federation_send_leave", cfg.Matrix.ServerName, keys, wakeup,
func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse {
if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("Forbidden by server ACLs"),
}
}
roomID := vars["roomID"]
eventID := vars["eventID"]
res := SendLeave(
httpReq, request, cfg, rsAPI, keys, roomID, eventID,
)
// not all responses get wrapped in [code, body]
var body interface{}
body = []interface{}{
res.Code, res.JSON,
}
jerr, ok := res.JSON.(*jsonerror.MatrixError)
if ok {
body = jerr
}
return util.JSONResponse{
Headers: res.Headers,
Code: res.Code,
JSON: body,
}
},
)).Methods(http.MethodPut)
v2fedmux.Handle("/send_leave/{roomID}/{eventID}", httputil.MakeFedAPI( v2fedmux.Handle("/send_leave/{roomID}/{eventID}", httputil.MakeFedAPI(
"federation_send_leave", cfg.Matrix.ServerName, keys, wakeup, "federation_send_leave", cfg.Matrix.ServerName, keys, wakeup,
func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse {
if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("Forbidden by server ACLs"),
}
}
roomID := vars["roomID"] roomID := vars["roomID"]
eventID := vars["eventID"] eventID := vars["eventID"]
return SendLeave( return SendLeave(
@ -285,6 +377,12 @@ func Setup(
v1fedmux.Handle("/get_missing_events/{roomID}", httputil.MakeFedAPI( v1fedmux.Handle("/get_missing_events/{roomID}", httputil.MakeFedAPI(
"federation_get_missing_events", cfg.Matrix.ServerName, keys, wakeup, "federation_get_missing_events", cfg.Matrix.ServerName, keys, wakeup,
func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse {
if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("Forbidden by server ACLs"),
}
}
return GetMissingEvents(httpReq, request, rsAPI, vars["roomID"]) return GetMissingEvents(httpReq, request, rsAPI, vars["roomID"])
}, },
)).Methods(http.MethodPost) )).Methods(http.MethodPost)
@ -292,6 +390,12 @@ func Setup(
v1fedmux.Handle("/backfill/{roomID}", httputil.MakeFedAPI( v1fedmux.Handle("/backfill/{roomID}", httputil.MakeFedAPI(
"federation_backfill", cfg.Matrix.ServerName, keys, wakeup, "federation_backfill", cfg.Matrix.ServerName, keys, wakeup,
func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse { func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest, vars map[string]string) util.JSONResponse {
if currentstateAPI.IsServerBannedFromRoom(httpReq.Context(), stateAPI, vars["roomID"], request.Origin()) {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("Forbidden by server ACLs"),
}
}
return Backfill(httpReq, request, rsAPI, vars["roomID"], cfg) return Backfill(httpReq, request, rsAPI, vars["roomID"], cfg)
}, },
)).Methods(http.MethodGet) )).Methods(http.MethodGet)

View file

@ -21,6 +21,7 @@ import (
"net/http" "net/http"
"github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/clientapi/jsonerror"
currentstateAPI "github.com/matrix-org/dendrite/currentstateserver/api"
eduserverAPI "github.com/matrix-org/dendrite/eduserver/api" eduserverAPI "github.com/matrix-org/dendrite/eduserver/api"
"github.com/matrix-org/dendrite/internal/config" "github.com/matrix-org/dendrite/internal/config"
keyapi "github.com/matrix-org/dendrite/keyserver/api" keyapi "github.com/matrix-org/dendrite/keyserver/api"
@ -39,6 +40,7 @@ func Send(
rsAPI api.RoomserverInternalAPI, rsAPI api.RoomserverInternalAPI,
eduAPI eduserverAPI.EDUServerInputAPI, eduAPI eduserverAPI.EDUServerInputAPI,
keyAPI keyapi.KeyInternalAPI, keyAPI keyapi.KeyInternalAPI,
stateAPI currentstateAPI.CurrentStateInternalAPI,
keys gomatrixserverlib.JSONVerifier, keys gomatrixserverlib.JSONVerifier,
federation *gomatrixserverlib.FederationClient, federation *gomatrixserverlib.FederationClient,
) util.JSONResponse { ) util.JSONResponse {
@ -46,6 +48,7 @@ func Send(
context: httpReq.Context(), context: httpReq.Context(),
rsAPI: rsAPI, rsAPI: rsAPI,
eduAPI: eduAPI, eduAPI: eduAPI,
stateAPI: stateAPI,
keys: keys, keys: keys,
federation: federation, federation: federation,
haveEvents: make(map[string]*gomatrixserverlib.HeaderedEvent), haveEvents: make(map[string]*gomatrixserverlib.HeaderedEvent),
@ -104,6 +107,7 @@ type txnReq struct {
rsAPI api.RoomserverInternalAPI rsAPI api.RoomserverInternalAPI
eduAPI eduserverAPI.EDUServerInputAPI eduAPI eduserverAPI.EDUServerInputAPI
keyAPI keyapi.KeyInternalAPI keyAPI keyapi.KeyInternalAPI
stateAPI currentstateAPI.CurrentStateInternalAPI
keys gomatrixserverlib.JSONVerifier keys gomatrixserverlib.JSONVerifier
federation txnFederationClient federation txnFederationClient
// local cache of events for auth checks, etc - this may include events // local cache of events for auth checks, etc - this may include events
@ -164,6 +168,12 @@ func (t *txnReq) processTransaction() (*gomatrixserverlib.RespSend, *util.JSONRe
util.GetLogger(t.context).WithError(err).Warnf("Transaction: Failed to parse event JSON of event %s", string(pdu)) util.GetLogger(t.context).WithError(err).Warnf("Transaction: Failed to parse event JSON of event %s", string(pdu))
continue continue
} }
if currentstateAPI.IsServerBannedFromRoom(t.context, t.stateAPI, event.RoomID(), event.Origin()) {
results[event.EventID()] = gomatrixserverlib.PDUResult{
Error: "Forbidden by server ACLs",
}
continue
}
if err = gomatrixserverlib.VerifyAllEventSignatures(t.context, []gomatrixserverlib.Event{event}, t.keys); err != nil { if err = gomatrixserverlib.VerifyAllEventSignatures(t.context, []gomatrixserverlib.Event{event}, t.keys); err != nil {
util.GetLogger(t.context).WithError(err).Warnf("Transaction: Couldn't validate signature of event %q", event.EventID()) util.GetLogger(t.context).WithError(err).Warnf("Transaction: Couldn't validate signature of event %q", event.EventID())
results[event.EventID()] = gomatrixserverlib.PDUResult{ results[event.EventID()] = gomatrixserverlib.PDUResult{